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

1705 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

1""" 

2A lightweight Traits like module. 

3 

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

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

6 

7* Validation 

8* Type specification with defaults 

9* Static and dynamic notification 

10* Basic predefined types 

11* An API that is similar to enthought.traits 

12 

13We don't support: 

14 

15* Delegation 

16* Automatic GUI generation 

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

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

19 contents change. 

20* API compatibility with enthought.traits 

21 

22There are also some important difference in our design: 

23 

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

25 

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

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

28including Jython and IronPython. 

29 

30Inheritance diagram: 

31 

32.. inheritance-diagram:: traitlets.traitlets 

33 :parts: 3 

34""" 

35 

36# Copyright (c) IPython Development Team. 

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

38# 

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

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

41from __future__ import annotations 

42 

43import contextlib 

44import enum 

45import inspect 

46import os 

47import re 

48import sys 

49import types 

50import typing as t 

51from ast import literal_eval 

52 

53from .utils.bunch import Bunch 

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

55from .utils.getargspec import getargspec 

56from .utils.importstring import import_item 

57from .utils.sentinel import Sentinel 

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

59 

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

61 

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

63ClassTypes = (type,) 

64 

65# exports: 

66 

67__all__ = [ 

68 "All", 

69 "Any", 

70 "BaseDescriptor", 

71 "Bool", 

72 "Bytes", 

73 "CBool", 

74 "CBytes", 

75 "CComplex", 

76 "CFloat", 

77 "CInt", 

78 "CLong", 

79 "CRegExp", 

80 "CUnicode", 

81 "Callable", 

82 "CaselessStrEnum", 

83 "ClassBasedTraitType", 

84 "Complex", 

85 "Container", 

86 "DefaultHandler", 

87 "Dict", 

88 "DottedObjectName", 

89 "Enum", 

90 "EventHandler", 

91 "Float", 

92 "ForwardDeclaredInstance", 

93 "ForwardDeclaredMixin", 

94 "ForwardDeclaredType", 

95 "FuzzyEnum", 

96 "HasDescriptors", 

97 "HasTraits", 

98 "Instance", 

99 "Int", 

100 "Integer", 

101 "List", 

102 "Long", 

103 "MetaHasDescriptors", 

104 "MetaHasTraits", 

105 "ObjectName", 

106 "ObserveHandler", 

107 "Set", 

108 "TCPAddress", 

109 "This", 

110 "TraitError", 

111 "TraitType", 

112 "Tuple", 

113 "Type", 

114 "Unicode", 

115 "Undefined", 

116 "Union", 

117 "UseEnum", 

118 "ValidateHandler", 

119 "default", 

120 "directional_link", 

121 "dlink", 

122 "link", 

123 "observe", 

124 "observe_compat", 

125 "parse_notifier_name", 

126 "validate", 

127] 

128 

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

130 

131# ----------------------------------------------------------------------------- 

132# Basic classes 

133# ----------------------------------------------------------------------------- 

134 

135 

136Undefined = Sentinel( 

137 "Undefined", 

138 "traitlets", 

139 """ 

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

141""", 

142) 

143 

144All = Sentinel( 

145 "All", 

146 "traitlets", 

147 """ 

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

149from all trait attributes. 

150""", 

151) 

152 

153# Deprecated alias 

154NoDefaultSpecified = Undefined 

155 

156 

157class TraitError(Exception): 

158 pass 

159 

160 

161# ----------------------------------------------------------------------------- 

162# Utilities 

163# ----------------------------------------------------------------------------- 

164 

165 

166def isidentifier(s): 

167 return s.isidentifier() 

168 

169 

170def _safe_literal_eval(s): 

171 """Safely evaluate an expression 

172 

173 Returns original string if eval fails. 

174 

175 Use only where types are ambiguous. 

176 """ 

177 try: 

178 return literal_eval(s) 

179 except (NameError, SyntaxError, ValueError): 

180 return s 

181 

182 

183def is_trait(t): 

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

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

186 

187 

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

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

190 

191 Examples 

192 -------- 

193 >>> parse_notifier_name([]) 

194 [traitlets.All] 

195 >>> parse_notifier_name("a") 

196 ['a'] 

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

198 ['a', 'b'] 

199 >>> parse_notifier_name(All) 

200 [traitlets.All] 

201 """ 

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

203 return [names] 

204 elif isinstance(names, Sentinel): 

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

206 else: 

207 if not names or All in names: 

208 return [All] 

209 for n in names: 

210 if not isinstance(n, str): 

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

212 return names 

213 

214 

215class _SimpleTest: 

216 def __init__(self, value): 

217 self.value = value 

218 

219 def __call__(self, test): 

220 return test == self.value 

221 

222 def __repr__(self): 

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

224 

225 def __str__(self): 

226 return self.__repr__() 

227 

228 

229def getmembers(object, predicate=None): 

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

231 

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

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

234 in zope.inteface with the __provides__ attribute. 

235 """ 

236 results = [] 

237 for key in dir(object): 

238 try: 

239 value = getattr(object, key) 

240 except AttributeError: 

241 pass 

242 else: 

243 if not predicate or predicate(value): 

244 results.append((key, value)) 

245 results.sort() 

246 return results 

247 

248 

249def _validate_link(*tuples): 

250 """Validate arguments for traitlet link functions""" 

251 for tup in tuples: 

252 if not len(tup) == 2: 

253 raise TypeError( 

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

255 ) 

256 obj, trait_name = tup 

257 if not isinstance(obj, HasTraits): 

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

259 if trait_name not in obj.traits(): 

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

261 

262 

263class link: 

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

265 

266 Parameters 

267 ---------- 

268 source : (object / attribute name) pair 

269 target : (object / attribute name) pair 

270 transform: iterable with two callables (optional) 

271 Data transformation between source and target and target and source. 

272 

273 Examples 

274 -------- 

275 >>> class X(HasTraits): 

276 ... value = Int() 

277 

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

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

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

281 

282 Setting source updates target objects: 

283 >>> src.value = 5 

284 >>> tgt.value 

285 5 

286 """ 

287 

288 updating = False 

289 

290 def __init__(self, source, target, transform=None): 

291 _validate_link(source, target) 

292 self.source, self.target = source, target 

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

294 

295 self.link() 

296 

297 def link(self): 

298 try: 

299 setattr( 

300 self.target[0], 

301 self.target[1], 

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

303 ) 

304 

305 finally: 

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

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

308 

309 @contextlib.contextmanager 

310 def _busy_updating(self): 

311 self.updating = True 

312 try: 

313 yield 

314 finally: 

315 self.updating = False 

316 

317 def _update_target(self, change): 

318 if self.updating: 

319 return 

320 with self._busy_updating(): 

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

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

323 raise TraitError( 

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

325 ) 

326 

327 def _update_source(self, change): 

328 if self.updating: 

329 return 

330 with self._busy_updating(): 

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

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

333 raise TraitError( 

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

335 ) 

336 

337 def unlink(self): 

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

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

340 

341 

342class directional_link: 

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

344 

345 Parameters 

346 ---------- 

347 source : (object, attribute name) pair 

348 target : (object, attribute name) pair 

349 transform: callable (optional) 

350 Data transformation between source and target. 

351 

352 Examples 

353 -------- 

354 >>> class X(HasTraits): 

355 ... value = Int() 

356 

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

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

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

360 

361 Setting source updates target objects: 

362 >>> src.value = 5 

363 >>> tgt.value 

364 5 

365 

366 Setting target does not update source object: 

367 >>> tgt.value = 6 

368 >>> src.value 

369 5 

370 

371 """ 

372 

373 updating = False 

374 

375 def __init__(self, source, target, transform=None): 

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

377 _validate_link(source, target) 

378 self.source, self.target = source, target 

379 self.link() 

380 

381 def link(self): 

382 try: 

383 setattr( 

384 self.target[0], 

385 self.target[1], 

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

387 ) 

388 finally: 

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

390 

391 @contextlib.contextmanager 

392 def _busy_updating(self): 

393 self.updating = True 

394 try: 

395 yield 

396 finally: 

397 self.updating = False 

398 

399 def _update(self, change): 

400 if self.updating: 

401 return 

402 with self._busy_updating(): 

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

404 

405 def unlink(self): 

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

407 

408 

409dlink = directional_link 

410 

411 

412# ----------------------------------------------------------------------------- 

413# Base Descriptor Class 

414# ----------------------------------------------------------------------------- 

415 

416 

417class BaseDescriptor: 

418 """Base descriptor class 

419 

420 Notes 

421 ----- 

422 This implements Python's descriptor protocol. 

423 

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

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

426 class that does the following: 

427 

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

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

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

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

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

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

434 """ 

435 

436 name: str | None = None 

437 this_class: type[t.Any] | None = None 

438 

439 def class_init(self, cls, name): 

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

441 HasDescriptors class. 

442 

443 It is typically overloaded for specific types. 

444 

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

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

447 has been assigned. 

448 """ 

449 self.this_class = cls 

450 self.name = name 

451 

452 def subclass_init(self, cls): 

453 # Instead of HasDescriptors.setup_instance calling 

454 # every instance_init, we opt in by default. 

455 # This gives descriptors a change to opt out for 

456 # performance reasons. 

457 # Because most traits do not need instance_init, 

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

459 # beging created, this otherwise gives a significant performance 

460 # pentalty. Most TypeTraits in traitlets opt out. 

461 cls._instance_inits.append(self.instance_init) 

462 

463 def instance_init(self, obj): 

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

465 HasDescriptors instance. 

466 

467 It is typically overloaded for specific types. 

468 

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

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

471 other descriptors. 

472 """ 

473 pass 

474 

475 

476G = t.TypeVar("G") 

477S = t.TypeVar("S") 

478T = t.TypeVar("T") 

479 

480 

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

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

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

484if t.TYPE_CHECKING: 

485 from typing_extensions import Literal, Self 

486 

487 

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

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

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

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

492 

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

494 allow_none: bool = False 

495 read_only: bool = False 

496 info_text: str = "any value" 

497 default_value: t.Any | None = Undefined 

498 

499 def __init__( 

500 self: TraitType[G, S], 

501 default_value: t.Any = Undefined, 

502 allow_none: bool = False, 

503 read_only: bool | None = None, 

504 help: str | None = None, 

505 config: t.Any = None, 

506 **kwargs: t.Any, 

507 ): 

508 """Declare a traitlet. 

509 

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

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

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

513 

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

515 

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

517 

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

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

520 """ 

521 if default_value is not Undefined: 

522 self.default_value = default_value 

523 if allow_none: 

524 self.allow_none = allow_none 

525 if read_only is not None: 

526 self.read_only = read_only 

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

528 if self.help: 

529 # define __doc__ so that inspectors like autodoc find traits 

530 self.__doc__ = self.help 

531 

532 if len(kwargs) > 0: 

533 stacklevel = 1 

534 f = inspect.currentframe() 

535 # count supers to determine stacklevel for warning 

536 assert f is not None 

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

538 stacklevel += 1 

539 f = f.f_back 

540 assert f is not None 

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

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

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

544 if should_warn(key): 

545 warn( 

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

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

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

549 DeprecationWarning, 

550 stacklevel=stacklevel, 

551 ) 

552 if len(self.metadata) > 0: 

553 self.metadata = self.metadata.copy() 

554 self.metadata.update(kwargs) 

555 else: 

556 self.metadata = kwargs 

557 else: 

558 self.metadata = self.metadata.copy() 

559 if config is not None: 

560 self.metadata["config"] = config 

561 

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

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

564 if help is not None: 

565 self.metadata["help"] = help 

566 

567 def from_string(self, s): 

568 """Get a value from a config string 

569 

570 such as an environment variable or CLI arguments. 

571 

572 Traits can override this method to define their own 

573 parsing of config strings. 

574 

575 .. seealso:: item_from_string 

576 

577 .. versionadded:: 5.0 

578 """ 

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

580 return None 

581 return s 

582 

583 def default(self, obj=None): 

584 """The default generator for this trait 

585 

586 Notes 

587 ----- 

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

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

590 """ 

591 if self.default_value is not Undefined: 

592 return self.default_value 

593 elif hasattr(self, "make_dynamic_default"): 

594 return self.make_dynamic_default() 

595 else: 

596 # Undefined will raise in TraitType.get 

597 return self.default_value 

598 

599 def get_default_value(self): 

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

601 Use self.default_value instead 

602 """ 

603 warn( 

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

605 DeprecationWarning, 

606 stacklevel=2, 

607 ) 

608 return self.default_value 

609 

610 def init_default_value(self, obj): 

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

612 warn( 

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

614 DeprecationWarning, 

615 stacklevel=2, 

616 ) 

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

618 obj._trait_values[self.name] = value 

619 return value 

620 

621 def get(self, obj: HasTraits, cls: t.Any = None) -> G | None: 

622 try: 

623 value = obj._trait_values[self.name] # type: ignore 

624 except KeyError: 

625 # Check for a dynamic initializer. 

626 default = obj.trait_defaults(self.name) 

627 if default is Undefined: 

628 warn( 

629 "Explicit using of Undefined as the default value " 

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

631 "exceptions in the future.", 

632 DeprecationWarning, 

633 stacklevel=2, 

634 ) 

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

636 # write out the obj.cross_validation_lock call here. 

637 _cross_validation_lock = obj._cross_validation_lock 

638 try: 

639 obj._cross_validation_lock = True 

640 value = self._validate(obj, default) 

641 finally: 

642 obj._cross_validation_lock = _cross_validation_lock 

643 obj._trait_values[self.name] = value # type: ignore 

644 obj._notify_observers( 

645 Bunch( 

646 name=self.name, 

647 value=value, 

648 owner=obj, 

649 type="default", 

650 ) 

651 ) 

652 return value # type: ignore 

653 except Exception as e: 

654 # This should never be reached. 

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

656 else: 

657 return value # type: ignore 

658 

659 if t.TYPE_CHECKING: 

660 # This gives ok type information, but not specific enough (e.g. it will) 

661 # always be a TraitType, not a subclass, like Bool. 

662 @t.overload 

663 def __new__( # type: ignore[misc] 

664 cls, 

665 default_value: S | Sentinel = Undefined, 

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

667 read_only: bool | None = None, 

668 help: str | None = None, 

669 config: t.Any = None, 

670 **kwargs: t.Any, 

671 ) -> TraitType[G, S]: 

672 ... 

673 

674 @t.overload 

675 def __new__( 

676 cls, 

677 default_value: S | None | Sentinel = Undefined, 

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

679 read_only: bool | None = None, 

680 help: str | None = None, 

681 config: t.Any = None, 

682 **kwargs: t.Any, 

683 ) -> TraitType[G | None, S]: 

684 ... 

685 

686 def __new__( # type: ignore[no-untyped-def, misc] 

687 cls, 

688 default_value: S | None | Sentinel = Undefined, 

689 allow_none: Literal[True, False] = False, 

690 read_only=None, 

691 help=None, 

692 config=None, 

693 **kwargs, 

694 ) -> TraitType[G | None, S] | TraitType[G, S]: 

695 ... 

696 

697 @t.overload 

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

699 ... 

700 

701 @t.overload 

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

703 ... 

704 

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

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

707 

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

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

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

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

712 """ 

713 if obj is None: 

714 return self 

715 else: 

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

717 

718 def set(self, obj, value): 

719 new_value = self._validate(obj, value) 

720 try: 

721 old_value = obj._trait_values[self.name] 

722 except KeyError: 

723 old_value = self.default_value 

724 

725 obj._trait_values[self.name] = new_value 

726 try: 

727 silent = bool(old_value == new_value) 

728 except Exception: 

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

730 silent = False 

731 if silent is not True: 

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

733 # comparison above returns something other than True/False 

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

735 

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

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

738 

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

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

741 """ 

742 if self.read_only: 

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

744 else: 

745 self.set(obj, value) 

746 

747 def _validate(self, obj, value): 

748 if value is None and self.allow_none: 

749 return value 

750 if hasattr(self, "validate"): 

751 value = self.validate(obj, value) 

752 if obj._cross_validation_lock is False: 

753 value = self._cross_validate(obj, value) 

754 return value 

755 

756 def _cross_validate(self, obj, value): 

757 if self.name in obj._trait_validators: 

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

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

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

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

762 cross_validate = getattr(obj, meth_name) 

763 deprecated_method( 

764 cross_validate, 

765 obj.__class__, 

766 meth_name, 

767 "use @validate decorator instead.", 

768 ) 

769 value = cross_validate(value, self) 

770 return value 

771 

772 def __or__(self, other): 

773 if isinstance(other, Union): 

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

775 else: 

776 return Union([self, other]) 

777 

778 def info(self): 

779 return self.info_text 

780 

781 def error(self, obj, value, error=None, info=None): 

782 """Raise a TraitError 

783 

784 Parameters 

785 ---------- 

786 obj : HasTraits or None 

787 The instance which owns the trait. If not 

788 object is given, then an object agnostic 

789 error will be raised. 

790 value : any 

791 The value that caused the error. 

792 error : Exception (default: None) 

793 An error that was raised by a child trait. 

794 The arguments of this exception should be 

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

796 Where the ``value`` and ``info`` are the 

797 problem value, and string describing the 

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

799 of :class:`TraitType` instances that are 

800 "children" of this one (the first being 

801 the deepest). 

802 info : str (default: None) 

803 A description of the expected value. By 

804 default this is infered from this trait's 

805 ``info`` method. 

806 """ 

807 if error is not None: 

808 # handle nested error 

809 error.args += (self,) 

810 if self.name is not None: 

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

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

813 if obj is not None: 

814 error.args = ( 

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

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

817 self.name, 

818 describe("an", obj), 

819 chain, 

820 error.args[1], 

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

822 ), 

823 ) 

824 else: 

825 error.args = ( 

826 "The '{}' trait contains {} which " 

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

828 self.name, 

829 chain, 

830 error.args[1], 

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

832 ), 

833 ) 

834 raise error 

835 else: 

836 # this trait caused an error 

837 if self.name is None: 

838 # this is not the root trait 

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

840 else: 

841 # this is the root trait 

842 if obj is not None: 

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

844 self.name, 

845 class_of(obj), 

846 self.info(), 

847 describe("the", value), 

848 ) 

849 else: 

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

851 self.name, 

852 self.info(), 

853 describe("the", value), 

854 ) 

855 raise TraitError(e) 

856 

857 def get_metadata(self, key, default=None): 

858 """DEPRECATED: Get a metadata value. 

859 

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

861 """ 

862 if key == "help": 

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

864 else: 

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

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

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

868 

869 def set_metadata(self, key, value): 

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

871 

872 Use .metadata[key] = value instead. 

873 """ 

874 if key == "help": 

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

876 else: 

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

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

879 self.metadata[key] = value 

880 

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

882 """Sets metadata and returns self. 

883 

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

885 

886 Examples 

887 -------- 

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

889 <traitlets.traitlets.Int object at ...> 

890 

891 """ 

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

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

894 ) 

895 if maybe_constructor_keywords: 

896 warn( 

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

898 % maybe_constructor_keywords, 

899 UserWarning, 

900 stacklevel=2, 

901 ) 

902 

903 self.metadata.update(metadata) 

904 return self 

905 

906 def default_value_repr(self): 

907 return repr(self.default_value) 

908 

909 

910# ----------------------------------------------------------------------------- 

911# The HasTraits implementation 

912# ----------------------------------------------------------------------------- 

913 

914 

915class _CallbackWrapper: 

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

917 

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

919 callbacks. 

920 """ 

921 

922 def __init__(self, cb): 

923 self.cb = cb 

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

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

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

927 if self.nargs > 4: 

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

929 

930 def __eq__(self, other): 

931 # The wrapper is equal to the wrapped element 

932 if isinstance(other, _CallbackWrapper): 

933 return self.cb == other.cb 

934 else: 

935 return self.cb == other 

936 

937 def __call__(self, change): 

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): 

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__(mcls, name, bases, classdict): # noqa 

966 """Create the HasDescriptors class.""" 

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

968 # ---------------------------------------------------------------- 

969 # Support of deprecated behavior allowing for TraitType types 

970 # to be used instead of TraitType instances. 

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

972 warn( 

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

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

975 DeprecationWarning, 

976 stacklevel=2, 

977 ) 

978 classdict[k] = v() 

979 # ---------------------------------------------------------------- 

980 

981 return super().__new__(mcls, name, bases, classdict) 

982 

983 def __init__(cls, name, bases, classdict): 

984 """Finish initializing the HasDescriptors class.""" 

985 super().__init__(name, bases, classdict) 

986 cls.setup_class(classdict) 

987 

988 def setup_class(cls, classdict): 

989 """Setup descriptor instance on the class 

990 

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

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

993 calling their :attr:`class_init` method. 

994 """ 

995 cls._descriptors = [] 

996 cls._instance_inits = [] 

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

998 if isinstance(v, BaseDescriptor): 

999 v.class_init(cls, k) 

1000 

1001 for _, v in getmembers(cls): 

1002 if isinstance(v, BaseDescriptor): 

1003 v.subclass_init(cls) 

1004 cls._descriptors.append(v) 

1005 

1006 

1007class MetaHasTraits(MetaHasDescriptors): 

1008 """A metaclass for HasTraits.""" 

1009 

1010 def setup_class(cls, classdict): # noqa 

1011 # for only the current class 

1012 cls._trait_default_generators = {} 

1013 # also looking at base classes 

1014 cls._all_trait_default_generators = {} 

1015 cls._traits = {} 

1016 cls._static_immutable_initial_values = {} 

1017 

1018 super().setup_class(classdict) 

1019 

1020 mro = cls.mro() 

1021 

1022 for name in dir(cls): 

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

1024 # __provides__ attributes even though they exist. This causes 

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

1026 try: 

1027 value = getattr(cls, name) 

1028 except AttributeError: 

1029 continue 

1030 if isinstance(value, TraitType): 

1031 cls._traits[name] = value 

1032 trait = value 

1033 default_method_name = "_%s_default" % name 

1034 mro_trait = mro 

1035 try: 

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

1037 except ValueError: 

1038 # this_class not in mro 

1039 pass 

1040 for c in mro_trait: 

1041 if default_method_name in c.__dict__: 

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

1043 break 

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

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

1046 break 

1047 else: 

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

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

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

1051 # of initial values to speed up instance creation. 

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

1053 # for instance ipywidgets. 

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

1055 if ( 

1056 type(trait) in [CInt, Int] 

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

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

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

1060 ): 

1061 cls._static_immutable_initial_values[name] = trait.default_value 

1062 elif ( 

1063 type(trait) in [CFloat, Float] 

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

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

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

1067 ): 

1068 cls._static_immutable_initial_values[name] = trait.default_value 

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

1070 isinstance(trait.default_value, bool) or none_ok 

1071 ): 

1072 cls._static_immutable_initial_values[name] = trait.default_value 

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

1074 isinstance(trait.default_value, str) or none_ok 

1075 ): 

1076 cls._static_immutable_initial_values[name] = trait.default_value 

1077 elif type(trait) == Any and ( 

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

1079 ): 

1080 cls._static_immutable_initial_values[name] = trait.default_value 

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

1082 cls._static_immutable_initial_values[name] = None 

1083 elif ( 

1084 isinstance(trait, Instance) 

1085 and trait.default_args is None 

1086 and trait.default_kwargs is None 

1087 and trait.allow_none 

1088 ): 

1089 cls._static_immutable_initial_values[name] = None 

1090 

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

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

1093 cls._all_trait_default_generators[name] = trait.default 

1094 

1095 

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

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

1098 

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

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

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

1102 name of the attribute that triggered the notification. 

1103 

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

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

1106 * ``owner`` : the HasTraits instance 

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

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

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

1110 

1111 Parameters 

1112 ---------- 

1113 *names 

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

1115 type : str, kwarg-only 

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

1117 """ 

1118 if not names: 

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

1120 for name in names: 

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

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

1123 return ObserveHandler(names, type=type) 

1124 

1125 

1126def observe_compat(func): 

1127 """Backward-compatibility shim decorator for observers 

1128 

1129 Use with: 

1130 

1131 @observe('name') 

1132 @observe_compat 

1133 def _foo_changed(self, change): 

1134 ... 

1135 

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

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

1138 """ 

1139 

1140 def compatible_observer(self, change_or_name, old=Undefined, new=Undefined): 

1141 if isinstance(change_or_name, dict): 

1142 change = change_or_name 

1143 else: 

1144 clsname = self.__class__.__name__ 

1145 warn( 

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

1147 DeprecationWarning, 

1148 stacklevel=2, 

1149 ) 

1150 change = Bunch( 

1151 type="change", 

1152 old=old, 

1153 new=new, 

1154 name=change_or_name, 

1155 owner=self, 

1156 ) 

1157 return func(self, change) 

1158 

1159 return compatible_observer 

1160 

1161 

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

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

1164 when a Trait is set. 

1165 

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

1167 The proposal dictionary must hold the following keys: 

1168 

1169 * ``owner`` : the HasTraits instance 

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

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

1172 

1173 Parameters 

1174 ---------- 

1175 *names 

1176 The str names of the Traits to validate. 

1177 

1178 Notes 

1179 ----- 

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

1181 the registered cross validator could potentially make changes to attributes 

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

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

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

1185 commute. 

1186 """ 

1187 if not names: 

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

1189 for name in names: 

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

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

1192 return ValidateHandler(names) 

1193 

1194 

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

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

1197 

1198 Parameters 

1199 ---------- 

1200 name 

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

1202 

1203 Notes 

1204 ----- 

1205 Unlike observers and validators which are properties of the HasTraits 

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

1207 

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

1209 subclasses of `this_type`. 

1210 

1211 :: 

1212 

1213 class A(HasTraits): 

1214 bar = Int() 

1215 

1216 @default('bar') 

1217 def get_bar_default(self): 

1218 return 11 

1219 

1220 class B(A): 

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

1222 # the base class A 

1223 

1224 class C(B): 

1225 

1226 @default('bar') 

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

1228 return 3.0 # ignored since it is defined in a 

1229 # class derived from B.a.this_class. 

1230 """ 

1231 if not isinstance(name, str): 

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

1233 return DefaultHandler(name) 

1234 

1235 

1236class EventHandler(BaseDescriptor): 

1237 def _init_call(self, func): 

1238 self.func = func 

1239 return self 

1240 

1241 def __call__(self, *args, **kwargs): 

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

1243 if hasattr(self, "func"): 

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

1245 else: 

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

1247 

1248 def __get__(self, inst, cls=None): 

1249 if inst is None: 

1250 return self 

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

1252 

1253 

1254class ObserveHandler(EventHandler): 

1255 def __init__(self, names, type): 

1256 self.trait_names = names 

1257 self.type = type 

1258 

1259 def instance_init(self, inst): 

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

1261 

1262 

1263class ValidateHandler(EventHandler): 

1264 def __init__(self, names): 

1265 self.trait_names = names 

1266 

1267 def instance_init(self, inst): 

1268 inst._register_validator(self, self.trait_names) 

1269 

1270 

1271class DefaultHandler(EventHandler): 

1272 def __init__(self, name): 

1273 self.trait_name = name 

1274 

1275 def class_init(self, cls, name): 

1276 super().class_init(cls, name) 

1277 cls._trait_default_generators[self.trait_name] = self 

1278 

1279 

1280class HasDescriptors(metaclass=MetaHasDescriptors): 

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

1282 

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

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

1285 cls = args[0] 

1286 args = args[1:] 

1287 

1288 # This is needed because object.__new__ only accepts 

1289 # the cls argument. 

1290 new_meth = super(HasDescriptors, cls).__new__ 

1291 if new_meth is object.__new__: 

1292 inst = new_meth(cls) 

1293 else: 

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

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

1296 return inst 

1297 

1298 def setup_instance(*args, **kwargs): 

1299 """ 

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

1301 """ 

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

1303 self = args[0] 

1304 args = args[1:] 

1305 

1306 self._cross_validation_lock = False # type:ignore[attr-defined] 

1307 cls = self.__class__ 

1308 # Let descriptors performance initialization when a HasDescriptor 

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

1310 # default creations or other bookkeepings. 

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

1312 # subclass_init. 

1313 for init in cls._instance_inits: 

1314 init(self) 

1315 

1316 

1317class HasTraits(HasDescriptors, metaclass=MetaHasTraits): 

1318 _trait_values: dict[str, t.Any] 

1319 _static_immutable_initial_values: dict[str, t.Any] 

1320 _trait_notifiers: dict[str, t.Any] 

1321 _trait_validators: dict[str, t.Any] 

1322 _cross_validation_lock: bool 

1323 _traits: dict[str, t.Any] 

1324 _all_trait_default_generators: dict[str, t.Any] 

1325 

1326 def setup_instance(*args, **kwargs): 

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

1328 self = args[0] 

1329 args = args[1:] 

1330 

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

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

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

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

1335 self._trait_notifiers = {} 

1336 self._trait_validators = {} 

1337 self._cross_validation_lock = False 

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

1339 

1340 def __init__(self, *args, **kwargs): 

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

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

1343 # notifications. 

1344 super_args = args 

1345 super_kwargs = {} 

1346 

1347 if kwargs: 

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

1349 # the hold_trait_notifications(self) context manager 

1350 def ignore(*_ignore_args): 

1351 pass 

1352 

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

1354 self._cross_validation_lock = True 

1355 changes = {} 

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

1357 if self.has_trait(key): 

1358 setattr(self, key, value) 

1359 changes[key] = Bunch( 

1360 name=key, 

1361 old=None, 

1362 new=value, 

1363 owner=self, 

1364 type="change", 

1365 ) 

1366 else: 

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

1368 super_kwargs[key] = value 

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

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

1371 for key in changed: 

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

1373 self.set_trait(key, value) 

1374 changes[key]['new'] = value 

1375 self._cross_validation_lock = False 

1376 # Restore method retrieval from class 

1377 del self.notify_change 

1378 for key in changed: 

1379 self.notify_change(changes[key]) 

1380 

1381 try: 

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

1383 except TypeError as e: 

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

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

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

1387 arg_s = ", ".join(arg_s_list) 

1388 warn( 

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

1390 "{error}\n" 

1391 "This is deprecated in traitlets 4.2." 

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

1393 arg_s=arg_s, 

1394 classname=self.__class__.__name__, 

1395 error=e, 

1396 ), 

1397 DeprecationWarning, 

1398 stacklevel=2, 

1399 ) 

1400 

1401 def __getstate__(self): 

1402 d = self.__dict__.copy() 

1403 # event handlers stored on an instance are 

1404 # expected to be reinstantiated during a 

1405 # recall of instance_init during __setstate__ 

1406 d["_trait_notifiers"] = {} 

1407 d["_trait_validators"] = {} 

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

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

1410 

1411 return d 

1412 

1413 def __setstate__(self, state): 

1414 self.__dict__ = state.copy() 

1415 

1416 # event handlers are reassigned to self 

1417 cls = self.__class__ 

1418 for key in dir(cls): 

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

1420 # __provides__ attributes even though they exist. This causes 

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

1422 try: 

1423 value = getattr(cls, key) 

1424 except AttributeError: 

1425 pass 

1426 else: 

1427 if isinstance(value, EventHandler): 

1428 value.instance_init(self) 

1429 

1430 @property 

1431 @contextlib.contextmanager 

1432 def cross_validation_lock(self): 

1433 """ 

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

1435 to True. 

1436 

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

1438 prior to entering the block. 

1439 """ 

1440 if self._cross_validation_lock: 

1441 yield 

1442 return 

1443 else: 

1444 try: 

1445 self._cross_validation_lock = True 

1446 yield 

1447 finally: 

1448 self._cross_validation_lock = False 

1449 

1450 @contextlib.contextmanager 

1451 def hold_trait_notifications(self): 

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

1453 validation. 

1454 

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

1456 race conditions in trait notifiers requesting other trait values. 

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

1458 """ 

1459 if self._cross_validation_lock: 

1460 yield 

1461 return 

1462 else: 

1463 cache: dict[str, t.Any] = {} 

1464 

1465 def compress(past_changes, change): 

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

1467 if past_changes is None: 

1468 return [change] 

1469 else: 

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

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

1472 else: 

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

1474 past_changes.append(change) 

1475 return past_changes 

1476 

1477 def hold(change): 

1478 name = change.name 

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

1480 

1481 try: 

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

1483 # notifications, disable cross validation and yield. 

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

1485 self._cross_validation_lock = True 

1486 yield 

1487 # Cross validate final values when context is released. 

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

1489 trait = getattr(self.__class__, name) 

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

1491 self.set_trait(name, value) 

1492 except TraitError as e: 

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

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

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

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

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

1498 if change.type == "change": 

1499 if change.old is not Undefined: 

1500 self.set_trait(name, change.old) 

1501 else: 

1502 self._trait_values.pop(name) 

1503 cache = {} 

1504 raise e 

1505 finally: 

1506 self._cross_validation_lock = False 

1507 # Restore method retrieval from class 

1508 del self.notify_change 

1509 

1510 # trigger delayed notifications 

1511 for changes in cache.values(): 

1512 for change in changes: 

1513 self.notify_change(change) 

1514 

1515 def _notify_trait(self, name, old_value, new_value): 

1516 self.notify_change( 

1517 Bunch( 

1518 name=name, 

1519 old=old_value, 

1520 new=new_value, 

1521 owner=self, 

1522 type="change", 

1523 ) 

1524 ) 

1525 

1526 def notify_change(self, change): 

1527 """Notify observers of a change event""" 

1528 return self._notify_observers(change) 

1529 

1530 def _notify_observers(self, event): 

1531 """Notify observers of any event""" 

1532 if not isinstance(event, Bunch): 

1533 # cast to bunch if given a dict 

1534 event = Bunch(event) 

1535 name, type = event['name'], event['type'] 

1536 

1537 callables = [] 

1538 if name in self._trait_notifiers: 

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

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

1541 if All in self._trait_notifiers: # type:ignore[comparison-overlap] 

1542 callables.extend( 

1543 self._trait_notifiers.get(All, {}).get(type, []) # type:ignore[call-overload] 

1544 ) 

1545 callables.extend( 

1546 self._trait_notifiers.get(All, {}).get(All, []) # type:ignore[call-overload] 

1547 ) 

1548 

1549 # Now static ones 

1550 magic_name = "_%s_changed" % name 

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(self, handler, name, type): 

1578 if name not in self._trait_notifiers: 

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

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

1581 else: 

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

1583 nlist = [] 

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

1585 else: 

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

1587 if handler not in nlist: 

1588 nlist.append(handler) 

1589 

1590 def _remove_notifiers(self, handler, name, type): 

1591 try: 

1592 if handler is None: 

1593 del self._trait_notifiers[name][type] 

1594 else: 

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

1596 except KeyError: 

1597 pass 

1598 

1599 def on_trait_change(self, handler=None, name=None, remove=False): 

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

1601 

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

1603 

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

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

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

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

1608 below). 

1609 

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

1611 handlers for the specified name are uninstalled. 

1612 

1613 Parameters 

1614 ---------- 

1615 handler : callable, None 

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

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

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

1619 name : list, str, None 

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

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

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

1623 remove : bool 

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

1625 then unintall it. 

1626 """ 

1627 warn( 

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

1629 DeprecationWarning, 

1630 stacklevel=2, 

1631 ) 

1632 if name is None: 

1633 name = All 

1634 if remove: 

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

1636 else: 

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

1638 

1639 def observe( 

1640 self, 

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

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

1643 type: Sentinel | str = "change", 

1644 ) -> None: 

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

1646 

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

1648 

1649 Parameters 

1650 ---------- 

1651 handler : callable 

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

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

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

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

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

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

1658 * ``owner`` : the HasTraits instance 

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

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

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

1662 names : list, str, All 

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

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

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

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

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

1668 notifications are passed to the observe handler. 

1669 """ 

1670 for name in parse_notifier_name(names): 

1671 self._add_notifiers(handler, name, type) 

1672 

1673 def unobserve( 

1674 self, 

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

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

1677 type: Sentinel | str = "change", 

1678 ) -> None: 

1679 """Remove a trait change handler. 

1680 

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

1682 

1683 Parameters 

1684 ---------- 

1685 handler : callable 

1686 The callable called when a trait attribute changes. 

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

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

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

1690 from the list of notifiers corresponding to all changes. 

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

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

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

1694 """ 

1695 for name in parse_notifier_name(names): 

1696 self._remove_notifiers(handler, name, type) 

1697 

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

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

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

1701 if name is All: 

1702 self._trait_notifiers: dict[str, t.Any] = {} 

1703 else: 

1704 try: 

1705 del self._trait_notifiers[name] 

1706 except KeyError: 

1707 pass 

1708 

1709 def _register_validator(self, handler, names): 

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

1711 

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

1713 

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

1715 TraitError is raised and no new validator is registered. 

1716 

1717 Parameters 

1718 ---------- 

1719 handler : callable 

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

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

1722 with the following attributes/keys: 

1723 * ``owner`` : the HasTraits instance 

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

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

1726 names : List of strings 

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

1728 """ 

1729 for name in names: 

1730 magic_name = "_%s_validate" % name 

1731 if hasattr(self, magic_name): 

1732 class_value = getattr(self.__class__, magic_name) 

1733 if not isinstance(class_value, ValidateHandler): 

1734 deprecated_method( 

1735 class_value, 

1736 self.__class__, 

1737 magic_name, 

1738 "use @validate decorator instead.", 

1739 ) 

1740 for name in names: 

1741 self._trait_validators[name] = handler 

1742 

1743 def add_traits(self, **traits): 

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

1745 cls = self.__class__ 

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

1747 if hasattr(cls, "__qualname__"): 

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

1749 attrs["__qualname__"] = cls.__qualname__ 

1750 attrs.update(traits) 

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

1752 for trait in traits.values(): 

1753 trait.instance_init(self) 

1754 

1755 def set_trait(self, name, value): 

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

1757 cls = self.__class__ 

1758 if not self.has_trait(name): 

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

1760 else: 

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

1762 

1763 @classmethod 

1764 def class_trait_names(cls, **metadata): 

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

1766 

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

1768 but is unbound. 

1769 """ 

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

1771 

1772 @classmethod 

1773 def class_traits(cls, **metadata): 

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

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

1776 

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

1778 

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

1780 that the various HasTrait's instances are holding. 

1781 

1782 The metadata kwargs allow functions to be passed in which 

1783 filter traits based on metadata values. The functions should 

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

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

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

1787 to the function. 

1788 """ 

1789 traits = cls._traits.copy() 

1790 

1791 if len(metadata) == 0: 

1792 return traits 

1793 

1794 result = {} 

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

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

1797 if not callable(meta_eval): 

1798 meta_eval = _SimpleTest(meta_eval) 

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

1800 break 

1801 else: 

1802 result[name] = trait 

1803 

1804 return result 

1805 

1806 @classmethod 

1807 def class_own_traits(cls, **metadata): 

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

1809 

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

1811 """ 

1812 sup = super(cls, cls) 

1813 return { 

1814 n: t 

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

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

1817 } 

1818 

1819 def has_trait(self, name): 

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

1821 return name in self._traits 

1822 

1823 def trait_has_value(self, name): 

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

1825 

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

1827 dynamically generated default value. These default values 

1828 will be recognized as existing only after they have been 

1829 generated. 

1830 

1831 Example 

1832 

1833 .. code-block:: python 

1834 

1835 class MyClass(HasTraits): 

1836 i = Int() 

1837 

1838 mc = MyClass() 

1839 assert not mc.trait_has_value("i") 

1840 mc.i # generates a default value 

1841 assert mc.trait_has_value("i") 

1842 """ 

1843 return name in self._trait_values 

1844 

1845 def trait_values(self, **metadata): 

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

1847 

1848 The metadata kwargs allow functions to be passed in which 

1849 filter traits based on metadata values. The functions should 

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

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

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

1853 to the function. 

1854 

1855 Returns 

1856 ------- 

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

1858 

1859 Notes 

1860 ----- 

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

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

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

1864 """ 

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

1866 

1867 def _get_trait_default_generator(self, name): 

1868 """Return default generator for a given trait 

1869 

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

1871 """ 

1872 method_name = "_%s_default" % name 

1873 if method_name in self.__dict__: 

1874 return getattr(self, method_name) 

1875 if method_name in self.__class__.__dict__: 

1876 return getattr(self.__class__, method_name) 

1877 return self._all_trait_default_generators[name] 

1878 

1879 def trait_defaults(self, *names, **metadata): 

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

1881 

1882 Notes 

1883 ----- 

1884 Dynamically generated default values may 

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

1886 for n in names: 

1887 if not self.has_trait(n): 

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

1889 

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

1891 return self._get_trait_default_generator(names[0])(self) 

1892 

1893 trait_names = self.trait_names(**metadata) 

1894 trait_names.extend(names) 

1895 

1896 defaults = {} 

1897 for n in trait_names: 

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

1899 return defaults 

1900 

1901 def trait_names(self, **metadata): 

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

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

1904 

1905 def traits(self, **metadata): 

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

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

1908 

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

1910 that the various HasTrait's instances are holding. 

1911 

1912 The metadata kwargs allow functions to be passed in which 

1913 filter traits based on metadata values. The functions should 

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

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

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

1917 to the function. 

1918 """ 

1919 traits = self._traits.copy() 

1920 

1921 if len(metadata) == 0: 

1922 return traits 

1923 

1924 result = {} 

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

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

1927 if not callable(meta_eval): 

1928 meta_eval = _SimpleTest(meta_eval) 

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

1930 break 

1931 else: 

1932 result[name] = trait 

1933 

1934 return result 

1935 

1936 def trait_metadata(self, traitname, key, default=None): 

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

1938 try: 

1939 trait = getattr(self.__class__, traitname) 

1940 except AttributeError as e: 

1941 raise TraitError( 

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

1943 ) from e 

1944 metadata_name = "_" + traitname + "_metadata" 

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

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

1947 else: 

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

1949 

1950 @classmethod 

1951 def class_own_trait_events(cls, name): 

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

1953 

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

1955 """ 

1956 sup = super(cls, cls) 

1957 return { 

1958 n: e 

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

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

1961 } 

1962 

1963 @classmethod 

1964 def trait_events(cls, name=None): 

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

1966 

1967 Parameters 

1968 ---------- 

1969 name : str (default: None) 

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

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

1972 

1973 Returns 

1974 ------- 

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

1976 """ 

1977 events = {} 

1978 for k, v in getmembers(cls): 

1979 if isinstance(v, EventHandler): 

1980 if name is None: 

1981 events[k] = v 

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

1983 events[k] = v 

1984 elif hasattr(v, "tags"): 

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

1986 events[k] = v 

1987 return events 

1988 

1989 

1990# ----------------------------------------------------------------------------- 

1991# Actual TraitTypes implementations/subclasses 

1992# ----------------------------------------------------------------------------- 

1993 

1994# ----------------------------------------------------------------------------- 

1995# TraitTypes subclasses for handling classes and instances of classes 

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

1997 

1998 

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

2000 """ 

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

2002 Instance and This. 

2003 """ 

2004 

2005 def _resolve_string(self, string): 

2006 """ 

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

2008 """ 

2009 return import_item(string) 

2010 

2011 

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

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

2014 

2015 if t.TYPE_CHECKING: 

2016 

2017 @t.overload 

2018 def __init__( 

2019 self: Type[object, object], 

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

2021 klass: None | str = ..., 

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

2023 read_only: bool | None = ..., 

2024 help: str | None = ..., 

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

2026 **kwargs: t.Any, 

2027 ): 

2028 ... 

2029 

2030 @t.overload 

2031 def __init__( 

2032 self: Type[object | None, object | None], 

2033 default_value: S | Sentinel | None | str = ..., 

2034 klass: None | str = ..., 

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

2036 read_only: bool | None = ..., 

2037 help: str | None = ..., 

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

2039 **kwargs: t.Any, 

2040 ): 

2041 ... 

2042 

2043 @t.overload 

2044 def __init__( 

2045 self: Type[S, S], 

2046 default_value: S | Sentinel | str = ..., 

2047 klass: type[S] = ..., 

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

2049 read_only: bool | None = ..., 

2050 help: str | None = ..., 

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

2052 **kwargs: t.Any, 

2053 ): 

2054 ... 

2055 

2056 @t.overload 

2057 def __init__( 

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

2059 default_value: S | Sentinel | None | str = ..., 

2060 klass: type[S] = ..., 

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

2062 read_only: bool | None = ..., 

2063 help: str | None = ..., 

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

2065 **kwargs: t.Any, 

2066 ): 

2067 ... 

2068 

2069 def __init__(self, default_value=Undefined, klass=None, allow_none=False, **kwargs): 

2070 """Construct a Type trait 

2071 

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

2073 a particular class. 

2074 

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

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

2077 

2078 Parameters 

2079 ---------- 

2080 default_value : class, str or None 

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

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

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

2084 :class:`HasTraits` class is instantiated. 

2085 klass : class, str [ default object ] 

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

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

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

2089 :class:`HasTraits` class is instantiated. 

2090 allow_none : bool [ default False ] 

2091 Indicates whether None is allowed as an assignable value. 

2092 **kwargs 

2093 extra kwargs passed to `ClassBasedTraitType` 

2094 """ 

2095 if default_value is Undefined: 

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

2097 else: 

2098 new_default_value = default_value 

2099 

2100 if klass is None: 

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

2102 klass = object 

2103 else: 

2104 klass = default_value 

2105 

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

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

2108 

2109 self.klass = klass 

2110 

2111 super().__init__(new_default_value, allow_none=allow_none, **kwargs) 

2112 

2113 def validate(self, obj, value): 

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

2115 if isinstance(value, str): 

2116 try: 

2117 value = self._resolve_string(value) 

2118 except ImportError as e: 

2119 raise TraitError( 

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

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

2122 ) from e 

2123 try: 

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

2125 return value 

2126 except Exception: 

2127 pass 

2128 

2129 self.error(obj, value) 

2130 

2131 def info(self): 

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

2133 if isinstance(self.klass, str): 

2134 klass = self.klass 

2135 else: 

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

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

2138 if self.allow_none: 

2139 return result + " or None" 

2140 return result 

2141 

2142 def instance_init(self, obj): 

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

2144 # might be called before all imports are done. 

2145 self._resolve_classes() 

2146 

2147 def _resolve_classes(self): 

2148 if isinstance(self.klass, str): 

2149 self.klass = self._resolve_string(self.klass) 

2150 if isinstance(self.default_value, str): 

2151 self.default_value = self._resolve_string(self.default_value) 

2152 

2153 def default_value_repr(self): 

2154 value = self.default_value 

2155 assert value is not None 

2156 if isinstance(value, str): 

2157 return repr(value) 

2158 else: 

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

2160 

2161 

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

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

2164 

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

2166 

2167 Subclasses can declare default classes by overriding the klass attribute 

2168 """ 

2169 

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

2171 

2172 if t.TYPE_CHECKING: 

2173 

2174 @t.overload 

2175 def __init__( 

2176 self: Instance[T], 

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

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

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

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

2181 read_only: bool | None = ..., 

2182 help: str | None = ..., 

2183 **kwargs: t.Any, 

2184 ) -> None: 

2185 ... 

2186 

2187 @t.overload 

2188 def __init__( 

2189 self: Instance[T | None], 

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

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

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

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

2194 read_only: bool | None = ..., 

2195 help: str | None = ..., 

2196 **kwargs: t.Any, 

2197 ) -> None: 

2198 ... 

2199 

2200 @t.overload 

2201 def __init__( 

2202 self: Instance[t.Any], 

2203 klass: str | None = ..., 

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

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

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

2207 read_only: bool | None = ..., 

2208 help: str | None = ..., 

2209 **kwargs: t.Any, 

2210 ) -> None: 

2211 ... 

2212 

2213 @t.overload 

2214 def __init__( 

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

2216 klass: str | None = ..., 

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

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

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

2220 read_only: bool | None = ..., 

2221 help: str | None = ..., 

2222 **kwargs: t.Any, 

2223 ) -> None: 

2224 ... 

2225 

2226 def __init__( 

2227 self, 

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

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

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

2231 allow_none: bool = False, 

2232 read_only: bool | None = None, 

2233 help: str | None = None, 

2234 **kwargs: t.Any, 

2235 ) -> None: 

2236 """Construct an Instance trait. 

2237 

2238 This trait allows values that are instances of a particular 

2239 class or its subclasses. Our implementation is quite different 

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

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

2242 

2243 Parameters 

2244 ---------- 

2245 klass : class, str 

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

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

2248 args : tuple 

2249 Positional arguments for generating the default value. 

2250 kw : dict 

2251 Keyword arguments for generating the default value. 

2252 allow_none : bool [ default False ] 

2253 Indicates whether None is allowed as a value. 

2254 **kwargs 

2255 Extra kwargs passed to `ClassBasedTraitType` 

2256 

2257 Notes 

2258 ----- 

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

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

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

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

2263 """ 

2264 if klass is None: 

2265 klass = self.klass 

2266 

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

2268 self.klass = klass 

2269 else: 

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

2271 

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

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

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

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

2276 

2277 self.default_args = args 

2278 self.default_kwargs = kw 

2279 

2280 super().__init__(allow_none=allow_none, **kwargs) 

2281 

2282 def validate(self, obj, value): 

2283 assert self.klass is not None 

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

2285 return value 

2286 else: 

2287 self.error(obj, value) 

2288 

2289 def info(self): 

2290 if isinstance(self.klass, str): 

2291 result = add_article(self.klass) 

2292 else: 

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

2294 if self.allow_none: 

2295 result += " or None" 

2296 return result 

2297 

2298 def instance_init(self, obj): 

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

2300 # might be called before all imports are done. 

2301 self._resolve_classes() 

2302 

2303 def _resolve_classes(self): 

2304 if isinstance(self.klass, str): 

2305 self.klass = self._resolve_string(self.klass) 

2306 

2307 def make_dynamic_default(self): 

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

2309 return None 

2310 assert self.klass is not None 

2311 return self.klass( 

2312 *(self.default_args or ()), **(self.default_kwargs or {}) 

2313 ) # type:ignore[operator] 

2314 

2315 def default_value_repr(self): 

2316 return repr(self.make_dynamic_default()) 

2317 

2318 def from_string(self, s): 

2319 return _safe_literal_eval(s) 

2320 

2321 

2322class ForwardDeclaredMixin: 

2323 """ 

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

2325 """ 

2326 

2327 def _resolve_string(self, string): 

2328 """ 

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

2330 our this_class attribute was defined. 

2331 """ 

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

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

2334 

2335 

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

2337 """ 

2338 Forward-declared version of Type. 

2339 """ 

2340 

2341 pass 

2342 

2343 

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

2345 """ 

2346 Forward-declared version of Instance. 

2347 """ 

2348 

2349 pass 

2350 

2351 

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

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

2354 

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

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

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

2358 """ 

2359 

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

2361 

2362 def __init__(self, **kwargs): 

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

2364 

2365 def validate(self, obj, value): 

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

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

2368 # trait. 

2369 assert self.this_class is not None 

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

2371 return value 

2372 else: 

2373 self.error(obj, value) 

2374 

2375 

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

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

2378 

2379 def __init__(self, trait_types, **kwargs): 

2380 """Construct a Union trait. 

2381 

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

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

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

2385 

2386 Parameters 

2387 ---------- 

2388 trait_types : sequence 

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

2390 **kwargs 

2391 Extra kwargs passed to `TraitType` 

2392 

2393 Notes 

2394 ----- 

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

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

2397 

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

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

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

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

2402 ``_literal_from_string_pairs`` in subclasses. 

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

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

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

2406 """ 

2407 self.trait_types = list(trait_types) 

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

2409 super().__init__(**kwargs) 

2410 

2411 def default(self, obj=None): 

2412 default = super().default(obj) 

2413 for trait in self.trait_types: 

2414 if default is Undefined: 

2415 default = trait.default(obj) 

2416 else: 

2417 break 

2418 return default 

2419 

2420 def class_init(self, cls, name): 

2421 for trait_type in reversed(self.trait_types): 

2422 trait_type.class_init(cls, None) 

2423 super().class_init(cls, name) 

2424 

2425 def subclass_init(self, cls): 

2426 for trait_type in reversed(self.trait_types): 

2427 trait_type.subclass_init(cls) 

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

2429 # to opt out of instance_init 

2430 

2431 def validate(self, obj, value): 

2432 with obj.cross_validation_lock: 

2433 for trait_type in self.trait_types: 

2434 try: 

2435 v = trait_type._validate(obj, value) 

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

2437 if self.name is not None: 

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

2439 return v 

2440 except TraitError: 

2441 continue 

2442 self.error(obj, value) 

2443 

2444 def __or__(self, other): 

2445 if isinstance(other, Union): 

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

2447 else: 

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

2449 

2450 def from_string(self, s): 

2451 for trait_type in self.trait_types: 

2452 try: 

2453 v = trait_type.from_string(s) 

2454 return trait_type.validate(None, v) 

2455 except (TraitError, ValueError): 

2456 continue 

2457 return super().from_string(s) 

2458 

2459 

2460# ----------------------------------------------------------------------------- 

2461# Basic TraitTypes implementations/subclasses 

2462# ----------------------------------------------------------------------------- 

2463 

2464 

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

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

2467 

2468 if t.TYPE_CHECKING: 

2469 

2470 @t.overload 

2471 def __init__( 

2472 self: Any, 

2473 default_value: str = ..., 

2474 *, 

2475 allow_none: Literal[False], 

2476 read_only: bool | None = ..., 

2477 help: str | None = ..., 

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

2479 **kwargs: t.Any, 

2480 ): 

2481 ... 

2482 

2483 @t.overload 

2484 def __init__( 

2485 self: Any, 

2486 default_value: str = ..., 

2487 *, 

2488 allow_none: Literal[True], 

2489 read_only: bool | None = ..., 

2490 help: str | None = ..., 

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

2492 **kwargs: t.Any, 

2493 ): 

2494 ... 

2495 

2496 @t.overload 

2497 def __init__( 

2498 self: Any, 

2499 default_value: str = ..., 

2500 *, 

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

2502 help: str | None = ..., 

2503 read_only: bool | None = False, 

2504 config: t.Any = None, 

2505 **kwargs: t.Any, 

2506 ): 

2507 ... 

2508 

2509 def __init__( 

2510 self: Any, 

2511 default_value: str = ..., 

2512 *, 

2513 allow_none: bool | None = False, 

2514 help: str | None = "", 

2515 read_only: bool | None = False, 

2516 config: t.Any = None, 

2517 **kwargs: t.Any, 

2518 ): 

2519 ... 

2520 

2521 @t.overload 

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

2523 ... 

2524 

2525 @t.overload 

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

2527 ... 

2528 

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

2530 ... 

2531 

2532 default_value: t.Any | None = None 

2533 allow_none = True 

2534 info_text = "any value" 

2535 

2536 def subclass_init(self, cls): 

2537 pass # fully opt out of instance_init 

2538 

2539 

2540def _validate_bounds(trait, obj, value): 

2541 """ 

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

2543 

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

2545 TraitError with an error message appropriate for this trait. 

2546 """ 

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

2548 raise TraitError( 

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

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

2551 "specified" 

2552 ) 

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

2554 raise TraitError( 

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

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

2557 "specified" 

2558 ) 

2559 return value 

2560 

2561 

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

2563 

2564 

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

2566 """An int trait.""" 

2567 

2568 default_value = 0 

2569 info_text = "an int" 

2570 

2571 @t.overload 

2572 def __init__( 

2573 self: Int[int, int], 

2574 default_value: int | Sentinel = ..., 

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

2576 read_only: bool | None = ..., 

2577 help: str | None = ..., 

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

2579 **kwargs: t.Any, 

2580 ): 

2581 ... 

2582 

2583 @t.overload 

2584 def __init__( 

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

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

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

2588 read_only: bool | None = ..., 

2589 help: str | None = ..., 

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

2591 **kwargs: t.Any, 

2592 ): 

2593 ... 

2594 

2595 def __init__(self, default_value=Undefined, allow_none=False, **kwargs): 

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

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

2598 super().__init__(default_value=default_value, allow_none=allow_none, **kwargs) 

2599 

2600 def validate(self, obj, value): 

2601 if not isinstance(value, int): 

2602 self.error(obj, value) 

2603 return _validate_bounds(self, obj, value) 

2604 

2605 def from_string(self, s): 

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

2607 return None 

2608 return int(s) 

2609 

2610 def subclass_init(self, cls): 

2611 pass # fully opt out of instance_init 

2612 

2613 

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

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

2616 

2617 if t.TYPE_CHECKING: 

2618 

2619 @t.overload 

2620 def __init__( 

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

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

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

2624 read_only: bool | None = ..., 

2625 help: str | None = ..., 

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

2627 **kwargs: t.Any, 

2628 ): 

2629 ... 

2630 

2631 @t.overload 

2632 def __init__( 

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

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

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

2636 read_only: bool | None = ..., 

2637 help: str | None = ..., 

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

2639 **kwargs: t.Any, 

2640 ): 

2641 ... 

2642 

2643 def __init__(self, default_value=Undefined, allow_none=False, **kwargs): 

2644 ... 

2645 

2646 def validate(self, obj, value): 

2647 try: 

2648 value = int(value) 

2649 except Exception: 

2650 self.error(obj, value) 

2651 return _validate_bounds(self, obj, value) 

2652 

2653 

2654Long, CLong = Int, CInt 

2655Integer = Int 

2656 

2657 

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

2659 """A float trait.""" 

2660 

2661 default_value = 0.0 

2662 info_text = "a float" 

2663 

2664 @t.overload 

2665 def __init__( 

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

2667 default_value: float | Sentinel = ..., 

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

2669 read_only: bool | None = ..., 

2670 help: str | None = ..., 

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

2672 **kwargs: t.Any, 

2673 ): 

2674 ... 

2675 

2676 @t.overload 

2677 def __init__( 

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

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

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

2681 read_only: bool | None = ..., 

2682 help: str | None = ..., 

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

2684 **kwargs: t.Any, 

2685 ): 

2686 ... 

2687 

2688 def __init__(self, default_value=Undefined, allow_none=False, **kwargs): 

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

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

2691 super().__init__(default_value=default_value, allow_none=allow_none, **kwargs) 

2692 

2693 def validate(self, obj, value): 

2694 if isinstance(value, int): 

2695 value = float(value) 

2696 if not isinstance(value, float): 

2697 self.error(obj, value) 

2698 return _validate_bounds(self, obj, value) 

2699 

2700 def from_string(self, s): 

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

2702 return None 

2703 return float(s) 

2704 

2705 def subclass_init(self, cls): 

2706 pass # fully opt out of instance_init 

2707 

2708 

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

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

2711 

2712 if t.TYPE_CHECKING: 

2713 

2714 @t.overload 

2715 def __init__( 

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

2717 default_value: t.Any = ..., 

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

2719 read_only: bool | None = ..., 

2720 help: str | None = ..., 

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

2722 **kwargs: t.Any, 

2723 ): 

2724 ... 

2725 

2726 @t.overload 

2727 def __init__( 

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

2729 default_value: t.Any = ..., 

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

2731 read_only: bool | None = ..., 

2732 help: str | None = ..., 

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

2734 **kwargs: t.Any, 

2735 ): 

2736 ... 

2737 

2738 def __init__(self, default_value=Undefined, allow_none=False, **kwargs): 

2739 ... 

2740 

2741 def validate(self, obj, value): 

2742 try: 

2743 value = float(value) 

2744 except Exception: 

2745 self.error(obj, value) 

2746 return _validate_bounds(self, obj, value) 

2747 

2748 

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

2750 """A trait for complex numbers.""" 

2751 

2752 default_value = 0.0 + 0.0j 

2753 info_text = "a complex number" 

2754 

2755 def validate(self, obj, value): 

2756 if isinstance(value, complex): 

2757 return value 

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

2759 return complex(value) 

2760 self.error(obj, value) 

2761 

2762 def from_string(self, s): 

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

2764 return None 

2765 return complex(s) 

2766 

2767 def subclass_init(self, cls): 

2768 pass # fully opt out of instance_init 

2769 

2770 

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

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

2773 

2774 def validate(self, obj, value): 

2775 try: 

2776 return complex(value) 

2777 except Exception: 

2778 self.error(obj, value) 

2779 

2780 

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

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

2783# we don't have a Str type. 

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

2785 """A trait for byte strings.""" 

2786 

2787 default_value = b"" 

2788 info_text = "a bytes object" 

2789 

2790 def validate(self, obj, value): 

2791 if isinstance(value, bytes): 

2792 return value 

2793 self.error(obj, value) 

2794 

2795 def from_string(self, s): 

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

2797 return None 

2798 if len(s) >= 3: 

2799 # handle deprecated b"string" 

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

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

2802 old_s = s 

2803 s = s[2:-1] 

2804 warn( 

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

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

2807 DeprecationWarning, 

2808 stacklevel=2, 

2809 ) 

2810 break 

2811 return s.encode("utf8") 

2812 

2813 def subclass_init(self, cls): 

2814 pass # fully opt out of instance_init 

2815 

2816 

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

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

2819 

2820 def validate(self, obj, value): 

2821 try: 

2822 return bytes(value) 

2823 except Exception: 

2824 self.error(obj, value) 

2825 

2826 

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

2828 """A trait for unicode strings.""" 

2829 

2830 default_value = "" 

2831 info_text = "a unicode string" 

2832 

2833 if t.TYPE_CHECKING: 

2834 

2835 @t.overload 

2836 def __init__( 

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

2838 default_value: str | Sentinel = ..., 

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

2840 read_only: bool | None = ..., 

2841 help: str | None = ..., 

2842 config: t.Any = ..., 

2843 **kwargs: t.Any, 

2844 ): 

2845 ... 

2846 

2847 @t.overload 

2848 def __init__( 

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

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

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

2852 read_only: bool | None = ..., 

2853 help: str | None = ..., 

2854 config: t.Any = ..., 

2855 **kwargs: t.Any, 

2856 ): 

2857 ... 

2858 

2859 def __init__(self, **kwargs): 

2860 ... 

2861 

2862 def validate(self, obj, value): 

2863 if isinstance(value, str): 

2864 return value 

2865 if isinstance(value, bytes): 

2866 try: 

2867 return value.decode("ascii", "strict") 

2868 except UnicodeDecodeError as e: 

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

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

2871 self.error(obj, value) 

2872 

2873 def from_string(self, s): 

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

2875 return None 

2876 s = os.path.expanduser(s) 

2877 if len(s) >= 2: 

2878 # handle deprecated "1" 

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

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

2881 old_s = s 

2882 s = s[1:-1] 

2883 warn( 

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

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

2886 DeprecationWarning, 

2887 stacklevel=2, 

2888 ) 

2889 return s 

2890 

2891 def subclass_init(self, cls): 

2892 pass # fully opt out of instance_init 

2893 

2894 

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

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

2897 

2898 if t.TYPE_CHECKING: 

2899 

2900 @t.overload 

2901 def __init__( 

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

2903 default_value: str | Sentinel = ..., 

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

2905 read_only: bool | None = ..., 

2906 help: str | None = ..., 

2907 config: t.Any = ..., 

2908 **kwargs: t.Any, 

2909 ): 

2910 ... 

2911 

2912 @t.overload 

2913 def __init__( 

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

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

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

2917 read_only: bool | None = ..., 

2918 help: str | None = ..., 

2919 config: t.Any = ..., 

2920 **kwargs: t.Any, 

2921 ): 

2922 ... 

2923 

2924 def __init__(self, **kwargs): 

2925 ... 

2926 

2927 def validate(self, obj, value): 

2928 try: 

2929 return str(value) 

2930 except Exception: 

2931 self.error(obj, value) 

2932 

2933 

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

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

2936 

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

2938 

2939 info_text = "a valid object identifier in Python" 

2940 

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

2942 

2943 def validate(self, obj, value): 

2944 value = self.coerce_str(obj, value) 

2945 

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

2947 return value 

2948 self.error(obj, value) 

2949 

2950 def from_string(self, s): 

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

2952 return None 

2953 return s 

2954 

2955 

2956class DottedObjectName(ObjectName): 

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

2958 

2959 def validate(self, obj, value): 

2960 value = self.coerce_str(obj, value) 

2961 

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

2963 return value 

2964 self.error(obj, value) 

2965 

2966 

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

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

2969 

2970 default_value = False 

2971 info_text = "a boolean" 

2972 

2973 if t.TYPE_CHECKING: 

2974 

2975 @t.overload 

2976 def __init__( 

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

2978 default_value: bool | Sentinel = ..., 

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

2980 read_only: bool | None = ..., 

2981 help: str | None = ..., 

2982 config: t.Any = ..., 

2983 **kwargs: t.Any, 

2984 ): 

2985 ... 

2986 

2987 @t.overload 

2988 def __init__( 

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

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

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

2992 read_only: bool | None = ..., 

2993 help: str | None = ..., 

2994 config: t.Any = ..., 

2995 **kwargs: t.Any, 

2996 ): 

2997 ... 

2998 

2999 def __init__(self, **kwargs): 

3000 ... 

3001 

3002 def validate(self, obj, value): 

3003 if isinstance(value, bool): 

3004 return value 

3005 elif isinstance(value, int): 

3006 if value == 1: 

3007 return True 

3008 elif value == 0: 

3009 return False 

3010 self.error(obj, value) 

3011 

3012 def from_string(self, s): 

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

3014 return None 

3015 s = s.lower() 

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

3017 return True 

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

3019 return False 

3020 else: 

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

3022 

3023 def subclass_init(self, cls): 

3024 pass # fully opt out of instance_init 

3025 

3026 def argcompleter(self, **kwargs): 

3027 """Completion hints for argcomplete""" 

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

3029 if self.allow_none: 

3030 completions.append("None") 

3031 return completions 

3032 

3033 

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

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

3036 

3037 if t.TYPE_CHECKING: 

3038 

3039 @t.overload 

3040 def __init__( 

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

3042 default_value: bool | Sentinel = ..., 

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

3044 read_only: bool | None = ..., 

3045 help: str | None = ..., 

3046 config: t.Any = ..., 

3047 **kwargs: t.Any, 

3048 ): 

3049 ... 

3050 

3051 @t.overload 

3052 def __init__( 

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

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

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

3056 read_only: bool | None = ..., 

3057 help: str | None = ..., 

3058 config: t.Any = ..., 

3059 **kwargs: t.Any, 

3060 ): 

3061 ... 

3062 

3063 def __init__(self, **kwargs): 

3064 ... 

3065 

3066 def validate(self, obj, value): 

3067 try: 

3068 return bool(value) 

3069 except Exception: 

3070 self.error(obj, value) 

3071 

3072 

3073class Enum(TraitType[G, S]): 

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

3075 

3076 def __init__( 

3077 self: Enum[t.Any, t.Any], values: t.Any, default_value: t.Any = Undefined, **kwargs: t.Any 

3078 ): 

3079 self.values = values 

3080 if kwargs.get("allow_none", False) and default_value is Undefined: 

3081 default_value = None 

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

3083 

3084 def validate(self, obj, value): 

3085 if value in self.values: 

3086 return value 

3087 self.error(obj, value) 

3088 

3089 def _choices_str(self, as_rst=False): 

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

3091 choices = self.values 

3092 if as_rst: 

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

3094 else: 

3095 choices = repr(list(choices)) 

3096 return choices 

3097 

3098 def _info(self, as_rst=False): 

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

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

3101 return f"any of {self._choices_str(as_rst)}{none}" 

3102 

3103 def info(self): 

3104 return self._info(as_rst=False) 

3105 

3106 def info_rst(self): 

3107 return self._info(as_rst=True) 

3108 

3109 def from_string(self, s): 

3110 try: 

3111 return self.validate(None, s) 

3112 except TraitError: 

3113 return _safe_literal_eval(s) 

3114 

3115 def subclass_init(self, cls): 

3116 pass # fully opt out of instance_init 

3117 

3118 def argcompleter(self, **kwargs): 

3119 """Completion hints for argcomplete""" 

3120 return [str(v) for v in self.values] 

3121 

3122 

3123class CaselessStrEnum(Enum[G, S]): 

3124 """An enum of strings where the case should be ignored.""" 

3125 

3126 def __init__( 

3127 self: CaselessStrEnum[t.Any, t.Any], 

3128 values: t.Any, 

3129 default_value: t.Any = Undefined, 

3130 **kwargs: t.Any, 

3131 ): 

3132 super().__init__(values, default_value=default_value, **kwargs) 

3133 

3134 def validate(self, obj, value): 

3135 if not isinstance(value, str): 

3136 self.error(obj, value) 

3137 

3138 for v in self.values: 

3139 if v.lower() == value.lower(): 

3140 return v 

3141 self.error(obj, value) 

3142 

3143 def _info(self, as_rst=False): 

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

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

3146 return f"any of {self._choices_str(as_rst)} (case-insensitive){none}" 

3147 

3148 def info(self): 

3149 return self._info(as_rst=False) 

3150 

3151 def info_rst(self): 

3152 return self._info(as_rst=True) 

3153 

3154 

3155class FuzzyEnum(Enum[G, S]): 

3156 """An case-ignoring enum matching choices by unique prefixes/substrings.""" 

3157 

3158 case_sensitive = False 

3159 #: If True, choices match anywhere in the string, otherwise match prefixes. 

3160 substring_matching = False 

3161 

3162 def __init__( 

3163 self: FuzzyEnum[t.Any, t.Any], 

3164 values: t.Any, 

3165 default_value: t.Any = Undefined, 

3166 case_sensitive: bool = False, 

3167 substring_matching: bool = False, 

3168 **kwargs: t.Any, 

3169 ): 

3170 self.case_sensitive = case_sensitive 

3171 self.substring_matching = substring_matching 

3172 super().__init__(values, default_value=default_value, **kwargs) 

3173 

3174 def validate(self, obj, value): 

3175 if not isinstance(value, str): 

3176 self.error(obj, value) 

3177 

3178 conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower() 

3179 substring_matching = self.substring_matching 

3180 match_func = (lambda v, c: v in c) if substring_matching else (lambda v, c: c.startswith(v)) 

3181 value = conv_func(value) 

3182 choices = self.values 

3183 matches = [match_func(value, conv_func(c)) for c in choices] 

3184 if sum(matches) == 1: 

3185 for v, m in zip(choices, matches): 

3186 if m: 

3187 return v 

3188 

3189 self.error(obj, value) 

3190 

3191 def _info(self, as_rst=False): 

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

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

3194 case = "sensitive" if self.case_sensitive else "insensitive" 

3195 substr = "substring" if self.substring_matching else "prefix" 

3196 return f"any case-{case} {substr} of {self._choices_str(as_rst)}{none}" 

3197 

3198 def info(self): 

3199 return self._info(as_rst=False) 

3200 

3201 def info_rst(self): 

3202 return self._info(as_rst=True) 

3203 

3204 

3205class Container(Instance[T]): 

3206 """An instance of a container (list, set, etc.) 

3207 

3208 To be subclassed by overriding klass. 

3209 """ 

3210 

3211 klass: type[T] | None = None 

3212 _cast_types: t.Any = () 

3213 _valid_defaults = SequenceTypes 

3214 _trait = None 

3215 _literal_from_string_pairs: t.Any = ("[]", "()") 

3216 

3217 @t.overload 

3218 def __init__( 

3219 self: Container[T], 

3220 kind: type[T], 

3221 *, 

3222 allow_none: Literal[False], 

3223 read_only: bool | None = ..., 

3224 help: str | None = ..., 

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

3226 **kwargs: t.Any, 

3227 ): 

3228 ... 

3229 

3230 @t.overload 

3231 def __init__( 

3232 self: Container[T | None], 

3233 kind: type[T], 

3234 *, 

3235 allow_none: Literal[True], 

3236 read_only: bool | None = ..., 

3237 help: str | None = ..., 

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

3239 **kwargs: t.Any, 

3240 ): 

3241 ... 

3242 

3243 @t.overload 

3244 def __init__( 

3245 self: Container[T], 

3246 kind: type[T], 

3247 *, 

3248 help: str = ..., 

3249 read_only: bool = ..., 

3250 config: t.Any = ..., 

3251 trait: t.Any = ..., 

3252 default_value: t.Any = ..., 

3253 **kwargs: t.Any, 

3254 ): 

3255 ... 

3256 

3257 def __init__(self, trait=None, default_value=Undefined, **kwargs): 

3258 """Create a container trait type from a list, set, or tuple. 

3259 

3260 The default value is created by doing ``List(default_value)``, 

3261 which creates a copy of the ``default_value``. 

3262 

3263 ``trait`` can be specified, which restricts the type of elements 

3264 in the container to that TraitType. 

3265 

3266 If only one arg is given and it is not a Trait, it is taken as 

3267 ``default_value``: 

3268 

3269 ``c = List([1, 2, 3])`` 

3270 

3271 Parameters 

3272 ---------- 

3273 trait : TraitType [ optional ] 

3274 the type for restricting the contents of the Container. If unspecified, 

3275 types are not checked. 

3276 default_value : SequenceType [ optional ] 

3277 The default value for the Trait. Must be list/tuple/set, and 

3278 will be cast to the container type. 

3279 allow_none : bool [ default False ] 

3280 Whether to allow the value to be None 

3281 **kwargs : any 

3282 further keys for extensions to the Trait (e.g. config) 

3283 

3284 """ 

3285 

3286 # allow List([values]): 

3287 if trait is not None and default_value is Undefined and not is_trait(trait): 

3288 default_value = trait 

3289 trait = None 

3290 

3291 if default_value is None and not kwargs.get("allow_none", False): 

3292 # improve backward-compatibility for possible subclasses 

3293 # specifying default_value=None as default, 

3294 # keeping 'unspecified' behavior (i.e. empty container) 

3295 warn( 

3296 f"Specifying {self.__class__.__name__}(default_value=None)" 

3297 " for no default is deprecated in traitlets 5.0.5." 

3298 " Use default_value=Undefined", 

3299 DeprecationWarning, 

3300 stacklevel=2, 

3301 ) 

3302 default_value = Undefined 

3303 

3304 if default_value is Undefined: 

3305 args: t.Any = () 

3306 elif default_value is None: 

3307 # default_value back on kwargs for super() to handle 

3308 args = () 

3309 kwargs["default_value"] = None 

3310 elif isinstance(default_value, self._valid_defaults): 

3311 args = (default_value,) 

3312 else: 

3313 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3314 

3315 if is_trait(trait): 

3316 if isinstance(trait, type): 

3317 warn( 

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

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

3320 DeprecationWarning, 

3321 stacklevel=3, 

3322 ) 

3323 self._trait = trait() if isinstance(trait, type) else trait 

3324 elif trait is not None: 

3325 raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait)) 

3326 

3327 super().__init__(klass=self.klass, args=args, **kwargs) 

3328 

3329 def validate(self, obj, value): 

3330 if isinstance(value, self._cast_types): 

3331 assert self.klass is not None 

3332 value = self.klass(value) # type:ignore[call-arg] 

3333 value = super().validate(obj, value) 

3334 if value is None: 

3335 return value 

3336 

3337 value = self.validate_elements(obj, value) 

3338 

3339 return value 

3340 

3341 def validate_elements(self, obj, value): 

3342 validated = [] 

3343 if self._trait is None or isinstance(self._trait, Any): 

3344 return value 

3345 for v in value: 

3346 try: 

3347 v = self._trait._validate(obj, v) 

3348 except TraitError as error: 

3349 self.error(obj, v, error) 

3350 else: 

3351 validated.append(v) 

3352 assert self.klass is not None 

3353 return self.klass(validated) # type:ignore[call-arg] 

3354 

3355 def class_init(self, cls, name): 

3356 if isinstance(self._trait, TraitType): 

3357 self._trait.class_init(cls, None) 

3358 super().class_init(cls, name) 

3359 

3360 def subclass_init(self, cls): 

3361 if isinstance(self._trait, TraitType): 

3362 self._trait.subclass_init(cls) 

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

3364 # to opt out of instance_init 

3365 

3366 def from_string(self, s): 

3367 """Load value from a single string""" 

3368 if not isinstance(s, str): 

3369 raise TraitError(f"Expected string, got {s!r}") 

3370 try: 

3371 test = literal_eval(s) 

3372 except Exception: 

3373 test = None 

3374 return self.validate(None, test) 

3375 

3376 def from_string_list(self, s_list): 

3377 """Return the value from a list of config strings 

3378 

3379 This is where we parse CLI configuration 

3380 """ 

3381 assert self.klass is not None 

3382 if len(s_list) == 1: 

3383 # check for deprecated --Class.trait="['a', 'b', 'c']" 

3384 r = s_list[0] 

3385 if r == "None" and self.allow_none: 

3386 return None 

3387 if len(r) >= 2 and any( 

3388 r.startswith(start) and r.endswith(end) 

3389 for start, end in self._literal_from_string_pairs 

3390 ): 

3391 if self.this_class: 

3392 clsname = self.this_class.__name__ + "." 

3393 else: 

3394 clsname = "" 

3395 assert self.name is not None 

3396 warn( 

3397 "--{0}={1} for containers is deprecated in traitlets 5.0. " 

3398 "You can pass `--{0} item` ... multiple times to add items to a list.".format( 

3399 clsname + self.name, r 

3400 ), 

3401 DeprecationWarning, 

3402 stacklevel=2, 

3403 ) 

