Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/traitlets/traitlets.py: 9%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1735 statements  

1""" 

2A lightweight Traits like module. 

3 

4This is designed to provide a lightweight, simple, pure Python version of 

5many of the capabilities of enthought.traits. This includes: 

6 

7* Validation 

8* Type specification with defaults 

9* Static and dynamic notification 

10* Basic predefined types 

11* An API that is similar to enthought.traits 

12 

13We don't support: 

14 

15* Delegation 

16* Automatic GUI generation 

17* A full set of trait types. Most importantly, we don't provide container 

18 traits (list, dict, tuple) that can trigger notifications if their 

19 contents change. 

20* API compatibility with enthought.traits 

21 

22There are also some important difference in our design: 

23 

24* enthought.traits does not validate default values. We do. 

25 

26We choose to create this module because we need these capabilities, but 

27we need them to be pure Python so they work in all Python implementations, 

28including Jython and IronPython. 

29 

30Inheritance diagram: 

31 

32.. inheritance-diagram:: traitlets.traitlets 

33 :parts: 3 

34""" 

35 

36# Copyright (c) IPython Development Team. 

37# Distributed under the terms of the Modified BSD License. 

38# 

39# Adapted from enthought.traits, Copyright (c) Enthought, Inc., 

40# also under the terms of the Modified BSD License. 

41 

42from __future__ import annotations 

43 

44import contextlib 

45import enum 

46import inspect 

47import os 

48import re 

49import sys 

50import types 

51import typing as t 

52from ast import literal_eval 

53 

54from .utils.bunch import Bunch 

55from .utils.descriptions import add_article, class_of, describe, repr_type 

56from .utils.getargspec import getargspec 

57from .utils.importstring import import_item 

58from .utils.sentinel import Sentinel 

59from .utils.warnings import deprecated_method, should_warn, warn 

60 

61SequenceTypes = (list, tuple, set, frozenset) 

62 

63# backward compatibility, use to differ between Python 2 and 3. 

64ClassTypes = (type,) 

65 

66if t.TYPE_CHECKING: 

67 from typing_extensions import TypeVar 

68else: 

69 from typing import TypeVar 

70 

71# exports: 

72 

73__all__ = [ 

74 "All", 

75 "Any", 

76 "BaseDescriptor", 

77 "Bool", 

78 "Bytes", 

79 "CBool", 

80 "CBytes", 

81 "CComplex", 

82 "CFloat", 

83 "CInt", 

84 "CLong", 

85 "CRegExp", 

86 "CUnicode", 

87 "Callable", 

88 "CaselessStrEnum", 

89 "ClassBasedTraitType", 

90 "Complex", 

91 "Container", 

92 "DefaultHandler", 

93 "Dict", 

94 "DottedObjectName", 

95 "Enum", 

96 "EventHandler", 

97 "Float", 

98 "ForwardDeclaredInstance", 

99 "ForwardDeclaredMixin", 

100 "ForwardDeclaredType", 

101 "FuzzyEnum", 

102 "HasDescriptors", 

103 "HasTraits", 

104 "Instance", 

105 "Int", 

106 "Integer", 

107 "List", 

108 "Long", 

109 "MetaHasDescriptors", 

110 "MetaHasTraits", 

111 "ObjectName", 

112 "ObserveHandler", 

113 "Set", 

114 "TCPAddress", 

115 "This", 

116 "TraitError", 

117 "TraitType", 

118 "Tuple", 

119 "Type", 

120 "Unicode", 

121 "Undefined", 

122 "Union", 

123 "UseEnum", 

124 "ValidateHandler", 

125 "default", 

126 "directional_link", 

127 "dlink", 

128 "link", 

129 "observe", 

130 "observe_compat", 

131 "parse_notifier_name", 

132 "validate", 

133] 

134 

135# any TraitType subclass (that doesn't start with _) will be added automatically 

136 

137# ----------------------------------------------------------------------------- 

138# Basic classes 

139# ----------------------------------------------------------------------------- 

140 

141 

142Undefined = Sentinel( 

143 "Undefined", 

144 "traitlets", 

145 """ 

146Used in Traitlets to specify that no defaults are set in kwargs 

147""", 

148) 

149 

150All = Sentinel( 

151 "All", 

152 "traitlets", 

153 """ 

154Used in Traitlets to listen to all types of notification or to notifications 

155from all trait attributes. 

156""", 

157) 

158 

159# Deprecated alias 

160NoDefaultSpecified = Undefined 

161 

162 

163class TraitError(Exception): 

164 pass 

165 

166 

167# ----------------------------------------------------------------------------- 

168# Utilities 

169# ----------------------------------------------------------------------------- 

170 

171 

172def isidentifier(s: t.Any) -> bool: 

173 return t.cast(bool, s.isidentifier()) 

174 

175 

176def _safe_literal_eval(s: str) -> t.Any: 

177 """Safely evaluate an expression 

178 

179 Returns original string if eval fails. 

180 

181 Use only where types are ambiguous. 

182 """ 

183 try: 

184 return literal_eval(s) 

185 except (NameError, SyntaxError, ValueError): 

186 return s 

187 

188 

189def is_trait(t: t.Any) -> bool: 

190 """Returns whether the given value is an instance or subclass of TraitType.""" 

191 return isinstance(t, TraitType) or (isinstance(t, type) and issubclass(t, TraitType)) 

192 

193 

194def parse_notifier_name(names: Sentinel | str | t.Iterable[Sentinel | str]) -> t.Iterable[t.Any]: 

195 """Convert the name argument to a list of names. 

196 

197 Examples 

198 -------- 

199 >>> parse_notifier_name([]) 

200 [traitlets.All] 

201 >>> parse_notifier_name("a") 

202 ['a'] 

203 >>> parse_notifier_name(["a", "b"]) 

204 ['a', 'b'] 

205 >>> parse_notifier_name(All) 

206 [traitlets.All] 

207 """ 

208 if names is All or isinstance(names, str): 

209 return [names] 

210 elif isinstance(names, Sentinel): 

211 raise TypeError("`names` must be either `All`, a str, or a list of strs.") 

212 else: 

213 if not names or All in names: 

214 return [All] 

215 for n in names: 

216 if not isinstance(n, str): 

217 raise TypeError(f"names must be strings, not {type(n).__name__}({n!r})") 

218 return names 

219 

220 

221class _SimpleTest: 

222 def __init__(self, value: t.Any) -> None: 

223 self.value = value 

224 

225 def __call__(self, test: t.Any) -> bool: 

226 return bool(test == self.value) 

227 

228 def __repr__(self) -> str: 

229 return "<SimpleTest(%r)" % self.value 

230 

231 def __str__(self) -> str: 

232 return self.__repr__() 

233 

234 

235def getmembers(object: t.Any, predicate: t.Any = None) -> list[tuple[str, t.Any]]: 

236 """A safe version of inspect.getmembers that handles missing attributes. 

237 

238 This is useful when there are descriptor based attributes that for 

239 some reason raise AttributeError even though they exist. This happens 

240 in zope.interface with the __provides__ attribute. 

241 """ 

242 results = [] 

243 for key in dir(object): 

244 try: 

245 value = getattr(object, key) 

246 except AttributeError: 

247 pass 

248 else: 

249 if not predicate or predicate(value): 

250 results.append((key, value)) 

251 results.sort() 

252 return results 

253 

254 

255def _validate_link(*tuples: t.Any) -> None: 

256 """Validate arguments for traitlet link functions""" 

257 for tup in tuples: 

258 if not len(tup) == 2: 

259 raise TypeError( 

260 "Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t 

261 ) 

262 obj, trait_name = tup 

263 if not isinstance(obj, HasTraits): 

264 raise TypeError("Each object must be HasTraits, not %r" % type(obj)) 

265 if trait_name not in obj.traits(): 

266 raise TypeError(f"{obj!r} has no trait {trait_name!r}") 

267 

268 

269class link: 

270 """Link traits from different objects together so they remain in sync. 

271 

272 Parameters 

273 ---------- 

274 source : (object / attribute name) pair 

275 target : (object / attribute name) pair 

276 transform: iterable with two callables (optional) 

277 Data transformation between source and target and target and source. 

278 

279 Examples 

280 -------- 

281 >>> class X(HasTraits): 

282 ... value = Int() 

283 

284 >>> src = X(value=1) 

285 >>> tgt = X(value=42) 

286 >>> c = link((src, "value"), (tgt, "value")) 

287 

288 Setting source updates target objects: 

289 >>> src.value = 5 

290 >>> tgt.value 

291 5 

292 """ 

293 

294 updating = False 

295 

296 def __init__(self, source: t.Any, target: t.Any, transform: t.Any = None) -> None: 

297 _validate_link(source, target) 

298 self.source, self.target = source, target 

299 self._transform, self._transform_inv = transform if transform else (lambda x: x,) * 2 

300 

301 self.link() 

302 

303 def link(self) -> None: 

304 try: 

305 setattr( 

306 self.target[0], 

307 self.target[1], 

308 self._transform(getattr(self.source[0], self.source[1])), 

309 ) 

310 

311 finally: 

312 self.source[0].observe(self._update_target, names=self.source[1]) 

313 self.target[0].observe(self._update_source, names=self.target[1]) 

314 

315 @contextlib.contextmanager 

316 def _busy_updating(self) -> t.Any: 

317 self.updating = True 

318 try: 

319 yield 

320 finally: 

321 self.updating = False 

322 

323 def _update_target(self, change: t.Any) -> None: 

324 if self.updating: 

325 return 

326 with self._busy_updating(): 

327 setattr(self.target[0], self.target[1], self._transform(change.new)) 

328 if getattr(self.source[0], self.source[1]) != change.new: 

329 raise TraitError( 

330 f"Broken link {self}: the source value changed while updating " "the target." 

331 ) 

332 

333 def _update_source(self, change: t.Any) -> None: 

334 if self.updating: 

335 return 

336 with self._busy_updating(): 

337 setattr(self.source[0], self.source[1], self._transform_inv(change.new)) 

338 if getattr(self.target[0], self.target[1]) != change.new: 

339 raise TraitError( 

340 f"Broken link {self}: the target value changed while updating " "the source." 

341 ) 

342 

343 def unlink(self) -> None: 

344 self.source[0].unobserve(self._update_target, names=self.source[1]) 

345 self.target[0].unobserve(self._update_source, names=self.target[1]) 

346 

347 

348class directional_link: 

349 """Link the trait of a source object with traits of target objects. 

350 

351 Parameters 

352 ---------- 

353 source : (object, attribute name) pair 

354 target : (object, attribute name) pair 

355 transform: callable (optional) 

356 Data transformation between source and target. 

357 

358 Examples 

359 -------- 

360 >>> class X(HasTraits): 

361 ... value = Int() 

362 

363 >>> src = X(value=1) 

364 >>> tgt = X(value=42) 

365 >>> c = directional_link((src, "value"), (tgt, "value")) 

366 

367 Setting source updates target objects: 

368 >>> src.value = 5 

369 >>> tgt.value 

370 5 

371 

372 Setting target does not update source object: 

373 >>> tgt.value = 6 

374 >>> src.value 

375 5 

376 

377 """ 

378 

379 updating = False 

380 

381 def __init__(self, source: t.Any, target: t.Any, transform: t.Any = None) -> None: 

382 self._transform = transform if transform else lambda x: x 

383 _validate_link(source, target) 

384 self.source, self.target = source, target 

385 self.link() 

386 

387 def link(self) -> None: 

388 try: 

389 setattr( 

390 self.target[0], 

391 self.target[1], 

392 self._transform(getattr(self.source[0], self.source[1])), 

393 ) 

394 finally: 

395 self.source[0].observe(self._update, names=self.source[1]) 

396 

397 @contextlib.contextmanager 

398 def _busy_updating(self) -> t.Any: 

399 self.updating = True 

400 try: 

401 yield 

402 finally: 

403 self.updating = False 

404 

405 def _update(self, change: t.Any) -> None: 

406 if self.updating: 

407 return 

408 with self._busy_updating(): 

409 setattr(self.target[0], self.target[1], self._transform(change.new)) 

410 

411 def unlink(self) -> None: 

412 self.source[0].unobserve(self._update, names=self.source[1]) 

413 

414 

415dlink = directional_link 

416 

417 

418# ----------------------------------------------------------------------------- 

419# Base Descriptor Class 

420# ----------------------------------------------------------------------------- 

421 

422 

423class BaseDescriptor: 

424 """Base descriptor class 

425 

426 Notes 

427 ----- 

428 This implements Python's descriptor protocol. 

429 

430 This class is the base class for all such descriptors. The 

431 only magic we use is a custom metaclass for the main :class:`HasTraits` 

432 class that does the following: 

433 

434 1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor` 

435 instance in the class dict to the name of the attribute. 

436 2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor` 

437 instance in the class dict to the *class* that declared the trait. 

438 This is used by the :class:`This` trait to allow subclasses to 

439 accept superclasses for :class:`This` values. 

440 """ 

441 

442 name: str | None = None 

443 this_class: type[HasTraits] | None = None 

444 

445 def class_init(self, cls: type[HasTraits], name: str | None) -> None: 

446 """Part of the initialization which may depend on the underlying 

447 HasDescriptors class. 

448 

449 It is typically overloaded for specific types. 

450 

451 This method is called by :meth:`MetaHasDescriptors.__init__` 

452 passing the class (`cls`) and `name` under which the descriptor 

453 has been assigned. 

454 """ 

455 self.this_class = cls 

456 self.name = name 

457 

458 def subclass_init(self, cls: type[HasTraits]) -> None: 

459 # Instead of HasDescriptors.setup_instance calling 

460 # every instance_init, we opt in by default. 

461 # This gives descriptors a change to opt out for 

462 # performance reasons. 

463 # Because most traits do not need instance_init, 

464 # and it will otherwise be called for every HasTrait instance 

465 # being created, this otherwise gives a significant performance 

466 # pentalty. Most TypeTraits in traitlets opt out. 

467 cls._instance_inits.append(self.instance_init) 

468 

469 def instance_init(self, obj: t.Any) -> None: 

470 """Part of the initialization which may depend on the underlying 

471 HasDescriptors instance. 

472 

473 It is typically overloaded for specific types. 

474 

475 This method is called by :meth:`HasTraits.__new__` and in the 

476 :meth:`BaseDescriptor.instance_init` method of descriptors holding 

477 other descriptors. 

478 """ 

479 

480 

481G = TypeVar("G") 

482S = TypeVar("S") 

483T = TypeVar("T") 

484 

485 

486# Self from typing extension doesn't work well with mypy https://github.com/python/mypy/pull/14041 

487# see https://peps.python.org/pep-0673/#use-in-generic-classes 

488# Self = t.TypeVar("Self", bound="TraitType[Any, Any]") 

489if t.TYPE_CHECKING: 

490 from typing_extensions import Literal, Self 

491 

492 K = TypeVar("K", default=str) 

493 V = TypeVar("V", default=t.Any) 

494 

495 

496# We use a type for the getter (G) and setter (G) because we allow 

497# for traits to cast (for instance CInt will use G=int, S=t.Any) 

498class TraitType(BaseDescriptor, t.Generic[G, S]): 

499 """A base class for all trait types.""" 

500 

501 metadata: dict[str, t.Any] = {} 

502 allow_none: bool = False 

503 read_only: bool = False 

504 info_text: str = "any value" 

505 default_value: t.Any = Undefined 

506 

507 def __init__( 

508 self: TraitType[G, S], 

509 default_value: t.Any = Undefined, 

510 allow_none: bool = False, 

511 read_only: bool | None = None, 

512 help: str | None = None, 

513 config: t.Any = None, 

514 **kwargs: t.Any, 

515 ) -> None: 

516 """Declare a traitlet. 

517 

518 If *allow_none* is True, None is a valid value in addition to any 

519 values that are normally valid. The default is up to the subclass. 

520 For most trait types, the default value for ``allow_none`` is False. 

521 

522 If *read_only* is True, attempts to directly modify a trait attribute raises a TraitError. 

523 

524 If *help* is a string, it documents the attribute's purpose. 

525 

526 Extra metadata can be associated with the traitlet using the .tag() convenience method 

527 or by using the traitlet instance's .metadata dictionary. 

528 """ 

529 if default_value is not Undefined: 

530 self.default_value = default_value 

531 if allow_none: 

532 self.allow_none = allow_none 

533 if read_only is not None: 

534 self.read_only = read_only 

535 self.help = help if help is not None else "" 

536 if self.help: 

537 # define __doc__ so that inspectors like autodoc find traits 

538 self.__doc__ = self.help 

539 

540 if len(kwargs) > 0: 

541 stacklevel = 1 

542 f = inspect.currentframe() 

543 # count supers to determine stacklevel for warning 

544 assert f is not None 

545 while f.f_code.co_name == "__init__": 

546 stacklevel += 1 

547 f = f.f_back 

548 assert f is not None 

549 mod = f.f_globals.get("__name__") or "" 

550 pkg = mod.split(".", 1)[0] 

551 key = ("metadata-tag", pkg, *sorted(kwargs)) 

552 if should_warn(key): 

553 warn( 

554 f"metadata {kwargs} was set from the constructor. " 

555 "With traitlets 4.1, metadata should be set using the .tag() method, " 

556 "e.g., Int().tag(key1='value1', key2='value2')", 

557 DeprecationWarning, 

558 stacklevel=stacklevel, 

559 ) 

560 if len(self.metadata) > 0: 

561 self.metadata = self.metadata.copy() 

562 self.metadata.update(kwargs) 

563 else: 

564 self.metadata = kwargs 

565 else: 

566 self.metadata = self.metadata.copy() 

567 if config is not None: 

568 self.metadata["config"] = config 

569 

570 # We add help to the metadata during a deprecation period so that 

571 # code that looks for the help string there can find it. 

572 if help is not None: 

573 self.metadata["help"] = help 

574 

575 def from_string(self, s: str) -> G | None: 

576 """Get a value from a config string 

577 

578 such as an environment variable or CLI arguments. 

579 

580 Traits can override this method to define their own 

581 parsing of config strings. 

582 

583 .. seealso:: item_from_string 

584 

585 .. versionadded:: 5.0 

586 """ 

587 if self.allow_none and s == "None": 

588 return None 

589 return s # type:ignore[return-value] 

590 

591 def default(self, obj: t.Any = None) -> G | None: 

592 """The default generator for this trait 

593 

594 Notes 

595 ----- 

596 This method is registered to HasTraits classes during ``class_init`` 

597 in the same way that dynamic defaults defined by ``@default`` are. 

598 """ 

599 if self.default_value is not Undefined: 

600 return t.cast(G, self.default_value) 

601 elif hasattr(self, "make_dynamic_default"): 

602 return t.cast(G, self.make_dynamic_default()) 

603 else: 

604 # Undefined will raise in TraitType.get 

605 return t.cast(G, self.default_value) 

606 

607 def get_default_value(self) -> G | None: 

608 """DEPRECATED: Retrieve the static default value for this trait. 

609 Use self.default_value instead 

610 """ 

611 warn( 

612 "get_default_value is deprecated in traitlets 4.0: use the .default_value attribute", 

613 DeprecationWarning, 

614 stacklevel=2, 

615 ) 

616 return t.cast(G, self.default_value) 

617 

618 def init_default_value(self, obj: t.Any) -> G | None: 

619 """DEPRECATED: Set the static default value for the trait type.""" 

620 warn( 

621 "init_default_value is deprecated in traitlets 4.0, and may be removed in the future", 

622 DeprecationWarning, 

623 stacklevel=2, 

624 ) 

625 value = self._validate(obj, self.default_value) 

626 obj._trait_values[self.name] = value 

627 return value 

628 

629 def get(self, obj: HasTraits, cls: type[t.Any] | None = None) -> G | None: 

630 assert self.name is not None 

631 try: 

632 value = obj._trait_values[self.name] 

633 except KeyError: 

634 # Check for a dynamic initializer. 

635 default = obj.trait_defaults(self.name) 

636 if default is Undefined: 

637 warn( 

638 "Explicit using of Undefined as the default value " 

639 "is deprecated in traitlets 5.0, and may cause " 

640 "exceptions in the future.", 

641 DeprecationWarning, 

642 stacklevel=2, 

643 ) 

644 # Using a context manager has a large runtime overhead, so we 

645 # write out the obj.cross_validation_lock call here. 

646 _cross_validation_lock = obj._cross_validation_lock 

647 try: 

648 obj._cross_validation_lock = True 

649 value = self._validate(obj, default) 

650 finally: 

651 obj._cross_validation_lock = _cross_validation_lock 

652 obj._trait_values[self.name] = value 

653 obj._notify_observers( 

654 Bunch( 

655 name=self.name, 

656 value=value, 

657 owner=obj, 

658 type="default", 

659 ) 

660 ) 

661 return t.cast(G, value) 

662 except Exception as e: 

663 # This should never be reached. 

664 raise TraitError("Unexpected error in TraitType: default value not set properly") from e 

665 else: 

666 return t.cast(G, value) 

667 

668 @t.overload 

669 def __get__(self, obj: None, cls: type[t.Any]) -> Self: 

670 ... 

671 

672 @t.overload 

673 def __get__(self, obj: t.Any, cls: type[t.Any]) -> G: 

674 ... 

675 

676 def __get__(self, obj: HasTraits | None, cls: type[t.Any]) -> Self | G: 

677 """Get the value of the trait by self.name for the instance. 

678 

679 Default values are instantiated when :meth:`HasTraits.__new__` 

680 is called. Thus by the time this method gets called either the 

681 default value or a user defined value (they called :meth:`__set__`) 

682 is in the :class:`HasTraits` instance. 

683 """ 

684 if obj is None: 

685 return self 

686 else: 

687 return t.cast(G, self.get(obj, cls)) # the G should encode the Optional 

688 

689 def set(self, obj: HasTraits, value: S) -> None: 

690 new_value = self._validate(obj, value) 

691 assert self.name is not None 

692 try: 

693 old_value = obj._trait_values[self.name] 

694 except KeyError: 

695 old_value = self.default_value 

696 

697 obj._trait_values[self.name] = new_value 

698 try: 

699 silent = bool(old_value == new_value) 

700 except Exception: 

701 # if there is an error in comparing, default to notify 

702 silent = False 

703 if silent is not True: 

704 # we explicitly compare silent to True just in case the equality 

705 # comparison above returns something other than True/False 

706 obj._notify_trait(self.name, old_value, new_value) 

707 

708 def __set__(self, obj: HasTraits, value: S) -> None: 

709 """Set the value of the trait by self.name for the instance. 

710 

711 Values pass through a validation stage where errors are raised when 

712 impropper types, or types that cannot be coerced, are encountered. 

713 """ 

714 if self.read_only: 

715 raise TraitError('The "%s" trait is read-only.' % self.name) 

716 self.set(obj, value) 

717 

718 def _validate(self, obj: t.Any, value: t.Any) -> G | None: 

719 if value is None and self.allow_none: 

720 return value 

721 if hasattr(self, "validate"): 

722 value = self.validate(obj, value) 

723 if obj._cross_validation_lock is False: 

724 value = self._cross_validate(obj, value) 

725 return t.cast(G, value) 

726 

727 def _cross_validate(self, obj: t.Any, value: t.Any) -> G | None: 

728 if self.name in obj._trait_validators: 

729 proposal = Bunch({"trait": self, "value": value, "owner": obj}) 

730 value = obj._trait_validators[self.name](obj, proposal) 

731 elif hasattr(obj, "_%s_validate" % self.name): 

732 meth_name = "_%s_validate" % self.name 

733 cross_validate = getattr(obj, meth_name) 

734 deprecated_method( 

735 cross_validate, 

736 obj.__class__, 

737 meth_name, 

738 "use @validate decorator instead.", 

739 ) 

740 value = cross_validate(value, self) 

741 return t.cast(G, value) 

742 

743 def __or__(self, other: TraitType[t.Any, t.Any]) -> Union: 

744 if isinstance(other, Union): 

745 return Union([self, *other.trait_types]) 

746 else: 

747 return Union([self, other]) 

748 

749 def info(self) -> str: 

750 return self.info_text 

751 

752 def error( 

753 self, 

754 obj: HasTraits | None, 

755 value: t.Any, 

756 error: Exception | None = None, 

757 info: str | None = None, 

758 ) -> t.NoReturn: 

759 """Raise a TraitError 

760 

761 Parameters 

762 ---------- 

763 obj : HasTraits or None 

764 The instance which owns the trait. If not 

765 object is given, then an object agnostic 

766 error will be raised. 

767 value : any 

768 The value that caused the error. 

769 error : Exception (default: None) 

770 An error that was raised by a child trait. 

771 The arguments of this exception should be 

772 of the form ``(value, info, *traits)``. 

773 Where the ``value`` and ``info`` are the 

774 problem value, and string describing the 

775 expected value. The ``traits`` are a series 

776 of :class:`TraitType` instances that are 

777 "children" of this one (the first being 

778 the deepest). 

779 info : str (default: None) 

780 A description of the expected value. By 

781 default this is inferred from this trait's 

782 ``info`` method. 

783 """ 

784 if error is not None: 

785 # handle nested error 

786 error.args += (self,) 

787 if self.name is not None: 

788 # this is the root trait that must format the final message 

789 chain = " of ".join(describe("a", t) for t in error.args[2:]) 

790 if obj is not None: 

791 error.args = ( 

792 "The '{}' trait of {} instance contains {} which " 

793 "expected {}, not {}.".format( 

794 self.name, 

795 describe("an", obj), 

796 chain, 

797 error.args[1], 

798 describe("the", error.args[0]), 

799 ), 

800 ) 

801 else: 

802 error.args = ( 

803 "The '{}' trait contains {} which " "expected {}, not {}.".format( 

804 self.name, 

805 chain, 

806 error.args[1], 

807 describe("the", error.args[0]), 

808 ), 

809 ) 

810 raise error 

811 

812 # this trait caused an error 

813 if self.name is None: 

814 # this is not the root trait 

815 raise TraitError(value, info or self.info(), self) 

816 

817 # this is the root trait 

818 if obj is not None: 

819 e = "The '{}' trait of {} instance expected {}, not {}.".format( 

820 self.name, 

821 class_of(obj), 

822 info or self.info(), 

823 describe("the", value), 

824 ) 

825 else: 

826 e = "The '{}' trait expected {}, not {}.".format( 

827 self.name, 

828 info or self.info(), 

829 describe("the", value), 

830 ) 

831 raise TraitError(e) 

832 

833 def get_metadata(self, key: str, default: t.Any = None) -> t.Any: 

834 """DEPRECATED: Get a metadata value. 

835 

836 Use .metadata[key] or .metadata.get(key, default) instead. 

837 """ 

838 if key == "help": 

839 msg = "use the instance .help string directly, like x.help" 

840 else: 

841 msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)" 

842 warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2) 

843 return self.metadata.get(key, default) 

844 

845 def set_metadata(self, key: str, value: t.Any) -> None: 

846 """DEPRECATED: Set a metadata key/value. 

847 

848 Use .metadata[key] = value instead. 

849 """ 

850 if key == "help": 

851 msg = "use the instance .help string directly, like x.help = value" 

852 else: 

853 msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value" 

854 warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2) 

855 self.metadata[key] = value 

856 

857 def tag(self, **metadata: t.Any) -> Self: 

858 """Sets metadata and returns self. 

859 

860 This allows convenient metadata tagging when initializing the trait, such as: 

861 

862 Examples 

863 -------- 

864 >>> Int(0).tag(config=True, sync=True) 

865 <traitlets.traitlets.Int object at ...> 

866 

867 """ 

868 maybe_constructor_keywords = set(metadata.keys()).intersection( 

869 {"help", "allow_none", "read_only", "default_value"} 

870 ) 

871 if maybe_constructor_keywords: 

872 warn( 

873 "The following attributes are set in using `tag`, but seem to be constructor keywords arguments: %s " 

874 % maybe_constructor_keywords, 

875 UserWarning, 

876 stacklevel=2, 

877 ) 

878 

879 self.metadata.update(metadata) 

880 return self 

881 

882 def default_value_repr(self) -> str: 

883 return repr(self.default_value) 

884 

885 

886# ----------------------------------------------------------------------------- 

887# The HasTraits implementation 

888# ----------------------------------------------------------------------------- 

889 

890 

891class _CallbackWrapper: 

892 """An object adapting a on_trait_change callback into an observe callback. 

893 

894 The comparison operator __eq__ is implemented to enable removal of wrapped 

895 callbacks. 

896 """ 

897 

898 def __init__(self, cb: t.Any) -> None: 

899 self.cb = cb 

900 # Bound methods have an additional 'self' argument. 

901 offset = -1 if isinstance(self.cb, types.MethodType) else 0 

902 self.nargs = len(getargspec(cb)[0]) + offset 

903 if self.nargs > 4: 

904 raise TraitError("a trait changed callback must have 0-4 arguments.") 

905 

906 def __eq__(self, other: object) -> bool: 

907 # The wrapper is equal to the wrapped element 

908 if isinstance(other, _CallbackWrapper): 

909 return bool(self.cb == other.cb) 

910 else: 

911 return bool(self.cb == other) 

912 

913 def __call__(self, change: Bunch) -> None: 

914 # The wrapper is callable 

915 if self.nargs == 0: 

916 self.cb() 

917 elif self.nargs == 1: 

918 self.cb(change.name) 

919 elif self.nargs == 2: 

920 self.cb(change.name, change.new) 

921 elif self.nargs == 3: 

922 self.cb(change.name, change.old, change.new) 

923 elif self.nargs == 4: 

924 self.cb(change.name, change.old, change.new, change.owner) 

925 

926 

927def _callback_wrapper(cb: t.Any) -> _CallbackWrapper: 

928 if isinstance(cb, _CallbackWrapper): 

929 return cb 

930 else: 

931 return _CallbackWrapper(cb) 

932 

933 

934class MetaHasDescriptors(type): 

935 """A metaclass for HasDescriptors. 

936 

937 This metaclass makes sure that any TraitType class attributes are 

938 instantiated and sets their name attribute. 

939 """ 

940 

941 def __new__( 

942 mcls: type[MetaHasDescriptors], 

943 name: str, 

944 bases: tuple[type, ...], 

945 classdict: dict[str, t.Any], 

946 **kwds: t.Any, 

947 ) -> MetaHasDescriptors: 

948 """Create the HasDescriptors class.""" 

949 for k, v in classdict.items(): 

950 # ---------------------------------------------------------------- 

951 # Support of deprecated behavior allowing for TraitType types 

952 # to be used instead of TraitType instances. 

953 if inspect.isclass(v) and issubclass(v, TraitType): 

954 warn( 

955 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)." 

956 " Passing types is deprecated in traitlets 4.1.", 

957 DeprecationWarning, 

958 stacklevel=2, 

959 ) 

960 classdict[k] = v() 

961 # ---------------------------------------------------------------- 

962 

963 return super().__new__(mcls, name, bases, classdict, **kwds) 

964 

965 def __init__( 

966 cls, name: str, bases: tuple[type, ...], classdict: dict[str, t.Any], **kwds: t.Any 

967 ) -> None: 

968 """Finish initializing the HasDescriptors class.""" 

969 super().__init__(name, bases, classdict, **kwds) 

970 cls.setup_class(classdict) 

971 

972 def setup_class(cls: MetaHasDescriptors, classdict: dict[str, t.Any]) -> None: 

973 """Setup descriptor instance on the class 

974 

975 This sets the :attr:`this_class` and :attr:`name` attributes of each 

976 BaseDescriptor in the class dict of the newly created ``cls`` before 

977 calling their :attr:`class_init` method. 

978 """ 

979 cls._descriptors = [] 

980 cls._instance_inits: list[t.Any] = [] 

981 for k, v in classdict.items(): 

982 if isinstance(v, BaseDescriptor): 

983 v.class_init(cls, k) # type:ignore[arg-type] 

984 

985 for _, v in getmembers(cls): 

986 if isinstance(v, BaseDescriptor): 

987 v.subclass_init(cls) # type:ignore[arg-type] 

988 cls._descriptors.append(v) 

989 

990 

991class MetaHasTraits(MetaHasDescriptors): 

992 """A metaclass for HasTraits.""" 

993 

994 def setup_class(cls: MetaHasTraits, classdict: dict[str, t.Any]) -> None: 

995 # for only the current class 

996 cls._trait_default_generators: dict[str, t.Any] = {} 

997 # also looking at base classes 

998 cls._all_trait_default_generators = {} 

999 cls._traits = {} 

1000 cls._static_immutable_initial_values = {} 

1001 

1002 super().setup_class(classdict) 

1003 

1004 mro = cls.mro() 

1005 

1006 for name in dir(cls): 

1007 # Some descriptors raise AttributeError like zope.interface's 

1008 # __provides__ attributes even though they exist. This causes 

1009 # AttributeErrors even though they are listed in dir(cls). 

1010 try: 

1011 value = getattr(cls, name) 

1012 except AttributeError: 

1013 continue 

1014 if isinstance(value, TraitType): 

1015 cls._traits[name] = value 

1016 trait = value 

1017 default_method_name = "_%s_default" % name 

1018 mro_trait = mro 

1019 try: 

1020 mro_trait = mro[: mro.index(trait.this_class) + 1] # type:ignore[arg-type] 

1021 except ValueError: 

1022 # this_class not in mro 

1023 pass 

1024 for c in mro_trait: 

1025 if default_method_name in c.__dict__: 

1026 cls._all_trait_default_generators[name] = c.__dict__[default_method_name] 

1027 break 

1028 if name in c.__dict__.get("_trait_default_generators", {}): 

1029 cls._all_trait_default_generators[name] = c._trait_default_generators[name] # type: ignore[attr-defined] 

1030 break 

1031 else: 

1032 # We don't have a dynamic default generator using @default etc. 

1033 # Now if the default value is not dynamic and immutable (string, number) 

1034 # and does not require any validation, we keep them in a dict 

1035 # of initial values to speed up instance creation. 

1036 # This is a very specific optimization, but a very common scenario in 

1037 # for instance ipywidgets. 

1038 none_ok = trait.default_value is None and trait.allow_none 

1039 if ( 

1040 type(trait) in [CInt, Int] 

1041 and trait.min is None # type: ignore[attr-defined] 

1042 and trait.max is None # type: ignore[attr-defined] 

1043 and (isinstance(trait.default_value, int) or none_ok) 

1044 ): 

1045 cls._static_immutable_initial_values[name] = trait.default_value 

1046 elif ( 

1047 type(trait) in [CFloat, Float] 

1048 and trait.min is None # type: ignore[attr-defined] 

1049 and trait.max is None # type: ignore[attr-defined] 

1050 and (isinstance(trait.default_value, float) or none_ok) 

1051 ): 

1052 cls._static_immutable_initial_values[name] = trait.default_value 

1053 elif type(trait) in [CBool, Bool] and ( 

1054 isinstance(trait.default_value, bool) or none_ok 

1055 ): 

1056 cls._static_immutable_initial_values[name] = trait.default_value 

1057 elif type(trait) in [CUnicode, Unicode] and ( 

1058 isinstance(trait.default_value, str) or none_ok 

1059 ): 

1060 cls._static_immutable_initial_values[name] = trait.default_value 

1061 elif type(trait) == Any and ( 

1062 isinstance(trait.default_value, (str, int, float, bool)) or none_ok 

1063 ): 

1064 cls._static_immutable_initial_values[name] = trait.default_value 

1065 elif type(trait) == Union and trait.default_value is None: 

1066 cls._static_immutable_initial_values[name] = None 

1067 elif ( 

1068 isinstance(trait, Instance) 

1069 and trait.default_args is None 

1070 and trait.default_kwargs is None 

1071 and trait.allow_none 

1072 ): 

1073 cls._static_immutable_initial_values[name] = None 

1074 

1075 # we always add it, because a class may change when we call add_trait 

1076 # and then the instance may not have all the _static_immutable_initial_values 

1077 cls._all_trait_default_generators[name] = trait.default 

1078 

1079 

1080def observe(*names: Sentinel | str, type: str = "change") -> ObserveHandler: 

1081 """A decorator which can be used to observe Traits on a class. 

1082 

1083 The handler passed to the decorator will be called with one ``change`` 

1084 dict argument. The change dictionary at least holds a 'type' key and a 

1085 'name' key, corresponding respectively to the type of notification and the 

1086 name of the attribute that triggered the notification. 

1087 

1088 Other keys may be passed depending on the value of 'type'. In the case 

1089 where type is 'change', we also have the following keys: 

1090 * ``owner`` : the HasTraits instance 

1091 * ``old`` : the old value of the modified trait attribute 

1092 * ``new`` : the new value of the modified trait attribute 

1093 * ``name`` : the name of the modified trait attribute. 

1094 

1095 Parameters 

1096 ---------- 

1097 *names 

1098 The str names of the Traits to observe on the object. 

1099 type : str, kwarg-only 

1100 The type of event to observe (e.g. 'change') 

1101 """ 

1102 if not names: 

1103 raise TypeError("Please specify at least one trait name to observe.") 

1104 for name in names: 

1105 if name is not All and not isinstance(name, str): 

1106 raise TypeError("trait names to observe must be strings or All, not %r" % name) 

1107 return ObserveHandler(names, type=type) 

1108 

1109 

1110def observe_compat(func: FuncT) -> FuncT: 

1111 """Backward-compatibility shim decorator for observers 

1112 

1113 Use with: 

1114 

1115 @observe('name') 

1116 @observe_compat 

1117 def _foo_changed(self, change): 

1118 ... 

1119 

1120 With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work. 

1121 Allows adoption of new observer API without breaking subclasses that override and super. 

1122 """ 

1123 

1124 def compatible_observer( 

1125 self: t.Any, change_or_name: str, old: t.Any = Undefined, new: t.Any = Undefined 

1126 ) -> t.Any: 

1127 if isinstance(change_or_name, dict): # type:ignore[unreachable] 

1128 change = Bunch(change_or_name) # type:ignore[unreachable] 

1129 else: 

1130 clsname = self.__class__.__name__ 

1131 warn( 

1132 f"A parent of {clsname}._{change_or_name}_changed has adopted the new (traitlets 4.1) @observe(change) API", 

1133 DeprecationWarning, 

1134 stacklevel=2, 

1135 ) 

1136 change = Bunch( 

1137 type="change", 

1138 old=old, 

1139 new=new, 

1140 name=change_or_name, 

1141 owner=self, 

1142 ) 

1143 return func(self, change) 

1144 

1145 return t.cast(FuncT, compatible_observer) 

1146 

1147 

1148def validate(*names: Sentinel | str) -> ValidateHandler: 

1149 """A decorator to register cross validator of HasTraits object's state 

1150 when a Trait is set. 

1151 

1152 The handler passed to the decorator must have one ``proposal`` dict argument. 

1153 The proposal dictionary must hold the following keys: 

1154 

1155 * ``owner`` : the HasTraits instance 

1156 * ``value`` : the proposed value for the modified trait attribute 

1157 * ``trait`` : the TraitType instance associated with the attribute 

1158 

1159 Parameters 

1160 ---------- 

1161 *names 

1162 The str names of the Traits to validate. 

1163 

1164 Notes 

1165 ----- 

1166 Since the owner has access to the ``HasTraits`` instance via the 'owner' key, 

1167 the registered cross validator could potentially make changes to attributes 

1168 of the ``HasTraits`` instance. However, we recommend not to do so. The reason 

1169 is that the cross-validation of attributes may run in arbitrary order when 

1170 exiting the ``hold_trait_notifications`` context, and such changes may not 

1171 commute. 

1172 """ 

1173 if not names: 

1174 raise TypeError("Please specify at least one trait name to validate.") 

1175 for name in names: 

1176 if name is not All and not isinstance(name, str): 

1177 raise TypeError("trait names to validate must be strings or All, not %r" % name) 

1178 return ValidateHandler(names) 

1179 

1180 

1181def default(name: str) -> DefaultHandler: 

1182 """A decorator which assigns a dynamic default for a Trait on a HasTraits object. 

1183 

1184 Parameters 

1185 ---------- 

1186 name 

1187 The str name of the Trait on the object whose default should be generated. 

1188 

1189 Notes 

1190 ----- 

1191 Unlike observers and validators which are properties of the HasTraits 

1192 instance, default value generators are class-level properties. 

1193 

1194 Besides, default generators are only invoked if they are registered in 

1195 subclasses of `this_type`. 

1196 

1197 :: 

1198 

1199 class A(HasTraits): 

1200 bar = Int() 

1201 

1202 @default('bar') 

1203 def get_bar_default(self): 

1204 return 11 

1205 

1206 class B(A): 

1207 bar = Float() # This trait ignores the default generator defined in 

1208 # the base class A 

1209 

1210 class C(B): 

1211 

1212 @default('bar') 

1213 def some_other_default(self): # This default generator should not be 

1214 return 3.0 # ignored since it is defined in a 

1215 # class derived from B.a.this_class. 

1216 """ 

1217 if not isinstance(name, str): 

1218 raise TypeError("Trait name must be a string or All, not %r" % name) 

1219 return DefaultHandler(name) 

1220 

1221 

1222FuncT = t.TypeVar("FuncT", bound=t.Callable[..., t.Any]) 

1223 

1224 

1225class EventHandler(BaseDescriptor): 

1226 def _init_call(self, func: FuncT) -> EventHandler: 

1227 self.func = func 

1228 return self 

1229 

1230 @t.overload 

1231 def __call__(self, func: FuncT, *args: t.Any, **kwargs: t.Any) -> FuncT: 

1232 ... 

1233 

1234 @t.overload 

1235 def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: 

1236 ... 

1237 

1238 def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: 

1239 """Pass `*args` and `**kwargs` to the handler's function if it exists.""" 

1240 if hasattr(self, "func"): 

1241 return self.func(*args, **kwargs) 

1242 else: 

1243 return self._init_call(*args, **kwargs) 

1244 

1245 def __get__(self, inst: t.Any, cls: t.Any = None) -> types.MethodType | EventHandler: 

1246 if inst is None: 

1247 return self 

1248 return types.MethodType(self.func, inst) 

1249 

1250 

1251class ObserveHandler(EventHandler): 

1252 def __init__(self, names: tuple[Sentinel | str, ...], type: str = "") -> None: 

1253 self.trait_names = names 

1254 self.type = type 

1255 

1256 def instance_init(self, inst: HasTraits) -> None: 

1257 inst.observe(self, self.trait_names, type=self.type) 

1258 

1259 

1260class ValidateHandler(EventHandler): 

1261 def __init__(self, names: tuple[Sentinel | str, ...]) -> None: 

1262 self.trait_names = names 

1263 

1264 def instance_init(self, inst: HasTraits) -> None: 

1265 inst._register_validator(self, self.trait_names) 

1266 

1267 

1268class DefaultHandler(EventHandler): 

1269 def __init__(self, name: str) -> None: 

1270 self.trait_name = name 

1271 

1272 def class_init(self, cls: type[HasTraits], name: str | None) -> None: 

1273 super().class_init(cls, name) 

1274 cls._trait_default_generators[self.trait_name] = self 

1275 

1276 

1277class HasDescriptors(metaclass=MetaHasDescriptors): 

1278 """The base class for all classes that have descriptors.""" 

1279 

1280 def __new__(*args: t.Any, **kwargs: t.Any) -> t.Any: 

1281 # Pass cls as args[0] to allow "cls" as keyword argument 

1282 cls = args[0] 

1283 args = args[1:] 

1284 

1285 # This is needed because object.__new__ only accepts 

1286 # the cls argument. 

1287 new_meth = super(HasDescriptors, cls).__new__ 

1288 if new_meth is object.__new__: 

1289 inst = new_meth(cls) 

1290 else: 

1291 inst = new_meth(cls, *args, **kwargs) 

1292 inst.setup_instance(*args, **kwargs) 

1293 return inst 

1294 

1295 def setup_instance(*args: t.Any, **kwargs: t.Any) -> None: 

1296 """ 

1297 This is called **before** self.__init__ is called. 

1298 """ 

1299 # Pass self as args[0] to allow "self" as keyword argument 

1300 self = args[0] 

1301 args = args[1:] 

1302 

1303 self._cross_validation_lock = False 

1304 cls = self.__class__ 

1305 # Let descriptors performance initialization when a HasDescriptor 

1306 # instance is created. This allows registration of observers and 

1307 # default creations or other bookkeepings. 

1308 # Note that descriptors can opt-out of this behavior by overriding 

1309 # subclass_init. 

1310 for init in cls._instance_inits: 

1311 init(self) 

1312 

1313 

1314class HasTraits(HasDescriptors, metaclass=MetaHasTraits): 

1315 _trait_values: dict[str, t.Any] 

1316 _static_immutable_initial_values: dict[str, t.Any] 

1317 _trait_notifiers: dict[str | Sentinel, t.Any] 

1318 _trait_validators: dict[str | Sentinel, t.Any] 

1319 _cross_validation_lock: bool 

1320 _traits: dict[str, t.Any] 

1321 _all_trait_default_generators: dict[str, t.Any] 

1322 

1323 def setup_instance(*args: t.Any, **kwargs: t.Any) -> None: 

1324 # Pass self as args[0] to allow "self" as keyword argument 

1325 self = args[0] 

1326 args = args[1:] 

1327 

1328 # although we'd prefer to set only the initial values not present 

1329 # in kwargs, we will overwrite them in `__init__`, and simply making 

1330 # a copy of a dict is faster than checking for each key. 

1331 self._trait_values = self._static_immutable_initial_values.copy() 

1332 self._trait_notifiers = {} 

1333 self._trait_validators = {} 

1334 self._cross_validation_lock = False 

1335 super(HasTraits, self).setup_instance(*args, **kwargs) 

1336 

1337 def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: 

1338 # Allow trait values to be set using keyword arguments. 

1339 # We need to use setattr for this to trigger validation and 

1340 # notifications. 

1341 super_args = args 

1342 super_kwargs = {} 

1343 

1344 if kwargs: 

1345 # this is a simplified (and faster) version of 

1346 # the hold_trait_notifications(self) context manager 

1347 def ignore(change: Bunch) -> None: 

1348 pass 

1349 

1350 self.notify_change = ignore # type:ignore[method-assign] 

1351 self._cross_validation_lock = True 

1352 changes = {} 

1353 for key, value in kwargs.items(): 

1354 if self.has_trait(key): 

1355 setattr(self, key, value) 

1356 changes[key] = Bunch( 

1357 name=key, 

1358 old=None, 

1359 new=value, 

1360 owner=self, 

1361 type="change", 

1362 ) 

1363 else: 

1364 # passthrough args that don't set traits to super 

1365 super_kwargs[key] = value 

1366 # notify and cross validate all trait changes that were set in kwargs 

1367 changed = set(kwargs) & set(self._traits) 

1368 for key in changed: 

1369 value = self._traits[key]._cross_validate(self, getattr(self, key)) 

1370 self.set_trait(key, value) 

1371 changes[key]["new"] = value 

1372 self._cross_validation_lock = False 

1373 # Restore method retrieval from class 

1374 del self.notify_change 

1375 for key in changed: 

1376 self.notify_change(changes[key]) 

1377 

1378 try: 

1379 super().__init__(*super_args, **super_kwargs) 

1380 except TypeError as e: 

1381 arg_s_list = [repr(arg) for arg in super_args] 

1382 for k, v in super_kwargs.items(): 

1383 arg_s_list.append(f"{k}={v!r}") 

1384 arg_s = ", ".join(arg_s_list) 

1385 warn( 

1386 "Passing unrecognized arguments to super({classname}).__init__({arg_s}).\n" 

1387 "{error}\n" 

1388 "This is deprecated in traitlets 4.2." 

1389 "This error will be raised in a future release of traitlets.".format( 

1390 arg_s=arg_s, 

1391 classname=self.__class__.__name__, 

1392 error=e, 

1393 ), 

1394 DeprecationWarning, 

1395 stacklevel=2, 

1396 ) 

1397 

1398 def __getstate__(self) -> dict[str, t.Any]: 

1399 d = self.__dict__.copy() 

1400 # event handlers stored on an instance are 

1401 # expected to be reinstantiated during a 

1402 # recall of instance_init during __setstate__ 

1403 d["_trait_notifiers"] = {} 

1404 d["_trait_validators"] = {} 

1405 d["_trait_values"] = self._trait_values.copy() 

1406 d["_cross_validation_lock"] = False # FIXME: raise if cloning locked! 

1407 

1408 return d 

1409 

1410 def __setstate__(self, state: dict[str, t.Any]) -> None: 

1411 self.__dict__ = state.copy() 

1412 

1413 # event handlers are reassigned to self 

1414 cls = self.__class__ 

1415 for key in dir(cls): 

1416 # Some descriptors raise AttributeError like zope.interface's 

1417 # __provides__ attributes even though they exist. This causes 

1418 # AttributeErrors even though they are listed in dir(cls). 

1419 try: 

1420 value = getattr(cls, key) 

1421 except AttributeError: 

1422 pass 

1423 else: 

1424 if isinstance(value, EventHandler): 

1425 value.instance_init(self) 

1426 

1427 @property 

1428 @contextlib.contextmanager 

1429 def cross_validation_lock(self) -> t.Any: 

1430 """ 

1431 A contextmanager for running a block with our cross validation lock set 

1432 to True. 

1433 

1434 At the end of the block, the lock's value is restored to its value 

1435 prior to entering the block. 

1436 """ 

1437 if self._cross_validation_lock: 

1438 yield 

1439 return 

1440 else: 

1441 try: 

1442 self._cross_validation_lock = True 

1443 yield 

1444 finally: 

1445 self._cross_validation_lock = False 

1446 

1447 @contextlib.contextmanager 

1448 def hold_trait_notifications(self) -> t.Any: 

1449 """Context manager for bundling trait change notifications and cross 

1450 validation. 

1451 

1452 Use this when doing multiple trait assignments (init, config), to avoid 

1453 race conditions in trait notifiers requesting other trait values. 

1454 All trait notifications will fire after all values have been assigned. 

1455 """ 

1456 if self._cross_validation_lock: 

1457 yield 

1458 return 

1459 else: 

1460 cache: dict[str, list[Bunch]] = {} 

1461 

1462 def compress(past_changes: list[Bunch] | None, change: Bunch) -> list[Bunch]: 

1463 """Merges the provided change with the last if possible.""" 

1464 if past_changes is None: 

1465 return [change] 

1466 else: 

1467 if past_changes[-1]["type"] == "change" and change.type == "change": 

1468 past_changes[-1]["new"] = change.new 

1469 else: 

1470 # In case of changes other than 'change', append the notification. 

1471 past_changes.append(change) 

1472 return past_changes 

1473 

1474 def hold(change: Bunch) -> None: 

1475 name = change.name 

1476 cache[name] = compress(cache.get(name), change) 

1477 

1478 try: 

1479 # Replace notify_change with `hold`, caching and compressing 

1480 # notifications, disable cross validation and yield. 

1481 self.notify_change = hold # type:ignore[method-assign] 

1482 self._cross_validation_lock = True 

1483 yield 

1484 # Cross validate final values when context is released. 

1485 for name in list(cache.keys()): 

1486 trait = getattr(self.__class__, name) 

1487 value = trait._cross_validate(self, getattr(self, name)) 

1488 self.set_trait(name, value) 

1489 except TraitError as e: 

1490 # Roll back in case of TraitError during final cross validation. 

1491 self.notify_change = lambda x: None # type:ignore[method-assign, assignment] # noqa: ARG005 

1492 for name, changes in cache.items(): 

1493 for change in changes[::-1]: 

1494 # TODO: Separate in a rollback function per notification type. 

1495 if change.type == "change": 

1496 if change.old is not Undefined: 

1497 self.set_trait(name, change.old) 

1498 else: 

1499 self._trait_values.pop(name) 

1500 cache = {} 

1501 raise e 

1502 finally: 

1503 self._cross_validation_lock = False 

1504 # Restore method retrieval from class 

1505 del self.notify_change 

1506 

1507 # trigger delayed notifications 

1508 for changes in cache.values(): 

1509 for change in changes: 

1510 self.notify_change(change) 

1511 

1512 def _notify_trait(self, name: str, old_value: t.Any, new_value: t.Any) -> None: 

1513 self.notify_change( 

1514 Bunch( 

1515 name=name, 

1516 old=old_value, 

1517 new=new_value, 

1518 owner=self, 

1519 type="change", 

1520 ) 

1521 ) 

1522 

1523 def notify_change(self, change: Bunch) -> None: 

1524 """Notify observers of a change event""" 

1525 return self._notify_observers(change) 

1526 

1527 def _notify_observers(self, event: Bunch) -> None: 

1528 """Notify observers of any event""" 

1529 if not isinstance(event, Bunch): 

1530 # cast to bunch if given a dict 

1531 event = Bunch(event) # type:ignore[unreachable] 

1532 name, type = event["name"], event["type"] 

1533 

1534 callables = [] 

1535 if name in self._trait_notifiers: 

1536 callables.extend(self._trait_notifiers.get(name, {}).get(type, [])) 

1537 callables.extend(self._trait_notifiers.get(name, {}).get(All, [])) 

1538 if All in self._trait_notifiers: 

1539 callables.extend(self._trait_notifiers.get(All, {}).get(type, [])) 

1540 callables.extend(self._trait_notifiers.get(All, {}).get(All, [])) 

1541 

1542 # Now static ones 

1543 magic_name = "_%s_changed" % name 

1544 if event["type"] == "change" and hasattr(self, magic_name): 

1545 class_value = getattr(self.__class__, magic_name) 

1546 if not isinstance(class_value, ObserveHandler): 

1547 deprecated_method( 

1548 class_value, 

1549 self.__class__, 

1550 magic_name, 

1551 "use @observe and @unobserve instead.", 

1552 ) 

1553 cb = getattr(self, magic_name) 

1554 # Only append the magic method if it was not manually registered 

1555 if cb not in callables: 

1556 callables.append(_callback_wrapper(cb)) 

1557 

1558 # Call them all now 

1559 # Traits catches and logs errors here. I allow them to raise 

1560 for c in callables: 

1561 # Bound methods have an additional 'self' argument. 

1562 

1563 if isinstance(c, _CallbackWrapper): 

1564 c = c.__call__ 

1565 elif isinstance(c, EventHandler) and c.name is not None: 

1566 c = getattr(self, c.name) 

1567 

1568 c(event) 

1569 

1570 def _add_notifiers( 

1571 self, handler: t.Callable[..., t.Any], name: Sentinel | str, type: str | Sentinel 

1572 ) -> None: 

1573 if name not in self._trait_notifiers: 

1574 nlist: list[t.Any] = [] 

1575 self._trait_notifiers[name] = {type: nlist} 

1576 else: 

1577 if type not in self._trait_notifiers[name]: 

1578 nlist = [] 

1579 self._trait_notifiers[name][type] = nlist 

1580 else: 

1581 nlist = self._trait_notifiers[name][type] 

1582 if handler not in nlist: 

1583 nlist.append(handler) 

1584 

1585 def _remove_notifiers( 

1586 self, handler: t.Callable[..., t.Any] | None, name: Sentinel | str, type: str | Sentinel 

1587 ) -> None: 

1588 try: 

1589 if handler is None: 

1590 del self._trait_notifiers[name][type] 

1591 else: 

1592 self._trait_notifiers[name][type].remove(handler) 

1593 except KeyError: 

1594 pass 

1595 

1596 def on_trait_change( 

1597 self, 

1598 handler: EventHandler | None = None, 

1599 name: Sentinel | str | None = None, 

1600 remove: bool = False, 

1601 ) -> None: 

1602 """DEPRECATED: Setup a handler to be called when a trait changes. 

1603 

1604 This is used to setup dynamic notifications of trait changes. 

1605 

1606 Static handlers can be created by creating methods on a HasTraits 

1607 subclass with the naming convention '_[traitname]_changed'. Thus, 

1608 to create static handler for the trait 'a', create the method 

1609 _a_changed(self, name, old, new) (fewer arguments can be used, see 

1610 below). 

1611 

1612 If `remove` is True and `handler` is not specified, all change 

1613 handlers for the specified name are uninstalled. 

1614 

1615 Parameters 

1616 ---------- 

1617 handler : callable, None 

1618 A callable that is called when a trait changes. Its 

1619 signature can be handler(), handler(name), handler(name, new), 

1620 handler(name, old, new), or handler(name, old, new, self). 

1621 name : list, str, None 

1622 If None, the handler will apply to all traits. If a list 

1623 of str, handler will apply to all names in the list. If a 

1624 str, the handler will apply just to that name. 

1625 remove : bool 

1626 If False (the default), then install the handler. If True 

1627 then unintall it. 

1628 """ 

1629 warn( 

1630 "on_trait_change is deprecated in traitlets 4.1: use observe instead", 

1631 DeprecationWarning, 

1632 stacklevel=2, 

1633 ) 

1634 if name is None: 

1635 name = All 

1636 if remove: 

1637 self.unobserve(_callback_wrapper(handler), names=name) 

1638 else: 

1639 self.observe(_callback_wrapper(handler), names=name) 

1640 

1641 def observe( 

1642 self, 

1643 handler: t.Callable[..., t.Any], 

1644 names: Sentinel | str | t.Iterable[Sentinel | str] = All, 

1645 type: Sentinel | str = "change", 

1646 ) -> None: 

1647 """Setup a handler to be called when a trait changes. 

1648 

1649 This is used to setup dynamic notifications of trait changes. 

1650 

1651 Parameters 

1652 ---------- 

1653 handler : callable 

1654 A callable that is called when a trait changes. Its 

1655 signature should be ``handler(change)``, where ``change`` is a 

1656 dictionary. The change dictionary at least holds a 'type' key. 

1657 * ``type``: the type of notification. 

1658 Other keys may be passed depending on the value of 'type'. In the 

1659 case where type is 'change', we also have the following keys: 

1660 * ``owner`` : the HasTraits instance 

1661 * ``old`` : the old value of the modified trait attribute 

1662 * ``new`` : the new value of the modified trait attribute 

1663 * ``name`` : the name of the modified trait attribute. 

1664 names : list, str, All 

1665 If names is All, the handler will apply to all traits. If a list 

1666 of str, handler will apply to all names in the list. If a 

1667 str, the handler will apply just to that name. 

1668 type : str, All (default: 'change') 

1669 The type of notification to filter by. If equal to All, then all 

1670 notifications are passed to the observe handler. 

1671 """ 

1672 for name in parse_notifier_name(names): 

1673 self._add_notifiers(handler, name, type) 

1674 

1675 def unobserve( 

1676 self, 

1677 handler: t.Callable[..., t.Any], 

1678 names: Sentinel | str | t.Iterable[Sentinel | str] = All, 

1679 type: Sentinel | str = "change", 

1680 ) -> None: 

1681 """Remove a trait change handler. 

1682 

1683 This is used to unregister handlers to trait change notifications. 

1684 

1685 Parameters 

1686 ---------- 

1687 handler : callable 

1688 The callable called when a trait attribute changes. 

1689 names : list, str, All (default: All) 

1690 The names of the traits for which the specified handler should be 

1691 uninstalled. If names is All, the specified handler is uninstalled 

1692 from the list of notifiers corresponding to all changes. 

1693 type : str or All (default: 'change') 

1694 The type of notification to filter by. If All, the specified handler 

1695 is uninstalled from the list of notifiers corresponding to all types. 

1696 """ 

1697 for name in parse_notifier_name(names): 

1698 self._remove_notifiers(handler, name, type) 

1699 

1700 def unobserve_all(self, name: str | t.Any = All) -> None: 

1701 """Remove trait change handlers of any type for the specified name. 

1702 If name is not specified, removes all trait notifiers.""" 

1703 if name is All: 

1704 self._trait_notifiers = {} 

1705 else: 

1706 try: 

1707 del self._trait_notifiers[name] 

1708 except KeyError: 

1709 pass 

1710 

1711 def _register_validator( 

1712 self, handler: t.Callable[..., None], names: tuple[str | Sentinel, ...] 

1713 ) -> None: 

1714 """Setup a handler to be called when a trait should be cross validated. 

1715 

1716 This is used to setup dynamic notifications for cross-validation. 

1717 

1718 If a validator is already registered for any of the provided names, a 

1719 TraitError is raised and no new validator is registered. 

1720 

1721 Parameters 

1722 ---------- 

1723 handler : callable 

1724 A callable that is called when the given trait is cross-validated. 

1725 Its signature is handler(proposal), where proposal is a Bunch (dictionary with attribute access) 

1726 with the following attributes/keys: 

1727 * ``owner`` : the HasTraits instance 

1728 * ``value`` : the proposed value for the modified trait attribute 

1729 * ``trait`` : the TraitType instance associated with the attribute 

1730 names : List of strings 

1731 The names of the traits that should be cross-validated 

1732 """ 

1733 for name in names: 

1734 magic_name = "_%s_validate" % name 

1735 if hasattr(self, magic_name): 

1736 class_value = getattr(self.__class__, magic_name) 

1737 if not isinstance(class_value, ValidateHandler): 

1738 deprecated_method( 

1739 class_value, 

1740 self.__class__, 

1741 magic_name, 

1742 "use @validate decorator instead.", 

1743 ) 

1744 for name in names: 

1745 self._trait_validators[name] = handler 

1746 

1747 def add_traits(self, **traits: t.Any) -> None: 

1748 """Dynamically add trait attributes to the HasTraits instance.""" 

1749 cls = self.__class__ 

1750 attrs = {"__module__": cls.__module__} 

1751 if hasattr(cls, "__qualname__"): 

1752 # __qualname__ introduced in Python 3.3 (see PEP 3155) 

1753 attrs["__qualname__"] = cls.__qualname__ 

1754 attrs.update(traits) 

1755 self.__class__ = type(cls.__name__, (cls,), attrs) 

1756 for trait in traits.values(): 

1757 trait.instance_init(self) 

1758 

1759 def set_trait(self, name: str, value: t.Any) -> None: 

1760 """Forcibly sets trait attribute, including read-only attributes.""" 

1761 cls = self.__class__ 

1762 if not self.has_trait(name): 

1763 raise TraitError(f"Class {cls.__name__} does not have a trait named {name}") 

1764 getattr(cls, name).set(self, value) 

1765 

1766 @classmethod 

1767 def class_trait_names(cls: type[HasTraits], **metadata: t.Any) -> list[str]: 

1768 """Get a list of all the names of this class' traits. 

1769 

1770 This method is just like the :meth:`trait_names` method, 

1771 but is unbound. 

1772 """ 

1773 return list(cls.class_traits(**metadata)) 

1774 

1775 @classmethod 

1776 def class_traits(cls: type[HasTraits], **metadata: t.Any) -> dict[str, TraitType[t.Any, t.Any]]: 

1777 """Get a ``dict`` of all the traits of this class. The dictionary 

1778 is keyed on the name and the values are the TraitType objects. 

1779 

1780 This method is just like the :meth:`traits` method, but is unbound. 

1781 

1782 The TraitTypes returned don't know anything about the values 

1783 that the various HasTrait's instances are holding. 

1784 

1785 The metadata kwargs allow functions to be passed in which 

1786 filter traits based on metadata values. The functions should 

1787 take a single value as an argument and return a boolean. If 

1788 any function returns False, then the trait is not included in 

1789 the output. If a metadata key doesn't exist, None will be passed 

1790 to the function. 

1791 """ 

1792 traits = cls._traits.copy() 

1793 

1794 if len(metadata) == 0: 

1795 return traits 

1796 

1797 result = {} 

1798 for name, trait in traits.items(): 

1799 for meta_name, meta_eval in metadata.items(): 

1800 if not callable(meta_eval): 

1801 meta_eval = _SimpleTest(meta_eval) 

1802 if not meta_eval(trait.metadata.get(meta_name, None)): 

1803 break 

1804 else: 

1805 result[name] = trait 

1806 

1807 return result 

1808 

1809 @classmethod 

1810 def class_own_traits( 

1811 cls: type[HasTraits], **metadata: t.Any 

1812 ) -> dict[str, TraitType[t.Any, t.Any]]: 

1813 """Get a dict of all the traitlets defined on this class, not a parent. 

1814 

1815 Works like `class_traits`, except for excluding traits from parents. 

1816 """ 

1817 sup = super(cls, cls) 

1818 return { 

1819 n: t 

1820 for (n, t) in cls.class_traits(**metadata).items() 

1821 if getattr(sup, n, None) is not t 

1822 } 

1823 

1824 def has_trait(self, name: str) -> bool: 

1825 """Returns True if the object has a trait with the specified name.""" 

1826 return name in self._traits 

1827 

1828 def trait_has_value(self, name: str) -> bool: 

1829 """Returns True if the specified trait has a value. 

1830 

1831 This will return false even if ``getattr`` would return a 

1832 dynamically generated default value. These default values 

1833 will be recognized as existing only after they have been 

1834 generated. 

1835 

1836 Example 

1837 

1838 .. code-block:: python 

1839 

1840 class MyClass(HasTraits): 

1841 i = Int() 

1842 

1843 

1844 mc = MyClass() 

1845 assert not mc.trait_has_value("i") 

1846 mc.i # generates a default value 

1847 assert mc.trait_has_value("i") 

1848 """ 

1849 return name in self._trait_values 

1850 

1851 def trait_values(self, **metadata: t.Any) -> dict[str, t.Any]: 

1852 """A ``dict`` of trait names and their values. 

1853 

1854 The metadata kwargs allow functions to be passed in which 

1855 filter traits based on metadata values. The functions should 

1856 take a single value as an argument and return a boolean. If 

1857 any function returns False, then the trait is not included in 

1858 the output. If a metadata key doesn't exist, None will be passed 

1859 to the function. 

1860 

1861 Returns 

1862 ------- 

1863 A ``dict`` of trait names and their values. 

1864 

1865 Notes 

1866 ----- 

1867 Trait values are retrieved via ``getattr``, any exceptions raised 

1868 by traits or the operations they may trigger will result in the 

1869 absence of a trait value in the result ``dict``. 

1870 """ 

1871 return {name: getattr(self, name) for name in self.trait_names(**metadata)} 

1872 

1873 def _get_trait_default_generator(self, name: str) -> t.Any: 

1874 """Return default generator for a given trait 

1875 

1876 Walk the MRO to resolve the correct default generator according to inheritance. 

1877 """ 

1878 method_name = "_%s_default" % name 

1879 if method_name in self.__dict__: 

1880 return getattr(self, method_name) 

1881 if method_name in self.__class__.__dict__: 

1882 return getattr(self.__class__, method_name) 

1883 return self._all_trait_default_generators[name] 

1884 

1885 def trait_defaults(self, *names: str, **metadata: t.Any) -> dict[str, t.Any] | Sentinel: 

1886 """Return a trait's default value or a dictionary of them 

1887 

1888 Notes 

1889 ----- 

1890 Dynamically generated default values may 

1891 depend on the current state of the object.""" 

1892 for n in names: 

1893 if not self.has_trait(n): 

1894 raise TraitError(f"'{n}' is not a trait of '{type(self).__name__}' instances") 

1895 

1896 if len(names) == 1 and len(metadata) == 0: 

1897 return t.cast(Sentinel, self._get_trait_default_generator(names[0])(self)) 

1898 

1899 trait_names = self.trait_names(**metadata) 

1900 trait_names.extend(names) 

1901 

1902 defaults = {} 

1903 for n in trait_names: 

1904 defaults[n] = self._get_trait_default_generator(n)(self) 

1905 return defaults 

1906 

1907 def trait_names(self, **metadata: t.Any) -> list[str]: 

1908 """Get a list of all the names of this class' traits.""" 

1909 return list(self.traits(**metadata)) 

1910 

1911 def traits(self, **metadata: t.Any) -> dict[str, TraitType[t.Any, t.Any]]: 

1912 """Get a ``dict`` of all the traits of this class. The dictionary 

1913 is keyed on the name and the values are the TraitType objects. 

1914 

1915 The TraitTypes returned don't know anything about the values 

1916 that the various HasTrait's instances are holding. 

1917 

1918 The metadata kwargs allow functions to be passed in which 

1919 filter traits based on metadata values. The functions should 

1920 take a single value as an argument and return a boolean. If 

1921 any function returns False, then the trait is not included in 

1922 the output. If a metadata key doesn't exist, None will be passed 

1923 to the function. 

1924 """ 

1925 traits = self._traits.copy() 

1926 

1927 if len(metadata) == 0: 

1928 return traits 

1929 

1930 result = {} 

1931 for name, trait in traits.items(): 

1932 for meta_name, meta_eval in metadata.items(): 

1933 if not callable(meta_eval): 

1934 meta_eval = _SimpleTest(meta_eval) 

1935 if not meta_eval(trait.metadata.get(meta_name, None)): 

1936 break 

1937 else: 

1938 result[name] = trait 

1939 

1940 return result 

1941 

1942 def trait_metadata(self, traitname: str, key: str, default: t.Any = None) -> t.Any: 

1943 """Get metadata values for trait by key.""" 

1944 try: 

1945 trait = getattr(self.__class__, traitname) 

1946 except AttributeError as e: 

1947 raise TraitError( 

1948 f"Class {self.__class__.__name__} does not have a trait named {traitname}" 

1949 ) from e 

1950 metadata_name = "_" + traitname + "_metadata" 

1951 if hasattr(self, metadata_name) and key in getattr(self, metadata_name): 

1952 return getattr(self, metadata_name).get(key, default) 

1953 else: 

1954 return trait.metadata.get(key, default) 

1955 

1956 @classmethod 

1957 def class_own_trait_events(cls: type[HasTraits], name: str) -> dict[str, EventHandler]: 

1958 """Get a dict of all event handlers defined on this class, not a parent. 

1959 

1960 Works like ``event_handlers``, except for excluding traits from parents. 

1961 """ 

1962 sup = super(cls, cls) 

1963 return { 

1964 n: e 

1965 for (n, e) in cls.events(name).items() # type:ignore[attr-defined] 

1966 if getattr(sup, n, None) is not e 

1967 } 

1968 

1969 @classmethod 

1970 def trait_events(cls: type[HasTraits], name: str | None = None) -> dict[str, EventHandler]: 

1971 """Get a ``dict`` of all the event handlers of this class. 

1972 

1973 Parameters 

1974 ---------- 

1975 name : str (default: None) 

1976 The name of a trait of this class. If name is ``None`` then all 

1977 the event handlers of this class will be returned instead. 

1978 

1979 Returns 

1980 ------- 

1981 The event handlers associated with a trait name, or all event handlers. 

1982 """ 

1983 events = {} 

1984 for k, v in getmembers(cls): 

1985 if isinstance(v, EventHandler): 

1986 if name is None: 

1987 events[k] = v 

1988 elif name in v.trait_names: # type:ignore[attr-defined] 

1989 events[k] = v 

1990 elif hasattr(v, "tags"): 

1991 if cls.trait_names(**v.tags): 

1992 events[k] = v 

1993 return events 

1994 

1995 

1996# ----------------------------------------------------------------------------- 

1997# Actual TraitTypes implementations/subclasses 

1998# ----------------------------------------------------------------------------- 

1999 

2000# ----------------------------------------------------------------------------- 

2001# TraitTypes subclasses for handling classes and instances of classes 

2002# ----------------------------------------------------------------------------- 

2003 

2004 

2005class ClassBasedTraitType(TraitType[G, S]): 

2006 """ 

2007 A trait with error reporting and string -> type resolution for Type, 

2008 Instance and This. 

2009 """ 

2010 

2011 def _resolve_string(self, string: str) -> t.Any: 

2012 """ 

2013 Resolve a string supplied for a type into an actual object. 

2014 """ 

2015 return import_item(string) 

2016 

2017 

2018class Type(ClassBasedTraitType[G, S]): 

2019 """A trait whose value must be a subclass of a specified class.""" 

2020 

2021 if t.TYPE_CHECKING: 

2022 

2023 @t.overload 

2024 def __init__( 

2025 self: Type[type, type], 

2026 default_value: Sentinel | None | str = ..., 

2027 klass: None | str = ..., 

2028 allow_none: Literal[False] = ..., 

2029 read_only: bool | None = ..., 

2030 help: str | None = ..., 

2031 config: t.Any | None = ..., 

2032 **kwargs: t.Any, 

2033 ) -> None: 

2034 ... 

2035 

2036 @t.overload 

2037 def __init__( 

2038 self: Type[type | None, type | None], 

2039 default_value: Sentinel | None | str = ..., 

2040 klass: None | str = ..., 

2041 allow_none: Literal[True] = ..., 

2042 read_only: bool | None = ..., 

2043 help: str | None = ..., 

2044 config: t.Any | None = ..., 

2045 **kwargs: t.Any, 

2046 ) -> None: 

2047 ... 

2048 

2049 @t.overload 

2050 def __init__( 

2051 self: Type[S, S], 

2052 default_value: S = ..., 

2053 klass: S = ..., 

2054 allow_none: Literal[False] = ..., 

2055 read_only: bool | None = ..., 

2056 help: str | None = ..., 

2057 config: t.Any | None = ..., 

2058 **kwargs: t.Any, 

2059 ) -> None: 

2060 ... 

2061 

2062 @t.overload 

2063 def __init__( 

2064 self: Type[S | None, S | None], 

2065 default_value: S | None = ..., 

2066 klass: S = ..., 

2067 allow_none: Literal[True] = ..., 

2068 read_only: bool | None = ..., 

2069 help: str | None = ..., 

2070 config: t.Any | None = ..., 

2071 **kwargs: t.Any, 

2072 ) -> None: 

2073 ... 

2074 

2075 def __init__( 

2076 self, 

2077 default_value: t.Any = Undefined, 

2078 klass: t.Any = None, 

2079 allow_none: bool = False, 

2080 read_only: bool | None = None, 

2081 help: str | None = None, 

2082 config: t.Any | None = None, 

2083 **kwargs: t.Any, 

2084 ) -> None: 

2085 """Construct a Type trait 

2086 

2087 A Type trait specifies that its values must be subclasses of 

2088 a particular class. 

2089 

2090 If only ``default_value`` is given, it is used for the ``klass`` as 

2091 well. If neither are given, both default to ``object``. 

2092 

2093 Parameters 

2094 ---------- 

2095 default_value : class, str or None 

2096 The default value must be a subclass of klass. If an str, 

2097 the str must be a fully specified class name, like 'foo.bar.Bah'. 

2098 The string is resolved into real class, when the parent 

2099 :class:`HasTraits` class is instantiated. 

2100 klass : class, str [ default object ] 

2101 Values of this trait must be a subclass of klass. The klass 

2102 may be specified in a string like: 'foo.bar.MyClass'. 

2103 The string is resolved into real class, when the parent 

2104 :class:`HasTraits` class is instantiated. 

2105 allow_none : bool [ default False ] 

2106 Indicates whether None is allowed as an assignable value. 

2107 **kwargs 

2108 extra kwargs passed to `ClassBasedTraitType` 

2109 """ 

2110 if default_value is Undefined: 

2111 new_default_value = object if (klass is None) else klass 

2112 else: 

2113 new_default_value = default_value 

2114 

2115 if klass is None: 

2116 if (default_value is None) or (default_value is Undefined): 

2117 klass = object 

2118 else: 

2119 klass = default_value 

2120 

2121 if not (inspect.isclass(klass) or isinstance(klass, str)): 

2122 raise TraitError("A Type trait must specify a class.") 

2123 

2124 self.klass = klass 

2125 

2126 super().__init__( 

2127 new_default_value, 

2128 allow_none=allow_none, 

2129 read_only=read_only, 

2130 help=help, 

2131 config=config, 

2132 **kwargs, 

2133 ) 

2134 

2135 def validate(self, obj: t.Any, value: t.Any) -> G: 

2136 """Validates that the value is a valid object instance.""" 

2137 if isinstance(value, str): 

2138 try: 

2139 value = self._resolve_string(value) 

2140 except ImportError as e: 

2141 raise TraitError( 

2142 f"The '{self.name}' trait of {obj} instance must be a type, but " 

2143 f"{value!r} could not be imported" 

2144 ) from e 

2145 try: 

2146 if issubclass(value, self.klass): # type:ignore[arg-type] 

2147 return t.cast(G, value) 

2148 except Exception: 

2149 pass 

2150 

2151 self.error(obj, value) 

2152 

2153 def info(self) -> str: 

2154 """Returns a description of the trait.""" 

2155 if isinstance(self.klass, str): 

2156 klass = self.klass 

2157 else: 

2158 klass = self.klass.__module__ + "." + self.klass.__name__ 

2159 result = "a subclass of '%s'" % klass 

2160 if self.allow_none: 

2161 return result + " or None" 

2162 return result 

2163 

2164 def instance_init(self, obj: t.Any) -> None: 

2165 # we can't do this in subclass_init because that 

2166 # might be called before all imports are done. 

2167 self._resolve_classes() 

2168 

2169 def _resolve_classes(self) -> None: 

2170 if isinstance(self.klass, str): 

2171 self.klass = self._resolve_string(self.klass) 

2172 if isinstance(self.default_value, str): 

2173 self.default_value = self._resolve_string(self.default_value) 

2174 

2175 def default_value_repr(self) -> str: 

2176 value = self.default_value 

2177 assert value is not None 

2178 if isinstance(value, str): 

2179 return repr(value) 

2180 else: 

2181 return repr(f"{value.__module__}.{value.__name__}") 

2182 

2183 

2184class Instance(ClassBasedTraitType[T, T]): 

2185 """A trait whose value must be an instance of a specified class. 

2186 

2187 The value can also be an instance of a subclass of the specified class. 

2188 

2189 Subclasses can declare default classes by overriding the klass attribute 

2190 """ 

2191 

2192 klass: str | type[T] | None = None 

2193 

2194 if t.TYPE_CHECKING: 

2195 

2196 @t.overload 

2197 def __init__( 

2198 self: Instance[T], 

2199 klass: type[T] = ..., 

2200 args: tuple[t.Any, ...] | None = ..., 

2201 kw: dict[str, t.Any] | None = ..., 

2202 allow_none: Literal[False] = ..., 

2203 read_only: bool | None = ..., 

2204 help: str | None = ..., 

2205 **kwargs: t.Any, 

2206 ) -> None: 

2207 ... 

2208 

2209 @t.overload 

2210 def __init__( 

2211 self: Instance[T | None], 

2212 klass: type[T] = ..., 

2213 args: tuple[t.Any, ...] | None = ..., 

2214 kw: dict[str, t.Any] | None = ..., 

2215 allow_none: Literal[True] = ..., 

2216 read_only: bool | None = ..., 

2217 help: str | None = ..., 

2218 **kwargs: t.Any, 

2219 ) -> None: 

2220 ... 

2221 

2222 @t.overload 

2223 def __init__( 

2224 self: Instance[t.Any], 

2225 klass: str | None = ..., 

2226 args: tuple[t.Any, ...] | None = ..., 

2227 kw: dict[str, t.Any] | None = ..., 

2228 allow_none: Literal[False] = ..., 

2229 read_only: bool | None = ..., 

2230 help: str | None = ..., 

2231 **kwargs: t.Any, 

2232 ) -> None: 

2233 ... 

2234 

2235 @t.overload 

2236 def __init__( 

2237 self: Instance[t.Any | None], 

2238 klass: str | None = ..., 

2239 args: tuple[t.Any, ...] | None = ..., 

2240 kw: dict[str, t.Any] | None = ..., 

2241 allow_none: Literal[True] = ..., 

2242 read_only: bool | None = ..., 

2243 help: str | None = ..., 

2244 **kwargs: t.Any, 

2245 ) -> None: 

2246 ... 

2247 

2248 def __init__( 

2249 self, 

2250 klass: str | type[T] | None = None, 

2251 args: tuple[t.Any, ...] | None = None, 

2252 kw: dict[str, t.Any] | None = None, 

2253 allow_none: bool = False, 

2254 read_only: bool | None = None, 

2255 help: str | None = None, 

2256 **kwargs: t.Any, 

2257 ) -> None: 

2258 """Construct an Instance trait. 

2259 

2260 This trait allows values that are instances of a particular 

2261 class or its subclasses. Our implementation is quite different 

2262 from that of enthough.traits as we don't allow instances to be used 

2263 for klass and we handle the ``args`` and ``kw`` arguments differently. 

2264 

2265 Parameters 

2266 ---------- 

2267 klass : class, str 

2268 The class that forms the basis for the trait. Class names 

2269 can also be specified as strings, like 'foo.bar.Bar'. 

2270 args : tuple 

2271 Positional arguments for generating the default value. 

2272 kw : dict 

2273 Keyword arguments for generating the default value. 

2274 allow_none : bool [ default False ] 

2275 Indicates whether None is allowed as a value. 

2276 **kwargs 

2277 Extra kwargs passed to `ClassBasedTraitType` 

2278 

2279 Notes 

2280 ----- 

2281 If both ``args`` and ``kw`` are None, then the default value is None. 

2282 If ``args`` is a tuple and ``kw`` is a dict, then the default is 

2283 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is 

2284 None, the None is replaced by ``()`` or ``{}``, respectively. 

2285 """ 

2286 if klass is None: 

2287 klass = self.klass 

2288 

2289 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, str)): 

2290 self.klass = klass 

2291 else: 

2292 raise TraitError("The klass attribute must be a class not: %r" % klass) 

2293 

2294 if (kw is not None) and not isinstance(kw, dict): 

2295 raise TraitError("The 'kw' argument must be a dict or None.") 

2296 if (args is not None) and not isinstance(args, tuple): 

2297 raise TraitError("The 'args' argument must be a tuple or None.") 

2298 

2299 self.default_args = args 

2300 self.default_kwargs = kw 

2301 

2302 super().__init__(allow_none=allow_none, read_only=read_only, help=help, **kwargs) 

2303 

2304 def validate(self, obj: t.Any, value: t.Any) -> T | None: 

2305 assert self.klass is not None 

2306 if self.allow_none and value is None: 

2307 return value 

2308 if isinstance(value, self.klass): # type:ignore[arg-type] 

2309 return t.cast(T, value) 

2310 else: 

2311 self.error(obj, value) 

2312 

2313 def info(self) -> str: 

2314 if isinstance(self.klass, str): 

2315 result = add_article(self.klass) 

2316 else: 

2317 result = describe("a", self.klass) 

2318 if self.allow_none: 

2319 result += " or None" 

2320 return result 

2321 

2322 def instance_init(self, obj: t.Any) -> None: 

2323 # we can't do this in subclass_init because that 

2324 # might be called before all imports are done. 

2325 self._resolve_classes() 

2326 

2327 def _resolve_classes(self) -> None: 

2328 if isinstance(self.klass, str): 

2329 self.klass = self._resolve_string(self.klass) 

2330 

2331 def make_dynamic_default(self) -> T | None: 

2332 if (self.default_args is None) and (self.default_kwargs is None): 

2333 return None 

2334 assert self.klass is not None 

2335 return self.klass(*(self.default_args or ()), **(self.default_kwargs or {})) # type:ignore[operator] 

2336 

2337 def default_value_repr(self) -> str: 

2338 return repr(self.make_dynamic_default()) 

2339 

2340 def from_string(self, s: str) -> T | None: 

2341 return t.cast(T, _safe_literal_eval(s)) 

2342 

2343 

2344class ForwardDeclaredMixin: 

2345 """ 

2346 Mixin for forward-declared versions of Instance and Type. 

2347 """ 

2348 

2349 def _resolve_string(self, string: str) -> t.Any: 

2350 """ 

2351 Find the specified class name by looking for it in the module in which 

2352 our this_class attribute was defined. 

2353 """ 

2354 modname = self.this_class.__module__ # type:ignore[attr-defined] 

2355 return import_item(".".join([modname, string])) 

2356 

2357 

2358class ForwardDeclaredType(ForwardDeclaredMixin, Type[G, S]): 

2359 """ 

2360 Forward-declared version of Type. 

2361 """ 

2362 

2363 

2364class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance[T]): 

2365 """ 

2366 Forward-declared version of Instance. 

2367 """ 

2368 

2369 

2370class This(ClassBasedTraitType[t.Optional[T], t.Optional[T]]): 

2371 """A trait for instances of the class containing this trait. 

2372 

2373 Because how how and when class bodies are executed, the ``This`` 

2374 trait can only have a default value of None. This, and because we 

2375 always validate default values, ``allow_none`` is *always* true. 

2376 """ 

2377 

2378 info_text = "an instance of the same type as the receiver or None" 

2379 

2380 def __init__(self, **kwargs: t.Any) -> None: 

2381 super().__init__(None, **kwargs) 

2382 

2383 def validate(self, obj: t.Any, value: t.Any) -> HasTraits | None: 

2384 # What if value is a superclass of obj.__class__? This is 

2385 # complicated if it was the superclass that defined the This 

2386 # trait. 

2387 assert self.this_class is not None 

2388 if isinstance(value, self.this_class) or (value is None): 

2389 return value 

2390 else: 

2391 self.error(obj, value) 

2392 

2393 

2394class Union(TraitType[t.Any, t.Any]): 

2395 """A trait type representing a Union type.""" 

2396 

2397 def __init__(self, trait_types: t.Any, **kwargs: t.Any) -> None: 

2398 """Construct a Union trait. 

2399 

2400 This trait allows values that are allowed by at least one of the 

2401 specified trait types. A Union traitlet cannot have metadata on 

2402 its own, besides the metadata of the listed types. 

2403 

2404 Parameters 

2405 ---------- 

2406 trait_types : sequence 

2407 The list of trait types of length at least 1. 

2408 **kwargs 

2409 Extra kwargs passed to `TraitType` 

2410 

2411 Notes 

2412 ----- 

2413 Union([Float(), Bool(), Int()]) attempts to validate the provided values 

2414 with the validation function of Float, then Bool, and finally Int. 

2415 

2416 Parsing from string is ambiguous for container types which accept other 

2417 collection-like literals (e.g. List accepting both `[]` and `()` 

2418 precludes Union from ever parsing ``Union([List(), Tuple()])`` as a tuple; 

2419 you can modify behaviour of too permissive container traits by overriding 

2420 ``_literal_from_string_pairs`` in subclasses. 

2421 Similarly, parsing unions of numeric types is only unambiguous if 

2422 types are provided in order of increasing permissiveness, e.g. 

2423 ``Union([Int(), Float()])`` (since floats accept integer-looking values). 

2424 """ 

2425 self.trait_types = list(trait_types) 

2426 self.info_text = " or ".join([tt.info() for tt in self.trait_types]) 

2427 super().__init__(**kwargs) 

2428 

2429 def default(self, obj: t.Any = None) -> t.Any: 

2430 default = super().default(obj) 

2431 for trait in self.trait_types: 

2432 if default is Undefined: 

2433 default = trait.default(obj) 

2434 else: 

2435 break 

2436 return default 

2437 

2438 def class_init(self, cls: type[HasTraits], name: str | None) -> None: 

2439 for trait_type in reversed(self.trait_types): 

2440 trait_type.class_init(cls, None) 

2441 super().class_init(cls, name) 

2442 

2443 def subclass_init(self, cls: type[t.Any]) -> None: 

2444 for trait_type in reversed(self.trait_types): 

2445 trait_type.subclass_init(cls) 

2446 # explicitly not calling super().subclass_init(cls) 

2447 # to opt out of instance_init 

2448 

2449 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

2450 with obj.cross_validation_lock: 

2451 for trait_type in self.trait_types: 

2452 try: 

2453 v = trait_type._validate(obj, value) 

2454 # In the case of an element trait, the name is None 

2455 if self.name is not None: 

2456 setattr(obj, "_" + self.name + "_metadata", trait_type.metadata) 

2457 return v 

2458 except TraitError: 

2459 continue 

2460 self.error(obj, value) 

2461 

2462 def __or__(self, other: t.Any) -> Union: 

2463 if isinstance(other, Union): 

2464 return Union(self.trait_types + other.trait_types) 

2465 else: 

2466 return Union([*self.trait_types, other]) 

2467 

2468 def from_string(self, s: str) -> t.Any: 

2469 for trait_type in self.trait_types: 

2470 try: 

2471 v = trait_type.from_string(s) 

2472 return trait_type.validate(None, v) 

2473 except (TraitError, ValueError): 

2474 continue 

2475 return super().from_string(s) 

2476 

2477 

2478# ----------------------------------------------------------------------------- 

2479# Basic TraitTypes implementations/subclasses 

2480# ----------------------------------------------------------------------------- 

2481 

2482 

2483class Any(TraitType[t.Optional[t.Any], t.Optional[t.Any]]): 

2484 """A trait which allows any value.""" 

2485 

2486 if t.TYPE_CHECKING: 

2487 

2488 @t.overload 

2489 def __init__( 

2490 self: Any, 

2491 default_value: t.Any = ..., 

2492 *, 

2493 allow_none: Literal[False], 

2494 read_only: bool | None = ..., 

2495 help: str | None = ..., 

2496 config: t.Any | None = ..., 

2497 **kwargs: t.Any, 

2498 ) -> None: 

2499 ... 

2500 

2501 @t.overload 

2502 def __init__( 

2503 self: Any, 

2504 default_value: t.Any = ..., 

2505 *, 

2506 allow_none: Literal[True], 

2507 read_only: bool | None = ..., 

2508 help: str | None = ..., 

2509 config: t.Any | None = ..., 

2510 **kwargs: t.Any, 

2511 ) -> None: 

2512 ... 

2513 

2514 @t.overload 

2515 def __init__( 

2516 self: Any, 

2517 default_value: t.Any = ..., 

2518 *, 

2519 allow_none: Literal[True, False] = ..., 

2520 help: str | None = ..., 

2521 read_only: bool | None = False, 

2522 config: t.Any = None, 

2523 **kwargs: t.Any, 

2524 ) -> None: 

2525 ... 

2526 

2527 def __init__( 

2528 self: Any, 

2529 default_value: t.Any = ..., 

2530 *, 

2531 allow_none: bool = False, 

2532 help: str | None = "", 

2533 read_only: bool | None = False, 

2534 config: t.Any = None, 

2535 **kwargs: t.Any, 

2536 ) -> None: 

2537 ... 

2538 

2539 @t.overload 

2540 def __get__(self, obj: None, cls: type[t.Any]) -> Any: 

2541 ... 

2542 

2543 @t.overload 

2544 def __get__(self, obj: t.Any, cls: type[t.Any]) -> t.Any: 

2545 ... 

2546 

2547 def __get__(self, obj: t.Any | None, cls: type[t.Any]) -> t.Any | Any: 

2548 ... 

2549 

2550 default_value: t.Any | None = None 

2551 allow_none = True 

2552 info_text = "any value" 

2553 

2554 def subclass_init(self, cls: type[t.Any]) -> None: 

2555 pass # fully opt out of instance_init 

2556 

2557 

2558def _validate_bounds( 

2559 trait: Int[t.Any, t.Any] | Float[t.Any, t.Any], obj: t.Any, value: t.Any 

2560) -> t.Any: 

2561 """ 

2562 Validate that a number to be applied to a trait is between bounds. 

2563 

2564 If value is not between min_bound and max_bound, this raises a 

2565 TraitError with an error message appropriate for this trait. 

2566 """ 

2567 if trait.min is not None and value < trait.min: 

2568 raise TraitError( 

2569 f"The value of the '{trait.name}' trait of {class_of(obj)} instance should " 

2570 f"not be less than {trait.min}, but a value of {value} was " 

2571 "specified" 

2572 ) 

2573 if trait.max is not None and value > trait.max: 

2574 raise TraitError( 

2575 f"The value of the '{trait.name}' trait of {class_of(obj)} instance should " 

2576 f"not be greater than {trait.max}, but a value of {value} was " 

2577 "specified" 

2578 ) 

2579 return value 

2580 

2581 

2582# I = t.TypeVar('I', t.Optional[int], int) 

2583 

2584 

2585class Int(TraitType[G, S]): 

2586 """An int trait.""" 

2587 

2588 default_value = 0 

2589 info_text = "an int" 

2590 

2591 @t.overload 

2592 def __init__( 

2593 self: Int[int, int], 

2594 default_value: int | Sentinel = ..., 

2595 allow_none: Literal[False] = ..., 

2596 read_only: bool | None = ..., 

2597 help: str | None = ..., 

2598 config: t.Any | None = ..., 

2599 **kwargs: t.Any, 

2600 ) -> None: 

2601 ... 

2602 

2603 @t.overload 

2604 def __init__( 

2605 self: Int[int | None, int | None], 

2606 default_value: int | Sentinel | None = ..., 

2607 allow_none: Literal[True] = ..., 

2608 read_only: bool | None = ..., 

2609 help: str | None = ..., 

2610 config: t.Any | None = ..., 

2611 **kwargs: t.Any, 

2612 ) -> None: 

2613 ... 

2614 

2615 def __init__( 

2616 self, 

2617 default_value: t.Any = Undefined, 

2618 allow_none: bool = False, 

2619 read_only: bool | None = None, 

2620 help: str | None = None, 

2621 config: t.Any | None = None, 

2622 **kwargs: t.Any, 

2623 ) -> None: 

2624 self.min = kwargs.pop("min", None) 

2625 self.max = kwargs.pop("max", None) 

2626 super().__init__( 

2627 default_value=default_value, 

2628 allow_none=allow_none, 

2629 read_only=read_only, 

2630 help=help, 

2631 config=config, 

2632 **kwargs, 

2633 ) 

2634 

2635 def validate(self, obj: t.Any, value: t.Any) -> G: 

2636 if not isinstance(value, int): 

2637 self.error(obj, value) 

2638 return t.cast(G, _validate_bounds(self, obj, value)) 

2639 

2640 def from_string(self, s: str) -> G: 

2641 if self.allow_none and s == "None": 

2642 return t.cast(G, None) 

2643 return t.cast(G, int(s)) 

2644 

2645 def subclass_init(self, cls: type[t.Any]) -> None: 

2646 pass # fully opt out of instance_init 

2647 

2648 

2649class CInt(Int[G, S]): 

2650 """A casting version of the int trait.""" 

2651 

2652 if t.TYPE_CHECKING: 

2653 

2654 @t.overload 

2655 def __init__( 

2656 self: CInt[int, t.Any], 

2657 default_value: t.Any | Sentinel = ..., 

2658 allow_none: Literal[False] = ..., 

2659 read_only: bool | None = ..., 

2660 help: str | None = ..., 

2661 config: t.Any | None = ..., 

2662 **kwargs: t.Any, 

2663 ) -> None: 

2664 ... 

2665 

2666 @t.overload 

2667 def __init__( 

2668 self: CInt[int | None, t.Any], 

2669 default_value: t.Any | Sentinel | None = ..., 

2670 allow_none: Literal[True] = ..., 

2671 read_only: bool | None = ..., 

2672 help: str | None = ..., 

2673 config: t.Any | None = ..., 

2674 **kwargs: t.Any, 

2675 ) -> None: 

2676 ... 

2677 

2678 def __init__( 

2679 self: CInt[int | None, t.Any], 

2680 default_value: t.Any | Sentinel | None = ..., 

2681 allow_none: bool = ..., 

2682 read_only: bool | None = ..., 

2683 help: str | None = ..., 

2684 config: t.Any | None = ..., 

2685 **kwargs: t.Any, 

2686 ) -> None: 

2687 ... 

2688 

2689 def validate(self, obj: t.Any, value: t.Any) -> G: 

2690 try: 

2691 value = int(value) 

2692 except Exception: 

2693 self.error(obj, value) 

2694 return t.cast(G, _validate_bounds(self, obj, value)) 

2695 

2696 

2697Long, CLong = Int, CInt 

2698Integer = Int 

2699 

2700 

2701class Float(TraitType[G, S]): 

2702 """A float trait.""" 

2703 

2704 default_value = 0.0 

2705 info_text = "a float" 

2706 

2707 @t.overload 

2708 def __init__( 

2709 self: Float[float, int | float], 

2710 default_value: float | Sentinel = ..., 

2711 allow_none: Literal[False] = ..., 

2712 read_only: bool | None = ..., 

2713 help: str | None = ..., 

2714 config: t.Any | None = ..., 

2715 **kwargs: t.Any, 

2716 ) -> None: 

2717 ... 

2718 

2719 @t.overload 

2720 def __init__( 

2721 self: Float[int | None, int | float | None], 

2722 default_value: float | Sentinel | None = ..., 

2723 allow_none: Literal[True] = ..., 

2724 read_only: bool | None = ..., 

2725 help: str | None = ..., 

2726 config: t.Any | None = ..., 

2727 **kwargs: t.Any, 

2728 ) -> None: 

2729 ... 

2730 

2731 def __init__( 

2732 self: Float[int | None, int | float | None], 

2733 default_value: float | Sentinel | None = Undefined, 

2734 allow_none: bool = False, 

2735 read_only: bool | None = False, 

2736 help: str | None = None, 

2737 config: t.Any | None = None, 

2738 **kwargs: t.Any, 

2739 ) -> None: 

2740 self.min = kwargs.pop("min", -float("inf")) 

2741 self.max = kwargs.pop("max", float("inf")) 

2742 super().__init__( 

2743 default_value=default_value, 

2744 allow_none=allow_none, 

2745 read_only=read_only, 

2746 help=help, 

2747 config=config, 

2748 **kwargs, 

2749 ) 

2750 

2751 def validate(self, obj: t.Any, value: t.Any) -> G: 

2752 if isinstance(value, int): 

2753 value = float(value) 

2754 if not isinstance(value, float): 

2755 self.error(obj, value) 

2756 return t.cast(G, _validate_bounds(self, obj, value)) 

2757 

2758 def from_string(self, s: str) -> G: 

2759 if self.allow_none and s == "None": 

2760 return t.cast(G, None) 

2761 return t.cast(G, float(s)) 

2762 

2763 def subclass_init(self, cls: type[t.Any]) -> None: 

2764 pass # fully opt out of instance_init 

2765 

2766 

2767class CFloat(Float[G, S]): 

2768 """A casting version of the float trait.""" 

2769 

2770 if t.TYPE_CHECKING: 

2771 

2772 @t.overload 

2773 def __init__( 

2774 self: CFloat[float, t.Any], 

2775 default_value: t.Any = ..., 

2776 allow_none: Literal[False] = ..., 

2777 read_only: bool | None = ..., 

2778 help: str | None = ..., 

2779 config: t.Any | None = ..., 

2780 **kwargs: t.Any, 

2781 ) -> None: 

2782 ... 

2783 

2784 @t.overload 

2785 def __init__( 

2786 self: CFloat[float | None, t.Any], 

2787 default_value: t.Any = ..., 

2788 allow_none: Literal[True] = ..., 

2789 read_only: bool | None = ..., 

2790 help: str | None = ..., 

2791 config: t.Any | None = ..., 

2792 **kwargs: t.Any, 

2793 ) -> None: 

2794 ... 

2795 

2796 def __init__( 

2797 self: CFloat[float | None, t.Any], 

2798 default_value: t.Any = ..., 

2799 allow_none: bool = ..., 

2800 read_only: bool | None = ..., 

2801 help: str | None = ..., 

2802 config: t.Any | None = ..., 

2803 **kwargs: t.Any, 

2804 ) -> None: 

2805 ... 

2806 

2807 def validate(self, obj: t.Any, value: t.Any) -> G: 

2808 try: 

2809 value = float(value) 

2810 except Exception: 

2811 self.error(obj, value) 

2812 return t.cast(G, _validate_bounds(self, obj, value)) 

2813 

2814 

2815class Complex(TraitType[complex, t.Union[complex, float, int]]): 

2816 """A trait for complex numbers.""" 

2817 

2818 default_value = 0.0 + 0.0j 

2819 info_text = "a complex number" 

2820 

2821 def validate(self, obj: t.Any, value: t.Any) -> complex | None: 

2822 if isinstance(value, complex): 

2823 return value 

2824 if isinstance(value, (float, int)): 

2825 return complex(value) 

2826 self.error(obj, value) 

2827 

2828 def from_string(self, s: str) -> complex | None: 

2829 if self.allow_none and s == "None": 

2830 return None 

2831 return complex(s) 

2832 

2833 def subclass_init(self, cls: type[t.Any]) -> None: 

2834 pass # fully opt out of instance_init 

2835 

2836 

2837class CComplex(Complex, TraitType[complex, t.Any]): 

2838 """A casting version of the complex number trait.""" 

2839 

2840 def validate(self, obj: t.Any, value: t.Any) -> complex | None: 

2841 try: 

2842 return complex(value) 

2843 except Exception: 

2844 self.error(obj, value) 

2845 

2846 

2847# We should always be explicit about whether we're using bytes or unicode, both 

2848# for Python 3 conversion and for reliable unicode behaviour on Python 2. So 

2849# we don't have a Str type. 

2850class Bytes(TraitType[bytes, bytes]): 

2851 """A trait for byte strings.""" 

2852 

2853 default_value = b"" 

2854 info_text = "a bytes object" 

2855 

2856 def validate(self, obj: t.Any, value: t.Any) -> bytes | None: 

2857 if isinstance(value, bytes): 

2858 return value 

2859 self.error(obj, value) 

2860 

2861 def from_string(self, s: str) -> bytes | None: 

2862 if self.allow_none and s == "None": 

2863 return None 

2864 if len(s) >= 3: 

2865 # handle deprecated b"string" 

2866 for quote in ('"', "'"): 

2867 if s[:2] == f"b{quote}" and s[-1] == quote: 

2868 old_s = s 

2869 s = s[2:-1] 

2870 warn( 

2871 "Supporting extra quotes around Bytes is deprecated in traitlets 5.0. " 

2872 f"Use {s!r} instead of {old_s!r}.", 

2873 DeprecationWarning, 

2874 stacklevel=2, 

2875 ) 

2876 break 

2877 return s.encode("utf8") 

2878 

2879 def subclass_init(self, cls: type[t.Any]) -> None: 

2880 pass # fully opt out of instance_init 

2881 

2882 

2883class CBytes(Bytes, TraitType[bytes, t.Any]): 

2884 """A casting version of the byte string trait.""" 

2885 

2886 def validate(self, obj: t.Any, value: t.Any) -> bytes | None: 

2887 try: 

2888 return bytes(value) 

2889 except Exception: 

2890 self.error(obj, value) 

2891 

2892 

2893class Unicode(TraitType[G, S]): 

2894 """A trait for unicode strings.""" 

2895 

2896 default_value = "" 

2897 info_text = "a unicode string" 

2898 

2899 if t.TYPE_CHECKING: 

2900 

2901 @t.overload 

2902 def __init__( 

2903 self: Unicode[str, str | bytes], 

2904 default_value: str | Sentinel = ..., 

2905 allow_none: Literal[False] = ..., 

2906 read_only: bool | None = ..., 

2907 help: str | None = ..., 

2908 config: t.Any = ..., 

2909 **kwargs: t.Any, 

2910 ) -> None: 

2911 ... 

2912 

2913 @t.overload 

2914 def __init__( 

2915 self: Unicode[str | None, str | bytes | None], 

2916 default_value: str | Sentinel | None = ..., 

2917 allow_none: Literal[True] = ..., 

2918 read_only: bool | None = ..., 

2919 help: str | None = ..., 

2920 config: t.Any = ..., 

2921 **kwargs: t.Any, 

2922 ) -> None: 

2923 ... 

2924 

2925 def __init__( 

2926 self: Unicode[str | None, str | bytes | None], 

2927 default_value: str | Sentinel | None = ..., 

2928 allow_none: bool = ..., 

2929 read_only: bool | None = ..., 

2930 help: str | None = ..., 

2931 config: t.Any = ..., 

2932 **kwargs: t.Any, 

2933 ) -> None: 

2934 ... 

2935 

2936 def validate(self, obj: t.Any, value: t.Any) -> G: 

2937 if isinstance(value, str): 

2938 return t.cast(G, value) 

2939 if isinstance(value, bytes): 

2940 try: 

2941 return t.cast(G, value.decode("ascii", "strict")) 

2942 except UnicodeDecodeError as e: 

2943 msg = "Could not decode {!r} for unicode trait '{}' of {} instance." 

2944 raise TraitError(msg.format(value, self.name, class_of(obj))) from e 

2945 self.error(obj, value) 

2946 

2947 def from_string(self, s: str) -> G: 

2948 if self.allow_none and s == "None": 

2949 return t.cast(G, None) 

2950 s = os.path.expanduser(s) 

2951 if len(s) >= 2: 

2952 # handle deprecated "1" 

2953 for c in ('"', "'"): 

2954 if s[0] == s[-1] == c: 

2955 old_s = s 

2956 s = s[1:-1] 

2957 warn( 

2958 "Supporting extra quotes around strings is deprecated in traitlets 5.0. " 

2959 f"You can use {s!r} instead of {old_s!r} if you require traitlets >=5.", 

2960 DeprecationWarning, 

2961 stacklevel=2, 

2962 ) 

2963 return t.cast(G, s) 

2964 

2965 def subclass_init(self, cls: type[t.Any]) -> None: 

2966 pass # fully opt out of instance_init 

2967 

2968 

2969class CUnicode(Unicode[G, S], TraitType[str, t.Any]): 

2970 """A casting version of the unicode trait.""" 

2971 

2972 if t.TYPE_CHECKING: 

2973 

2974 @t.overload 

2975 def __init__( 

2976 self: CUnicode[str, t.Any], 

2977 default_value: str | Sentinel = ..., 

2978 allow_none: Literal[False] = ..., 

2979 read_only: bool | None = ..., 

2980 help: str | None = ..., 

2981 config: t.Any = ..., 

2982 **kwargs: t.Any, 

2983 ) -> None: 

2984 ... 

2985 

2986 @t.overload 

2987 def __init__( 

2988 self: CUnicode[str | None, t.Any], 

2989 default_value: str | Sentinel | None = ..., 

2990 allow_none: Literal[True] = ..., 

2991 read_only: bool | None = ..., 

2992 help: str | None = ..., 

2993 config: t.Any = ..., 

2994 **kwargs: t.Any, 

2995 ) -> None: 

2996 ... 

2997 

2998 def __init__( 

2999 self: CUnicode[str | None, t.Any], 

3000 default_value: str | Sentinel | None = ..., 

3001 allow_none: bool = ..., 

3002 read_only: bool | None = ..., 

3003 help: str | None = ..., 

3004 config: t.Any = ..., 

3005 **kwargs: t.Any, 

3006 ) -> None: 

3007 ... 

3008 

3009 def validate(self, obj: t.Any, value: t.Any) -> G: 

3010 try: 

3011 return t.cast(G, str(value)) 

3012 except Exception: 

3013 self.error(obj, value) 

3014 

3015 

3016class ObjectName(TraitType[str, str]): 

3017 """A string holding a valid object name in this version of Python. 

3018 

3019 This does not check that the name exists in any scope.""" 

3020 

3021 info_text = "a valid object identifier in Python" 

3022 

3023 coerce_str = staticmethod(lambda _, s: s) 

3024 

3025 def validate(self, obj: t.Any, value: t.Any) -> str: 

3026 value = self.coerce_str(obj, value) 

3027 

3028 if isinstance(value, str) and isidentifier(value): 

3029 return value 

3030 self.error(obj, value) 

3031 

3032 def from_string(self, s: str) -> str | None: 

3033 if self.allow_none and s == "None": 

3034 return None 

3035 return s 

3036 

3037 

3038class DottedObjectName(ObjectName): 

3039 """A string holding a valid dotted object name in Python, such as A.b3._c""" 

3040 

3041 def validate(self, obj: t.Any, value: t.Any) -> str: 

3042 value = self.coerce_str(obj, value) 

3043 

3044 if isinstance(value, str) and all(isidentifier(a) for a in value.split(".")): 

3045 return value 

3046 self.error(obj, value) 

3047 

3048 

3049class Bool(TraitType[G, S]): 

3050 """A boolean (True, False) trait.""" 

3051 

3052 default_value = False 

3053 info_text = "a boolean" 

3054 

3055 if t.TYPE_CHECKING: 

3056 

3057 @t.overload 

3058 def __init__( 

3059 self: Bool[bool, bool | int], 

3060 default_value: bool | Sentinel = ..., 

3061 allow_none: Literal[False] = ..., 

3062 read_only: bool | None = ..., 

3063 help: str | None = ..., 

3064 config: t.Any = ..., 

3065 **kwargs: t.Any, 

3066 ) -> None: 

3067 ... 

3068 

3069 @t.overload 

3070 def __init__( 

3071 self: Bool[bool | None, bool | int | None], 

3072 default_value: bool | Sentinel | None = ..., 

3073 allow_none: Literal[True] = ..., 

3074 read_only: bool | None = ..., 

3075 help: str | None = ..., 

3076 config: t.Any = ..., 

3077 **kwargs: t.Any, 

3078 ) -> None: 

3079 ... 

3080 

3081 def __init__( 

3082 self: Bool[bool | None, bool | int | None], 

3083 default_value: bool | Sentinel | None = ..., 

3084 allow_none: bool = ..., 

3085 read_only: bool | None = ..., 

3086 help: str | None = ..., 

3087 config: t.Any = ..., 

3088 **kwargs: t.Any, 

3089 ) -> None: 

3090 ... 

3091 

3092 def validate(self, obj: t.Any, value: t.Any) -> G: 

3093 if isinstance(value, bool): 

3094 return t.cast(G, value) 

3095 elif isinstance(value, int): 

3096 if value == 1: 

3097 return t.cast(G, True) 

3098 elif value == 0: 

3099 return t.cast(G, False) 

3100 self.error(obj, value) 

3101 

3102 def from_string(self, s: str) -> G: 

3103 if self.allow_none and s == "None": 

3104 return t.cast(G, None) 

3105 s = s.lower() 

3106 if s in {"true", "1"}: 

3107 return t.cast(G, True) 

3108 elif s in {"false", "0"}: 

3109 return t.cast(G, False) 

3110 else: 

3111 raise ValueError("%r is not 1, 0, true, or false") 

3112 

3113 def subclass_init(self, cls: type[t.Any]) -> None: 

3114 pass # fully opt out of instance_init 

3115 

3116 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3117 """Completion hints for argcomplete""" 

3118 completions = ["true", "1", "false", "0"] 

3119 if self.allow_none: 

3120 completions.append("None") 

3121 return completions 

3122 

3123 

3124class CBool(Bool[G, S]): 

3125 """A casting version of the boolean trait.""" 

3126 

3127 if t.TYPE_CHECKING: 

3128 

3129 @t.overload 

3130 def __init__( 

3131 self: CBool[bool, t.Any], 

3132 default_value: bool | Sentinel = ..., 

3133 allow_none: Literal[False] = ..., 

3134 read_only: bool | None = ..., 

3135 help: str | None = ..., 

3136 config: t.Any = ..., 

3137 **kwargs: t.Any, 

3138 ) -> None: 

3139 ... 

3140 

3141 @t.overload 

3142 def __init__( 

3143 self: CBool[bool | None, t.Any], 

3144 default_value: bool | Sentinel | None = ..., 

3145 allow_none: Literal[True] = ..., 

3146 read_only: bool | None = ..., 

3147 help: str | None = ..., 

3148 config: t.Any = ..., 

3149 **kwargs: t.Any, 

3150 ) -> None: 

3151 ... 

3152 

3153 def __init__( 

3154 self: CBool[bool | None, t.Any], 

3155 default_value: bool | Sentinel | None = ..., 

3156 allow_none: bool = ..., 

3157 read_only: bool | None = ..., 

3158 help: str | None = ..., 

3159 config: t.Any = ..., 

3160 **kwargs: t.Any, 

3161 ) -> None: 

3162 ... 

3163 

3164 def validate(self, obj: t.Any, value: t.Any) -> G: 

3165 try: 

3166 return t.cast(G, bool(value)) 

3167 except Exception: 

3168 self.error(obj, value) 

3169 

3170 

3171class Enum(TraitType[G, G]): 

3172 """An enum whose value must be in a given sequence.""" 

3173 

3174 if t.TYPE_CHECKING: 

3175 

3176 @t.overload 

3177 def __init__( 

3178 self: Enum[G], 

3179 values: t.Sequence[G], 

3180 default_value: G | Sentinel = ..., 

3181 allow_none: Literal[False] = ..., 

3182 read_only: bool | None = ..., 

3183 help: str | None = ..., 

3184 config: t.Any = ..., 

3185 **kwargs: t.Any, 

3186 ) -> None: 

3187 ... 

3188 

3189 @t.overload 

3190 def __init__( 

3191 self: Enum[G | None], 

3192 values: t.Sequence[G] | None, 

3193 default_value: G | Sentinel | None = ..., 

3194 allow_none: Literal[True] = ..., 

3195 read_only: bool | None = ..., 

3196 help: str | None = ..., 

3197 config: t.Any = ..., 

3198 **kwargs: t.Any, 

3199 ) -> None: 

3200 ... 

3201 

3202 def __init__( 

3203 self: Enum[G], 

3204 values: t.Sequence[G] | None, 

3205 default_value: G | Sentinel | None = Undefined, 

3206 allow_none: bool = False, 

3207 read_only: bool | None = None, 

3208 help: str | None = None, 

3209 config: t.Any = None, 

3210 **kwargs: t.Any, 

3211 ) -> None: 

3212 self.values = values 

3213 if allow_none is True and default_value is Undefined: 

3214 default_value = None 

3215 kwargs["allow_none"] = allow_none 

3216 kwargs["read_only"] = read_only 

3217 kwargs["help"] = help 

3218 kwargs["config"] = config 

3219 super().__init__(default_value, **kwargs) 

3220 

3221 def validate(self, obj: t.Any, value: t.Any) -> G: 

3222 if self.values and value in self.values: 

3223 return t.cast(G, value) 

3224 self.error(obj, value) 

3225 

3226 def _choices_str(self, as_rst: bool = False) -> str: 

3227 """Returns a description of the trait choices (not none).""" 

3228 choices = self.values or [] 

3229 if as_rst: 

3230 choice_str = "|".join("``%r``" % x for x in choices) 

3231 else: 

3232 choice_str = repr(list(choices)) 

3233 return choice_str 

3234 

3235 def _info(self, as_rst: bool = False) -> str: 

3236 """Returns a description of the trait.""" 

3237 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3238 return f"any of {self._choices_str(as_rst)}{none}" 

3239 

3240 def info(self) -> str: 

3241 return self._info(as_rst=False) 

3242 

3243 def info_rst(self) -> str: 

3244 return self._info(as_rst=True) 

3245 

3246 def from_string(self, s: str) -> G: 

3247 try: 

3248 return self.validate(None, s) 

3249 except TraitError: 

3250 return t.cast(G, _safe_literal_eval(s)) 

3251 

3252 def subclass_init(self, cls: type[t.Any]) -> None: 

3253 pass # fully opt out of instance_init 

3254 

3255 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3256 """Completion hints for argcomplete""" 

3257 return [str(v) for v in self.values or []] 

3258 

3259 

3260class CaselessStrEnum(Enum[G]): 

3261 """An enum of strings where the case should be ignored.""" 

3262 

3263 def __init__( 

3264 self: CaselessStrEnum[t.Any], 

3265 values: t.Any, 

3266 default_value: t.Any = Undefined, 

3267 **kwargs: t.Any, 

3268 ) -> None: 

3269 super().__init__(values, default_value=default_value, **kwargs) 

3270 

3271 def validate(self, obj: t.Any, value: t.Any) -> G: 

3272 if not isinstance(value, str): 

3273 self.error(obj, value) 

3274 

3275 for v in self.values or []: 

3276 assert isinstance(v, str) 

3277 if v.lower() == value.lower(): 

3278 return t.cast(G, v) 

3279 self.error(obj, value) 

3280 

3281 def _info(self, as_rst: bool = False) -> str: 

3282 """Returns a description of the trait.""" 

3283 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3284 return f"any of {self._choices_str(as_rst)} (case-insensitive){none}" 

3285 

3286 def info(self) -> str: 

3287 return self._info(as_rst=False) 

3288 

3289 def info_rst(self) -> str: 

3290 return self._info(as_rst=True) 

3291 

3292 

3293class FuzzyEnum(Enum[G]): 

3294 """An case-ignoring enum matching choices by unique prefixes/substrings.""" 

3295 

3296 case_sensitive = False 

3297 #: If True, choices match anywhere in the string, otherwise match prefixes. 

3298 substring_matching = False 

3299 

3300 def __init__( 

3301 self: FuzzyEnum[t.Any], 

3302 values: t.Any, 

3303 default_value: t.Any = Undefined, 

3304 case_sensitive: bool = False, 

3305 substring_matching: bool = False, 

3306 **kwargs: t.Any, 

3307 ) -> None: 

3308 self.case_sensitive = case_sensitive 

3309 self.substring_matching = substring_matching 

3310 super().__init__(values, default_value=default_value, **kwargs) 

3311 

3312 def validate(self, obj: t.Any, value: t.Any) -> G: 

3313 if not isinstance(value, str): 

3314 self.error(obj, value) 

3315 

3316 conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower() 

3317 substring_matching = self.substring_matching 

3318 match_func = (lambda v, c: v in c) if substring_matching else (lambda v, c: c.startswith(v)) 

3319 value = conv_func(value) # type:ignore[no-untyped-call] 

3320 choices = self.values or [] 

3321 matches = [match_func(value, conv_func(c)) for c in choices] # type:ignore[no-untyped-call] 

3322 if sum(matches) == 1: 

3323 for v, m in zip(choices, matches): 

3324 if m: 

3325 return v 

3326 

3327 self.error(obj, value) 

3328 

3329 def _info(self, as_rst: bool = False) -> str: 

3330 """Returns a description of the trait.""" 

3331 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3332 case = "sensitive" if self.case_sensitive else "insensitive" 

3333 substr = "substring" if self.substring_matching else "prefix" 

3334 return f"any case-{case} {substr} of {self._choices_str(as_rst)}{none}" 

3335 

3336 def info(self) -> str: 

3337 return self._info(as_rst=False) 

3338 

3339 def info_rst(self) -> str: 

3340 return self._info(as_rst=True) 

3341 

3342 

3343class Container(Instance[T]): 

3344 """An instance of a container (list, set, etc.) 

3345 

3346 To be subclassed by overriding klass. 

3347 """ 

3348 

3349 klass: type[T] | None = None 

3350 _cast_types: t.Any = () 

3351 _valid_defaults = SequenceTypes 

3352 _trait: t.Any = None 

3353 _literal_from_string_pairs: t.Any = ("[]", "()") 

3354 

3355 @t.overload 

3356 def __init__( 

3357 self: Container[T], 

3358 *, 

3359 allow_none: Literal[False], 

3360 read_only: bool | None = ..., 

3361 help: str | None = ..., 

3362 config: t.Any | None = ..., 

3363 **kwargs: t.Any, 

3364 ) -> None: 

3365 ... 

3366 

3367 @t.overload 

3368 def __init__( 

3369 self: Container[T | None], 

3370 *, 

3371 allow_none: Literal[True], 

3372 read_only: bool | None = ..., 

3373 help: str | None = ..., 

3374 config: t.Any | None = ..., 

3375 **kwargs: t.Any, 

3376 ) -> None: 

3377 ... 

3378 

3379 @t.overload 

3380 def __init__( 

3381 self: Container[T], 

3382 *, 

3383 trait: t.Any = ..., 

3384 default_value: t.Any = ..., 

3385 help: str = ..., 

3386 read_only: bool = ..., 

3387 config: t.Any = ..., 

3388 **kwargs: t.Any, 

3389 ) -> None: 

3390 ... 

3391 

3392 def __init__( 

3393 self, 

3394 trait: t.Any | None = None, 

3395 default_value: t.Any = Undefined, 

3396 help: str | None = None, 

3397 read_only: bool | None = None, 

3398 config: t.Any | None = None, 

3399 **kwargs: t.Any, 

3400 ) -> None: 

3401 """Create a container trait type from a list, set, or tuple. 

3402 

3403 The default value is created by doing ``List(default_value)``, 

3404 which creates a copy of the ``default_value``. 

3405 

3406 ``trait`` can be specified, which restricts the type of elements 

3407 in the container to that TraitType. 

3408 

3409 If only one arg is given and it is not a Trait, it is taken as 

3410 ``default_value``: 

3411 

3412 ``c = List([1, 2, 3])`` 

3413 

3414 Parameters 

3415 ---------- 

3416 trait : TraitType [ optional ] 

3417 the type for restricting the contents of the Container. If unspecified, 

3418 types are not checked. 

3419 default_value : SequenceType [ optional ] 

3420 The default value for the Trait. Must be list/tuple/set, and 

3421 will be cast to the container type. 

3422 allow_none : bool [ default False ] 

3423 Whether to allow the value to be None 

3424 **kwargs : any 

3425 further keys for extensions to the Trait (e.g. config) 

3426 

3427 """ 

3428 

3429 # allow List([values]): 

3430 if trait is not None and default_value is Undefined and not is_trait(trait): 

3431 default_value = trait 

3432 trait = None 

3433 

3434 if default_value is None and not kwargs.get("allow_none", False): 

3435 # improve backward-compatibility for possible subclasses 

3436 # specifying default_value=None as default, 

3437 # keeping 'unspecified' behavior (i.e. empty container) 

3438 warn( 

3439 f"Specifying {self.__class__.__name__}(default_value=None)" 

3440 " for no default is deprecated in traitlets 5.0.5." 

3441 " Use default_value=Undefined", 

3442 DeprecationWarning, 

3443 stacklevel=2, 

3444 ) 

3445 default_value = Undefined 

3446 

3447 if default_value is Undefined: 

3448 args: t.Any = () 

3449 elif default_value is None: 

3450 # default_value back on kwargs for super() to handle 

3451 args = () 

3452 kwargs["default_value"] = None 

3453 elif isinstance(default_value, self._valid_defaults): 

3454 args = (default_value,) 

3455 else: 

3456 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3457 

3458 if is_trait(trait): 

3459 if isinstance(trait, type): 

3460 warn( 

3461 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)." 

3462 " Passing types is deprecated in traitlets 4.1.", 

3463 DeprecationWarning, 

3464 stacklevel=3, 

3465 ) 

3466 self._trait = trait() if isinstance(trait, type) else trait 

3467 elif trait is not None: 

3468 raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait)) 

3469 

3470 super().__init__( 

3471 klass=self.klass, args=args, help=help, read_only=read_only, config=config, **kwargs 

3472 ) 

3473 

3474 def validate(self, obj: t.Any, value: t.Any) -> T | None: 

3475 if isinstance(value, self._cast_types): 

3476 assert self.klass is not None 

3477 value = self.klass(value) # type:ignore[call-arg] 

3478 value = super().validate(obj, value) 

3479 if value is None: 

3480 return value 

3481 

3482 value = self.validate_elements(obj, value) 

3483 

3484 return t.cast(T, value) 

3485 

3486 def validate_elements(self, obj: t.Any, value: t.Any) -> T | None: 

3487 validated = [] 

3488 if self._trait is None or isinstance(self._trait, Any): 

3489 return t.cast(T, value) 

3490 for v in value: 

3491 try: 

3492 v = self._trait._validate(obj, v) 

3493 except TraitError as error: 

3494 self.error(obj, v, error) 

3495 else: 

3496 validated.append(v) 

3497 assert self.klass is not None 

3498 return self.klass(validated) # type:ignore[call-arg] 

3499 

3500 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3501 if isinstance(self._trait, TraitType): 

3502 self._trait.class_init(cls, None) 

3503 super().class_init(cls, name) 

3504 

3505 def subclass_init(self, cls: type[t.Any]) -> None: 

3506 if isinstance(self._trait, TraitType): 

3507 self._trait.subclass_init(cls) 

3508 # explicitly not calling super().subclass_init(cls) 

3509 # to opt out of instance_init 

3510 

3511 def from_string(self, s: str) -> T | None: 

3512 """Load value from a single string""" 

3513 if not isinstance(s, str): 

3514 raise TraitError(f"Expected string, got {s!r}") 

3515 try: 

3516 test = literal_eval(s) 

3517 except Exception: 

3518 test = None 

3519 return self.validate(None, test) 

3520 

3521 def from_string_list(self, s_list: list[str]) -> T | None: 

3522 """Return the value from a list of config strings 

3523 

3524 This is where we parse CLI configuration 

3525 """ 

3526 assert self.klass is not None 

3527 if len(s_list) == 1: 

3528 # check for deprecated --Class.trait="['a', 'b', 'c']" 

3529 r = s_list[0] 

3530 if r == "None" and self.allow_none: 

3531 return None 

3532 if len(r) >= 2 and any( 

3533 r.startswith(start) and r.endswith(end) 

3534 for start, end in self._literal_from_string_pairs 

3535 ): 

3536 if self.this_class: 

3537 clsname = self.this_class.__name__ + "." 

3538 else: 

3539 clsname = "" 

3540 assert self.name is not None 

3541 warn( 

3542 "--{0}={1} for containers is deprecated in traitlets 5.0. " 

3543 "You can pass `--{0} item` ... multiple times to add items to a list.".format( 

3544 clsname + self.name, r 

3545 ), 

3546 DeprecationWarning, 

3547 stacklevel=2, 

3548 ) 

3549 return self.klass(literal_eval(r)) # type:ignore[call-arg] 

3550 sig = inspect.signature(self.item_from_string) 

3551 if "index" in sig.parameters: 

3552 item_from_string = self.item_from_string 

3553 else: 

3554 # backward-compat: allow item_from_string to ignore index arg 

3555 def item_from_string(s: str, index: int | None = None) -> T | str: 

3556 return t.cast(T, self.item_from_string(s)) 

3557 

3558 return self.klass( # type:ignore[call-arg] 

3559 [item_from_string(s, index=idx) for idx, s in enumerate(s_list)] 

3560 ) 

3561 

3562 def item_from_string(self, s: str, index: int | None = None) -> T | str: 

3563 """Cast a single item from a string 

3564 

3565 Evaluated when parsing CLI configuration from a string 

3566 """ 

3567 if self._trait: 

3568 return t.cast(T, self._trait.from_string(s)) 

3569 else: 

3570 return s 

3571 

3572 

3573class List(Container[t.List[T]]): 

3574 """An instance of a Python list.""" 

3575 

3576 klass = list # type:ignore[assignment] 

3577 _cast_types: t.Any = (tuple,) 

3578 

3579 def __init__( 

3580 self, 

3581 trait: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | TraitType[T, t.Any] | None = None, 

3582 default_value: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | None = Undefined, 

3583 minlen: int = 0, 

3584 maxlen: int = sys.maxsize, 

3585 **kwargs: t.Any, 

3586 ) -> None: 

3587 """Create a List trait type from a list, set, or tuple. 

3588 

3589 The default value is created by doing ``list(default_value)``, 

3590 which creates a copy of the ``default_value``. 

3591 

3592 ``trait`` can be specified, which restricts the type of elements 

3593 in the container to that TraitType. 

3594 

3595 If only one arg is given and it is not a Trait, it is taken as 

3596 ``default_value``: 

3597 

3598 ``c = List([1, 2, 3])`` 

3599 

3600 Parameters 

3601 ---------- 

3602 trait : TraitType [ optional ] 

3603 the type for restricting the contents of the Container. 

3604 If unspecified, types are not checked. 

3605 default_value : SequenceType [ optional ] 

3606 The default value for the Trait. Must be list/tuple/set, and 

3607 will be cast to the container type. 

3608 minlen : Int [ default 0 ] 

3609 The minimum length of the input list 

3610 maxlen : Int [ default sys.maxsize ] 

3611 The maximum length of the input list 

3612 """ 

3613 self._maxlen = maxlen 

3614 self._minlen = minlen 

3615 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3616 

3617 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3618 e = ( 

3619 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3620 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3621 ) 

3622 raise TraitError(e) 

3623 

3624 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3625 length = len(value) 

3626 if length < self._minlen or length > self._maxlen: 

3627 self.length_error(obj, value) 

3628 

3629 return super().validate_elements(obj, value) 

3630 

3631 def set(self, obj: t.Any, value: t.Any) -> None: 

3632 if isinstance(value, str): 

3633 return super().set(obj, [value]) # type:ignore[list-item] 

3634 else: 

3635 return super().set(obj, value) 

3636 

3637 

3638class Set(Container[t.Set[t.Any]]): 

3639 """An instance of a Python set.""" 

3640 

3641 klass = set 

3642 _cast_types = (tuple, list) 

3643 

3644 _literal_from_string_pairs = ("[]", "()", "{}") 

3645 

3646 # Redefine __init__ just to make the docstring more accurate. 

3647 def __init__( 

3648 self, 

3649 trait: t.Any = None, 

3650 default_value: t.Any = Undefined, 

3651 minlen: int = 0, 

3652 maxlen: int = sys.maxsize, 

3653 **kwargs: t.Any, 

3654 ) -> None: 

3655 """Create a Set trait type from a list, set, or tuple. 

3656 

3657 The default value is created by doing ``set(default_value)``, 

3658 which creates a copy of the ``default_value``. 

3659 

3660 ``trait`` can be specified, which restricts the type of elements 

3661 in the container to that TraitType. 

3662 

3663 If only one arg is given and it is not a Trait, it is taken as 

3664 ``default_value``: 

3665 

3666 ``c = Set({1, 2, 3})`` 

3667 

3668 Parameters 

3669 ---------- 

3670 trait : TraitType [ optional ] 

3671 the type for restricting the contents of the Container. 

3672 If unspecified, types are not checked. 

3673 default_value : SequenceType [ optional ] 

3674 The default value for the Trait. Must be list/tuple/set, and 

3675 will be cast to the container type. 

3676 minlen : Int [ default 0 ] 

3677 The minimum length of the input list 

3678 maxlen : Int [ default sys.maxsize ] 

3679 The maximum length of the input list 

3680 """ 

3681 self._maxlen = maxlen 

3682 self._minlen = minlen 

3683 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3684 

3685 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3686 e = ( 

3687 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3688 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3689 ) 

3690 raise TraitError(e) 

3691 

3692 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3693 length = len(value) 

3694 if length < self._minlen or length > self._maxlen: 

3695 self.length_error(obj, value) 

3696 

3697 return super().validate_elements(obj, value) 

3698 

3699 def set(self, obj: t.Any, value: t.Any) -> None: 

3700 if isinstance(value, str): 

3701 return super().set(obj, {value}) 

3702 else: 

3703 return super().set(obj, value) 

3704 

3705 def default_value_repr(self) -> str: 

3706 # Ensure default value is sorted for a reproducible build 

3707 list_repr = repr(sorted(self.make_dynamic_default() or [])) 

3708 if list_repr == "[]": 

3709 return "set()" 

3710 return "{" + list_repr[1:-1] + "}" 

3711 

3712 

3713class Tuple(Container[t.Tuple[t.Any, ...]]): 

3714 """An instance of a Python tuple.""" 

3715 

3716 klass = tuple 

3717 _cast_types = (list,) 

3718 

3719 def __init__(self, *traits: t.Any, **kwargs: t.Any) -> None: 

3720 """Create a tuple from a list, set, or tuple. 

3721 

3722 Create a fixed-type tuple with Traits: 

3723 

3724 ``t = Tuple(Int(), Str(), CStr())`` 

3725 

3726 would be length 3, with Int,Str,CStr for each element. 

3727 

3728 If only one arg is given and it is not a Trait, it is taken as 

3729 default_value: 

3730 

3731 ``t = Tuple((1, 2, 3))`` 

3732 

3733 Otherwise, ``default_value`` *must* be specified by keyword. 

3734 

3735 Parameters 

3736 ---------- 

3737 *traits : TraitTypes [ optional ] 

3738 the types for restricting the contents of the Tuple. If unspecified, 

3739 types are not checked. If specified, then each positional argument 

3740 corresponds to an element of the tuple. Tuples defined with traits 

3741 are of fixed length. 

3742 default_value : SequenceType [ optional ] 

3743 The default value for the Tuple. Must be list/tuple/set, and 

3744 will be cast to a tuple. If ``traits`` are specified, 

3745 ``default_value`` must conform to the shape and type they specify. 

3746 **kwargs 

3747 Other kwargs passed to `Container` 

3748 """ 

3749 default_value = kwargs.pop("default_value", Undefined) 

3750 # allow Tuple((values,)): 

3751 if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]): 

3752 default_value = traits[0] 

3753 traits = () 

3754 

3755 if default_value is None and not kwargs.get("allow_none", False): 

3756 # improve backward-compatibility for possible subclasses 

3757 # specifying default_value=None as default, 

3758 # keeping 'unspecified' behavior (i.e. empty container) 

3759 warn( 

3760 f"Specifying {self.__class__.__name__}(default_value=None)" 

3761 " for no default is deprecated in traitlets 5.0.5." 

3762 " Use default_value=Undefined", 

3763 DeprecationWarning, 

3764 stacklevel=2, 

3765 ) 

3766 default_value = Undefined 

3767 

3768 if default_value is Undefined: 

3769 args: t.Any = () 

3770 elif default_value is None: 

3771 # default_value back on kwargs for super() to handle 

3772 args = () 

3773 kwargs["default_value"] = None 

3774 elif isinstance(default_value, self._valid_defaults): 

3775 args = (default_value,) 

3776 else: 

3777 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3778 

3779 self._traits = [] 

3780 for trait in traits: 

3781 if isinstance(trait, type): 

3782 warn( 

3783 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3784 " Passing types is deprecated in traitlets 4.1.", 

3785 DeprecationWarning, 

3786 stacklevel=2, 

3787 ) 

3788 trait = trait() 

3789 self._traits.append(trait) 

3790 

3791 if self._traits and (default_value is None or default_value is Undefined): 

3792 # don't allow default to be an empty container if length is specified 

3793 args = None 

3794 super(Container, self).__init__(klass=self.klass, args=args, **kwargs) 

3795 

3796 def item_from_string(self, s: str, index: int) -> t.Any: # type:ignore[override] 

3797 """Cast a single item from a string 

3798 

3799 Evaluated when parsing CLI configuration from a string 

3800 """ 

3801 if not self._traits or index >= len(self._traits): 

3802 # return s instead of raising index error 

3803 # length errors will be raised later on validation 

3804 return s 

3805 return self._traits[index].from_string(s) 

3806 

3807 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3808 if not self._traits: 

3809 # nothing to validate 

3810 return value 

3811 if len(value) != len(self._traits): 

3812 e = ( 

3813 "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." 

3814 % (self.name, class_of(obj), len(self._traits), repr_type(value)) 

3815 ) 

3816 raise TraitError(e) 

3817 

3818 validated = [] 

3819 for trait, v in zip(self._traits, value): 

3820 try: 

3821 v = trait._validate(obj, v) 

3822 except TraitError as error: 

3823 self.error(obj, v, error) 

3824 else: 

3825 validated.append(v) 

3826 return tuple(validated) 

3827 

3828 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3829 for trait in self._traits: 

3830 if isinstance(trait, TraitType): 

3831 trait.class_init(cls, None) 

3832 super(Container, self).class_init(cls, name) 

3833 

3834 def subclass_init(self, cls: type[t.Any]) -> None: 

3835 for trait in self._traits: 

3836 if isinstance(trait, TraitType): 

3837 trait.subclass_init(cls) 

3838 # explicitly not calling super().subclass_init(cls) 

3839 # to opt out of instance_init 

3840 

3841 

3842class Dict(Instance["dict[K, V]"]): 

3843 """An instance of a Python dict. 

3844 

3845 One or more traits can be passed to the constructor 

3846 to validate the keys and/or values of the dict. 

3847 If you need more detailed validation, 

3848 you may use a custom validator method. 

3849 

3850 .. versionchanged:: 5.0 

3851 Added key_trait for validating dict keys. 

3852 

3853 .. versionchanged:: 5.0 

3854 Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``. 

3855 """ 

3856 

3857 _value_trait = None 

3858 _key_trait = None 

3859 

3860 def __init__( 

3861 self, 

3862 value_trait: TraitType[t.Any, t.Any] | dict[K, V] | Sentinel | None = None, 

3863 per_key_traits: t.Any = None, 

3864 key_trait: TraitType[t.Any, t.Any] | None = None, 

3865 default_value: dict[K, V] | Sentinel | None = Undefined, 

3866 **kwargs: t.Any, 

3867 ) -> None: 

3868 """Create a dict trait type from a Python dict. 

3869 

3870 The default value is created by doing ``dict(default_value)``, 

3871 which creates a copy of the ``default_value``. 

3872 

3873 Parameters 

3874 ---------- 

3875 value_trait : TraitType [ optional ] 

3876 The specified trait type to check and use to restrict the values of 

3877 the dict. If unspecified, values are not checked. 

3878 per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ] 

3879 A Python dictionary containing the types that are valid for 

3880 restricting the values of the dict on a per-key basis. 

3881 Each value in this dict should be a Trait for validating 

3882 key_trait : TraitType [ optional, keyword-only ] 

3883 The type for restricting the keys of the dict. If 

3884 unspecified, the types of the keys are not checked. 

3885 default_value : SequenceType [ optional, keyword-only ] 

3886 The default value for the Dict. Must be dict, tuple, or None, and 

3887 will be cast to a dict if not None. If any key or value traits are specified, 

3888 the `default_value` must conform to the constraints. 

3889 

3890 Examples 

3891 -------- 

3892 a dict whose values must be text 

3893 >>> d = Dict(Unicode()) 

3894 

3895 d2['n'] must be an integer 

3896 d2['s'] must be text 

3897 >>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()}) 

3898 

3899 d3's keys must be text 

3900 d3's values must be integers 

3901 >>> d3 = Dict(value_trait=Integer(), key_trait=Unicode()) 

3902 

3903 """ 

3904 

3905 # handle deprecated keywords 

3906 trait = kwargs.pop("trait", None) 

3907 if trait is not None: 

3908 if value_trait is not None: 

3909 raise TypeError( 

3910 "Found a value for both `value_trait` and its deprecated alias `trait`." 

3911 ) 

3912 value_trait = trait 

3913 warn( 

3914 "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead", 

3915 DeprecationWarning, 

3916 stacklevel=2, 

3917 ) 

3918 traits = kwargs.pop("traits", None) 

3919 if traits is not None: 

3920 if per_key_traits is not None: 

3921 raise TypeError( 

3922 "Found a value for both `per_key_traits` and its deprecated alias `traits`." 

3923 ) 

3924 per_key_traits = traits 

3925 warn( 

3926 "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead", 

3927 DeprecationWarning, 

3928 stacklevel=2, 

3929 ) 

3930 

3931 # Handling positional arguments 

3932 if default_value is Undefined and value_trait is not None: 

3933 if not is_trait(value_trait): 

3934 assert not isinstance(value_trait, TraitType) 

3935 default_value = value_trait 

3936 value_trait = None 

3937 

3938 if key_trait is None and per_key_traits is not None: 

3939 if is_trait(per_key_traits): 

3940 key_trait = per_key_traits 

3941 per_key_traits = None 

3942 

3943 # Handling default value 

3944 if default_value is Undefined: 

3945 default_value = {} 

3946 if default_value is None: 

3947 args: t.Any = None 

3948 elif isinstance(default_value, dict): 

3949 args = (default_value,) 

3950 elif isinstance(default_value, SequenceTypes): 

3951 args = (default_value,) 

3952 else: 

3953 raise TypeError("default value of Dict was %s" % default_value) 

3954 

3955 # Case where a type of TraitType is provided rather than an instance 

3956 if is_trait(value_trait): 

3957 if isinstance(value_trait, type): 

3958 warn( # type:ignore[unreachable] 

3959 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3960 " Passing types is deprecated in traitlets 4.1.", 

3961 DeprecationWarning, 

3962 stacklevel=2, 

3963 ) 

3964 value_trait = value_trait() 

3965 self._value_trait = value_trait 

3966 elif value_trait is not None: 

3967 raise TypeError( 

3968 "`value_trait` must be a Trait or None, got %s" % repr_type(value_trait) 

3969 ) 

3970 

3971 if is_trait(key_trait): 

3972 if isinstance(key_trait, type): 

3973 warn( # type:ignore[unreachable] 

3974 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3975 " Passing types is deprecated in traitlets 4.1.", 

3976 DeprecationWarning, 

3977 stacklevel=2, 

3978 ) 

3979 key_trait = key_trait() 

3980 self._key_trait = key_trait 

3981 elif key_trait is not None: 

3982 raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait)) 

3983 

3984 self._per_key_traits = per_key_traits 

3985 

3986 super().__init__(klass=dict, args=args, **kwargs) 

3987 

3988 def element_error( 

3989 self, obj: t.Any, element: t.Any, validator: t.Any, side: str = "Values" 

3990 ) -> None: 

3991 e = ( 

3992 side 

3993 + f" of the '{self.name}' trait of {class_of(obj)} instance must be {validator.info()}, but a value of {repr_type(element)} was specified." 

3994 ) 

3995 raise TraitError(e) 

3996 

3997 def validate(self, obj: t.Any, value: t.Any) -> dict[K, V] | None: 

3998 value = super().validate(obj, value) 

3999 if value is None: 

4000 return value 

4001 return self.validate_elements(obj, value) 

4002 

4003 def validate_elements(self, obj: t.Any, value: dict[t.Any, t.Any]) -> dict[K, V] | None: 

4004 per_key_override = self._per_key_traits or {} 

4005 key_trait = self._key_trait 

4006 value_trait = self._value_trait 

4007 if not (key_trait or value_trait or per_key_override): 

4008 return value 

4009 

4010 validated = {} 

4011 for key in value: 

4012 v = value[key] 

4013 if key_trait: 

4014 try: 

4015 key = key_trait._validate(obj, key) 

4016 except TraitError: 

4017 self.element_error(obj, key, key_trait, "Keys") 

4018 active_value_trait = per_key_override.get(key, value_trait) 

4019 if active_value_trait: 

4020 try: 

4021 v = active_value_trait._validate(obj, v) 

4022 except TraitError: 

4023 self.element_error(obj, v, active_value_trait, "Values") 

4024 validated[key] = v 

4025 

4026 return self.klass(validated) # type:ignore[misc,operator] 

4027 

4028 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

4029 if isinstance(self._value_trait, TraitType): 

4030 self._value_trait.class_init(cls, None) 

4031 if isinstance(self._key_trait, TraitType): 

4032 self._key_trait.class_init(cls, None) 

4033 if self._per_key_traits is not None: 

4034 for trait in self._per_key_traits.values(): 

4035 trait.class_init(cls, None) 

4036 super().class_init(cls, name) 

4037 

4038 def subclass_init(self, cls: type[t.Any]) -> None: 

4039 if isinstance(self._value_trait, TraitType): 

4040 self._value_trait.subclass_init(cls) 

4041 if isinstance(self._key_trait, TraitType): 

4042 self._key_trait.subclass_init(cls) 

4043 if self._per_key_traits is not None: 

4044 for trait in self._per_key_traits.values(): 

4045 trait.subclass_init(cls) 

4046 # explicitly not calling super().subclass_init(cls) 

4047 # to opt out of instance_init 

4048 

4049 def from_string(self, s: str) -> dict[K, V] | None: 

4050 """Load value from a single string""" 

4051 if not isinstance(s, str): 

4052 raise TypeError(f"from_string expects a string, got {s!r} of type {type(s)}") 

4053 try: 

4054 return t.cast("dict[K, V]", self.from_string_list([s])) 

4055 except Exception: 

4056 test = _safe_literal_eval(s) 

4057 if isinstance(test, dict): 

4058 return test 

4059 raise 

4060 

4061 def from_string_list(self, s_list: list[str]) -> t.Any: 

4062 """Return a dict from a list of config strings. 

4063 

4064 This is where we parse CLI configuration. 

4065 

4066 Each item should have the form ``"key=value"``. 

4067 

4068 item parsing is done in :meth:`.item_from_string`. 

4069 """ 

4070 if len(s_list) == 1 and s_list[0] == "None" and self.allow_none: 

4071 return None 

4072 if len(s_list) == 1 and s_list[0].startswith("{") and s_list[0].endswith("}"): 

4073 warn( 

4074 f"--{self.name}={s_list[0]} for dict-traits is deprecated in traitlets 5.0. " 

4075 f"You can pass --{self.name} <key=value> ... multiple times to add items to a dict.", 

4076 DeprecationWarning, 

4077 stacklevel=2, 

4078 ) 

4079 

4080 return literal_eval(s_list[0]) 

4081 

4082 combined = {} 

4083 for d in [self.item_from_string(s) for s in s_list]: 

4084 combined.update(d) 

4085 return combined 

4086 

4087 def item_from_string(self, s: str) -> dict[K, V]: 

4088 """Cast a single-key dict from a string. 

4089 

4090 Evaluated when parsing CLI configuration from a string. 

4091 

4092 Dicts expect strings of the form key=value. 

4093 

4094 Returns a one-key dictionary, 

4095 which will be merged in :meth:`.from_string_list`. 

4096 """ 

4097 

4098 if "=" not in s: 

4099 raise TraitError( 

4100 f"'{self.__class__.__name__}' options must have the form 'key=value', got {s!r}" 

4101 ) 

4102 key, value = s.split("=", 1) 

4103 

4104 # cast key with key trait, if defined 

4105 if self._key_trait: 

4106 key = self._key_trait.from_string(key) 

4107 

4108 # cast value with value trait, if defined (per-key or global) 

4109 value_trait = (self._per_key_traits or {}).get(key, self._value_trait) 

4110 if value_trait: 

4111 value = value_trait.from_string(value) 

4112 return t.cast("dict[K, V]", {key: value}) 

4113 

4114 

4115class TCPAddress(TraitType[G, S]): 

4116 """A trait for an (ip, port) tuple. 

4117 

4118 This allows for both IPv4 IP addresses as well as hostnames. 

4119 """ 

4120 

4121 default_value = ("127.0.0.1", 0) 

4122 info_text = "an (ip, port) tuple" 

4123 

4124 if t.TYPE_CHECKING: 

4125 

4126 @t.overload 

4127 def __init__( 

4128 self: TCPAddress[tuple[str, int], tuple[str, int]], 

4129 default_value: bool | Sentinel = ..., 

4130 allow_none: Literal[False] = ..., 

4131 read_only: bool | None = ..., 

4132 help: str | None = ..., 

4133 config: t.Any = ..., 

4134 **kwargs: t.Any, 

4135 ) -> None: 

4136 ... 

4137 

4138 @t.overload 

4139 def __init__( 

4140 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None], 

4141 default_value: bool | None | Sentinel = ..., 

4142 allow_none: Literal[True] = ..., 

4143 read_only: bool | None = ..., 

4144 help: str | None = ..., 

4145 config: t.Any = ..., 

4146 **kwargs: t.Any, 

4147 ) -> None: 

4148 ... 

4149 

4150 def __init__( 

4151 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None] 

4152 | TCPAddress[tuple[str, int], tuple[str, int]], 

4153 default_value: bool | None | Sentinel = Undefined, 

4154 allow_none: Literal[True, False] = False, 

4155 read_only: bool | None = None, 

4156 help: str | None = None, 

4157 config: t.Any = None, 

4158 **kwargs: t.Any, 

4159 ) -> None: 

4160 ... 

4161 

4162 def validate(self, obj: t.Any, value: t.Any) -> G: 

4163 if isinstance(value, tuple): 

4164 if len(value) == 2: 

4165 if isinstance(value[0], str) and isinstance(value[1], int): 

4166 port = value[1] 

4167 if port >= 0 and port <= 65535: 

4168 return t.cast(G, value) 

4169 self.error(obj, value) 

4170 

4171 def from_string(self, s: str) -> G: 

4172 if self.allow_none and s == "None": 

4173 return t.cast(G, None) 

4174 if ":" not in s: 

4175 raise ValueError("Require `ip:port`, got %r" % s) 

4176 ip, port_str = s.split(":", 1) 

4177 port = int(port_str) 

4178 return t.cast(G, (ip, port)) 

4179 

4180 

4181class CRegExp(TraitType["re.Pattern[t.Any]", t.Union["re.Pattern[t.Any]", str]]): 

4182 """A casting compiled regular expression trait. 

4183 

4184 Accepts both strings and compiled regular expressions. The resulting 

4185 attribute will be a compiled regular expression.""" 

4186 

4187 info_text = "a regular expression" 

4188 

4189 def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None: 

4190 try: 

4191 return re.compile(value) 

4192 except Exception: 

4193 self.error(obj, value) 

4194 

4195 

4196class UseEnum(TraitType[t.Any, t.Any]): 

4197 """Use a Enum class as model for the data type description. 

4198 Note that if no default-value is provided, the first enum-value is used 

4199 as default-value. 

4200 

4201 .. sourcecode:: python 

4202 

4203 # -- SINCE: Python 3.4 (or install backport: pip install enum34) 

4204 import enum 

4205 from traitlets import HasTraits, UseEnum 

4206 

4207 

4208 class Color(enum.Enum): 

4209 red = 1 # -- IMPLICIT: default_value 

4210 blue = 2 

4211 green = 3 

4212 

4213 

4214 class MyEntity(HasTraits): 

4215 color = UseEnum(Color, default_value=Color.blue) 

4216 

4217 

4218 entity = MyEntity(color=Color.red) 

4219 entity.color = Color.green # USE: Enum-value (preferred) 

4220 entity.color = "green" # USE: name (as string) 

4221 entity.color = "Color.green" # USE: scoped-name (as string) 

4222 entity.color = 3 # USE: number (as int) 

4223 assert entity.color is Color.green 

4224 """ 

4225 

4226 default_value: enum.Enum | None = None 

4227 info_text = "Trait type adapter to a Enum class" 

4228 

4229 def __init__( 

4230 self, enum_class: type[t.Any], default_value: t.Any = None, **kwargs: t.Any 

4231 ) -> None: 

4232 assert issubclass(enum_class, enum.Enum), "REQUIRE: enum.Enum, but was: %r" % enum_class 

4233 allow_none = kwargs.get("allow_none", False) 

4234 if default_value is None and not allow_none: 

4235 default_value = next(iter(enum_class.__members__.values())) 

4236 super().__init__(default_value=default_value, **kwargs) 

4237 self.enum_class = enum_class 

4238 self.name_prefix = enum_class.__name__ + "." 

4239 

4240 def select_by_number(self, value: int, default: t.Any = Undefined) -> t.Any: 

4241 """Selects enum-value by using its number-constant.""" 

4242 assert isinstance(value, int) 

4243 enum_members = self.enum_class.__members__ 

4244 for enum_item in enum_members.values(): 

4245 if enum_item.value == value: 

4246 return enum_item 

4247 # -- NOT FOUND: 

4248 return default 

4249 

4250 def select_by_name(self, value: str, default: t.Any = Undefined) -> t.Any: 

4251 """Selects enum-value by using its name or scoped-name.""" 

4252 assert isinstance(value, str) 

4253 if value.startswith(self.name_prefix): 

4254 # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" 

4255 value = value.replace(self.name_prefix, "", 1) 

4256 return self.enum_class.__members__.get(value, default) 

4257 

4258 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4259 if isinstance(value, self.enum_class): 

4260 return value 

4261 elif isinstance(value, int): 

4262 # -- CONVERT: number => enum_value (item) 

4263 value2 = self.select_by_number(value) 

4264 if value2 is not Undefined: 

4265 return value2 

4266 elif isinstance(value, str): 

4267 # -- CONVERT: name or scoped_name (as string) => enum_value (item) 

4268 value2 = self.select_by_name(value) 

4269 if value2 is not Undefined: 

4270 return value2 

4271 elif value is None: 

4272 if self.allow_none: 

4273 return None 

4274 else: 

4275 return self.default_value 

4276 self.error(obj, value) 

4277 

4278 def _choices_str(self, as_rst: bool = False) -> str: 

4279 """Returns a description of the trait choices (not none).""" 

4280 choices = self.enum_class.__members__.keys() 

4281 if as_rst: 

4282 return "|".join("``%r``" % x for x in choices) 

4283 else: 

4284 return repr(list(choices)) # Listify because py3.4- prints odict-class 

4285 

4286 def _info(self, as_rst: bool = False) -> str: 

4287 """Returns a description of the trait.""" 

4288 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

4289 return f"any of {self._choices_str(as_rst)}{none}" 

4290 

4291 def info(self) -> str: 

4292 return self._info(as_rst=False) 

4293 

4294 def info_rst(self) -> str: 

4295 return self._info(as_rst=True) 

4296 

4297 

4298class Callable(TraitType[t.Callable[..., t.Any], t.Callable[..., t.Any]]): 

4299 """A trait which is callable. 

4300 

4301 Notes 

4302 ----- 

4303 Classes are callable, as are instances 

4304 with a __call__() method.""" 

4305 

4306 info_text = "a callable" 

4307 

4308 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4309 if callable(value): 

4310 return value 

4311 else: 

4312 self.error(obj, value)