21 Commits
Author SHA1 Message Date
jakob.scheid a85cd1b433 Merge pull request 'Chore: prepare 0.2.2' (#69) from chore/prepare-0-2-2 into main
Generate API reference / generate (push) Successful in 21s
Release package / Build package (release) Successful in 24s
Release package / Prepare release asset upload (release) Successful in 13s
Release package / Publish package to package registry (release) Successful in 15s
Release package / Upload release asset (dist/jcloud_docsgen-0.2.2-py3-none-any.whl) (release) Successful in 25s
Release package / Upload release asset (dist/jcloud_docsgen-0.2.2.tar.gz) (release) Successful in 16s
Reviewed-on: #69
2026-10-14 23:59:40 +02:00
jakob.scheid 06f91a18e9 docs(changelog): move Unreleased section to 0.2.2 2026-10-14 23:58:11 +02:00
jakob.scheid bc542ed781 chore: update project version in pyproject.toml 2026-10-14 23:57:37 +02:00
jakob.scheid aa44f9c3a1 Merge pull request 'Fix(deps): add typing-extensions dependency' (#67) from fix/typing-extensions-import-error into main
Generate API reference / generate (push) Successful in 22s
Reviewed-on: #67
2026-10-14 23:54:31 +02:00
jakob.scheid 0f4c344676 docs(changelog): add import error fix entry
Added a changelog entry for the fix of the typing-extensions import
error in Python <3.13.
2026-10-14 23:50:47 +02:00
jakob.scheid ae01f223e3 fix(deps): add typing-extensions
Added typing-extensions to fix import error in Python <3.13.
2026-10-14 23:49:36 +02:00
jakob.scheid 4781e21f5d Merge pull request 'Docs(changelog): update 0.2.1 release date to today' (#65) from chore/update-0-2-1-release-date into main
Generate API reference / generate (push) Successful in 21s
Release package / Build package (release) Successful in 25s
Release package / Prepare release asset upload (release) Successful in 13s
Release package / Publish package to package registry (release) Successful in 15s
Release package / Upload release asset (dist/jcloud_docsgen-0.2.1-py3-none-any.whl) (release) Successful in 17s
Release package / Upload release asset (dist/jcloud_docsgen-0.2.1.tar.gz) (release) Successful in 18s
Reviewed-on: #65
2026-10-06 17:52:01 +02:00
jakob.scheid 0d86785dba docs(changelog): update 0.2.1 release date
Updated the release date of version 0.2.1 to 2026-10-06.
2026-10-06 17:49:59 +02:00
jakob.scheid 5402dd960e Merge pull request 'Chore: preapre release 0.2.1' (#63) from chore/prepare-0-2-1 into main
Generate API reference / generate (push) Successful in 20s
Reviewed-on: #63
2026-10-04 17:26:58 +02:00
jakob.scheid d8249d3eb1 docs(changelog): move Unreleased section to 0.2.1 2026-10-04 17:25:18 +02:00
jakob.scheid 6c593a286c chore: update version information to 0.2.1 2026-10-04 17:24:48 +02:00
jcloud-bot Bot 4b659d7d2d chore: update API reference 2026-10-04 15:19:27 +00:00
jakob.scheid 1ccb610310 Merge pull request 'Fix(python-generator): ignore invalid namespaces' (#61) from fix/ignore-invalid-python-namespaces into main
Generate API reference / generate (push) Successful in 22s
Reviewed-on: #61
2026-10-04 17:19:13 +02:00
jakob.scheid a25c66d77b docs(changelog): add entry for stricter Python namespace name validation 2026-10-04 17:06:44 +02:00
jakob.scheid 82e10c5493 docs(changelog): add Python invalid namespaces ignoring entry 2026-10-04 17:04:04 +02:00
jakob.scheid 03fa81f0df fix(python-generator): ignore invalid namespaces
Python modules or packages are now ignored if their name is not a valid
Python namespace name.
2026-10-04 16:54:48 +02:00
jakob.scheid efa2dbf724 test(python-namespaces): add test for namespace name validation 2026-10-04 16:47:33 +02:00
jakob.scheid 0f8f9d3cfa feat(python-namespaces)!: add validation for namespace names
Added more strict validation for namespaces names to PythonNamespace
than before and updated error messages.
All tests depending on this were updated to the new behavior.
2026-10-04 16:39:31 +02:00
jakob.scheid 3e77ebb7d0 Merge pull request 'Refactor: define public module exports' (#60) from refactor/modules-all-variables into main
Generate API reference / generate (push) Successful in 30s
Reviewed-on: #60
2026-10-03 18:50:32 +02:00
jakob.scheid 310861dacc refactor(exceptions): define public module exports
Added an __all__ variable to the exceptions module.
2026-10-03 18:50:04 +02:00
jakob.scheid 4365de27a8 refactor(utils): define public module exports
Added an __all__ variable to the utility module.
2026-10-03 18:49:47 +02:00
40 changed files with 248 additions and 122 deletions

No files matched your search

+12 -1
View File
@@ -1,11 +1,22 @@
# Changelog # Changelog
## [Unreleased] ## [0.2.2] - 2026-10-14
### Fixed
- Import error in Python <3.13 which prevented the tool from running and made it unusuable in Python <3.13
## [0.2.1] - 2026-10-06
### Fixed ### Fixed
- Python namespace name conflict when package and module have the same name - 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 - 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 ## [0.2.0] - 2026-09-28
@@ -1,5 +1,45 @@
# Module `jcloud_docsgen.core.python.namespaces` # Module `jcloud_docsgen.core.python.namespaces`
## Function `validate_namespace_name`
Checks whether a namespace name is valid. Namespace names are valid
if they contain no dots and are valid Python identifiers.
### Function signature
```python
validate_namespace_name(name: str) -> bool
```
### Returns
Whether the namespace name is valid.
Return type: `bool`
### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `name` | `str` | normal | -- | The namespace name to validate. |
### Docstring
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
## Class `PythonNamespace` ## Class `PythonNamespace`
The base class for all Python namespaces such as modules or packages. The base class for all Python namespaces such as modules or packages.
@@ -57,6 +97,8 @@ The base class for all Python namespaces such as modules or packages.
:param name: The name of the namespace. :param name: The name of the namespace.
:type name: str :type name: str
:raises InvalidNamespaceError: If the namespace name is invalid.
## Class `PythonPackageNamespace` ## Class `PythonPackageNamespace`
A subclass of ``PythonNamespace`` for Python package namespaces. A subclass of ``PythonNamespace`` for Python package namespaces.
@@ -381,6 +423,8 @@ A subclass of ``PythonNamespace`` for Python package namespaces.
:param sub_namespaces: The sub-namespaces. :param sub_namespaces: The sub-namespaces.
:type sub_namespaces: list[PythonNamespace] :type sub_namespaces: list[PythonNamespace]
:raises InvalidNamespaceError: If the namespace name is invalid.
## Class `PythonModuleNamespace` ## Class `PythonModuleNamespace`
A subclass of ``PythonNamespace`` for Python module namespaces. A subclass of ``PythonNamespace`` for Python module namespaces.
Generated
+4 -4
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.4.3 and should not be changed by hand. # This file is automatically @generated by Poetry 2.5.1 and should not be changed by hand.
[[package]] [[package]]
name = "colorama" name = "colorama"
@@ -235,14 +235,14 @@ version = "4.16.0"
description = "Backported and Experimental Type Hints for Python 3.9+" description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"] groups = ["main", "dev"]
markers = "python_version == \"3.10\""
files = [ files = [
{file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"},
{file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"},
] ]
markers = {dev = "python_version == \"3.10\""}
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.10" python-versions = ">=3.10"
content-hash = "24c9a2d464f6c737eeae0a66f792fa23824c3960b7cc72a22fff1408f21ec812" content-hash = "0fb3b53540e7a7fe173bf29a2031bd59f863a8dea94a29d51933de2375c3bfa2"
+2 -2
View File
@@ -4,9 +4,9 @@ build-backend = "poetry.core.masonry.api"
[project] [project]
name = "jcloud-docsgen" name = "jcloud-docsgen"
version = "0.2.0" version = "0.2.2"
description = "A tool that generates Markdown documentation for software project source code" description = "A tool that generates Markdown documentation for software project source code"
dependencies = ["docstring-parser"] dependencies = ["docstring-parser", "typing-extensions (>=4.16.0,<5.0.0)"]
requires-python = ">=3.10" requires-python = ">=3.10"
license = "Apache-2.0" license = "Apache-2.0"
+22 -9
View File
@@ -37,7 +37,11 @@ from .definitions import (
PythonFunctionDefinition, PythonFunctionDefinition,
) )
from .docstrings import PythonDocstring from .docstrings import PythonDocstring
from .namespaces import PythonModuleNamespace, PythonPackageNamespace from .namespaces import (
PythonModuleNamespace,
PythonPackageNamespace,
validate_namespace_name,
)
__all__ = [ __all__ = [
'PythonDefinitionDocumentationIncludeSections', 'PythonDefinitionDocumentationIncludeSections',
@@ -926,9 +930,12 @@ class PythonModuleDocumentationGenerator:
), ),
**kwargs, **kwargs,
) -> None: ) -> None:
if module_path.name.count('.') > 1: if not (
module_path.suffix == '.py'
and validate_namespace_name(module_path.stem)
):
raise InvalidNamespaceError( raise InvalidNamespaceError(
'namespace identifier cannot contain more than one dot', 'invalid module file name',
namespace_identifier=module_path.name, namespace_identifier=module_path.name,
) )
@@ -1107,12 +1114,18 @@ class PythonDocumentationGenerator:
) )
for entry in directory.iterdir(): for entry in directory.iterdir():
if entry.is_dir() and ( try:
entry.parent / (f'{entry.stem}.py') if entry.is_dir() and (
).resolve() not in [e.resolve() for e in entry.parent.iterdir()]: entry.parent / (f'{entry.stem}.py')
namespace.add(self._namespace(entry)) ).resolve() not in [
elif entry.suffix == '.py': e.resolve() for e in entry.parent.iterdir()
namespace.add(PythonModuleNamespace(entry.stem)) ]:
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 return namespace
+26 -3
View File
@@ -14,6 +14,7 @@
from __future__ import annotations from __future__ import annotations
import keyword
from typing import TypeAlias, Union from typing import TypeAlias, Union
from ...exceptions import ( from ...exceptions import (
@@ -24,27 +25,47 @@ from ...exceptions import (
from ...utils import assert_that_is_instance from ...utils import assert_that_is_instance
__all__ = [ __all__ = [
'validate_namespace_name',
'PythonNamespace', 'PythonNamespace',
'PythonPackageNamespace', 'PythonPackageNamespace',
'PythonModuleNamespace', '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: class PythonNamespace:
""" """
The base class for all Python namespaces such as modules or packages. The base class for all Python namespaces such as modules or packages.
:param name: The name of the namespace. :param name: The name of the namespace.
:type name: str :type name: str
:raises InvalidNamespaceError: If the namespace name is invalid.
""" """
def __init__(self, name: str) -> None: def __init__(self, name: str) -> None:
assert_that_is_instance(name, str) assert_that_is_instance(name, str)
if not name.strip(): if not validate_namespace_name(name):
raise InvalidNamespaceError( raise InvalidNamespaceError(
'invalid namespace', 'invalid namespace', namespace_identifier=name
namespace_identifier=name,
) )
self.name = name self.name = name
@@ -70,6 +91,8 @@ class PythonPackageNamespace(PythonNamespace):
:type name: str :type name: str
:param sub_namespaces: The sub-namespaces. :param sub_namespaces: The sub-namespaces.
:type sub_namespaces: list[PythonNamespace] :type sub_namespaces: list[PythonNamespace]
:raises InvalidNamespaceError: If the namespace name is invalid.
""" """
def __init__( def __init__(
+14
View File
@@ -14,6 +14,20 @@
import errno import errno
__all__ = [
'NamespaceError',
'InvalidNamespaceError',
'NamespaceNotFoundError',
'NamespaceExistsError',
'PythonIdentifierError',
'InvalidPythonIdentifierError',
'PythonAnnotationError',
'InvalidPythonAnnotationError',
'PythonDefinitionError',
'PythonArgumentStructureError',
'DirectoryNotEmptyError',
]
class NamespaceError(ValueError): class NamespaceError(ValueError):
""" """
+10
View File
@@ -27,6 +27,16 @@ except ImportError:
from typing_extensions import deprecated from typing_extensions import deprecated
__all__ = [
'ExistingDirectory',
'ExistingFile',
'human_readable_list',
'assert_that_is_instance',
'non_empty_str',
'get_relative_path_segments',
]
@deprecated('Use pathlib.Path instead.') @deprecated('Use pathlib.Path instead.')
class ExistingDirectory(pathlib.Path): class ExistingDirectory(pathlib.Path):
""" """
@@ -0,0 +1,9 @@
# Module `pkg.a.b`
## Function `fn`
### Function signature
```python
fn()
```
@@ -25,176 +25,134 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError
[ [
( (
pathlib.Path( pathlib.Path(
( ('tests/unit/core/python/_core/invalid_module_names/a.b.c'),
'tests/unit/core/python/_core/fil'
'es_with_more_than_one_dot/a.b.c'
),
), ),
None, None,
InvalidNamespaceError, 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( pathlib.Path(
( (
'tests/unit/core/python/_core/file' 'tests/unit/core/python/_core/i'
's_with_more_than_one_dot/a.b.c.d' 'nvalid_module_names/a.b.c.d.e'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
'namespace identifier cannot contain more than one dot: a.b.c.d', 'invalid module file name: a.b.c.d.e',
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files' 'tests/unit/core/python/_core/in'
'_with_more_than_one_dot/a.b.c.d.e' 'valid_module_names/a.b.c.d.e.f'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f',
'namespace identifier cannot cont'
'ain more than one dot: a.b.c.d.e'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_' 'tests/unit/core/python/_core/inv'
'with_more_than_one_dot/a.b.c.d.e.f' 'alid_module_names/a.b.c.d.e.f.g'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f.g',
'namespace identifier cannot conta'
'in more than one dot: a.b.c.d.e.f'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_w' 'tests/unit/core/python/_core/inva'
'ith_more_than_one_dot/a.b.c.d.e.f.g' 'lid_module_names/a.b.c.d.e.f.g.h'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f.g.h',
'namespace identifier cannot contai'
'n more than one dot: a.b.c.d.e.f.g'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_wi' 'tests/unit/core/python/_core/inval'
'th_more_than_one_dot/a.b.c.d.e.f.g.h' 'id_module_names/a.b.c.d.e.f.g.h.py'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f.g.h.py',
'namespace identifier cannot contain'
' more than one dot: a.b.c.d.e.f.g.h'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_wit' 'tests/unit/core/python/_core/inva'
'h_more_than_one_dot/a.b.c.d.e.f.g.h.py' 'lid_module_names/a.b.c.d.e.f.g.py'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f.g.py',
'namespace identifier cannot contain m'
'ore than one dot: a.b.c.d.e.f.g.h.py'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_wi' 'tests/unit/core/python/_core/in'
'th_more_than_one_dot/a.b.c.d.e.f.g.py' 'lid_module_names/a.b.c.d.e.f.py'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.f.py',
'namespace identifier cannot contain '
'more than one dot: a.b.c.d.e.f.g.py'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_w' 'tests/unit/core/python/_core/in'
'ith_more_than_one_dot/a.b.c.d.e.f.py' 'valid_module_names/a.b.c.d.e.py'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.e.py',
'namespace identifier cannot contain'
' more than one dot: a.b.c.d.e.f.py'
),
), ),
( (
pathlib.Path( pathlib.Path(
( (
'tests/unit/core/python/_core/files_' 'tests/unit/core/python/_core/i'
'with_more_than_one_dot/a.b.c.d.e.py' 'nvalid_module_names/a.b.c.d.py'
), ),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.d.py',
'namespace identifier cannot contai'
'n more than one dot: a.b.c.d.e.py'
),
), ),
( (
pathlib.Path( pathlib.Path(
( ('tests/unit/core/python/_core/invalid_module_names/a.b.c.py'),
'tests/unit/core/python/_core/files'
'_with_more_than_one_dot/a.b.c.d.py'
),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.c.py',
'namespace identifier cannot conta'
'in more than one dot: a.b.c.d.py'
),
), ),
( (
pathlib.Path( pathlib.Path(
( ('tests/unit/core/python/_core/invalid_module_names/a.b.py'),
'tests/unit/core/python/_core/file'
's_with_more_than_one_dot/a.b.c.py'
),
), ),
None, None,
InvalidNamespaceError, InvalidNamespaceError,
( 'invalid module file name: a.b.py',
'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',
), ),
( (
pathlib.Path( pathlib.Path(
@@ -205,7 +163,7 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError
), ),
None, None,
InvalidNamespaceError, 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', 'namespace,expected_name',
[ [
(PythonModuleNamespace('a'), 'a'), (PythonModuleNamespace('a'), 'a'),
(PythonModuleNamespace('1'), '1'),
(PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')), (PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')),
], ],
) )
@@ -41,11 +40,7 @@ def test_PythonModuleNamespace_name_attribute(namespace, expected_name):
[ [
(PythonModuleNamespace('a'), PythonModuleNamespace('a'), True), (PythonModuleNamespace('a'), PythonModuleNamespace('a'), True),
(PythonModuleNamespace('b'), PythonModuleNamespace('b'), True), (PythonModuleNamespace('b'), PythonModuleNamespace('b'), True),
(PythonModuleNamespace('1'), PythonModuleNamespace('1'), True),
(PythonModuleNamespace('42'), PythonModuleNamespace('42'), True),
(PythonModuleNamespace('a'), PythonModuleNamespace('b'), False), (PythonModuleNamespace('a'), PythonModuleNamespace('b'), False),
(PythonModuleNamespace('0'), PythonModuleNamespace('-0'), False),
(PythonModuleNamespace('1'), PythonModuleNamespace('a'), False),
(PythonModuleNamespace('a'), 'a', False), (PythonModuleNamespace('a'), 'a', False),
(PythonModuleNamespace('a'), 'b', False), (PythonModuleNamespace('a'), 'b', False),
(PythonModuleNamespace('a'), PythonPackageNamespace('a', []), False), (PythonModuleNamespace('a'), PythonPackageNamespace('a', []), False),
@@ -61,7 +56,6 @@ def test_PythonModuleNamespace___eq__(namespace1, namespace2, expected):
'namespace,expected', 'namespace,expected',
[ [
(PythonModuleNamespace('a'), "PythonModuleNamespace('a')"), (PythonModuleNamespace('a'), "PythonModuleNamespace('a')"),
(PythonModuleNamespace('1'), "PythonModuleNamespace('1')"),
( (
PythonModuleNamespace(StrSubclass('a')), PythonModuleNamespace(StrSubclass('a')),
"PythonModuleNamespace('a')", "PythonModuleNamespace('a')",
@@ -24,7 +24,6 @@ class StrSubclass(str): ...
'namespace,expected_name', 'namespace,expected_name',
[ [
(PythonNamespace('a'), 'a'), (PythonNamespace('a'), 'a'),
(PythonNamespace('1'), '1'),
(PythonNamespace(StrSubclass('a')), StrSubclass('a')), (PythonNamespace(StrSubclass('a')), StrSubclass('a')),
], ],
) )
@@ -36,7 +35,6 @@ def test_PythonNamespace_name_attribute(namespace, expected_name):
'namespace,expected', 'namespace,expected',
[ [
(PythonNamespace('a'), "PythonNamespace('a')"), (PythonNamespace('a'), "PythonNamespace('a')"),
(PythonNamespace('1'), "PythonNamespace('1')"),
(PythonNamespace(StrSubclass('a')), "PythonNamespace('a')"), (PythonNamespace(StrSubclass('a')), "PythonNamespace('a')"),
], ],
) )
@@ -38,13 +38,6 @@ class StrSubclass(str): ...
), ),
'a', 'a',
), ),
(
PythonPackageNamespace(
'1',
[],
),
'1',
),
( (
PythonPackageNamespace( PythonPackageNamespace(
StrSubclass('a'), StrSubclass('a'),
@@ -291,10 +284,6 @@ def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected):
PythonPackageNamespace('a', []), PythonPackageNamespace('a', []),
"PythonPackageNamespace('a', [])", "PythonPackageNamespace('a', [])",
), ),
(
PythonPackageNamespace('1', []),
"PythonPackageNamespace('1', [])",
),
( (
PythonPackageNamespace(StrSubclass('a'), []), PythonPackageNamespace(StrSubclass('a'), []),
"PythonPackageNamespace('a', [])", "PythonPackageNamespace('a', [])",
@@ -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