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

1739 statements  

« prev     ^ index     » next       coverage.py v7.3.3, created at 2023-12-15 06:13 +0000

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 pass 

480 

481 

482G = TypeVar("G") 

483S = TypeVar("S") 

484T = TypeVar("T") 

485 

486 

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

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

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

490if t.TYPE_CHECKING: 

491 from typing_extensions import Literal, Self 

492 

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

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

495 

496 

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

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

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

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

501 

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

503 allow_none: bool = False 

504 read_only: bool = False 

505 info_text: str = "any value" 

506 default_value: t.Any = Undefined 

507 

508 def __init__( 

509 self: TraitType[G, S], 

510 default_value: t.Any = Undefined, 

511 allow_none: bool = False, 

512 read_only: bool | None = None, 

513 help: str | None = None, 

514 config: t.Any = None, 

515 **kwargs: t.Any, 

516 ) -> None: 

517 """Declare a traitlet. 

518 

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

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

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

522 

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

524 

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

526 

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

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

529 """ 

530 if default_value is not Undefined: 

531 self.default_value = default_value 

532 if allow_none: 

533 self.allow_none = allow_none 

534 if read_only is not None: 

535 self.read_only = read_only 

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

537 if self.help: 

538 # define __doc__ so that inspectors like autodoc find traits 

539 self.__doc__ = self.help 

540 

541 if len(kwargs) > 0: 

542 stacklevel = 1 

543 f = inspect.currentframe() 

544 # count supers to determine stacklevel for warning 

545 assert f is not None 

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

547 stacklevel += 1 

548 f = f.f_back 

549 assert f is not None 

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

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

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

553 if should_warn(key): 

554 warn( 

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

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

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

558 DeprecationWarning, 

559 stacklevel=stacklevel, 

560 ) 

561 if len(self.metadata) > 0: 

562 self.metadata = self.metadata.copy() 

563 self.metadata.update(kwargs) 

564 else: 

565 self.metadata = kwargs 

566 else: 

567 self.metadata = self.metadata.copy() 

568 if config is not None: 

569 self.metadata["config"] = config 

570 

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

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

573 if help is not None: 

574 self.metadata["help"] = help 

575 

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

577 """Get a value from a config string 

578 

579 such as an environment variable or CLI arguments. 

580 

581 Traits can override this method to define their own 

582 parsing of config strings. 

583 

584 .. seealso:: item_from_string 

585 

586 .. versionadded:: 5.0 

587 """ 

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

589 return None 

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

591 

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

593 """The default generator for this trait 

594 

595 Notes 

596 ----- 

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

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

599 """ 

600 if self.default_value is not Undefined: 

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

602 elif hasattr(self, "make_dynamic_default"): 

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

604 else: 

605 # Undefined will raise in TraitType.get 

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

607 

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

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

610 Use self.default_value instead 

611 """ 

612 warn( 

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

614 DeprecationWarning, 

615 stacklevel=2, 

616 ) 

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

618 

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

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

621 warn( 

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

623 DeprecationWarning, 

624 stacklevel=2, 

625 ) 

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

627 obj._trait_values[self.name] = value 

628 return value 

629 

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

631 assert self.name is not None 

632 try: 

633 value = obj._trait_values[self.name] 

634 except KeyError: 

635 # Check for a dynamic initializer. 

636 default = obj.trait_defaults(self.name) 

637 if default is Undefined: 

638 warn( 

639 "Explicit using of Undefined as the default value " 

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

641 "exceptions in the future.", 

642 DeprecationWarning, 

643 stacklevel=2, 

644 ) 

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

646 # write out the obj.cross_validation_lock call here. 

647 _cross_validation_lock = obj._cross_validation_lock 

648 try: 

649 obj._cross_validation_lock = True 

650 value = self._validate(obj, default) 

651 finally: 

652 obj._cross_validation_lock = _cross_validation_lock 

653 obj._trait_values[self.name] = value 

654 obj._notify_observers( 

655 Bunch( 

656 name=self.name, 

657 value=value, 

658 owner=obj, 

659 type="default", 

660 ) 

661 ) 

662 return t.cast(G, value) 

663 except Exception as e: 

664 # This should never be reached. 

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

666 else: 

667 return t.cast(G, value) 

668 

669 @t.overload 

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

671 ... 

672 

673 @t.overload 

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

675 ... 

676 

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

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

679 

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

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

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

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

684 """ 

685 if obj is None: 

686 return self 

687 else: 

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

689 

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

691 new_value = self._validate(obj, value) 

692 assert self.name is not None 

693 try: 

694 old_value = obj._trait_values[self.name] 

695 except KeyError: 

696 old_value = self.default_value 

697 

698 obj._trait_values[self.name] = new_value 

699 try: 

700 silent = bool(old_value == new_value) 

701 except Exception: 

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

703 silent = False 

704 if silent is not True: 

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

706 # comparison above returns something other than True/False 

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

708 

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

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

711 

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

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

714 """ 

715 if self.read_only: 

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

717 else: 

718 self.set(obj, value) 

719 

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

721 if value is None and self.allow_none: 

722 return value 

723 if hasattr(self, "validate"): 

724 value = self.validate(obj, value) 

725 if obj._cross_validation_lock is False: 

726 value = self._cross_validate(obj, value) 

727 return t.cast(G, value) 

728 

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

730 if self.name in obj._trait_validators: 

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

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

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

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

735 cross_validate = getattr(obj, meth_name) 

736 deprecated_method( 

737 cross_validate, 

738 obj.__class__, 

739 meth_name, 

740 "use @validate decorator instead.", 

741 ) 

742 value = cross_validate(value, self) 

743 return t.cast(G, value) 

744 

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

746 if isinstance(other, Union): 

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

748 else: 

749 return Union([self, other]) 

750 

751 def info(self) -> str: 

752 return self.info_text 

753 

754 def error( 

755 self, 

756 obj: HasTraits | None, 

757 value: t.Any, 

758 error: Exception | None = None, 

759 info: str | None = None, 

760 ) -> t.NoReturn: 

761 """Raise a TraitError 

762 

763 Parameters 

764 ---------- 

765 obj : HasTraits or None 

766 The instance which owns the trait. If not 

767 object is given, then an object agnostic 

768 error will be raised. 

769 value : any 

770 The value that caused the error. 

771 error : Exception (default: None) 

772 An error that was raised by a child trait. 

773 The arguments of this exception should be 

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

775 Where the ``value`` and ``info`` are the 

776 problem value, and string describing the 

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

778 of :class:`TraitType` instances that are 

779 "children" of this one (the first being 

780 the deepest). 

781 info : str (default: None) 

782 A description of the expected value. By 

783 default this is inferred from this trait's 

784 ``info`` method. 

785 """ 

786 if error is not None: 

787 # handle nested error 

788 error.args += (self,) 

789 if self.name is not None: 

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

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

792 if obj is not None: 

793 error.args = ( 

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

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

796 self.name, 

797 describe("an", obj), 

798 chain, 

799 error.args[1], 

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

801 ), 

802 ) 

803 else: 

804 error.args = ( 

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

806 self.name, 

807 chain, 

808 error.args[1], 

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

810 ), 

811 ) 

812 raise error 

813 else: 

814 # this trait caused an error 

815 if self.name is None: 

816 # this is not the root trait 

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

818 else: 

819 # this is the root trait 

820 if obj is not None: 

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

822 self.name, 

823 class_of(obj), 

824 info or self.info(), 

825 describe("the", value), 

826 ) 

827 else: 

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

829 self.name, 

830 info or self.info(), 

831 describe("the", value), 

832 ) 

833 raise TraitError(e) 

834 

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

836 """DEPRECATED: Get a metadata value. 

837 

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

839 """ 

840 if key == "help": 

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

842 else: 

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

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

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

846 

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

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

849 

850 Use .metadata[key] = value instead. 

851 """ 

852 if key == "help": 

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

854 else: 

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

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

857 self.metadata[key] = value 

858 

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

860 """Sets metadata and returns self. 

861 

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

863 

864 Examples 

865 -------- 

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

867 <traitlets.traitlets.Int object at ...> 

868 

869 """ 

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

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

872 ) 

873 if maybe_constructor_keywords: 

874 warn( 

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

876 % maybe_constructor_keywords, 

877 UserWarning, 

878 stacklevel=2, 

879 ) 

880 

881 self.metadata.update(metadata) 

882 return self 

883 

884 def default_value_repr(self) -> str: 

885 return repr(self.default_value) 

886 

887 

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

889# The HasTraits implementation 

890# ----------------------------------------------------------------------------- 

891 

892 

893class _CallbackWrapper: 

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

895 

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

897 callbacks. 

898 """ 

899 

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

901 self.cb = cb 

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

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

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

905 if self.nargs > 4: 

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

907 

908 def __eq__(self, other: t.Any) -> bool: 

909 # The wrapper is equal to the wrapped element 

910 if isinstance(other, _CallbackWrapper): 

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

912 else: 

913 return bool(self.cb == other) 

914 

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

916 # The wrapper is callable 

917 if self.nargs == 0: 

918 self.cb() 

919 elif self.nargs == 1: 

920 self.cb(change.name) 

921 elif self.nargs == 2: 

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

923 elif self.nargs == 3: 

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

925 elif self.nargs == 4: 

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

927 

928 

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

930 if isinstance(cb, _CallbackWrapper): 

931 return cb 

932 else: 

933 return _CallbackWrapper(cb) 

934 

935 

936class MetaHasDescriptors(type): 

937 """A metaclass for HasDescriptors. 

938 

939 This metaclass makes sure that any TraitType class attributes are 

940 instantiated and sets their name attribute. 

941 """ 

942 

943 def __new__( 

944 mcls: type[MetaHasDescriptors], # noqa: N804 

945 name: str, 

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

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

948 **kwds: t.Any, 

949 ) -> MetaHasDescriptors: 

950 """Create the HasDescriptors class.""" 

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

952 # ---------------------------------------------------------------- 

953 # Support of deprecated behavior allowing for TraitType types 

954 # to be used instead of TraitType instances. 

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

956 warn( 

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

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

959 DeprecationWarning, 

960 stacklevel=2, 

961 ) 

962 classdict[k] = v() 

963 # ---------------------------------------------------------------- 

964 

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

966 

967 def __init__( 

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

969 ) -> None: 

970 """Finish initializing the HasDescriptors class.""" 

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

972 cls.setup_class(classdict) 

973 

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

975 """Setup descriptor instance on the class 

976 

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

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

979 calling their :attr:`class_init` method. 

980 """ 

981 cls._descriptors = [] 

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

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

984 if isinstance(v, BaseDescriptor): 

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

986 

987 for _, v in getmembers(cls): 

988 if isinstance(v, BaseDescriptor): 

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

990 cls._descriptors.append(v) 

991 

992 

993class MetaHasTraits(MetaHasDescriptors): 

994 """A metaclass for HasTraits.""" 

995 

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

997 # for only the current class 

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

999 # also looking at base classes 

1000 cls._all_trait_default_generators = {} 

1001 cls._traits = {} 

1002 cls._static_immutable_initial_values = {} 

1003 

1004 super().setup_class(classdict) 

1005 

1006 mro = cls.mro() 

1007 

1008 for name in dir(cls): 

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

1010 # __provides__ attributes even though they exist. This causes 

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

1012 try: 

1013 value = getattr(cls, name) 

1014 except AttributeError: 

1015 continue 

1016 if isinstance(value, TraitType): 

1017 cls._traits[name] = value 

1018 trait = value 

1019 default_method_name = "_%s_default" % name 

1020 mro_trait = mro 

1021 try: 

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

1023 except ValueError: 

1024 # this_class not in mro 

1025 pass 

1026 for c in mro_trait: 

1027 if default_method_name in c.__dict__: 

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

1029 break 

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

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

1032 break 

1033 else: 

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

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

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

1037 # of initial values to speed up instance creation. 

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

1039 # for instance ipywidgets. 

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

1041 if ( 

1042 type(trait) in [CInt, Int] 

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

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

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

1046 ): 

1047 cls._static_immutable_initial_values[name] = trait.default_value 

1048 elif ( 

1049 type(trait) in [CFloat, Float] 

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

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

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

1053 ): 

1054 cls._static_immutable_initial_values[name] = trait.default_value 

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

1056 isinstance(trait.default_value, bool) or none_ok 

1057 ): 

1058 cls._static_immutable_initial_values[name] = trait.default_value 

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

1060 isinstance(trait.default_value, str) or none_ok 

1061 ): 

1062 cls._static_immutable_initial_values[name] = trait.default_value 

1063 elif type(trait) == Any and ( 

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

1065 ): 

1066 cls._static_immutable_initial_values[name] = trait.default_value 

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

1068 cls._static_immutable_initial_values[name] = None 

1069 elif ( 

1070 isinstance(trait, Instance) 

1071 and trait.default_args is None 

1072 and trait.default_kwargs is None 

1073 and trait.allow_none 

1074 ): 

1075 cls._static_immutable_initial_values[name] = None 

1076 

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

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

1079 cls._all_trait_default_generators[name] = trait.default 

1080 

1081 

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

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

1084 

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

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

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

1088 name of the attribute that triggered the notification. 

1089 

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

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

1092 * ``owner`` : the HasTraits instance 

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

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

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

1096 

1097 Parameters 

1098 ---------- 

1099 *names 

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

1101 type : str, kwarg-only 

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

1103 """ 

1104 if not names: 

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

1106 for name in names: 

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

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

1109 return ObserveHandler(names, type=type) 

1110 

1111 

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

1113 """Backward-compatibility shim decorator for observers 

1114 

1115 Use with: 

1116 

1117 @observe('name') 

1118 @observe_compat 

1119 def _foo_changed(self, change): 

1120 ... 

1121 

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

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

1124 """ 

1125 

1126 def compatible_observer( 

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

1128 ) -> t.Any: 

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

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

1131 else: 

1132 clsname = self.__class__.__name__ 

1133 warn( 

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

1135 DeprecationWarning, 

1136 stacklevel=2, 

1137 ) 

1138 change = Bunch( 

1139 type="change", 

1140 old=old, 

1141 new=new, 

1142 name=change_or_name, 

1143 owner=self, 

1144 ) 

1145 return func(self, change) 

1146 

1147 return t.cast(FuncT, compatible_observer) 

1148 

1149 

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

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

1152 when a Trait is set. 

1153 

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

1155 The proposal dictionary must hold the following keys: 

1156 

1157 * ``owner`` : the HasTraits instance 

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

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

1160 

1161 Parameters 

1162 ---------- 

1163 *names 

1164 The str names of the Traits to validate. 

1165 

1166 Notes 

1167 ----- 

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

1169 the registered cross validator could potentially make changes to attributes 

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

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

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

1173 commute. 

1174 """ 

1175 if not names: 

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

1177 for name in names: 

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

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

1180 return ValidateHandler(names) 

1181 

1182 

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

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

1185 

1186 Parameters 

1187 ---------- 

1188 name 

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

1190 

1191 Notes 

1192 ----- 

1193 Unlike observers and validators which are properties of the HasTraits 

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

1195 

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

1197 subclasses of `this_type`. 

1198 

1199 :: 

1200 

1201 class A(HasTraits): 

1202 bar = Int() 

1203 

1204 @default('bar') 

1205 def get_bar_default(self): 

1206 return 11 

1207 

1208 class B(A): 

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

1210 # the base class A 

1211 

1212 class C(B): 

1213 

1214 @default('bar') 

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

1216 return 3.0 # ignored since it is defined in a 

1217 # class derived from B.a.this_class. 

1218 """ 

1219 if not isinstance(name, str): 

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

1221 return DefaultHandler(name) 

1222 

1223 

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

1225 

1226 

1227class EventHandler(BaseDescriptor): 

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

1229 self.func = func 

1230 return self 

1231 

1232 @t.overload 

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

1234 ... 

1235 

1236 @t.overload 

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

1238 ... 

1239 

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

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

1242 if hasattr(self, "func"): 

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

1244 else: 

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

1246 

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

1248 if inst is None: 

1249 return self 

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

1251 

1252 

1253class ObserveHandler(EventHandler): 

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

1255 self.trait_names = names 

1256 self.type = type 

1257 

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

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

1260 

1261 

1262class ValidateHandler(EventHandler): 

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

1264 self.trait_names = names 

1265 

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

1267 inst._register_validator(self, self.trait_names) 

1268 

1269 

1270class DefaultHandler(EventHandler): 

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

1272 self.trait_name = name 

1273 

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

1275 super().class_init(cls, name) 

1276 cls._trait_default_generators[self.trait_name] = self 

1277 

1278 

1279class HasDescriptors(metaclass=MetaHasDescriptors): 

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

1281 

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

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

1284 cls = args[0] 

1285 args = args[1:] 

1286 

1287 # This is needed because object.__new__ only accepts 

1288 # the cls argument. 

1289 new_meth = super(HasDescriptors, cls).__new__ 

1290 if new_meth is object.__new__: 

1291 inst = new_meth(cls) 

1292 else: 

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

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

1295 return inst 

1296 

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

1298 """ 

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

1300 """ 

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

1302 self = args[0] 

1303 args = args[1:] 

1304 

1305 self._cross_validation_lock = False 

1306 cls = self.__class__ 

1307 # Let descriptors performance initialization when a HasDescriptor 

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

1309 # default creations or other bookkeepings. 

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

1311 # subclass_init. 

1312 for init in cls._instance_inits: 

1313 init(self) 

1314 

1315 

1316class HasTraits(HasDescriptors, metaclass=MetaHasTraits): 

1317 _trait_values: dict[str, t.Any] 

1318 _static_immutable_initial_values: dict[str, t.Any] 

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

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

1321 _cross_validation_lock: bool 

1322 _traits: dict[str, t.Any] 

1323 _all_trait_default_generators: dict[str, t.Any] 

1324 

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

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

1327 self = args[0] 

1328 args = args[1:] 

1329 

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

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

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

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

1334 self._trait_notifiers = {} 

1335 self._trait_validators = {} 

1336 self._cross_validation_lock = False 

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

1338 

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

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

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

1342 # notifications. 

1343 super_args = args 

1344 super_kwargs = {} 

1345 

1346 if kwargs: 

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

1348 # the hold_trait_notifications(self) context manager 

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

1350 pass 

1351 

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

1353 self._cross_validation_lock = True 

1354 changes = {} 

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

1356 if self.has_trait(key): 

1357 setattr(self, key, value) 

1358 changes[key] = Bunch( 

1359 name=key, 

1360 old=None, 

1361 new=value, 

1362 owner=self, 

1363 type="change", 

1364 ) 

1365 else: 

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

1367 super_kwargs[key] = value 

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

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

1370 for key in changed: 

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

1372 self.set_trait(key, value) 

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

1374 self._cross_validation_lock = False 

1375 # Restore method retrieval from class 

1376 del self.notify_change 

1377 for key in changed: 

1378 self.notify_change(changes[key]) 

1379 

1380 try: 

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

1382 except TypeError as e: 

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

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

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

1386 arg_s = ", ".join(arg_s_list) 

1387 warn( 

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

1389 "{error}\n" 

1390 "This is deprecated in traitlets 4.2." 

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

1392 arg_s=arg_s, 

1393 classname=self.__class__.__name__, 

1394 error=e, 

1395 ), 

1396 DeprecationWarning, 

1397 stacklevel=2, 

1398 ) 

1399 

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

1401 d = self.__dict__.copy() 

1402 # event handlers stored on an instance are 

1403 # expected to be reinstantiated during a 

1404 # recall of instance_init during __setstate__ 

1405 d["_trait_notifiers"] = {} 

1406 d["_trait_validators"] = {} 

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

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

1409 

1410 return d 

1411 

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

1413 self.__dict__ = state.copy() 

1414 

1415 # event handlers are reassigned to self 

1416 cls = self.__class__ 

1417 for key in dir(cls): 

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

1419 # __provides__ attributes even though they exist. This causes 

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

1421 try: 

1422 value = getattr(cls, key) 

1423 except AttributeError: 

1424 pass 

1425 else: 

1426 if isinstance(value, EventHandler): 

1427 value.instance_init(self) 

1428 

1429 @property 

1430 @contextlib.contextmanager 

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

1432 """ 

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

1434 to True. 

1435 

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

1437 prior to entering the block. 

1438 """ 

1439 if self._cross_validation_lock: 

1440 yield 

1441 return 

1442 else: 

1443 try: 

1444 self._cross_validation_lock = True 

1445 yield 

1446 finally: 

1447 self._cross_validation_lock = False 

1448 

1449 @contextlib.contextmanager 

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

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

1452 validation. 

1453 

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

1455 race conditions in trait notifiers requesting other trait values. 

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

1457 """ 

1458 if self._cross_validation_lock: 

1459 yield 

1460 return 

1461 else: 

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

1463 

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

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

1466 if past_changes is None: 

1467 return [change] 

1468 else: 

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

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

1471 else: 

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

1473 past_changes.append(change) 

1474 return past_changes 

1475 

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

1477 name = change.name 

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

1479 

1480 try: 

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

1482 # notifications, disable cross validation and yield. 

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

1484 self._cross_validation_lock = True 

1485 yield 

1486 # Cross validate final values when context is released. 

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

1488 trait = getattr(self.__class__, name) 

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

1490 self.set_trait(name, value) 

1491 except TraitError as e: 

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

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

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

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

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

1497 if change.type == "change": 

1498 if change.old is not Undefined: 

1499 self.set_trait(name, change.old) 

1500 else: 

1501 self._trait_values.pop(name) 

1502 cache = {} 

1503 raise e 

1504 finally: 

1505 self._cross_validation_lock = False 

1506 # Restore method retrieval from class 

1507 del self.notify_change 

1508 

1509 # trigger delayed notifications 

1510 for changes in cache.values(): 

1511 for change in changes: 

1512 self.notify_change(change) 

1513 

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

1515 self.notify_change( 

1516 Bunch( 

1517 name=name, 

1518 old=old_value, 

1519 new=new_value, 

1520 owner=self, 

1521 type="change", 

1522 ) 

1523 ) 

1524 

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

1526 """Notify observers of a change event""" 

1527 return self._notify_observers(change) 

1528 

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

1530 """Notify observers of any event""" 

1531 if not isinstance(event, Bunch): 

1532 # cast to bunch if given a dict 

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

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

1535 

1536 callables = [] 

1537 if name in self._trait_notifiers: 

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

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

1540 if All in self._trait_notifiers: 

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

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

1543 

1544 # Now static ones 

1545 magic_name = "_%s_changed" % name 

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

1547 class_value = getattr(self.__class__, magic_name) 

1548 if not isinstance(class_value, ObserveHandler): 

1549 deprecated_method( 

1550 class_value, 

1551 self.__class__, 

1552 magic_name, 

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

1554 ) 

1555 cb = getattr(self, magic_name) 

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

1557 if cb not in callables: 

1558 callables.append(_callback_wrapper(cb)) 

1559 

1560 # Call them all now 

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

1562 for c in callables: 

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

1564 

1565 if isinstance(c, _CallbackWrapper): 

1566 c = c.__call__ 

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

1568 c = getattr(self, c.name) 

1569 

1570 c(event) 

1571 

1572 def _add_notifiers( 

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

1574 ) -> None: 

1575 if name not in self._trait_notifiers: 

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

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

1578 else: 

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

1580 nlist = [] 

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

1582 else: 

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

1584 if handler not in nlist: 

1585 nlist.append(handler) 

1586 

1587 def _remove_notifiers( 

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

1589 ) -> None: 

1590 try: 

1591 if handler is None: 

1592 del self._trait_notifiers[name][type] 

1593 else: 

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

1595 except KeyError: 

1596 pass 

1597 

1598 def on_trait_change( 

1599 self, 

1600 handler: EventHandler | None = None, 

1601 name: Sentinel | str | None = None, 

1602 remove: bool = False, 

1603 ) -> None: 

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

1605 

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

1607 

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

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

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

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

1612 below). 

1613 

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

1615 handlers for the specified name are uninstalled. 

1616 

1617 Parameters 

1618 ---------- 

1619 handler : callable, None 

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

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

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

1623 name : list, str, None 

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

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

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

1627 remove : bool 

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

1629 then unintall it. 

1630 """ 

1631 warn( 

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

1633 DeprecationWarning, 

1634 stacklevel=2, 

1635 ) 

1636 if name is None: 

1637 name = All 

1638 if remove: 

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

1640 else: 

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

1642 

1643 def observe( 

1644 self, 

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

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

1647 type: Sentinel | str = "change", 

1648 ) -> None: 

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

1650 

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

1652 

1653 Parameters 

1654 ---------- 

1655 handler : callable 

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

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

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

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

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

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

1662 * ``owner`` : the HasTraits instance 

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

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

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

1666 names : list, str, All 

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

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

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

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

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

1672 notifications are passed to the observe handler. 

1673 """ 

1674 for name in parse_notifier_name(names): 

1675 self._add_notifiers(handler, name, type) 

1676 

1677 def unobserve( 

1678 self, 

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

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

1681 type: Sentinel | str = "change", 

1682 ) -> None: 

1683 """Remove a trait change handler. 

1684 

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

1686 

1687 Parameters 

1688 ---------- 

1689 handler : callable 

1690 The callable called when a trait attribute changes. 

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

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

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

1694 from the list of notifiers corresponding to all changes. 

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

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

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

1698 """ 

1699 for name in parse_notifier_name(names): 

1700 self._remove_notifiers(handler, name, type) 

1701 

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

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

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

1705 if name is All: 

1706 self._trait_notifiers = {} 

1707 else: 

1708 try: 

1709 del self._trait_notifiers[name] 

1710 except KeyError: 

1711 pass 

1712 

1713 def _register_validator( 

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

1715 ) -> None: 

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

1717 

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

1719 

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

1721 TraitError is raised and no new validator is registered. 

1722 

1723 Parameters 

1724 ---------- 

1725 handler : callable 

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

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

1728 with the following attributes/keys: 

1729 * ``owner`` : the HasTraits instance 

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

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

1732 names : List of strings 

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

1734 """ 

1735 for name in names: 

1736 magic_name = "_%s_validate" % name 

1737 if hasattr(self, magic_name): 

1738 class_value = getattr(self.__class__, magic_name) 

1739 if not isinstance(class_value, ValidateHandler): 

1740 deprecated_method( 

1741 class_value, 

1742 self.__class__, 

1743 magic_name, 

1744 "use @validate decorator instead.", 

1745 ) 

1746 for name in names: 

1747 self._trait_validators[name] = handler 

1748 

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

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

1751 cls = self.__class__ 

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

1753 if hasattr(cls, "__qualname__"): 

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

1755 attrs["__qualname__"] = cls.__qualname__ 

1756 attrs.update(traits) 

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

1758 for trait in traits.values(): 

1759 trait.instance_init(self) 

1760 

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

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

1763 cls = self.__class__ 

1764 if not self.has_trait(name): 

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

1766 else: 

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

1768 

1769 @classmethod 

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

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

1772 

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

1774 but is unbound. 

1775 """ 

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

1777 

1778 @classmethod 

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

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

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

1782 

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

1784 

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

1786 that the various HasTrait's instances are holding. 

1787 

1788 The metadata kwargs allow functions to be passed in which 

1789 filter traits based on metadata values. The functions should 

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

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

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

1793 to the function. 

1794 """ 

1795 traits = cls._traits.copy() 

1796 

1797 if len(metadata) == 0: 

1798 return traits 

1799 

1800 result = {} 

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

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

1803 if not callable(meta_eval): 

1804 meta_eval = _SimpleTest(meta_eval) 

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

1806 break 

1807 else: 

1808 result[name] = trait 

1809 

1810 return result 

1811 

1812 @classmethod 

1813 def class_own_traits( 

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

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

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

1817 

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

1819 """ 

1820 sup = super(cls, cls) 

1821 return { 

1822 n: t 

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

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

1825 } 

1826 

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

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

1829 return name in self._traits 

1830 

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

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

1833 

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

1835 dynamically generated default value. These default values 

1836 will be recognized as existing only after they have been 

1837 generated. 

1838 

1839 Example 

1840 

1841 .. code-block:: python 

1842 

1843 class MyClass(HasTraits): 

1844 i = Int() 

1845 

1846 

1847 mc = MyClass() 

1848 assert not mc.trait_has_value("i") 

1849 mc.i # generates a default value 

1850 assert mc.trait_has_value("i") 

1851 """ 

1852 return name in self._trait_values 

1853 

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

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

1856 

1857 The metadata kwargs allow functions to be passed in which 

1858 filter traits based on metadata values. The functions should 

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

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

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

1862 to the function. 

1863 

1864 Returns 

1865 ------- 

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

1867 

1868 Notes 

1869 ----- 

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

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

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

1873 """ 

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

1875 

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

1877 """Return default generator for a given trait 

1878 

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

1880 """ 

1881 method_name = "_%s_default" % name 

1882 if method_name in self.__dict__: 

1883 return getattr(self, method_name) 

1884 if method_name in self.__class__.__dict__: 

1885 return getattr(self.__class__, method_name) 

1886 return self._all_trait_default_generators[name] 

1887 

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

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

1890 

1891 Notes 

1892 ----- 

1893 Dynamically generated default values may 

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

1895 for n in names: 

1896 if not self.has_trait(n): 

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

1898 

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

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

1901 

1902 trait_names = self.trait_names(**metadata) 

1903 trait_names.extend(names) 

1904 

1905 defaults = {} 

1906 for n in trait_names: 

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

1908 return defaults 

1909 

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

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

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

1913 

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

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

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

1917 

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

1919 that the various HasTrait's instances are holding. 

1920 

1921 The metadata kwargs allow functions to be passed in which 

1922 filter traits based on metadata values. The functions should 

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

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

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

1926 to the function. 

1927 """ 

1928 traits = self._traits.copy() 

1929 

1930 if len(metadata) == 0: 

1931 return traits 

1932 

1933 result = {} 

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

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

1936 if not callable(meta_eval): 

1937 meta_eval = _SimpleTest(meta_eval) 

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

1939 break 

1940 else: 

1941 result[name] = trait 

1942 

1943 return result 

1944 

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

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

1947 try: 

1948 trait = getattr(self.__class__, traitname) 

1949 except AttributeError as e: 

1950 raise TraitError( 

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

1952 ) from e 

1953 metadata_name = "_" + traitname + "_metadata" 

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

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

1956 else: 

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

1958 

1959 @classmethod 

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

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

1962 

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

1964 """ 

1965 sup = super(cls, cls) 

1966 return { 

1967 n: e 

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

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

1970 } 

1971 

1972 @classmethod 

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

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

1975 

1976 Parameters 

1977 ---------- 

1978 name : str (default: None) 

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

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

1981 

1982 Returns 

1983 ------- 

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

1985 """ 

1986 events = {} 

1987 for k, v in getmembers(cls): 

1988 if isinstance(v, EventHandler): 

1989 if name is None: 

1990 events[k] = v 

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

1992 events[k] = v 

1993 elif hasattr(v, "tags"): 

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

1995 events[k] = v 

1996 return events 

1997 

1998 

1999# ----------------------------------------------------------------------------- 

2000# Actual TraitTypes implementations/subclasses 

2001# ----------------------------------------------------------------------------- 

2002 

2003# ----------------------------------------------------------------------------- 

2004# TraitTypes subclasses for handling classes and instances of classes 

2005# ----------------------------------------------------------------------------- 

2006 

2007 

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

2009 """ 

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

2011 Instance and This. 

2012 """ 

2013 

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

2015 """ 

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

2017 """ 

2018 return import_item(string) 

2019 

2020 

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

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

2023 

2024 if t.TYPE_CHECKING: 

2025 

2026 @t.overload 

2027 def __init__( 

2028 self: Type[type, type], 

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

2030 klass: None | str = ..., 

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

2032 read_only: bool | None = ..., 

2033 help: str | None = ..., 

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

2035 **kwargs: t.Any, 

2036 ) -> None: 

2037 ... 

2038 

2039 @t.overload 

2040 def __init__( 

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

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

2043 klass: None | str = ..., 

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

2045 read_only: bool | None = ..., 

2046 help: str | None = ..., 

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

2048 **kwargs: t.Any, 

2049 ) -> None: 

2050 ... 

2051 

2052 @t.overload 

2053 def __init__( 

2054 self: Type[S, S], 

2055 default_value: S = ..., 

2056 klass: S = ..., 

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

2058 read_only: bool | None = ..., 

2059 help: str | None = ..., 

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

2061 **kwargs: t.Any, 

2062 ) -> None: 

2063 ... 

2064 

2065 @t.overload 

2066 def __init__( 

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

2068 default_value: S | None = ..., 

2069 klass: S = ..., 

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

2071 read_only: bool | None = ..., 

2072 help: str | None = ..., 

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

2074 **kwargs: t.Any, 

2075 ) -> None: 

2076 ... 

2077 

2078 def __init__( 

2079 self, 

2080 default_value: t.Any = Undefined, 

2081 klass: t.Any = None, 

2082 allow_none: bool = False, 

2083 read_only: bool | None = None, 

2084 help: str | None = None, 

2085 config: t.Any | None = None, 

2086 **kwargs: t.Any, 

2087 ) -> None: 

2088 """Construct a Type trait 

2089 

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

2091 a particular class. 

2092 

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

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

2095 

2096 Parameters 

2097 ---------- 

2098 default_value : class, str or None 

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

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

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

2102 :class:`HasTraits` class is instantiated. 

2103 klass : class, str [ default object ] 

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

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

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

2107 :class:`HasTraits` class is instantiated. 

2108 allow_none : bool [ default False ] 

2109 Indicates whether None is allowed as an assignable value. 

2110 **kwargs 

2111 extra kwargs passed to `ClassBasedTraitType` 

2112 """ 

2113 if default_value is Undefined: 

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

2115 else: 

2116 new_default_value = default_value 

2117 

2118 if klass is None: 

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

2120 klass = object 

2121 else: 

2122 klass = default_value 

2123 

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

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

2126 

2127 self.klass = klass 

2128 

2129 super().__init__( 

2130 new_default_value, 

2131 allow_none=allow_none, 

2132 read_only=read_only, 

2133 help=help, 

2134 config=config, 

2135 **kwargs, 

2136 ) 

2137 

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

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

2140 if isinstance(value, str): 

2141 try: 

2142 value = self._resolve_string(value) 

2143 except ImportError as e: 

2144 raise TraitError( 

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

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

2147 ) from e 

2148 try: 

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

2150 return t.cast(G, value) 

2151 except Exception: 

2152 pass 

2153 

2154 self.error(obj, value) 

2155 

2156 def info(self) -> str: 

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

2158 if isinstance(self.klass, str): 

2159 klass = self.klass 

2160 else: 

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

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

2163 if self.allow_none: 

2164 return result + " or None" 

2165 return result 

2166 

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

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

2169 # might be called before all imports are done. 

2170 self._resolve_classes() 

2171 

2172 def _resolve_classes(self) -> None: 

2173 if isinstance(self.klass, str): 

2174 self.klass = self._resolve_string(self.klass) 

2175 if isinstance(self.default_value, str): 

2176 self.default_value = self._resolve_string(self.default_value) 

2177 

2178 def default_value_repr(self) -> str: 

2179 value = self.default_value 

2180 assert value is not None 

2181 if isinstance(value, str): 

2182 return repr(value) 

2183 else: 

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

2185 

2186 

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

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

2189 

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

2191 

2192 Subclasses can declare default classes by overriding the klass attribute 

2193 """ 

2194 

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

2196 

2197 if t.TYPE_CHECKING: 

2198 

2199 @t.overload 

2200 def __init__( 

2201 self: Instance[T], 

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

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

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

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

2206 read_only: bool | None = ..., 

2207 help: str | None = ..., 

2208 **kwargs: t.Any, 

2209 ) -> None: 

2210 ... 

2211 

2212 @t.overload 

2213 def __init__( 

2214 self: Instance[T | None], 

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

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

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

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

2219 read_only: bool | None = ..., 

2220 help: str | None = ..., 

2221 **kwargs: t.Any, 

2222 ) -> None: 

2223 ... 

2224 

2225 @t.overload 

2226 def __init__( 

2227 self: Instance[t.Any], 

2228 klass: str | None = ..., 

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

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

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

2232 read_only: bool | None = ..., 

2233 help: str | None = ..., 

2234 **kwargs: t.Any, 

2235 ) -> None: 

2236 ... 

2237 

2238 @t.overload 

2239 def __init__( 

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

2241 klass: str | None = ..., 

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

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

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

2245 read_only: bool | None = ..., 

2246 help: str | None = ..., 

2247 **kwargs: t.Any, 

2248 ) -> None: 

2249 ... 

2250 

2251 def __init__( 

2252 self, 

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

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

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

2256 allow_none: bool = False, 

2257 read_only: bool | None = None, 

2258 help: str | None = None, 

2259 **kwargs: t.Any, 

2260 ) -> None: 

2261 """Construct an Instance trait. 

2262 

2263 This trait allows values that are instances of a particular 

2264 class or its subclasses. Our implementation is quite different 

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

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

2267 

2268 Parameters 

2269 ---------- 

2270 klass : class, str 

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

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

2273 args : tuple 

2274 Positional arguments for generating the default value. 

2275 kw : dict 

2276 Keyword arguments for generating the default value. 

2277 allow_none : bool [ default False ] 

2278 Indicates whether None is allowed as a value. 

2279 **kwargs 

2280 Extra kwargs passed to `ClassBasedTraitType` 

2281 

2282 Notes 

2283 ----- 

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

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

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

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

2288 """ 

2289 if klass is None: 

2290 klass = self.klass 

2291 

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

2293 self.klass = klass 

2294 else: 

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

2296 

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

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

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

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

2301 

2302 self.default_args = args 

2303 self.default_kwargs = kw 

2304 

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

2306 

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

2308 assert self.klass is not None 

2309 if self.allow_none and value is None: 

2310 return value 

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

2312 return t.cast(T, value) 

2313 else: 

2314 self.error(obj, value) 

2315 

2316 def info(self) -> str: 

2317 if isinstance(self.klass, str): 

2318 result = add_article(self.klass) 

2319 else: 

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

2321 if self.allow_none: 

2322 result += " or None" 

2323 return result 

2324 

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

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

2327 # might be called before all imports are done. 

2328 self._resolve_classes() 

2329 

2330 def _resolve_classes(self) -> None: 

2331 if isinstance(self.klass, str): 

2332 self.klass = self._resolve_string(self.klass) 

2333 

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

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

2336 return None 

2337 assert self.klass is not None 

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

2339 

2340 def default_value_repr(self) -> str: 

2341 return repr(self.make_dynamic_default()) 

2342 

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

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

2345 

2346 

2347class ForwardDeclaredMixin: 

2348 """ 

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

2350 """ 

2351 

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

2353 """ 

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

2355 our this_class attribute was defined. 

2356 """ 

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

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

2359 

2360 

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

2362 """ 

2363 Forward-declared version of Type. 

2364 """ 

2365 

2366 pass 

2367 

2368 

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

2370 """ 

2371 Forward-declared version of Instance. 

2372 """ 

2373 

2374 pass 

2375 

2376 

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

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

2379 

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

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

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

2383 """ 

2384 

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

2386 

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

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

2389 

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

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

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

2393 # trait. 

2394 assert self.this_class is not None 

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

2396 return value 

2397 else: 

2398 self.error(obj, value) 

2399 

2400 

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

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

2403 

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

2405 """Construct a Union trait. 

2406 

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

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

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

2410 

2411 Parameters 

2412 ---------- 

2413 trait_types : sequence 

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

2415 **kwargs 

2416 Extra kwargs passed to `TraitType` 

2417 

2418 Notes 

2419 ----- 

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

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

2422 

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

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

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

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

2427 ``_literal_from_string_pairs`` in subclasses. 

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

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

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

2431 """ 

2432 self.trait_types = list(trait_types) 

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

2434 super().__init__(**kwargs) 

2435 

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

2437 default = super().default(obj) 

2438 for trait in self.trait_types: 

2439 if default is Undefined: 

2440 default = trait.default(obj) 

2441 else: 

2442 break 

2443 return default 

2444 

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

2446 for trait_type in reversed(self.trait_types): 

2447 trait_type.class_init(cls, None) 

2448 super().class_init(cls, name) 

2449 

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

2451 for trait_type in reversed(self.trait_types): 

2452 trait_type.subclass_init(cls) 

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

2454 # to opt out of instance_init 

2455 

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

2457 with obj.cross_validation_lock: 

2458 for trait_type in self.trait_types: 

2459 try: 

2460 v = trait_type._validate(obj, value) 

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

2462 if self.name is not None: 

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

2464 return v 

2465 except TraitError: 

2466 continue 

2467 self.error(obj, value) 

2468 

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

2470 if isinstance(other, Union): 

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

2472 else: 

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

2474 

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

2476 for trait_type in self.trait_types: 

2477 try: 

2478 v = trait_type.from_string(s) 

2479 return trait_type.validate(None, v) 

2480 except (TraitError, ValueError): 

2481 continue 

2482 return super().from_string(s) 

2483 

2484 

2485# ----------------------------------------------------------------------------- 

2486# Basic TraitTypes implementations/subclasses 

2487# ----------------------------------------------------------------------------- 

2488 

2489 

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

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

2492 

2493 if t.TYPE_CHECKING: 

2494 

2495 @t.overload 

2496 def __init__( 

2497 self: Any, 

2498 default_value: t.Any = ..., 

2499 *, 

2500 allow_none: Literal[False], 

2501 read_only: bool | None = ..., 

2502 help: str | None = ..., 

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

2504 **kwargs: t.Any, 

2505 ) -> None: 

2506 ... 

2507 

2508 @t.overload 

2509 def __init__( 

2510 self: Any, 

2511 default_value: t.Any = ..., 

2512 *, 

2513 allow_none: Literal[True], 

2514 read_only: bool | None = ..., 

2515 help: str | None = ..., 

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

2517 **kwargs: t.Any, 

2518 ) -> None: 

2519 ... 

2520 

2521 @t.overload 

2522 def __init__( 

2523 self: Any, 

2524 default_value: t.Any = ..., 

2525 *, 

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

2527 help: str | None = ..., 

2528 read_only: bool | None = False, 

2529 config: t.Any = None, 

2530 **kwargs: t.Any, 

2531 ) -> None: 

2532 ... 

2533 

2534 def __init__( 

2535 self: Any, 

2536 default_value: t.Any = ..., 

2537 *, 

2538 allow_none: bool = False, 

2539 help: str | None = "", 

2540 read_only: bool | None = False, 

2541 config: t.Any = None, 

2542 **kwargs: t.Any, 

2543 ) -> None: 

2544 ... 

2545 

2546 @t.overload 

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

2548 ... 

2549 

2550 @t.overload 

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

2552 ... 

2553 

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

2555 ... 

2556 

2557 default_value: t.Any | None = None 

2558 allow_none = True 

2559 info_text = "any value" 

2560 

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

2562 pass # fully opt out of instance_init 

2563 

2564 

2565def _validate_bounds( 

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

2567) -> t.Any: 

2568 """ 

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

2570 

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

2572 TraitError with an error message appropriate for this trait. 

2573 """ 

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

2575 raise TraitError( 

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

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

2578 "specified" 

2579 ) 

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

2581 raise TraitError( 

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

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

2584 "specified" 

2585 ) 

2586 return value 

2587 

2588 

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

2590 

2591 

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

2593 """An int trait.""" 

2594 

2595 default_value = 0 

2596 info_text = "an int" 

2597 

2598 @t.overload 

2599 def __init__( 

2600 self: Int[int, int], 

2601 default_value: int | Sentinel = ..., 

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

2603 read_only: bool | None = ..., 

2604 help: str | None = ..., 

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

2606 **kwargs: t.Any, 

2607 ) -> None: 

2608 ... 

2609 

2610 @t.overload 

2611 def __init__( 

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

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

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

2615 read_only: bool | None = ..., 

2616 help: str | None = ..., 

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

2618 **kwargs: t.Any, 

2619 ) -> None: 

2620 ... 

2621 

2622 def __init__( 

2623 self, 

2624 default_value: t.Any = Undefined, 

2625 allow_none: bool = False, 

2626 read_only: bool | None = None, 

2627 help: str | None = None, 

2628 config: t.Any | None = None, 

2629 **kwargs: t.Any, 

2630 ) -> None: 

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

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

2633 super().__init__( 

2634 default_value=default_value, 

2635 allow_none=allow_none, 

2636 read_only=read_only, 

2637 help=help, 

2638 config=config, 

2639 **kwargs, 

2640 ) 

2641 

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

2643 if not isinstance(value, int): 

2644 self.error(obj, value) 

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

2646 

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

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

2649 return t.cast(G, None) 

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

2651 

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

2653 pass # fully opt out of instance_init 

2654 

2655 

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

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

2658 

2659 if t.TYPE_CHECKING: 

2660 

2661 @t.overload 

2662 def __init__( 

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

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

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

2666 read_only: bool | None = ..., 

2667 help: str | None = ..., 

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

2669 **kwargs: t.Any, 

2670 ) -> None: 

2671 ... 

2672 

2673 @t.overload 

2674 def __init__( 

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

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

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

2678 read_only: bool | None = ..., 

2679 help: str | None = ..., 

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

2681 **kwargs: t.Any, 

2682 ) -> None: 

2683 ... 

2684 

2685 def __init__( 

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

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

2688 allow_none: bool = ..., 

2689 read_only: bool | None = ..., 

2690 help: str | None = ..., 

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

2692 **kwargs: t.Any, 

2693 ) -> None: 

2694 ... 

2695 

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

2697 try: 

2698 value = int(value) 

2699 except Exception: 

2700 self.error(obj, value) 

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

2702 

2703 

2704Long, CLong = Int, CInt 

2705Integer = Int 

2706 

2707 

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

2709 """A float trait.""" 

2710 

2711 default_value = 0.0 

2712 info_text = "a float" 

2713 

2714 @t.overload 

2715 def __init__( 

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

2717 default_value: float | Sentinel = ..., 

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

2719 read_only: bool | None = ..., 

2720 help: str | None = ..., 

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

2722 **kwargs: t.Any, 

2723 ) -> None: 

2724 ... 

2725 

2726 @t.overload 

2727 def __init__( 

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

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

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

2731 read_only: bool | None = ..., 

2732 help: str | None = ..., 

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

2734 **kwargs: t.Any, 

2735 ) -> None: 

2736 ... 

2737 

2738 def __init__( 

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

2740 default_value: float | Sentinel | None = Undefined, 

2741 allow_none: bool = False, 

2742 read_only: bool | None = False, 

2743 help: str | None = None, 

2744 config: t.Any | None = None, 

2745 **kwargs: t.Any, 

2746 ) -> None: 

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

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

2749 super().__init__( 

2750 default_value=default_value, 

2751 allow_none=allow_none, 

2752 read_only=read_only, 

2753 help=help, 

2754 config=config, 

2755 **kwargs, 

2756 ) 

2757 

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

2759 if isinstance(value, int): 

2760 value = float(value) 

2761 if not isinstance(value, float): 

2762 self.error(obj, value) 

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

2764 

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

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

2767 return t.cast(G, None) 

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

2769 

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

2771 pass # fully opt out of instance_init 

2772 

2773 

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

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

2776 

2777 if t.TYPE_CHECKING: 

2778 

2779 @t.overload 

2780 def __init__( 

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

2782 default_value: t.Any = ..., 

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

2784 read_only: bool | None = ..., 

2785 help: str | None = ..., 

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

2787 **kwargs: t.Any, 

2788 ) -> None: 

2789 ... 

2790 

2791 @t.overload 

2792 def __init__( 

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

2794 default_value: t.Any = ..., 

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

2796 read_only: bool | None = ..., 

2797 help: str | None = ..., 

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

2799 **kwargs: t.Any, 

2800 ) -> None: 

2801 ... 

2802 

2803 def __init__( 

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

2805 default_value: t.Any = ..., 

2806 allow_none: bool = ..., 

2807 read_only: bool | None = ..., 

2808 help: str | None = ..., 

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

2810 **kwargs: t.Any, 

2811 ) -> None: 

2812 ... 

2813 

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

2815 try: 

2816 value = float(value) 

2817 except Exception: 

2818 self.error(obj, value) 

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

2820 

2821 

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

2823 """A trait for complex numbers.""" 

2824 

2825 default_value = 0.0 + 0.0j 

2826 info_text = "a complex number" 

2827 

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

2829 if isinstance(value, complex): 

2830 return value 

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

2832 return complex(value) 

2833 self.error(obj, value) 

2834 

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

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

2837 return None 

2838 return complex(s) 

2839 

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

2841 pass # fully opt out of instance_init 

2842 

2843 

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

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

2846 

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

2848 try: 

2849 return complex(value) 

2850 except Exception: 

2851 self.error(obj, value) 

2852 

2853 

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

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

2856# we don't have a Str type. 

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

2858 """A trait for byte strings.""" 

2859 

2860 default_value = b"" 

2861 info_text = "a bytes object" 

2862 

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

2864 if isinstance(value, bytes): 

2865 return value 

2866 self.error(obj, value) 

2867 

2868 def from_string(self, s: str) -> bytes | None: 

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

2870 return None 

2871 if len(s) >= 3: 

2872 # handle deprecated b"string" 

2873 for quote in ('"', "'"): 

2874 if s[:2] == f"b{quote}" and s[-1] == quote: 

2875 old_s = s 

2876 s = s[2:-1] 

2877 warn( 

2878 "Supporting extra quotes around Bytes is deprecated in traitlets 5.0. " 

2879 f"Use {s!r} instead of {old_s!r}.", 

2880 DeprecationWarning, 

2881 stacklevel=2, 

2882 ) 

2883 break 

2884 return s.encode("utf8") 

2885 

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

2887 pass # fully opt out of instance_init 

2888 

2889 

2890class CBytes(Bytes, TraitType[bytes, t.Any]): 

2891 """A casting version of the byte string trait.""" 

2892 

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

2894 try: 

2895 return bytes(value) 

2896 except Exception: 

2897 self.error(obj, value) 

2898 

2899 

2900class Unicode(TraitType[G, S]): 

2901 """A trait for unicode strings.""" 

2902 

2903 default_value = "" 

2904 info_text = "a unicode string" 

2905 

2906 if t.TYPE_CHECKING: 

2907 

2908 @t.overload 

2909 def __init__( 

2910 self: Unicode[str, str | bytes], 

2911 default_value: str | Sentinel = ..., 

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

2913 read_only: bool | None = ..., 

2914 help: str | None = ..., 

2915 config: t.Any = ..., 

2916 **kwargs: t.Any, 

2917 ) -> None: 

2918 ... 

2919 

2920 @t.overload 

2921 def __init__( 

2922 self: Unicode[str | None, str | bytes | None], 

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

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

2925 read_only: bool | None = ..., 

2926 help: str | None = ..., 

2927 config: t.Any = ..., 

2928 **kwargs: t.Any, 

2929 ) -> None: 

2930 ... 

2931 

2932 def __init__( 

2933 self: Unicode[str | None, str | bytes | None], 

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

2935 allow_none: bool = ..., 

2936 read_only: bool | None = ..., 

2937 help: str | None = ..., 

2938 config: t.Any = ..., 

2939 **kwargs: t.Any, 

2940 ) -> None: 

2941 ... 

2942 

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

2944 if isinstance(value, str): 

2945 return t.cast(G, value) 

2946 if isinstance(value, bytes): 

2947 try: 

2948 return t.cast(G, value.decode("ascii", "strict")) 

2949 except UnicodeDecodeError as e: 

2950 msg = "Could not decode {!r} for unicode trait '{}' of {} instance." 

2951 raise TraitError(msg.format(value, self.name, class_of(obj))) from e 

2952 self.error(obj, value) 

2953 

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

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

2956 return t.cast(G, None) 

2957 s = os.path.expanduser(s) 

2958 if len(s) >= 2: 

2959 # handle deprecated "1" 

2960 for c in ('"', "'"): 

2961 if s[0] == s[-1] == c: 

2962 old_s = s 

2963 s = s[1:-1] 

2964 warn( 

2965 "Supporting extra quotes around strings is deprecated in traitlets 5.0. " 

2966 f"You can use {s!r} instead of {old_s!r} if you require traitlets >=5.", 

2967 DeprecationWarning, 

2968 stacklevel=2, 

2969 ) 

2970 return t.cast(G, s) 

2971 

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

2973 pass # fully opt out of instance_init 

2974 

2975 

2976class CUnicode(Unicode[G, S], TraitType[str, t.Any]): 

2977 """A casting version of the unicode trait.""" 

2978 

2979 if t.TYPE_CHECKING: 

2980 

2981 @t.overload 

2982 def __init__( 

2983 self: CUnicode[str, t.Any], 

2984 default_value: str | Sentinel = ..., 

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

2986 read_only: bool | None = ..., 

2987 help: str | None = ..., 

2988 config: t.Any = ..., 

2989 **kwargs: t.Any, 

2990 ) -> None: 

2991 ... 

2992 

2993 @t.overload 

2994 def __init__( 

2995 self: CUnicode[str | None, t.Any], 

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

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

2998 read_only: bool | None = ..., 

2999 help: str | None = ..., 

3000 config: t.Any = ..., 

3001 **kwargs: t.Any, 

3002 ) -> None: 

3003 ... 

3004 

3005 def __init__( 

3006 self: CUnicode[str | None, t.Any], 

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

3008 allow_none: bool = ..., 

3009 read_only: bool | None = ..., 

3010 help: str | None = ..., 

3011 config: t.Any = ..., 

3012 **kwargs: t.Any, 

3013 ) -> None: 

3014 ... 

3015 

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

3017 try: 

3018 return t.cast(G, str(value)) 

3019 except Exception: 

3020 self.error(obj, value) 

3021 

3022 

3023class ObjectName(TraitType[str, str]): 

3024 """A string holding a valid object name in this version of Python. 

3025 

3026 This does not check that the name exists in any scope.""" 

3027 

3028 info_text = "a valid object identifier in Python" 

3029 

3030 coerce_str = staticmethod(lambda _, s: s) 

3031 

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

3033 value = self.coerce_str(obj, value) 

3034 

3035 if isinstance(value, str) and isidentifier(value): 

3036 return value 

3037 self.error(obj, value) 

3038 

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

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

3041 return None 

3042 return s 

3043 

3044 

3045class DottedObjectName(ObjectName): 

3046 """A string holding a valid dotted object name in Python, such as A.b3._c""" 

3047 

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

3049 value = self.coerce_str(obj, value) 

3050 

3051 if isinstance(value, str) and all(isidentifier(a) for a in value.split(".")): 

3052 return value 

3053 self.error(obj, value) 

3054 

3055 

3056class Bool(TraitType[G, S]): 

3057 """A boolean (True, False) trait.""" 

3058 

3059 default_value = False 

3060 info_text = "a boolean" 

3061 

3062 if t.TYPE_CHECKING: 

3063 

3064 @t.overload 

3065 def __init__( 

3066 self: Bool[bool, bool | int], 

3067 default_value: bool | Sentinel = ..., 

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

3069 read_only: bool | None = ..., 

3070 help: str | None = ..., 

3071 config: t.Any = ..., 

3072 **kwargs: t.Any, 

3073 ) -> None: 

3074 ... 

3075 

3076 @t.overload 

3077 def __init__( 

3078 self: Bool[bool | None, bool | int | None], 

3079 default_value: bool | Sentinel | None = ..., 

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

3081 read_only: bool | None = ..., 

3082 help: str | None = ..., 

3083 config: t.Any = ..., 

3084 **kwargs: t.Any, 

3085 ) -> None: 

3086 ... 

3087 

3088 def __init__( 

3089 self: Bool[bool | None, bool | int | None], 

3090 default_value: bool | Sentinel | None = ..., 

3091 allow_none: bool = ..., 

3092 read_only: bool | None = ..., 

3093 help: str | None = ..., 

3094 config: t.Any = ..., 

3095 **kwargs: t.Any, 

3096 ) -> None: 

3097 ... 

3098 

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

3100 if isinstance(value, bool): 

3101 return t.cast(G, value) 

3102 elif isinstance(value, int): 

3103 if value == 1: 

3104 return t.cast(G, True) 

3105 elif value == 0: 

3106 return t.cast(G, False) 

3107 self.error(obj, value) 

3108 

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

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

3111 return t.cast(G, None) 

3112 s = s.lower() 

3113 if s in {"true", "1"}: 

3114 return t.cast(G, True) 

3115 elif s in {"false", "0"}: 

3116 return t.cast(G, False) 

3117 else: 

3118 raise ValueError("%r is not 1, 0, true, or false") 

3119 

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

3121 pass # fully opt out of instance_init 

3122 

3123 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3124 """Completion hints for argcomplete""" 

3125 completions = ["true", "1", "false", "0"] 

3126 if self.allow_none: 

3127 completions.append("None") 

3128 return completions 

3129 

3130 

3131class CBool(Bool[G, S]): 

3132 """A casting version of the boolean trait.""" 

3133 

3134 if t.TYPE_CHECKING: 

3135 

3136 @t.overload 

3137 def __init__( 

3138 self: CBool[bool, t.Any], 

3139 default_value: bool | Sentinel = ..., 

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

3141 read_only: bool | None = ..., 

3142 help: str | None = ..., 

3143 config: t.Any = ..., 

3144 **kwargs: t.Any, 

3145 ) -> None: 

3146 ... 

3147 

3148 @t.overload 

3149 def __init__( 

3150 self: CBool[bool | None, t.Any], 

3151 default_value: bool | Sentinel | None = ..., 

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

3153 read_only: bool | None = ..., 

3154 help: str | None = ..., 

3155 config: t.Any = ..., 

3156 **kwargs: t.Any, 

3157 ) -> None: 

3158 ... 

3159 

3160 def __init__( 

3161 self: CBool[bool | None, t.Any], 

3162 default_value: bool | Sentinel | None = ..., 

3163 allow_none: bool = ..., 

3164 read_only: bool | None = ..., 

3165 help: str | None = ..., 

3166 config: t.Any = ..., 

3167 **kwargs: t.Any, 

3168 ) -> None: 

3169 ... 

3170 

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

3172 try: 

3173 return t.cast(G, bool(value)) 

3174 except Exception: 

3175 self.error(obj, value) 

3176 

3177 

3178class Enum(TraitType[G, G]): 

3179 """An enum whose value must be in a given sequence.""" 

3180 

3181 if t.TYPE_CHECKING: 

3182 

3183 @t.overload 

3184 def __init__( 

3185 self: Enum[G], 

3186 values: t.Sequence[G], 

3187 default_value: G | Sentinel = ..., 

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

3189 read_only: bool | None = ..., 

3190 help: str | None = ..., 

3191 config: t.Any = ..., 

3192 **kwargs: t.Any, 

3193 ) -> None: 

3194 ... 

3195 

3196 @t.overload 

3197 def __init__( 

3198 self: Enum[G | None], 

3199 values: t.Sequence[G] | None, 

3200 default_value: G | Sentinel | None = ..., 

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

3202 read_only: bool | None = ..., 

3203 help: str | None = ..., 

3204 config: t.Any = ..., 

3205 **kwargs: t.Any, 

3206 ) -> None: 

3207 ... 

3208 

3209 def __init__( 

3210 self: Enum[G], 

3211 values: t.Sequence[G] | None, 

3212 default_value: G | Sentinel | None = Undefined, 

3213 allow_none: bool = False, 

3214 read_only: bool | None = None, 

3215 help: str | None = None, 

3216 config: t.Any = None, 

3217 **kwargs: t.Any, 

3218 ) -> None: 

3219 self.values = values 

3220 if allow_none is True and default_value is Undefined: 

3221 default_value = None 

3222 kwargs["allow_none"] = allow_none 

3223 kwargs["read_only"] = read_only 

3224 kwargs["help"] = help 

3225 kwargs["config"] = config 

3226 super().__init__(default_value, **kwargs) 

3227 

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

3229 if self.values and value in self.values: 

3230 return t.cast(G, value) 

3231 self.error(obj, value) 

3232 

3233 def _choices_str(self, as_rst: bool = False) -> str: 

3234 """Returns a description of the trait choices (not none).""" 

3235 choices = self.values or [] 

3236 if as_rst: 

3237 choice_str = "|".join("``%r``" % x for x in choices) 

3238 else: 

3239 choice_str = repr(list(choices)) 

3240 return choice_str 

3241 

3242 def _info(self, as_rst: bool = False) -> str: 

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

3244 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3245 return f"any of {self._choices_str(as_rst)}{none}" 

3246 

3247 def info(self) -> str: 

3248 return self._info(as_rst=False) 

3249 

3250 def info_rst(self) -> str: 

3251 return self._info(as_rst=True) 

3252 

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

3254 try: 

3255 return self.validate(None, s) 

3256 except TraitError: 

3257 return t.cast(G, _safe_literal_eval(s)) 

3258 

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

3260 pass # fully opt out of instance_init 

3261 

3262 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3263 """Completion hints for argcomplete""" 

3264 return [str(v) for v in self.values or []] 

3265 

3266 

3267class CaselessStrEnum(Enum[G]): 

3268 """An enum of strings where the case should be ignored.""" 

3269 

3270 def __init__( 

3271 self: CaselessStrEnum[t.Any], 

3272 values: t.Any, 

3273 default_value: t.Any = Undefined, 

3274 **kwargs: t.Any, 

3275 ) -> None: 

3276 super().__init__(values, default_value=default_value, **kwargs) 

3277 

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

3279 if not isinstance(value, str): 

3280 self.error(obj, value) 

3281 

3282 for v in self.values or []: 

3283 assert isinstance(v, str) 

3284 if v.lower() == value.lower(): 

3285 return t.cast(G, v) 

3286 self.error(obj, value) 

3287 

3288 def _info(self, as_rst: bool = False) -> str: 

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

3290 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3291 return f"any of {self._choices_str(as_rst)} (case-insensitive){none}" 

3292 

3293 def info(self) -> str: 

3294 return self._info(as_rst=False) 

3295 

3296 def info_rst(self) -> str: 

3297 return self._info(as_rst=True) 

3298 

3299 

3300class FuzzyEnum(Enum[G]): 

3301 """An case-ignoring enum matching choices by unique prefixes/substrings.""" 

3302 

3303 case_sensitive = False 

3304 #: If True, choices match anywhere in the string, otherwise match prefixes. 

3305 substring_matching = False 

3306 

3307 def __init__( 

3308 self: FuzzyEnum[t.Any], 

3309 values: t.Any, 

3310 default_value: t.Any = Undefined, 

3311 case_sensitive: bool = False, 

3312 substring_matching: bool = False, 

3313 **kwargs: t.Any, 

3314 ) -> None: 

3315 self.case_sensitive = case_sensitive 

3316 self.substring_matching = substring_matching 

3317 super().__init__(values, default_value=default_value, **kwargs) 

3318 

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

3320 if not isinstance(value, str): 

3321 self.error(obj, value) 

3322 

3323 conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower() 

3324 substring_matching = self.substring_matching 

3325 match_func = (lambda v, c: v in c) if substring_matching else (lambda v, c: c.startswith(v)) 

3326 value = conv_func(value) # type:ignore[no-untyped-call] 

3327 choices = self.values or [] 

3328 matches = [match_func(value, conv_func(c)) for c in choices] # type:ignore[no-untyped-call] 

3329 if sum(matches) == 1: 

3330 for v, m in zip(choices, matches): 

3331 if m: 

3332 return v 

3333 

3334 self.error(obj, value) 

3335 

3336 def _info(self, as_rst: bool = False) -> str: 

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

3338 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3339 case = "sensitive" if self.case_sensitive else "insensitive" 

3340 substr = "substring" if self.substring_matching else "prefix" 

3341 return f"any case-{case} {substr} of {self._choices_str(as_rst)}{none}" 

3342 

3343 def info(self) -> str: 

3344 return self._info(as_rst=False) 

3345 

3346 def info_rst(self) -> str: 

3347 return self._info(as_rst=True) 

3348 

3349 

3350class Container(Instance[T]): 

3351 """An instance of a container (list, set, etc.) 

3352 

3353 To be subclassed by overriding klass. 

3354 """ 

3355 

3356 klass: type[T] | None = None 

3357 _cast_types: t.Any = () 

3358 _valid_defaults = SequenceTypes 

3359 _trait: t.Any = None 

3360 _literal_from_string_pairs: t.Any = ("[]", "()") 

3361 

3362 @t.overload 

3363 def __init__( 

3364 self: Container[T], 

3365 *, 

3366 allow_none: Literal[False], 

3367 read_only: bool | None = ..., 

3368 help: str | None = ..., 

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

3370 **kwargs: t.Any, 

3371 ) -> None: 

3372 ... 

3373 

3374 @t.overload 

3375 def __init__( 

3376 self: Container[T | None], 

3377 *, 

3378 allow_none: Literal[True], 

3379 read_only: bool | None = ..., 

3380 help: str | None = ..., 

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

3382 **kwargs: t.Any, 

3383 ) -> None: 

3384 ... 

3385 

3386 @t.overload 

3387 def __init__( 

3388 self: Container[T], 

3389 *, 

3390 trait: t.Any = ..., 

3391 default_value: t.Any = ..., 

3392 help: str = ..., 

3393 read_only: bool = ..., 

3394 config: t.Any = ..., 

3395 **kwargs: t.Any, 

3396 ) -> None: 

3397 ... 

3398 

3399 def __init__( 

3400 self, 

3401 trait: t.Any | None = None, 

3402 default_value: t.Any = Undefined, 

3403 help: str | None = None, 

3404 read_only: bool | None = None, 

3405 config: t.Any | None = None, 

3406 **kwargs: t.Any, 

3407 ) -> None: 

3408 """Create a container trait type from a list, set, or tuple. 

3409 

3410 The default value is created by doing ``List(default_value)``, 

3411 which creates a copy of the ``default_value``. 

3412 

3413 ``trait`` can be specified, which restricts the type of elements 

3414 in the container to that TraitType. 

3415 

3416 If only one arg is given and it is not a Trait, it is taken as 

3417 ``default_value``: 

3418 

3419 ``c = List([1, 2, 3])`` 

3420 

3421 Parameters 

3422 ---------- 

3423 trait : TraitType [ optional ] 

3424 the type for restricting the contents of the Container. If unspecified, 

3425 types are not checked. 

3426 default_value : SequenceType [ optional ] 

3427 The default value for the Trait. Must be list/tuple/set, and 

3428 will be cast to the container type. 

3429 allow_none : bool [ default False ] 

3430 Whether to allow the value to be None 

3431 **kwargs : any 

3432 further keys for extensions to the Trait (e.g. config) 

3433 

3434 """ 

3435 

3436 # allow List([values]): 

3437 if trait is not None and default_value is Undefined and not is_trait(trait): 

3438 default_value = trait 

3439 trait = None 

3440 

3441 if default_value is None and not kwargs.get("allow_none", False): 

3442 # improve backward-compatibility for possible subclasses 

3443 # specifying default_value=None as default, 

3444 # keeping 'unspecified' behavior (i.e. empty container) 

3445 warn( 

3446 f"Specifying {self.__class__.__name__}(default_value=None)" 

3447 " for no default is deprecated in traitlets 5.0.5." 

3448 " Use default_value=Undefined", 

3449 DeprecationWarning, 

3450 stacklevel=2, 

3451 ) 

3452 default_value = Undefined 

3453 

3454 if default_value is Undefined: 

3455 args: t.Any = () 

3456 elif default_value is None: 

3457 # default_value back on kwargs for super() to handle 

3458 args = () 

3459 kwargs["default_value"] = None 

3460 elif isinstance(default_value, self._valid_defaults): 

3461 args = (default_value,) 

3462 else: 

3463 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3464 

3465 if is_trait(trait): 

3466 if isinstance(trait, type): 

3467 warn( 

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

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

3470 DeprecationWarning, 

3471 stacklevel=3, 

3472 ) 

3473 self._trait = trait() if isinstance(trait, type) else trait 

3474 elif trait is not None: 

3475 raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait)) 

3476 

3477 super().__init__( 

3478 klass=self.klass, args=args, help=help, read_only=read_only, config=config, **kwargs 

3479 ) 

3480 

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

3482 if isinstance(value, self._cast_types): 

3483 assert self.klass is not None 

3484 value = self.klass(value) # type:ignore[call-arg] 

3485 value = super().validate(obj, value) 

3486 if value is None: 

3487 return value 

3488 

3489 value = self.validate_elements(obj, value) 

3490 

3491 return t.cast(T, value) 

3492 

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

3494 validated = [] 

3495 if self._trait is None or isinstance(self._trait, Any): 

3496 return t.cast(T, value) 

3497 for v in value: 

3498 try: 

3499 v = self._trait._validate(obj, v) 

3500 except TraitError as error: 

3501 self.error(obj, v, error) 

3502 else: 

3503 validated.append(v) 

3504 assert self.klass is not None 

3505 return self.klass(validated) # type:ignore[call-arg] 

3506 

3507 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3508 if isinstance(self._trait, TraitType): 

3509 self._trait.class_init(cls, None) 

3510 super().class_init(cls, name) 

3511 

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

3513 if isinstance(self._trait, TraitType): 

3514 self._trait.subclass_init(cls) 

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

3516 # to opt out of instance_init 

3517 

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

3519 """Load value from a single string""" 

3520 if not isinstance(s, str): 

3521 raise TraitError(f"Expected string, got {s!r}") 

3522 try: 

3523 test = literal_eval(s) 

3524 except Exception: 

3525 test = None 

3526 return self.validate(None, test) 

3527 

3528 def from_string_list(self, s_list: list[str]) -> T | None: 

3529 """Return the value from a list of config strings 

3530 

3531 This is where we parse CLI configuration 

3532 """ 

3533 assert self.klass is not None 

3534 if len(s_list) == 1: 

3535 # check for deprecated --Class.trait="['a', 'b', 'c']" 

3536 r = s_list[0] 

3537 if r == "None" and self.allow_none: 

3538 return None 

3539 if len(r) >= 2 and any( 

3540 r.startswith(start) and r.endswith(end) 

3541 for start, end in self._literal_from_string_pairs 

3542 ): 

3543 if self.this_class: 

3544 clsname = self.this_class.__name__ + "." 

3545 else: 

3546 clsname = "" 

3547 assert self.name is not None 

3548 warn( 

3549 "--{0}={1} for containers is deprecated in traitlets 5.0. " 

3550 "You can pass `--{0} item` ... multiple times to add items to a list.".format( 

3551 clsname + self.name, r 

3552 ), 

3553 DeprecationWarning, 

3554 stacklevel=2, 

3555 ) 

3556 return self.klass(literal_eval(r)) # type:ignore[call-arg] 

3557 sig = inspect.signature(self.item_from_string) 

3558 if "index" in sig.parameters: 

3559 item_from_string = self.item_from_string 

3560 else: 

3561 # backward-compat: allow item_from_string to ignore index arg 

3562 def item_from_string(s: str, index: int | None = None) -> T | str: 

3563 return t.cast(T, self.item_from_string(s)) 

3564 

3565 return self.klass( # type:ignore[call-arg] 

3566 [item_from_string(s, index=idx) for idx, s in enumerate(s_list)] 

3567 ) 

3568 

3569 def item_from_string(self, s: str, index: int | None = None) -> T | str: 

3570 """Cast a single item from a string 

3571 

3572 Evaluated when parsing CLI configuration from a string 

3573 """ 

3574 if self._trait: 

3575 return t.cast(T, self._trait.from_string(s)) 

3576 else: 

3577 return s 

3578 

3579 

3580class List(Container[t.List[T]]): 

3581 """An instance of a Python list.""" 

3582 

3583 klass = list # type:ignore[assignment] 

3584 _cast_types: t.Any = (tuple,) 

3585 

3586 def __init__( 

3587 self, 

3588 trait: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | TraitType[T, t.Any] | None = None, 

3589 default_value: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | None = Undefined, 

3590 minlen: int = 0, 

3591 maxlen: int = sys.maxsize, 

3592 **kwargs: t.Any, 

3593 ) -> None: 

3594 """Create a List trait type from a list, set, or tuple. 

3595 

3596 The default value is created by doing ``list(default_value)``, 

3597 which creates a copy of the ``default_value``. 

3598 

3599 ``trait`` can be specified, which restricts the type of elements 

3600 in the container to that TraitType. 

3601 

3602 If only one arg is given and it is not a Trait, it is taken as 

3603 ``default_value``: 

3604 

3605 ``c = List([1, 2, 3])`` 

3606 

3607 Parameters 

3608 ---------- 

3609 trait : TraitType [ optional ] 

3610 the type for restricting the contents of the Container. 

3611 If unspecified, types are not checked. 

3612 default_value : SequenceType [ optional ] 

3613 The default value for the Trait. Must be list/tuple/set, and 

3614 will be cast to the container type. 

3615 minlen : Int [ default 0 ] 

3616 The minimum length of the input list 

3617 maxlen : Int [ default sys.maxsize ] 

3618 The maximum length of the input list 

3619 """ 

3620 self._maxlen = maxlen 

3621 self._minlen = minlen 

3622 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3623 

3624 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3625 e = ( 

3626 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3627 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3628 ) 

3629 raise TraitError(e) 

3630 

3631 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3632 length = len(value) 

3633 if length < self._minlen or length > self._maxlen: 

3634 self.length_error(obj, value) 

3635 

3636 return super().validate_elements(obj, value) 

3637 

3638 def set(self, obj: t.Any, value: t.Any) -> None: 

3639 if isinstance(value, str): 

3640 return super().set(obj, [value]) # type:ignore[list-item] 

3641 else: 

3642 return super().set(obj, value) 

3643 

3644 

3645class Set(Container[t.Set[t.Any]]): 

3646 """An instance of a Python set.""" 

3647 

3648 klass = set 

3649 _cast_types = (tuple, list) 

3650 

3651 _literal_from_string_pairs = ("[]", "()", "{}") 

3652 

3653 # Redefine __init__ just to make the docstring more accurate. 

3654 def __init__( 

3655 self, 

3656 trait: t.Any = None, 

3657 default_value: t.Any = Undefined, 

3658 minlen: int = 0, 

3659 maxlen: int = sys.maxsize, 

3660 **kwargs: t.Any, 

3661 ) -> None: 

3662 """Create a Set trait type from a list, set, or tuple. 

3663 

3664 The default value is created by doing ``set(default_value)``, 

3665 which creates a copy of the ``default_value``. 

3666 

3667 ``trait`` can be specified, which restricts the type of elements 

3668 in the container to that TraitType. 

3669 

3670 If only one arg is given and it is not a Trait, it is taken as 

3671 ``default_value``: 

3672 

3673 ``c = Set({1, 2, 3})`` 

3674 

3675 Parameters 

3676 ---------- 

3677 trait : TraitType [ optional ] 

3678 the type for restricting the contents of the Container. 

3679 If unspecified, types are not checked. 

3680 default_value : SequenceType [ optional ] 

3681 The default value for the Trait. Must be list/tuple/set, and 

3682 will be cast to the container type. 

3683 minlen : Int [ default 0 ] 

3684 The minimum length of the input list 

3685 maxlen : Int [ default sys.maxsize ] 

3686 The maximum length of the input list 

3687 """ 

3688 self._maxlen = maxlen 

3689 self._minlen = minlen 

3690 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3691 

3692 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3693 e = ( 

3694 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3695 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3696 ) 

3697 raise TraitError(e) 

3698 

3699 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3700 length = len(value) 

3701 if length < self._minlen or length > self._maxlen: 

3702 self.length_error(obj, value) 

3703 

3704 return super().validate_elements(obj, value) 

3705 

3706 def set(self, obj: t.Any, value: t.Any) -> None: 

3707 if isinstance(value, str): 

3708 return super().set( 

3709 obj, 

3710 set( 

3711 value, 

3712 ), 

3713 ) 

3714 else: 

3715 return super().set(obj, value) 

3716 

3717 def default_value_repr(self) -> str: 

3718 # Ensure default value is sorted for a reproducible build 

3719 list_repr = repr(sorted(self.make_dynamic_default() or [])) 

3720 if list_repr == "[]": 

3721 return "set()" 

3722 return "{" + list_repr[1:-1] + "}" 

3723 

3724 

3725class Tuple(Container[t.Tuple[t.Any, ...]]): 

3726 """An instance of a Python tuple.""" 

3727 

3728 klass = tuple 

3729 _cast_types = (list,) 

3730 

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

3732 """Create a tuple from a list, set, or tuple. 

3733 

3734 Create a fixed-type tuple with Traits: 

3735 

3736 ``t = Tuple(Int(), Str(), CStr())`` 

3737 

3738 would be length 3, with Int,Str,CStr for each element. 

3739 

3740 If only one arg is given and it is not a Trait, it is taken as 

3741 default_value: 

3742 

3743 ``t = Tuple((1, 2, 3))`` 

3744 

3745 Otherwise, ``default_value`` *must* be specified by keyword. 

3746 

3747 Parameters 

3748 ---------- 

3749 *traits : TraitTypes [ optional ] 

3750 the types for restricting the contents of the Tuple. If unspecified, 

3751 types are not checked. If specified, then each positional argument 

3752 corresponds to an element of the tuple. Tuples defined with traits 

3753 are of fixed length. 

3754 default_value : SequenceType [ optional ] 

3755 The default value for the Tuple. Must be list/tuple/set, and 

3756 will be cast to a tuple. If ``traits`` are specified, 

3757 ``default_value`` must conform to the shape and type they specify. 

3758 **kwargs 

3759 Other kwargs passed to `Container` 

3760 """ 

3761 default_value = kwargs.pop("default_value", Undefined) 

3762 # allow Tuple((values,)): 

3763 if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]): 

3764 default_value = traits[0] 

3765 traits = () 

3766 

3767 if default_value is None and not kwargs.get("allow_none", False): 

3768 # improve backward-compatibility for possible subclasses 

3769 # specifying default_value=None as default, 

3770 # keeping 'unspecified' behavior (i.e. empty container) 

3771 warn( 

3772 f"Specifying {self.__class__.__name__}(default_value=None)" 

3773 " for no default is deprecated in traitlets 5.0.5." 

3774 " Use default_value=Undefined", 

3775 DeprecationWarning, 

3776 stacklevel=2, 

3777 ) 

3778 default_value = Undefined 

3779 

3780 if default_value is Undefined: 

3781 args: t.Any = () 

3782 elif default_value is None: 

3783 # default_value back on kwargs for super() to handle 

3784 args = () 

3785 kwargs["default_value"] = None 

3786 elif isinstance(default_value, self._valid_defaults): 

3787 args = (default_value,) 

3788 else: 

3789 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3790 

3791 self._traits = [] 

3792 for trait in traits: 

3793 if isinstance(trait, type): 

3794 warn( 

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

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

3797 DeprecationWarning, 

3798 stacklevel=2, 

3799 ) 

3800 trait = trait() 

3801 self._traits.append(trait) 

3802 

3803 if self._traits and (default_value is None or default_value is Undefined): 

3804 # don't allow default to be an empty container if length is specified 

3805 args = None 

3806 super(Container, self).__init__(klass=self.klass, args=args, **kwargs) 

3807 

3808 def item_from_string(self, s: str, index: int) -> t.Any: # type:ignore[override] 

3809 """Cast a single item from a string 

3810 

3811 Evaluated when parsing CLI configuration from a string 

3812 """ 

3813 if not self._traits or index >= len(self._traits): 

3814 # return s instead of raising index error 

3815 # length errors will be raised later on validation 

3816 return s 

3817 return self._traits[index].from_string(s) 

3818 

3819 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3820 if not self._traits: 

3821 # nothing to validate 

3822 return value 

3823 if len(value) != len(self._traits): 

3824 e = ( 

3825 "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." 

3826 % (self.name, class_of(obj), len(self._traits), repr_type(value)) 

3827 ) 

3828 raise TraitError(e) 

3829 

3830 validated = [] 

3831 for trait, v in zip(self._traits, value): 

3832 try: 

3833 v = trait._validate(obj, v) 

3834 except TraitError as error: 

3835 self.error(obj, v, error) 

3836 else: 

3837 validated.append(v) 

3838 return tuple(validated) 

3839 

3840 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3841 for trait in self._traits: 

3842 if isinstance(trait, TraitType): 

3843 trait.class_init(cls, None) 

3844 super(Container, self).class_init(cls, name) 

3845 

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

3847 for trait in self._traits: 

3848 if isinstance(trait, TraitType): 

3849 trait.subclass_init(cls) 

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

3851 # to opt out of instance_init 

3852 

3853 

3854class Dict(Instance["dict[K, V]"]): 

3855 """An instance of a Python dict. 

3856 

3857 One or more traits can be passed to the constructor 

3858 to validate the keys and/or values of the dict. 

3859 If you need more detailed validation, 

3860 you may use a custom validator method. 

3861 

3862 .. versionchanged:: 5.0 

3863 Added key_trait for validating dict keys. 

3864 

3865 .. versionchanged:: 5.0 

3866 Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``. 

3867 """ 

3868 

3869 _value_trait = None 

3870 _key_trait = None 

3871 

3872 def __init__( 

3873 self, 

3874 value_trait: TraitType[t.Any, t.Any] | dict[K, V] | Sentinel | None = None, 

3875 per_key_traits: t.Any = None, 

3876 key_trait: TraitType[t.Any, t.Any] | None = None, 

3877 default_value: dict[K, V] | Sentinel | None = Undefined, 

3878 **kwargs: t.Any, 

3879 ) -> None: 

3880 """Create a dict trait type from a Python dict. 

3881 

3882 The default value is created by doing ``dict(default_value)``, 

3883 which creates a copy of the ``default_value``. 

3884 

3885 Parameters 

3886 ---------- 

3887 value_trait : TraitType [ optional ] 

3888 The specified trait type to check and use to restrict the values of 

3889 the dict. If unspecified, values are not checked. 

3890 per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ] 

3891 A Python dictionary containing the types that are valid for 

3892 restricting the values of the dict on a per-key basis. 

3893 Each value in this dict should be a Trait for validating 

3894 key_trait : TraitType [ optional, keyword-only ] 

3895 The type for restricting the keys of the dict. If 

3896 unspecified, the types of the keys are not checked. 

3897 default_value : SequenceType [ optional, keyword-only ] 

3898 The default value for the Dict. Must be dict, tuple, or None, and 

3899 will be cast to a dict if not None. If any key or value traits are specified, 

3900 the `default_value` must conform to the constraints. 

3901 

3902 Examples 

3903 -------- 

3904 a dict whose values must be text 

3905 >>> d = Dict(Unicode()) 

3906 

3907 d2['n'] must be an integer 

3908 d2['s'] must be text 

3909 >>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()}) 

3910 

3911 d3's keys must be text 

3912 d3's values must be integers 

3913 >>> d3 = Dict(value_trait=Integer(), key_trait=Unicode()) 

3914 

3915 """ 

3916 

3917 # handle deprecated keywords 

3918 trait = kwargs.pop("trait", None) 

3919 if trait is not None: 

3920 if value_trait is not None: 

3921 raise TypeError( 

3922 "Found a value for both `value_trait` and its deprecated alias `trait`." 

3923 ) 

3924 value_trait = trait 

3925 warn( 

3926 "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead", 

3927 DeprecationWarning, 

3928 stacklevel=2, 

3929 ) 

3930 traits = kwargs.pop("traits", None) 

3931 if traits is not None: 

3932 if per_key_traits is not None: 

3933 raise TypeError( 

3934 "Found a value for both `per_key_traits` and its deprecated alias `traits`." 

3935 ) 

3936 per_key_traits = traits 

3937 warn( 

3938 "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead", 

3939 DeprecationWarning, 

3940 stacklevel=2, 

3941 ) 

3942 

3943 # Handling positional arguments 

3944 if default_value is Undefined and value_trait is not None: 

3945 if not is_trait(value_trait): 

3946 assert not isinstance(value_trait, TraitType) 

3947 default_value = value_trait 

3948 value_trait = None 

3949 

3950 if key_trait is None and per_key_traits is not None: 

3951 if is_trait(per_key_traits): 

3952 key_trait = per_key_traits 

3953 per_key_traits = None 

3954 

3955 # Handling default value 

3956 if default_value is Undefined: 

3957 default_value = {} 

3958 if default_value is None: 

3959 args: t.Any = None 

3960 elif isinstance(default_value, dict): 

3961 args = (default_value,) 

3962 elif isinstance(default_value, SequenceTypes): 

3963 args = (default_value,) 

3964 else: 

3965 raise TypeError("default value of Dict was %s" % default_value) 

3966 

3967 # Case where a type of TraitType is provided rather than an instance 

3968 if is_trait(value_trait): 

3969 if isinstance(value_trait, type): 

3970 warn( # type:ignore[unreachable] 

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

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

3973 DeprecationWarning, 

3974 stacklevel=2, 

3975 ) 

3976 value_trait = value_trait() 

3977 self._value_trait = value_trait 

3978 elif value_trait is not None: 

3979 raise TypeError( 

3980 "`value_trait` must be a Trait or None, got %s" % repr_type(value_trait) 

3981 ) 

3982 

3983 if is_trait(key_trait): 

3984 if isinstance(key_trait, type): 

3985 warn( # type:ignore[unreachable] 

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

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

3988 DeprecationWarning, 

3989 stacklevel=2, 

3990 ) 

3991 key_trait = key_trait() 

3992 self._key_trait = key_trait 

3993 elif key_trait is not None: 

3994 raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait)) 

3995 

3996 self._per_key_traits = per_key_traits 

3997 

3998 super().__init__(klass=dict, args=args, **kwargs) 

3999 

4000 def element_error( 

4001 self, obj: t.Any, element: t.Any, validator: t.Any, side: str = "Values" 

4002 ) -> None: 

4003 e = ( 

4004 side 

4005 + f" of the '{self.name}' trait of {class_of(obj)} instance must be {validator.info()}, but a value of {repr_type(element)} was specified." 

4006 ) 

4007 raise TraitError(e) 

4008 

4009 def validate(self, obj: t.Any, value: t.Any) -> dict[K, V] | None: 

4010 value = super().validate(obj, value) 

4011 if value is None: 

4012 return value 

4013 value_dict = self.validate_elements(obj, value) 

4014 return value_dict 

4015 

4016 def validate_elements(self, obj: t.Any, value: dict[t.Any, t.Any]) -> dict[K, V] | None: 

4017 per_key_override = self._per_key_traits or {} 

4018 key_trait = self._key_trait 

4019 value_trait = self._value_trait 

4020 if not (key_trait or value_trait or per_key_override): 

4021 return value 

4022 

4023 validated = {} 

4024 for key in value: 

4025 v = value[key] 

4026 if key_trait: 

4027 try: 

4028 key = key_trait._validate(obj, key) 

4029 except TraitError: 

4030 self.element_error(obj, key, key_trait, "Keys") 

4031 active_value_trait = per_key_override.get(key, value_trait) 

4032 if active_value_trait: 

4033 try: 

4034 v = active_value_trait._validate(obj, v) 

4035 except TraitError: 

4036 self.element_error(obj, v, active_value_trait, "Values") 

4037 validated[key] = v 

4038 

4039 return self.klass(validated) # type:ignore[misc,operator] 

4040 

4041 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

4042 if isinstance(self._value_trait, TraitType): 

4043 self._value_trait.class_init(cls, None) 

4044 if isinstance(self._key_trait, TraitType): 

4045 self._key_trait.class_init(cls, None) 

4046 if self._per_key_traits is not None: 

4047 for trait in self._per_key_traits.values(): 

4048 trait.class_init(cls, None) 

4049 super().class_init(cls, name) 

4050 

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

4052 if isinstance(self._value_trait, TraitType): 

4053 self._value_trait.subclass_init(cls) 

4054 if isinstance(self._key_trait, TraitType): 

4055 self._key_trait.subclass_init(cls) 

4056 if self._per_key_traits is not None: 

4057 for trait in self._per_key_traits.values(): 

4058 trait.subclass_init(cls) 

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

4060 # to opt out of instance_init 

4061 

4062 def from_string(self, s: str) -> dict[K, V] | None: 

4063 """Load value from a single string""" 

4064 if not isinstance(s, str): 

4065 raise TypeError(f"from_string expects a string, got {s!r} of type {type(s)}") 

4066 try: 

4067 return t.cast("dict[K, V]", self.from_string_list([s])) 

4068 except Exception: 

4069 test = _safe_literal_eval(s) 

4070 if isinstance(test, dict): 

4071 return test 

4072 raise 

4073 

4074 def from_string_list(self, s_list: list[str]) -> t.Any: 

4075 """Return a dict from a list of config strings. 

4076 

4077 This is where we parse CLI configuration. 

4078 

4079 Each item should have the form ``"key=value"``. 

4080 

4081 item parsing is done in :meth:`.item_from_string`. 

4082 """ 

4083 if len(s_list) == 1 and s_list[0] == "None" and self.allow_none: 

4084 return None 

4085 if len(s_list) == 1 and s_list[0].startswith("{") and s_list[0].endswith("}"): 

4086 warn( 

4087 f"--{self.name}={s_list[0]} for dict-traits is deprecated in traitlets 5.0. " 

4088 f"You can pass --{self.name} <key=value> ... multiple times to add items to a dict.", 

4089 DeprecationWarning, 

4090 stacklevel=2, 

4091 ) 

4092 

4093 return literal_eval(s_list[0]) 

4094 

4095 combined = {} 

4096 for d in [self.item_from_string(s) for s in s_list]: 

4097 combined.update(d) 

4098 return combined 

4099 

4100 def item_from_string(self, s: str) -> dict[K, V]: 

4101 """Cast a single-key dict from a string. 

4102 

4103 Evaluated when parsing CLI configuration from a string. 

4104 

4105 Dicts expect strings of the form key=value. 

4106 

4107 Returns a one-key dictionary, 

4108 which will be merged in :meth:`.from_string_list`. 

4109 """ 

4110 

4111 if "=" not in s: 

4112 raise TraitError( 

4113 f"'{self.__class__.__name__}' options must have the form 'key=value', got {s!r}" 

4114 ) 

4115 key, value = s.split("=", 1) 

4116 

4117 # cast key with key trait, if defined 

4118 if self._key_trait: 

4119 key = self._key_trait.from_string(key) 

4120 

4121 # cast value with value trait, if defined (per-key or global) 

4122 value_trait = (self._per_key_traits or {}).get(key, self._value_trait) 

4123 if value_trait: 

4124 value = value_trait.from_string(value) 

4125 return t.cast("dict[K, V]", {key: value}) 

4126 

4127 

4128class TCPAddress(TraitType[G, S]): 

4129 """A trait for an (ip, port) tuple. 

4130 

4131 This allows for both IPv4 IP addresses as well as hostnames. 

4132 """ 

4133 

4134 default_value = ("127.0.0.1", 0) 

4135 info_text = "an (ip, port) tuple" 

4136 

4137 if t.TYPE_CHECKING: 

4138 

4139 @t.overload 

4140 def __init__( 

4141 self: TCPAddress[tuple[str, int], tuple[str, int]], 

4142 default_value: bool | Sentinel = ..., 

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

4144 read_only: bool | None = ..., 

4145 help: str | None = ..., 

4146 config: t.Any = ..., 

4147 **kwargs: t.Any, 

4148 ) -> None: 

4149 ... 

4150 

4151 @t.overload 

4152 def __init__( 

4153 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None], 

4154 default_value: bool | None | Sentinel = ..., 

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

4156 read_only: bool | None = ..., 

4157 help: str | None = ..., 

4158 config: t.Any = ..., 

4159 **kwargs: t.Any, 

4160 ) -> None: 

4161 ... 

4162 

4163 def __init__( 

4164 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None] 

4165 | TCPAddress[tuple[str, int], tuple[str, int]], 

4166 default_value: bool | None | Sentinel = Undefined, 

4167 allow_none: Literal[True, False] = False, 

4168 read_only: bool | None = None, 

4169 help: str | None = None, 

4170 config: t.Any = None, 

4171 **kwargs: t.Any, 

4172 ) -> None: 

4173 ... 

4174 

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

4176 if isinstance(value, tuple): 

4177 if len(value) == 2: 

4178 if isinstance(value[0], str) and isinstance(value[1], int): 

4179 port = value[1] 

4180 if port >= 0 and port <= 65535: 

4181 return t.cast(G, value) 

4182 self.error(obj, value) 

4183 

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

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

4186 return t.cast(G, None) 

4187 if ":" not in s: 

4188 raise ValueError("Require `ip:port`, got %r" % s) 

4189 ip, port_str = s.split(":", 1) 

4190 port = int(port_str) 

4191 return t.cast(G, (ip, port)) 

4192 

4193 

4194class CRegExp(TraitType["re.Pattern[t.Any]", t.Union["re.Pattern[t.Any]", str]]): 

4195 """A casting compiled regular expression trait. 

4196 

4197 Accepts both strings and compiled regular expressions. The resulting 

4198 attribute will be a compiled regular expression.""" 

4199 

4200 info_text = "a regular expression" 

4201 

4202 def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None: 

4203 try: 

4204 return re.compile(value) 

4205 except Exception: 

4206 self.error(obj, value) 

4207 

4208 

4209class UseEnum(TraitType[t.Any, t.Any]): 

4210 """Use a Enum class as model for the data type description. 

4211 Note that if no default-value is provided, the first enum-value is used 

4212 as default-value. 

4213 

4214 .. sourcecode:: python 

4215 

4216 # -- SINCE: Python 3.4 (or install backport: pip install enum34) 

4217 import enum 

4218 from traitlets import HasTraits, UseEnum 

4219 

4220 

4221 class Color(enum.Enum): 

4222 red = 1 # -- IMPLICIT: default_value 

4223 blue = 2 

4224 green = 3 

4225 

4226 

4227 class MyEntity(HasTraits): 

4228 color = UseEnum(Color, default_value=Color.blue) 

4229 

4230 

4231 entity = MyEntity(color=Color.red) 

4232 entity.color = Color.green # USE: Enum-value (preferred) 

4233 entity.color = "green" # USE: name (as string) 

4234 entity.color = "Color.green" # USE: scoped-name (as string) 

4235 entity.color = 3 # USE: number (as int) 

4236 assert entity.color is Color.green 

4237 """ 

4238 

4239 default_value: enum.Enum | None = None 

4240 info_text = "Trait type adapter to a Enum class" 

4241 

4242 def __init__( 

4243 self, enum_class: type[t.Any], default_value: t.Any = None, **kwargs: t.Any 

4244 ) -> None: 

4245 assert issubclass(enum_class, enum.Enum), "REQUIRE: enum.Enum, but was: %r" % enum_class 

4246 allow_none = kwargs.get("allow_none", False) 

4247 if default_value is None and not allow_none: 

4248 default_value = next(iter(enum_class.__members__.values())) 

4249 super().__init__(default_value=default_value, **kwargs) 

4250 self.enum_class = enum_class 

4251 self.name_prefix = enum_class.__name__ + "." 

4252 

4253 def select_by_number(self, value: int, default: t.Any = Undefined) -> t.Any: 

4254 """Selects enum-value by using its number-constant.""" 

4255 assert isinstance(value, int) 

4256 enum_members = self.enum_class.__members__ 

4257 for enum_item in enum_members.values(): 

4258 if enum_item.value == value: 

4259 return enum_item 

4260 # -- NOT FOUND: 

4261 return default 

4262 

4263 def select_by_name(self, value: str, default: t.Any = Undefined) -> t.Any: 

4264 """Selects enum-value by using its name or scoped-name.""" 

4265 assert isinstance(value, str) 

4266 if value.startswith(self.name_prefix): 

4267 # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" 

4268 value = value.replace(self.name_prefix, "", 1) 

4269 return self.enum_class.__members__.get(value, default) 

4270 

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

4272 if isinstance(value, self.enum_class): 

4273 return value 

4274 elif isinstance(value, int): 

4275 # -- CONVERT: number => enum_value (item) 

4276 value2 = self.select_by_number(value) 

4277 if value2 is not Undefined: 

4278 return value2 

4279 elif isinstance(value, str): 

4280 # -- CONVERT: name or scoped_name (as string) => enum_value (item) 

4281 value2 = self.select_by_name(value) 

4282 if value2 is not Undefined: 

4283 return value2 

4284 elif value is None: 

4285 if self.allow_none: 

4286 return None 

4287 else: 

4288 return self.default_value 

4289 self.error(obj, value) 

4290 

4291 def _choices_str(self, as_rst: bool = False) -> str: 

4292 """Returns a description of the trait choices (not none).""" 

4293 choices = self.enum_class.__members__.keys() 

4294 if as_rst: 

4295 return "|".join("``%r``" % x for x in choices) 

4296 else: 

4297 return repr(list(choices)) # Listify because py3.4- prints odict-class 

4298 

4299 def _info(self, as_rst: bool = False) -> str: 

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

4301 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

4302 return f"any of {self._choices_str(as_rst)}{none}" 

4303 

4304 def info(self) -> str: 

4305 return self._info(as_rst=False) 

4306 

4307 def info_rst(self) -> str: 

4308 return self._info(as_rst=True) 

4309 

4310 

4311class Callable(TraitType[t.Callable[..., t.Any], t.Callable[..., t.Any]]): 

4312 """A trait which is callable. 

4313 

4314 Notes 

4315 ----- 

4316 Classes are callable, as are instances 

4317 with a __call__() method.""" 

4318 

4319 info_text = "a callable" 

4320 

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

4322 if callable(value): 

4323 return value 

4324 else: 

4325 self.error(obj, value)