Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3697afa

Browse files
committed
improve docstrings unit test for deprecation, seq args; skip duplicates
1 parentb767e52 commit3697afa

File tree

1 file changed

+111
-24
lines changed

1 file changed

+111
-24
lines changed

‎control/tests/docstrings_test.py

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,71 +17,158 @@
1717
importcontrol.flatsys
1818

1919
# List of functions that we can skip testing (special cases)
20-
skiplist= [
20+
function_skiplist= [
2121
control.ControlPlot.reshape,# needed for legacy interface
22+
control.phase_plot,# legacy function
2223
]
2324

25+
# List of keywords that we can skip testing (special cases)
26+
keyword_skiplist= {
27+
control.input_output_response: ['method'],
28+
control.nyquist_plot: ['color'],# checked separately
29+
control.optimal.solve_ocp: ['method'],# deprecated
30+
control.sisotool: ['kvect'],# deprecated
31+
}
32+
33+
# Decide on the level of verbosity (use -rP when running pytest)
34+
verbose=1
35+
2436
@pytest.mark.parametrize("module, prefix", [
2537
(control,""), (control.flatsys,"flatsys."),
2638
(control.optimal,"optimal."), (control.phaseplot,"phaseplot.")
2739
])
2840
deftest_docstrings(module,prefix):
2941
# Look through every object in the package
30-
print(f"Checking module{module}")
42+
ifverbose>1:
43+
print(f"Checking module{module}")
3144
forname,objininspect.getmembers(module):
3245
# Skip anything that is outside of this module
33-
ifinspect.getmodule(obj)isnotNoneand \
34-
notinspect.getmodule(obj).__name__.startswith('control'):
46+
ifinspect.getmodule(obj)isnotNoneand (
47+
notinspect.getmodule(obj).__name__.startswith('control')
48+
orprefix!=""andinspect.getmodule(obj)!=module):
3549
# Skip anything that isn't part of the control package
3650
continue
3751

3852
ifinspect.isclass(obj):
39-
print(f" Checking class{name}")
53+
ifverbose>1:
54+
print(f" Checking class{name}")
4055
# Check member functions within the class
41-
test_docstrings(obj,prefix+obj.__name__+'.')
56+
test_docstrings(obj,prefix+name+'.')
4257

4358
ifinspect.isfunction(obj):
44-
# Skip anything that is inheritedorhidden
45-
ifinspect.isclass(module)andobj.__name__notinmodule.__dict__ \
46-
orobj.__name__.startswith('_')orobjinskiplist:
59+
# Skip anything that is inherited, hidden,ordeprecated
60+
ifinspect.isclass(module)andnamenotinmodule.__dict__ \
61+
orname.startswith('_')orobjinfunction_skiplist:
4762
continue
4863

49-
# Make sure there is a docstring
50-
print(f" Checking function{name}")
64+
# Get the docstring (skip w/ warning if there isn't one)
65+
ifverbose>1:
66+
print(f" Checking function{name}")
5167
ifobj.__doc__isNone:
5268
warnings.warn(
53-
f"{module.__name__}.{obj.__name__} is missing docstring")
69+
f"{module.__name__}.{name} is missing docstring")
70+
continue
71+
else:
72+
docstring=inspect.getdoc(obj)
73+
source=inspect.getsource(obj)
74+
75+
# Skip deprecated functions
76+
iff"{name} is deprecated"indocstringor \
77+
"function is deprecated"indocstringor \
78+
".. deprecated::"indocstring:
79+
ifverbose>1:
80+
print(" [deprecated]")
5481
continue
55-
82+
83+
eliff"{name} is deprecated"insource:
84+
ifverbose:
85+
print(f"{name} is deprecated, but not documented")
86+
warnings.warn(f"{name} deprecated, but not documented")
87+
continue
88+
5689
# Get the signature for the function
5790
sig=inspect.signature(obj)
5891

5992
# Go through each parameter and make sure it is in the docstring
6093
forargname,parinsig.parameters.items():
61-
ifargname=='self'orargname[0]=='_':
94+
95+
# Look for arguments that we can skip
96+
ifargname=='self'orargname[0]=='_'or \
97+
objinkeyword_skiplistandargnameinkeyword_skiplist[obj]:
6298
continue
63-
64-
ifpar.kind==inspect.Parameter.VAR_KEYWORD:
65-
# Found a keyword argument; look at code for parsing
66-
warnings.warn("keyword argument checks not yet implemented")
99+
100+
# Check for positional arguments
101+
ifpar.kind==inspect.Parameter.VAR_POSITIONAL:
102+
# Too complicated to check
103+
iff"*{argname}"notindocstringandverbose:
104+
print(f"{name} has positional arguments; "
105+
"check manually")
106+
continue
107+
108+
# Check for keyword arguments (then look at code for parsing)
109+
elifpar.kind==inspect.Parameter.VAR_KEYWORD:
110+
# See if we documented the keyward argumnt directly
111+
iff"**{argname}"indocstring:
112+
continue
113+
114+
# Look for direct kwargs argument access
115+
kwargnames=set()
116+
for_,kwargnameinre.findall(
117+
argname+r"(\[|\.pop\(|\.get\()'([\w]+)'",
118+
source):
119+
ifverbose>2:
120+
print(" Found direct keyword argument",
121+
kwargname)
122+
kwargnames.add(kwargname)
123+
124+
# Look for kwargs access via _process_legacy_keyword
125+
forkwargnameinre.findall(
126+
r"_process_legacy_keyword\([\s]*"+argname+
127+
r",[\s]*'[\w]+',[\s]*'([\w]+)'",source):
128+
ifverbose>2:
129+
print(" Found legacy keyword argument",
130+
{kwargname})
131+
kwargnames.add(kwargname)
132+
133+
forkwargnameinkwargnames:
134+
ifobjinkeyword_skiplistand \
135+
kwargnameinkeyword_skiplist[obj]:
136+
continue
137+
ifverbose>3:
138+
print(f" Checking keyword argument{kwargname}")
139+
assert_check_docstring(
140+
name,kwargname,inspect.getdoc(obj),
141+
prefix=prefix)
67142

68143
# Make sure this argument is documented properly in docstring
69144
else:
70-
assert_check_docstring(obj.__name__,argname,obj.__doc__)
145+
ifverbose>3:
146+
print(f" Checking argument{argname}")
147+
assert_check_docstring(
148+
name,argname,docstring,prefix=prefix)
71149

72150

73151
# Utility function to check for an argument in a docstring
74-
def_check_docstring(funcname,argname,docstring):
75-
ifre.search(f" ([\\w]+, )*{argname}(,[\\w]+)*[^ ]:",docstring):
152+
def_check_docstring(funcname,argname,docstring,prefix=""):
153+
funcname=prefix+funcname
154+
ifre.search(
155+
"\n"+r"((\w+|\.{3}), )*"+argname+r"(, (\w+|\.{3}))*:",
156+
docstring):
76157
# Found the string, but not in numpydoc form
158+
ifverbose:
159+
print(f"{funcname}:{argname} docstring missing space")
77160
warnings.warn(f"{funcname} '{argname}' docstring missing space")
78161
returnTrue
79-
80-
elifnotre.search(f" ([\\w]+, )*{argname}(,[\\w]+)* :",docstring):
162+
163+
elifnotre.search(
164+
"\n"+r"((\w+|\.{3}), )*"+argname+r"(, (\w+|\.{3}))* :",
165+
docstring):
81166
# return False
82167
#
83168
# Just issue a warning for now
169+
ifverbose:
170+
print(f"{funcname}:{argname} not documented")
84171
warnings.warn(f"{funcname} '{argname}' not documented")
85172
returnTrue
86-
173+
87174
returnTrue

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp