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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1744 statements  

1""" 

2A lightweight Traits like module. 

3 

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

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

6 

7* Validation 

8* Type specification with defaults 

9* Static and dynamic notification 

10* Basic predefined types 

11* An API that is similar to enthought.traits 

12 

13We don't support: 

14 

15* Delegation 

16* Automatic GUI generation 

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

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

19 contents change. 

20* API compatibility with enthought.traits 

21 

22There are also some important difference in our design: 

23 

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

25 

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

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

28including Jython and IronPython. 

29 

30Inheritance diagram: 

31 

32.. inheritance-diagram:: traitlets.traitlets 

33 :parts: 3 

34""" 

35 

36# Copyright (c) IPython Development Team. 

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

38# 

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

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

41 

42from __future__ import annotations 

43 

44import contextlib 

45import enum 

46import inspect 

47import numbers 

48import os 

49import re 

50import sys 

51import types 

52import typing as t 

53from ast import literal_eval 

54 

55from .utils.bunch import Bunch 

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

57from .utils.getargspec import getargspec 

58from .utils.importstring import import_item 

59from .utils.sentinel import Sentinel 

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

61 

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

63 

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

65ClassTypes = (type,) 

66 

67if t.TYPE_CHECKING: 

68 from typing_extensions import TypeVar 

69else: 

70 from typing import TypeVar 

71 

72# exports: 

73 

74__all__ = [ 

75 "All", 

76 "Any", 

77 "BaseDescriptor", 

78 "Bool", 

79 "Bytes", 

80 "CBool", 

81 "CBytes", 

82 "CComplex", 

83 "CFloat", 

84 "CInt", 

85 "CLong", 

86 "CRegExp", 

87 "CUnicode", 

88 "Callable", 

89 "CaselessStrEnum", 

90 "ClassBasedTraitType", 

91 "Complex", 

92 "Container", 

93 "DefaultHandler", 

94 "Dict", 

95 "DottedObjectName", 

96 "Enum", 

97 "EventHandler", 

98 "Float", 

99 "ForwardDeclaredInstance", 

100 "ForwardDeclaredMixin", 

101 "ForwardDeclaredType", 

102 "FuzzyEnum", 

103 "HasDescriptors", 

104 "HasTraits", 

105 "Instance", 

106 "Int", 

107 "Integer", 

108 "List", 

109 "Long", 

110 "MetaHasDescriptors", 

111 "MetaHasTraits", 

112 "ObjectName", 

113 "ObserveHandler", 

114 "Set", 

115 "TCPAddress", 

116 "This", 

117 "TraitError", 

118 "TraitType", 

119 "Tuple", 

120 "Type", 

121 "Unicode", 

122 "Undefined", 

123 "Union", 

124 "UseEnum", 

125 "ValidateHandler", 

126 "default", 

127 "directional_link", 

128 "dlink", 

129 "link", 

130 "observe", 

131 "observe_compat", 

132 "parse_notifier_name", 

133 "validate", 

134] 

135 

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

137 

138# ----------------------------------------------------------------------------- 

139# Basic classes 

140# ----------------------------------------------------------------------------- 

141 

142 

143Undefined = Sentinel( 

144 "Undefined", 

145 "traitlets", 

146 """ 

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

148""", 

149) 

150 

151All = Sentinel( 

152 "All", 

153 "traitlets", 

154 """ 

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

156from all trait attributes. 

157""", 

158) 

159 

160# Deprecated alias 

161NoDefaultSpecified = Undefined 

162 

163 

164class TraitError(Exception): 

165 pass 

166 

167 

168# ----------------------------------------------------------------------------- 

169# Utilities 

170# ----------------------------------------------------------------------------- 

171 

172 

173def isidentifier(s: str) -> bool: 

174 warn( 

175 "traitlets.traitlets.isidentifier(s) is deprecated since traitlets 5.14.4 Use `s.isidentifier()`.", 

176 DeprecationWarning, 

177 stacklevel=2, 

178 ) 

179 return s.isidentifier() 

180 

181 

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

183 """Safely evaluate an expression 

184 

185 Returns original string if eval fails. 

186 

187 Use only where types are ambiguous. 

188 """ 

189 try: 

190 return literal_eval(s) 

191 except (NameError, SyntaxError, ValueError): 

192 return s 

193 

194 

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

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

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

198 

199 

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

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

202 

203 Examples 

204 -------- 

205 >>> parse_notifier_name([]) 

206 [traitlets.All] 

207 >>> parse_notifier_name("a") 

208 ['a'] 

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

210 ['a', 'b'] 

211 >>> parse_notifier_name(All) 

212 [traitlets.All] 

213 """ 

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

215 return [names] 

216 elif isinstance(names, Sentinel): 

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

218 else: 

219 if not names or All in names: 

220 return [All] 

221 for n in names: 

222 if not isinstance(n, str): 

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

224 return names 

225 

226 

227class _SimpleTest: 

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

229 self.value = value 

230 

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

232 return bool(test == self.value) 

233 

234 def __repr__(self) -> str: 

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

236 

237 def __str__(self) -> str: 

238 return self.__repr__() 

239 

240 

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

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

243 

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

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

246 in zope.interface with the __provides__ attribute. 

247 """ 

248 results = [] 

249 for key in dir(object): 

250 try: 

251 value = getattr(object, key) 

252 except AttributeError: 

253 pass 

254 else: 

255 if not predicate or predicate(value): 

256 results.append((key, value)) 

257 results.sort() 

258 return results 

259 

260 

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

262 """Validate arguments for traitlet link functions""" 

263 for tup in tuples: 

264 if not len(tup) == 2: 

265 raise TypeError( 

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

267 ) 

268 obj, trait_name = tup 

269 if not isinstance(obj, HasTraits): 

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

271 if trait_name not in obj.traits(): 

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

273 

274 

275class link: 

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

277 

278 Parameters 

279 ---------- 

280 source : (object / attribute name) pair 

281 target : (object / attribute name) pair 

282 transform: iterable with two callables (optional) 

283 Data transformation between source and target and target and source. 

284 

285 Examples 

286 -------- 

287 >>> class X(HasTraits): 

288 ... value = Int() 

289 

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

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

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

293 

294 Setting source updates target objects: 

295 >>> src.value = 5 

296 >>> tgt.value 

297 5 

298 """ 

299 

300 updating = False 

301 

302 def __init__( 

303 self, source: t.Any, target: t.Any, transform: t.Iterable[FuncT] | None = None 

304 ) -> None: 

305 _validate_link(source, target) 

306 self.source, self.target = source, target 

307 if transform: 

308 self._transform, self._transform_inv = transform # type:ignore[method-assign] 

309 self.link() 

310 

311 def _transform(self, x: T) -> T: 

312 """default transform: no-op""" 

313 return x 

314 

315 _transform_inv = _transform 

316 

317 def link(self) -> None: 

318 try: 

319 setattr( 

320 self.target[0], 

321 self.target[1], 

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

323 ) 

324 

325 finally: 

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

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

328 

329 @contextlib.contextmanager 

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

331 self.updating = True 

332 try: 

333 yield 

334 finally: 

335 self.updating = False 

336 

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

338 if self.updating: 

339 return 

340 with self._busy_updating(): 

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

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

343 raise TraitError( 

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

345 ) 

346 

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

348 if self.updating: 

349 return 

350 with self._busy_updating(): 

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

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

353 raise TraitError( 

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

355 ) 

356 

357 def unlink(self) -> None: 

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

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

360 

361 

362class directional_link: 

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

364 

365 Parameters 

366 ---------- 

367 source : (object, attribute name) pair 

368 target : (object, attribute name) pair 

369 transform: callable (optional) 

370 Data transformation between source and target. 

371 

372 Examples 

373 -------- 

374 >>> class X(HasTraits): 

375 ... value = Int() 

376 

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

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

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

380 

381 Setting source updates target objects: 

382 >>> src.value = 5 

383 >>> tgt.value 

384 5 

385 

386 Setting target does not update source object: 

387 >>> tgt.value = 6 

388 >>> src.value 

389 5 

390 

391 """ 

392 

393 updating = False 

394 

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

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

397 _validate_link(source, target) 

398 self.source, self.target = source, target 

399 self.link() 

400 

401 def link(self) -> None: 

402 try: 

403 setattr( 

404 self.target[0], 

405 self.target[1], 

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

407 ) 

408 finally: 

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

410 

411 @contextlib.contextmanager 

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

413 self.updating = True 

414 try: 

415 yield 

416 finally: 

417 self.updating = False 

418 

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

420 if self.updating: 

421 return 

422 with self._busy_updating(): 

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

424 

425 def unlink(self) -> None: 

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

427 

428 

429dlink = directional_link 

430 

431 

432# ----------------------------------------------------------------------------- 

433# Base Descriptor Class 

434# ----------------------------------------------------------------------------- 

435 

436 

437class BaseDescriptor: 

438 """Base descriptor class 

439 

440 Notes 

441 ----- 

442 This implements Python's descriptor protocol. 

443 

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

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

446 class that does the following: 

447 

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

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

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

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

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

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

454 """ 

455 

456 name: str | None = None 

457 this_class: type[HasTraits] | None = None 

458 

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

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

461 HasDescriptors class. 

462 

463 It is typically overloaded for specific types. 

464 

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

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

467 has been assigned. 

468 """ 

469 self.this_class = cls 

470 self.name = name 

471 

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

473 # Instead of HasDescriptors.setup_instance calling 

474 # every instance_init, we opt in by default. 

475 # This gives descriptors a change to opt out for 

476 # performance reasons. 

477 # Because most traits do not need instance_init, 

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

479 # being created, this otherwise gives a significant performance 

480 # pentalty. Most TypeTraits in traitlets opt out. 

481 cls._instance_inits.append(self.instance_init) 

482 

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

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

485 HasDescriptors instance. 

486 

487 It is typically overloaded for specific types. 

488 

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

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

491 other descriptors. 

492 """ 

493 

494 

495G = TypeVar("G") 

496S = TypeVar("S") 

497T = TypeVar("T") 

498 

499 

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

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

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

503if t.TYPE_CHECKING: 

504 from typing_extensions import Literal, Self 

505 

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

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

508else: 

509 # This is required to avoid warnings about unresolved references when generating 

510 # the documentation of downstream projects. 

511 K = TypeVar("K") 

512 V = TypeVar("V") 

513 

514 

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

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

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

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

519 

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

521 allow_none: bool = False 

522 read_only: bool = False 

523 info_text: str = "any value" 

524 default_value: t.Any = Undefined 

525 

526 def __init__( 

527 self: TraitType[G, S], 

528 default_value: t.Any = Undefined, 

529 allow_none: bool = False, 

530 read_only: bool | None = None, 

531 help: str | None = None, 

532 config: t.Any = None, 

533 **kwargs: t.Any, 

534 ) -> None: 

535 """Declare a traitlet. 

536 

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

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

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

540 

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

542 

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

544 

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

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

547 """ 

548 if default_value is not Undefined: 

549 self.default_value = default_value 

550 if allow_none: 

551 self.allow_none = allow_none 

552 if read_only is not None: 

553 self.read_only = read_only 

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

555 if self.help: 

556 # define __doc__ so that inspectors like autodoc find traits 

557 self.__doc__ = self.help 

558 

559 if len(kwargs) > 0: 

560 stacklevel = 1 

561 f = inspect.currentframe() 

562 # count supers to determine stacklevel for warning 

563 assert f is not None 

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

565 stacklevel += 1 

566 f = f.f_back 

567 assert f is not None 

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

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

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

571 if should_warn(key): 

572 warn( 

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

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

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

576 DeprecationWarning, 

577 stacklevel=stacklevel, 

578 ) 

579 if len(self.metadata) > 0: 

580 self.metadata = self.metadata.copy() 

581 self.metadata.update(kwargs) 

582 else: 

583 self.metadata = kwargs 

584 else: 

585 self.metadata = self.metadata.copy() 

586 if config is not None: 

587 self.metadata["config"] = config 

588 

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

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

591 if help is not None: 

592 self.metadata["help"] = help 

593 

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

595 """Get a value from a config string 

596 

597 such as an environment variable or CLI arguments. 

598 

599 Traits can override this method to define their own 

600 parsing of config strings. 

601 

602 .. seealso:: item_from_string 

603 

604 .. versionadded:: 5.0 

605 """ 

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

607 return None 

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

609 

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

611 """The default generator for this trait 

612 

613 Notes 

614 ----- 

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

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

617 """ 

618 if self.default_value is not Undefined: 

619 return self.default_value # type:ignore[no-any-return] 

620 elif hasattr(self, "make_dynamic_default"): 

621 return self.make_dynamic_default() # type:ignore[no-any-return] 

622 else: 

623 # Undefined will raise in TraitType.get 

624 return self.default_value # type:ignore[no-any-return] 

625 

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

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

628 Use self.default_value instead 

629 """ 

630 warn( 

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

632 DeprecationWarning, 

633 stacklevel=2, 

634 ) 

635 return self.default_value # type:ignore[no-any-return] 

636 

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

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

639 warn( 

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

641 DeprecationWarning, 

642 stacklevel=2, 

643 ) 

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

645 obj._trait_values[self.name] = value 

646 return value 

647 

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

649 assert self.name is not None 

650 try: 

651 value = obj._trait_values[self.name] 

652 except KeyError: 

653 # Check for a dynamic initializer. 

654 default = obj.trait_defaults(self.name) 

655 if default is Undefined: 

656 warn( 

657 "Explicit using of Undefined as the default value " 

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

659 "exceptions in the future.", 

660 DeprecationWarning, 

661 stacklevel=2, 

662 ) 

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

664 # write out the obj.cross_validation_lock call here. 

665 _cross_validation_lock = obj._cross_validation_lock 

666 try: 

667 obj._cross_validation_lock = True 

668 value = self._validate(obj, default) 

669 finally: 

670 obj._cross_validation_lock = _cross_validation_lock 

671 obj._trait_values[self.name] = value 

672 obj._notify_observers( 

673 Bunch( 

674 name=self.name, 

675 value=value, 

676 owner=obj, 

677 type="default", 

678 ) 

679 ) 

680 return value # type:ignore[no-any-return] 

681 except Exception as e: 

682 # This should never be reached. 

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

684 else: 

685 return value # type:ignore[no-any-return] 

686 

687 @t.overload 

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

689 ... 

690 

691 @t.overload 

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

693 ... 

694 

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

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

697 

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

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

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

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

702 """ 

703 if obj is None: 

704 return self 

705 else: 

706 return self.get(obj, cls) # type:ignore[return-value] 

707 

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

709 new_value = self._validate(obj, value) 

710 assert self.name is not None 

711 try: 

712 old_value = obj._trait_values[self.name] 

713 except KeyError: 

714 old_value = self.default_value 

715 

716 obj._trait_values[self.name] = new_value 

717 try: 

718 silent = bool(old_value == new_value) 

719 except Exception: 

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

721 silent = False 

722 if silent is not True: 

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

724 # comparison above returns something other than True/False 

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

726 

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

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

729 

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

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

732 """ 

733 if self.read_only: 

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

735 self.set(obj, value) 

736 

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

738 if value is None and self.allow_none: 

739 return value 

740 if hasattr(self, "validate"): 

741 value = self.validate(obj, value) 

742 if obj._cross_validation_lock is False: 

743 value = self._cross_validate(obj, value) 

744 return value # type:ignore[no-any-return] 

745 

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

747 if self.name in obj._trait_validators: 

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

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

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

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

752 cross_validate = getattr(obj, meth_name) 

753 deprecated_method( 

754 cross_validate, 

755 obj.__class__, 

756 meth_name, 

757 "use @validate decorator instead.", 

758 ) 

759 value = cross_validate(value, self) 

760 return value # type:ignore[no-any-return] 

761 

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

763 if isinstance(other, Union): 

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

765 else: 

766 return Union([self, other]) 

767 

768 def info(self) -> str: 

769 return self.info_text 

770 

771 def error( 

772 self, 

773 obj: HasTraits | None, 

774 value: t.Any, 

775 error: Exception | None = None, 

776 info: str | None = None, 

777 ) -> t.NoReturn: 

778 """Raise a TraitError 

779 

780 Parameters 

781 ---------- 

782 obj : HasTraits or None 

783 The instance which owns the trait. If not 

784 object is given, then an object agnostic 

785 error will be raised. 

786 value : any 

787 The value that caused the error. 

788 error : Exception (default: None) 

789 An error that was raised by a child trait. 

790 The arguments of this exception should be 

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

792 Where the ``value`` and ``info`` are the 

793 problem value, and string describing the 

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

795 of :class:`TraitType` instances that are 

796 "children" of this one (the first being 

797 the deepest). 

798 info : str (default: None) 

799 A description of the expected value. By 

800 default this is inferred from this trait's 

801 ``info`` method. 

802 """ 

803 if error is not None: 

804 # handle nested error 

805 error.args += (self,) 

806 if self.name is not None: 

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

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

809 if obj is not None: 

810 error.args = ( 

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

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

813 self.name, 

814 describe("an", obj), 

815 chain, 

816 error.args[1], 

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

818 ), 

819 ) 

820 else: 

821 error.args = ( 

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

823 self.name, 

824 chain, 

825 error.args[1], 

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

827 ), 

828 ) 

829 raise error 

830 

831 # this trait caused an error 

832 if self.name is None: 

833 # this is not the root trait 

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

835 

836 # this is the root trait 

837 if obj is not None: 

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

839 self.name, 

840 class_of(obj), 

841 info or self.info(), 

842 describe("the", value), 

843 ) 

844 else: 

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

846 self.name, 

847 info or self.info(), 

848 describe("the", value), 

849 ) 

850 raise TraitError(e) 

851 

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

853 """DEPRECATED: Get a metadata value. 

854 

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

856 """ 

857 if key == "help": 

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

859 else: 

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

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

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

863 

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

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

866 

867 Use .metadata[key] = value instead. 

868 """ 

869 if key == "help": 

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

871 else: 

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

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

874 self.metadata[key] = value 

875 

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

877 """Sets metadata and returns self. 

878 

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

880 

881 Examples 

882 -------- 

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

884 <traitlets.traitlets.Int object at ...> 

885 

886 """ 

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

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

889 ) 

890 if maybe_constructor_keywords: 

891 warn( 

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

893 % maybe_constructor_keywords, 

894 UserWarning, 

895 stacklevel=2, 

896 ) 

897 

898 self.metadata.update(metadata) 

899 return self 

900 

901 def default_value_repr(self) -> str: 

902 return repr(self.default_value) 

903 

904 

905# ----------------------------------------------------------------------------- 

906# The HasTraits implementation 

907# ----------------------------------------------------------------------------- 

908 

909 

910class _CallbackWrapper: 

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

912 

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

914 callbacks. 

915 """ 

916 

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

918 self.cb = cb 

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

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

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

922 if self.nargs > 4: 

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

924 

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

926 # The wrapper is equal to the wrapped element 

927 if isinstance(other, _CallbackWrapper): 

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

929 else: 

930 return bool(self.cb == other) 

931 

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

933 # The wrapper is callable 

934 if self.nargs == 0: 

935 self.cb() 

936 elif self.nargs == 1: 

937 self.cb(change.name) 

938 elif self.nargs == 2: 

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

940 elif self.nargs == 3: 

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

942 elif self.nargs == 4: 

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

944 

945 

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

947 if isinstance(cb, _CallbackWrapper): 

948 return cb 

949 else: 

950 return _CallbackWrapper(cb) 

951 

952 

953class MetaHasDescriptors(type): 

954 """A metaclass for HasDescriptors. 

955 

956 This metaclass makes sure that any TraitType class attributes are 

957 instantiated and sets their name attribute. 

958 """ 

959 

960 def __new__( 

961 mcls: type[MetaHasDescriptors], 

962 name: str, 

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

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

965 **kwds: t.Any, 

966 ) -> MetaHasDescriptors: 

967 """Create the HasDescriptors class.""" 

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

969 # ---------------------------------------------------------------- 

970 # Support of deprecated behavior allowing for TraitType types 

971 # to be used instead of TraitType instances. 

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

973 warn( 

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

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

976 DeprecationWarning, 

977 stacklevel=2, 

978 ) 

979 classdict[k] = v() 

980 # ---------------------------------------------------------------- 

981 

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

983 

984 def __init__( 

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

986 ) -> None: 

987 """Finish initializing the HasDescriptors class.""" 

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

989 cls.setup_class(classdict) 

990 

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

992 """Setup descriptor instance on the class 

993 

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

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

996 calling their :attr:`class_init` method. 

997 """ 

998 cls._descriptors = [] 

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

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

1001 if isinstance(v, BaseDescriptor): 

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

1003 

1004 for _, v in getmembers(cls): 

1005 if isinstance(v, BaseDescriptor): 

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

1007 cls._descriptors.append(v) 

1008 

1009 

1010class MetaHasTraits(MetaHasDescriptors): 

1011 """A metaclass for HasTraits.""" 

1012 

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

1014 # for only the current class 

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

1016 # also looking at base classes 

1017 cls._all_trait_default_generators = {} 

1018 cls._traits = {} 

1019 cls._static_immutable_initial_values = {} 

1020 

1021 super().setup_class(classdict) 

1022 

1023 mro = cls.mro() 

1024 

1025 for name in dir(cls): 

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

1027 # __provides__ attributes even though they exist. This causes 

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

1029 try: 

1030 value = getattr(cls, name) 

1031 except AttributeError: 

1032 continue 

1033 if isinstance(value, TraitType): 

1034 cls._traits[name] = value 

1035 trait = value 

1036 default_method_name = "_%s_default" % name 

1037 mro_trait = mro 

1038 try: 

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

1040 except ValueError: 

1041 # this_class not in mro 

1042 pass 

1043 for c in mro_trait: 

1044 if default_method_name in c.__dict__: 

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

1046 break 

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

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

1049 break 

1050 else: 

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

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

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

1054 # of initial values to speed up instance creation. 

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

1056 # for instance ipywidgets. 

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

1058 if ( 

1059 type(trait) in [CInt, Int] 

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

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

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

1063 ): 

1064 cls._static_immutable_initial_values[name] = trait.default_value 

1065 elif ( 

1066 type(trait) in [CFloat, Float] 

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

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

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

1070 ): 

1071 cls._static_immutable_initial_values[name] = trait.default_value 

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

1073 isinstance(trait.default_value, bool) or none_ok 

1074 ): 

1075 cls._static_immutable_initial_values[name] = trait.default_value 

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

1077 isinstance(trait.default_value, str) or none_ok 

1078 ): 

1079 cls._static_immutable_initial_values[name] = trait.default_value 

1080 elif type(trait) == Any and ( 

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

1082 ): 

1083 cls._static_immutable_initial_values[name] = trait.default_value 

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

1085 cls._static_immutable_initial_values[name] = None 

1086 elif ( 

1087 isinstance(trait, Instance) 

1088 and trait.default_args is None 

1089 and trait.default_kwargs is None 

1090 and trait.allow_none 

1091 ): 

1092 cls._static_immutable_initial_values[name] = None 

1093 

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

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

1096 cls._all_trait_default_generators[name] = trait.default 

1097 

1098 

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

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

1101 

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

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

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

1105 name of the attribute that triggered the notification. 

1106 

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

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

1109 * ``owner`` : the HasTraits instance 

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

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

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

1113 

1114 Parameters 

1115 ---------- 

1116 *names 

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

1118 type : str, kwarg-only 

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

1120 """ 

1121 if not names: 

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

1123 for name in names: 

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

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

1126 return ObserveHandler(names, type=type) 

1127 

1128 

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

1130 """Backward-compatibility shim decorator for observers 

1131 

1132 Use with: 

1133 

1134 @observe('name') 

1135 @observe_compat 

1136 def _foo_changed(self, change): 

1137 ... 

1138 

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

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

1141 """ 

1142 

1143 def compatible_observer( 

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

1145 ) -> t.Any: 

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

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

1148 else: 

1149 clsname = self.__class__.__name__ 

1150 warn( 

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

1152 DeprecationWarning, 

1153 stacklevel=2, 

1154 ) 

1155 change = Bunch( 

1156 type="change", 

1157 old=old, 

1158 new=new, 

1159 name=change_or_name, 

1160 owner=self, 

1161 ) 

1162 return func(self, change) 

1163 

1164 return compatible_observer # type:ignore[return-value] 

1165 

1166 

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

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

1169 when a Trait is set. 

1170 

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

1172 The proposal dictionary must hold the following keys: 

1173 

1174 * ``owner`` : the HasTraits instance 

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

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

1177 

1178 Parameters 

1179 ---------- 

1180 *names 

1181 The str names of the Traits to validate. 

1182 

1183 Notes 

1184 ----- 

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

1186 the registered cross validator could potentially make changes to attributes 

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

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

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

1190 commute. 

1191 """ 

1192 if not names: 

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

1194 for name in names: 

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

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

1197 return ValidateHandler(names) 

1198 

1199 

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

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

1202 

1203 Parameters 

1204 ---------- 

1205 name 

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

1207 

1208 Notes 

1209 ----- 

1210 Unlike observers and validators which are properties of the HasTraits 

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

1212 

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

1214 subclasses of `this_type`. 

1215 

1216 :: 

1217 

1218 class A(HasTraits): 

1219 bar = Int() 

1220 

1221 @default('bar') 

1222 def get_bar_default(self): 

1223 return 11 

1224 

1225 class B(A): 

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

1227 # the base class A 

1228 

1229 class C(B): 

1230 

1231 @default('bar') 

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

1233 return 3.0 # ignored since it is defined in a 

1234 # class derived from B.a.this_class. 

1235 """ 

1236 if not isinstance(name, str): 

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

1238 return DefaultHandler(name) 

1239 

1240 

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

1242 

1243 

1244class EventHandler(BaseDescriptor): 

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

1246 self.func = func 

1247 return self 

1248 

1249 @t.overload 

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

1251 ... 

1252 

1253 @t.overload 

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

1255 ... 

1256 

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

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

1259 if hasattr(self, "func"): 

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

1261 else: 

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

1263 

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

1265 if inst is None: 

1266 return self 

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

1268 

1269 

1270class ObserveHandler(EventHandler): 

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

1272 self.trait_names = names 

1273 self.type = type 

1274 

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

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

1277 

1278 

1279class ValidateHandler(EventHandler): 

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

1281 self.trait_names = names 

1282 

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

1284 inst._register_validator(self, self.trait_names) 

1285 

1286 

1287class DefaultHandler(EventHandler): 

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

1289 self.trait_name = name 

1290 

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

1292 super().class_init(cls, name) 

1293 cls._trait_default_generators[self.trait_name] = self 

1294 

1295 

1296class HasDescriptors(metaclass=MetaHasDescriptors): 

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

1298 

1299 def __new__(cls, /, *args: t.Any, **kwargs: t.Any) -> Self: 

1300 # This is needed because object.__new__ only accepts 

1301 # the cls argument. 

1302 new_meth = super(HasDescriptors, cls).__new__ 

1303 if new_meth is object.__new__: 

1304 inst = new_meth(cls) 

1305 else: 

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

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

1308 return inst 

1309 

1310 def setup_instance(self, /, *args: t.Any, **kwargs: t.Any) -> None: 

1311 """ 

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

1313 """ 

1314 

1315 self._cross_validation_lock = False 

1316 cls = self.__class__ 

1317 # Let descriptors performance initialization when a HasDescriptor 

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

1319 # default creations or other bookkeepings. 

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

1321 # subclass_init. 

1322 for init in cls._instance_inits: 

1323 init(self) 

1324 

1325 

1326class HasTraits(HasDescriptors, metaclass=MetaHasTraits): 

1327 _trait_values: dict[str, t.Any] 

1328 _static_immutable_initial_values: dict[str, t.Any] 

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

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

1331 _cross_validation_lock: bool 

1332 _traits: dict[str, t.Any] 

1333 _all_trait_default_generators: dict[str, t.Any] 

1334 

1335 def setup_instance(self, /, *args: t.Any, **kwargs: t.Any) -> None: 

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

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

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

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

1340 self._trait_notifiers = {} 

1341 self._trait_validators = {} 

1342 self._cross_validation_lock = False 

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

1344 

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

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

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

1348 # notifications. 

1349 super_args = args 

1350 super_kwargs = {} 

1351 

1352 if kwargs: 

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

1354 # the hold_trait_notifications(self) context manager 

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

1356 pass 

1357 

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

1359 self._cross_validation_lock = True 

1360 changes = {} 

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

1362 if self.has_trait(key): 

1363 setattr(self, key, value) 

1364 changes[key] = Bunch( 

1365 name=key, 

1366 old=None, 

1367 new=value, 

1368 owner=self, 

1369 type="change", 

1370 ) 

1371 else: 

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

1373 super_kwargs[key] = value 

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

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

1376 for key in changed: 

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

1378 self.set_trait(key, value) 

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

1380 self._cross_validation_lock = False 

1381 # Restore method retrieval from class 

1382 del self.notify_change 

1383 for key in changed: 

1384 self.notify_change(changes[key]) 

1385 

1386 try: 

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

1388 except TypeError as e: 

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

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

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

1392 arg_s = ", ".join(arg_s_list) 

1393 warn( 

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

1395 "{error}\n" 

1396 "This is deprecated in traitlets 4.2." 

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

1398 arg_s=arg_s, 

1399 classname=self.__class__.__name__, 

1400 error=e, 

1401 ), 

1402 DeprecationWarning, 

1403 stacklevel=2, 

1404 ) 

1405 

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

1407 d = self.__dict__.copy() 

1408 # event handlers stored on an instance are 

1409 # expected to be reinstantiated during a 

1410 # recall of instance_init during __setstate__ 

1411 d["_trait_notifiers"] = {} 

1412 d["_trait_validators"] = {} 

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

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

1415 

1416 return d 

1417 

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

1419 self.__dict__ = state.copy() 

1420 

1421 # event handlers are reassigned to self 

1422 cls = self.__class__ 

1423 for key in dir(cls): 

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

1425 # __provides__ attributes even though they exist. This causes 

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

1427 try: 

1428 value = getattr(cls, key) 

1429 except AttributeError: 

1430 pass 

1431 else: 

1432 if isinstance(value, EventHandler): 

1433 value.instance_init(self) 

1434 

1435 @property 

1436 @contextlib.contextmanager 

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

1438 """ 

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

1440 to True. 

1441 

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

1443 prior to entering the block. 

1444 """ 

1445 if self._cross_validation_lock: 

1446 yield 

1447 return 

1448 else: 

1449 try: 

1450 self._cross_validation_lock = True 

1451 yield 

1452 finally: 

1453 self._cross_validation_lock = False 

1454 

1455 @contextlib.contextmanager 

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

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

1458 validation. 

1459 

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

1461 race conditions in trait notifiers requesting other trait values. 

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

1463 """ 

1464 if self._cross_validation_lock: 

1465 yield 

1466 return 

1467 else: 

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

1469 

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

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

1472 if past_changes is None: 

1473 return [change] 

1474 else: 

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

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

1477 else: 

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

1479 past_changes.append(change) 

1480 return past_changes 

1481 

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

1483 name = change.name 

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

1485 

1486 try: 

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

1488 # notifications, disable cross validation and yield. 

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

1490 self._cross_validation_lock = True 

1491 yield 

1492 # Cross validate final values when context is released. 

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

1494 trait = getattr(self.__class__, name) 

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

1496 self.set_trait(name, value) 

1497 except TraitError as e: 

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

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

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

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

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

1503 if change.type == "change": 

1504 if change.old is not Undefined: 

1505 self.set_trait(name, change.old) 

1506 else: 

1507 self._trait_values.pop(name) 

1508 cache = {} 

1509 raise e 

1510 finally: 

1511 self._cross_validation_lock = False 

1512 # Restore method retrieval from class 

1513 del self.notify_change 

1514 

1515 # trigger delayed notifications 

1516 for changes in cache.values(): 

1517 for change in changes: 

1518 self.notify_change(change) 

1519 

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

1521 self.notify_change( 

1522 Bunch( 

1523 name=name, 

1524 old=old_value, 

1525 new=new_value, 

1526 owner=self, 

1527 type="change", 

1528 ) 

1529 ) 

1530 

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

1532 """Notify observers of a change event""" 

1533 return self._notify_observers(change) 

1534 

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

1536 """Notify observers of any event""" 

1537 if not isinstance(event, Bunch): 

1538 # cast to bunch if given a dict 

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

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

1541 

1542 callables = [] 

1543 if name in self._trait_notifiers: 

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

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

1546 if All in self._trait_notifiers: 

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

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

1549 

1550 # Now static ones 

1551 magic_name = "_%s_changed" % name 

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

1553 class_value = getattr(self.__class__, magic_name) 

1554 if not isinstance(class_value, ObserveHandler): 

1555 deprecated_method( 

1556 class_value, 

1557 self.__class__, 

1558 magic_name, 

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

1560 ) 

1561 cb = getattr(self, magic_name) 

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

1563 if cb not in callables: 

1564 callables.append(_callback_wrapper(cb)) 

1565 

1566 # Call them all now 

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

1568 for c in callables: 

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

1570 

1571 if isinstance(c, _CallbackWrapper): 

1572 c = c.__call__ 

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

1574 c = getattr(self, c.name) 

1575 

1576 c(event) 

1577 

1578 def _add_notifiers( 

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

1580 ) -> None: 

1581 if name not in self._trait_notifiers: 

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

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

1584 else: 

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

1586 nlist = [] 

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

1588 else: 

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

1590 if handler not in nlist: 

1591 nlist.append(handler) 

1592 

1593 def _remove_notifiers( 

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

1595 ) -> None: 

1596 try: 

1597 if handler is None: 

1598 del self._trait_notifiers[name][type] 

1599 else: 

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

1601 except KeyError: 

1602 pass 

1603 

1604 def on_trait_change( 

1605 self, 

1606 handler: EventHandler | None = None, 

1607 name: Sentinel | str | None = None, 

1608 remove: bool = False, 

1609 ) -> None: 

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

1611 

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

1613 

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

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

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

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

1618 below). 

1619 

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

1621 handlers for the specified name are uninstalled. 

1622 

1623 Parameters 

1624 ---------- 

1625 handler : callable, None 

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

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

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

1629 name : list, str, None 

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

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

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

1633 remove : bool 

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

1635 then unintall it. 

1636 """ 

1637 warn( 

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

1639 DeprecationWarning, 

1640 stacklevel=2, 

1641 ) 

1642 if name is None: 

1643 name = All 

1644 if remove: 

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

1646 else: 

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

1648 

1649 def observe( 

1650 self, 

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

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

1653 type: Sentinel | str = "change", 

1654 ) -> None: 

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

1656 

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

1658 

1659 Parameters 

1660 ---------- 

1661 handler : callable 

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

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

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

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

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

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

1668 * ``owner`` : the HasTraits instance 

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

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

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

1672 names : list, str, All 

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

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

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

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

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

1678 notifications are passed to the observe handler. 

1679 """ 

1680 for name in parse_notifier_name(names): 

1681 self._add_notifiers(handler, name, type) 

1682 

1683 def unobserve( 

1684 self, 

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

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

1687 type: Sentinel | str = "change", 

1688 ) -> None: 

1689 """Remove a trait change handler. 

1690 

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

1692 

1693 Parameters 

1694 ---------- 

1695 handler : callable 

1696 The callable called when a trait attribute changes. 

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

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

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

1700 from the list of notifiers corresponding to all changes. 

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

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

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

1704 """ 

1705 for name in parse_notifier_name(names): 

1706 self._remove_notifiers(handler, name, type) 

1707 

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

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

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

1711 if name is All: 

1712 self._trait_notifiers = {} 

1713 else: 

1714 try: 

1715 del self._trait_notifiers[name] 

1716 except KeyError: 

1717 pass 

1718 

1719 def _register_validator( 

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

1721 ) -> None: 

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

1723 

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

1725 

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

1727 TraitError is raised and no new validator is registered. 

1728 

1729 Parameters 

1730 ---------- 

1731 handler : callable 

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

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

1734 with the following attributes/keys: 

1735 * ``owner`` : the HasTraits instance 

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

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

1738 names : List of strings 

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

1740 """ 

1741 for name in names: 

1742 magic_name = "_%s_validate" % name 

1743 if hasattr(self, magic_name): 

1744 class_value = getattr(self.__class__, magic_name) 

1745 if not isinstance(class_value, ValidateHandler): 

1746 deprecated_method( 

1747 class_value, 

1748 self.__class__, 

1749 magic_name, 

1750 "use @validate decorator instead.", 

1751 ) 

1752 for name in names: 

1753 self._trait_validators[name] = handler 

1754 

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

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

1757 cls = self.__class__ 

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

1759 if hasattr(cls, "__qualname__"): 

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

1761 attrs["__qualname__"] = cls.__qualname__ 

1762 attrs.update(traits) 

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

1764 for trait in traits.values(): 

1765 trait.instance_init(self) 

1766 

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

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

1769 cls = self.__class__ 

1770 if not self.has_trait(name): 

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

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

1773 

1774 @classmethod 

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

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

1777 

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

1779 but is unbound. 

1780 """ 

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

1782 

1783 @classmethod 

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

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

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

1787 

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

1789 

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

1791 that the various HasTrait's instances are holding. 

1792 

1793 The metadata kwargs allow functions to be passed in which 

1794 filter traits based on metadata values. The functions should 

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

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

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

1798 to the function. 

1799 """ 

1800 traits = cls._traits.copy() 

1801 

1802 if len(metadata) == 0: 

1803 return traits 

1804 

1805 result = {} 

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

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

1808 if not callable(meta_eval): 

1809 meta_eval = _SimpleTest(meta_eval) 

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

1811 break 

1812 else: 

1813 result[name] = trait 

1814 

1815 return result 

1816 

1817 @classmethod 

1818 def class_own_traits( 

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

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

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

1822 

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

1824 """ 

1825 sup = super(cls, cls) 

1826 return { 

1827 n: t 

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

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

1830 } 

1831 

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

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

1834 return name in self._traits 

1835 

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

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

1838 

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

1840 dynamically generated default value. These default values 

1841 will be recognized as existing only after they have been 

1842 generated. 

1843 

1844 Example 

1845 

1846 .. code-block:: python 

1847 

1848 class MyClass(HasTraits): 

1849 i = Int() 

1850 

1851 

1852 mc = MyClass() 

1853 assert not mc.trait_has_value("i") 

1854 mc.i # generates a default value 

1855 assert mc.trait_has_value("i") 

1856 """ 

1857 return name in self._trait_values 

1858 

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

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

1861 

1862 The metadata kwargs allow functions to be passed in which 

1863 filter traits based on metadata values. The functions should 

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

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

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

1867 to the function. 

1868 

1869 Returns 

1870 ------- 

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

1872 

1873 Notes 

1874 ----- 

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

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

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

1878 """ 

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

1880 

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

1882 """Return default generator for a given trait 

1883 

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

1885 """ 

1886 method_name = "_%s_default" % name 

1887 if method_name in self.__dict__: 

1888 return getattr(self, method_name) 

1889 if method_name in self.__class__.__dict__: 

1890 return getattr(self.__class__, method_name) 

1891 return self._all_trait_default_generators[name] 

1892 

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

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

1895 

1896 Notes 

1897 ----- 

1898 Dynamically generated default values may 

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

1900 for n in names: 

1901 if not self.has_trait(n): 

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

1903 

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

1905 return self._get_trait_default_generator(names[0])(self) # type:ignore[no-any-return] 

1906 

1907 trait_names = self.trait_names(**metadata) 

1908 trait_names.extend(names) 

1909 

1910 defaults = {} 

1911 for n in trait_names: 

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

1913 return defaults 

1914 

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

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

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

1918 

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

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

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

1922 

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

1924 that the various HasTrait's instances are holding. 

1925 

1926 The metadata kwargs allow functions to be passed in which 

1927 filter traits based on metadata values. The functions should 

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

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

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

1931 to the function. 

1932 """ 

1933 traits = self._traits.copy() 

1934 

1935 if len(metadata) == 0: 

1936 return traits 

1937 

1938 result = {} 

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

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

1941 if not callable(meta_eval): 

1942 meta_eval = _SimpleTest(meta_eval) 

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

1944 break 

1945 else: 

1946 result[name] = trait 

1947 

1948 return result 

1949 

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

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

1952 try: 

1953 trait = getattr(self.__class__, traitname) 

1954 except AttributeError as e: 

1955 raise TraitError( 

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

1957 ) from e 

1958 metadata_name = "_" + traitname + "_metadata" 

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

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

1961 else: 

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

1963 

1964 @classmethod 

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

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

1967 

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

1969 """ 

1970 sup = super(cls, cls) 

1971 return { 

1972 n: e 

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

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

1975 } 

1976 

1977 @classmethod 

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

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

1980 

1981 Parameters 

1982 ---------- 

1983 name : str (default: None) 

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

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

1986 

1987 Returns 

1988 ------- 

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

1990 """ 

1991 events = {} 

1992 for k, v in getmembers(cls): 

1993 if isinstance(v, EventHandler): 

1994 if name is None: 

1995 events[k] = v 

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

1997 events[k] = v 

1998 elif hasattr(v, "tags"): 

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

2000 events[k] = v 

2001 return events 

2002 

2003 

2004# ----------------------------------------------------------------------------- 

2005# Actual TraitTypes implementations/subclasses 

2006# ----------------------------------------------------------------------------- 

2007 

2008# ----------------------------------------------------------------------------- 

2009# TraitTypes subclasses for handling classes and instances of classes 

2010# ----------------------------------------------------------------------------- 

2011 

2012 

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

2014 """ 

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

2016 Instance and This. 

2017 """ 

2018 

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

2020 """ 

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

2022 """ 

2023 return import_item(string) 

2024 

2025 

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

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

2028 

2029 if t.TYPE_CHECKING: 

2030 

2031 @t.overload 

2032 def __init__( 

2033 self: Type[type, type], 

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

2035 klass: None | str = ..., 

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

2037 read_only: bool | None = ..., 

2038 help: str | None = ..., 

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

2040 **kwargs: t.Any, 

2041 ) -> None: 

2042 ... 

2043 

2044 @t.overload 

2045 def __init__( 

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

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

2048 klass: None | str = ..., 

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

2050 read_only: bool | None = ..., 

2051 help: str | None = ..., 

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

2053 **kwargs: t.Any, 

2054 ) -> None: 

2055 ... 

2056 

2057 @t.overload 

2058 def __init__( 

2059 self: Type[S, S], 

2060 default_value: S = ..., 

2061 klass: S = ..., 

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

2063 read_only: bool | None = ..., 

2064 help: str | None = ..., 

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

2066 **kwargs: t.Any, 

2067 ) -> None: 

2068 ... 

2069 

2070 @t.overload 

2071 def __init__( 

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

2073 default_value: S | None = ..., 

2074 klass: S = ..., 

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

2076 read_only: bool | None = ..., 

2077 help: str | None = ..., 

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

2079 **kwargs: t.Any, 

2080 ) -> None: 

2081 ... 

2082 

2083 def __init__( 

2084 self, 

2085 default_value: t.Any = Undefined, 

2086 klass: t.Any = None, 

2087 allow_none: bool = False, 

2088 read_only: bool | None = None, 

2089 help: str | None = None, 

2090 config: t.Any | None = None, 

2091 **kwargs: t.Any, 

2092 ) -> None: 

2093 """Construct a Type trait 

2094 

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

2096 a particular class. 

2097 

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

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

2100 

2101 Parameters 

2102 ---------- 

2103 default_value : class, str or None 

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

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

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

2107 :class:`HasTraits` class is instantiated. 

2108 klass : class, str [ default object ] 

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

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

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

2112 :class:`HasTraits` class is instantiated. 

2113 allow_none : bool [ default False ] 

2114 Indicates whether None is allowed as an assignable value. 

2115 **kwargs 

2116 extra kwargs passed to `ClassBasedTraitType` 

2117 """ 

2118 if default_value is Undefined: 

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

2120 else: 

2121 new_default_value = default_value 

2122 

2123 if klass is None: 

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

2125 klass = object 

2126 else: 

2127 klass = default_value 

2128 

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

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

2131 

2132 self.klass = klass 

2133 

2134 super().__init__( 

2135 new_default_value, 

2136 allow_none=allow_none, 

2137 read_only=read_only, 

2138 help=help, 

2139 config=config, 

2140 **kwargs, 

2141 ) 

2142 

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

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

2145 if isinstance(value, str): 

2146 try: 

2147 value = self._resolve_string(value) 

2148 except ImportError as e: 

2149 raise TraitError( 

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

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

2152 ) from e 

2153 try: 

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

2155 return value # type:ignore[no-any-return] 

2156 except Exception: 

2157 pass 

2158 

2159 self.error(obj, value) 

2160 

2161 def info(self) -> str: 

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

2163 if isinstance(self.klass, str): 

2164 klass = self.klass 

2165 else: 

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

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

2168 if self.allow_none: 

2169 return result + " or None" 

2170 return result 

2171 

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

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

2174 # might be called before all imports are done. 

2175 self._resolve_classes() 

2176 

2177 def _resolve_classes(self) -> None: 

2178 if isinstance(self.klass, str): 

2179 self.klass = self._resolve_string(self.klass) 

2180 if isinstance(self.default_value, str): 

2181 self.default_value = self._resolve_string(self.default_value) 

2182 

2183 def default_value_repr(self) -> str: 

2184 value = self.default_value 

2185 assert value is not None 

2186 if isinstance(value, str): 

2187 return repr(value) 

2188 else: 

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

2190 

2191 

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

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

2194 

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

2196 

2197 Subclasses can declare default classes by overriding the klass attribute 

2198 """ 

2199 

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

2201 

2202 if t.TYPE_CHECKING: 

2203 

2204 @t.overload 

2205 def __init__( 

2206 self: Instance[T], 

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

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

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

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

2211 read_only: bool | None = ..., 

2212 help: str | None = ..., 

2213 **kwargs: t.Any, 

2214 ) -> None: 

2215 ... 

2216 

2217 @t.overload 

2218 def __init__( 

2219 self: Instance[T | None], 

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

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

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

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

2224 read_only: bool | None = ..., 

2225 help: str | None = ..., 

2226 **kwargs: t.Any, 

2227 ) -> None: 

2228 ... 

2229 

2230 @t.overload 

2231 def __init__( 

2232 self: Instance[t.Any], 

2233 klass: str | None = ..., 

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

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

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

2237 read_only: bool | None = ..., 

2238 help: str | None = ..., 

2239 **kwargs: t.Any, 

2240 ) -> None: 

2241 ... 

2242 

2243 @t.overload 

2244 def __init__( 

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

2246 klass: str | None = ..., 

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

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

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

2250 read_only: bool | None = ..., 

2251 help: str | None = ..., 

2252 **kwargs: t.Any, 

2253 ) -> None: 

2254 ... 

2255 

2256 def __init__( 

2257 self, 

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

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

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

2261 allow_none: bool = False, 

2262 read_only: bool | None = None, 

2263 help: str | None = None, 

2264 **kwargs: t.Any, 

2265 ) -> None: 

2266 """Construct an Instance trait. 

2267 

2268 This trait allows values that are instances of a particular 

2269 class or its subclasses. Our implementation is quite different 

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

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

2272 

2273 Parameters 

2274 ---------- 

2275 klass : class, str 

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

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

2278 args : tuple 

2279 Positional arguments for generating the default value. 

2280 kw : dict 

2281 Keyword arguments for generating the default value. 

2282 allow_none : bool [ default False ] 

2283 Indicates whether None is allowed as a value. 

2284 **kwargs 

2285 Extra kwargs passed to `ClassBasedTraitType` 

2286 

2287 Notes 

2288 ----- 

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

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

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

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

2293 """ 

2294 if klass is None: 

2295 klass = self.klass 

2296 

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

2298 self.klass = klass 

2299 else: 

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

2301 

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

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

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

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

2306 

2307 self.default_args = args 

2308 self.default_kwargs = kw 

2309 

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

2311 

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

2313 assert self.klass is not None 

2314 if self.allow_none and value is None: 

2315 return value 

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

2317 return value # type:ignore[no-any-return] 

2318 else: 

2319 self.error(obj, value) 

2320 

2321 def info(self) -> str: 

2322 if isinstance(self.klass, str): 

2323 result = add_article(self.klass) 

2324 else: 

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

2326 if self.allow_none: 

2327 result += " or None" 

2328 return result 

2329 

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

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

2332 # might be called before all imports are done. 

2333 self._resolve_classes() 

2334 

2335 def _resolve_classes(self) -> None: 

2336 if isinstance(self.klass, str): 

2337 self.klass = self._resolve_string(self.klass) 

2338 

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

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

2341 return None 

2342 assert self.klass is not None 

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

2344 

2345 def default_value_repr(self) -> str: 

2346 return repr(self.make_dynamic_default()) 

2347 

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

2349 return _safe_literal_eval(s) # type:ignore[no-any-return] 

2350 

2351 

2352class ForwardDeclaredMixin: 

2353 """ 

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

2355 """ 

2356 

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

2358 """ 

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

2360 our this_class attribute was defined. 

2361 """ 

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

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

2364 

2365 

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

2367 """ 

2368 Forward-declared version of Type. 

2369 """ 

2370 

2371 

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

2373 """ 

2374 Forward-declared version of Instance. 

2375 """ 

2376 

2377 

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

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

2380 

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

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

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

2384 """ 

2385 

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

2387 

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

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

2390 

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

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

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

2394 # trait. 

2395 assert self.this_class is not None 

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

2397 return value 

2398 else: 

2399 self.error(obj, value) 

2400 

2401 

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

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

2404 

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

2406 """Construct a Union trait. 

2407 

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

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

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

2411 

2412 Parameters 

2413 ---------- 

2414 trait_types : sequence 

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

2416 **kwargs 

2417 Extra kwargs passed to `TraitType` 

2418 

2419 Notes 

2420 ----- 

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

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

2423 

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

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

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

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

2428 ``_literal_from_string_pairs`` in subclasses. 

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

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

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

2432 """ 

2433 self.trait_types = list(trait_types) 

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

2435 super().__init__(**kwargs) 

2436 

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

2438 default = super().default(obj) 

2439 for trait in self.trait_types: 

2440 if default is Undefined: 

2441 default = trait.default(obj) 

2442 else: 

2443 break 

2444 return default 

2445 

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

2447 for trait_type in reversed(self.trait_types): 

2448 trait_type.class_init(cls, None) 

2449 super().class_init(cls, name) 

2450 

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

2452 for trait_type in reversed(self.trait_types): 

2453 trait_type.subclass_init(cls) 

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

2455 # to opt out of instance_init 

2456 

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

2458 with obj.cross_validation_lock: 

2459 for trait_type in self.trait_types: 

2460 try: 

2461 v = trait_type._validate(obj, value) 

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

2463 if self.name is not None: 

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

2465 return v 

2466 except TraitError: 

2467 continue 

2468 self.error(obj, value) 

2469 

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

2471 if isinstance(other, Union): 

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

2473 else: 

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

2475 

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

2477 for trait_type in self.trait_types: 

2478 try: 

2479 v = trait_type.from_string(s) 

2480 return trait_type.validate(None, v) 

2481 except (TraitError, ValueError): 

2482 continue 

2483 return super().from_string(s) 

2484 

2485 

2486# ----------------------------------------------------------------------------- 

2487# Basic TraitTypes implementations/subclasses 

2488# ----------------------------------------------------------------------------- 

2489 

2490 

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

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

2493 

2494 if t.TYPE_CHECKING: 

2495 

2496 @t.overload 

2497 def __init__( 

2498 self: Any, 

2499 default_value: t.Any = ..., 

2500 *, 

2501 allow_none: Literal[False], 

2502 read_only: bool | None = ..., 

2503 help: str | None = ..., 

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

2505 **kwargs: t.Any, 

2506 ) -> None: 

2507 ... 

2508 

2509 @t.overload 

2510 def __init__( 

2511 self: Any, 

2512 default_value: t.Any = ..., 

2513 *, 

2514 allow_none: Literal[True], 

2515 read_only: bool | None = ..., 

2516 help: str | None = ..., 

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

2518 **kwargs: t.Any, 

2519 ) -> None: 

2520 ... 

2521 

2522 @t.overload 

2523 def __init__( 

2524 self: Any, 

2525 default_value: t.Any = ..., 

2526 *, 

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

2528 help: str | None = ..., 

2529 read_only: bool | None = False, 

2530 config: t.Any = None, 

2531 **kwargs: t.Any, 

2532 ) -> None: 

2533 ... 

2534 

2535 def __init__( 

2536 self: Any, 

2537 default_value: t.Any = ..., 

2538 *, 

2539 allow_none: bool = False, 

2540 help: str | None = "", 

2541 read_only: bool | None = False, 

2542 config: t.Any = None, 

2543 **kwargs: t.Any, 

2544 ) -> None: 

2545 ... 

2546 

2547 @t.overload 

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

2549 ... 

2550 

2551 @t.overload 

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

2553 ... 

2554 

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

2556 ... 

2557 

2558 default_value: t.Any | None = None 

2559 allow_none = True 

2560 info_text = "any value" 

2561 

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

2563 pass # fully opt out of instance_init 

2564 

2565 

2566def _validate_bounds( 

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

2568) -> t.Any: 

2569 """ 

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

2571 

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

2573 TraitError with an error message appropriate for this trait. 

2574 """ 

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

2576 raise TraitError( 

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

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

2579 "specified" 

2580 ) 

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

2582 raise TraitError( 

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

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

2585 "specified" 

2586 ) 

2587 return value 

2588 

2589 

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

2591 

2592 

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

2594 """An int trait.""" 

2595 

2596 default_value = 0 

2597 info_text = "an int" 

2598 

2599 @t.overload 

2600 def __init__( 

2601 self: Int[int, int], 

2602 default_value: int | Sentinel = ..., 

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

2604 read_only: bool | None = ..., 

2605 help: str | None = ..., 

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

2607 **kwargs: t.Any, 

2608 ) -> None: 

2609 ... 

2610 

2611 @t.overload 

2612 def __init__( 

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

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

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

2616 read_only: bool | None = ..., 

2617 help: str | None = ..., 

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

2619 **kwargs: t.Any, 

2620 ) -> None: 

2621 ... 

2622 

2623 def __init__( 

2624 self, 

2625 default_value: t.Any = Undefined, 

2626 allow_none: bool = False, 

2627 read_only: bool | None = None, 

2628 help: str | None = None, 

2629 config: t.Any | None = None, 

2630 **kwargs: t.Any, 

2631 ) -> None: 

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

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

2634 super().__init__( 

2635 default_value=default_value, 

2636 allow_none=allow_none, 

2637 read_only=read_only, 

2638 help=help, 

2639 config=config, 

2640 **kwargs, 

2641 ) 

2642 

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

2644 if not isinstance(value, int) and isinstance(value, numbers.Number): 

2645 # allow casting integer-valued numbers to int 

2646 # allows for more concise assignment like `4e9` which is a float 

2647 try: 

2648 int_value = int(value) 

2649 if int_value == value: 

2650 value = int_value 

2651 except Exception: 

2652 pass 

2653 if not isinstance(value, int): 

2654 self.error(obj, value) 

2655 return _validate_bounds(self, obj, value) # type:ignore[no-any-return] 

2656 

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

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

2659 return None # type:ignore[return-value] 

2660 return int(s) # type:ignore[return-value] 

2661 

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

2663 pass # fully opt out of instance_init 

2664 

2665 

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

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

2668 

2669 if t.TYPE_CHECKING: 

2670 

2671 @t.overload 

2672 def __init__( 

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

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

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

2676 read_only: bool | None = ..., 

2677 help: str | None = ..., 

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

2679 **kwargs: t.Any, 

2680 ) -> None: 

2681 ... 

2682 

2683 @t.overload 

2684 def __init__( 

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

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

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

2688 read_only: bool | None = ..., 

2689 help: str | None = ..., 

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

2691 **kwargs: t.Any, 

2692 ) -> None: 

2693 ... 

2694 

2695 def __init__( 

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

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

2698 allow_none: bool = ..., 

2699 read_only: bool | None = ..., 

2700 help: str | None = ..., 

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

2702 **kwargs: t.Any, 

2703 ) -> None: 

2704 ... 

2705 

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

2707 try: 

2708 value = int(value) 

2709 except Exception: 

2710 self.error(obj, value) 

2711 return _validate_bounds(self, obj, value) # type:ignore[no-any-return] 

2712 

2713 

2714Long, CLong = Int, CInt 

2715Integer = Int 

2716 

2717 

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

2719 """A float trait.""" 

2720 

2721 default_value = 0.0 

2722 info_text = "a float" 

2723 

2724 @t.overload 

2725 def __init__( 

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

2727 default_value: float | Sentinel = ..., 

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

2729 read_only: bool | None = ..., 

2730 help: str | None = ..., 

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

2732 **kwargs: t.Any, 

2733 ) -> None: 

2734 ... 

2735 

2736 @t.overload 

2737 def __init__( 

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

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

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

2741 read_only: bool | None = ..., 

2742 help: str | None = ..., 

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

2744 **kwargs: t.Any, 

2745 ) -> None: 

2746 ... 

2747 

2748 def __init__( 

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

2750 default_value: float | Sentinel | None = Undefined, 

2751 allow_none: bool = False, 

2752 read_only: bool | None = False, 

2753 help: str | None = None, 

2754 config: t.Any | None = None, 

2755 **kwargs: t.Any, 

2756 ) -> None: 

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

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

2759 super().__init__( 

2760 default_value=default_value, 

2761 allow_none=allow_none, 

2762 read_only=read_only, 

2763 help=help, 

2764 config=config, 

2765 **kwargs, 

2766 ) 

2767 

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

2769 if isinstance(value, int): 

2770 value = float(value) 

2771 if not isinstance(value, float): 

2772 self.error(obj, value) 

2773 return _validate_bounds(self, obj, value) # type:ignore[no-any-return] 

2774 

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

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

2777 return None # type:ignore[return-value] 

2778 return float(s) # type:ignore[return-value] 

2779 

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

2781 pass # fully opt out of instance_init 

2782 

2783 

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

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

2786 

2787 if t.TYPE_CHECKING: 

2788 

2789 @t.overload 

2790 def __init__( 

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

2792 default_value: t.Any = ..., 

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

2794 read_only: bool | None = ..., 

2795 help: str | None = ..., 

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

2797 **kwargs: t.Any, 

2798 ) -> None: 

2799 ... 

2800 

2801 @t.overload 

2802 def __init__( 

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

2804 default_value: t.Any = ..., 

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

2806 read_only: bool | None = ..., 

2807 help: str | None = ..., 

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

2809 **kwargs: t.Any, 

2810 ) -> None: 

2811 ... 

2812 

2813 def __init__( 

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

2815 default_value: t.Any = ..., 

2816 allow_none: bool = ..., 

2817 read_only: bool | None = ..., 

2818 help: str | None = ..., 

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

2820 **kwargs: t.Any, 

2821 ) -> None: 

2822 ... 

2823 

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

2825 try: 

2826 value = float(value) 

2827 except Exception: 

2828 self.error(obj, value) 

2829 return _validate_bounds(self, obj, value) # type:ignore[no-any-return] 

2830 

2831 

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

2833 """A trait for complex numbers.""" 

2834 

2835 default_value = 0.0 + 0.0j 

2836 info_text = "a complex number" 

2837 

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

2839 if isinstance(value, complex): 

2840 return value 

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

2842 return complex(value) 

2843 self.error(obj, value) 

2844 

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

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

2847 return None 

2848 return complex(s) 

2849 

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

2851 pass # fully opt out of instance_init 

2852 

2853 

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

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

2856 

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

2858 try: 

2859 return complex(value) 

2860 except Exception: 

2861 self.error(obj, value) 

2862 

2863 

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

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

2866# we don't have a Str type. 

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

2868 """A trait for byte strings.""" 

2869 

2870 default_value = b"" 

2871 info_text = "a bytes object" 

2872 

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

2874 if isinstance(value, bytes): 

2875 return value 

2876 self.error(obj, value) 

2877 

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

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

2880 return None 

2881 if len(s) >= 3: 

2882 # handle deprecated b"string" 

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

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

2885 old_s = s 

2886 s = s[2:-1] 

2887 warn( 

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

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

2890 DeprecationWarning, 

2891 stacklevel=2, 

2892 ) 

2893 break 

2894 return s.encode("utf8") 

2895 

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

2897 pass # fully opt out of instance_init 

2898 

2899 

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

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

2902 

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

2904 try: 

2905 return bytes(value) 

2906 except Exception: 

2907 self.error(obj, value) 

2908 

2909 

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

2911 """A trait for unicode strings.""" 

2912 

2913 default_value = "" 

2914 info_text = "a unicode string" 

2915 

2916 if t.TYPE_CHECKING: 

2917 

2918 @t.overload 

2919 def __init__( 

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

2921 default_value: str | Sentinel = ..., 

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

2923 read_only: bool | None = ..., 

2924 help: str | None = ..., 

2925 config: t.Any = ..., 

2926 **kwargs: t.Any, 

2927 ) -> None: 

2928 ... 

2929 

2930 @t.overload 

2931 def __init__( 

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

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

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

2935 read_only: bool | None = ..., 

2936 help: str | None = ..., 

2937 config: t.Any = ..., 

2938 **kwargs: t.Any, 

2939 ) -> None: 

2940 ... 

2941 

2942 def __init__( 

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

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

2945 allow_none: bool = ..., 

2946 read_only: bool | None = ..., 

2947 help: str | None = ..., 

2948 config: t.Any = ..., 

2949 **kwargs: t.Any, 

2950 ) -> None: 

2951 ... 

2952 

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

2954 if isinstance(value, str): 

2955 return value # type:ignore[return-value] 

2956 if isinstance(value, bytes): 

2957 try: 

2958 return value.decode("ascii", "strict") # type:ignore[return-value] 

2959 except UnicodeDecodeError as e: 

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

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

2962 self.error(obj, value) 

2963 

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

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

2966 return None # type:ignore[return-value] 

2967 s = os.path.expanduser(s) 

2968 if len(s) >= 2: 

2969 # handle deprecated "1" 

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

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

2972 old_s = s 

2973 s = s[1:-1] 

2974 warn( 

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

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

2977 DeprecationWarning, 

2978 stacklevel=2, 

2979 ) 

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

2981 

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

2983 pass # fully opt out of instance_init 

2984 

2985 

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

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

2988 

2989 if t.TYPE_CHECKING: 

2990 

2991 @t.overload 

2992 def __init__( 

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

2994 default_value: str | Sentinel = ..., 

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

2996 read_only: bool | None = ..., 

2997 help: str | None = ..., 

2998 config: t.Any = ..., 

2999 **kwargs: t.Any, 

3000 ) -> None: 

3001 ... 

3002 

3003 @t.overload 

3004 def __init__( 

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

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

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

3008 read_only: bool | None = ..., 

3009 help: str | None = ..., 

3010 config: t.Any = ..., 

3011 **kwargs: t.Any, 

3012 ) -> None: 

3013 ... 

3014 

3015 def __init__( 

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

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

3018 allow_none: bool = ..., 

3019 read_only: bool | None = ..., 

3020 help: str | None = ..., 

3021 config: t.Any = ..., 

3022 **kwargs: t.Any, 

3023 ) -> None: 

3024 ... 

3025 

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

3027 try: 

3028 return str(value) # type:ignore[return-value] 

3029 except Exception: 

3030 self.error(obj, value) 

3031 

3032 

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

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

3035 

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

3037 

3038 info_text = "a valid object identifier in Python" 

3039 

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

3041 

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

3043 value = self.coerce_str(obj, value) 

3044 

3045 if isinstance(value, str) and value.isidentifier(): 

3046 return value 

3047 self.error(obj, value) 

3048 

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

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

3051 return None 

3052 return s 

3053 

3054 

3055class DottedObjectName(ObjectName): 

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

3057 

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

3059 value = self.coerce_str(obj, value) 

3060 

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

3062 return value 

3063 self.error(obj, value) 

3064 

3065 

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

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

3068 

3069 default_value = False 

3070 info_text = "a boolean" 

3071 

3072 if t.TYPE_CHECKING: 

3073 

3074 @t.overload 

3075 def __init__( 

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

3077 default_value: bool | Sentinel = ..., 

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

3079 read_only: bool | None = ..., 

3080 help: str | None = ..., 

3081 config: t.Any = ..., 

3082 **kwargs: t.Any, 

3083 ) -> None: 

3084 ... 

3085 

3086 @t.overload 

3087 def __init__( 

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

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

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

3091 read_only: bool | None = ..., 

3092 help: str | None = ..., 

3093 config: t.Any = ..., 

3094 **kwargs: t.Any, 

3095 ) -> None: 

3096 ... 

3097 

3098 def __init__( 

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

3100 default_value: bool | Sentinel | None = ..., 

3101 allow_none: bool = ..., 

3102 read_only: bool | None = ..., 

3103 help: str | None = ..., 

3104 config: t.Any = ..., 

3105 **kwargs: t.Any, 

3106 ) -> None: 

3107 ... 

3108 

3109 def validate(self, obj: t.Any, value: t.Any) -> G: 

3110 if isinstance(value, bool): 

3111 return value # type:ignore[return-value] 

3112 elif isinstance(value, int): 

3113 if value == 1: 

3114 return True # type:ignore[return-value] 

3115 elif value == 0: 

3116 return False # type:ignore[return-value] 

3117 self.error(obj, value) 

3118 

3119 def from_string(self, s: str) -> G: 

3120 if self.allow_none and s == "None": 

3121 return None # type:ignore[return-value] 

3122 s = s.lower() 

3123 if s in {"true", "1"}: 

3124 return True # type:ignore[return-value] 

3125 elif s in {"false", "0"}: 

3126 return False # type:ignore[return-value] 

3127 else: 

3128 raise ValueError("%r is not 1, 0, true, or false") 

3129 

3130 def subclass_init(self, cls: type[t.Any]) -> None: 

3131 pass # fully opt out of instance_init 

3132 

3133 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3134 """Completion hints for argcomplete""" 

3135 completions = ["true", "1", "false", "0"] 

3136 if self.allow_none: 

3137 completions.append("None") 

3138 return completions 

3139 

3140 

3141class CBool(Bool[G, S]): 

3142 """A casting version of the boolean trait.""" 

3143 

3144 if t.TYPE_CHECKING: 

3145 

3146 @t.overload 

3147 def __init__( 

3148 self: CBool[bool, t.Any], 

3149 default_value: bool | Sentinel = ..., 

3150 allow_none: Literal[False] = ..., 

3151 read_only: bool | None = ..., 

3152 help: str | None = ..., 

3153 config: t.Any = ..., 

3154 **kwargs: t.Any, 

3155 ) -> None: 

3156 ... 

3157 

3158 @t.overload 

3159 def __init__( 

3160 self: CBool[bool | None, t.Any], 

3161 default_value: bool | Sentinel | None = ..., 

3162 allow_none: Literal[True] = ..., 

3163 read_only: bool | None = ..., 

3164 help: str | None = ..., 

3165 config: t.Any = ..., 

3166 **kwargs: t.Any, 

3167 ) -> None: 

3168 ... 

3169 

3170 def __init__( 

3171 self: CBool[bool | None, t.Any], 

3172 default_value: bool | Sentinel | None = ..., 

3173 allow_none: bool = ..., 

3174 read_only: bool | None = ..., 

3175 help: str | None = ..., 

3176 config: t.Any = ..., 

3177 **kwargs: t.Any, 

3178 ) -> None: 

3179 ... 

3180 

3181 def validate(self, obj: t.Any, value: t.Any) -> G: 

3182 try: 

3183 return bool(value) # type:ignore[return-value] 

3184 except Exception: 

3185 self.error(obj, value) 

3186 

3187 

3188class Enum(TraitType[G, G]): 

3189 """An enum whose value must be in a given sequence.""" 

3190 

3191 if t.TYPE_CHECKING: 

3192 

3193 @t.overload 

3194 def __init__( 

3195 self: Enum[G], 

3196 values: t.Sequence[G], 

3197 default_value: G | Sentinel = ..., 

3198 allow_none: Literal[False] = ..., 

3199 read_only: bool | None = ..., 

3200 help: str | None = ..., 

3201 config: t.Any = ..., 

3202 **kwargs: t.Any, 

3203 ) -> None: 

3204 ... 

3205 

3206 @t.overload 

3207 def __init__( 

3208 self: Enum[G | None], 

3209 values: t.Sequence[G] | None, 

3210 default_value: G | Sentinel | None = ..., 

3211 allow_none: Literal[True] = ..., 

3212 read_only: bool | None = ..., 

3213 help: str | None = ..., 

3214 config: t.Any = ..., 

3215 **kwargs: t.Any, 

3216 ) -> None: 

3217 ... 

3218 

3219 def __init__( 

3220 self: Enum[G], 

3221 values: t.Sequence[G] | None, 

3222 default_value: G | Sentinel | None = Undefined, 

3223 allow_none: bool = False, 

3224 read_only: bool | None = None, 

3225 help: str | None = None, 

3226 config: t.Any = None, 

3227 **kwargs: t.Any, 

3228 ) -> None: 

3229 self.values = values 

3230 if allow_none is True and default_value is Undefined: 

3231 default_value = None 

3232 kwargs["allow_none"] = allow_none 

3233 kwargs["read_only"] = read_only 

3234 kwargs["help"] = help 

3235 kwargs["config"] = config 

3236 super().__init__(default_value, **kwargs) 

3237 

3238 def validate(self, obj: t.Any, value: t.Any) -> G: 

3239 if self.values and value in self.values: 

3240 return value # type:ignore[no-any-return] 

3241 self.error(obj, value) 

3242 

3243 def _choices_str(self, as_rst: bool = False) -> str: 

3244 """Returns a description of the trait choices (not none).""" 

3245 choices = self.values or [] 

3246 if as_rst: 

3247 choice_str = "|".join("``%r``" % x for x in choices) 

3248 else: 

3249 choice_str = repr(list(choices)) 

3250 return choice_str 

3251 

3252 def _info(self, as_rst: bool = False) -> str: 

3253 """Returns a description of the trait.""" 

3254 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3255 return f"any of {self._choices_str(as_rst)}{none}" 

3256 

3257 def info(self) -> str: 

3258 return self._info(as_rst=False) 

3259 

3260 def info_rst(self) -> str: 

3261 return self._info(as_rst=True) 

3262 

3263 def from_string(self, s: str) -> G: 

3264 try: 

3265 return self.validate(None, s) 

3266 except TraitError: 

3267 return _safe_literal_eval(s) # type:ignore[no-any-return] 

3268 

3269 def subclass_init(self, cls: type[t.Any]) -> None: 

3270 pass # fully opt out of instance_init 

3271 

3272 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3273 """Completion hints for argcomplete""" 

3274 return [str(v) for v in self.values or []] 

3275 

3276 

3277class CaselessStrEnum(Enum[G]): 

3278 """An enum of strings where the case should be ignored.""" 

3279 

3280 def __init__( 

3281 self: CaselessStrEnum[t.Any], 

3282 values: t.Any, 

3283 default_value: t.Any = Undefined, 

3284 **kwargs: t.Any, 

3285 ) -> None: 

3286 super().__init__(values, default_value=default_value, **kwargs) 

3287 

3288 def validate(self, obj: t.Any, value: t.Any) -> G: 

3289 if not isinstance(value, str): 

3290 self.error(obj, value) 

3291 

3292 for v in self.values or []: 

3293 assert isinstance(v, str) 

3294 if v.lower() == value.lower(): 

3295 return v # type:ignore[return-value] 

3296 self.error(obj, value) 

3297 

3298 def _info(self, as_rst: bool = False) -> str: 

3299 """Returns a description of the trait.""" 

3300 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3301 return f"any of {self._choices_str(as_rst)} (case-insensitive){none}" 

3302 

3303 def info(self) -> str: 

3304 return self._info(as_rst=False) 

3305 

3306 def info_rst(self) -> str: 

3307 return self._info(as_rst=True) 

3308 

3309 

3310class FuzzyEnum(Enum[G]): 

3311 """An case-ignoring enum matching choices by unique prefixes/substrings.""" 

3312 

3313 case_sensitive = False 

3314 #: If True, choices match anywhere in the string, otherwise match prefixes. 

3315 substring_matching = False 

3316 

3317 def __init__( 

3318 self: FuzzyEnum[t.Any], 

3319 values: t.Any, 

3320 default_value: t.Any = Undefined, 

3321 case_sensitive: bool = False, 

3322 substring_matching: bool = False, 

3323 **kwargs: t.Any, 

3324 ) -> None: 

3325 self.case_sensitive = case_sensitive 

3326 self.substring_matching = substring_matching 

3327 super().__init__(values, default_value=default_value, **kwargs) 

3328 

3329 def validate(self, obj: t.Any, value: t.Any) -> G: 

3330 if not isinstance(value, str): 

3331 self.error(obj, value) 

3332 

3333 conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower() 

3334 substring_matching = self.substring_matching 

3335 match_func = (lambda v, c: v in c) if substring_matching else (lambda v, c: c.startswith(v)) 

3336 value = conv_func(value) # type:ignore[no-untyped-call] 

3337 choices = self.values or [] 

3338 matches = [match_func(value, conv_func(c)) for c in choices] # type:ignore[no-untyped-call] 

3339 if sum(matches) == 1: 

3340 for v, m in zip(choices, matches): 

3341 if m: 

3342 return v 

3343 

3344 self.error(obj, value) 

3345 

3346 def _info(self, as_rst: bool = False) -> str: 

3347 """Returns a description of the trait.""" 

3348 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3349 case = "sensitive" if self.case_sensitive else "insensitive" 

3350 substr = "substring" if self.substring_matching else "prefix" 

3351 return f"any case-{case} {substr} of {self._choices_str(as_rst)}{none}" 

3352 

3353 def info(self) -> str: 

3354 return self._info(as_rst=False) 

3355 

3356 def info_rst(self) -> str: 

3357 return self._info(as_rst=True) 

3358 

3359 

3360class Container(Instance[T]): 

3361 """An instance of a container (list, set, etc.) 

3362 

3363 To be subclassed by overriding klass. 

3364 """ 

3365 

3366 klass: type[T] | None = None 

3367 _cast_types: t.Any = () 

3368 _valid_defaults = SequenceTypes 

3369 _trait: t.Any = None 

3370 _literal_from_string_pairs: t.Any = ("[]", "()") 

3371 

3372 @t.overload 

3373 def __init__( 

3374 self: Container[T], 

3375 *, 

3376 allow_none: Literal[False], 

3377 read_only: bool | None = ..., 

3378 help: str | None = ..., 

3379 config: t.Any | None = ..., 

3380 **kwargs: t.Any, 

3381 ) -> None: 

3382 ... 

3383 

3384 @t.overload 

3385 def __init__( 

3386 self: Container[T | None], 

3387 *, 

3388 allow_none: Literal[True], 

3389 read_only: bool | None = ..., 

3390 help: str | None = ..., 

3391 config: t.Any | None = ..., 

3392 **kwargs: t.Any, 

3393 ) -> None: 

3394 ... 

3395 

3396 @t.overload 

3397 def __init__( 

3398 self: Container[T], 

3399 *, 

3400 trait: t.Any = ..., 

3401 default_value: t.Any = ..., 

3402 help: str = ..., 

3403 read_only: bool = ..., 

3404 config: t.Any = ..., 

3405 **kwargs: t.Any, 

3406 ) -> None: 

3407 ... 

3408 

3409 def __init__( 

3410 self, 

3411 trait: t.Any | None = None, 

3412 default_value: t.Any = Undefined, 

3413 help: str | None = None, 

3414 read_only: bool | None = None, 

3415 config: t.Any | None = None, 

3416 **kwargs: t.Any, 

3417 ) -> None: 

3418 """Create a container trait type from a list, set, or tuple. 

3419 

3420 The default value is created by doing ``List(default_value)``, 

3421 which creates a copy of the ``default_value``. 

3422 

3423 ``trait`` can be specified, which restricts the type of elements 

3424 in the container to that TraitType. 

3425 

3426 If only one arg is given and it is not a Trait, it is taken as 

3427 ``default_value``: 

3428 

3429 ``c = List([1, 2, 3])`` 

3430 

3431 Parameters 

3432 ---------- 

3433 trait : TraitType [ optional ] 

3434 the type for restricting the contents of the Container. If unspecified, 

3435 types are not checked. 

3436 default_value : SequenceType [ optional ] 

3437 The default value for the Trait. Must be list/tuple/set, and 

3438 will be cast to the container type. 

3439 allow_none : bool [ default False ] 

3440 Whether to allow the value to be None 

3441 **kwargs : any 

3442 further keys for extensions to the Trait (e.g. config) 

3443 

3444 """ 

3445 

3446 # allow List([values]): 

3447 if trait is not None and default_value is Undefined and not is_trait(trait): 

3448 default_value = trait 

3449 trait = None 

3450 

3451 if default_value is None and not kwargs.get("allow_none", False): 

3452 # improve backward-compatibility for possible subclasses 

3453 # specifying default_value=None as default, 

3454 # keeping 'unspecified' behavior (i.e. empty container) 

3455 warn( 

3456 f"Specifying {self.__class__.__name__}(default_value=None)" 

3457 " for no default is deprecated in traitlets 5.0.5." 

3458 " Use default_value=Undefined", 

3459 DeprecationWarning, 

3460 stacklevel=2, 

3461 ) 

3462 default_value = Undefined 

3463 

3464 if default_value is Undefined: 

3465 args: t.Any = () 

3466 elif default_value is None: 

3467 # default_value back on kwargs for super() to handle 

3468 args = () 

3469 kwargs["default_value"] = None 

3470 elif isinstance(default_value, self._valid_defaults): 

3471 args = (default_value,) 

3472 else: 

3473 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3474 

3475 if is_trait(trait): 

3476 if isinstance(trait, type): 

3477 warn( 

3478 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)." 

3479 " Passing types is deprecated in traitlets 4.1.", 

3480 DeprecationWarning, 

3481 stacklevel=3, 

3482 ) 

