Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit f22663b

Browse files
committed
Add the ability to skip checking some functions.
There are times when you might not want to check a function's docstring. Specifically, we frequently want to ignore test files. There is already an exemption that causes test functions to be considered non-public. This extends on that idea and makes it configurable so a user can cause a test function to be allowed to have no docstring.
1 parent 50894da commit f22663b

File tree

5 files changed

+70
-8
lines changed

5 files changed

+70
-8
lines changed

src/pydocstyle/checker.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def check_source(
136136
ignore_decorators=None,
137137
property_decorators=None,
138138
ignore_inline_noqa=False,
139+
ignore_functions=None,
139140
):
140141
self.property_decorators = (
141142
{} if property_decorators is None else property_decorators
@@ -150,9 +151,19 @@ def check_source(
150151
len(ignore_decorators.findall(dec.name)) > 0
151152
for dec in definition.decorators
152153
)
154+
# Only skip checking the docstring if absent.
155+
# If the docstring is present, then check it as normal
156+
name_skip = (
157+
ignore_functions is not None
158+
and not definition.docstring
159+
and bool(ignore_functions.findall(definition.name))
160+
)
161+
153162
if (
154-
ignore_inline_noqa or not skipping_all
155-
) and not decorator_skip:
163+
(ignore_inline_noqa or not skipping_all)
164+
and not decorator_skip
165+
and not name_skip
166+
):
156167
error = this_check(
157168
self, definition, definition.docstring
158169
)
@@ -1088,6 +1099,7 @@ def check(
10881099
ignore_decorators=None,
10891100
property_decorators=None,
10901101
ignore_inline_noqa=False,
1102+
ignore_functions=None,
10911103
):
10921104
"""Generate docstring errors that exist in `filenames` iterable.
10931105
@@ -1141,9 +1153,10 @@ def check(
11411153
for error in ConventionChecker().check_source(
11421154
source,
11431155
filename,
1144-
ignore_decorators,
1145-
property_decorators,
1146-
ignore_inline_noqa,
1156+
ignore_decorators=ignore_decorators,
1157+
property_decorators=property_decorators,
1158+
ignore_inline_noqa=ignore_inline_noqa,
1159+
ignore_functions=ignore_functions,
11471160
):
11481161
code = getattr(error, 'code', None)
11491162
if code in checked_codes:

src/pydocstyle/cli.py

+2
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ def run_pydocstyle():
4343
checked_codes,
4444
ignore_decorators,
4545
property_decorators,
46+
ignore_functions,
4647
) in conf.get_files_to_check():
4748
errors.extend(
4849
check(
4950
(filename,),
5051
select=checked_codes,
5152
ignore_decorators=ignore_decorators,
5253
property_decorators=property_decorators,
54+
ignore_functions=ignore_functions,
5355
)
5456
)
5557
except IllegalConfiguration as error:

src/pydocstyle/config.py

+31-3
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class ConfigurationParser:
180180
'match',
181181
'match-dir',
182182
'ignore-decorators',
183+
'ignore-functions',
183184
)
184185
BASE_ERROR_SELECTION_OPTIONS = ('ignore', 'select', 'convention')
185186

@@ -189,6 +190,7 @@ class ConfigurationParser:
189190
DEFAULT_PROPERTY_DECORATORS = (
190191
"property,cached_property,functools.cached_property"
191192
)
193+
DEFAULT_IGNORE_FUNCTIONS_RE = ''
192194
DEFAULT_CONVENTION = conventions.pep257
193195

194196
PROJECT_CONFIG_FILES = (
@@ -263,6 +265,10 @@ def _get_matches(conf):
263265
match_dir_func = re(conf.match_dir + '$').match
264266
return match_func, match_dir_func
265267

268+
def _get_ignore_functions(conf):
269+
"""Return the `ignore_functions` as None or regex."""
270+
return re(conf.ignore_functions) if conf.ignore_functions else None
271+
266272
def _get_ignore_decorators(conf):
267273
"""Return the `ignore_decorators` as None or regex."""
268274
return (
@@ -284,6 +290,7 @@ def _get_property_decorators(conf):
284290
match, match_dir = _get_matches(config)
285291
ignore_decorators = _get_ignore_decorators(config)
286292
property_decorators = _get_property_decorators(config)
293+
ignore_functions = _get_ignore_functions(config)
287294

288295
# Skip any dirs that do not match match_dir
289296
dirs[:] = [d for d in dirs if match_dir(d)]
@@ -296,18 +303,22 @@ def _get_property_decorators(conf):
296303
list(config.checked_codes),
297304
ignore_decorators,
298305
property_decorators,
306+
ignore_functions,
299307
)
300308
else:
301309
config = self._get_config(os.path.abspath(name))
302310
match, _ = _get_matches(config)
303311
ignore_decorators = _get_ignore_decorators(config)
304312
property_decorators = _get_property_decorators(config)
313+
ignore_functions = _get_ignore_functions(config)
314+
305315
if match(os.path.basename(name)):
306316
yield (
307317
name,
308318
list(config.checked_codes),
309319
ignore_decorators,
310320
property_decorators,
321+
ignore_functions,
311322
)
312323

313324
# --------------------------- Private Methods -----------------------------
@@ -509,6 +520,7 @@ def _merge_configuration(self, parent_config, child_options):
509520
'match_dir',
510521
'ignore_decorators',
511522
'property_decorators',
523+
'ignore_functions',
512524
):
513525
kwargs[key] = getattr(child_options, key) or getattr(
514526
parent_config, key
@@ -548,6 +560,7 @@ def _create_check_config(cls, options, use_defaults=True):
548560
'match_dir': "MATCH_DIR_RE",
549561
'ignore_decorators': "IGNORE_DECORATORS_RE",
550562
'property_decorators': "PROPERTY_DECORATORS",
563+
'ignore_functions': "IGNORE_FUNCTIONS_RE",
551564
}
552565
for key, default in defaults.items():
553566
kwargs[key] = (
@@ -780,9 +793,10 @@ def _create_option_parser(cls):
780793
OptionGroup(
781794
parser,
782795
'Note',
783-
'When using --match, --match-dir or --ignore-decorators consider '
784-
'whether you should use a single quote (\') or a double quote ("), '
785-
'depending on your OS, Shell, etc.',
796+
'When using --match, --match-dir, --ignore-decorators or '
797+
'--ignore-functions consider whether you should use a single '
798+
'quote (\') or a double quote ("), depending on your OS, '
799+
'Shell, etc.',
786800
)
787801
)
788802

@@ -899,6 +913,19 @@ def _create_option_parser(cls):
899913
),
900914
)
901915

916+
# Function selection
917+
option(
918+
'--ignore-functions',
919+
metavar='<functions>',
920+
default=None,
921+
help=(
922+
"ignore any functions or methods whose names fit "
923+
"the <functions> regular expression; default is "
924+
"--ignore-functions='{}' which does not ignore any "
925+
"functions.".format(cls.DEFAULT_IGNORE_DECORATORS_RE)
926+
),
927+
)
928+
902929
return parser
903930

904931

@@ -911,6 +938,7 @@ def _create_option_parser(cls):
911938
'match_dir',
912939
'ignore_decorators',
913940
'property_decorators',
941+
'ignore_functions',
914942
),
915943
)
916944

src/tests/test_cases/functions.py

+18
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,21 @@ class inner():
7575
def func_with_weird_backslash():
7676
"""Test a function with a weird backslash.\
7777
"""
78+
79+
80+
def ignored_function():
81+
# This function does not need a docstring
82+
# because the name matches "ignored_function.*"
83+
pass
84+
85+
86+
def ignored_function_with_suffix():
87+
# This function does not need a docstring
88+
# because the name matches "ignored_function.*"
89+
pass
90+
91+
92+
@expect('D103: Missing docstring in public function')
93+
def missing_docstring_function():
94+
# This function should have a docstring.
95+
pass

src/tests/test_definitions.py

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def test_complex_file(test_case):
4444
select=set(ErrorRegistry.get_error_codes()),
4545
ignore_decorators=re.compile('wraps|ignored_decorator'),
4646
property_decorators=DEFAULT_PROPERTY_DECORATORS,
47+
ignore_functions=re.compile("ignored_function.*"),
4748
)
4849
)
4950
for error in results:

0 commit comments

Comments
 (0)