docsig 0.86.1__tar.gz → 0.87.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {docsig-0.86.1 → docsig-0.87.1}/LICENSE +5 -0
  2. docsig-0.87.1/LICENSES/SPHINX_LICENSE +39 -0
  3. {docsig-0.86.1 → docsig-0.87.1}/PKG-INFO +15 -10
  4. {docsig-0.86.1 → docsig-0.87.1}/README.rst +11 -5
  5. {docsig-0.86.1 → docsig-0.87.1}/docsig/_check.py +2 -2
  6. {docsig-0.86.1 → docsig-0.87.1}/docsig/_config.py +4 -24
  7. {docsig-0.86.1 → docsig-0.87.1}/docsig/_core.py +8 -4
  8. {docsig-0.86.1 → docsig-0.87.1}/docsig/_decorators.py +25 -10
  9. {docsig-0.86.1 → docsig-0.87.1}/docsig/_files.py +4 -12
  10. {docsig-0.86.1 → docsig-0.87.1}/docsig/_main.py +4 -1
  11. {docsig-0.86.1 → docsig-0.87.1}/docsig/_module.py +22 -13
  12. {docsig-0.86.1 → docsig-0.87.1}/docsig/_report.py +90 -29
  13. {docsig-0.86.1 → docsig-0.87.1}/docsig/_stub.py +6 -4
  14. {docsig-0.86.1 → docsig-0.87.1}/docsig/_utils.py +23 -0
  15. docsig-0.87.1/docsig/_vendor/sphinx/ext/napoleon.py +1373 -0
  16. {docsig-0.86.1 → docsig-0.87.1}/docsig/_version.py +1 -1
  17. {docsig-0.86.1 → docsig-0.87.1}/pyproject.toml +47 -38
  18. {docsig-0.86.1 → docsig-0.87.1}/docsig/__init__.py +0 -0
  19. {docsig-0.86.1 → docsig-0.87.1}/docsig/__main__.py +0 -0
  20. {docsig-0.86.1 → docsig-0.87.1}/docsig/_directives.py +0 -0
  21. {docsig-0.86.1 → docsig-0.87.1}/docsig/_hooks.py +0 -0
  22. {docsig-0.86.1 → docsig-0.87.1}/docsig/_parsers.py +0 -0
  23. {docsig-0.86.1 → docsig-0.87.1}/docsig/messages.py +0 -0
  24. {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/__init__.py +0 -0
  25. {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/_flake8.py +0 -0
  26. {docsig-0.86.1 → docsig-0.87.1}/docsig/plugin/_validate_pyproject.py +0 -0
  27. {docsig-0.86.1 → docsig-0.87.1}/docsig/py.typed +0 -0
@@ -19,3 +19,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
23
+ This project includes code derived from Sphinx (sphinx.ext.napoleon),
24
+ licensed under the BSD 2-Clause License.
25
+
26
+ See licenses/SPHINX_LICENSE for details.
@@ -0,0 +1,39 @@
1
+ This project includes code derived from Sphinx (sphinx.ext.napoleon).
2
+
3
+ Sphinx is a documentation generator: https://www.sphinx-doc.org/
4
+
5
+ The original code is licensed under the BSD 2-Clause License.
6
+
7
+ ---
8
+
9
+ License for Sphinx
10
+ ==================
11
+
12
+ Unless otherwise indicated, all code in the Sphinx project is licenced under the
13
+ two clause BSD licence below.
14
+
15
+ Copyright (c) 2007-2025 by the Sphinx team (see AUTHORS file).
16
+ All rights reserved.
17
+
18
+ Redistribution and use in source and binary forms, with or without
19
+ modification, are permitted provided that the following conditions are
20
+ met:
21
+
22
+ * Redistributions of source code must retain the above copyright
23
+ notice, this list of conditions and the following disclaimer.
24
+
25
+ * Redistributions in binary form must reproduce the above copyright
26
+ notice, this list of conditions and the following disclaimer in the
27
+ documentation and/or other materials provided with the distribution.
28
+
29
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,23 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docsig
3
- Version: 0.86.1
3
+ Version: 0.87.1
4
4
  Summary: Check signature params for proper documentation
5
- License: MIT
5
+ License-Expression: MIT
6
6
  License-File: LICENSE
7
+ License-File: LICENSES/SPHINX_LICENSE
7
8
  Keywords: check,docs,docstring,params,signature
8
9
  Author: jshwi
9
10
  Author-email: s.whitlock@live.com
10
11
  Maintainer: jshwi
11
12
  Maintainer-email: s.whitlock@live.com
12
- Requires-Python: >=3.10,<4.0
13
- Classifier: License :: OSI Approved :: MIT License
13
+ Requires-Python: >=3.10
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Programming Language :: Python :: 3.14
20
- Requires-Dist: Sphinx (>=7,<9)
21
20
  Requires-Dist: astroid (>=4.0.2,<5.0.0)
22
21
  Requires-Dist: pathspec (>=0.12.1,<1.2.0)
23
22
  Requires-Dist: tomli (>=2.0.1,<3.0.0)
@@ -36,7 +35,7 @@ Description-Content-Type: text/x-rst
36
35
 
37
36
  |
38
37
 
39
- |License| |PyPI| |CI| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
38
+ |License| |PyPI| |Jetbrains Plugin| |CI| |Jetbrains Build| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
40
39
 
41
40
  .. |License| image:: https://img.shields.io/badge/License-MIT-yellow.svg
42
41
  :target: https://opensource.org/licenses/MIT
@@ -74,6 +73,12 @@ Description-Content-Type: text/x-rst
74
73
  .. |Security Status| image:: https://img.shields.io/badge/security-bandit-yellow.svg
75
74
  :target: https://github.com/PyCQA/bandit
76
75
  :alt: Security Status
76
+ .. |Jetbrains Plugin| image:: https://img.shields.io/jetbrains/plugin/v/32129.svg
77
+ :target: https://plugins.jetbrains.com/plugin/32129
78
+ :alt: Jetbrains Plugin
79
+ .. |Jetbrains Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml/badge.svg
80
+ :target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
81
+ :alt: Build
77
82
 
78
83
  Check Python signature params for proper documentation
79
84
  -------------------------------------------------------
@@ -87,7 +92,7 @@ Maintain accurate and up-to-date Python documentation by automatically checking
87
92
  that all parameters in function signatures match their docstring documentation.
88
93
  Use docsig as a standalone tool, integrate it with ``flake8``, or add it as a
89
94
  ``pre-commit`` hook to catch documentation issues before they reach your
90
- repository.
95
+ repository. **docsig** is also available as a ``PyCharm`` plugin
91
96
 
92
97
  Contributing
93
98
  ------------
@@ -195,7 +200,7 @@ ensure your installation has registered `docsig`
195
200
  .. code-block:: console
196
201
 
197
202
  $ flake8 --version
198
- 7.3.0 (docsig: 0.86.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
203
+ 7.3.0 (docsig: 0.87.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
199
204
 
200
205
  And now use `flake8` to lint your files
201
206
 
@@ -281,7 +286,7 @@ Standalone
281
286
 
282
287
  repos:
283
288
  - repo: https://github.com/jshwi/docsig
284
- rev: v0.86.1
289
+ rev: v0.87.1
285
290
  hooks:
286
291
  - id: docsig
287
292
  args:
@@ -300,7 +305,7 @@ or integrated with ``flake8``
300
305
  hooks:
301
306
  - id: flake8
302
307
  additional_dependencies:
303
- - docsig==0.86.1
308
+ - docsig==0.87.1
304
309
  args:
305
310
  - "--sig-check-class"
306
311
  - "--sig-check-dunders"
@@ -7,7 +7,7 @@
7
7
 
8
8
  |
9
9
 
10
- |License| |PyPI| |CI| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
10
+ |License| |PyPI| |Jetbrains Plugin| |CI| |Jetbrains Build| |CodeQL| |pre-commit.ci status| |codecov.io| |readthedocs.org| |python3.10| |Black| |isort| |pylint| |Security Status|
11
11
 
12
12
  .. |License| image:: https://img.shields.io/badge/License-MIT-yellow.svg
13
13
  :target: https://opensource.org/licenses/MIT
@@ -45,6 +45,12 @@
45
45
  .. |Security Status| image:: https://img.shields.io/badge/security-bandit-yellow.svg
46
46
  :target: https://github.com/PyCQA/bandit
47
47
  :alt: Security Status
48
+ .. |Jetbrains Plugin| image:: https://img.shields.io/jetbrains/plugin/v/32129.svg
49
+ :target: https://plugins.jetbrains.com/plugin/32129
50
+ :alt: Jetbrains Plugin
51
+ .. |Jetbrains Build| image:: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml/badge.svg
52
+ :target: https://github.com/jshwi/docsig/actions/workflows/build-intellij-plugin.yaml
53
+ :alt: Build
48
54
 
49
55
  Check Python signature params for proper documentation
50
56
  -------------------------------------------------------
@@ -58,7 +64,7 @@ Maintain accurate and up-to-date Python documentation by automatically checking
58
64
  that all parameters in function signatures match their docstring documentation.
59
65
  Use docsig as a standalone tool, integrate it with ``flake8``, or add it as a
60
66
  ``pre-commit`` hook to catch documentation issues before they reach your
61
- repository.
67
+ repository. **docsig** is also available as a ``PyCharm`` plugin
62
68
 
63
69
  Contributing
64
70
  ------------
@@ -166,7 +172,7 @@ ensure your installation has registered `docsig`
166
172
  .. code-block:: console
167
173
 
168
174
  $ flake8 --version
169
- 7.3.0 (docsig: 0.86.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
175
+ 7.3.0 (docsig: 0.87.1, mccabe: 0.7.0, pycodestyle: 2.14.0, pyflakes: 3.4.0) CPython 3.10.19 on Darwin
170
176
 
171
177
  And now use `flake8` to lint your files
172
178
 
@@ -252,7 +258,7 @@ Standalone
252
258
 
253
259
  repos:
254
260
  - repo: https://github.com/jshwi/docsig
255
- rev: v0.86.1
261
+ rev: v0.87.1
256
262
  hooks:
257
263
  - id: docsig
258
264
  args:
@@ -271,7 +277,7 @@ or integrated with ``flake8``
271
277
  hooks:
272
278
  - id: flake8
273
279
  additional_dependencies:
274
- - docsig==0.86.1
280
+ - docsig==0.87.1
275
281
  args:
276
282
  - "--sig-check-class"
277
283
  - "--sig-check-dunders"
@@ -6,8 +6,8 @@ docsig._check
6
6
  from ._config import Config as _Config
7
7
  from ._module import Function as _Function
8
8
  from ._module import Parent as _Parent
9
- from ._report import Failure as _Failure
10
9
  from ._report import Failures as _Failures
10
+ from ._report import check_function as _check_function
11
11
 
12
12
 
13
13
  def _should_check_function(
@@ -47,7 +47,7 @@ def _run_check(
47
47
  parent,
48
48
  config,
49
49
  ):
50
- failure = _Failure(child, config)
50
+ failure = _check_function(child, config)
51
51
  if failure:
52
52
  failures.append(failure)
53
53
 
@@ -6,7 +6,6 @@ Config dataclasses, pyproject.toml loader, and commandline.
6
6
  """
7
7
 
8
8
  import argparse as _argparse
9
- import os as _os
10
9
  import re as _re
11
10
  import typing as _t
12
11
  from dataclasses import dataclass as _dataclass
@@ -15,6 +14,7 @@ from pathlib import Path as _Path
15
14
 
16
15
  import tomli as _tomli
17
16
 
17
+ from ._utils import get_parent_that_has as _get_parent_that_has
18
18
  from ._version import __version__
19
19
  from .messages import Messages as _Messages
20
20
 
@@ -26,31 +26,18 @@ def _split_comma(value: str) -> list[str]:
26
26
  return [i.replace("\\,", ",") for i in _re.split(r"(?<!\\),", value)]
27
27
 
28
28
 
29
- # attempt to locate a pyproject.toml file if one exists in parents
30
- def _find_pyproject_toml(path: _Path | None = None) -> _Path | None:
31
- if not path:
32
- path = _Path.cwd()
33
-
34
- pyproject_toml = path / PYPROJECT_TOML
35
- if pyproject_toml.is_file():
36
- return pyproject_toml
37
-
38
- if str(path) == _os.path.abspath(_os.sep):
39
- return None
40
-
41
- return _find_pyproject_toml(path.parent)
42
-
43
-
44
29
  def get_config(prog: str) -> dict[str, _t.Any]:
45
30
  """Return the program's tool-section config from pyproject.toml.
46
31
 
47
32
  :param prog: Program name.
48
33
  :return: Config dict, or empty dict if no config is found.
49
34
  """
50
- pyproject_file = _find_pyproject_toml()
35
+ # attempt to locate a pyproject.toml file if one exists in parents
36
+ pyproject_file = _get_parent_that_has(PYPROJECT_TOML)
51
37
  if pyproject_file is None:
52
38
  return {}
53
39
 
40
+ pyproject_file /= PYPROJECT_TOML
54
41
  return {
55
42
  k.replace("-", "_"): v
56
43
  for k, v in _tomli.loads(pyproject_file.read_text())
@@ -220,17 +207,10 @@ def build_parser() -> _ArgumentParser:
220
207
  action="store_true",
221
208
  help="ignore kwargs prefixed with two asterisks",
222
209
  )
223
- parser.add_argument(
224
- "-i",
225
- action="store_true",
226
- help=_argparse.SUPPRESS,
227
- dest="ignore_no_params",
228
- )
229
210
  parser.add_argument(
230
211
  "--ignore-no-params",
231
212
  action="store_true",
232
213
  help="ignore docstrings where parameters are not documented",
233
- dest="ignore_no_params",
234
214
  )
235
215
  parser.add_argument(
236
216
  "--ignore-typechecker",
@@ -21,7 +21,9 @@ from ._files import Files as _Files
21
21
  from ._parsers import parse_from_file as _parse_from_file
22
22
  from ._parsers import parse_from_string as _parse_from_string
23
23
  from ._report import Failures as _Failures
24
+ from ._report import RetCode as _RetCode
24
25
  from ._report import report as _report
26
+ from ._utils import get_parent_that_has as _get_parent_that_has
25
27
  from ._utils import print_checks as _print_checks
26
28
  from .messages import E as _E
27
29
  from .messages import Messages as _Messages
@@ -211,6 +213,7 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
211
213
  logger = _logging.getLogger(__package__)
212
214
  logger.debug(_pformat(config))
213
215
  if config.list_checks:
216
+ # noinspection PyNoneFunctionAssignment
214
217
  return int(bool(_print_checks())) # type: ignore
215
218
 
216
219
  if string:
@@ -218,11 +221,12 @@ def docsig( # pylint: disable=too-many-locals,too-many-arguments
218
221
  failures = _run_checks(module, config)
219
222
  return _report(failures, config)
220
223
 
221
- retcodes = [0]
222
- files = _Files(path, config.filters)
224
+ retcodes = _RetCode()
225
+ repo = _get_parent_that_has(".git/HEAD")
226
+ files = _Files(path, config.filters, repo)
223
227
  for file in files:
224
228
  failures = runner(file, config)
225
229
  retcode = _report(failures, config, str(file))
226
- retcodes.append(retcode)
230
+ retcodes.add(retcode)
227
231
 
228
- return max(retcodes)
232
+ return retcodes.result
@@ -4,10 +4,13 @@ docsig._decorators
4
4
  """
5
5
 
6
6
  import functools as _functools
7
+ import json as _json
8
+ import os as _os
7
9
  import sys as _sys
8
10
  import typing as _t
9
11
  from pathlib import Path as _Path
10
12
 
13
+ from ._report import RetCode as _RetCode
11
14
  from .messages import E as _E
12
15
 
13
16
  _FuncType = _t.Callable[..., int]
@@ -35,6 +38,7 @@ def parse_msgs(func: _WrappedFuncType) -> _WrappedFuncType:
35
38
  return _wrapper
36
39
 
37
40
 
41
+ # TODO: make report json by default and wrap with a reporter for cli
38
42
  def validate_args(func: _FuncType) -> _WrappedFuncType:
39
43
  """Validate arguments before calling the wrapped function.
40
44
 
@@ -51,29 +55,31 @@ def validate_args(func: _FuncType) -> _WrappedFuncType:
51
55
 
52
56
  @_functools.wraps(func)
53
57
  def _wrapper(*args: str | _Path, **kwargs: _t.Any) -> int:
54
- stderr = []
58
+ format_json = _os.getenv("_DOCSIG_FORMAT_JSON") is not None
59
+ retcode = _RetCode(2)
60
+ errors = []
55
61
  if not kwargs.get("list_checks", False):
56
62
  if not args and not kwargs.get("string"):
57
- stderr.append(
63
+ errors.append(
58
64
  "the following arguments are required: path(s) or string",
59
65
  )
60
66
 
61
67
  for message in kwargs.get("disable") or []:
62
68
  if not message.isknown:
63
- stderr.append(
69
+ errors.append(
64
70
  f"unknown option to disable '{message.description}'",
65
71
  )
66
72
 
67
73
  for message in kwargs.get("target") or []:
68
74
  if not message.isknown:
69
- stderr.append(
75
+ errors.append(
70
76
  f"unknown option to target '{message.description}'",
71
77
  )
72
78
 
73
79
  if kwargs.get("check_class") and kwargs.get(
74
80
  "check_class_constructor",
75
81
  ):
76
- stderr.append(
82
+ errors.append(
77
83
  "argument to check class constructor not allowed with"
78
84
  " argument to check class",
79
85
  )
@@ -84,13 +90,22 @@ def validate_args(func: _FuncType) -> _WrappedFuncType:
84
90
  # passing both commandline args as argparse won't
85
91
  # allow it, therefore, this must be an issue with
86
92
  # the pyproject.toml configuration
87
- stderr.append(
93
+ errors.append(
88
94
  "please check your pyproject.toml configuration",
89
95
  )
90
-
91
- if stderr:
92
- print("\n".join(stderr), file=_sys.stderr)
93
- return 2
96
+ if errors:
97
+ message = "\n".join(errors)
98
+ if format_json:
99
+ obj = [ # pragma: no cover
100
+ {"line": None, "message": message, "exit": retcode.result},
101
+ ]
102
+ if format_json: # pragma: no cover
103
+ print(_json.dumps(obj).strip()) # pragma: no cover
104
+
105
+ else:
106
+ print(message, file=_sys.stderr)
107
+
108
+ return retcode.result
94
109
 
95
110
  return func(*args, **kwargs)
96
111
 
@@ -20,18 +20,8 @@ FILE_INFO = "%s: %s"
20
20
 
21
21
 
22
22
  class _Gitignore(_PathSpec):
23
- def _get_repo_relative_to(self, path: _Path) -> _Path | None:
24
- if (path / ".git" / "HEAD").is_file():
25
- return path
26
-
27
- if str(path) == _os.path.abspath(_os.sep):
28
- return None
29
-
30
- return self._get_repo_relative_to(path.parent)
31
-
32
- def __init__(self) -> None:
23
+ def __init__(self, repo: _Path | None = None) -> None:
33
24
  patterns = []
34
- repo = self._get_repo_relative_to(_Path.cwd())
35
25
  # only consider gitignore patterns valid if inside a git repo
36
26
  # there might be stray gitignore files lying about
37
27
  if repo is not None:
@@ -73,16 +63,18 @@ class Files(list[_Path]):
73
63
 
74
64
  :param paths: Path(s) to collect (files or directories).
75
65
  :param filters: Filters object.
66
+ :param repo: Path to the repo root.
76
67
  """
77
68
 
78
69
  def __init__(
79
70
  self,
80
71
  paths: tuple[str | _Path, ...],
81
72
  filters: _Filters,
73
+ repo: _Path | None = None,
82
74
  ) -> None:
83
75
  super().__init__()
84
76
  self._include_ignored = filters.include_ignored
85
- self._gitignore = _Gitignore()
77
+ self._gitignore = _Gitignore(repo)
86
78
  logger = _logging.getLogger(__package__)
87
79
  for path in paths:
88
80
  self._populate(_Path(path))
@@ -5,6 +5,7 @@ docsig._main
5
5
  CLI entry point that parses args and runs docsig.
6
6
  """
7
7
 
8
+ import os as _os
8
9
  import sys as _sys
9
10
  import warnings as _warnings
10
11
 
@@ -20,7 +21,6 @@ def _warn_on_deprecated_short_flags() -> None:
20
21
  "-o": "--check-overridden",
21
22
  "-p": "--check-protected",
22
23
  "-P": "--check-property-returns",
23
- "-i": "--ignore-no-params",
24
24
  }
25
25
  raw_args = _sys.argv[1:]
26
26
  expanded_flags = []
@@ -47,6 +47,9 @@ def main() -> str | int:
47
47
 
48
48
  :return: Exit code (non-zero if any check failed).
49
49
  """
50
+ if _os.getenv("_DOCSIG_FORMAT_JSON"):
51
+ _warnings.simplefilter("ignore", FutureWarning) # pragma: no cover
52
+
50
53
  _warn_on_deprecated_short_flags()
51
54
  a = _parse_args()
52
55
  _excepthook(a.no_ansi)
@@ -57,7 +57,6 @@ class Parent: # pylint: disable=too-many-instance-attributes
57
57
  _ast.nodes.Module
58
58
  | _ast.nodes.ClassDef
59
59
  | _ast.nodes.FunctionDef
60
- | _ast.nodes.NodeNG
61
60
  | None
62
61
  ) = None,
63
62
  directives: _Directives | None = None,
@@ -82,7 +81,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
82
81
  # later inspected to report on the module error (the
83
82
  # report doesn't analyze modules or classes - it
84
83
  # analyzes functions)
85
- self._children.append(Function(file, error=error))
84
+ self._children.append(Function(error=error))
86
85
  else:
87
86
  self._name = node.name
88
87
  self._parse_ast(node, file)
@@ -101,13 +100,13 @@ class Parent: # pylint: disable=too-many-instance-attributes
101
100
  # resolved in the directive object, they are needed to notify
102
101
  # the user in the case that they are invalid
103
102
  parent_comments, parent_disabled = self._directives.get(
104
- node.lineno,
103
+ node.lineno or 0,
105
104
  (_Comments(), _Messages()),
106
105
  )
107
106
  if hasattr(node, "body"):
108
107
  for subnode in node.body:
109
108
  comments, disabled = self._directives.get(
110
- subnode.lineno,
109
+ subnode.lineno or 0,
111
110
  (_Comments(), _Messages()),
112
111
  )
113
112
  comments.extend(parent_comments)
@@ -119,7 +118,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
119
118
  for name in subnode.names:
120
119
  original, alias = name
121
120
  self._imports[original] = alias or original
122
- elif isinstance(subnode, _ast.FunctionDef):
121
+ elif isinstance(subnode, _ast.nodes.FunctionDef):
123
122
  func = Function(
124
123
  subnode,
125
124
  comments,
@@ -147,7 +146,7 @@ class Parent: # pylint: disable=too-many-instance-attributes
147
146
  )
148
147
 
149
148
  self._children.append(func)
150
- elif isinstance(subnode, _ast.ClassDef):
149
+ elif isinstance(subnode, _ast.nodes.ClassDef):
151
150
  self._children.append(
152
151
  Parent(
153
152
  subnode,
@@ -192,7 +191,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
192
191
  # pylint: disable-next=too-many-arguments,too-many-positional-arguments
193
192
  def __init__(
194
193
  self,
195
- node: _ast.nodes.FunctionDef | _ast.nodes.NodeNG | None = None,
194
+ node: _ast.nodes.FunctionDef | None = None,
196
195
  comments: _Comments | None = None,
197
196
  directives: _Directives | None = None,
198
197
  messages: _Messages | None = None,
@@ -216,7 +215,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
216
215
  self._docstring = _Docstring()
217
216
  self._lineno = 0
218
217
  self._error = error
219
- if node is not None:
218
+ if node is not None and node.parent is not None:
220
219
  self._parent = node.parent.frame()
221
220
  self._decorators = node.decorators
222
221
  self._lineno = node.lineno
@@ -230,7 +229,11 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
230
229
  node,
231
230
  self._config.ignore,
232
231
  )
233
- if self.isinit and not self._config.check.class_constructor:
232
+ if (
233
+ self.isinit
234
+ and not self._config.check.class_constructor
235
+ and not isinstance(self._parent, _ast.nodes.Lambda)
236
+ ):
234
237
  # docstring for __init__ is expected on the class
235
238
  # docstring
236
239
  relevant_doc_node = self._parent.doc_node
@@ -259,7 +262,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
259
262
  @property
260
263
  def ismethod(self) -> bool:
261
264
  """Whether this function is defined in a class (method)."""
262
- return isinstance(self._parent, _ast.ClassDef)
265
+ return isinstance(self._parent, _ast.nodes.ClassDef)
263
266
 
264
267
  @property
265
268
  def isproperty(self) -> bool:
@@ -282,11 +285,16 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
282
285
  @property
283
286
  def isoverridden(self) -> bool:
284
287
  """Whether this function overrides a base class method."""
285
- if self.ismethod and not self.isinit and self._parent is not None:
288
+ if (
289
+ self.ismethod
290
+ and not self.isinit
291
+ and self._parent is not None
292
+ and isinstance(self._parent, _ast.nodes.ClassDef)
293
+ ):
286
294
  for ancestor in self._parent.ancestors():
287
295
  if self.name in ancestor and isinstance(
288
296
  ancestor[self.name],
289
- _ast.FunctionDef,
297
+ _ast.nodes.FunctionDef,
290
298
  ):
291
299
  return True
292
300
 
@@ -324,6 +332,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
324
332
  | _ast.nodes.Module
325
333
  | _ast.nodes.ClassDef
326
334
  | _ast.nodes.Lambda
335
+ | None
327
336
  ):
328
337
  """Function's parent node."""
329
338
  return self._parent
@@ -331,7 +340,7 @@ class Function(Parent): # pylint: disable=too-many-instance-attributes
331
340
  @property
332
341
  def lineno(self) -> int:
333
342
  """Line number of function declaration."""
334
- return self._lineno
343
+ return self._lineno or 0
335
344
 
336
345
  @property
337
346
  def signature(self) -> _Signature: