Skip to content

Commit 6940715

Browse files
[refactor] Create a file for the DocstringChecker in pylint.checker.base
1 parent 84d22cf commit 6940715

File tree

2 files changed

+211
-189
lines changed

2 files changed

+211
-189
lines changed

pylint/checkers/base/__init__.py

Lines changed: 2 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,9 @@
1717
from pylint.checkers import utils
1818
from pylint.checkers.base.basic_checker import _BasicChecker
1919
from pylint.checkers.base.comparison_checker import ComparisonChecker
20+
from pylint.checkers.base.docstring_checker import DocStringChecker
2021
from pylint.checkers.base.pass_checker import PassChecker
21-
from pylint.checkers.utils import (
22-
infer_all,
23-
is_overload_stub,
24-
is_property_deleter,
25-
is_property_setter,
26-
)
22+
from pylint.checkers.utils import infer_all, is_property_deleter, is_property_setter
2723
from pylint.reporters.ureports import nodes as reporter_nodes
2824
from pylint.utils import LinterStats
2925
from pylint.utils.utils import get_global_option
@@ -132,8 +128,6 @@ class AnyStyle(NamingStyle):
132128
)
133129
}
134130

135-
# do not require a doc string on private/system methods
136-
NO_REQUIRED_DOC_RGX = re.compile("^_")
137131
REVERSED_PROTOCOL_METHOD = "__reversed__"
138132
SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
139133
REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
@@ -2146,187 +2140,6 @@ def _check_typevar_variance(self, name: str, node: nodes.AssignName) -> None:
21462140
)
21472141

21482142

2149-
class DocStringChecker(_BasicChecker):
2150-
msgs = {
2151-
"C0112": (
2152-
"Empty %s docstring",
2153-
"empty-docstring",
2154-
"Used when a module, function, class or method has an empty "
2155-
"docstring (it would be too easy ;).",
2156-
{"old_names": [("W0132", "old-empty-docstring")]},
2157-
),
2158-
"C0114": (
2159-
"Missing module docstring",
2160-
"missing-module-docstring",
2161-
"Used when a module has no docstring."
2162-
"Empty modules do not require a docstring.",
2163-
{"old_names": [("C0111", "missing-docstring")]},
2164-
),
2165-
"C0115": (
2166-
"Missing class docstring",
2167-
"missing-class-docstring",
2168-
"Used when a class has no docstring."
2169-
"Even an empty class must have a docstring.",
2170-
{"old_names": [("C0111", "missing-docstring")]},
2171-
),
2172-
"C0116": (
2173-
"Missing function or method docstring",
2174-
"missing-function-docstring",
2175-
"Used when a function or method has no docstring."
2176-
"Some special methods like __init__ do not require a "
2177-
"docstring.",
2178-
{"old_names": [("C0111", "missing-docstring")]},
2179-
),
2180-
}
2181-
options = (
2182-
(
2183-
"no-docstring-rgx",
2184-
{
2185-
"default": NO_REQUIRED_DOC_RGX,
2186-
"type": "regexp",
2187-
"metavar": "<regexp>",
2188-
"help": "Regular expression which should only match "
2189-
"function or class names that do not require a "
2190-
"docstring.",
2191-
},
2192-
),
2193-
(
2194-
"docstring-min-length",
2195-
{
2196-
"default": -1,
2197-
"type": "int",
2198-
"metavar": "<int>",
2199-
"help": (
2200-
"Minimum line length for functions/classes that"
2201-
" require docstrings, shorter ones are exempt."
2202-
),
2203-
},
2204-
),
2205-
)
2206-
2207-
def open(self):
2208-
self.linter.stats.reset_undocumented()
2209-
2210-
@utils.check_messages("missing-docstring", "empty-docstring")
2211-
def visit_module(self, node: nodes.Module) -> None:
2212-
self._check_docstring("module", node)
2213-
2214-
@utils.check_messages("missing-docstring", "empty-docstring")
2215-
def visit_classdef(self, node: nodes.ClassDef) -> None:
2216-
if self.config.no_docstring_rgx.match(node.name) is None:
2217-
self._check_docstring("class", node)
2218-
2219-
@utils.check_messages("missing-docstring", "empty-docstring")
2220-
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
2221-
if self.config.no_docstring_rgx.match(node.name) is None:
2222-
ftype = "method" if node.is_method() else "function"
2223-
if (
2224-
is_property_setter(node)
2225-
or is_property_deleter(node)
2226-
or is_overload_stub(node)
2227-
):
2228-
return
2229-
2230-
if isinstance(node.parent.frame(future=True), nodes.ClassDef):
2231-
overridden = False
2232-
confidence = (
2233-
interfaces.INFERENCE
2234-
if utils.has_known_bases(node.parent.frame(future=True))
2235-
else interfaces.INFERENCE_FAILURE
2236-
)
2237-
# check if node is from a method overridden by its ancestor
2238-
for ancestor in node.parent.frame(future=True).ancestors():
2239-
if ancestor.qname() == "builtins.object":
2240-
continue
2241-
if node.name in ancestor and isinstance(
2242-
ancestor[node.name], nodes.FunctionDef
2243-
):
2244-
overridden = True
2245-
break
2246-
self._check_docstring(
2247-
ftype, node, report_missing=not overridden, confidence=confidence # type: ignore[arg-type]
2248-
)
2249-
elif isinstance(node.parent.frame(future=True), nodes.Module):
2250-
self._check_docstring(ftype, node) # type: ignore[arg-type]
2251-
else:
2252-
return
2253-
2254-
visit_asyncfunctiondef = visit_functiondef
2255-
2256-
def _check_docstring(
2257-
self,
2258-
node_type: Literal["class", "function", "method", "module"],
2259-
node,
2260-
report_missing=True,
2261-
confidence=interfaces.HIGH,
2262-
):
2263-
"""Check if the node has a non-empty docstring."""
2264-
docstring = node.doc_node.value if node.doc_node else None
2265-
if docstring is None:
2266-
docstring = _infer_dunder_doc_attribute(node)
2267-
2268-
if docstring is None:
2269-
if not report_missing:
2270-
return
2271-
lines = utils.get_node_last_lineno(node) - node.lineno
2272-
2273-
if node_type == "module" and not lines:
2274-
# If the module does not have a body, there's no reason
2275-
# to require a docstring.
2276-
return
2277-
max_lines = self.config.docstring_min_length
2278-
2279-
if node_type != "module" and max_lines > -1 and lines < max_lines:
2280-
return
2281-
if node_type == "class":
2282-
self.linter.stats.undocumented["klass"] += 1
2283-
else:
2284-
self.linter.stats.undocumented[node_type] += 1
2285-
if (
2286-
node.body
2287-
and isinstance(node.body[0], nodes.Expr)
2288-
and isinstance(node.body[0].value, nodes.Call)
2289-
):
2290-
# Most likely a string with a format call. Let's see.
2291-
func = utils.safe_infer(node.body[0].value.func)
2292-
if isinstance(func, astroid.BoundMethod) and isinstance(
2293-
func.bound, astroid.Instance
2294-
):
2295-
# Strings.
2296-
if func.bound.name in {"str", "unicode", "bytes"}:
2297-
return
2298-
if node_type == "module":
2299-
message = "missing-module-docstring"
2300-
elif node_type == "class":
2301-
message = "missing-class-docstring"
2302-
else:
2303-
message = "missing-function-docstring"
2304-
self.add_message(message, node=node, confidence=confidence)
2305-
elif not docstring.strip():
2306-
if node_type == "class":
2307-
self.linter.stats.undocumented["klass"] += 1
2308-
else:
2309-
self.linter.stats.undocumented[node_type] += 1
2310-
self.add_message(
2311-
"empty-docstring", node=node, args=(node_type,), confidence=confidence
2312-
)
2313-
2314-
2315-
def _infer_dunder_doc_attribute(node):
2316-
# Try to see if we have a `__doc__` attribute.
2317-
try:
2318-
docstring = node["__doc__"]
2319-
except KeyError:
2320-
return None
2321-
2322-
docstring = utils.safe_infer(docstring)
2323-
if not docstring:
2324-
return None
2325-
if not isinstance(docstring, nodes.Const):
2326-
return None
2327-
return docstring.value
2328-
2329-
23302143
def register(linter: "PyLinter") -> None:
23312144
linter.register_checker(BasicErrorChecker(linter))
23322145
linter.register_checker(BasicChecker(linter))

0 commit comments

Comments
 (0)