Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ You can also add callbacks to a trait dynamically:

If a trait attribute with a dynamic default value has another value set
before it is used, the default will not be calculated.
Any callbacks on that trait will will fire, and *old_value* will be ``None``.
Any callbacks on that trait will fire, and *old_value* will be the
trait type's static default (e.g. ``''`` for :class:`Unicode`), or
``traitlets.Undefined`` for trait types whose default can only be
computed dynamically (e.g. containers and :class:`Instance`).

Validating proposed changes
---------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ subclass

# Sample configurable:
from traitlets.config.configurable import Configurable
from traitlets import Int, Float, Unicode, Bool
from traitlets import Integer, Float, Unicode, Bool


class School(Configurable):
Expand Down
2 changes: 1 addition & 1 deletion docs/source/migration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,4 @@ which automatically shims the deprecated signature into the new signature:
@observe("path")
@observe_compat # <- this allows super()._path_changed in subclasses to work with the old signature.
def _path_changed(self, change):
self.prefix = os.path.dirname(change["value"])
self.prefix = os.path.dirname(change["new"])
21 changes: 14 additions & 7 deletions docs/source/trait_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,37 @@ Numbers

.. autoclass:: Integer

An integer trait. On Python 2, this automatically uses the ``int`` or
``long`` types as necessary.
An integer trait.

.. class:: Int
.. class:: Long

On Python 2, these are traitlets for values where the ``int`` and ``long``
types are not interchangeable. On Python 3, they are both aliases for
:class:`Integer`.
.. deprecated:: 5.16
Use :class:`Integer` instead.

In almost all situations, you should use :class:`Integer` instead of these.
These were traitlets for values where the Python 2 ``int`` and ``long``
types were not interchangeable. Now they are both deprecated aliases for
:class:`Integer` and emit a :exc:`DeprecationWarning` when used.

.. autoclass:: Float

.. autoclass:: Complex

.. class:: CInt
CLong
CFloat
CComplex

Casting variants of the above. When a value is assigned to the attribute,
these will attempt to convert it by calling e.g. ``value = int(value)``.

.. class:: CLong

.. deprecated:: 5.16
Use :class:`CInt` instead.

A deprecated alias for :class:`CInt`; emits a :exc:`DeprecationWarning`
when used.

Strings
-------

Expand Down
13 changes: 7 additions & 6 deletions examples/myapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@
to set the following options:

* ``config``: set to ``True`` to make the attribute configurable.
* ``shortname``: by default, configurable attributes are set using the syntax
"Classname.attributename". At the command line, this is a bit verbose, so
we allow "shortnames" to be declared. Setting a shortname is optional, but
when you do this, you can set the option at the command line using the
syntax: "shortname=value".
* ``help``: set the help string to display a help message when the ``-h``
option is given at the command line. The help string should be valid ReST.

By default, configurable attributes are set at the command line using the
syntax "--Classname.attributename=value". This is a bit verbose, so an
Application can declare ``aliases``, mapping a short name to a
"Classname.attributename" (see MyApp below); the option can then be set
with "--alias value".

When the config attribute of an Application is updated, it will fire all of
the trait's events for all of the config=True attributes.
"""
Expand All @@ -51,7 +52,7 @@ class Foo(Configurable):

i = Int(0, help="The integer i.").tag(config=True)
j = Int(1, help="The integer j.").tag(config=True)
name = Unicode("Brian", help="First name.").tag(config=True, shortname="B")
name = Unicode("Brian", help="First name.").tag(config=True)
mode = Enum(values=["on", "off", "other"], default_value="on").tag(config=True)

def __init__(self, **kwargs):
Expand Down
8 changes: 2 additions & 6 deletions tests/config/test_configurable.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ class MyConfigurable(Configurable):
The integer b.
Current: 4.0"""

# On Python 3, the Integer trait is a synonym for Int
mc_help = mc_help.replace("<Integer>", "<Int>")
mc_help_inst = mc_help_inst.replace("<Integer>", "<Int>")


class Foo(Configurable):
a = Integer(0, help="The integer a.").tag(config=True)
Expand All @@ -76,7 +72,7 @@ class Bar(Foo):

foo_help = """Foo(Configurable) options
-------------------------
--Foo.a=<Int>
--Foo.a=<Integer>
The integer a.
Default: 0
--Foo.b=<Unicode>
Expand All @@ -88,7 +84,7 @@ class Bar(Foo):

bar_help = """Bar(Foo) options
----------------
--Bar.a=<Int>
--Bar.a=<Integer>
The integer a.
Default: 0
--Bar.bdict <key-1>=<value-1>...
Expand Down
26 changes: 19 additions & 7 deletions tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,25 +346,37 @@ def test_trait_types_deprecated(self):
with expected_warnings(["Traits should be given as instances"]):

class C(HasTraits):
t = Int
t = Integer

def test_trait_types_list_deprecated(self):
with expected_warnings(["Traits should be given as instances"]):

class C(HasTraits):
t = List(Int)
t = List(Integer)

def test_trait_types_tuple_deprecated(self):
with expected_warnings(["Traits should be given as instances"]):

class C(HasTraits):
t = Tuple(Int)
t = Tuple(Integer)

def test_trait_types_dict_deprecated(self):
with expected_warnings(["Traits should be given as instances"]):

class C(HasTraits):
t = Dict(Int)
t = Dict(Integer)

def test_int_long_deprecated_aliases(self):
for cls, replacement in [(Int, "Integer"), (Long, "Integer"), (CLong, "CInt")]:
with expected_warnings([f"`{cls.__name__}` trait is deprecated"]):
trait = cls(3)
# deprecated aliases still behave like their replacement
assert isinstance(trait, Integer)

class C(HasTraits):
x = trait

assert C().x == 3


class TestHasDescriptorsMeta(TestCase):
Expand Down Expand Up @@ -841,7 +853,7 @@ def test_trait_metadata_deprecated(self):
with expected_warnings([r"metadata should be set using the \.tag\(\) method"]):

class A(HasTraits):
i = Int(config_key="MY_VALUE")
i = Integer(config_key="MY_VALUE")

a = A()
self.assertEqual(a.trait_metadata("i", "config_key"), "MY_VALUE")
Expand Down Expand Up @@ -890,9 +902,9 @@ def test_traits_metadata_deprecated(self):
with expected_warnings([r"metadata should be set using the \.tag\(\) method"] * 2):

class A(HasTraits):
i = Int(config_key="VALUE1", other_thing="VALUE2")
i = Integer(config_key="VALUE1", other_thing="VALUE2")
f = Float(config_key="VALUE3", other_thing="VALUE2")
j = Int(0)
j = Integer(0)

a = A()
self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j))
Expand Down
5 changes: 0 additions & 5 deletions traitlets/config/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,11 +832,6 @@ def __init__(
Dict of flags to full traitlets names for CLI parsing
log
Passed to `ConfigLoader`

Returns
-------
config : Config
The resulting Config object.
"""
classes = classes or []
super(CommandLineConfigLoader, self).__init__(log=log)
Expand Down
97 changes: 85 additions & 12 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,8 +882,8 @@ def tag(self, **metadata: t.Any) -> Self:

Examples
--------
>>> Int(0).tag(config=True, sync=True)
<traitlets.traitlets.Int object at ...>
>>> Integer(0).tag(config=True, sync=True)
<traitlets.traitlets.Integer object at ...>

"""
maybe_constructor_keywords = set(metadata.keys()).intersection(
Expand Down Expand Up @@ -1058,7 +1058,7 @@ def setup_class(cls: MetaHasTraits, classdict: dict[str, t.Any]) -> None:
# for instance ipywidgets.
none_ok = trait.default_value is None and trait.allow_none
if (
type(trait) in [CInt, Int]
type(trait) in [CInt, Integer, Int, Long, CLong]
and trait.min is None # type: ignore[attr-defined]
and trait.max is None # type: ignore[attr-defined]
and (isinstance(trait.default_value, int) or none_ok)
Expand Down Expand Up @@ -2566,7 +2566,7 @@ def subclass_init(self, cls: type[t.Any]) -> None:


def _validate_bounds(
trait: Int[t.Any, t.Any] | Float[t.Any, t.Any], obj: t.Any, value: t.Any
trait: Integer[t.Any, t.Any] | Float[t.Any, t.Any], obj: t.Any, value: t.Any
) -> t.Any:
"""
Validate that a number to be applied to a trait is between bounds.
Expand All @@ -2592,15 +2592,15 @@ def _validate_bounds(
# I = t.TypeVar('I', t.Optional[int], int)


class Int(TraitType[G, S]):
"""An int trait."""
class Integer(TraitType[G, S]):
"""An integer trait."""

default_value = 0
info_text = "an int"

@t.overload
def __init__(
self: Int[int, int],
self: Integer[int, int],
default_value: int | Sentinel = ...,
allow_none: Literal[False] = ...,
read_only: bool | None = ...,
Expand All @@ -2612,7 +2612,7 @@ def __init__(

@t.overload
def __init__(
self: Int[int | None, int | None],
self: Integer[int | None, int | None],
default_value: int | Sentinel | None = ...,
allow_none: Literal[True] = ...,
read_only: bool | None = ...,
Expand Down Expand Up @@ -2665,7 +2665,7 @@ def subclass_init(self, cls: type[t.Any]) -> None:
pass # fully opt out of instance_init


class CInt(Int[G, S]):
class CInt(Integer[G, S]):
"""A casting version of the int trait."""

if t.TYPE_CHECKING:
Expand Down Expand Up @@ -2713,8 +2713,82 @@ def validate(self, obj: t.Any, value: t.Any) -> G:
return _validate_bounds(self, obj, value) # type:ignore[no-any-return]


Long, CLong = Int, CInt
Integer = Int
class Int(Integer[G, S]):
"""A deprecated alias for :class:`Integer`.

.. deprecated:: 5.16
Use :class:`Integer` instead. ``Int`` and ``Long`` used to be distinct
traits back when Python 2 had separate ``int`` and ``long`` types; they
are now both just integers.
"""

if t.TYPE_CHECKING:

@t.overload
def __init__(
self: Int[int, int],
default_value: int | Sentinel = ...,
allow_none: Literal[False] = ...,
read_only: bool | None = ...,
help: str | None = ...,
config: t.Any | None = ...,
**kwargs: t.Any,
) -> None:
...

@t.overload
def __init__(
self: Int[int | None, int | None],
default_value: int | Sentinel | None = ...,
allow_none: Literal[True] = ...,
read_only: bool | None = ...,
help: str | None = ...,
config: t.Any | None = ...,
**kwargs: t.Any,
) -> None:
...

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
warn(
"The `Int` trait is deprecated since traitlets 5.16, use `Integer` instead.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(*args, **kwargs) # type:ignore[misc]


class Long(Integer[G, S]):
"""A deprecated alias for :class:`Integer`.

.. deprecated:: 5.16
Use :class:`Integer` instead. ``Int`` and ``Long`` used to be distinct
traits back when Python 2 had separate ``int`` and ``long`` types; they
are now both just integers.
"""

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
warn(
"The `Long` trait is deprecated since traitlets 5.16, use `Integer` instead.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(*args, **kwargs) # type:ignore[misc]


class CLong(CInt[G, S]):
"""A deprecated alias for :class:`CInt`.

.. deprecated:: 5.16
Use :class:`CInt` instead.
"""

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
warn(
"The `CLong` trait is deprecated since traitlets 5.16, use `CInt` instead.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(*args, **kwargs) # type:ignore[misc]


class Float(TraitType[G, S]):
Expand Down Expand Up @@ -4236,7 +4310,6 @@ class UseEnum(TraitType[t.Any, t.Any]):

.. sourcecode:: python

# -- SINCE: Python 3.4 (or install backport: pip install enum34)
import enum
from traitlets import HasTraits, UseEnum

Expand Down
6 changes: 4 additions & 2 deletions traitlets/utils/importstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ def import_item(name: str) -> Any:

Returns
-------
mod : module object
The module that was imported.
mod : object
The imported object: for a dotted name ``foo.bar``, the ``bar``
attribute of module ``foo`` (which may be any object); for an
un-dotted name, the module itself.
"""
if not isinstance(name, str):
raise TypeError("import_item accepts strings, not '%s'." % type(name))
Expand Down
Loading