Skip to content

Commit 40e1a64

Browse files
DanielNoordcdce8p
andauthored
Deprecate accessing and setting the doc attribute on nodes (#1434)
Co-authored-by: Marc Mueller <[email protected]>
1 parent 393b276 commit 40e1a64

File tree

6 files changed

+200
-28
lines changed

6 files changed

+200
-28
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Release date: TBA
99
* Add new (optional) ``doc_node`` attribute to ``nodes.Module``, ``nodes.ClassDef``,
1010
and ``nodes.FunctionDef``.
1111

12+
* Accessing the ``doc`` attribute of ``nodes.Module``, ``nodes.ClassDef``, and
13+
``nodes.FunctionDef`` has been deprecated in favour of the ``doc_node`` attribute.
14+
Note: ``doc_node`` is an (optional) ``nodes.Const`` whereas ``doc`` was an (optional) ``str``.
15+
1216
* Replace custom ``cachedproperty`` with ``functools.cached_property`` and deprecate it
1317
for Python 3.8+.
1418

astroid/nodes/scoped_nodes/scoped_nodes.py

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ class Module(LocalsDictNodeNG):
438438
def __init__(
439439
self,
440440
name: str,
441-
doc: Optional[str],
441+
doc: Optional[str] = None,
442442
file: Optional[str] = None,
443443
path: Optional[List[str]] = None,
444444
package: Optional[bool] = None,
@@ -463,7 +463,7 @@ def __init__(
463463
self.name = name
464464
"""The name of the module."""
465465

466-
self.doc = doc
466+
self._doc = doc
467467
"""The module docstring."""
468468

469469
self.file = file
@@ -509,6 +509,27 @@ def postinit(self, body=None, *, doc_node: Optional[Const] = None):
509509
"""
510510
self.body = body
511511
self.doc_node = doc_node
512+
if doc_node:
513+
self._doc = doc_node.value
514+
515+
@property
516+
def doc(self) -> Optional[str]:
517+
"""The module docstring."""
518+
warnings.warn(
519+
"The 'Module.doc' attribute is deprecated, "
520+
"use 'Module.doc_node' instead.",
521+
DeprecationWarning,
522+
)
523+
return self._doc
524+
525+
@doc.setter
526+
def doc(self, value: Optional[str]) -> None:
527+
warnings.warn(
528+
"Setting the 'Module.doc' attribute is deprecated, "
529+
"use 'Module.doc_node' instead.",
530+
DeprecationWarning,
531+
)
532+
self._doc = value
512533

513534
def _get_stream(self):
514535
if self.file_bytes is not None:
@@ -1515,7 +1536,7 @@ class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
15151536
def __init__(
15161537
self,
15171538
name=None,
1518-
doc=None,
1539+
doc: Optional[str] = None,
15191540
lineno=None,
15201541
col_offset=None,
15211542
parent=None,
@@ -1527,8 +1548,7 @@ def __init__(
15271548
:param name: The name of the function.
15281549
:type name: str or None
15291550
1530-
:param doc: The function's docstring.
1531-
:type doc: str or None
1551+
:param doc: The function docstring.
15321552
15331553
:param lineno: The line that this node appears on in the source code.
15341554
:type lineno: int or None
@@ -1553,11 +1573,8 @@ def __init__(
15531573
:type name: str or None
15541574
"""
15551575

1556-
self.doc = doc
1557-
"""The function's docstring.
1558-
1559-
:type doc: str or None
1560-
"""
1576+
self._doc = doc
1577+
"""The function docstring."""
15611578

15621579
self.doc_node: Optional[Const] = None
15631580
"""The doc node associated with this node."""
@@ -1614,6 +1631,27 @@ def postinit(
16141631
self.type_comment_args = type_comment_args
16151632
self.position = position
16161633
self.doc_node = doc_node
1634+
if doc_node:
1635+
self._doc = doc_node.value
1636+
1637+
@property
1638+
def doc(self) -> Optional[str]:
1639+
"""The function docstring."""
1640+
warnings.warn(
1641+
"The 'FunctionDef.doc' attribute is deprecated, "
1642+
"use 'FunctionDef.doc_node' instead.",
1643+
DeprecationWarning,
1644+
)
1645+
return self._doc
1646+
1647+
@doc.setter
1648+
def doc(self, value: Optional[str]) -> None:
1649+
warnings.warn(
1650+
"Setting the 'FunctionDef.doc' attribute is deprecated, "
1651+
"use 'FunctionDef.doc_node' instead.",
1652+
DeprecationWarning,
1653+
)
1654+
self._doc = value
16171655

16181656
@cached_property
16191657
def extra_decorators(self) -> List[node_classes.Call]:
@@ -2164,7 +2202,7 @@ def my_meth(self, arg):
21642202
def __init__(
21652203
self,
21662204
name=None,
2167-
doc=None,
2205+
doc: Optional[str] = None,
21682206
lineno=None,
21692207
col_offset=None,
21702208
parent=None,
@@ -2176,8 +2214,7 @@ def __init__(
21762214
:param name: The name of the class.
21772215
:type name: str or None
21782216
2179-
:param doc: The function's docstring.
2180-
:type doc: str or None
2217+
:param doc: The class docstring.
21812218
21822219
:param lineno: The line that this node appears on in the source code.
21832220
:type lineno: int or None
@@ -2229,11 +2266,8 @@ def __init__(
22292266
:type name: str or None
22302267
"""
22312268

2232-
self.doc = doc
2233-
"""The class' docstring.
2234-
2235-
:type doc: str or None
2236-
"""
2269+
self._doc = doc
2270+
"""The class docstring."""
22372271

22382272
self.doc_node: Optional[Const] = None
22392273
"""The doc node associated with this node."""
@@ -2254,6 +2288,25 @@ def __init__(
22542288
for local_name, node in self.implicit_locals():
22552289
self.add_local_node(node, local_name)
22562290

2291+
@property
2292+
def doc(self) -> Optional[str]:
2293+
"""The class docstring."""
2294+
warnings.warn(
2295+
"The 'ClassDef.doc' attribute is deprecated, "
2296+
"use 'ClassDef.doc_node' instead.",
2297+
DeprecationWarning,
2298+
)
2299+
return self._doc
2300+
2301+
@doc.setter
2302+
def doc(self, value: Optional[str]) -> None:
2303+
warnings.warn(
2304+
"Setting the 'ClassDef.doc' attribute is deprecated, "
2305+
"use 'ClassDef.doc_node.value' instead.",
2306+
DeprecationWarning,
2307+
)
2308+
self._doc = value
2309+
22572310
def implicit_parameters(self):
22582311
return 1
22592312

@@ -2316,6 +2369,8 @@ def postinit(
23162369
self._metaclass = metaclass
23172370
self.position = position
23182371
self.doc_node = doc_node
2372+
if doc_node:
2373+
self._doc = doc_node.value
23192374

23202375
def _newstyle_impl(self, context=None):
23212376
if context is None:

tests/unittest_builder.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,11 @@ def test_module_base_props(self) -> None:
769769
"""test base properties and method of an astroid module"""
770770
module = self.module
771771
self.assertEqual(module.name, "data.module")
772-
self.assertEqual(module.doc, "test module for astroid\n")
772+
with pytest.warns(DeprecationWarning) as records:
773+
self.assertEqual(module.doc, "test module for astroid\n")
774+
assert len(records) == 1
775+
assert isinstance(module.doc_node, nodes.Const)
776+
self.assertEqual(module.doc_node.value, "test module for astroid\n")
773777
self.assertEqual(module.fromlineno, 0)
774778
self.assertIsNone(module.parent)
775779
self.assertEqual(module.frame(), module)
@@ -811,7 +815,11 @@ def test_function_base_props(self) -> None:
811815
module = self.module
812816
function = module["global_access"]
813817
self.assertEqual(function.name, "global_access")
814-
self.assertEqual(function.doc, "function test")
818+
with pytest.warns(DeprecationWarning) as records:
819+
self.assertEqual(function.doc, "function test")
820+
assert len(records)
821+
assert isinstance(function.doc_node, nodes.Const)
822+
self.assertEqual(function.doc_node.value, "function test")
815823
self.assertEqual(function.fromlineno, 11)
816824
self.assertTrue(function.parent)
817825
self.assertEqual(function.frame(), function)
@@ -834,7 +842,11 @@ def test_class_base_props(self) -> None:
834842
module = self.module
835843
klass = module["YO"]
836844
self.assertEqual(klass.name, "YO")
837-
self.assertEqual(klass.doc, "hehe\n haha")
845+
with pytest.warns(DeprecationWarning) as records:
846+
self.assertEqual(klass.doc, "hehe\n haha")
847+
assert len(records) == 1
848+
assert isinstance(klass.doc_node, nodes.Const)
849+
self.assertEqual(klass.doc_node.value, "hehe\n haha")
838850
self.assertEqual(klass.fromlineno, 25)
839851
self.assertTrue(klass.parent)
840852
self.assertEqual(klass.frame(), klass)
@@ -888,7 +900,11 @@ def test_method_base_props(self) -> None:
888900
method = klass2["method"]
889901
self.assertEqual(method.name, "method")
890902
self.assertEqual([n.name for n in method.args.args], ["self"])
891-
self.assertEqual(method.doc, "method\n test")
903+
with pytest.warns(DeprecationWarning) as records:
904+
self.assertEqual(method.doc, "method\n test")
905+
assert len(records) == 1
906+
assert isinstance(method.doc_node, nodes.Const)
907+
self.assertEqual(method.doc_node.value, "method\n test")
892908
self.assertEqual(method.fromlineno, 48)
893909
self.assertEqual(method.type, "method")
894910
# class method

tests/unittest_nodes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,9 @@ def func():
16051605
"""
16061606
)
16071607
node: nodes.FunctionDef = astroid.extract_node(code) # type: ignore[assignment]
1608-
assert node.doc == "Docstring"
1608+
with pytest.warns(DeprecationWarning) as records:
1609+
assert node.doc == "Docstring"
1610+
assert len(records) == 1
16091611
assert isinstance(node.doc_node, nodes.Const)
16101612
assert node.doc_node.value == "Docstring"
16111613
assert node.doc_node.lineno == 2
@@ -1621,7 +1623,9 @@ def func():
16211623
"""
16221624
)
16231625
node = astroid.extract_node(code)
1624-
assert node.doc is None
1626+
with pytest.warns(DeprecationWarning) as records:
1627+
assert node.doc is None
1628+
assert len(records) == 1
16251629
assert node.doc_node is None
16261630

16271631

tests/unittest_raw_building.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import unittest
1818

1919
import _io
20+
import pytest
2021

2122
from astroid.builder import AstroidBuilder
2223
from astroid.raw_building import (
@@ -44,12 +45,18 @@ def test_build_module(self) -> None:
4445
def test_build_class(self) -> None:
4546
node = build_class("MyClass")
4647
self.assertEqual(node.name, "MyClass")
47-
self.assertEqual(node.doc, None)
48+
with pytest.warns(DeprecationWarning) as records:
49+
self.assertEqual(node.doc, None)
50+
assert len(records) == 1
51+
self.assertEqual(node.doc_node, None)
4852

4953
def test_build_function(self) -> None:
5054
node = build_function("MyFunction")
5155
self.assertEqual(node.name, "MyFunction")
52-
self.assertEqual(node.doc, None)
56+
with pytest.warns(DeprecationWarning) as records:
57+
self.assertEqual(node.doc, None)
58+
assert len(records) == 1
59+
self.assertEqual(node.doc_node, None)
5360

5461
def test_build_function_args(self) -> None:
5562
args = ["myArgs1", "myArgs2"]

0 commit comments

Comments
 (0)