3404 return self.klass(literal_eval(r)) # type:ignore[call-arg] 

3405 sig = inspect.signature(self.item_from_string) 

3406 if "index" in sig.parameters: 

3407 item_from_string = self.item_from_string 

3408 else: 

3409 # backward-compat: allow item_from_string to ignore index arg 

3410 def item_from_string(s, index=None): 

3411 return self.item_from_string(s) 

3412 

3413 return self.klass( # type:ignore[call-arg] 

3414 [item_from_string(s, index=idx) for idx, s in enumerate(s_list)] 

3415 ) 

3416 

3417 def item_from_string(self, s, index=None): 

3418 """Cast a single item from a string 

3419 

3420 Evaluated when parsing CLI configuration from a string 

3421 """ 

3422 if self._trait: 

3423 return self._trait.from_string(s) 

3424 else: 

3425 return s 

3426 

3427 

3428class List(Container[t.List[t.Any]]): 

3429 """An instance of a Python list.""" 

3430 

3431 klass = list 

3432 _cast_types: t.Any = (tuple,) 

3433 

3434 def __init__( 

3435 self, 

3436 trait=None, 

3437 default_value=Undefined, 

3438 minlen=0, 

3439 maxlen=sys.maxsize, 

3440 **kwargs, 

3441 ): 

3442 """Create a List trait type from a list, set, or tuple. 

3443 

3444 The default value is created by doing ``list(default_value)``, 

3445 which creates a copy of the ``default_value``. 

3446 

3447 ``trait`` can be specified, which restricts the type of elements 

3448 in the container to that TraitType. 

3449 

3450 If only one arg is given and it is not a Trait, it is taken as 

3451 ``default_value``: 

3452 

3453 ``c = List([1, 2, 3])`` 

3454 

3455 Parameters 

3456 ---------- 

3457 trait : TraitType [ optional ] 

3458 the type for restricting the contents of the Container. 

3459 If unspecified, types are not checked. 

3460 default_value : SequenceType [ optional ] 

3461 The default value for the Trait. Must be list/tuple/set, and 

3462 will be cast to the container type. 

3463 minlen : Int [ default 0 ] 

3464 The minimum length of the input list 

3465 maxlen : Int [ default sys.maxsize ] 

3466 The maximum length of the input list 

3467 """ 

3468 self._minlen = minlen 

3469 self._maxlen = maxlen 

3470 super().__init__(trait=trait, default_value=default_value, **kwargs) 

3471 

3472 def length_error(self, obj, value): 

3473 e = ( 

3474 "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." 

3475 % (self.name, class_of(obj), self._minlen, self._maxlen, value) 

3476 ) 

3477 raise TraitError(e) 

3478 

3479 def validate_elements(self, obj, value): 

3480 length = len(value) 

3481 if length < self._minlen or length > self._maxlen: 

3482 self.length_error(obj, value) 

3483 

3484 return super().validate_elements(obj, value) 

3485 

3486 def set(self, obj, value): 

3487 if isinstance(value, str): 

3488 return super().set(obj, [value]) 

3489 else: 

3490 return super().set(obj, value) 

3491 

3492 

3493class Set(List): 

3494 """An instance of a Python set.""" 

3495 

3496 klass = set # type:ignore[assignment] 

3497 _cast_types = (tuple, list) 

3498 

3499 _literal_from_string_pairs = ("[]", "()", "{}") 

3500 

3501 # Redefine __init__ just to make the docstring more accurate. 

3502 def __init__( 

3503 self, 

3504 trait=None, 

3505 default_value=Undefined, 

3506 minlen=0, 

3507 maxlen=sys.maxsize, 

3508 **kwargs, 

3509 ): 

3510 """Create a Set trait type from a list, set, or tuple. 

3511 

3512 The default value is created by doing ``set(default_value)``, 

3513 which creates a copy of the ``default_value``. 

3514 

3515 ``trait`` can be specified, which restricts the type of elements 

3516 in the container to that TraitType. 

3517 

3518 If only one arg is given and it is not a Trait, it is taken as 

3519 ``default_value``: 

3520 

3521 ``c = Set({1, 2, 3})`` 

3522 

3523 Parameters 

3524 ---------- 

3525 trait : TraitType [ optional ] 

3526 the type for restricting the contents of the Container. 

3527 If unspecified, types are not checked. 

3528 default_value : SequenceType [ optional ] 

3529 The default value for the Trait. Must be list/tuple/set, and 

3530 will be cast to the container type. 

3531 minlen : Int [ default 0 ] 

3532 The minimum length of the input list 

3533 maxlen : Int [ default sys.maxsize ] 

3534 The maximum length of the input list 

3535 """ 

3536 super().__init__(trait, default_value, minlen, maxlen, **kwargs) 

3537 

3538 def default_value_repr(self): 

3539 # Ensure default value is sorted for a reproducible build 

3540 list_repr = repr(sorted(self.make_dynamic_default())) 

3541 if list_repr == "[]": 

3542 return "set()" 

3543 return "{" + list_repr[1:-1] + "}" 

3544 

3545 

3546class Tuple(Container[t.Tuple[t.Any, ...]]): 

3547 """An instance of a Python tuple.""" 

3548 

3549 klass = tuple 

3550 _cast_types = (list,) 

3551 

3552 def __init__(self, *traits, **kwargs): 

3553 """Create a tuple from a list, set, or tuple. 

3554 

3555 Create a fixed-type tuple with Traits: 

3556 

3557 ``t = Tuple(Int(), Str(), CStr())`` 

3558 

3559 would be length 3, with Int,Str,CStr for each element. 

3560 

3561 If only one arg is given and it is not a Trait, it is taken as 

3562 default_value: 

3563 

3564 ``t = Tuple((1, 2, 3))`` 

3565 

3566 Otherwise, ``default_value`` *must* be specified by keyword. 

3567 

3568 Parameters 

3569 ---------- 

3570 *traits : TraitTypes [ optional ] 

3571 the types for restricting the contents of the Tuple. If unspecified, 

3572 types are not checked. If specified, then each positional argument 

3573 corresponds to an element of the tuple. Tuples defined with traits 

3574 are of fixed length. 

3575 default_value : SequenceType [ optional ] 

3576 The default value for the Tuple. Must be list/tuple/set, and 

3577 will be cast to a tuple. If ``traits`` are specified, 

3578 ``default_value`` must conform to the shape and type they specify. 

3579 **kwargs 

3580 Other kwargs passed to `Container` 

3581 """ 

3582 default_value = kwargs.pop("default_value", Undefined) 

3583 # allow Tuple((values,)): 

3584 if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]): 

3585 default_value = traits[0] 

3586 traits = () 

3587 

3588 if default_value is None and not kwargs.get("allow_none", False): 

3589 # improve backward-compatibility for possible subclasses 

3590 # specifying default_value=None as default, 

3591 # keeping 'unspecified' behavior (i.e. empty container) 

3592 warn( 

3593 f"Specifying {self.__class__.__name__}(default_value=None)" 

3594 " for no default is deprecated in traitlets 5.0.5." 

3595 " Use default_value=Undefined", 

3596 DeprecationWarning, 

3597 stacklevel=2, 

3598 ) 

3599 default_value = Undefined 

3600 

3601 if default_value is Undefined: 

3602 args: t.Any = () 

3603 elif default_value is None: 

3604 # default_value back on kwargs for super() to handle 

3605 args = () 

3606 kwargs["default_value"] = None 

3607 elif isinstance(default_value, self._valid_defaults): 

3608 args = (default_value,) 

3609 else: 

3610 raise TypeError(f"default value of {self.__class__.__name__} was {default_value}") 

3611 

3612 self._traits = [] 

3613 for trait in traits: 

3614 if isinstance(trait, type): 

3615 warn( 

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

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

3618 DeprecationWarning, 

3619 stacklevel=2, 

3620 ) 

3621 trait = trait() 

3622 self._traits.append(trait) 

3623 

3624 if self._traits and (default_value is None or default_value is Undefined): 

3625 # don't allow default to be an empty container if length is specified 

3626 args = None 

3627 super(Container, self).__init__(klass=self.klass, args=args, **kwargs) 

3628 

3629 def item_from_string(self, s, index): 

3630 """Cast a single item from a string 

3631 

3632 Evaluated when parsing CLI configuration from a string 

3633 """ 

3634 if not self._traits or index >= len(self._traits): 

3635 # return s instead of raising index error 

3636 # length errors will be raised later on validation 

3637 return s 

3638 return self._traits[index].from_string(s) 

3639 

3640 def validate_elements(self, obj, value): 

3641 if not self._traits: 

3642 # nothing to validate 

3643 return value 

3644 if len(value) != len(self._traits): 

3645 e = ( 

3646 "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." 

3647 % (self.name, class_of(obj), len(self._traits), repr_type(value)) 

3648 ) 

3649 raise TraitError(e) 

3650 

3651 validated = [] 

3652 for trait, v in zip(self._traits, value): 

3653 try: 

3654 v = trait._validate(obj, v) 

3655 except TraitError as error: 

3656 self.error(obj, v, error) 

3657 else: 

3658 validated.append(v) 

3659 return tuple(validated) 

3660 

3661 def class_init(self, cls, name): 

3662 for trait in self._traits: 

3663 if isinstance(trait, TraitType): 

3664 trait.class_init(cls, None) 

3665 super(Container, self).class_init(cls, name) 

3666 

3667 def subclass_init(self, cls): 

3668 for trait in self._traits: 

3669 if isinstance(trait, TraitType): 

3670 trait.subclass_init(cls) 

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

3672 # to opt out of instance_init 

3673 

3674 

3675class Dict(Instance[t.Dict[t.Any, t.Any]]): 

3676 """An instance of a Python dict. 

3677 

3678 One or more traits can be passed to the constructor 

3679 to validate the keys and/or values of the dict. 

3680 If you need more detailed validation, 

3681 you may use a custom validator method. 

3682 

3683 .. versionchanged:: 5.0 

3684 Added key_trait for validating dict keys. 

3685 

3686 .. versionchanged:: 5.0 

3687 Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``. 

3688 """ 

3689 

3690 _value_trait = None 

3691 _key_trait = None 

3692 

3693 def __init__( 

3694 self, 

3695 value_trait=None, 

3696 per_key_traits=None, 

3697 key_trait=None, 

3698 default_value=Undefined, 

3699 **kwargs, 

3700 ): 

3701 """Create a dict trait type from a Python dict. 

3702 

3703 The default value is created by doing ``dict(default_value)``, 

3704 which creates a copy of the ``default_value``. 

3705 

3706 Parameters 

3707 ---------- 

3708 value_trait : TraitType [ optional ] 

3709 The specified trait type to check and use to restrict the values of 

3710 the dict. If unspecified, values are not checked. 

3711 per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ] 

3712 A Python dictionary containing the types that are valid for 

3713 restricting the values of the dict on a per-key basis. 

3714 Each value in this dict should be a Trait for validating 

3715 key_trait : TraitType [ optional, keyword-only ] 

3716 The type for restricting the keys of the dict. If 

3717 unspecified, the types of the keys are not checked. 

3718 default_value : SequenceType [ optional, keyword-only ] 

3719 The default value for the Dict. Must be dict, tuple, or None, and 

3720 will be cast to a dict if not None. If any key or value traits are specified, 

3721 the `default_value` must conform to the constraints. 

3722 

3723 Examples 

3724 -------- 

3725 a dict whose values must be text 

3726 >>> d = Dict(Unicode()) 

3727 

3728 d2['n'] must be an integer 

3729 d2['s'] must be text 

3730 >>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()}) 

3731 

3732 d3's keys must be text 

3733 d3's values must be integers 

3734 >>> d3 = Dict(value_trait=Integer(), key_trait=Unicode()) 

3735 

3736 """ 

3737 

3738 # handle deprecated keywords 

3739 trait = kwargs.pop("trait", None) 

3740 if trait is not None: 

3741 if value_trait is not None: 

3742 raise TypeError( 

3743 "Found a value for both `value_trait` and its deprecated alias `trait`." 

3744 ) 

3745 value_trait = trait 

3746 warn( 

3747 "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead", 

3748 DeprecationWarning, 

3749 stacklevel=2, 

3750 ) 

3751 traits = kwargs.pop("traits", None) 

3752 if traits is not None: 

3753 if per_key_traits is not None: 

3754 raise TypeError( 

3755 "Found a value for both `per_key_traits` and its deprecated alias `traits`." 

3756 ) 

3757 per_key_traits = traits 

3758 warn( 

3759 "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead", 

3760 DeprecationWarning, 

3761 stacklevel=2, 

3762 ) 

3763 

3764 # Handling positional arguments 

3765 if default_value is Undefined and value_trait is not None: 

3766 if not is_trait(value_trait): 

3767 default_value = value_trait 

3768 value_trait = None 

3769 

3770 if key_trait is None and per_key_traits is not None: 

3771 if is_trait(per_key_traits): 

