diff --git a/tests/config/test_configurable.py b/tests/config/test_configurable.py index 4938e5ee..154af680 100644 --- a/tests/config/test_configurable.py +++ b/tests/config/test_configurable.py @@ -969,3 +969,9 @@ def test_logger_adapter(caplog, capsys): app.log.info("test message") assert "adapted test message" in capsys.readouterr().err + + +def test_get_log_handler_deprecated(): + app = Application(log=logging.getLogger("test_get_log_handler_deprecated")) + with pytest.deprecated_call(match="_get_log_handler has been deprecated"): + app._get_log_handler() diff --git a/tests/config/test_loader.py b/tests/config/test_loader.py index dcccb5a1..08fd6794 100644 --- a/tests/config/test_loader.py +++ b/tests/config/test_loader.py @@ -503,6 +503,14 @@ def test_auto_section(self): del c.A self.assertEqual(c.A, Config()) + def test_has_key_deprecated(self): + c = Config() + c.a = 10 + with pytest.deprecated_call(match="Config.has_key has been deprecated"): + assert c.has_key("a") + with pytest.deprecated_call(match="Config.has_key has been deprecated"): + assert not c.has_key("b") + def test_merge_doesnt_exist(self): c1 = Config() c2 = Config() diff --git a/tests/test_traitlets.py b/tests/test_traitlets.py index 2e7b8459..968d034f 100644 --- a/tests/test_traitlets.py +++ b/tests/test_traitlets.py @@ -367,6 +367,17 @@ def test_trait_types_dict_deprecated(self): class C(HasTraits): t = Dict(Int) + def test_no_default_specified_deprecated(self): + import traitlets.traitlets as tt + + with expected_warnings(["NoDefaultSpecified is deprecated"]): + value = tt.NoDefaultSpecified + assert value is tt.Undefined + # still discoverable + assert "NoDefaultSpecified" in dir(tt) + with pytest.raises(AttributeError): + tt.this_does_not_exist + def test_long_deprecated_aliases(self): for cls, replacement in [(Long, "Integer"), (CLong, "CInt")]: with expected_warnings([f"`{cls.__name__}` trait is deprecated"]): diff --git a/traitlets/config/configurable.py b/traitlets/config/configurable.py index 0bf7e23c..41efc1f3 100644 --- a/traitlets/config/configurable.py +++ b/traitlets/config/configurable.py @@ -506,9 +506,17 @@ def _get_log_handler(self) -> logging.Handler | None: Returns None if none can be found - Deprecated, this now returns the first log handler which may or may - not be the default one. + .. deprecated:: 5.2 + + This now returns the first log handler, which may or may not be + the default one. Use ``self.log.handlers`` directly instead. """ + warnings.warn( + "Configurable._get_log_handler has been deprecated since traitlets 5.2 - 2022," + " use the `.handlers` attribute of the logger directly.", + DeprecationWarning, + stacklevel=2, + ) if not self.log: return None logger: logging.Logger = ( diff --git a/traitlets/config/loader.py b/traitlets/config/loader.py index 379cd479..33818e58 100644 --- a/traitlets/config/loader.py +++ b/traitlets/config/loader.py @@ -304,8 +304,15 @@ def __contains__(self, key: t.Any) -> bool: return super().__contains__(key) - # .has_key is deprecated for dictionaries. - has_key = __contains__ + def has_key(self, key: t.Any) -> bool: + """Deprecated since traitlets 4.0 - 2015, use ``key in config`` instead.""" + warnings.warn( + "Config.has_key has been deprecated since traitlets 4.0 - 2015," + " use `key in config` instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.__contains__(key) def _has_section(self, key: str) -> bool: return _is_section_key(key) and key in self diff --git a/traitlets/traitlets.py b/traitlets/traitlets.py index 0989ea98..1fe0d723 100644 --- a/traitlets/traitlets.py +++ b/traitlets/traitlets.py @@ -155,8 +155,31 @@ """, ) -# Deprecated alias -NoDefaultSpecified = Undefined +# Deprecated aliases, kept accessible through the module-level ``__getattr__`` +# below so that reaching for them emits a DeprecationWarning (PEP 562). +_deprecated_module_attrs = { + # name: (current name, message) + "NoDefaultSpecified": ( + "Undefined", + ( + "traitlets.traitlets.NoDefaultSpecified is deprecated since traitlets 4.0 - 2015," + " use traitlets.Undefined instead." + ), + ), +} + + +def __getattr__(name: str) -> t.Any: + try: + target, msg = _deprecated_module_attrs[name] + except KeyError: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None + warn(msg, DeprecationWarning, stacklevel=2) + return globals()[target] + + +def __dir__() -> list[str]: + return sorted([*globals(), *_deprecated_module_attrs]) class TraitError(Exception):