diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 01926dc..7f0d925 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,11 @@ - Python namespace name conflict when package and module have the same name - Internal namespace name error which occured when generating Python documentation with `.` as the project directory +- Ignore Python namespaces with invalid names during documentation generation + +### Changed + +- Stricter Python namespace name validation ## [0.2.0] - 2026-09-28 diff --git a/src/jcloud_docsgen/core/python/_core.py b/src/jcloud_docsgen/core/python/_core.py index 78b3f14..2df4681 100644 --- a/src/jcloud_docsgen/core/python/_core.py +++ b/src/jcloud_docsgen/core/python/_core.py @@ -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, ) @@ -1107,12 +1114,18 @@ class PythonDocumentationGenerator: ) for entry in directory.iterdir(): - if entry.is_dir() and ( - entry.parent / (f'{entry.stem}.py') - ).resolve() not in [e.resolve() for e in entry.parent.iterdir()]: - namespace.add(self._namespace(entry)) - elif entry.suffix == '.py': - namespace.add(PythonModuleNamespace(entry.stem)) + try: + if entry.is_dir() and ( + entry.parent / (f'{entry.stem}.py') + ).resolve() not in [ + e.resolve() for e in entry.parent.iterdir() + ]: + namespace.add(self._namespace(entry)) + elif entry.suffix == '.py': + namespace.add(PythonModuleNamespace(entry.stem)) + except InvalidNamespaceError: + # the name of the directory or module is invalid; ignore. + pass return namespace diff --git a/src/jcloud_docsgen/core/python/namespaces.py b/src/jcloud_docsgen/core/python/namespaces.py index 706a816..4391cff 100644 --- a/src/jcloud_docsgen/core/python/namespaces.py +++ b/src/jcloud_docsgen/core/python/namespaces.py @@ -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__( diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c b/tests/unit/core/python/_core/invalid_module_names/a.b.c similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c rename to tests/unit/core/python/_core/invalid_module_names/a.b.c diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py b/tests/unit/core/python/_core/invalid_module_names/a.b.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.py diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/expected/pkg/a/b.md b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/expected/pkg/a/b.md new file mode 100644 index 0000000..3238719 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/expected/pkg/a/b.md @@ -0,0 +1,9 @@ +# Module `pkg.a.b` + +## Function `fn` + +### Function signature + +```python +fn() +``` \ No newline at end of file diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c-d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c-d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c-d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a-b/c.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c-d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c-d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c-d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a.b/c.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b-c.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b-c.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b-c.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.c.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.c.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.c.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/a/b.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/b-c/.gitkeep b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/b-c/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/b.c/.gitkeep b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/b.c/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c-d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c-d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c-d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c.d.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c.d.py new file mode 100644 index 0000000..9b7a678 --- /dev/null +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/case02/project/pkg/c.d.py @@ -0,0 +1 @@ +def fn(): ... diff --git a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py index 33c63b7..03921c9 100644 --- a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py @@ -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', ), ], ) diff --git a/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py b/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py index 53d7e0e..3f86f7f 100644 --- a/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonModuleNamespace.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')", diff --git a/tests/unit/core/python/namespaces/test_PythonNamespace.py b/tests/unit/core/python/namespaces/test_PythonNamespace.py index e4ee640..ba56d89 100644 --- a/tests/unit/core/python/namespaces/test_PythonNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonNamespace.py @@ -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')"), ], ) diff --git a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py index 1087cf0..30ca226 100644 --- a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py @@ -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', [])", diff --git a/tests/unit/core/python/namespaces/test_validate_namespace_name.py b/tests/unit/core/python/namespaces/test_validate_namespace_name.py new file mode 100644 index 0000000..a254eb4 --- /dev/null +++ b/tests/unit/core/python/namespaces/test_validate_namespace_name.py @@ -0,0 +1,52 @@ +# Copyright 2026 jCloud + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from src.jcloud_docsgen.core.python.namespaces import validate_namespace_name + + +@pytest.mark.parametrize( + 'name,expected', + [ + ('a', True), + ('namespace', True), + ('match', True), + ('case', True), + ('type', True), + ('_', True), + ('__', True), + ('___', True), + ('_a', True), + ('_a_', True), + ('_a.', False), + ('a.b', False), + ('a-b', False), + ('a.', False), + ('a-', False), + ('.a', False), + ('-a', False), + ('.a.', False), + ('-a-', False), + ('.', False), + ('a-.b', False), + ('"', False), + ('""', False), + ('def', False), + ('class', False), + ('import', False), + ], +) +def test_validate_namespace_name(name, expected): + assert validate_namespace_name(name) == expected