Fix(python-generator): ignore invalid namespaces #61

Merged
jakob.scheid merged 5 commits from fix/ignore-invalid-python-namespaces into main 2026-10-04 17:19:13 +02:00
19 changed files with 78 additions and 109 deletions
Showing only changes of commit 0f8f9d3cfa - Show all commits

No files matched your search

+10 -3
View File
@@ -37,7 +37,11 @@ from .definitions import (
PythonFunctionDefinition,
)
from .docstrings import PythonDocstring
from .namespaces import PythonModuleNamespace, PythonPackageNamespace
from .namespaces import (
PythonModuleNamespace,
PythonPackageNamespace,
validate_namespace_name,
)
__all__ = [
'PythonDefinitionDocumentationIncludeSections',
@@ -926,9 +930,12 @@ class PythonModuleDocumentationGenerator:
),
**kwargs,
) -> None:
if module_path.name.count('.') > 1:
if not (
module_path.suffix == '.py'
and validate_namespace_name(module_path.stem)
):
raise InvalidNamespaceError(
'namespace identifier cannot contain more than one dot',
'invalid module file name',
namespace_identifier=module_path.name,
)
+26 -3
View File
@@ -14,6 +14,7 @@
from __future__ import annotations
import keyword
from typing import TypeAlias, Union
from ...exceptions import (
@@ -24,27 +25,47 @@ from ...exceptions import (
from ...utils import assert_that_is_instance
__all__ = [
'validate_namespace_name',
'PythonNamespace',
'PythonPackageNamespace',
'PythonModuleNamespace',
]
def validate_namespace_name(name: str) -> bool:
"""
Checks whether a namespace name is valid. Namespace names are valid
if they contain no dots and are valid Python identifiers.
:param name: The namespace name to validate.
:type name: str
:returns: Whether the namespace name is valid.
:rtype: bool
"""
if not isinstance(name, str):
return False
return name.isidentifier() and not keyword.iskeyword(name)
class PythonNamespace:
"""
The base class for all Python namespaces such as modules or packages.
:param name: The name of the namespace.
:type name: str
:raises InvalidNamespaceError: If the namespace name is invalid.
"""
def __init__(self, name: str) -> None:
assert_that_is_instance(name, str)
if not name.strip():
if not validate_namespace_name(name):
raise InvalidNamespaceError(
'invalid namespace',
namespace_identifier=name,
'invalid namespace', namespace_identifier=name
)
self.name = name
@@ -70,6 +91,8 @@ class PythonPackageNamespace(PythonNamespace):
:type name: str
:param sub_namespaces: The sub-namespaces.
:type sub_namespaces: list[PythonNamespace]
:raises InvalidNamespaceError: If the namespace name is invalid.
"""
def __init__(
@@ -25,176 +25,134 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError
[
(
pathlib.Path(
(
'tests/unit/core/python/_core/fil'
'es_with_more_than_one_dot/a.b.c'
),
('tests/unit/core/python/_core/invalid_module_names/a.b.c'),
),
None,
InvalidNamespaceError,
'namespace identifier cannot contain more than one dot: a.b.c',
'invalid module file name: a.b.c',
),
(
pathlib.Path(
('tests/unit/core/python/_core/invalid_module_names/a.b.c.d'),
),
None,
InvalidNamespaceError,
'invalid module file name: a.b.c.d',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/file'
's_with_more_than_one_dot/a.b.c.d'
'tests/unit/core/python/_core/i'
'nvalid_module_names/a.b.c.d.e'
),
),
None,
InvalidNamespaceError,
'namespace identifier cannot contain more than one dot: a.b.c.d',
'invalid module file name: a.b.c.d.e',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files'
'_with_more_than_one_dot/a.b.c.d.e'
'tests/unit/core/python/_core/in'
'valid_module_names/a.b.c.d.e.f'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot cont'
'ain more than one dot: a.b.c.d.e'
),
'invalid module file name: a.b.c.d.e.f',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_'
'with_more_than_one_dot/a.b.c.d.e.f'
'tests/unit/core/python/_core/inv'
'alid_module_names/a.b.c.d.e.f.g'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot conta'
'in more than one dot: a.b.c.d.e.f'
),
'invalid module file name: a.b.c.d.e.f.g',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_w'
'ith_more_than_one_dot/a.b.c.d.e.f.g'
'tests/unit/core/python/_core/inva'
'lid_module_names/a.b.c.d.e.f.g.h'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contai'
'n more than one dot: a.b.c.d.e.f.g'
),
'invalid module file name: a.b.c.d.e.f.g.h',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_wi'
'th_more_than_one_dot/a.b.c.d.e.f.g.h'
'tests/unit/core/python/_core/inval'
'id_module_names/a.b.c.d.e.f.g.h.py'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contain'
' more than one dot: a.b.c.d.e.f.g.h'
),
'invalid module file name: a.b.c.d.e.f.g.h.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_wit'
'h_more_than_one_dot/a.b.c.d.e.f.g.h.py'
'tests/unit/core/python/_core/inva'
'lid_module_names/a.b.c.d.e.f.g.py'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contain m'
'ore than one dot: a.b.c.d.e.f.g.h.py'
),
'invalid module file name: a.b.c.d.e.f.g.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_wi'
'th_more_than_one_dot/a.b.c.d.e.f.g.py'
'tests/unit/core/python/_core/in'
'lid_module_names/a.b.c.d.e.f.py'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contain '
'more than one dot: a.b.c.d.e.f.g.py'
),
'invalid module file name: a.b.c.d.e.f.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_w'
'ith_more_than_one_dot/a.b.c.d.e.f.py'
'tests/unit/core/python/_core/in'
'valid_module_names/a.b.c.d.e.py'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contain'
' more than one dot: a.b.c.d.e.f.py'
),
'invalid module file name: a.b.c.d.e.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files_'
'with_more_than_one_dot/a.b.c.d.e.py'
'tests/unit/core/python/_core/i'
'nvalid_module_names/a.b.c.d.py'
),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot contai'
'n more than one dot: a.b.c.d.e.py'
),
'invalid module file name: a.b.c.d.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/files'
'_with_more_than_one_dot/a.b.c.d.py'
),
('tests/unit/core/python/_core/invalid_module_names/a.b.c.py'),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot conta'
'in more than one dot: a.b.c.d.py'
),
'invalid module file name: a.b.c.py',
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/file'
's_with_more_than_one_dot/a.b.c.py'
),
('tests/unit/core/python/_core/invalid_module_names/a.b.py'),
),
None,
InvalidNamespaceError,
(
'namespace identifier cannot cont'
'ain more than one dot: a.b.c.py'
),
),
(
pathlib.Path(
(
'tests/unit/core/python/_core/fil'
'es_with_more_than_one_dot/a.b.py'
),
),
None,
InvalidNamespaceError,
'namespace identifier cannot contain more than one dot: a.b.py',
'invalid module file name: a.b.py',
),
(
pathlib.Path(
@@ -205,7 +163,7 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError
),
None,
InvalidNamespaceError,
'namespace identifier cannot contain more than one dot: a.b.py',
'invalid module file name: a.b.py',
),
],
)
@@ -28,7 +28,6 @@ class StrSubclass(str): ...
'namespace,expected_name',
[
(PythonModuleNamespace('a'), 'a'),
(PythonModuleNamespace('1'), '1'),
(PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')),
],
)
@@ -41,11 +40,7 @@ def test_PythonModuleNamespace_name_attribute(namespace, expected_name):
[
(PythonModuleNamespace('a'), PythonModuleNamespace('a'), True),
(PythonModuleNamespace('b'), PythonModuleNamespace('b'), True),
(PythonModuleNamespace('1'), PythonModuleNamespace('1'), True),
(PythonModuleNamespace('42'), PythonModuleNamespace('42'), True),
(PythonModuleNamespace('a'), PythonModuleNamespace('b'), False),
(PythonModuleNamespace('0'), PythonModuleNamespace('-0'), False),
(PythonModuleNamespace('1'), PythonModuleNamespace('a'), False),
(PythonModuleNamespace('a'), 'a', False),
(PythonModuleNamespace('a'), 'b', False),
(PythonModuleNamespace('a'), PythonPackageNamespace('a', []), False),
@@ -61,7 +56,6 @@ def test_PythonModuleNamespace___eq__(namespace1, namespace2, expected):
'namespace,expected',
[
(PythonModuleNamespace('a'), "PythonModuleNamespace('a')"),
(PythonModuleNamespace('1'), "PythonModuleNamespace('1')"),
(
PythonModuleNamespace(StrSubclass('a')),
"PythonModuleNamespace('a')",
@@ -24,7 +24,6 @@ class StrSubclass(str): ...
'namespace,expected_name',
[
(PythonNamespace('a'), 'a'),
(PythonNamespace('1'), '1'),
(PythonNamespace(StrSubclass('a')), StrSubclass('a')),
],
)
@@ -36,7 +35,6 @@ def test_PythonNamespace_name_attribute(namespace, expected_name):
'namespace,expected',
[
(PythonNamespace('a'), "PythonNamespace('a')"),
(PythonNamespace('1'), "PythonNamespace('1')"),
(PythonNamespace(StrSubclass('a')), "PythonNamespace('a')"),
],
)
@@ -38,13 +38,6 @@ class StrSubclass(str): ...
),
'a',
),
(
PythonPackageNamespace(
'1',
[],
),
'1',
),
(
PythonPackageNamespace(
StrSubclass('a'),
@@ -291,10 +284,6 @@ def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected):
PythonPackageNamespace('a', []),
"PythonPackageNamespace('a', [])",
),
(
PythonPackageNamespace('1', []),
"PythonPackageNamespace('1', [])",
),
(
PythonPackageNamespace(StrSubclass('a'), []),
"PythonPackageNamespace('a', [])",