3772 key_trait = per_key_traits 

3773 per_key_traits = None 

3774 

3775 # Handling default value 

3776 if default_value is Undefined: 

3777 default_value = {} 

3778 if default_value is None: 

3779 args: t.Any = None 

3780 elif isinstance(default_value, dict): 

3781 args = (default_value,) 

3782 elif isinstance(default_value, SequenceTypes): 

3783 args = (default_value,) 

3784 else: 

3785 raise TypeError("default value of Dict was %s" % default_value) 

3786 

3787 # Case where a type of TraitType is provided rather than an instance 

3788 if is_trait(value_trait): 

3789 if isinstance(value_trait, type): 

3790 warn( 

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

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

3793 DeprecationWarning, 

3794 stacklevel=2, 

3795 ) 

3796 value_trait = value_trait() 

3797 self._value_trait = value_trait 

3798 elif value_trait is not None: 

3799 raise TypeError( 

3800 "`value_trait` must be a Trait or None, got %s" % repr_type(value_trait) 

3801 ) 

3802 

3803 if is_trait(key_trait): 

3804 if isinstance(key_trait, type): 

3805 warn( 

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

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

3808 DeprecationWarning, 

3809 stacklevel=2, 

3810 ) 

3811 key_trait = key_trait() 

3812 self._key_trait = key_trait 

3813 elif key_trait is not None: 

3814 raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait)) 

3815 

3816 self._per_key_traits = per_key_traits 

3817 

3818 super().__init__(klass=dict, args=args, **kwargs) 

3819 

3820 def element_error(self, obj, element, validator, side="Values"): 

3821 e = ( 

3822 side 

3823 + f" of the '{self.name}' trait of {class_of(obj)} instance must be {validator.info()}, but a value of {repr_type(element)} was specified." 

3824 ) 

3825 raise TraitError(e) 

3826 

3827 def validate(self, obj, value): 

3828 value = super().validate(obj, value) 

3829 if value is None: 

3830 return value 

3831 value = self.validate_elements(obj, value) 

3832 return value 

3833 

3834 def validate_elements(self, obj, value): 

3835 per_key_override = self._per_key_traits or {} 

3836 key_trait = self._key_trait 

3837 value_trait = self._value_trait 

3838 if not (key_trait or value_trait or per_key_override): 

3839 return value 

3840 

3841 validated = {} 

3842 for key in value: 

3843 v = value[key] 

3844 if key_trait: 

3845 try: 

3846 key = key_trait._validate(obj, key) 

3847 except TraitError: 

3848 self.element_error(obj, key, key_trait, "Keys") 

3849 active_value_trait = per_key_override.get(key, value_trait) 

3850 if active_value_trait: 

3851 try: 

3852 v = active_value_trait._validate(obj, v) 

3853 except TraitError: 

3854 self.element_error(obj, v, active_value_trait, "Values") 

3855 validated[key] = v 

3856 

3857 return self.klass(validated) # type:ignore 

3858 

3859 def class_init(self, cls, name): 

3860 if isinstance(self._value_trait, TraitType): 

3861 self._value_trait.class_init(cls, None) 

3862 if isinstance(self._key_trait, TraitType): 

3863 self._key_trait.class_init(cls, None) 

3864 if self._per_key_traits is not None: 

3865 for trait in self._per_key_traits.values(): 

3866 trait.class_init(cls, None) 

3867 super().class_init(cls, name) 

3868 

3869 def subclass_init(self, cls): 

3870 if isinstance(self._value_trait, TraitType): 

3871 self._value_trait.subclass_init(cls) 

3872 if isinstance(self._key_trait, TraitType): 

3873 self._key_trait.subclass_init(cls) 

3874 if self._per_key_traits is not None: 

3875 for trait in self._per_key_traits.values(): 

3876 trait.subclass_init(cls) 

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

3878 # to opt out of instance_init 

3879 

3880 def from_string(self, s): 

3881 """Load value from a single string""" 

3882 if not isinstance(s, str): 

3883 raise TypeError(f"from_string expects a string, got {s!r} of type {type(s)}") 

3884 try: 

3885 return self.from_string_list([s]) 

3886 except Exception: 

3887 test = _safe_literal_eval(s) 

3888 if isinstance(test, dict): 

3889 return test 

3890 raise 

3891 

3892 def from_string_list(self, s_list): 

3893 """Return a dict from a list of config strings. 

3894 

3895 This is where we parse CLI configuration. 

3896 

3897 Each item should have the form ``"key=value"``. 

3898 

3899 item parsing is done in :meth:`.item_from_string`. 

3900 """ 

3901 if len(s_list) == 1 and s_list[0] == "None" and self.allow_none: 

3902 return None 

3903 if len(s_list) == 1 and s_list[0].startswith("{") and s_list[0].endswith("}"): 

3904 warn( 

3905 f"--{self.name}={s_list[0]} for dict-traits is deprecated in traitlets 5.0. " 

3906 f"You can pass --{self.name} <key=value> ... multiple times to add items to a dict.", 

3907 DeprecationWarning, 

3908 stacklevel=2, 

3909 ) 

3910 

3911 return literal_eval(s_list[0]) 

3912 

3913 combined = {} 

3914 for d in [self.item_from_string(s) for s in s_list]: 

3915 combined.update(d) 

3916 return combined 

3917 

3918 def item_from_string(self, s): 

3919 """Cast a single-key dict from a string. 

3920 

3921 Evaluated when parsing CLI configuration from a string. 

3922 

3923 Dicts expect strings of the form key=value. 

3924 

3925 Returns a one-key dictionary, 

3926 which will be merged in :meth:`.from_string_list`. 

3927 """ 

3928 

3929 if "=" not in s: 

3930 raise TraitError( 

3931 f"'{self.__class__.__name__}' options must have the form 'key=value', got {s!r}" 

3932 ) 

3933 key, value = s.split("=", 1) 

3934 

3935 # cast key with key trait, if defined 

3936 if self._key_trait: 

3937 key = self._key_trait.from_string(key) 

3938 

3939 # cast value with value trait, if defined (per-key or global) 

3940 value_trait = (self._per_key_traits or {}).get(key, self._value_trait) 

3941 if value_trait: 

3942 value = value_trait.from_string(value) 

3943 return {key: value} 

3944 

3945 

3946class TCPAddress(TraitType[G, S]): 

3947 """A trait for an (ip, port) tuple. 

3948 

3949 This allows for both IPv4 IP addresses as well as hostnames. 

3950 """ 

3951 

3952 default_value = ("127.0.0.1", 0) 

3953 info_text = "an (ip, port) tuple" 

3954 

3955 if t.TYPE_CHECKING: 

3956 

3957 @t.overload 

3958 def __init__( 

3959 self: TCPAddress[tuple[str, int], tuple[str, int]], 

3960 default_value: bool | Sentinel = ..., 

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

3962 read_only: bool | None = ..., 

3963 help: str | None = ..., 

3964 config: t.Any = ..., 

3965 **kwargs: t.Any, 

3966 ): 

3967 ... 

3968 

3969 @t.overload 

3970 def __init__( 

3971 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None], 

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

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

3974 read_only: bool | None = ..., 

3975 help: str | None = ..., 

3976 config: t.Any = ..., 

3977 **kwargs: t.Any, 

3978 ): 

3979 ... 

3980 

3981 def __init__( 

3982 self: TCPAddress[tuple[str, int] | None, tuple[str, int] | None] 

3983 | TCPAddress[tuple[str, int], tuple[str, int]], 

3984 default_value: bool | None | Sentinel = Undefined, 

3985 allow_none: Literal[True, False] = False, 

3986 read_only: bool | None = None, 

3987 help: str | None = None, 

3988 config: t.Any = None, 

3989 **kwargs: t.Any, 

3990 ): 

3991 ... 

3992 

3993 def validate(self, obj, value): 

3994 if isinstance(value, tuple): 

3995 if len(value) == 2: 

3996 if isinstance(value[0], str) and isinstance(value[1], int): 

3997 port = value[1] 

3998 if port >= 0 and port <= 65535: 

3999 return value 

4000 self.error(obj, value) 

4001 

4002 def from_string(self, s): 

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

4004 return None 

4005 if ":" not in s: 

4006 raise ValueError("Require `ip:port`, got %r" % s) 

4007 ip, port = s.split(":", 1) 

4008 port = int(port) 

4009 return (ip, port) 

4010 

4011 

4012class CRegExp(TraitType["re.Pattern[t.Any]", t.Union["re.Pattern[t.Any]", str]]): 

4013 """A casting compiled regular expression trait. 

4014 

4015 Accepts both strings and compiled regular expressions. The resulting 

4016 attribute will be a compiled regular expression.""" 

4017 

4018 info_text = "a regular expression" 

4019 

4020 def validate(self, obj, value): 

4021 try: 

4022 return re.compile(value) 

4023 except Exception: 

4024 self.error(obj, value) 

4025 

4026 

4027class UseEnum(TraitType[t.Any, t.Any]): 

4028 """Use a Enum class as model for the data type description. 

4029 Note that if no default-value is provided, the first enum-value is used 

4030 as default-value. 

4031 

4032 .. sourcecode:: python 

4033 

4034 # -- SINCE: Python 3.4 (or install backport: pip install enum34) 

4035 import enum 

4036 from traitlets import HasTraits, UseEnum 

4037 

4038 class Color(enum.Enum): 

4039 red = 1 # -- IMPLICIT: default_value 

4040 blue = 2 

4041 green = 3 

4042 

4043 class MyEntity(HasTraits): 

4044 color = UseEnum(Color, default_value=Color.blue) 

4045 

4046 entity = MyEntity(color=Color.red) 

4047 entity.color = Color.green # USE: Enum-value (preferred) 

4048 entity.color = "green" # USE: name (as string) 

4049 entity.color = "Color.green" # USE: scoped-name (as string) 

4050 entity.color = 3 # USE: number (as int) 

4051 assert entity.color is Color.green 

4052 """ 

4053 

4054 default_value: enum.Enum | None = None 

4055 info_text = "Trait type adapter to a Enum class" 

4056 

4057 def __init__(self, enum_class, default_value=None, **kwargs): 

4058 assert issubclass(enum_class, enum.Enum), "REQUIRE: enum.Enum, but was: %r" % enum_class 

4059 allow_none = kwargs.get("allow_none", False) 

4060 if default_value is None and not allow_none: 

4061 default_value = next(iter(enum_class.__members__.values())) 

4062 super().__init__(default_value=default_value, **kwargs) 

4063 self.enum_class = enum_class 

4064 self.name_prefix = enum_class.__name__ + "." 

4065 

4066 def select_by_number(self, value, default=Undefined): 

4067 """Selects enum-value by using its number-constant.""" 

4068 assert isinstance(value, int) 

4069 enum_members = self.enum_class.__members__ 

4070 for enum_item in enum_members.values(): 

4071 if enum_item.value == value: 

4072 return enum_item 

4073 # -- NOT FOUND: 

4074 return default 

4075 

4076 def select_by_name(self, value, default=Undefined): 

4077 """Selects enum-value by using its name or scoped-name.""" 

4078 assert isinstance(value, str) 

4079 if value.startswith(self.name_prefix): 

4080 # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" 

4081 value = value.replace(self.name_prefix, "", 1) 

4082 return self.enum_class.__members__.get(value, default) 

4083 

4084 def validate(self, obj, value): 

4085 if isinstance(value, self.enum_class): 

4086 return value 

4087 elif isinstance(value, int): 

4088 # -- CONVERT: number => enum_value (item) 

4089 value2 = self.select_by_number(value) 

4090 if value2 is not Undefined: 

4091 return value2 

4092 elif isinstance(value, str): 

4093 # -- CONVERT: name or scoped_name (as string) => enum_value (item) 

4094 value2 = self.select_by_name(value) 

4095 if value2 is not Undefined: 

4096 return value2 

4097 elif value is None: 

4098 if self.allow_none: 

4099 return None 

4100 else: 

4101 return self.default_value 

4102 self.error(obj, value) 

4103 

4104 def _choices_str(self, as_rst=False): 

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

4106 choices = self.enum_class.__members__.keys() 

4107 if as_rst: 

4108 return "|".join("``%r``" % x for x in choices) 

4109 else: 

4110 return repr(list(choices)) # Listify because py3.4- prints odict-class 

4111 

4112 def _info(self, as_rst=False): 

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

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

4115 return f"any of {self._choices_str(as_rst)}{none}" 

4116 

4117 def info(self): 

4118 return self._info(as_rst=False) 

4119 

4120 def info_rst(self): 

4121 return self._info(as_rst=True) 

4122 

4123 

4124class Callable(TraitType[t.Callable[..., t.Any], t.Callable[..., t.Any]]): 

4125 """A trait which is callable. 

4126 

4127 Notes 

4128 ----- 

4129 Classes are callable, as are instances 

4130 with a __call__() method.""" 

4131 

4132 info_text = "a callable" 

4133 

4134 def validate(self, obj, value): 

4135 if callable(value): 

4136 return value 

4137 else: 

4138 self.error(obj, value)