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

1711 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 pathlib 

50import re 

51import sys 

52import types 

53import typing as t 

54from ast import literal_eval 

55 

56from .utils.bunch import Bunch 

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

58from .utils.getargspec import getargspec 

59from .utils.importstring import import_item 

60from .utils.sentinel import Sentinel 

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

62 

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

64 

65if t.TYPE_CHECKING: 

66 from typing_extensions import TypeVar 

67else: 

68 from typing import TypeVar 

69 

70# exports: 

71 

72__all__ = [ 

73 "All", 

74 "Any", 

75 "BaseDescriptor", 

76 "Bool", 

77 "Bytes", 

78 "CBool", 

79 "CBytes", 

80 "CComplex", 

81 "CFloat", 

82 "CInt", 

83 "CLong", 

84 "CRegExp", 

85 "CUnicode", 

86 "Callable", 

87 "CaselessStrEnum", 

88 "ClassBasedTraitType", 

89 "Complex", 

90 "Container", 

91 "DefaultHandler", 

92 "Dict", 

93 "DottedObjectName", 

94 "Enum", 

95 "EventHandler", 

96 "Float", 

97 "ForwardDeclaredInstance", 

98 "ForwardDeclaredMixin", 

99 "ForwardDeclaredType", 

100 "FuzzyEnum", 

101 "HasDescriptors", 

102 "HasTraits", 

103 "Instance", 

104 "Int", 

105 "Integer", 

106 "List", 

107 "Long", 

108 "MetaHasDescriptors", 

109 "MetaHasTraits", 

110 "ObjectName", 

111 "ObserveHandler", 

112 "Path", 

113 "Set", 

114 "TCPAddress", 

115 "This", 

116 "TraitError", 

117 "TraitType", 

118 "Tuple", 

119 "Type", 

120 "Undefined", 

121 "Unicode", 

122 "Union", 

123 "UseEnum", 

124 "ValidateHandler", 

125 "default", 

126 "directional_link", 

127 "dlink", 

128 "link", 

129 "observe", 

130 "observe_compat", 

131 "parse_notifier_name", 

132 "validate", 

133] 

134 

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

136 

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

138# Basic classes 

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

140 

141 

142Undefined = Sentinel( 

143 "Undefined", 

144 "traitlets", 

145 """ 

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

147""", 

148) 

149 

150All = Sentinel( 

151 "All", 

152 "traitlets", 

153 """ 

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

155from all trait attributes. 

156""", 

157) 

158 

159# Deprecated alias 

160NoDefaultSpecified = Undefined 

161 

162 

163class TraitError(Exception): 

164 pass 

165 

166 

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

168# Utilities 

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

170 

171 

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

173 warn( 

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

175 DeprecationWarning, 

176 stacklevel=2, 

177 ) 

178 return s.isidentifier() 

179 

180 

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

182 """Safely evaluate an expression 

183 

184 Returns original string if eval fails. 

185 

186 Use only where types are ambiguous. 

187 """ 

188 try: 

189 return literal_eval(s) 

190 except (NameError, SyntaxError, ValueError): 

191 return s 

192 

193 

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

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

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

197 

198 

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

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

201 

202 Examples 

203 -------- 

204 >>> parse_notifier_name([]) 

205 [traitlets.All] 

206 >>> parse_notifier_name("a") 

207 ['a'] 

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

209 ['a', 'b'] 

210 >>> parse_notifier_name(All) 

211 [traitlets.All] 

212 """ 

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

214 return [names] 

215 elif isinstance(names, Sentinel): 

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

217 else: 

218 if not names or All in names: 

219 return [All] 

220 for n in names: 

221 if not isinstance(n, str): 

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

223 return names 

224 

225 

226class _SimpleTest: 

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

228 self.value = value 

229 

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

231 return bool(test == self.value) 

232 

233 def __repr__(self) -> str: 

234 return f"<SimpleTest({self.value!r})" 

235 

236 def __str__(self) -> str: 

237 return self.__repr__() 

238 

239 

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

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

242 

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

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

245 in zope.interface with the __provides__ attribute. 

246 """ 

247 results = [] 

248 for key in dir(object): 

249 try: 

250 value = getattr(object, key) 

251 except AttributeError: 

252 pass 

253 else: 

254 if not predicate or predicate(value): 

255 results.append((key, value)) 

256 results.sort() 

257 return results 

258 

259 

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

261 """Validate arguments for traitlet link functions""" 

262 for tup in tuples: 

263 if not len(tup) == 2: 

264 raise TypeError( 

265 f"Each linked traitlet must be specified as (HasTraits, 'trait_name'), not {t!r}" 

266 ) 

267 obj, trait_name = tup 

268 if not isinstance(obj, HasTraits): 

269 raise TypeError(f"Each object must be HasTraits, not {type(obj)!r}") 

270 if trait_name not in obj.traits(): 

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

272 

273 

274class link: 

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

276 

277 Parameters 

278 ---------- 

279 source : (object / attribute name) pair 

280 target : (object / attribute name) pair 

281 transform: iterable with two callables (optional) 

282 Data transformation between source and target and target and source. 

283 

284 Examples 

285 -------- 

286 >>> class X(HasTraits): 

287 ... value = Int() 

288 

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

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

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

292 

293 Setting source updates target objects: 

294 >>> src.value = 5 

295 >>> tgt.value 

296 5 

297 """ 

298 

299 updating = False 

300 

301 def __init__( 

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

303 ) -> None: 

304 _validate_link(source, target) 

305 self.source, self.target = source, target 

306 if transform: 

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

308 self.link() 

309 

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

311 """default transform: no-op""" 

312 return x 

313 

314 _transform_inv = _transform 

315 

316 def link(self) -> None: 

317 try: 

318 setattr( 

319 self.target[0], 

320 self.target[1], 

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

322 ) 

323 

324 finally: 

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

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

327 

328 @contextlib.contextmanager 

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

330 self.updating = True 

331 try: 

332 yield 

333 finally: 

334 self.updating = False 

335 

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

337 if self.updating: 

338 return 

339 with self._busy_updating(): 

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

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

342 raise TraitError( 

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

344 ) 

345 

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

347 if self.updating: 

348 return 

349 with self._busy_updating(): 

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

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

352 raise TraitError( 

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

354 ) 

355 

356 def unlink(self) -> None: 

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

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

359 

360 

361class directional_link: 

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

363 

364 Parameters 

365 ---------- 

366 source : (object, attribute name) pair 

367 target : (object, attribute name) pair 

368 transform: callable (optional) 

369 Data transformation between source and target. 

370 

371 Examples 

372 -------- 

373 >>> class X(HasTraits): 

374 ... value = Int() 

375 

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

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

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

379 

380 Setting source updates target objects: 

381 >>> src.value = 5 

382 >>> tgt.value 

383 5 

384 

385 Setting target does not update source object: 

386 >>> tgt.value = 6 

387 >>> src.value 

388 5 

389 

390 """ 

391 

392 updating = False 

393 

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

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

396 _validate_link(source, target) 

397 self.source, self.target = source, target 

398 self.link() 

399 

400 def link(self) -> None: 

401 try: 

402 setattr( 

403 self.target[0], 

404 self.target[1], 

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

406 ) 

407 finally: 

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

409 

410 @contextlib.contextmanager 

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

412 self.updating = True 

413 try: 

414 yield 

415 finally: 

416 self.updating = False 

417 

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

419 if self.updating: 

420 return 

421 with self._busy_updating(): 

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

423 

424 def unlink(self) -> None: 

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

426 

427 

428dlink = directional_link 

429 

430 

431# ----------------------------------------------------------------------------- 

432# Base Descriptor Class 

433# ----------------------------------------------------------------------------- 

434 

435 

436class BaseDescriptor: 

437 """Base descriptor class 

438 

439 Notes 

440 ----- 

441 This implements Python's descriptor protocol. 

442 

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

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

445 class that does the following: 

446 

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

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

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

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

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

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

453 """ 

454 

455 name: str | None = None 

456 this_class: type[HasTraits] | None = None 

457 

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

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

460 HasDescriptors class. 

461 

462 It is typically overloaded for specific types. 

463 

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

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

466 has been assigned. 

467 """ 

468 self.this_class = cls 

469 self.name = name 

470 

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

472 # Instead of HasDescriptors.setup_instance calling 

473 # every instance_init, we opt in by default. 

474 # This gives descriptors a change to opt out for 

475 # performance reasons. 

476 # Because most traits do not need instance_init, 

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

478 # being created, this otherwise gives a significant performance 

479 # pentalty. Most TypeTraits in traitlets opt out. 

480 cls._instance_inits.append(self.instance_init) 

481 

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

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

484 HasDescriptors instance. 

485 

486 It is typically overloaded for specific types. 

487 

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

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

490 other descriptors. 

491 """ 

492 

493 

494G = TypeVar("G") 

495S = TypeVar("S") 

496T = TypeVar("T") 

497 

498 

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

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

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

502if t.TYPE_CHECKING: 

503 from typing import Literal 

504 

505 from typing_extensions import Self 

506 

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

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

509else: 

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

511 # the documentation of downstream projects. 

512 K = TypeVar("K") 

513 V = TypeVar("V") 

514 

515 

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

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

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

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

520 

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

522 allow_none: bool = False 

523 read_only: bool = False 

524 info_text: str = "any value" 

525 default_value: t.Any = Undefined 

526 

527 def __init__( 

528 self: TraitType[G, S], 

529 default_value: t.Any = Undefined, 

530 allow_none: bool = False, 

531 read_only: bool | None = None, 

532 help: str | None = None, 

533 config: t.Any = None, 

534 **kwargs: t.Any, 

535 ) -> None: 

536 """Declare a traitlet. 

537 

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

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

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

541 

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

543 

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

545 

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

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

548 """ 

549 if default_value is not Undefined: 

550 self.default_value = default_value 

551 if allow_none: 

552 self.allow_none = allow_none 

553 if read_only is not None: 

554 self.read_only = read_only 

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

556 if self.help: 

557 # define __doc__ so that inspectors like autodoc find traits 

558 self.__doc__ = self.help 

559 

560 if len(kwargs) > 0: 

561 stacklevel = 1 

562 f = inspect.currentframe() 

563 # count supers to determine stacklevel for warning 

564 assert f is not None 

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

566 stacklevel += 1 

567 f = f.f_back 

568 assert f is not None 

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

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

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

572 if should_warn(key): 

573 warn( 

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

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

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

577 DeprecationWarning, 

578 stacklevel=stacklevel, 

579 ) 

580 if len(self.metadata) > 0: 

581 self.metadata = self.metadata.copy() 

582 self.metadata.update(kwargs) 

583 else: 

584 self.metadata = kwargs 

585 else: 

586 self.metadata = self.metadata.copy() 

587 if config is not None: 

588 self.metadata["config"] = config 

589 

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

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

592 if help is not None: 

593 self.metadata["help"] = help 

594 

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

596 """Get a value from a config string 

597 

598 such as an environment variable or CLI arguments. 

599 

600 Traits can override this method to define their own 

601 parsing of config strings. 

602 

603 .. seealso:: item_from_string 

604 

605 .. versionadded:: 5.0 

606 """ 

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

608 return None 

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

610 

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

612 """The default generator for this trait 

613 

614 Notes 

615 ----- 

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

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

618 """ 

619 if self.default_value is not Undefined: 

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

621 elif hasattr(self, "make_dynamic_default"): 

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

623 else: 

624 # Undefined will raise in TraitType.get 

625 return self.default_value # type:ignore[return-value] 

626 

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

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

629 Use self.default_value instead 

630 """ 

631 warn( 

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

633 DeprecationWarning, 

634 stacklevel=2, 

635 ) 

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

637 

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

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

640 warn( 

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

642 DeprecationWarning, 

643 stacklevel=2, 

644 ) 

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

646 obj._trait_values[self.name] = value 

647 return value 

648 

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

650 assert self.name is not None 

651 try: 

652 value = obj._trait_values[self.name] 

653 except KeyError: 

654 # Check for a dynamic initializer. 

655 default = obj.trait_defaults(self.name) 

656 if default is Undefined: 

657 warn( 

658 "Explicit using of Undefined as the default value " 

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

660 "exceptions in the future.", 

661 DeprecationWarning, 

662 stacklevel=2, 

663 ) 

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

665 # write out the obj.cross_validation_lock call here. 

666 _cross_validation_lock = obj._cross_validation_lock 

667 try: 

668 obj._cross_validation_lock = True 

669 value = self._validate(obj, default) 

670 finally: 

671 obj._cross_validation_lock = _cross_validation_lock 

672 obj._trait_values[self.name] = value 

673 obj._notify_observers( 

674 Bunch( 

675 name=self.name, 

676 value=value, 

677 owner=obj, 

678 type="default", 

679 ) 

680 ) 

681 return value 

682 except Exception as e: 

683 # This should never be reached. 

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

685 else: 

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

687 

688 @t.overload 

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

690 

691 @t.overload 

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

693 

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

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

696 

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

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

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

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

701 """ 

702 if obj is None: 

703 return self 

704 else: 

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

706 

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

708 new_value = self._validate(obj, value) 

709 assert self.name is not None 

710 try: 

711 old_value = obj._trait_values[self.name] 

712 except KeyError: 

713 old_value = self.default_value 

714 

715 obj._trait_values[self.name] = new_value 

716 try: 

717 silent = bool(old_value == new_value) 

718 except Exception: 

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

720 silent = False 

721 if silent is not True: 

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

723 # comparison above returns something other than True/False 

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

725 

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

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

728 

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

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

731 """ 

732 if self.read_only: 

733 raise TraitError(f'The "{self.name}" trait is read-only.') 

734 self.set(obj, value) 

735 

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

737 if value is None and self.allow_none: 

738 return value 

739 if hasattr(self, "validate"): 

740 value = self.validate(obj, value) 

741 if obj._cross_validation_lock is False: 

742 value = self._cross_validate(obj, value) 

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

744 

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

746 if self.name in obj._trait_validators: 

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

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

749 elif hasattr(obj, f"_{self.name}_validate"): 

750 meth_name = f"_{self.name}_validate" 

751 cross_validate = getattr(obj, meth_name) 

752 deprecated_method( 

753 cross_validate, 

754 obj.__class__, 

755 meth_name, 

756 "use @validate decorator instead.", 

757 ) 

758 value = cross_validate(value, self) 

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

760 

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

762 if isinstance(other, Union): 

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

764 else: 

765 return Union([self, other]) 

766 

767 def info(self) -> str: 

768 return self.info_text 

769 

770 def error( 

771 self, 

772 obj: HasTraits | None, 

773 value: t.Any, 

774 error: Exception | None = None, 

775 info: str | None = None, 

776 ) -> t.NoReturn: 

777 """Raise a TraitError 

778 

779 Parameters 

780 ---------- 

781 obj : HasTraits or None 

782 The instance which owns the trait. If not 

783 object is given, then an object agnostic 

784 error will be raised. 

785 value : any 

786 The value that caused the error. 

787 error : Exception (default: None) 

788 An error that was raised by a child trait. 

789 The arguments of this exception should be 

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

791 Where the ``value`` and ``info`` are the 

792 problem value, and string describing the 

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

794 of :class:`TraitType` instances that are 

795 "children" of this one (the first being 

796 the deepest). 

797 info : str (default: None) 

798 A description of the expected value. By 

799 default this is inferred from this trait's 

800 ``info`` method. 

801 """ 

802 if error is not None: 

803 # handle nested error 

804 error.args += (self,) 

805 if self.name is not None: 

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

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

808 if obj is not None: 

809 error.args = ( 

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

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

812 self.name, 

813 describe("an", obj), 

814 chain, 

815 error.args[1], 

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

817 ), 

818 ) 

819 else: 

820 error.args = ( 

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

822 self.name, 

823 chain, 

824 error.args[1], 

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

826 ), 

827 ) 

828 raise error 

829 

830 # this trait caused an error 

831 if self.name is None: 

832 # this is not the root trait 

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

834 

835 # this is the root trait 

836 if obj is not None: 

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

838 self.name, 

839 class_of(obj), 

840 info or self.info(), 

841 describe("the", value), 

842 ) 

843 else: 

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

845 self.name, 

846 info or self.info(), 

847 describe("the", value), 

848 ) 

849 raise TraitError(e) 

850 

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

852 """DEPRECATED: Get a metadata value. 

853 

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

855 """ 

856 if key == "help": 

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

858 else: 

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

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

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

862 

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

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

865 

866 Use .metadata[key] = value instead. 

867 """ 

868 if key == "help": 

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

870 else: 

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

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

873 self.metadata[key] = value 

874 

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

876 """Sets metadata and returns self. 

877 

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

879 

880 Examples 

881 -------- 

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

883 <traitlets.traitlets.Int object at ...> 

884 

885 """ 

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

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

888 ) 

889 if maybe_constructor_keywords: 

890 warn( 

891 f"The following attributes are set in using `tag`, but seem to be constructor keywords arguments: {maybe_constructor_keywords} ", 

892 UserWarning, 

893 stacklevel=2, 

894 ) 

895 

896 self.metadata.update(metadata) 

897 return self 

898 

899 def default_value_repr(self) -> str: 

900 return repr(self.default_value) 

901 

902 

903# ----------------------------------------------------------------------------- 

904# The HasTraits implementation 

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

906 

907 

908class _CallbackWrapper: 

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

910 

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

912 callbacks. 

913 

914 __hash__ is deliberately left unset: instances compare equal to the callback 

915 they wrap, so no hash could stay consistent with __eq__. 

916 """ 

917 

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

919 self.cb = cb 

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

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

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

923 if self.nargs > 4: 

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

925 

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

927 # The wrapper is equal to the wrapped element 

928 if isinstance(other, _CallbackWrapper): 

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

930 else: 

931 return bool(self.cb == other) 

932 

933 def __hash__(self) -> int: 

934 # Keep the hash consistent with __eq__ (a wrapper hashes like its callback). 

935 return hash(self.cb) 

936 

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

938 # The wrapper is callable 

939 if self.nargs == 0: 

940 self.cb() 

941 elif self.nargs == 1: 

942 self.cb(change.name) 

943 elif self.nargs == 2: 

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

945 elif self.nargs == 3: 

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

947 elif self.nargs == 4: 

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

949 

950 

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

952 if isinstance(cb, _CallbackWrapper): 

953 return cb 

954 else: 

955 return _CallbackWrapper(cb) 

956 

957 

958class MetaHasDescriptors(type): 

959 """A metaclass for HasDescriptors. 

960 

961 This metaclass makes sure that any TraitType class attributes are 

962 instantiated and sets their name attribute. 

963 """ 

964 

965 def __new__( 

966 mcls: type[MetaHasDescriptors], 

967 name: str, 

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

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

970 **kwds: t.Any, 

971 ) -> MetaHasDescriptors: 

972 """Create the HasDescriptors class.""" 

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

974 # ---------------------------------------------------------------- 

975 # Support of deprecated behavior allowing for TraitType types 

976 # to be used instead of TraitType instances. 

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

978 warn( 

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

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

981 DeprecationWarning, 

982 stacklevel=2, 

983 ) 

984 classdict[k] = v() 

985 # ---------------------------------------------------------------- 

986 

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

988 

989 def __init__( 

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

991 ) -> None: 

992 """Finish initializing the HasDescriptors class.""" 

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

994 cls.setup_class(classdict) 

995 

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

997 """Setup descriptor instance on the class 

998 

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

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

1001 calling their :attr:`class_init` method. 

1002 """ 

1003 cls._descriptors = [] 

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

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

1006 if isinstance(v, BaseDescriptor): 

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

1008 

1009 for _, v in getmembers(cls): 

1010 if isinstance(v, BaseDescriptor): 

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

1012 cls._descriptors.append(v) 

1013 

1014 

1015class MetaHasTraits(MetaHasDescriptors): 

1016 """A metaclass for HasTraits.""" 

1017 

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

1019 # for only the current class 

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

1021 # also looking at base classes 

1022 cls._all_trait_default_generators = {} 

1023 cls._traits = {} 

1024 cls._static_immutable_initial_values = {} 

1025 

1026 super().setup_class(classdict) 

1027 

1028 mro = cls.mro() 

1029 

1030 for name in dir(cls): 

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

1032 # __provides__ attributes even though they exist. This causes 

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

1034 try: 

1035 value = getattr(cls, name) 

1036 except AttributeError: 

1037 continue 

1038 if isinstance(value, TraitType): 

1039 cls._traits[name] = value 

1040 trait = value 

1041 default_method_name = f"_{name}_default" 

1042 mro_trait = mro 

1043 try: 

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

1045 except ValueError: 

1046 # this_class not in mro 

1047 pass 

1048 for c in mro_trait: 

1049 if default_method_name in c.__dict__: 

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

1051 break 

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

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

1054 break 

1055 else: 

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

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

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

1059 # of initial values to speed up instance creation. 

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

1061 # for instance ipywidgets. 

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

1063 if ( 

1064 type(trait) in [CInt, Int, Long, CLong] 

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

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

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

1068 ): 

1069 cls._static_immutable_initial_values[name] = trait.default_value 

1070 elif ( 

1071 type(trait) in [CFloat, Float] 

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

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

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

1075 ): 

1076 cls._static_immutable_initial_values[name] = trait.default_value 

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

1078 isinstance(trait.default_value, bool) or none_ok 

1079 ): 

1080 cls._static_immutable_initial_values[name] = trait.default_value 

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

1082 isinstance(trait.default_value, str) or none_ok 

1083 ): 

1084 cls._static_immutable_initial_values[name] = trait.default_value 

1085 elif type(trait) is Any and ( 

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

1087 ): 

1088 cls._static_immutable_initial_values[name] = trait.default_value 

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

1090 cls._static_immutable_initial_values[name] = None 

1091 elif ( 

1092 isinstance(trait, Instance) 

1093 and trait.default_args is None 

1094 and trait.default_kwargs is None 

1095 and trait.allow_none 

1096 ): 

1097 cls._static_immutable_initial_values[name] = None 

1098 

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

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

1101 cls._all_trait_default_generators[name] = trait.default 

1102 

1103 

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

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

1106 

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

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

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

1110 name of the attribute that triggered the notification. 

1111 

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

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

1114 * ``owner`` : the HasTraits instance 

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

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

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

1118 

1119 Parameters 

1120 ---------- 

1121 *names 

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

1123 type : str, kwarg-only 

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

1125 """ 

1126 if not names: 

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

1128 for name in names: 

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

1130 raise TypeError(f"trait names to observe must be strings or All, not {name!r}") 

1131 return ObserveHandler(names, type=type) 

1132 

1133 

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

1135 """Backward-compatibility shim decorator for observers 

1136 

1137 Use with: 

1138 

1139 @observe('name') 

1140 @observe_compat 

1141 def _foo_changed(self, change): 

1142 ... 

1143 

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

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

1146 """ 

1147 

1148 def compatible_observer( 

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

1150 ) -> t.Any: 

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

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

1153 else: 

1154 clsname = self.__class__.__name__ 

1155 warn( 

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

1157 DeprecationWarning, 

1158 stacklevel=2, 

1159 ) 

1160 change = Bunch( 

1161 type="change", 

1162 old=old, 

1163 new=new, 

1164 name=change_or_name, 

1165 owner=self, 

1166 ) 

1167 return func(self, change) 

1168 

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

1170 

1171 

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

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

1174 when a Trait is set. 

1175 

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

1177 The proposal dictionary must hold the following keys: 

1178 

1179 * ``owner`` : the HasTraits instance 

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

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

1182 

1183 Parameters 

1184 ---------- 

1185 *names 

1186 The str names of the Traits to validate. 

1187 

1188 Notes 

1189 ----- 

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

1191 the registered cross validator could potentially make changes to attributes 

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

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

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

1195 commute. 

1196 """ 

1197 if not names: 

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

1199 for name in names: 

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

1201 raise TypeError(f"trait names to validate must be strings or All, not {name!r}") 

1202 return ValidateHandler(names) 

1203 

1204 

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

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

1207 

1208 Parameters 

1209 ---------- 

1210 name 

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

1212 

1213 Notes 

1214 ----- 

1215 Unlike observers and validators which are properties of the HasTraits 

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

1217 

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

1219 subclasses of `this_type`. 

1220 

1221 :: 

1222 

1223 class A(HasTraits): 

1224 bar = Int() 

1225 

1226 @default('bar') 

1227 def get_bar_default(self): 

1228 return 11 

1229 

1230 class B(A): 

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

1232 # the base class A 

1233 

1234 class C(B): 

1235 

1236 @default('bar') 

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

1238 return 3.0 # ignored since it is defined in a 

1239 # class derived from B.a.this_class. 

1240 """ 

1241 if not isinstance(name, str): 

1242 raise TypeError(f"Trait name must be a string or All, not {name!r}") 

1243 return DefaultHandler(name) 

1244 

1245 

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

1247 

1248 

1249class EventHandler(BaseDescriptor): 

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

1251 self.func = func 

1252 return self 

1253 

1254 @t.overload 

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

1256 

1257 @t.overload 

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

1259 

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

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

1262 if hasattr(self, "func"): 

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

1264 else: 

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

1266 

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

1268 if inst is None: 

1269 return self 

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

1271 

1272 

1273class ObserveHandler(EventHandler): 

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

1275 self.trait_names = names 

1276 self.type = type 

1277 

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

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

1280 

1281 

1282class ValidateHandler(EventHandler): 

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

1284 self.trait_names = names 

1285 

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

1287 inst._register_validator(self, self.trait_names) 

1288 

1289 

1290class DefaultHandler(EventHandler): 

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

1292 self.trait_name = name 

1293 

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

1295 super().class_init(cls, name) 

1296 cls._trait_default_generators[self.trait_name] = self 

1297 

1298 

1299class HasDescriptors(metaclass=MetaHasDescriptors): 

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

1301 

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

1303 # This is needed because object.__new__ only accepts 

1304 # the cls argument. 

1305 new_meth = super(HasDescriptors, cls).__new__ 

1306 if new_meth is object.__new__: 

1307 inst = new_meth(cls) 

1308 else: 

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

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

1311 return inst 

1312 

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

1314 """ 

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

1316 """ 

1317 

1318 self._cross_validation_lock = False 

1319 cls = self.__class__ 

1320 # Let descriptors performance initialization when a HasDescriptor 

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

1322 # default creations or other bookkeepings. 

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

1324 # subclass_init. 

1325 for init in cls._instance_inits: 

1326 init(self) 

1327 

1328 

1329class HasTraits(HasDescriptors, metaclass=MetaHasTraits): 

1330 _trait_values: dict[str, t.Any] 

1331 _static_immutable_initial_values: dict[str, t.Any] 

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

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

1334 _cross_validation_lock: bool 

1335 _traits: dict[str, t.Any] 

