From 6cea5260e8988aacca55d086b83f03791c5fd847 Mon Sep 17 00:00:00 2001 From: mjhajharia Date: Mon, 20 Feb 2023 13:12:15 +0200 Subject: [PATCH] some stuff --- .github/workflows/python-package.yml | 2 +- deprecat/classic.py | 41 +++++++++++++++++------- deprecat/example.py | 13 +++++--- deprecat/sphinx.py | 48 +++++++++++++++++----------- docs/source/usage.rst | 41 ++++++++++++++++++++---- 5 files changed, 103 insertions(+), 42 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1a5cd72..4cbf611 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: platform: [ ubuntu-latest, macos-latest, windows-latest ] - python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10" ] + python-version: [ "3.7", "3.8", "3.9", "3.10" ] steps: - uses: actions/checkout@v2 diff --git a/deprecat/classic.py b/deprecat/classic.py index 82c26c7..8ad38d2 100644 --- a/deprecat/classic.py +++ b/deprecat/classic.py @@ -44,6 +44,9 @@ class ClassicAdapter(wrapt.AdapterFactory): version: str Version of your project which deprecates this method or class. + remove_version: str + Version of your project which removes this method or class. + action: str A warning filter used to specify the deprecation warning. Can be one of "error", "ignore", "always", "default", "module", or "once". @@ -60,9 +63,10 @@ class ClassicAdapter(wrapt.AdapterFactory): """ - def __init__(self, reason="", version="", action=None, deprecated_args=None, category=DeprecationWarning): - self.reason = reason or "" - self.version = version or "" + def __init__(self, reason="", version="", remove_version="", action=None, deprecated_args=None, category=DeprecationWarning): + self.reason = reason + self.version = version + self.remove_version = remove_version self.action = action self.category = category self.deprecated_args = deprecated_args @@ -99,14 +103,18 @@ def get_deprecated_msg(self, wrapped, instance, kwargs): else: fmt = "Call to deprecated method {name}." + if self.deprecated_args is None: name = wrapped.__name__ - if self.reason: + if self.reason != "": fmt += " ({reason})" - if self.version: - fmt += " -- Deprecated since version {version}." + if self.version != "": + fmt += "\n-- Deprecated since version {version}." + + if self.remove_version != "": + fmt += "\n-- Will be removed in version {remove_version}." - return {f'{name}': fmt.format(name=name, reason=self.reason or "", version=self.version or "")} + return {f'{name}': fmt.format(name=name, reason=self.reason, version=self.version, remove_version=self.remove_version)} if self.deprecated_args is not None: @@ -117,11 +125,20 @@ def get_deprecated_msg(self, wrapped, instance, kwargs): for arg in self.argstodeprecate: name = arg fmt = "Call to deprecated Parameter {name}." - if self.deprecated_args[arg]['reason']: - fmt += " ({reason})" - if self.deprecated_args[arg]['version']: - fmt += " -- Deprecated since v{version}." - warningargs[arg] = fmt.format(name=name, reason=self.deprecated_args[arg]['reason'] or "", version=self.deprecated_args[arg]['version'] or "") + r='' + v='' + rv='' + if self.deprecated_args[arg].get('reason') is not None: + r = self.deprecated_args[arg]['reason'] + fmt += " {reason}" + if self.deprecated_args[arg].get('version') is not None: + v = self.deprecated_args[arg]['version'] + fmt += "\n-- Deprecated since v{version}." + if self.deprecated_args[arg].get('remove_version') is not None: + rv = self.deprecated_args[arg]['remove_version'] + fmt += "\n-- Will be removed in version {remove_version}." + + warningargs[arg] = fmt.format(name=name, reason=r, version=v, remove_version=rv) else: name="" diff --git a/deprecat/example.py b/deprecat/example.py index 81992b5..73e457f 100644 --- a/deprecat/example.py +++ b/deprecat/example.py @@ -12,10 +12,10 @@ from deprecat.sphinx import deprecat -@deprecat(reason="useless", version = 2.0) +@deprecat(reason="useless", version = "2.0") class test_deprecat_on_class: """ - Here we test deprecation on a class, like this one. + Here we test deprecation on a class, like this one. """ def __init__(self): pass @@ -26,17 +26,20 @@ class test_deprecat_on_class_method: def __init__(self): pass - @deprecat(reason="useless", version = 2.0) + @deprecat(reason="useless", version = "2.0", remove_version = "3.0") def randomfunction(self, a, b): """ - Here we test deprecation on a method of a class, like this one. + Here we test deprecation on a method of a class, like this one. + Note that you can also add `remove_version` to the decorator + to specify the version when the class/function/kwarg/feature will be removed. + """ return a + b x = test_deprecat_on_class_method() x.randomfunction(1,2) -@deprecat(reason="useless", version = 2.0) +@deprecat(reason="useless", version = "2.0") def test_deprecat_on_function(a, b): """ Here we test deprecation on a function. diff --git a/deprecat/sphinx.py b/deprecat/sphinx.py index 4c0d21c..b132228 100644 --- a/deprecat/sphinx.py +++ b/deprecat/sphinx.py @@ -51,6 +51,9 @@ class SphinxAdapter(ClassicAdapter): version: str Version of your project which deprecates this feature. + remove_version: str + Version of your project which removes this method or class. + action: str A warning filter used to specify the deprecation warning. Can be one of "error", "ignore", "always", "default", "module", or "once". @@ -97,6 +100,7 @@ def __init__( directive, reason="", version="", + remove_version="", action=None, category=DeprecationWarning, line_length=70, @@ -105,7 +109,7 @@ def __init__( self.deprecated_args = deprecated_args self.directive = directive self.line_length = line_length - super(SphinxAdapter, self).__init__(reason=reason, version=version, action=action, category=category, deprecated_args=deprecated_args) + super(SphinxAdapter, self).__init__(reason=reason, version=version, remove_version=remove_version, action=action, category=category, deprecated_args=deprecated_args) def __call__(self, wrapped): """ @@ -130,7 +134,10 @@ def __call__(self, wrapped): docstring = "\n" width = self.line_length - 3 if self.line_length > 3 else 2 ** 16 - reason = textwrap.dedent(self.reason).strip() + reason=self.reason + if self.remove_version!="": + reason += f'\n\n Warning: This deprecated feature will be removed in version {self.remove_version}' + reason = textwrap.dedent(reason).strip() if self.deprecated_args is None: fmt = ".. {directive}:: {version}" if self.version else ".. {directive}::" @@ -149,7 +156,6 @@ def __call__(self, wrapped): ) else: div_lines.append("") - # -- append the directive division to the docstring docstring += "".join("{}\n".format(line) for line in div_lines) @@ -192,29 +198,31 @@ def __call__(self, wrapped): insert_pos = len(params_section[description_start:]) #finally we store the warning fmt string - if self.deprecated_args[arg]['version']!="": + if self.deprecated_args[arg].get('version') is not None: #the spaces are specifically cherrypicked for numpydoc docstrings fmt = "\n\n .. admonition:: Deprecated\n :class: warning\n\n Parameter {arg} deprecated since {version}" - div_lines = [fmt.format(version=self.deprecated_args[arg]['version'],arg=arg)] + if self.deprecated_args[arg].get('remove_version') is not None: + fmt += " and will be removed in version {remove_version}." + div_lines = [fmt.format(version=self.deprecated_args[arg]['version'],arg=arg, remove_version=self.deprecated_args[arg]['remove_version'])] + else: + div_lines = [fmt.format(version=self.deprecated_args[arg]['version'],arg=arg)] else: fmt = "\n\n .. admonition:: Deprecated\n :class: warning\n\n Parameter {arg} deprecated" div_lines = [fmt.format(version=self.deprecated_args[arg]['version'],arg=arg)] width = 2**16 + if self.remove_version!="": + self.reason += f'\n\n Warning: This deprecated feature will be removed in version {self.remove_version}' reason = textwrap.dedent(self.reason).strip() #formatting for docstring for paragraph in reason.splitlines(): - if paragraph: - div_lines.extend( - textwrap.fill( - paragraph, - width=width, - initial_indent=indent, - subsequent_indent=indent, - ).splitlines() - ) - else: - div_lines.append("") - + div_lines.extend( + textwrap.fill( + paragraph, + width=width, + initial_indent=indent, + subsequent_indent=indent, + ).splitlines() + ) # -- append the directive division to the docstring a='' a += "".join("{}\n".format(line) for line in div_lines) @@ -326,7 +334,7 @@ def versionchanged(reason="", version="", line_length=70): return adapter -def deprecat(reason="", directive="deprecated", version="", line_length=70, deprecated_args=None, **kwargs): +def deprecat(reason="", directive="deprecated", version="", remove_version="", line_length=70, deprecated_args=None, **kwargs): """ This decorator can be used to insert a "deprecated" directive in your function/class docstring in order to documents the @@ -340,6 +348,9 @@ def deprecat(reason="", directive="deprecated", version="", line_length=70, depr version: str Version of your project which deprecates this method or class. + remove_version: str + Version of your project which removes this method or class. + action: str A warning filter used to specify the deprecation warning. Can be one of "error", "ignore", "always", "default", "module", or "once". @@ -369,6 +380,7 @@ def deprecat(reason="", directive="deprecated", version="", line_length=70, depr adapter_cls = kwargs.pop('adapter_cls', SphinxAdapter) kwargs["reason"] = reason kwargs["version"] = version + kwargs["remove_version"] = remove_version kwargs["line_length"] = line_length kwargs["deprecated_args"] = deprecated_args diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 15c07e9..e944914 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -11,7 +11,7 @@ Deprecated function from deprecat import deprecat - @deprecat(reason="this is a bad function", version = '2.0') + @deprecat(reason="this is a bad function", version = "2.0") def some_deprecated_function(x, y): return x+y @@ -39,7 +39,7 @@ Deprecated method def __init__(self,value): self.value = value - @deprecat(reason="this is a bad method", version = 2.0) + @deprecat(reason="this is a bad method", version = "2.0") def some_deprecated_function(self): print(self.value) @@ -68,7 +68,7 @@ Deprecated class from deprecat import deprecat - @deprecat(reason="useless", version = 2.0) + @deprecat(reason="useless", version = "2.0") class badclass: def __init__(self): @@ -169,6 +169,35 @@ This is the output we get when we try to run ``multiply(a=1,b=2,c=3)`` 6 -Examples --------- -`Check here for live examples `__ \ No newline at end of file +Adding Removal Version +---------------------- + +You can add a removal version to the deprecation warning by adding the ``removal_version`` parameter to the decorator. This will add a warning to the user that the function will be removed in the next versions. + +.. code-block:: python + + from deprecat.sphinx import deprecat + + @deprecat(reason="this is a bad function", version = "2.0", remove_version = "3.0") + def some_deprecated_function(x, y): + """ + Parameters + ---------- + x: float + x is a nice number + + y: float + y is also a nice number + """ + return x+y + +This is the output we get when we try to run ``some_deprecated_function(x=2, y=3)`` + +.. code-block:: sh + + DeprecationWarning: Call to deprecated method randomfunction. useless + Warning: This deprecated feature will be removed in version 3.0 -- Deprecated since version 2.0. -- Will be removed in version 3.0. + +Live Examples +------------- +If you want to see the sphinx admonitions in action, `check this out `__ \ No newline at end of file