Skip to main content

pyDeprecate

Simple tooling for marking deprecated functions or classes and re-routing to the new successors' instance.

PyPI - Python Version PyPI Status PyPI Status license

CI testing Code formatting codecov CodeFactor


The common use-case is moving your functions across codebase or outsourcing some functionalities to new packages. For most of these cases, you want to hold some compatibility, so you cannot simply remove past function, and also for some time you want to warn users that functionality they have been using is moved and not it is deprecated in favor of another function (which shall be used instead) and soon it will be removed completely.

Another good aspect is to do not overwhelm a user with too many warnings, so per function/class, this warning is raised only N times in the preferable stream (warning, logger, etc.).

Installation

Simple installation from PyPI:

pip install pyDeprecate
Other installations

Simply install with pip from source:

pip install https://github.com/Borda/pyDeprecate/archive/main.zip

Use-cases

The functionality is kept simple and all default shall be reasonable, but still you can do extra customization such as:

  • define user warning message and preferable stream
  • extended argument mapping to target function/method
  • define deprecation logic for self arguments
  • specify warning count per:
    • called function (for func deprecation)
    • used arguments (for argument deprecation)

In particular the target values (cases):

  • None - raise only warning message (ignore all argument mapping)
  • True - deprecation some argument of itself (argument mapping shall be specified)
  • Callable - forward call to new methods (optional also argument mapping or extras)

Simple function forwarding

It is very straight forward, you forward your function call to new function and all arguments are mapped:

def base_sum(a: int = 0, b: int = 3) -> int:
    """My new function anywhere in codebase or even other package."""
    return a + b

# ---------------------------

from deprecate import deprecated

@deprecated(target=base_sum, deprecated_in="0.1", remove_in="0.5")
def depr_sum(a: int, b: int = 5) -> int:
    """
    My deprecated function which now has empty body
     as all calls are routed to the new function.
    """
    pass  # or you can just place docstring as one above

# call this function will raise deprecation warning:
#   The `depr_sum` was deprecated since v0.1 in favor of `__main__.base_sum`.
#   It will be removed in v0.5.
print(depr_sum(1, 2))

sample output:

3

Advanced target argument mapping

Another more complex example is using argument mapping is:

Advanced example
import logging
from sklearn.metrics import accuracy_score
from deprecate import deprecated, void

@deprecated(
  # use standard sklearn accuracy implementation
  target=accuracy_score,
  # custom warning stream
  stream=logging.warning,
  # number or warnings per lifetime (with -1 for always_
  num_warns=5,
  # custom message template
  template_mgs="`%(source_name)s` was deprecated, use `%(target_path)s`",
  # as target args are different, define mapping from source to target func
  args_mapping={'preds': 'y_pred', 'target': 'y_true', 'blabla': None}
)
def depr_accuracy(preds: list, target: list, blabla: float) -> float:
    """My deprecated function which is mapping to sklearn accuracy."""
    # to stop complain your IDE about unused argument you can use void/empty function
    void(preds, target, blabla)

# call this function will raise deprecation warning:
#   WARNING:root:`depr_accuracy` was deprecated, use `sklearn.metrics.accuracy_score`
print(depr_accuracy([1, 0, 1, 2], [0, 1, 1, 2], 1.23))

sample output:

0.5

Deprecation warning only

Base use-case with no forwarding and just raising warning :

from deprecate import deprecated

@deprecated(target=None, deprecated_in="0.1", remove_in="0.5")
def my_sum(a: int, b: int = 5) -> int:
    """My deprecated function which still has to have implementation."""
    return a + b

# call this function will raise deprecation warning:
#   The `my_sum` was deprecated since v0.1. It will be removed in v0.5.
print(my_sum(1, 2))

sample output:

3

Self argument mapping

We also support deprecation and argument mapping for the function itself:

from deprecate import deprecated

@deprecated(
  # define as depreaction some self argument - mapping
  target=True, args_mapping={'coef': 'new_coef'},
  # common version info
  deprecated_in="0.2", remove_in="0.4",
)
def any_pow(base: float, coef: float = 0, new_coef: float = 0) -> float:
    """My function with deprecated argument `coef` mapped to `new_coef`."""
    return base ** new_coef

# call this function will raise deprecation warning:
#   The `any_pow` uses deprecated arguments: `coef` -> `new_coef`.
#   They were deprecated since v0.2 and will be removed in v0.4.
print(any_pow(2, 3))

sample output:

8

Eventually you can set multiple deprecation levels via chaining deprecation arguments as each could be deprecated in another version:

from deprecate import deprecated

@deprecated(
  True, "0.3", "0.6", args_mapping=dict(c1='nc1'),
  template_mgs="Depr: v%(deprecated_in)s rm v%(remove_in)s for args: %(argument_map)s."
)
@deprecated(
  True, "0.4", "0.7", args_mapping=dict(nc1='nc2'),
  template_mgs="Depr: v%(deprecated_in)s rm v%(remove_in)s for args: %(argument_map)s."
)
def any_pow(base, c1: float = 0, nc1: float = 0, nc2: float = 2) -> float:
    return base**nc2

# call this function will raise deprecation warning:
#   DeprecationWarning('Depr: v0.3 rm v0.6 for args: `c1` -> `nc1`.')
#   DeprecationWarning('Depr: v0.4 rm v0.7 for args: `nc1` -> `nc2`.')
print(any_pow(2, 3))

sample output:

8

Class deprecation

This case can be quite complex as you may deprecate just some methods, here we show full class deprecation:

class NewCls:
    """My new class anywhere in the codebase or other package."""

    def __init__(self, c: float, d: str = "abc"):
        self.my_c = c
        self.my_d = d

# ---------------------------

from deprecate import deprecated, void

class PastCls(NewCls):
    """
    The deprecated class shall be inherited from the successor class
     to hold all methods.
    """

    @deprecated(target=NewCls, deprecated_in="0.2", remove_in="0.4")
    def __init__(self, c: int, d: str = "efg"):
        """
        You place the decorator around __init__ as you want
         to warn user just at the time of creating object.
        """
        void(c, d)

# call this function will raise deprecation warning:
#   The `PastCls` was deprecated since v0.2 in favor of `__main__.NewCls`.
#   It will be removed in v0.4.
inst = PastCls(7)
print(inst.my_c)  # returns: 7
print(inst.my_d)  # returns: "efg"

sample output:

7
efg

Contribution

Have you faced this in past or even now, do you have good ideas for improvement, all is welcome!

Metadata

Release files for pyDeprecate 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyDeprecate 0.2.0
File Size Uploaded
pyDeprecate-0.2.0.tar.gz 14.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyDeprecate 0.2.0
File Interpreter ABI Platform
pyDeprecate-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.8 kB

Release files / pyDeprecate-0.2.0.tar.gz

Download URL pyDeprecate-0.2.0.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c07e8ef5d9f40409183a952ddc288e7164c5d47644556077388c6b33c9aed545
BLAKE2b-256 checksum
How to use checksums
2a5cfd9bc93a0ed6c572cba6d044be8c546cae8ee78f8c80134a2b414db08f20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.1 importlib_metadata/3.9.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.9.2

Release files / pyDeprecate-0.2.0-py3-none-any.whl

Download URL pyDeprecate-0.2.0-py3-none-any.whl
Size 13.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
46869a24815de7b3e1708b356b2acad403465adfa72863069dc618c8a88cc775
BLAKE2b-256 checksum
How to use checksums
8d588e75b9c11370dd2db3b8cc4c70717c9466114e2a95cf2e521cb9641e1d42
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.1 importlib_metadata/3.9.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.9.2
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page