Skip to content

Commit 6721cd1

Browse files
matusvaloPierre-Sassoulas
authored andcommitted
Introduced deprecated attributes
1 parent d7cc8be commit 6721cd1

File tree

5 files changed

+393
-49
lines changed

5 files changed

+393
-49
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Release date: TBA
99
..
1010
Put new features here
1111

12+
* Introduce logic for checking deprecated attributes in DeprecationMixin.
13+
1214

1315
What's New in Pylint 2.7.3?
1416
===========================

doc/whatsnew/2.8.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Summary -- Release highlights
1212
New checkers
1313
============
1414

15+
* Add ``deprecated-argument`` check for deprecated arguments.
1516

1617
Other Changes
1718
=============

examples/deprecation_checker.py

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,39 @@
88
def deprecated_function():
99
pass
1010
11+
def myfunction(arg0, arg1, deprecated_arg1=None, arg2='foo', arg3='bar', deprecated_arg2='spam'):
12+
pass
13+
1114
class MyClass:
1215
def deprecated_method(self):
1316
pass
1417
18+
def mymethod(self, arg0, arg1, deprecated1=None, arg2='foo', deprecated2='bar', arg3='spam'):
19+
pass
20+
1521
$ cat mymain.py
16-
from mymodule import deprecated_function, MyClass
22+
from mymodule import deprecated_function, myfunction, MyClass
1723
1824
deprecated_function()
25+
myfunction(0, 1, 'deprecated_arg1', deprecated_arg2=None)
1926
MyClass().deprecated_method()
27+
MyClass().mymethod(0, 1, deprecated1=None, deprecated2=None)
2028
2129
$ pylint --load-plugins=deprecation_checker mymain.py
2230
************* Module mymain
2331
mymain.py:3:0: W1505: Using deprecated method deprecated_function() (deprecated-method)
24-
mymain.py:4:0: W1505: Using deprecated method deprecated_method() (deprecated-method)
32+
mymain.py:4:0: W1511: Using deprecated argument deprecated_arg1 of method myfunction() (deprecated-argument)
33+
mymain.py:4:0: W1511: Using deprecated argument deprecated_arg2 of method myfunction() (deprecated-argument)
34+
mymain.py:5:0: W1505: Using deprecated method deprecated_method() (deprecated-method)
35+
mymain.py:6:0: W1511: Using deprecated argument deprecated1 of method mymethod() (deprecated-argument)
36+
mymain.py:6:0: W1511: Using deprecated argument deprecated2 of method mymethod() (deprecated-argument)
2537
2638
------------------------------------------------------------------
27-
Your code has been rated at 3.33/10 (previous run: 3.33/10, +0.00)
39+
Your code has been rated at 2.00/10 (previous run: 2.00/10, +0.00)
2840
"""
2941

30-
import astroid
3142

32-
from pylint.checkers import BaseChecker, DeprecatedMixin, utils
43+
from pylint.checkers import BaseChecker, DeprecatedMixin
3344
from pylint.interfaces import IAstroidChecker
3445

3546

@@ -45,24 +56,6 @@ class DeprecationChecker(DeprecatedMixin, BaseChecker):
4556
# The name defines a custom section of the config for this checker.
4657
name = "deprecated"
4758

48-
@utils.check_messages(
49-
"deprecated-method",
50-
)
51-
def visit_call(self, node):
52-
"""Called when a :class:`.astroid.node_classes.Call` node is visited.
53-
54-
See :mod:`astroid` for the description of available nodes.
55-
56-
:param node: The node to check.
57-
:type node: astroid.node_classes.Call
58-
"""
59-
try:
60-
for inferred in node.func.infer():
61-
# Calling entry point for deprecation check logic.
62-
self.check_deprecated_method(node, inferred)
63-
except astroid.InferenceError:
64-
return
65-
6659
def deprecated_methods(self):
6760
"""Callback method called by DeprecatedMixin for every method/function found in the code.
6861
@@ -71,6 +64,29 @@ def deprecated_methods(self):
7164
"""
7265
return {"mymodule.deprecated_function", "mymodule.MyClass.deprecated_method"}
7366

67+
def deprecated_arguments(self, method: str):
68+
"""Callback returning the deprecated arguments of method/function.
69+
70+
Returns:
71+
collections.abc.Iterable in form:
72+
((POSITION1, PARAM1), (POSITION2: PARAM2) ...)
73+
where
74+
* POSITIONX - position of deprecated argument PARAMX in function definition.
75+
If argument is keyword-only, POSITIONX should be None.
76+
* PARAMX - name of the deprecated argument.
77+
"""
78+
if method == "mymodule.myfunction":
79+
# myfunction() has two deprecated arguments:
80+
# * deprecated_arg1 defined at 2nd position and
81+
# * deprecated_arg2 defined at 5th position.
82+
return ((2, "deprecated_arg1"), (5, "deprecated_arg2"))
83+
if method == "mymodule.MyClass.mymethod":
84+
# mymethod() has two deprecated arguments:
85+
# * deprecated1 defined at 2nd position and
86+
# * deprecated2 defined at 4th position.
87+
return ((2, "deprecated1"), (4, "deprecated2"))
88+
return ()
89+
7490

7591
def register(linter):
7692
"""This required method auto registers the checker.

pylint/checkers/deprecated.py

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@
33

44
"""Checker mixin for deprecated functionality."""
55

6-
import abc
6+
from itertools import chain
77
from typing import Any
88

99
import astroid
1010

11+
from pylint.checkers import utils
12+
1113
ACCEPTABLE_NODES = (
1214
astroid.BoundMethod,
1315
astroid.UnboundMethod,
1416
astroid.FunctionDef,
1517
)
1618

1719

18-
class DeprecatedMixin(metaclass=abc.ABCMeta):
20+
class DeprecatedMixin:
1921
"""A mixin implementing logic for checking deprecated symbols.
20-
A class imlementing mixin must define "deprecated-method" Message.
22+
A class implementing mixin must define "deprecated-method" Message.
2123
"""
2224

2325
msgs: Any = {
@@ -26,15 +28,65 @@ class DeprecatedMixin(metaclass=abc.ABCMeta):
2628
"deprecated-method",
2729
"The method is marked as deprecated and will be removed in the future.",
2830
),
31+
"W1511": (
32+
"Using deprecated argument %s of method %s()",
33+
"deprecated-argument",
34+
"The argument is marked as deprecated and will be removed in the future.",
35+
),
2936
}
3037

31-
@abc.abstractmethod
38+
@utils.check_messages(
39+
"deprecated-method",
40+
"deprecated-argument",
41+
)
42+
def visit_call(self, node):
43+
"""Called when a :class:`.astroid.node_classes.Call` node is visited.
44+
45+
Args:
46+
node (astroid.node_classes.Call): The node to check.
47+
"""
48+
try:
49+
for inferred in node.func.infer():
50+
# Calling entry point for deprecation check logic.
51+
self.check_deprecated_method(node, inferred)
52+
except astroid.InferenceError:
53+
return
54+
3255
def deprecated_methods(self):
3356
"""Callback returning the deprecated methods/functions.
3457
3558
Returns:
3659
collections.abc.Container of deprecated function/method names.
3760
"""
61+
# pylint: disable=no-self-use
62+
return ()
63+
64+
def deprecated_arguments(self, method: str):
65+
"""Callback returning the deprecated arguments of method/function.
66+
67+
Args:
68+
method (str): name of function/method checked for deprecated arguments
69+
70+
Returns:
71+
collections.abc.Iterable in form:
72+
((POSITION1, PARAM1), (POSITION2: PARAM2) ...)
73+
where
74+
* POSITIONX - position of deprecated argument PARAMX in function definition.
75+
If argument is keyword-only, POSITIONX should be None.
76+
* PARAMX - name of the deprecated argument.
77+
E.g. suppose function:
78+
79+
.. code-block:: python
80+
def bar(arg1, arg2, arg3, arg4, arg5='spam')
81+
82+
with deprecated arguments `arg2` and `arg4`. `deprecated_arguments` should return:
83+
84+
.. code-block:: python
85+
((1, 'arg2'), (3, 'arg4'))
86+
"""
87+
# pylint: disable=no-self-use
88+
# pylint: disable=unused-argument
89+
return ()
3890

3991
def check_deprecated_method(self, node, inferred):
4092
"""Executes the checker for the given node. This method should
@@ -56,3 +108,18 @@ def check_deprecated_method(self, node, inferred):
56108
qname = inferred.qname()
57109
if any(name in self.deprecated_methods() for name in (qname, func_name)):
58110
self.add_message("deprecated-method", node=node, args=(func_name,))
111+
num_of_args = len(node.args)
112+
kwargs = {kw.arg for kw in node.keywords} if node.keywords else {}
113+
for position, arg_name in chain(
114+
self.deprecated_arguments(func_name), self.deprecated_arguments(qname)
115+
):
116+
if arg_name in kwargs:
117+
# function was called with deprecated argument as keyword argument
118+
self.add_message(
119+
"deprecated-argument", node=node, args=(arg_name, func_name)
120+
)
121+
elif position is not None and position < num_of_args:
122+
# function was called with deprecated argument as positional argument
123+
self.add_message(
124+
"deprecated-argument", node=node, args=(arg_name, func_name)
125+
)

0 commit comments

Comments
 (0)