1336 _all_trait_default_generators: dict[str, t.Any] 

1337 

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

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

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

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

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

1343 self._trait_notifiers = {} 

1344 self._trait_validators = {} 

1345 self._cross_validation_lock = False 

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

1347 

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

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

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

1351 # notifications. 

1352 super_args = args 

1353 super_kwargs = {} 

1354 

1355 if kwargs: 

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

1357 # the hold_trait_notifications(self) context manager 

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

1359 pass 

1360 

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

1362 self._cross_validation_lock = True 

1363 changes = {} 

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

1365 if self.has_trait(key): 

1366 setattr(self, key, value) 

1367 changes[key] = Bunch( 

1368 name=key, 

1369 old=None, 

1370 new=value, 

1371 owner=self, 

1372 type="change", 

1373 ) 

1374 else: 

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

1376 super_kwargs[key] = value 

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

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

1379 for key in changed: 

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

1381 self.set_trait(key, value) 

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

1383 self._cross_validation_lock = False 

1384 # Restore method retrieval from class 

1385 del self.notify_change 

1386 for key in changed: 

1387 self.notify_change(changes[key]) 

1388 

1389 try: 

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

1391 except TypeError as e: 

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

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

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

1395 arg_s = ", ".join(arg_s_list) 

1396 warn( 

1397 f"Passing unrecognized arguments to super({self.__class__.__name__}).__init__({arg_s}).\n" 

1398 f"{e}\n" 

1399 "This is deprecated in traitlets 4.2." 

1400 "This error will be raised in a future release of traitlets.", 

1401 DeprecationWarning, 

1402 stacklevel=2, 

1403 ) 

1404 

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

1406 d = self.__dict__.copy() 

1407 # event handlers stored on an instance are 

1408 # expected to be reinstantiated during a 

1409 # recall of instance_init during __setstate__ 

1410 d["_trait_notifiers"] = {} 

1411 d["_trait_validators"] = {} 

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

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

1414 

1415 return d 

1416 

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

1418 self.__dict__ = state.copy() 

1419 

1420 # event handlers are reassigned to self 

1421 cls = self.__class__ 

1422 for key in dir(cls): 

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

1424 # __provides__ attributes even though they exist. This causes 

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

1426 try: 

1427 value = getattr(cls, key) 

1428 except AttributeError: 

1429 pass 

1430 else: 

1431 if isinstance(value, EventHandler): 

1432 value.instance_init(self) 

1433 

1434 @property 

1435 @contextlib.contextmanager 

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

1437 """ 

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

1439 to True. 

1440 

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

1442 prior to entering the block. 

1443 """ 

1444 if self._cross_validation_lock: 

1445 yield 

1446 return 

1447 else: 

1448 try: 

1449 self._cross_validation_lock = True 

1450 yield 

1451 finally: 

1452 self._cross_validation_lock = False 

1453 

1454 @contextlib.contextmanager 

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

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

1457 validation. 

1458 

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

1460 race conditions in trait notifiers requesting other trait values. 

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

1462 """ 

1463 if self._cross_validation_lock: 

1464 yield 

1465 return 

1466 else: 

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

1468 

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

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

1471 if past_changes is None: 

1472 return [change] 

1473 else: 

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

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

1476 else: 

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

1478 past_changes.append(change) 

1479 return past_changes 

1480 

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

1482 name = change.name 

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

1484 

1485 try: 

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

1487 # notifications, disable cross validation and yield. 

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

1489 self._cross_validation_lock = True 

1490 yield 

1491 # Cross validate final values when context is released. 

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

1493 trait = getattr(self.__class__, name) 

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

1495 self.set_trait(name, value) 

1496 except TraitError as e: 

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

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

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

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

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

1502 if change.type == "change": 

1503 if change.old is not Undefined: 

1504 self.set_trait(name, change.old) 

1505 else: 

1506 self._trait_values.pop(name) 

1507 cache = {} 

1508 raise e 

1509 finally: 

1510 self._cross_validation_lock = False 

1511 # Restore method retrieval from class 

1512 del self.notify_change 

1513 

1514 # trigger delayed notifications 

1515 for changes in cache.values(): 

1516 for change in changes: 

1517 self.notify_change(change) 

1518 

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

1520 self.notify_change( 

1521 Bunch( 

1522 name=name, 

1523 old=old_value, 

1524 new=new_value, 

1525 owner=self, 

1526 type="change", 

1527 ) 

1528 ) 

1529 

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

1531 """Notify observers of a change event""" 

1532 return self._notify_observers(change) 

1533 

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

1535 """Notify observers of any event""" 

1536 if not isinstance(event, Bunch): 

1537 # cast to bunch if given a dict 

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

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

1540 

1541 callables = [] 

1542 if name in self._trait_notifiers: 

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

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

1545 if All in self._trait_notifiers: 

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

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

1548 

1549 # Now static ones 

1550 magic_name = f"_{name}_changed" 

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

1552 class_value = getattr(self.__class__, magic_name) 

1553 if not isinstance(class_value, ObserveHandler): 

1554 deprecated_method( 

1555 class_value, 

1556 self.__class__, 

1557 magic_name, 

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

1559 ) 

1560 cb = getattr(self, magic_name) 

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

1562 if cb not in callables: 

1563 callables.append(_callback_wrapper(cb)) 

1564 

1565 # Call them all now 

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

1567 for c in callables: 

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

1569 

1570 if isinstance(c, _CallbackWrapper): 

1571 c = c.__call__ 

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

1573 c = getattr(self, c.name) 

1574 

1575 c(event) 

1576 

1577 def _add_notifiers( 

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

1579 ) -> None: 

1580 if name not in self._trait_notifiers: 

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

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

1583 else: 

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

1585 nlist = [] 

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

1587 else: 

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

1589 if handler not in nlist: 

1590 nlist.append(handler) 

1591 

1592 def _remove_notifiers( 

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

1594 ) -> None: 

1595 try: 

1596 if handler is None: 

1597 del self._trait_notifiers[name][type] 

1598 else: 

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

1600 except KeyError: 

1601 pass 

1602 

1603 def on_trait_change( 

1604 self, 

1605 handler: EventHandler | None = None, 

1606 name: Sentinel | str | None = None, 

1607 remove: bool = False, 

1608 ) -> None: 

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

1610 

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

1612 

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

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

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

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

1617 below). 

1618 

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

1620 handlers for the specified name are uninstalled. 

1621 

1622 Parameters 

1623 ---------- 

1624 handler : callable, None 

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

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

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

1628 name : list, str, None 

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

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

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

1632 remove : bool 

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

1634 then unintall it. 

1635 """ 

1636 warn( 

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

1638 DeprecationWarning, 

1639 stacklevel=2, 

1640 ) 

1641 if name is None: 

1642 name = All 

1643 if remove: 

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

1645 else: 

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

1647 

1648 def observe( 

1649 self, 

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

1651 names: Sentinel | str | t.Collection[Sentinel | str] = All, 

1652 type: Sentinel | str = "change", 

1653 ) -> None: 

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

1655 

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

1657 

1658 Parameters 

1659 ---------- 

1660 handler : callable 

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

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

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

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

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

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

1667 * ``owner`` : the HasTraits instance 

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

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

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

1671 names : list, str, All 

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

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

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

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

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

1677 notifications are passed to the observe handler. 

1678 """ 

1679 for name in parse_notifier_name(names): 

1680 self._add_notifiers(handler, name, type) 

1681 

1682 def unobserve( 

1683 self, 

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

1685 names: Sentinel | str | t.Collection[Sentinel | str] = All, 

1686 type: Sentinel | str = "change", 

1687 ) -> None: 

1688 """Remove a trait change handler. 

1689 

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

1691 

1692 Parameters 

1693 ---------- 

1694 handler : callable 

1695 The callable called when a trait attribute changes. 

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

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

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

1699 from the list of notifiers corresponding to all changes. 

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

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

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

1703 """ 

1704 for name in parse_notifier_name(names): 

1705 self._remove_notifiers(handler, name, type) 

1706 

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

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

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

1710 if name is All: 

1711 self._trait_notifiers = {} 

1712 else: 

1713 try: 

1714 del self._trait_notifiers[name] 

1715 except KeyError: 

1716 pass 

1717 

1718 def _register_validator( 

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

1720 ) -> None: 

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

1722 

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

1724 

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

1726 TraitError is raised and no new validator is registered. 

1727 

1728 Parameters 

1729 ---------- 

1730 handler : callable 

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

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

1733 with the following attributes/keys: 

1734 * ``owner`` : the HasTraits instance 

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

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

1737 names : List of strings 

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

1739 """ 

1740 for name in names: 

1741 magic_name = f"_{name}_validate" 

1742 if hasattr(self, magic_name): 

1743 class_value = getattr(self.__class__, magic_name) 

1744 if not isinstance(class_value, ValidateHandler): 

1745 deprecated_method( 

1746 class_value, 

1747 self.__class__, 

1748 magic_name, 

1749 "use @validate decorator instead.", 

1750 ) 

1751 for name in names: 

1752 self._trait_validators[name] = handler 

1753 

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

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

1756 cls = self.__class__ 

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

1758 if hasattr(cls, "__qualname__"): 

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

1760 attrs["__qualname__"] = cls.__qualname__ 

1761 attrs.update(traits) 

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

1763 for trait in traits.values(): 

1764 trait.instance_init(self) 

1765 

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

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

1768 cls = self.__class__ 

1769 if not self.has_trait(name): 

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

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

1772 

1773 @classmethod 

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

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

1776 

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

1778 but is unbound. 

1779 """ 

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

1781 

1782 @classmethod 

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

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

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

1786 

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

1788 

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

1790 that the various HasTrait's instances are holding. 

1791 

1792 The metadata kwargs allow functions to be passed in which 

1793 filter traits based on metadata values. The functions should 

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

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

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

1797 to the function. 

1798 """ 

1799 traits = cls._traits.copy() 

1800 

1801 if len(metadata) == 0: 

1802 return traits 

1803 

1804 result = {} 

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

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

1807 if not callable(meta_eval): 

1808 meta_eval = _SimpleTest(meta_eval) 

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

1810 break 

1811 else: 

1812 result[name] = trait 

1813 

1814 return result 

1815 

1816 @classmethod 

1817 def class_own_traits( 

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

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

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

1821 

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

1823 """ 

1824 sup = super(cls, cls) 

1825 return { 

1826 n: t 

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

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

1829 } 

1830 

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

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

1833 return name in self._traits 

1834 

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

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

1837 

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

1839 dynamically generated default value. These default values 

1840 will be recognized as existing only after they have been 

1841 generated. 

1842 

1843 Example 

1844 

1845 .. code-block:: python 

1846 

1847 class MyClass(HasTraits): 

1848 i = Int() 

1849 

1850 

1851 mc = MyClass() 

1852 assert not mc.trait_has_value("i") 

1853 mc.i # generates a default value 

1854 assert mc.trait_has_value("i") 

1855 """ 

1856 return name in self._trait_values 

1857 

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

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

1860 

1861 The metadata kwargs allow functions to be passed in which 

1862 filter traits based on metadata values. The functions should 

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

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

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

1866 to the function. 

1867 

1868 Returns 

1869 ------- 

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

1871 

1872 Notes 

1873 ----- 

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

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

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

1877 """ 

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

1879 

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

1881 """Return default generator for a given trait 

1882 

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

1884 """ 

1885 method_name = f"_{name}_default" 

1886 if method_name in self.__dict__: 

1887 return getattr(self, method_name) 

1888 if method_name in self.__class__.__dict__: 

1889 return getattr(self.__class__, method_name) 

1890 return self._all_trait_default_generators[name] 

1891 

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

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

1894 

1895 Notes 

1896 ----- 

1897 Dynamically generated default values may 

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

1899 for n in names: 

1900 if not self.has_trait(n): 

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

1902 

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

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

1905 

1906 trait_names = self.trait_names(**metadata) 

1907 trait_names.extend(names) 

1908 

1909 defaults = {} 

1910 for n in trait_names: 

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

1912 return defaults 

1913 

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

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

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

1917 

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

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

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

1921 

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

1923 that the various HasTrait's instances are holding. 

1924 

1925 The metadata kwargs allow functions to be passed in which 

1926 filter traits based on metadata values. The functions should 

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

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

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

1930 to the function. 

1931 """ 

1932 traits = self._traits.copy() 

1933 

1934 if len(metadata) == 0: 

1935 return traits 

1936 

1937 result = {} 

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

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

1940 if not callable(meta_eval): 

1941 meta_eval = _SimpleTest(meta_eval) 

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

1943 break 

1944 else: 

1945 result[name] = trait 

1946 

1947 return result 

1948 

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

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

1951 try: 

1952 trait = getattr(self.__class__, traitname) 

1953 except AttributeError as e: 

1954 raise TraitError( 

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

1956 ) from e 

1957 metadata_name = "_" + traitname + "_metadata" 

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

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

1960 else: 

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

1962 

1963 @classmethod 

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

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

1966 

1967 Works like ``trait_events``, except for excluding traits from parents. 

1968 """ 

1969 sup = super(cls, cls) 

1970 return {n: e for (n, e) in cls.trait_events(name).items() if getattr(sup, n, None) is not e} 

1971 

1972 @classmethod 

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

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

1975 

1976 Parameters 

1977 ---------- 

1978 name : str (default: None) 

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

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

1981 

1982 Returns 

1983 ------- 

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

1985 """ 

1986 events = {} 

1987 for k, v in getmembers(cls): 

1988 if isinstance(v, EventHandler): 

1989 if name is None: 

1990 events[k] = v 

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

1992 events[k] = v 

1993 return events 

1994 

1995 

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

1997# Actual TraitTypes implementations/subclasses 

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

1999 

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

2001# TraitTypes subclasses for handling classes and instances of classes 

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

2003 

2004 

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

2006 """ 

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

2008 Instance and This. 

2009 """ 

2010 

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

2012 """ 

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

2014 """ 

2015 return import_item(string) 

2016 

2017 

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

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

2020 

2021 if t.TYPE_CHECKING: 

2022 

2023 @t.overload 

2024 def __init__( 

2025 self: Type[type, type], 

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

2027 klass: None | str = ..., 

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

2029 read_only: bool | None = ..., 

2030 help: str | None = ..., 

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

2032 **kwargs: t.Any, 

2033 ) -> None: ... 

2034 

2035 @t.overload 

2036 def __init__( 

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

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

2039 klass: None | str = ..., 

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

2041 read_only: bool | None = ..., 

2042 help: str | None = ..., 

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

2044 **kwargs: t.Any, 

2045 ) -> None: ... 

2046 

2047 @t.overload 

2048 def __init__( 

2049 self: Type[S, S], 

2050 default_value: S = ..., 

2051 klass: S = ..., 

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

2053 read_only: bool | None = ..., 

2054 help: str | None = ..., 

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

2056 **kwargs: t.Any, 

2057 ) -> None: ... 

2058 

2059 @t.overload 

2060 def __init__( 

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

2062 default_value: S | None = ..., 

2063 klass: S = ..., 

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

2065 read_only: bool | None = ..., 

2066 help: str | None = ..., 

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

2068 **kwargs: t.Any, 

2069 ) -> None: ... 

2070 

2071 def __init__( 

2072 self, 

2073 default_value: t.Any = Undefined, 

2074 klass: t.Any = None, 

2075 allow_none: bool = False, 

2076 read_only: bool | None = None, 

2077 help: str | None = None, 

2078 config: t.Any | None = None, 

2079 **kwargs: t.Any, 

2080 ) -> None: 

2081 """Construct a Type trait 

2082 

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

2084 a particular class. 

2085 

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

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

2088 

2089 Parameters 

2090 ---------- 

2091 default_value : class, str or None 

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

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

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

2095 :class:`HasTraits` class is instantiated. 

2096 klass : class, str [ default object ] 

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

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

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

2100 :class:`HasTraits` class is instantiated. 

2101 allow_none : bool [ default False ] 

2102 Indicates whether None is allowed as an assignable value. 

2103 **kwargs 

2104 extra kwargs passed to `ClassBasedTraitType` 

2105 """ 

2106 if default_value is Undefined: 

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

2108 else: 

2109 new_default_value = default_value 

2110 

2111 if klass is None: 

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

2113 klass = object 

2114 else: 

2115 klass = default_value 

2116 

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

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

2119 

2120 self.klass = klass 

2121 

2122 super().__init__( 

2123 new_default_value, 

2124 allow_none=allow_none, 

2125 read_only=read_only, 

2126 help=help, 

2127 config=config, 

2128 **kwargs, 

2129 ) 

2130 

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

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

2133 if isinstance(value, str): 

2134 try: 

2135 value = self._resolve_string(value) 

2136 except ImportError as e: 

2137 raise TraitError( 

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

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

2140 ) from e 

2141 try: 

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

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

2144 except Exception: 

2145 pass 

2146 

2147 self.error(obj, value) 

2148 

2149 def info(self) -> str: 

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

2151 if isinstance(self.klass, str): 

2152 klass = self.klass 

2153 else: 

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

2155 result = f"a subclass of '{klass}'" 

2156 if self.allow_none: 

2157 return result + " or None" 

2158 return result 

2159 

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

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

2162 # might be called before all imports are done. 

2163 self._resolve_classes() 

2164 

2165 def _resolve_classes(self) -> None: 

2166 if isinstance(self.klass, str): 

2167 self.klass = self._resolve_string(self.klass) 

2168 if isinstance(self.default_value, str): 

2169 self.default_value = self._resolve_string(self.default_value) 

2170 

2171 def default_value_repr(self) -> str: 

2172 value = self.default_value 

2173 assert value is not None 

2174 if isinstance(value, str): 

2175 return repr(value) 

2176 else: 

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

2178 

2179 

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

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

2182 

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

2184 

2185 Subclasses can declare default classes by overriding the klass attribute 

2186 """ 

2187 

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

2189 

2190 if t.TYPE_CHECKING: 

2191 

2192 @t.overload 

2193 def __init__( 

2194 self: Instance[T], 

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

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

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

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

2199 read_only: bool | None = ..., 

2200 help: str | None = ..., 

2201 **kwargs: t.Any, 

2202 ) -> None: ... 

2203 

2204 @t.overload 

2205 def __init__( 

2206 self: Instance[T | None], 

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

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

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

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

2211 read_only: bool | None = ..., 

2212 help: str | None = ..., 

2213 **kwargs: t.Any, 

2214 ) -> None: ... 

2215 

2216 @t.overload 

2217 def __init__( 

2218 self: Instance[t.Any], 

2219 klass: str | None = ..., 

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

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

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

2223 read_only: bool | None = ..., 

2224 help: str | None = ..., 

2225 **kwargs: t.Any, 

2226 ) -> None: ... 

2227 

2228 @t.overload 

2229 def __init__( 

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

2231 klass: str | None = ..., 

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

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

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

2235 read_only: bool | None = ..., 

2236 help: str | None = ..., 

2237 **kwargs: t.Any, 

2238 ) -> None: ... 

2239 

2240 def __init__( 

2241 self, 

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

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

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

2245 allow_none: bool = False, 

2246 read_only: bool | None = None, 

2247 help: str | None = None, 

2248 **kwargs: t.Any, 

2249 ) -> None: 

2250 """Construct an Instance trait. 

2251 

2252 This trait allows values that are instances of a particular 

2253 class or its subclasses. Our implementation is quite different 

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

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

2256 

2257 Parameters 

2258 ---------- 

2259 klass : class, str 

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

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

2262 args : tuple 

2263 Positional arguments for generating the default value. 

2264 kw : dict 

2265 Keyword arguments for generating the default value. 

2266 allow_none : bool [ default False ] 

2267 Indicates whether None is allowed as a value. 

2268 **kwargs 

2269 Extra kwargs passed to `ClassBasedTraitType` 

2270 

2271 Notes 

2272 ----- 

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

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

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

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

2277 """ 

2278 if klass is None: 

2279 klass = self.klass 

2280 

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

2282 self.klass = klass 

2283 else: 

2284 raise TraitError(f"The klass attribute must be a class not: {klass!r}") 

2285 

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

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

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

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

2290 

2291 self.default_args = args 

2292 self.default_kwargs = kw 

2293 

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

2295 

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

2297 assert self.klass is not None 

2298 if self.allow_none and value is None: 

2299 return value 

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

2301 return value 

2302 else: 

2303 self.error(obj, value) 

2304 

2305 def info(self) -> str: 

2306 if isinstance(self.klass, str): 

2307 result = add_article(self.klass) 

2308 else: 

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

2310 if self.allow_none: 

2311 result += " or None" 

2312 return result 

2313 

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

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

2316 # might be called before all imports are done. 

2317 self._resolve_classes() 

2318 

2319 def _resolve_classes(self) -> None: 

2320 if isinstance(self.klass, str): 

2321 self.klass = self._resolve_string(self.klass) 

2322 

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

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

2325 return None 

2326 assert self.klass is not None 

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

2328 

2329 def default_value_repr(self) -> str: 

2330 return repr(self.make_dynamic_default()) 

2331 

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

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

2334 

2335 

2336class ForwardDeclaredMixin: 

2337 """ 

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

2339 """ 

2340 

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

2342 """ 

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

2344 our this_class attribute was defined. 

2345 """ 

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

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

2348 

2349 

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

2351 """ 

2352 Forward-declared version of Type. 

2353 """ 

2354 

2355 

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

2357 """ 

2358 Forward-declared version of Instance. 

2359 """ 

2360 

2361 

2362class This(ClassBasedTraitType[T | None, T | None]): 

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

2364 

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

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

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

2368 """ 

2369 

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

2371 

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

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

2374 

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

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

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

2378 # trait. 

2379 assert self.this_class is not None 

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

2381 return value 

2382 else: 

2383 self.error(obj, value) 

2384 

2385 

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

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

2388 

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

2390 """Construct a Union trait. 

2391 

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

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

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

2395 

2396 Parameters 

2397 ---------- 

2398 trait_types : sequence 

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

2400 **kwargs 

2401 Extra kwargs passed to `TraitType` 

2402 

2403 Notes 

2404 ----- 

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

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

2407 

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

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

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

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

2412 ``_literal_from_string_pairs`` in subclasses. 

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

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

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

2416 """ 

2417 self.trait_types = list(trait_types) 

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

2419 super().__init__(**kwargs) 

2420 

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

2422 default = super().default(obj) 

2423 for trait in self.trait_types: 

2424 if default is Undefined: 

2425 default = trait.default(obj) 

2426 else: 

2427 break 

2428 return default 

2429 

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

2431 for trait_type in reversed(self.trait_types): 

2432 trait_type.class_init(cls, None) 

2433 super().class_init(cls, name) 

2434 

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

2436 for trait_type in reversed(self.trait_types): 

2437 trait_type.subclass_init(cls) 

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

2439 # to opt out of instance_init 

2440 

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

2442 with obj.cross_validation_lock: 

2443 for trait_type in self.trait_types: 

2444 try: 

2445 v = trait_type._validate(obj, value) 

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

2447 if self.name is not None: 

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

2449 return v 

2450 except TraitError: 

2451 continue 

2452 self.error(obj, value) 

2453 

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

2455 if isinstance(other, Union): 

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

2457 else: 

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

2459 

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

2461 for trait_type in self.trait_types: 

2462 try: 

2463 v = trait_type.from_string(s) 

2464 return trait_type.validate(None, v) 

2465 except (TraitError, ValueError): 

2466 continue 

2467 return super().from_string(s) 

2468 

2469 

2470# ----------------------------------------------------------------------------- 

2471# Basic TraitTypes implementations/subclasses 

2472# ----------------------------------------------------------------------------- 

2473 

2474 

2475class Any(TraitType[t.Any | None, t.Any | None]): 

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

2477 

2478 if t.TYPE_CHECKING: 

2479 

2480 @t.overload 

2481 def __init__( 

2482 self: Any, 

2483 default_value: t.Any = ..., 

2484 *, 

2485 allow_none: Literal[False], 

2486 read_only: bool | None = ..., 

2487 help: str | None = ..., 

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

2489 **kwargs: t.Any, 

2490 ) -> None: ... 

2491 

2492 @t.overload 

2493 def __init__( 

2494 self: Any, 

2495 default_value: t.Any = ..., 

2496 *, 

2497 allow_none: Literal[True], 

2498 read_only: bool | None = ..., 

2499 help: str | None = ..., 

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

2501 **kwargs: t.Any, 

2502 ) -> None: ... 

2503 

2504 @t.overload 

2505 def __init__( 

2506 self: Any, 

2507 default_value: t.Any = ..., 

2508 *, 

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

2510 help: str | None = ..., 

2511 read_only: bool | None = False, 

2512 config: t.Any = None, 

2513 **kwargs: t.Any, 

2514 ) -> None: ... 

2515 

2516 def __init__( 

2517 self: Any, 

2518 default_value: t.Any = ..., 

2519 *, 

2520 allow_none: bool = False, 

2521 help: str | None = "", 

2522 read_only: bool | None = False, 

2523 config: t.Any = None, 

2524 **kwargs: t.Any, 

2525 ) -> None: ... 

2526 

2527 @t.overload 

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

2529 

2530 @t.overload 

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

2532 

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

2534 

2535 default_value: t.Any | None = None 

2536 allow_none = True 

2537 info_text = "any value" 

2538 

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

2540 pass # fully opt out of instance_init 

2541 

2542 

2543def _validate_bounds( 

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

2545) -> t.Any: 

2546 """ 

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

2548 

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

2550 TraitError with an error message appropriate for this trait. 

2551 """ 

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

2553 raise TraitError( 

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

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

2556 "specified" 

2557 ) 

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

2559 raise TraitError( 

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

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

2562 "specified" 

2563 ) 

2564 return value 

2565 

2566 

2567# I = t.TypeVar('I', int | None, int) 

2568 

2569 

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

2571 """An integer trait.""" 

2572 

2573 default_value = 0 

2574 info_text = "an int" 

2575 

2576 @t.overload 

2577 def __init__( 

2578 self: Int[int, int], 

2579 default_value: int | Sentinel = ..., 

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

2581 read_only: bool | None = ..., 

2582 help: str | None = ..., 

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

2584 **kwargs: t.Any, 

2585 ) -> None: ... 

2586 

2587 @t.overload 

2588 def __init__( 

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

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

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

2592 read_only: bool | None = ..., 

2593 help: str | None = ..., 

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

2595 **kwargs: t.Any, 

2596 ) -> None: ... 

2597 

2598 def __init__( 

2599 self, 

2600 default_value: t.Any = Undefined, 

2601 allow_none: bool = False, 

2602 read_only: bool | None = None, 

2603 help: str | None = None, 

2604 config: t.Any | None = None, 

2605 **kwargs: t.Any, 

2606 ) -> None: 

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

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

2609 super().__init__( 

2610 default_value=default_value, 

2611 allow_none=allow_none, 

2612 read_only=read_only, 

2613 help=help, 

2614 config=config, 

2615 **kwargs, 

2616 ) 

2617 

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

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

2620 # allow casting integer-valued numbers to int 

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

2622 try: 

2623 int_value = int(value) # type:ignore[call-overload] 

2624 if int_value == value: 

2625 value = int_value 

2626 except Exception: 

2627 pass 

2628 if not isinstance(value, int): 

2629 self.error(obj, value) 

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

2631 

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

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

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

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

2636 

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

2638 pass # fully opt out of instance_init 

2639 

2640 

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

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

2643 

2644 if t.TYPE_CHECKING: 

2645 

2646 @t.overload 

2647 def __init__( 

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

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

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

2651 read_only: bool | None = ..., 

2652 help: str | None = ..., 

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

2654 **kwargs: t.Any, 

2655 ) -> None: ... 

2656 

2657 @t.overload 

2658 def __init__( 

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

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

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

2662 read_only: bool | None = ..., 

2663 help: str | None = ..., 

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

2665 **kwargs: t.Any, 

2666 ) -> None: ... 

2667 

2668 def __init__( 

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

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

2671 allow_none: bool = ..., 

2672 read_only: bool | None = ..., 

2673 help: str | None = ..., 

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

2675 **kwargs: t.Any, 

2676 ) -> None: ... 

2677 

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

2679 try: 

2680 value = int(value) 

2681 except Exception: 

2682 self.error(obj, value) 

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

2684 

2685 

2686Integer = Int 

2687 

2688 

2689class Long(Int[G, S]): 

2690 """A deprecated alias for :class:`Integer`. 

2691 

2692 .. deprecated:: 5.16 

2693 Use :class:`Integer` instead. ``Int`` and ``Long`` used to be distinct 

2694 traits back when Python 2 had separate ``int`` and ``long`` types; they 

2695 are now both just integers. 

2696 """ 

2697 

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

2699 warn( 

2700 "The `Long` trait is deprecated since traitlets 5.16, use `Integer` instead.", 

2701 DeprecationWarning, 

2702 stacklevel=2, 

2703 ) 

2704 super().__init__(*args, **kwargs) # type:ignore[misc] 

2705 

2706 

2707class CLong(CInt[G, S]): 

2708 """A deprecated alias for :class:`CInt`. 

2709 

2710 .. deprecated:: 5.16 

2711 Use :class:`CInt` instead. 

2712 """ 

2713 

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

2715 warn( 

2716 "The `CLong` trait is deprecated since traitlets 5.16, use `CInt` instead.", 

2717 DeprecationWarning, 

2718 stacklevel=2, 

2719 ) 

2720 super().__init__(*args, **kwargs) # type:ignore[misc] 

2721 

2722 

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

2724 """A float trait.""" 

2725 

2726 default_value = 0.0 

2727 info_text = "a float" 

2728 

2729 @t.overload 

2730 def __init__( 

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

2732 default_value: float | Sentinel = ..., 

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

2734 read_only: bool | None = ..., 

2735 help: str | None = ..., 

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

2737 **kwargs: t.Any, 

2738 ) -> None: ... 

2739 

2740 @t.overload 

2741 def __init__( 

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

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

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

2745 read_only: bool | None = ..., 

2746 help: str | None = ..., 

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

2748 **kwargs: t.Any, 

2749 ) -> None: ... 

2750 

2751 def __init__( 

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

2753 default_value: float | Sentinel | None = Undefined, 

2754 allow_none: bool = False, 

2755 read_only: bool | None = False, 

2756 help: str | None = None, 

2757 config: t.Any | None = None, 

2758 **kwargs: t.Any, 

2759 ) -> None: 

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

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

2762 super().__init__( 

2763 default_value=default_value, 

2764 allow_none=allow_none, 

2765 read_only=read_only, 

2766 help=help, 

2767 config=config, 

2768 **kwargs, 

2769 ) 

2770 

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

2772 if isinstance(value, int): 

2773 value = float(value) 

2774 if not isinstance(value, float): 

2775 self.error(obj, value) 

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

2777 

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

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

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

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

2782 

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

2784 pass # fully opt out of instance_init 

2785 

2786 

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

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

2789 

2790 if t.TYPE_CHECKING: 

2791 

2792 @t.overload 

2793 def __init__( 

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

2795 default_value: t.Any = ..., 

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

2797 read_only: bool | None = ..., 

2798 help: str | None = ..., 

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

2800 **kwargs: t.Any, 

2801 ) -> None: ... 

2802 

2803 @t.overload 

2804 def __init__( 

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

2806 default_value: t.Any = ..., 

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

2808 read_only: bool | None = ..., 

2809 help: str | None = ..., 

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

2811 **kwargs: t.Any, 

2812 ) -> None: ... 

2813 

2814 def __init__( 

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

2816 default_value: t.Any = ..., 

2817 allow_none: bool = ..., 

2818 read_only: bool | None = ..., 

2819 help: str | None = ..., 

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

2821 **kwargs: t.Any, 

2822 ) -> None: ... 

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, 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 @t.overload 

2930 def __init__( 

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

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

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

2934 read_only: bool | None = ..., 

2935 help: str | None = ..., 

2936 config: t.Any = ..., 

2937 **kwargs: t.Any, 

2938 ) -> None: ... 

2939 

2940 def __init__( 

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

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

2943 allow_none: bool = ..., 

2944 read_only: bool | None = ..., 

2945 help: str | None = ..., 

2946 config: t.Any = ..., 

2947 **kwargs: t.Any, 

2948 ) -> None: ... 

2949 

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

2951 if isinstance(value, str): 

2952 return value # type:ignore[return-value] 

2953 if isinstance(value, bytes): 

2954 try: 

2955 return value.decode("ascii", "strict") # type:ignore[return-value] 

2956 except UnicodeDecodeError as e: 

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

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

2959 self.error(obj, value) 

2960 

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

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

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

2964 s = os.path.expanduser(s) 

2965 if len(s) >= 2: 

2966 # handle deprecated "1" 

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

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

2969 old_s = s 

2970 s = s[1:-1] 

2971 warn( 

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

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

2974 DeprecationWarning, 

2975 stacklevel=2, 

2976 ) 

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

2978 

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

2980 pass # fully opt out of instance_init 

2981 

2982 

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

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

2985 

2986 if t.TYPE_CHECKING: 

2987 

2988 @t.overload 

2989 def __init__( 

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

2991 default_value: str | Sentinel = ..., 

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

2993 read_only: bool | None = ..., 

2994 help: str | None = ..., 

2995 config: t.Any = ..., 

2996 **kwargs: t.Any, 

2997 ) -> None: ... 

2998 

2999 @t.overload 

3000 def __init__( 

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

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

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

3004 read_only: bool | None = ..., 

3005 help: str | None = ..., 

3006 config: t.Any = ..., 

3007 **kwargs: t.Any, 

3008 ) -> None: ... 

3009 

3010 def __init__( 

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

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

3013 allow_none: bool = ..., 

3014 read_only: bool | None = ..., 

3015 help: str | None = ..., 

3016 config: t.Any = ..., 

3017 **kwargs: t.Any, 

3018 ) -> None: ... 

3019 

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

3021 try: 

3022 return str(value) # type:ignore[return-value] 

3023 except Exception: 

3024 self.error(obj, value) 

3025 

3026 

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

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

3029 

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

3031 

3032 info_text = "a valid object identifier in Python" 

3033 

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

3035 

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

3037 value = self.coerce_str(obj, value) 

3038 

3039 if isinstance(value, str) and value.isidentifier(): 

3040 return value 

3041 self.error(obj, value) 

3042 

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

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

3045 return None 

3046 return s 

3047 

3048 

3049class DottedObjectName(ObjectName): 

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

3051 

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

3053 value = self.coerce_str(obj, value) 

3054 

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

3056 return value 

3057 self.error(obj, value) 

3058 

3059 

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

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

3062 

3063 default_value = False 

3064 info_text = "a boolean" 

3065 

3066 if t.TYPE_CHECKING: 

3067 

3068 @t.overload 

3069 def __init__( 

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

3071 default_value: bool | Sentinel = ..., 

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

3073 read_only: bool | None = ..., 

3074 help: str | None = ..., 

3075 config: t.Any = ..., 

3076 **kwargs: t.Any, 

3077 ) -> None: ... 

3078 

3079 @t.overload 

3080 def __init__( 

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

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

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

3084 read_only: bool | None = ..., 

3085 help: str | None = ..., 

3086 config: t.Any = ..., 

3087 **kwargs: t.Any, 

3088 ) -> None: ... 

3089 

3090 def __init__( 

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

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

3093 allow_none: bool = ..., 

3094 read_only: bool | None = ..., 

3095 help: str | None = ..., 

3096 config: t.Any = ..., 

3097 **kwargs: t.Any, 

3098 ) -> None: ... 

3099 

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

3101 if isinstance(value, bool): 

3102 return value # type:ignore[return-value] 

3103 elif isinstance(value, int): 

3104 if value == 1: 

3105 return True # type:ignore[return-value] 

3106 elif value == 0: 

3107 return False # type:ignore[return-value] 

3108 self.error(obj, value) 

3109 

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

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

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

3113 s = s.lower() 

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

3115 return True # type:ignore[return-value] 

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

3117 return False # type:ignore[return-value] 

3118 else: 

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

3120 

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

3122 pass # fully opt out of instance_init 

3123 

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

3125 """Completion hints for argcomplete""" 

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

3127 if self.allow_none: 

3128 completions.append("None") 

3129 return completions 

3130 

3131 

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

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

3134 

3135 if t.TYPE_CHECKING: 

3136 

3137 @t.overload 

3138 def __init__( 

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

3140 default_value: bool | Sentinel = ..., 

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

3142 read_only: bool | None = ..., 

3143 help: str | None = ..., 

3144 config: t.Any = ..., 

3145 **kwargs: t.Any, 

3146 ) -> None: ... 

3147 

3148 @t.overload 

3149 def __init__( 

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

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

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

3153 read_only: bool | None = ..., 

3154 help: str | None = ..., 

3155 config: t.Any = ..., 

3156 **kwargs: t.Any, 

3157 ) -> None: ... 

3158 

3159 def __init__( 

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

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

3162 allow_none: bool = ..., 

3163 read_only: bool | None = ..., 

3164 help: str | None = ..., 

3165 config: t.Any = ..., 

3166 **kwargs: t.Any, 

3167 ) -> None: ... 

3168 

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

3170 try: 

3171 return bool(value) # type:ignore[return-value] 

3172 except Exception: 

3173 self.error(obj, value) 

3174 

3175 

3176class Enum(TraitType[G, G]): 

3177 """An enum whose value must be in a given sequence.""" 

3178 

3179 if t.TYPE_CHECKING: 

3180 

3181 @t.overload 

3182 def __init__( 

3183 self: Enum[G], 

3184 values: t.Sequence[G], 

3185 default_value: G | Sentinel = ..., 

3186 allow_none: Literal[False] = ..., 

3187 read_only: bool | None = ..., 

3188 help: str | None = ..., 

3189 config: t.Any = ..., 

3190 **kwargs: t.Any, 

3191 ) -> None: ... 

3192 

3193 @t.overload 

3194 def __init__( 

3195 self: Enum[G | None], 

3196 values: t.Sequence[G] | None, 

3197 default_value: G | Sentinel | None = ..., 

3198 allow_none: Literal[True] = ..., 

3199 read_only: bool | None = ..., 

3200 help: str | None = ..., 

3201 config: t.Any = ..., 

3202 **kwargs: t.Any, 

3203 ) -> None: ... 

3204 

3205 def __init__( 

3206 self: Enum[G], 

3207 values: t.Sequence[G] | None, 

3208 default_value: G | Sentinel | None = Undefined, 

3209 allow_none: bool = False, 

3210 read_only: bool | None = None, 

3211 help: str | None = None, 

3212 config: t.Any = None, 

3213 **kwargs: t.Any, 

3214 ) -> None: 

3215 self.values = values 

3216 if allow_none is True and default_value is Undefined: 

3217 default_value = None 

3218 kwargs["allow_none"] = allow_none 

3219 kwargs["read_only"] = read_only 

3220 kwargs["help"] = help 

3221 kwargs["config"] = config 

3222 super().__init__(default_value, **kwargs) 

3223 

3224 def validate(self, obj: t.Any, value: t.Any) -> G: 

3225 if self.values and value in self.values: 

3226 return value # type:ignore[no-any-return] 

3227 self.error(obj, value) 

3228 

3229 def _choices_str(self, as_rst: bool = False) -> str: 

3230 """Returns a description of the trait choices (not none).""" 

3231 choices = self.values or [] 

3232 if as_rst: 

3233 choice_str = "|".join(f"``{x!r}``" for x in choices) 

3234 else: 

3235 choice_str = repr(list(choices)) 

3236 return choice_str 

3237 

3238 def _info(self, as_rst: bool = False) -> str: 

3239 """Returns a description of the trait.""" 

3240 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3241 return f"any of {self._choices_str(as_rst)}{none}" 

3242 

3243 def info(self) -> str: 

3244 return self._info(as_rst=False) 

3245 

3246 def info_rst(self) -> str: 

3247 return self._info(as_rst=True) 

3248 

3249 def from_string(self, s: str) -> G: 

3250 try: 

3251 return self.validate(None, s) 

3252 except TraitError: 

3253 return _safe_literal_eval(s) # type:ignore[no-any-return] 

3254 

3255 def subclass_init(self, cls: type[t.Any]) -> None: 

3256 pass # fully opt out of instance_init 

3257 

3258 def argcompleter(self, **kwargs: t.Any) -> list[str]: 

3259 """Completion hints for argcomplete""" 

3260 return [str(v) for v in self.values or []] 

3261 

3262 

3263class CaselessStrEnum(Enum[G]): 

3264 """An enum of strings where the case should be ignored.""" 

3265 

3266 def __init__( 

3267 self: CaselessStrEnum[t.Any], 

3268 values: t.Any, 

3269 default_value: t.Any = Undefined, 

3270 **kwargs: t.Any, 

3271 ) -> None: 

3272 super().__init__(values, default_value=default_value, **kwargs) 

3273 

3274 def validate(self, obj: t.Any, value: t.Any) -> G: 

3275 if not isinstance(value, str): 

3276 self.error(obj, value) 

3277 

3278 for v in self.values or []: 

3279 assert isinstance(v, str) 

3280 if v.lower() == value.lower(): 

3281 return v 

3282 self.error(obj, value) 

3283 

3284 def _info(self, as_rst: bool = False) -> str: 

3285 """Returns a description of the trait.""" 

3286 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3287 return f"any of {self._choices_str(as_rst)} (case-insensitive){none}" 

3288 

3289 def info(self) -> str: 

3290 return self._info(as_rst=False) 

3291 

3292 def info_rst(self) -> str: 

3293 return self._info(as_rst=True) 

3294 

3295 

3296class FuzzyEnum(Enum[G]): 

3297 """An case-ignoring enum matching choices by unique prefixes/substrings.""" 

3298 

3299 case_sensitive = False 

3300 #: If True, choices match anywhere in the string, otherwise match prefixes. 

3301 substring_matching = False 

3302 

3303 def __init__( 

3304 self: FuzzyEnum[t.Any], 

3305 values: t.Any, 

3306 default_value: t.Any = Undefined, 

3307 case_sensitive: bool = False, 

3308 substring_matching: bool = False, 

3309 **kwargs: t.Any, 

3310 ) -> None: 

3311 self.case_sensitive = case_sensitive 

3312 self.substring_matching = substring_matching 

3313 super().__init__(values, default_value=default_value, **kwargs) 

3314 

3315 def validate(self, obj: t.Any, value: t.Any) -> G: 

3316 if not isinstance(value, str): 

3317 self.error(obj, value) 

3318 

3319 conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower() 

3320 substring_matching = self.substring_matching 

3321 match_func = (lambda v, c: v in c) if substring_matching else (lambda v, c: c.startswith(v)) 

3322 value = conv_func(value) # type:ignore[no-untyped-call] 

3323 choices = self.values or [] 

3324 matches = [match_func(value, conv_func(c)) for c in choices] # type:ignore[no-untyped-call] 

3325 if sum(matches) == 1: 

3326 for v, m in zip(choices, matches, strict=True): 

3327 if m: 

3328 return v 

3329 

3330 self.error(obj, value) 

3331 

3332 def _info(self, as_rst: bool = False) -> str: 

3333 """Returns a description of the trait.""" 

3334 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

3335 case = "sensitive" if self.case_sensitive else "insensitive" 

3336 substr = "substring" if self.substring_matching else "prefix" 

3337 return f"any case-{case} {substr} of {self._choices_str(as_rst)}{none}" 

3338 

3339 def info(self) -> str: 

3340 return self._info(as_rst=False) 

3341 

3342 def info_rst(self) -> str: 

3343 return self._info(as_rst=True) 

3344 

3345 

3346class Container(Instance[T]): 

3347 """An instance of a container (list, set, etc.) 

3348 

3349 To be subclassed by overriding klass. 

3350 """ 

3351 

3352 klass: type[T] | None = None 

3353 _cast_types: t.Any = () 

3354 _valid_defaults = SequenceTypes 

3355 _trait: t.Any = None 

3356 _literal_from_string_pairs: t.Any = ("[]", "()") 

3357 

3358 @t.overload 

3359 def __init__( 

3360 self: Container[T], 

3361 *, 

3362 allow_none: Literal[False], 

3363 read_only: bool | None = ..., 

3364 help: str | None = ..., 

3365 config: t.Any | None = ..., 

3366 **kwargs: t.Any, 

3367 ) -> None: ... 

3368 

3369 @t.overload 

3370 def __init__( 

3371 self: Container[T | None], 

3372 *, 

3373 allow_none: Literal[True], 

3374 read_only: bool | None = ..., 

3375 help: str | None = ..., 

3376 config: t.Any | None = ..., 

3377 **kwargs: t.Any, 

3378 ) -> None: ... 

3379 

3380 @t.overload 

3381 def __init__( 

3382 self: Container[T], 

3383 *, 

3384 trait: t.Any = ..., 

3385 default_value: t.Any = ..., 

3386 help: str = ..., 

3387 read_only: bool = ..., 

3388 config: t.Any = ..., 

3389 **kwargs: t.Any, 

3390 ) -> None: ... 

3391 

3392 def __init__( 

3393 self, 

3394 trait: t.Any | None = None, 

3395 default_value: t.Any = Undefined, 

3396 help: str | None = None, 

3397 read_only: bool | None = None, 

3398 config: t.Any | None = None, 

3399 **kwargs: t.Any, 

3400 ) -> None: 

3401 """Create a container trait type from a list, set, or tuple. 

3402 

3403 The default value is created by doing ``List(default_value)``, 

3404 which creates a copy of the ``default_value``. 

3405 

3406 ``trait`` can be specified, which restricts the type of elements 

3407 in the container to that TraitType. 

3408 

3409 If only one arg is given and it is not a Trait, it is taken as 

3410 ``default_value``: 

3411 

3412 ``c = List([1, 2, 3])`` 

3413 

3414 Parameters 

3415 ---------- 

3416 trait : TraitType [ optional ] 

3417 the type for restricting the contents of the Container. If unspecified, 

3418 types are not checked. 

3419 default_value : SequenceType [ optional ] 

3420 The default value for the Trait. Must be list/tuple/set, and 

3421 will be cast to the container type. 

3422 allow_none : bool [ default False ] 

3423 Whether to allow the value to be None 

3424 **kwargs : any 

3425 further keys for extensions to the Trait (e.g. config) 

3426 

3427 """ 

3428 

3429 # allow List([values]): 

3430 if trait is not None and default_value is Undefined and not is_trait(trait): 

3431 default_value = trait 

3432 trait = None 

3433 

3434 if default_value is None and not kwargs.get("allow_none", False): 

3435 # improve backward-compatibility for possible subclasses 

3436 # specifying default_value=None as default, 

3437 # keeping 'unspecified' behavior (i.e. empty container) 

3438 warn( 

3439 f"Specifying {self.__class__.__name__}(default_value=None)" 

3440 " for no default is deprecated in traitlets 5.0.5." 

3441 " Use default_value=Undefined", 

3442 DeprecationWarning, 

3443 stacklevel=2, 

3444 ) 

3445 default_value = Undefined 

3446 

3447 if default_value is Undefined: 

3448 args: t.Any = () 

3449 elif default_value is None: 

3450 # default_value back on kwargs for super() to handle 

3451 args = () 

3452 kwargs["default_value"] = None 

3453 elif isinstance(default_value, self._valid_defaults): 

3454 args = (default_value,) 

3455 else: 

3456 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3457 

3458 if is_trait(trait): 

3459 if isinstance(trait, type): 

3460 warn( 

3461 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)." 

3462 " Passing types is deprecated in traitlets 4.1.", 

3463 DeprecationWarning, 

3464 stacklevel=3, 

3465 ) 

3466 self._trait = trait() if isinstance(trait, type) else trait 

3467 elif trait is not None: 

3468 raise TypeError(f"`trait` must be a Trait or None, got {repr_type(trait)}") 

3469 

3470 super().__init__( 

3471 klass=self.klass, args=args, help=help, read_only=read_only, config=config, **kwargs 

3472 ) 

3473 

3474 def validate(self, obj: t.Any, value: t.Any) -> T | None: 

3475 if isinstance(value, self._cast_types): 

3476 assert self.klass is not None 

3477 value = self.klass(value) # type:ignore[call-arg] 

3478 value = super().validate(obj, value) 

3479 if value is None: 

3480 return value 

3481 

3482 return self.validate_elements(obj, value) 

3483 

3484 def validate_elements(self, obj: t.Any, value: t.Any) -> T | None: 

3485 validated = [] 

3486 if self._trait is None or isinstance(self._trait, Any): 

3487 return value # type:ignore[no-any-return] 

3488 for v in value: 

3489 try: 

3490 v = self._trait._validate(obj, v) 

3491 except TraitError as error: 

3492 self.error(obj, v, error) 

3493 else: 

3494 validated.append(v) 

3495 assert self.klass is not None 

3496 return self.klass(validated) # type:ignore[call-arg] 

3497 

3498 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3499 if isinstance(self._trait, TraitType): 

3500 self._trait.class_init(cls, None) 

3501 super().class_init(cls, name) 

3502 

3503 def subclass_init(self, cls: type[t.Any]) -> None: 

3504 if isinstance(self._trait, TraitType): 

3505 self._trait.subclass_init(cls) 

3506 # explicitly not calling super().subclass_init(cls) 

3507 # to opt out of instance_init 

3508 

3509 def from_string(self, s: str) -> T | None: 

3510 """Load value from a single string""" 

3511 if not isinstance(s, str): 

3512 raise TraitError(f"Expected string, got {s!r}") 

3513 try: 

3514 test = literal_eval(s) 

3515 except Exception: 

3516 test = None 

3517 return self.validate(None, test) 

3518 

3519 def from_string_list(self, s_list: list[str]) -> T | None: 

3520 """Return the value from a list of config strings 

3521 

3522 This is where we parse CLI configuration 

3523 """ 

3524 assert self.klass is not None 

3525 if len(s_list) == 1: 

3526 # check for deprecated --Class.trait="['a', 'b', 'c']" 

3527 r = s_list[0] 

3528 if r == "None" and self.allow_none: 

3529 return None 

3530 if len(r) >= 2 and any( 

3531 r.startswith(start) and r.endswith(end) 

3532 for start, end in self._literal_from_string_pairs 

3533 ): 

3534 if self.this_class: 

3535 clsname = self.this_class.__name__ + "." 

3536 else: 

3537 clsname = "" 

3538 assert self.name is not None 

3539 warn( 

3540 f"--{clsname + self.name}={r} for containers is deprecated in traitlets 5.0. " 

3541 f"You can pass `--{clsname + self.name} item` ... multiple times to add items to a list.", 

3542 DeprecationWarning, 

3543 stacklevel=2, 

3544 ) 

3545 return self.klass(literal_eval(r)) # type:ignore[call-arg] 

3546 sig = inspect.signature(self.item_from_string) 

3547 if "index" in sig.parameters: 

3548 item_from_string = self.item_from_string 

3549 else: 

3550 # backward-compat: allow item_from_string to ignore index arg 

3551 def item_from_string(s: str, index: int | None = None) -> T | str: 

3552 return self.item_from_string(s) 

3553 

3554 return self.klass( # type:ignore[call-arg] 

3555 [item_from_string(s, index=idx) for idx, s in enumerate(s_list)] 

3556 ) 

3557 

3558 def item_from_string(self, s: str, index: int | None = None) -> T | str: 

3559 """Cast a single item from a string 

3560 

3561 Evaluated when parsing CLI configuration from a string 

3562 """ 

3563 if self._trait: 

3564 return self._trait.from_string(s) # type:ignore[no-any-return] 

3565 else: 

3566 return s 

3567 

3568 

3569class List(Container[list[T]]): 

3570 """An instance of a Python list.""" 

3571 

3572 klass = list # type:ignore[assignment] 

3573 _cast_types: t.Any = (tuple,) 

3574 

3575 def __init__( 

3576 self, 

3577 trait: list[T] | tuple[T] | set[T] | Sentinel | TraitType[T, t.Any] | None = None, 

3578 default_value: list[T] | tuple[T] | set[T] | Sentinel | None = Undefined, 

3579 minlen: int = 0, 

3580 maxlen: int = sys.maxsize, 

3581 **kwargs: t.Any, 

3582 ) -> None: 

3583 """Create a List trait type from a list, set, or tuple. 

3584 

3585 The default value is created by doing ``list(default_value)``, 

3586 which creates a copy of the ``default_value``. 

3587 

3588 ``trait`` can be specified, which restricts the type of elements 

3589 in the container to that TraitType. 

3590 

3591 If only one arg is given and it is not a Trait, it is taken as 

3592 ``default_value``: 

3593 

3594 ``c = List([1, 2, 3])`` 

3595 

3596 Parameters 

3597 ---------- 

3598 trait : TraitType [ optional ] 

3599 the type for restricting the contents of the Container. 

3600 If unspecified, types are not checked. 

3601 default_value : SequenceType [ optional ] 

3602 The default value for the Trait. Must be list/tuple/set, and 

3603 will be cast to the container type. 

3604 minlen : Int [ default 0 ] 

3605 The minimum length of the input list 

3606 maxlen : Int [ default sys.maxsize ] 

3607 The maximum length of the input list 

3608 """ 

3609 self._maxlen = maxlen 

3610 self._minlen = minlen 

3611 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3612 

3613 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3614 e = ( 

3615 f"The '{self.name}' trait of {class_of(obj)} instance must be of length" 

3616 f" {self._minlen:d} <= L <= {self._maxlen:d}, but a value of {value} was specified." 

3617 ) 

3618 raise TraitError(e) 

3619 

3620 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3621 length = len(value) 

3622 if length < self._minlen or length > self._maxlen: 

3623 self.length_error(obj, value) 

3624 

3625 return super().validate_elements(obj, value) 

3626 

3627 def set(self, obj: t.Any, value: t.Any) -> None: 

3628 if isinstance(value, str): 

3629 return super().set(obj, [value]) # type:ignore[list-item] 

3630 else: 

3631 return super().set(obj, value) 

3632 

3633 

3634class Set(Container[set[t.Any]]): 

3635 """An instance of a Python set.""" 

3636 

3637 klass = set 

3638 _cast_types = (tuple, list) 

3639 

3640 _literal_from_string_pairs = ("[]", "()", "{}") 

3641 

3642 # Redefine __init__ just to make the docstring more accurate. 

3643 def __init__( 

3644 self, 

3645 trait: t.Any = None, 

3646 default_value: t.Any = Undefined, 

3647 minlen: int = 0, 

3648 maxlen: int = sys.maxsize, 

3649 **kwargs: t.Any, 

3650 ) -> None: 

3651 """Create a Set trait type from a list, set, or tuple. 

3652 

3653 The default value is created by doing ``set(default_value)``, 

3654 which creates a copy of the ``default_value``. 

3655 

3656 ``trait`` can be specified, which restricts the type of elements 

3657 in the container to that TraitType. 

3658 

3659 If only one arg is given and it is not a Trait, it is taken as 

3660 ``default_value``: 

3661 

3662 ``c = Set({1, 2, 3})`` 

3663 

3664 Parameters 

3665 ---------- 

3666 trait : TraitType [ optional ] 

3667 the type for restricting the contents of the Container. 

3668 If unspecified, types are not checked. 

3669 default_value : SequenceType [ optional ] 

3670 The default value for the Trait. Must be list/tuple/set, and 

3671 will be cast to the container type. 

3672 minlen : Int [ default 0 ] 

3673 The minimum length of the input list 

3674 maxlen : Int [ default sys.maxsize ] 

3675 The maximum length of the input list 

3676 """ 

3677 self._maxlen = maxlen 

3678 self._minlen = minlen 

3679 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3680 

3681 def length_error(self, obj: t.Any, value: t.Any) -> None: 

3682 e = ( 

3683 f"The '{self.name}' trait of {class_of(obj)} instance must be of length" 

3684 f" {self._minlen:d} <= L <= {self._maxlen:d}, but a value of {value} was specified." 

3685 ) 

3686 raise TraitError(e) 

3687 

3688 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3689 length = len(value) 

3690 if length < self._minlen or length > self._maxlen: 

3691 self.length_error(obj, value) 

3692 

3693 return super().validate_elements(obj, value) 

3694 

3695 def set(self, obj: t.Any, value: t.Any) -> None: 

3696 if isinstance(value, str): 

3697 return super().set(obj, {value}) 

3698 else: 

3699 return super().set(obj, value) 

3700 

3701 def default_value_repr(self) -> str: 

3702 # Ensure default value is sorted for a reproducible build 

3703 list_repr = repr(sorted(self.make_dynamic_default() or [])) 

3704 if list_repr == "[]": 

3705 return "set()" 

3706 return "{" + list_repr[1:-1] + "}" 

3707 

3708 

3709class Tuple(Container[tuple[t.Any, ...]]): 

3710 """An instance of a Python tuple.""" 

3711 

3712 klass = tuple 

3713 _cast_types = (list,) 

3714 

3715 def __init__(self, *traits: t.Any, **kwargs: t.Any) -> None: 

3716 """Create a tuple from a list, set, or tuple. 

3717 

3718 Create a fixed-type tuple with Traits: 

3719 

3720 ``t = Tuple(Int(), Str(), CStr())`` 

3721 

3722 would be length 3, with Int,Str,CStr for each element. 

3723 

3724 If only one arg is given and it is not a Trait, it is taken as 

3725 default_value: 

3726 

3727 ``t = Tuple((1, 2, 3))`` 

3728 

3729 Otherwise, ``default_value`` *must* be specified by keyword. 

3730 

3731 Parameters 

3732 ---------- 

3733 *traits : TraitTypes [ optional ] 

3734 the types for restricting the contents of the Tuple. If unspecified, 

3735 types are not checked. If specified, then each positional argument 

3736 corresponds to an element of the tuple. Tuples defined with traits 

3737 are of fixed length. 

3738 default_value : SequenceType [ optional ] 

3739 The default value for the Tuple. Must be list/tuple/set, and 

3740 will be cast to a tuple. If ``traits`` are specified, 

3741 ``default_value`` must conform to the shape and type they specify. 

3742 **kwargs 

3743 Other kwargs passed to `Container` 

3744 """ 

3745 default_value = kwargs.pop("default_value", Undefined) 

3746 # allow Tuple((values,)): 

3747 if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]): 

3748 default_value = traits[0] 

3749 traits = () 

3750 

3751 if default_value is None and not kwargs.get("allow_none", False): 

3752 # improve backward-compatibility for possible subclasses 

3753 # specifying default_value=None as default, 

3754 # keeping 'unspecified' behavior (i.e. empty container) 

3755 warn( 

3756 f"Specifying {self.__class__.__name__}(default_value=None)" 

3757 " for no default is deprecated in traitlets 5.0.5." 

3758 " Use default_value=Undefined", 

3759 DeprecationWarning, 

3760 stacklevel=2, 

3761 ) 

3762 default_value = Undefined 

3763 

3764 if default_value is Undefined: 

3765 args: t.Any = () 

3766 elif default_value is None: 

3767 # default_value back on kwargs for super() to handle 

3768 args = () 

3769 kwargs["default_value"] = None 

3770 elif isinstance(default_value, self._valid_defaults): 

3771 args = (default_value,) 

3772 else: 

3773 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3774 

3775 self._traits = [] 

3776 for trait in traits: 

3777 if isinstance(trait, type): 

3778 warn( 

3779 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3780 " Passing types is deprecated in traitlets 4.1.", 

3781 DeprecationWarning, 

3782 stacklevel=2, 

3783 ) 

3784 trait = trait() 

3785 self._traits.append(trait) 

3786 

3787 if self._traits and (default_value is None or default_value is Undefined): 

3788 # don't allow default to be an empty container if length is specified 

3789 args = None 

3790 super(Container, self).__init__(klass=self.klass, args=args, **kwargs) 

3791 

3792 def item_from_string(self, s: str, index: int) -> t.Any: # type:ignore[override] 

3793 """Cast a single item from a string 

3794 

3795 Evaluated when parsing CLI configuration from a string 

3796 """ 

3797 if not self._traits or index >= len(self._traits): 

3798 # return s instead of raising index error 

3799 # length errors will be raised later on validation 

3800 return s 

3801 return self._traits[index].from_string(s) 

3802 

3803 def validate_elements(self, obj: t.Any, value: t.Any) -> t.Any: 

3804 if not self._traits: 

3805 # nothing to validate 

3806 return value 

3807 if len(value) != len(self._traits): 

3808 e = ( 

3809 f"The '{self.name}' trait of {class_of(obj)} instance requires" 

3810 f" {len(self._traits):d} elements, but a value of {repr_type(value)} was specified." 

3811 ) 

3812 raise TraitError(e) 

3813 

3814 validated = [] 

3815 for trait, v in zip(self._traits, value, strict=True): 

3816 try: 

3817 v = trait._validate(obj, v) 

3818 except TraitError as error: 

3819 self.error(obj, v, error) 

3820 else: 

3821 validated.append(v) 

3822 return tuple(validated) 

3823 

3824 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

3825 for trait in self._traits: 

3826 if isinstance(trait, TraitType): 

3827 trait.class_init(cls, None) 

3828 super(Container, self).class_init(cls, name) 

3829 

3830 def subclass_init(self, cls: type[t.Any]) -> None: 

3831 for trait in self._traits: 

3832 if isinstance(trait, TraitType): 

3833 trait.subclass_init(cls) 

3834 # explicitly not calling super().subclass_init(cls) 

3835 # to opt out of instance_init 

3836 

3837 

3838class Dict(Instance["dict[K, V]"]): 

3839 """An instance of a Python dict. 

3840 

3841 One or more traits can be passed to the constructor 

3842 to validate the keys and/or values of the dict. 

3843 If you need more detailed validation, 

3844 you may use a custom validator method. 

3845 

3846 .. versionchanged:: 5.0 

3847 Added key_trait for validating dict keys. 

3848 

3849 .. versionchanged:: 5.0 

3850 Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``. 

3851 """ 

3852 

3853 _value_trait = None 

3854 _key_trait = None 

3855 

3856 def __init__( 

3857 self, 

3858 value_trait: TraitType[t.Any, t.Any] | dict[K, V] | Sentinel | None = None, 

3859 per_key_traits: t.Any = None, 

3860 key_trait: TraitType[t.Any, t.Any] | None = None, 

3861 default_value: dict[K, V] | Sentinel | None = Undefined, 

3862 **kwargs: t.Any, 

3863 ) -> None: 

3864 """Create a dict trait type from a Python dict. 

3865 

3866 The default value is created by doing ``dict(default_value)``, 

3867 which creates a copy of the ``default_value``. 

3868 

3869 Parameters 

3870 ---------- 

3871 value_trait : TraitType [ optional ] 

3872 The specified trait type to check and use to restrict the values of 

3873 the dict. If unspecified, values are not checked. 

3874 per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ] 

3875 A Python dictionary containing the types that are valid for 

3876 restricting the values of the dict on a per-key basis. 

3877 Each value in this dict should be a Trait for validating 

3878 key_trait : TraitType [ optional, keyword-only ] 

3879 The type for restricting the keys of the dict. If 

3880 unspecified, the types of the keys are not checked. 

3881 default_value : SequenceType [ optional, keyword-only ] 

3882 The default value for the Dict. Must be dict, tuple, or None, and 

3883 will be cast to a dict if not None. If any key or value traits are specified, 

3884 the `default_value` must conform to the constraints. 

3885 

3886 Examples 

3887 -------- 

3888 a dict whose values must be text 

3889 >>> d = Dict(Unicode()) 

3890 

3891 d2['n'] must be an integer 

3892 d2['s'] must be text 

3893 >>> d2 = Dict(per_key_traits={"n": Int(), "s": Unicode()}) 

3894 

3895 d3's keys must be text 

3896 d3's values must be integers 

3897 >>> d3 = Dict(value_trait=Int(), key_trait=Unicode()) 

3898 

3899 """ 

3900 

3901 # handle deprecated keywords 

3902 trait = kwargs.pop("trait", None) 

3903 if trait is not None: 

3904 if value_trait is not None: 

3905 raise TypeError( 

3906 "Found a value for both `value_trait` and its deprecated alias `trait`." 

3907 ) 

3908 value_trait = trait 

3909 warn( 

3910 "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead", 

3911 DeprecationWarning, 

3912 stacklevel=2, 

3913 ) 

3914 traits = kwargs.pop("traits", None) 

3915 if traits is not None: 

3916 if per_key_traits is not None: 

3917 raise TypeError( 

3918 "Found a value for both `per_key_traits` and its deprecated alias `traits`." 

3919 ) 

3920 per_key_traits = traits 

3921 warn( 

3922 "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead", 

3923 DeprecationWarning, 

3924 stacklevel=2, 

3925 ) 

3926 

3927 # Handling positional arguments 

3928 if default_value is Undefined and value_trait is not None: 

3929 if not is_trait(value_trait): 

3930 assert not isinstance(value_trait, TraitType) 

3931 default_value = value_trait 

3932 value_trait = None 

3933 

3934 if key_trait is None and per_key_traits is not None: 

3935 if is_trait(per_key_traits): 

3936 key_trait = per_key_traits 

3937 per_key_traits = None 

3938 

3939 # Handling default value 

3940 if default_value is Undefined: 

3941 default_value = {} 

3942 if default_value is None: 

3943 args: t.Any = None 

3944 elif isinstance(default_value, dict): 

3945 args = (default_value,) 

3946 elif isinstance(default_value, SequenceTypes): 

3947 args = (default_value,) 

3948 else: 

3949 raise TypeError(f"default value of Dict was {default_value}") 

3950 

3951 # Case where a type of TraitType is provided rather than an instance 

3952 if is_trait(value_trait): 

3953 if isinstance(value_trait, type): 

3954 warn( # type:ignore[unreachable] 

3955 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3956 " Passing types is deprecated in traitlets 4.1.", 

3957 DeprecationWarning, 

3958 stacklevel=2, 

3959 ) 

3960 value_trait = value_trait() 

3961 self._value_trait = value_trait 

3962 elif value_trait is not None: 

3963 raise TypeError(f"`value_trait` must be a Trait or None, got {repr_type(value_trait)}") 

3964 

3965 if is_trait(key_trait): 

3966 if isinstance(key_trait, type): 

3967 warn( # type:ignore[unreachable] 

3968 "Traits should be given as instances, not types (for example, `Int()`, not `Int`)" 

3969 " Passing types is deprecated in traitlets 4.1.", 

3970 DeprecationWarning, 

3971 stacklevel=2, 

3972 ) 

3973 key_trait = key_trait() 

3974 self._key_trait = key_trait 

3975 elif key_trait is not None: 

3976 raise TypeError(f"`key_trait` must be a Trait or None, got {repr_type(key_trait)}") 

3977 

3978 self._per_key_traits = per_key_traits 

3979 

3980 super().__init__(klass=dict, args=args, **kwargs) 

3981 

3982 def element_error( 

3983 self, obj: t.Any, element: t.Any, validator: t.Any, side: str = "Values" 

3984 ) -> None: 

3985 e = ( 

3986 side 

3987 + f" of the '{self.name}' trait of {class_of(obj)} instance must be {validator.info()}, but a value of {repr_type(element)} was specified." 

3988 ) 

3989 raise TraitError(e) 

3990 

3991 def validate(self, obj: t.Any, value: t.Any) -> dict[K, V] | None: 

3992 value = super().validate(obj, value) 

3993 if value is None: 

3994 return value 

3995 return self.validate_elements(obj, value) 

3996 

3997 def validate_elements(self, obj: t.Any, value: dict[t.Any, t.Any]) -> dict[K, V] | None: 

3998 per_key_override = self._per_key_traits or {} 

3999 key_trait = self._key_trait 

4000 value_trait = self._value_trait 

4001 if not (key_trait or value_trait or per_key_override): 

4002 return value 

4003 

4004 validated = {} 

4005 for key, v in value.items(): 

4006 if key_trait: 

4007 try: 

4008 key = key_trait._validate(obj, key) 

4009 except TraitError: 

4010 self.element_error(obj, key, key_trait, "Keys") 

4011 active_value_trait = per_key_override.get(key, value_trait) 

4012 if active_value_trait: 

4013 try: 

4014 v = active_value_trait._validate(obj, v) 

4015 except TraitError: 

4016 self.element_error(obj, v, active_value_trait, "Values") 

4017 validated[key] = v 

4018 

4019 return self.klass(validated) # type:ignore[misc,operator] 

4020 

4021 def class_init(self, cls: type[t.Any], name: str | None) -> None: 

4022 if isinstance(self._value_trait, TraitType): 

4023 self._value_trait.class_init(cls, None) 

4024 if isinstance(self._key_trait, TraitType): 

4025 self._key_trait.class_init(cls, None) 

4026 if self._per_key_traits is not None: 

4027 for trait in self._per_key_traits.values(): 

4028 trait.class_init(cls, None) 

4029 super().class_init(cls, name) 

4030 

4031 def subclass_init(self, cls: type[t.Any]) -> None: 

4032 if isinstance(self._value_trait, TraitType): 

4033 self._value_trait.subclass_init(cls) 

4034 if isinstance(self._key_trait, TraitType): 

4035 self._key_trait.subclass_init(cls) 

4036 if self._per_key_traits is not None: 

4037 for trait in self._per_key_traits.values(): 

4038 trait.subclass_init(cls) 

4039 # explicitly not calling super().subclass_init(cls) 

4040 # to opt out of instance_init 

4041 

4042 def from_string(self, s: str) -> dict[K, V] | None: 

4043 """Load value from a single string""" 

4044 if not isinstance(s, str): 

4045 raise TypeError(f"from_string expects a string, got {s!r} of type {type(s)}") 

4046 try: 

4047 return self.from_string_list([s]) # type:ignore[no-any-return] 

4048 except Exception: 

4049 test = _safe_literal_eval(s) 

4050 if isinstance(test, dict): 

4051 return test 

4052 raise 

4053 

4054 def from_string_list(self, s_list: list[str]) -> t.Any: 

4055 """Return a dict from a list of config strings. 

4056 

4057 This is where we parse CLI configuration. 

4058 

4059 Each item should have the form ``"key=value"``. 

4060 

4061 item parsing is done in :meth:`.item_from_string`. 

4062 """ 

4063 if len(s_list) == 1 and s_list[0] == "None" and self.allow_none: 

4064 return None 

4065 if len(s_list) == 1 and s_list[0].startswith("{") and s_list[0].endswith("}"): 

4066 warn( 

4067 f"--{self.name}={s_list[0]} for dict-traits is deprecated in traitlets 5.0. " 

4068 f"You can pass --{self.name} <key=value> ... multiple times to add items to a dict.", 

4069 DeprecationWarning, 

4070 stacklevel=2, 

4071 ) 

4072 

4073 return literal_eval(s_list[0]) 

4074 

4075 combined = {} 

4076 for d in [self.item_from_string(s) for s in s_list]: 

4077 combined.update(d) 

4078 return combined 

4079 

4080 def item_from_string(self, s: str) -> dict[K, V]: 

4081 """Cast a single-key dict from a string. 

4082 

4083 Evaluated when parsing CLI configuration from a string. 

4084 

4085 Dicts expect strings of the form key=value. 

4086 

4087 Returns a one-key dictionary, 

4088 which will be merged in :meth:`.from_string_list`. 

4089 """ 

4090 

4091 if "=" not in s: 

4092 raise TraitError( 

4093 f"'{self.__class__.__name__}' options must have the form 'key=value', got {s!r}" 

4094 ) 

4095 key, value = s.split("=", 1) 

4096 

4097 # cast key with key trait, if defined 

4098 if self._key_trait: 

4099 key = self._key_trait.from_string(key) 

4100 

4101 # cast value with value trait, if defined (per-key or global) 

4102 value_trait = (self._per_key_traits or {}).get(key, self._value_trait) 

4103 if value_trait: 

4104 value = value_trait.from_string(value) 

4105 return {key: value} # type:ignore[dict-item] 

4106 

4107 

4108class TCPAddress(TraitType[G, S]): 

4109 """A trait for an (ip, port) tuple. 

4110 

4111 This allows for both IPv4 IP addresses as well as hostnames. 

4112 """ 

4113 

4114 default_value = ("127.0.0.1", 0) 

4115 info_text = "an (ip, port) tuple" 

4116 

4117 if t.TYPE_CHECKING: 

4118 

4119 @t.overload 

4120 def __init__( 

4121 self: TCPAddress[tuple[str, int], tuple[str, int]], 

4122 default_value: bool | Sentinel = ..., 

4123 allow_none: Literal[False] = ..., 

4124 read_only: bool | None = ..., 

4125 help: str | None = ..., 

4126 config: t.Any = ..., 

4127 **kwargs: t.Any, 

4128 ) -> None: ... 

4129 

4130 @t.overload 

4131 def __init__( 

4132 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None], 

4133 default_value: bool | None | Sentinel = ..., 

4134 allow_none: Literal[True] = ..., 

4135 read_only: bool | None = ..., 

4136 help: str | None = ..., 

4137 config: t.Any = ..., 

4138 **kwargs: t.Any, 

4139 ) -> None: ... 

4140 

4141 def __init__( 

4142 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None] 

4143 | TCPAddress[tuple[str, int], tuple[str, int]], 

4144 default_value: bool | None | Sentinel = Undefined, 

4145 allow_none: Literal[True, False] = False, 

4146 read_only: bool | None = None, 

4147 help: str | None = None, 

4148 config: t.Any = None, 

4149 **kwargs: t.Any, 

4150 ) -> None: ... 

4151 

4152 def validate(self, obj: t.Any, value: t.Any) -> G: 

4153 if isinstance(value, tuple): 

4154 if len(value) == 2: 

4155 if isinstance(value[0], str) and isinstance(value[1], int): 

4156 port = value[1] 

4157 if port >= 0 and port <= 65535: 

4158 return value # type:ignore[return-value] 

4159 self.error(obj, value) 

4160 

4161 def from_string(self, s: str) -> G: 

4162 if self.allow_none and s == "None": 

4163 return None # type:ignore[return-value] 

4164 if ":" not in s: 

4165 raise ValueError(f"Require `ip:port`, got {s!r}") 

4166 ip, port_str = s.split(":", 1) 

4167 port = int(port_str) 

4168 return (ip, port) # type:ignore[return-value] 

4169 

4170 

4171class CRegExp(TraitType[re.Pattern[t.Any], re.Pattern[t.Any] | str]): 

4172 """A casting compiled regular expression trait. 

4173 

4174 Accepts both strings and compiled regular expressions. The resulting 

4175 attribute will be a compiled regular expression.""" 

4176 

4177 info_text = "a regular expression" 

4178 

4179 def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None: 

4180 try: 

4181 return re.compile(value) 

4182 except Exception: 

4183 self.error(obj, value) 

4184 

4185 

4186class Path(TraitType["pathlib.Path", t.Union["pathlib.Path", str, "os.PathLike[str]"]]): 

4187 """A trait for filesystem paths. 

4188 

4189 Accepts strings and :class:`os.PathLike` objects. The resulting 

4190 attribute will be a :class:`pathlib.Path` instance.""" 

4191 

4192 info_text = "a filesystem path" 

4193 

4194 def validate(self, obj: t.Any, value: t.Any) -> pathlib.Path | None: 

4195 if isinstance(value, (str, os.PathLike)): 

4196 return pathlib.Path(value) 

4197 self.error(obj, value) 

4198 

4199 def from_string(self, s: str) -> pathlib.Path | None: 

4200 if self.allow_none and s == "None": 

4201 return None 

4202 return pathlib.Path(s) 

4203 

4204 

4205class UseEnum(TraitType[t.Any, t.Any]): 

4206 """Use a Enum class as model for the data type description. 

4207 Note that if no default-value is provided, the first enum-value is used 

4208 as default-value. 

4209 

4210 .. sourcecode:: python 

4211 

4212 import enum 

4213 from traitlets import HasTraits, UseEnum 

4214 

4215 

4216 class Color(enum.Enum): 

4217 red = 1 # -- IMPLICIT: default_value 

4218 blue = 2 

4219 green = 3 

4220 

4221 

4222 class MyEntity(HasTraits): 

4223 color = UseEnum(Color, default_value=Color.blue) 

4224 

4225 

4226 entity = MyEntity(color=Color.red) 

4227 entity.color = Color.green # USE: Enum-value (preferred) 

4228 entity.color = "green" # USE: name (as string) 

4229 entity.color = "Color.green" # USE: scoped-name (as string) 

4230 entity.color = 3 # USE: number (as int) 

4231 assert entity.color is Color.green 

4232 """ 

4233 

4234 default_value: enum.Enum | None = None 

4235 info_text = "Trait type adapter to a Enum class" 

4236 

4237 def __init__( 

4238 self, enum_class: type[t.Any], default_value: t.Any = None, **kwargs: t.Any 

4239 ) -> None: 

4240 assert issubclass(enum_class, enum.Enum), f"REQUIRE: enum.Enum, but was: {enum_class!r}" 

4241 allow_none = kwargs.get("allow_none", False) 

4242 if default_value is None and not allow_none: 

4243 default_value = next(iter(enum_class.__members__.values())) 

4244 super().__init__(default_value=default_value, **kwargs) 

4245 self.enum_class = enum_class 

4246 self.name_prefix = enum_class.__name__ + "." 

4247 

4248 def select_by_number(self, value: int, default: t.Any = Undefined) -> t.Any: 

4249 """Selects enum-value by using its number-constant.""" 

4250 assert isinstance(value, int) 

4251 enum_members = self.enum_class.__members__ 

4252 for enum_item in enum_members.values(): 

4253 if enum_item.value == value: 

4254 return enum_item 

4255 # -- NOT FOUND: 

4256 return default 

4257 

4258 def select_by_name(self, value: str, default: t.Any = Undefined) -> t.Any: 

4259 """Selects enum-value by using its name or scoped-name.""" 

4260 assert isinstance(value, str) 

4261 if value.startswith(self.name_prefix): 

4262 # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" 

4263 value = value.replace(self.name_prefix, "", 1) 

4264 return self.enum_class.__members__.get(value, default) 

4265 

4266 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4267 if isinstance(value, self.enum_class): 

4268 return value 

4269 elif isinstance(value, int): 

4270 # -- CONVERT: number => enum_value (item) 

4271 value2 = self.select_by_number(value) 

4272 if value2 is not Undefined: 

4273 return value2 

4274 elif isinstance(value, str): 

4275 # -- CONVERT: name or scoped_name (as string) => enum_value (item) 

4276 value2 = self.select_by_name(value) 

4277 if value2 is not Undefined: 

4278 return value2 

4279 elif value is None: 

4280 if self.allow_none: 

4281 return None 

4282 else: 

4283 return self.default_value 

4284 self.error(obj, value) 

4285 

4286 def _choices_str(self, as_rst: bool = False) -> str: 

4287 """Returns a description of the trait choices (not none).""" 

4288 choices = self.enum_class.__members__.keys() 

4289 if as_rst: 

4290 return "|".join(f"``{x!r}``" for x in choices) 

4291 else: 

4292 return repr(list(choices)) # Listify because py3.4- prints odict-class 

4293 

4294 def _info(self, as_rst: bool = False) -> str: 

4295 """Returns a description of the trait.""" 

4296 none = " or %s" % ("`None`" if as_rst else "None") if self.allow_none else "" 

4297 return f"any of {self._choices_str(as_rst)}{none}" 

4298 

4299 def info(self) -> str: 

4300 return self._info(as_rst=False) 

4301 

4302 def info_rst(self) -> str: 

4303 return self._info(as_rst=True) 

4304 

4305 

4306class Callable(TraitType[t.Callable[..., t.Any], t.Callable[..., t.Any]]): 

4307 """A trait which is callable. 

4308 

4309 Notes 

4310 ----- 

4311 Classes are callable, as are instances 

4312 with a __call__() method.""" 

4313 

4314 info_text = "a callable" 

4315 

4316 def validate(self, obj: t.Any, value: t.Any) -> t.Any: 

4317 if callable(value): 

4318 return value 

4319 else: 

4320 self.error(obj, value)