3483 self._trait = trait() if isinstance(trait, type) else trait 

3484 elif trait is not None: 

3485 raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait)) 

3486 

3487 super().__init__( 

3488 klass=self.klass, args=args, help=help, read_only=read_only, config=config, **kwargs 

3489 ) 

3490 

3491 def validate(self, obj: t.Any, value: t.Any) -> T | None: 

3492 if isinstance(value, self._cast_types): 

3493 assert self.klass is not None 

3494 value = self.klass(value) # type:ignore[call-arg] 

3495 value = super().validate(obj, value) 

3496 if value is None: 

3497 return value 

3498 

3499 return self.validate_elements(obj, value) 

3500 

3501 def validate_elements(self, obj: t.Any, value: t.Any) -> T | None: 

3502 validated = [] 

3503 if self._trait is None or isinstance(self._trait, Any): 

3504 return value # type:ignore[no-any-return] 

3505 for v in value: 

3506 try: 

3507 v = self._trait._validate(obj, v) 

3508 except TraitError as error: 

3509 self.error(obj, v, error) 

3510 else: 

3511 validated.append(v) 

3512 assert self.klass is not None 

3513 return self.klass(validated) # type:ignore[call-arg] 

3514 

3515 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3516 if isinstance(self._trait, TraitType): 

3517 self._trait.class_init(cls, None) 

3518 super().class_init(cls, name) 

3519 

3520 def subclass_init(self, cls: type[t.Any]) -> None: 

3521 if isinstance(self._trait, TraitType): 

3522 self._trait.subclass_init(cls) 

3523 # explicitly not calling super().subclass_init(cls) 

3524 # to opt out of instance_init 

3525 

3526 def from_string(self, s: str) -> T | None: 

3527 """Load value from a single string""" 

3528 if not isinstance(s, str): 

3529 raise TraitError(f"Expected string, got {s!r}") 

3530 try: 

3531 test = literal_eval(s) 

3532 except Exception: 

3533 test = None 

3534 return self.validate(None, test) 

3535 

3536 def from_string_list(self, s_list: list[str]) -> T | None: 

3537 """Return the value from a list of config strings 

3538 

3539 This is where we parse CLI configuration 

3540 """ 

3541 assert self.klass is not None 

3542 if len(s_list) == 1: 

3543 # check for deprecated --Class.trait="['a', 'b', 'c']" 

3544 r = s_list[0] 

3545 if r == "None" and self.allow_none: 

3546 return None 

3547 if len(r) >= 2 and any( 

3548 r.startswith(start) and r.endswith(end) 

3549 for start, end in self._literal_from_string_pairs 

3550 ): 

3551 if self.this_class: 

3552 clsname = self.this_class.__name__ + "." 

3553 else: 

3554 clsname = "" 

3555 assert self.name is not None 

3556 warn( 

3557 "--{0}={1} for containers is deprecated in traitlets 5.0. " 

3558 "You can pass `--{0} item` ... multiple times to add items to a list.".format( 

3559 clsname + self.name, r 

3560 ), 

3561 DeprecationWarning, 

3562 stacklevel=2, 

3563 ) 

3564 return self.klass(literal_eval(r)) # type:ignore[call-arg] 

3565 sig = inspect.signature(self.item_from_string) 

3566 if "index" in sig.parameters: 

3567 item_from_string = self.item_from_string 

3568 else: 

3569 # backward-compat: allow item_from_string to ignore index arg 

3570 def item_from_string(s: str, index: int | None = None) -> T | str: 

3571 return self.item_from_string(s) 

3572 

3573 return self.klass( # type:ignore[call-arg] 

3574 [item_from_string(s, index=idx) for idx, s in enumerate(s_list)] 

3575 ) 

3576 

3577 def item_from_string(self, s: str, index: int | None = None) -> T | str: 

3578 """Cast a single item from a string 

3579 

3580 Evaluated when parsing CLI configuration from a string 

3581 """ 

3582 if self._trait: 

3583 return self._trait.from_string(s) # type:ignore[no-any-return] 

3584 else: 

3585 return s 

3586 

3587 

3588class List(Container[t.List[T]]): 

3589 """An instance of a Python list.""" 

3590 

3591 klass = list # type:ignore[assignment] 

3592 _cast_types: t.Any = (tuple,) 

3593 

3594 def __init__( 

3595 self, 

3596 trait: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | TraitType[T, t.Any] | None = None, 

3597 default_value: t.List[T] | t.Tuple[T] | t.Set[T] | Sentinel | None = Undefined, 

3598 minlen: int = 0, 

3599 maxlen: int = sys.maxsize, 

3600 **kwargs: t.Any, 

3601 ) -> None: 

3602 """Create a List trait type from a list, set, or tuple. 

3603 

3604 The default value is created by doing ``list(default_value)``, 

3605 which creates a copy of the ``default_value``. 

3606 

3607 ``trait`` can be specified, which restricts the type of elements 

3608 in the container to that TraitType. 

3609 

3610 If only one arg is given and it is not a Trait, it is taken as 

3611 ``default_value``: 

3612 

3613 ``c = List([1, 2, 3])`` 

3614 

3615 Parameters 

3616 ---------- 

3617 trait : TraitType [ optional ] 

3618 the type for restricting the contents of the Container. 

3619 If unspecified, types are not checked. 

3620 default_value : SequenceType [ optional ] 

3621 The default value for the Trait. Must be list/tuple/set, and 

3622 will be cast to the container type. 

3623 minlen : Int [ default 0 ] 

3624 The minimum length of the input list 

3625 maxlen : Int [ default sys.maxsize ] 

3626 The maximum length of the input list 

3627 """ 

3628 self._maxlen = maxlen 

3629 self._minlen = minlen 

3630 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3631 

3632 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3633 e = ( 

3634 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3635 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3636 ) 

3637 raise TraitError(e) 

3638 

3639 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3640 length = len(value) 

3641 if length < self._minlen or length > self._maxlen: 

3642 self.length_error(obj, value) 

3643 

3644 return super().validate_elements(obj, value) 

3645 

3646 def set(self, obj: t.Any, value: t.Any) -> None: 

3647 if isinstance(value, str): 

3648 return super().set(obj, [value]) # type:ignore[list-item] 

3649 else: 

3650 return super().set(obj, value) 

3651 

3652 

3653class Set(Container[t.Set[t.Any]]): 

3654 """An instance of a Python set.""" 

3655 

3656 klass = set 

3657 _cast_types = (tuple, list) 

3658 

3659 _literal_from_string_pairs = ("[]", "()", "{}") 

3660 

3661 # Redefine __init__ just to make the docstring more accurate. 

3662 def __init__( 

3663 self, 

3664 trait: t.Any = None, 

3665 default_value: t.Any = Undefined, 

3666 minlen: int = 0, 

3667 maxlen: int = sys.maxsize, 

3668 **kwargs: t.Any, 

3669 ) -> None: 

3670 """Create a Set trait type from a list, set, or tuple. 

3671 

3672 The default value is created by doing ``set(default_value)``, 

3673 which creates a copy of the ``default_value``. 

3674 

3675 ``trait`` can be specified, which restricts the type of elements 

3676 in the container to that TraitType. 

3677 

3678 If only one arg is given and it is not a Trait, it is taken as 

3679 ``default_value``: 

3680 

3681 ``c = Set({1, 2, 3})`` 

3682 

3683 Parameters 

3684 ---------- 

3685 trait : TraitType [ optional ] 

3686 the type for restricting the contents of the Container. 

3687 If unspecified, types are not checked. 

3688 default_value : SequenceType [ optional ] 

3689 The default value for the Trait. Must be list/tuple/set, and 

3690 will be cast to the container type. 

3691 minlen : Int [ default 0 ] 

3692 The minimum length of the input list 

3693 maxlen : Int [ default sys.maxsize ] 

3694 The maximum length of the input list 

3695 """ 

3696 self._maxlen = maxlen 

3697 self._minlen = minlen 

3698 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3699 

3700 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3701 e = ( 

3702 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3703 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3704 ) 

3705 raise TraitError(e) 

3706 

3707 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3708 length = len(value) 

3709 if length < self._minlen or length > self._maxlen: 

3710 self.length_error(obj, value) 

3711 

3712 return super().validate_elements(obj, value) 

3713 

3714 def set(self, obj: t.Any, value: t.Any) -> None: 

3715 if isinstance(value, str): 

3716 return super().set(obj, {value}) 

3717 else: 

3718 return super().set(obj, value) 

3719 

3720 def default_value_repr(self) -> str: 

3721 # Ensure default value is sorted for a reproducible build 

3722 list_repr = repr(sorted(self.make_dynamic_default() or [])) 

3723 if list_repr == "[]": 

3724 return "set()" 

3725 return "{" + list_repr[1:-1] + "}" 

3726 

3727 

3728class Tuple(Container[t.Tuple[t.Any, ...]]): 

3729 """An instance of a Python tuple.""" 

3730 

3731 klass = tuple 

3732 _cast_types = (list,) 

3733 

3734 def __init__(self, *traits: t.Any, **kwargs: t.Any) -> None: 

3735 """Create a tuple from a list, set, or tuple. 

3736 

3737 Create a fixed-type tuple with Traits: 

3738 

3739 ``t = Tuple(Int(), Str(), CStr())`` 

3740 

3741 would be length 3, with Int,Str,CStr for each element. 

3742 

3743 If only one arg is given and it is not a Trait, it is taken as 

3744 default_value: 

3745 

3746 ``t = Tuple((1, 2, 3))`` 

3747 

3748 Otherwise, ``default_value`` *must* be specified by keyword. 

3749 

3750 Parameters 

3751 ---------- 

3752 *traits : TraitTypes [ optional ] 

3753 the types for restricting the contents of the Tuple. If unspecified, 

3754 types are not checked. If specified, then each positional argument 

3755 corresponds to an element of the tuple. Tuples defined with traits 

3756 are of fixed length. 

3757 default_value : SequenceType [ optional ] 

3758 The default value for the Tuple. Must be list/tuple/set, and 

3759 will be cast to a tuple. If ``traits`` are specified, 

3760 ``default_value`` must conform to the shape and type they specify. 

3761 **kwargs 

3762 Other kwargs passed to `Container` 

3763 """ 

3764 default_value = kwargs.pop("default_value", Undefined) 

3765 # allow Tuple((values,)): 

3766 if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]): 

3767 default_value = traits[0] 

3768 traits = () 

3769 

3770 if default_value is None and not kwargs.get("allow_none", False): 

3771 # improve backward-compatibility for possible subclasses 

3772 # specifying default_value=None as default, 

3773 # keeping 'unspecified' behavior (i.e. empty container) 

3774 warn( 

3775 f"Specifying {self.__class__.__name__}(default_value=None)" 

3776 " for no default is deprecated in traitlets 5.0.5." 

3777 " Use default_value=Undefined", 

3778 DeprecationWarning, 

3779 stacklevel=2, 

3780 ) 

3781 default_value = Undefined 

3782 

3783 if default_value is Undefined: 

3784 args: t.Any = () 

3785 elif default_value is None: 

3786 # default_value back on kwargs for super() to handle 

3787 args = () 

3788 kwargs["default_value"] = None 

3789 elif isinstance(default_value, self._valid_defaults): 

3790 args = (default_value,) 

3791 else: 

3792 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3793 

3794 self._traits = [] 

3795 for trait in traits: 

3796 if isinstance(trait, type): 

3797 warn( 

3798 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3799 " Passing types is deprecated in traitlets 4.1.", 

3800 DeprecationWarning, 

3801 stacklevel=2, 

3802 ) 

3803 trait = trait() 

3804 self._traits.append(trait) 

3805 

3806 if self._traits and (default_value is None or default_value is Undefined): 

3807 # don't allow default to be an empty container if length is specified 

3808 args = None 

3809 super(Container, self).__init__(klass=self.klass, args=args, **kwargs) 

3810 

3811 def item_from_string(self, s: str, index: int) -> t.Any: # type:ignore[override] 

3812 """Cast a single item from a string 

3813 

3814 Evaluated when parsing CLI configuration from a string 

3815 """ 

3816 if not self._traits or index >= len(self._traits): 

3817 # return s instead of raising index error 

3818 # length errors will be raised later on validation 

3819 return s 

3820 return self._traits[index].from_string(s) 

3821 

3822 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3823 if not self._traits: 

3824 # nothing to validate 

3825 return value 

3826 if len(value) != len(self._traits): 

3827 e = ( 

3828 "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." 

3829 % (self.name, class_of(obj), len(self._traits), repr_type(value)) 

3830 ) 

3831 raise TraitError(e) 

3832 

3833 validated = [] 

3834 for trait, v in zip(self._traits, value): 

3835 try: 

3836 v = trait._validate(obj, v) 

3837 except TraitError as error: 

3838 self.error(obj, v, error) 

3839 else: 

3840 validated.append(v) 

3841 return tuple(validated) 

3842 

3843 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3844 for trait in self._traits: 

3845 if isinstance(trait, TraitType): 

3846 trait.class_init(cls, None) 

3847 super(Container, self).class_init(cls, name) 

3848 

3849 def subclass_init(self, cls: type[t.Any]) -> None: 

3850 for trait in self._traits: 

3851 if isinstance(trait, TraitType): 

3852 trait.subclass_init(cls) 

3853 # explicitly not calling super().subclass_init(cls) 

3854 # to opt out of instance_init 

3855 

3856 

3857class Dict(Instance["dict[K, V]"]): 

3858 """An instance of a Python dict. 

3859 

3860 One or more traits can be passed to the constructor 

3861 to validate the keys and/or values of the dict. 

3862 If you need more detailed validation, 

3863 you may use a custom validator method. 

3864 

3865 .. versionchanged:: 5.0 

3866 Added key_trait for validating dict keys. 

3867 

3868 .. versionchanged:: 5.0 

3869 Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``. 

3870 """ 

3871 

3872 _value_trait = None 

3873 _key_trait = None 

3874 

3875 def __init__( 

3876 self, 

3877 value_trait: TraitType[t.Any, t.Any] | dict[K, V] | Sentinel | None = None, 

3878 per_key_traits: t.Any = None, 

3879 key_trait: TraitType[t.Any, t.Any] | None = None, 

3880 default_value: dict[K, V] | Sentinel | None = Undefined, 

3881 **kwargs: t.Any, 

3882 ) -> None: 

3883 """Create a dict trait type from a Python dict. 

3884 

3885 The default value is created by doing ``dict(default_value)``, 

3886 which creates a copy of the ``default_value``. 

3887 

3888 Parameters 

3889 ---------- 

3890 value_trait : TraitType [ optional ] 

3891 The specified trait type to check and use to restrict the values of 

3892 the dict. If unspecified, values are not checked. 

3893 per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ] 

3894 A Python dictionary containing the types that are valid for 

3895 restricting the values of the dict on a per-key basis. 

3896 Each value in this dict should be a Trait for validating 

3897 key_trait : TraitType [ optional, keyword-only ] 

3898 The type for restricting the keys of the dict. If 

3899 unspecified, the types of the keys are not checked. 

3900 default_value : SequenceType [ optional, keyword-only ] 

3901 The default value for the Dict. Must be dict, tuple, or None, and 

3902 will be cast to a dict if not None. If any key or value traits are specified, 

3903 the `default_value` must conform to the constraints. 

3904 

3905 Examples 

3906 -------- 

3907 a dict whose values must be text 

3908 >>> d = Dict(Unicode()) 

3909 

3910 d2['n'] must be an integer 

3911 d2['s'] must be text 

3912 >>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()}) 

3913 

3914 d3's keys must be text 

3915 d3's values must be integers 

3916 >>> d3 = Dict(value_trait=Integer(), key_trait=Unicode()) 

3917 

3918 """ 

3919 

3920 # handle deprecated keywords 

3921 trait = kwargs.pop("trait", None) 

3922 if trait is not None: 

3923 if value_trait is not None: 

3924 raise TypeError( 

3925 "Found a value for both `value_trait` and its deprecated alias `trait`." 

3926 ) 

3927 value_trait = trait 

3928 warn( 

3929 "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead", 

3930 DeprecationWarning, 

3931 stacklevel=2, 

3932 ) 

3933 traits = kwargs.pop("traits", None) 

3934 if traits is not None: 

3935 if per_key_traits is not None: 

3936 raise TypeError( 

3937 "Found a value for both `per_key_traits` and its deprecated alias `traits`." 

3938 ) 

3939 per_key_traits = traits 

3940 warn( 

3941 "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead", 

3942 DeprecationWarning, 

3943 stacklevel=2, 

3944 ) 

3945 

3946 # Handling positional arguments 

3947 if default_value is Undefined and value_trait is not None: 

3948 if not is_trait(value_trait): 

3949 assert not isinstance(value_trait, TraitType) 

3950 default_value = value_trait 

3951 value_trait = None 

3952 

3953 if key_trait is None and per_key_traits is not None: 

3954 if is_trait(per_key_traits): 

3955 key_trait = per_key_traits 

3956 per_key_traits = None 

3957 

3958 # Handling default value 

3959 if default_value is Undefined: 

3960 default_value = {} 

3961 if default_value is None: 

3962 args: t.Any = None 

3963 elif isinstance(default_value, dict): 

3964 args = (default_value,) 

3965 elif isinstance(default_value, SequenceTypes): 

3966 args = (default_value,) 

3967 else: 

3968 raise TypeError("default value of Dict was %s" % default_value) 

3969 

3970 # Case where a type of TraitType is provided rather than an instance 

3971 if is_trait(value_trait): 

3972 if isinstance(value_trait, type): 

3973 warn( # type:ignore[unreachable] 

3974 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3975 " Passing types is deprecated in traitlets 4.1.", 

3976 DeprecationWarning, 

3977 stacklevel=2, 

3978 ) 

3979 value_trait = value_trait() 

3980 self._value_trait = value_trait 

3981 elif value_trait is not None: 

3982 raise TypeError( 

3983 "`value_trait` must be a Trait or None, got %s" % repr_type(value_trait) 

3984 ) 

3985 

3986 if is_trait(key_trait): 

3987 if isinstance(key_trait, type): 

3988 warn( # type:ignore[unreachable] 

3989 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3990 " Passing types is deprecated in traitlets 4.1.", 

3991 DeprecationWarning, 

3992 stacklevel=2, 

3993 ) 

3994 key_trait = key_trait() 

3995 self._key_trait = key_trait 

3996 elif key_trait is not None: 

3997 raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait)) 

3998 

3999 self._per_key_traits = per_key_traits 

4000 

4001 super().__init__(klass=dict, args=args, **kwargs) 

4002 

4003 def element_error( 

4004 self, obj: t.Any, element: t.Any, validator: t.Any, side: str = "Values" 

4005 ) -> None: 

4006 e = ( 

4007 side 

4008 + f" of the '{self.name}' trait of {class_of(obj)} instance must be {validator.info()}, but a value of {repr_type(element)} was specified." 

4009 ) 

4010 raise TraitError(e) 

4011 

4012 def validate(self, obj: t.Any, value: t.Any) -> dict[K, V] | None: 

4013 value = super().validate(obj, value) 

4014 if value is None: 

4015 return value 

4016 return self.validate_elements(obj, value) 

4017 

4018 def validate_elements(self, obj: t.Any, value: dict[t.Any, t.Any]) -> dict[K, V] | None: 

4019 per_key_override = self._per_key_traits or {} 

4020 key_trait = self._key_trait 

4021 value_trait = self._value_trait 

4022 if not (key_trait or value_trait or per_key_override): 

4023 return value 

4024 

4025 validated = {} 

4026 for key in value: 

4027 v = value[key] 

4028 if key_trait: 

4029 try: 

4030 key = key_trait._validate(obj, key) 

4031 except TraitError: 

4032 self.element_error(obj, key, key_trait, "Keys") 

4033 active_value_trait = per_key_override.get(key, value_trait) 

4034 if active_value_trait: 

4035 try: 

4036 v = active_value_trait._validate(obj, v) 

4037 except TraitError: 

4038 self.element_error(obj, v, active_value_trait, "Values") 

4039 validated[key] = v 

4040 

4041 return self.klass(validated) # type:ignore[misc,operator] 

4042 

4043 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

4044 if isinstance(self._value_trait, TraitType): 

4045 self._value_trait.class_init(cls, None) 

4046 if isinstance(self._key_trait, TraitType): 

4047 self._key_trait.class_init(cls, None) 

4048 if self._per_key_traits is not None: 

4049 for trait in self._per_key_traits.values(): 

4050 trait.class_init(cls, None) 

4051 super().class_init(cls, name) 

4052 

4053 def subclass_init(self, cls: type[t.Any]) -> None: 

4054 if isinstance(self._value_trait, TraitType): 

4055 self._value_trait.subclass_init(cls) 

4056 if isinstance(self._key_trait, TraitType): 

4057 self._key_trait.subclass_init(cls) 

4058 if self._per_key_traits is not None: 

4059 for trait in self._per_key_traits.values(): 

4060 trait.subclass_init(cls) 

4061 # explicitly not calling super().subclass_init(cls) 

4062 # to opt out of instance_init 

4063 

4064 def from_string(self, s: str) -> dict[K, V] | None: 

4065 """Load value from a single string""" 

4066 if not isinstance(s, str): 

4067 raise TypeError(f"from_string expects a string, got {s!r} of type {type(s)}") 

4068 try: 

4069 return self.from_string_list([s]) # type:ignore[no-any-return] 

4070 except Exception: 

4071 test = _safe_literal_eval(s) 

4072 if isinstance(test, dict): 

4073 return test 

4074 raise 

4075 

4076 def from_string_list(self, s_list: list[str]) -> t.Any: 

4077 """Return a dict from a list of config strings. 

4078 

4079 This is where we parse CLI configuration. 

4080 

4081 Each item should have the form ``"key=value"``. 

4082 

4083 item parsing is done in :meth:`.item_from_string`. 

4084 """ 

4085 if len(s_list) == 1 and s_list[0] == "None" and self.allow_none: 

4086 return None 

4087 if len(s_list) == 1 and s_list[0].startswith("{") and s_list[0].endswith("}"): 

4088 warn( 

4089 f"--{self.name}={s_list[0]} for dict-traits is deprecated in traitlets 5.0. " 

4090 f"You can pass --{self.name} <key=value> ... multiple times to add items to a dict.", 

4091 DeprecationWarning, 

4092 stacklevel=2, 

4093 ) 

4094 

4095 return literal_eval(s_list[0]) 

4096 

4097 combined = {} 

4098 for d in [self.item_from_string(s) for s in s_list]: 

4099 combined.update(d) 

4100 return combined 

4101 

4102 def item_from_string(self, s: str) -> dict[K, V]: 

4103 """Cast a single-key dict from a string. 

4104 

4105 Evaluated when parsing CLI configuration from a string. 

4106 

4107 Dicts expect strings of the form key=value. 

4108 

4109 Returns a one-key dictionary, 

4110 which will be merged in :meth:`.from_string_list`. 

4111 """ 

4112 

4113 if "=" not in s: 

4114 raise TraitError( 

4115 f"'{self.__class__.__name__}' options must have the form 'key=value', got {s!r}" 

4116 ) 

4117 key, value = s.split("=", 1) 

4118 

4119 # cast key with key trait, if defined 

4120 if self._key_trait: 

4121 key = self._key_trait.from_string(key) 

4122 

4123 # cast value with value trait, if defined (per-key or global) 

4124 value_trait = (self._per_key_traits or {}).get(key, self._value_trait) 

4125 if value_trait: 

4126 value = value_trait.from_string(value) 

4127 return {key: value} # type:ignore[dict-item] 

4128 

4129 

4130class TCPAddress(TraitType[G, S]): 

4131 """A trait for an (ip, port) tuple. 

4132 

4133 This allows for both IPv4 IP addresses as well as hostnames. 

4134 """ 

4135 

4136 default_value = ("127.0.0.1", 0) 

4137 info_text = "an (ip, port) tuple" 

4138 

4139 if t.TYPE_CHECKING: 

4140 

4141 @t.overload 

4142 def __init__( 

4143 self: TCPAddress[tuple[str, int], tuple[str, int]], 

4144 default_value: bool | Sentinel = ..., 

4145 allow_none: Literal[False] = ..., 

4146 read_only: bool | None = ..., 

4147 help: str | None = ..., 

4148 config: t.Any = ..., 

4149 **kwargs: t.Any, 

4150 ) -> None: 

4151 ... 

4152 

4153 @t.overload 

4154 def __init__( 

4155 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None], 

4156 default_value: bool | None | Sentinel = ..., 

4157 allow_none: Literal[True] = ..., 

4158 read_only: bool | None = ..., 

4159 help: str | None = ..., 

4160 config: t.Any = ..., 

4161 **kwargs: t.Any, 

4162 ) -> None: 

4163 ... 

4164 

4165 def __init__( 

4166 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None] 

4167 | TCPAddress[tuple[str, int], tuple[str, int]], 

4168 default_value: bool | None | Sentinel = Undefined, 

4169 allow_none: Literal[True, False] = False, 

4170 read_only: bool | None = None, 

4171 help: str | None = None, 

4172 config: t.Any = None, 

4173 **kwargs: t.Any, 

4174 ) -> None: 

4175 ... 

4176 

4177 def validate(self, obj: t.Any, value: t.Any) -> G: 

4178 if isinstance(value, tuple): 

4179 if len(value) == 2: 

4180 if isinstance(value[0], str) and isinstance(value[1], int): 

4181 port = value[1] 

4182 if port >= 0 and port <= 65535: 

4183 return value # type:ignore[return-value] 

4184 self.error(obj, value) 

4185 

4186 def from_string(self, s: str) -> G: 

4187 if self.allow_none and s == "None": 

4188 return None # type:ignore[return-value] 

4189 if ":" not in s: 

4190 raise ValueError("Require `ip:port`, got %r" % s) 

4191 ip, port_str = s.split(":", 1) 

4192 port = int(port_str) 

4193 return (ip, port) # type:ignore[return-value] 

4194 

4195 

4196class CRegExp(TraitType["re.Pattern[t.Any]", t.Union["re.Pattern[t.Any]", str]]): 

4197 """A casting compiled regular expression trait. 

4198 

4199 Accepts both strings and compiled regular expressions. The resulting 

4200 attribute will be a compiled regular expression.""" 

4201 

4202 info_text = "a regular expression" 

4203 

4204 def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None: 

4205 try: 

4206 return re.compile(value) 

4207 except Exception: 

4208 self.error(obj, value) 

4209 

4210 

4211class UseEnum(TraitType[t.Any, t.Any]): 

4212 """Use a Enum class as model for the data type description. 

4213 Note that if no default-value is provided, the first enum-value is used 

4214 as default-value. 

4215 

4216 .. sourcecode:: python 

4217 

4218 # -- SINCE: Python 3.4 (or install backport: pip install enum34) 

4219 import enum 

4220 from traitlets import HasTraits, UseEnum 

4221 

4222 

4223 class Color(enum.Enum): 

4224 red = 1 # -- IMPLICIT: default_value 

4225 blue = 2 

4226 green = 3 

4227 

4228 

4229 class MyEntity(HasTraits): 

4230 color = UseEnum(Color, default_value=Color.blue) 

4231 

4232 

4233 entity = MyEntity(color=Color.red) 

4234 entity.color = Color.green # USE: Enum-value (preferred) 

4235 entity.color = "green" # USE: name (as string) 

4236 entity.color = "Color.green" # USE: scoped-name (as string) 

4237 entity.color = 3 # USE: number (as int) 

4238 assert entity.color is Color.green 

4239 """ 

4240 

4241 default_value: enum.Enum | None = None 

4242 info_text = "Trait type adapter to a Enum class" 

4243 

4244 def __init__( 

4245 self, enum_class: type[t.Any], default_value: t.Any = None, **kwargs: t.Any 

4246 ) -> None: 

4247 assert issubclass(enum_class, enum.Enum), "REQUIRE: enum.Enum, but was: %r" % enum_class 

4248 allow_none = kwargs.get("allow_none", False) 

4249 if default_value is None and not allow_none: 

4250 default_value = next(iter(enum_class.__members__.values())) 

4251 super().__init__(default_value=default_value, **kwargs) 

4252 self.enum_class = enum_class 

4253 self.name_prefix = enum_class.__name__ + "." 

4254 

4255 def select_by_number(self, value: int, default: t.Any = Undefined) -> t.Any: 

4256 """Selects enum-value by using its number-constant.""" 

4257 assert isinstance(value, int) 

4258 enum_members = self.enum_class.__members__ 

4259 for enum_item in enum_members.values(): 

4260 if enum_item.value == value: 

4261 return enum_item 

4262 # -- NOT FOUND: 

4263 return default 

4264 

4265 def select_by_name(self, value: str, default: t.Any = Undefined) -> t.Any: 

4266 """Selects enum-value by using its name or scoped-name.""" 

4267 assert isinstance(value, str) 

4268 if value.startswith(self.name_prefix): 

4269 # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" 

4270 value = value.replace(self.name_prefix, "", 1) 

4271 return self.enum_class.__members__.get(value, default) 

4272 

4273 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4274 if isinstance(value, self.enum_class): 

4275 return value 

4276 elif isinstance(value, int): 

4277 # -- CONVERT: number => enum_value (item) 

4278 value2 = self.select_by_number(value) 

4279 if value2 is not Undefined: 

4280 return value2 

4281 elif isinstance(value, str): 

4282 # -- CONVERT: name or scoped_name (as string) => enum_value (item) 

4283 value2 = self.select_by_name(value) 

4284 if value2 is not Undefined: 

4285 return value2 

4286 elif value is None: 

4287 if self.allow_none: 

4288 return None 

4289 else: 

4290 return self.default_value 

4291 self.error(obj, value) 

4292 

4293 def _choices_str(self, as_rst: bool = False) -> str: 

4294 """Returns a description of the trait choices (not none).""" 

4295 choices = self.enum_class.__members__.keys() 

4296 if as_rst: 

4297 return "|".join("``%r``" % x for x in choices) 

4298 else: 

4299 return repr(list(choices)) # Listify because py3.4- prints odict-class 

4300 

4301 def _info(self, as_rst: bool = False) -> str: 

4302 """Returns a description of the trait.""" 

4303 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

4304 return f"any of {self._choices_str(as_rst)}{none}" 

4305 

4306 def info(self) -> str: 

4307 return self._info(as_rst=False) 

4308 

4309 def info_rst(self) -> str: 

4310 return self._info(as_rst=True) 

4311 

4312 

4313class Callable(TraitType[t.Callable[..., t.Any], t.Callable[..., t.Any]]): 

4314 """A trait which is callable. 

4315 

4316 Notes 

4317 ----- 

4318 Classes are callable, as are instances 

4319 with a __call__() method.""" 

4320 

4321 info_text = "a callable" 

4322 

4323 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4324 if callable(value): 

4325 return value 

4326 else: 

4327 self.error(obj, value)