| 1011 | """A metaclass for HasTraits.""" |
| 1012 | |
| 1013 | def setup_class(cls: MetaHasTraits, classdict: dict[str, t.Any]) -> None: |
| 1014 | # for only the current class |
| 1015 | cls._trait_default_generators: dict[str, t.Any] = {} |
| 1016 | # also looking at base classes |
| 1017 | cls._all_trait_default_generators = {} |
| 1018 | cls._traits = {} |
| 1019 | cls._static_immutable_initial_values = {} |
| 1020 | |
| 1021 | super().setup_class(classdict) |
| 1022 | |
| 1023 | mro = cls.mro() |
| 1024 | |
| 1025 | for name in dir(cls): |
| 1026 | # Some descriptors raise AttributeError like zope.interface's |
| 1027 | # __provides__ attributes even though they exist. This causes |
| 1028 | # AttributeErrors even though they are listed in dir(cls). |
| 1029 | try: |
| 1030 | value = getattr(cls, name) |
| 1031 | except AttributeError: |
| 1032 | continue |
| 1033 | if isinstance(value, TraitType): |
| 1034 | cls._traits[name] = value |
| 1035 | trait = value |
| 1036 | default_method_name = "_%s_default" % name |
| 1037 | mro_trait = mro |
| 1038 | try: |
| 1039 | mro_trait = mro[: mro.index(trait.this_class) + 1] # type:ignore[arg-type] |
| 1040 | except ValueError: |
| 1041 | # this_class not in mro |
| 1042 | pass |
| 1043 | for c in mro_trait: |
| 1044 | if default_method_name in c.__dict__: |
| 1045 | cls._all_trait_default_generators[name] = c.__dict__[default_method_name] |
| 1046 | break |
| 1047 | if name in c.__dict__.get("_trait_default_generators", {}): |
| 1048 | cls._all_trait_default_generators[name] = c._trait_default_generators[name] # type: ignore[attr-defined] |
| 1049 | break |
| 1050 | else: |
| 1051 | # We don't have a dynamic default generator using @default etc. |
| 1052 | # Now if the default value is not dynamic and immutable (string, number) |
| 1053 | # and does not require any validation, we keep them in a dict |
| 1054 | # of initial values to speed up instance creation. |
| 1055 | # This is a very specific optimization, but a very common scenario in |
| 1056 | # for instance ipywidgets. |
| 1057 | none_ok = trait.default_value is None and trait.allow_none |
| 1058 | if ( |
| 1059 | type(trait) in [CInt, Int] |
| 1060 | and trait.min is None # type: ignore[attr-defined] |
| 1061 | and trait.max is None # type: ignore[attr-defined] |
| 1062 | and (isinstance(trait.default_value, int) or none_ok) |
| 1063 | ): |
| 1064 | cls._static_immutable_initial_values[name] = trait.default_value |
| 1065 | elif ( |
| 1066 | type(trait) in [CFloat, Float] |
| 1067 | and trait.min is None # type: ignore[attr-defined] |
| 1068 | and trait.max is None # type: ignore[attr-defined] |
| 1069 | and (isinstance(trait.default_value, float) or none_ok) |
| 1070 | ): |