Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate a function parameter — Contribution Guide #8

Closed
tantale opened this issue Mar 1, 2019 · 4 comments
Closed

Deprecate a function parameter — Contribution Guide #8

tantale opened this issue Mar 1, 2019 · 4 comments

Comments

@tantale
Copy link
Collaborator

tantale commented Mar 1, 2019

Introduction

This article concerns the addition of one (or more) decorator(s) to deprecate the use of a function parameter. For example:

@deprecated_param(version="0.2.3",
                  reason="you may consider using *styles* instead.",
                  deprecated_args='color background_color')
def paragraph(text, color=None, background_color=None, styles=None):
    styles = styles or {}
    if color:
        styles['color'] = color
    if background_color:
        styles['background-color'] = background_color
    html_styles = " ".join("{k}: {v};".format(k=k, v=v) for k, v in styles.items())
    html_text = xml.sax.saxutils.escape(text)
    return ('<p styles="{html_styles}">{html_text}</p>'
            .format(html_styles=html_styles, html_text=html_text))

Such a decorator could be coded as follows:

import functools


def deprecated_param(version, reason, deprecated_args):
    def decorate(func):
        @functools.wraps(func)
        def call(*args, **kwargs):
            # todo: check deprecated arguments here...
            return func(*args, **kwargs)

        return call

    return decorate

Advantages and disadvantages of the decorator

Using a decorator to deprecate the use of a function parameter is interesting and offers some advantages:

  • The decorator allows you to isolate the code that checks the use of this parameter from the rest of the function processing: Separation of Concerns design pattern.
  • The decorator allows explicit documentation of the code, so the use of comments is unnecessary: “Readability matters”.
  • The decorator could be used to enhance the code documentation (the docstring of the decorated function).

Disadvantages:

  • The implementation of such a decorator is necessarily more complex than a specific solution.
  • The resulting function (after decoration) is necessarily slower than the decorated function because of the introspection and parameters usage check.
    Another important consideration:
  • The resulting function (after decoration) must be of the same nature as the decorated function: function, method, static method, etc.

Generalization:

In addition to the case of deprecating a parameter usage, a warning could also be issued in the following cases:

  • A parameter has been renamed: the new name is more meaningful, more explicit, etc.
  • One parameter is replaced by another of more general use.
  • The parameter type changes, it is better to use another one.
  • A parameter is considered obsolete, it will be deleted in a future version.
  • A new parameter has been added, its use is strongly recommended.
  • Etc.

The use cases mentioned above fall into 3 categories:

  1. Deleted parameter,
  2. Added parameter,
  3. Modified parameter (default value, modified type or use).

The third case (modified parameter) is the most difficult to specify in general terms.

Examples of deprecations

(This section is incomplete)
Before starting a complex implementation, it would be necessary to study how it is done, on one hand in the Standard Library, and on the other hand in the popular libraries of the Open Source world such as: pip, urllib3, boto3, six, requests, setuptools, futures, Flask, Django…
At least two questions must be considered regarding deprecation:

  • How is it implemented in the source code?
  • How is it documented?

Implementation

(This section is incomplete)
A parameter deprecation decorator implementation must be flexible. It is necessary to be as flexible as possible and to use what exists for @deprecated.basic.deprecated and @deprecated.sphinx.deprecated. Especially, the use of Wrapt is a must-have.
It is also necessary to define test cases that correspond to the most common scenarios. It is also likely that some atypical situations are clearly not implemented.
For now, I would like to continue supporting Python 2.7, as it is still used by 25% of users (according to the 2018 State of Developer Ecosystem survey).

@tantale tantale changed the title Deprecate a function parameter Deprecate a function parameter — Contribution Guide Mar 1, 2019
@kba
Copy link

kba commented Dec 20, 2019

This looks pretty elegant: https://stackoverflow.com/questions/49802412/how-to-implement-deprecation-in-python-with-argument-alias:

import functools
import warnings

def deprecated_alias(**aliases):
    def deco(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            rename_kwargs(f.__name__, kwargs, aliases)
            return f(*args, **kwargs)
        return wrapper
    return deco

def rename_kwargs(func_name, kwargs, aliases):
    for alias, new in aliases.items():
        if alias in kwargs:
            if new in kwargs:
                raise TypeError('{} received both {} and {}'.format(
                    func_name, alias, new))
            warnings.warn('{} is deprecated; use {}'.format(alias, new),
                          DeprecationWarning)
            kwargs[new] = kwargs.pop(alias)

class MyClass(object):
    @deprecated_alias(object_id='id_object')
    def __init__(self, id_object):
        self.id = id_object

@tantale
Copy link
Collaborator Author

tantale commented Dec 20, 2019

It’s a good starting point. We also need to add a warning message.

@tantale tantale pinned this issue Feb 15, 2020
@stefano-xy
Copy link

Since it looks like it's taking some time to discuss and implement this improvement, or find someone that could, can I suggest to introduce a function to emit properly a deprecation warning, to be used inside the code for complex situations that can only be represented using runtime checks by the programmer? Those runtime checks are probably already there.

Something like:

deprecation_warning(version=..., reason=...)

That can be used in any place needed?

@mjhajharia
Copy link

mjhajharia commented Nov 30, 2021

hi @tantale, consider checking this PR #51, if you think I can contribute something similar!

@tantale tantale closed this as completed May 27, 2023
@tantale tantale unpinned this issue Jun 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants