generated from jCloud/repository-template
Add classes for Python function arguments
This commit is contained in:
@@ -16,3 +16,4 @@
|
|||||||
- Add string representation for python namespace objects
|
- Add string representation for python namespace objects
|
||||||
- Add `PythonDocumentationGenerator` method to collect all namespaces
|
- Add `PythonDocumentationGenerator` method to collect all namespaces
|
||||||
- Add class for existing files
|
- Add class for existing files
|
||||||
|
- Add classes for Python function arguments
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# Copyright 2026 jCloud Services GbR
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Union
|
||||||
|
import ast
|
||||||
|
from ...utils import assert_that_is_instance
|
||||||
|
from ...exceptions import InvalidPythonIdentifierError, InvalidPythonAnnotationError
|
||||||
|
import keyword
|
||||||
|
import types
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'PythonArgumentKind',
|
||||||
|
'PythonFunctionArgumentDefault',
|
||||||
|
'PythonFunctionArgument',
|
||||||
|
'PythonASTArgumentsListParser'
|
||||||
|
]
|
||||||
|
|
||||||
|
class PythonArgumentKind(Enum):
|
||||||
|
NORMAL = 0
|
||||||
|
POSITIONAL_ONLY = 1
|
||||||
|
KEYWORD_ONLY = 2
|
||||||
|
VARARG = 3
|
||||||
|
KWARGS = 4
|
||||||
|
|
||||||
|
class PythonFunctionArgumentDefault:
|
||||||
|
'''
|
||||||
|
Represents the default value of a Python function argument.
|
||||||
|
|
||||||
|
:param value: The value
|
||||||
|
:type value: ast.expr
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, value: ast.expr) -> None:
|
||||||
|
assert_that_is_instance(value, ast.expr)
|
||||||
|
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __eq__(self, value: PythonFunctionArgumentDefault) -> bool:
|
||||||
|
if not isinstance(value, PythonFunctionArgumentDefault):
|
||||||
|
return False
|
||||||
|
return ast.dump(value.value, include_attributes = False) == ast.dump(self.value, include_attributes = False)
|
||||||
|
|
||||||
|
class PythonFunctionArgument:
|
||||||
|
'''
|
||||||
|
Represents an argument of a python function.
|
||||||
|
|
||||||
|
:param name: The name of the argument.
|
||||||
|
:type name: str
|
||||||
|
:param kind: The kind of the argument.
|
||||||
|
:type kind: ArgumentKind
|
||||||
|
:param default: The default value of the argument.
|
||||||
|
:type default: Union[PythonFunctionArgumentDefault, None]
|
||||||
|
:param annotation: The type annotation of the argument.
|
||||||
|
:type annotation: Union[str, None]
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, name: str, kind: PythonArgumentKind, default: Union[PythonFunctionArgumentDefault, None], annotation: Union[str, None]) -> None:
|
||||||
|
assert_that_is_instance(name, str)
|
||||||
|
assert_that_is_instance(kind, PythonArgumentKind)
|
||||||
|
assert_that_is_instance(default, (PythonFunctionArgumentDefault, types.NoneType))
|
||||||
|
assert_that_is_instance(annotation, (str, types.NoneType))
|
||||||
|
|
||||||
|
# check whether name is a valid identifier
|
||||||
|
if not name or not name.isidentifier() or keyword.iskeyword(name):
|
||||||
|
raise InvalidPythonIdentifierError('invalid identifier', identifier = name)
|
||||||
|
|
||||||
|
# check whether the annotation is a valid annotation
|
||||||
|
# Currently, it is only checked whether the parameter is not
|
||||||
|
# empty.
|
||||||
|
if annotation is not None and not annotation:
|
||||||
|
raise InvalidPythonAnnotationError('invalid annotation', annotation = annotation)
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.kind = kind
|
||||||
|
self.default = default
|
||||||
|
self.annotation = annotation
|
||||||
|
|
||||||
|
def __eq__(self, value: PythonFunctionArgument) -> bool:
|
||||||
|
if not isinstance(value, PythonFunctionArgument):
|
||||||
|
return False
|
||||||
|
return self.name == value.name and self.kind == value.kind and self.default == value.default and self.annotation == value.annotation
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return type(self).__name__ + repr((
|
||||||
|
self.name,
|
||||||
|
self.kind,
|
||||||
|
self.default,
|
||||||
|
self.annotation
|
||||||
|
))
|
||||||
|
|
||||||
|
class PythonASTArgumentsListParser:
|
||||||
|
'''
|
||||||
|
A parser for making ``PythonFunctionArgument`` objects from an
|
||||||
|
``ast.arguments`` arguments list.
|
||||||
|
|
||||||
|
:param ast_arguments_list: The ``ast.arguments`` arguments list.
|
||||||
|
:type ast_arguments_list: ast.arguments
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, ast_arguments_list: ast.arguments) -> None:
|
||||||
|
self.ast_arguments_list = ast_arguments_list
|
||||||
|
|
||||||
|
def to_argument_list(self) -> list[PythonFunctionArgument]:
|
||||||
|
'''
|
||||||
|
Converts the AST arguments list to a list of python function
|
||||||
|
argument objects.
|
||||||
|
|
||||||
|
:return: The list of python function argument objects.
|
||||||
|
:rtype: list[PythonFunctionArgument]
|
||||||
|
'''
|
||||||
|
|
||||||
|
arguments = []
|
||||||
|
for arg in self.ast_arguments_list.posonlyargs:
|
||||||
|
if arg.annotation is None:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.POSITIONAL_ONLY, None, None))
|
||||||
|
else:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.POSITIONAL_ONLY, None, arg.annotation.id))
|
||||||
|
for arg in self.ast_arguments_list.args:
|
||||||
|
if arg.annotation is None:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.NORMAL, None, None))
|
||||||
|
else:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.NORMAL, None, arg.annotation.id))
|
||||||
|
if self.ast_arguments_list.vararg is not None:
|
||||||
|
if self.ast_arguments_list.vararg.annotation is None:
|
||||||
|
arguments.append(PythonFunctionArgument(self.ast_arguments_list.vararg.arg, PythonArgumentKind.VARARG, None, None))
|
||||||
|
else:
|
||||||
|
arguments.append(PythonFunctionArgument(self.ast_arguments_list.vararg.arg, PythonArgumentKind.VARARG, None, self.ast_arguments_list.vararg.annotation.id))
|
||||||
|
for arg in self.ast_arguments_list.kwonlyargs:
|
||||||
|
if arg.annotation is None:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.KEYWORD_ONLY, None, None))
|
||||||
|
else:
|
||||||
|
arguments.append(PythonFunctionArgument(arg.arg, PythonArgumentKind.KEYWORD_ONLY, None, arg.annotation.id))
|
||||||
|
if self.ast_arguments_list.kwarg is not None:
|
||||||
|
if self.ast_arguments_list.kwarg.annotation is None:
|
||||||
|
arguments.append(PythonFunctionArgument(self.ast_arguments_list.kwarg.arg, PythonArgumentKind.KWARGS, None, None))
|
||||||
|
else:
|
||||||
|
arguments.append(PythonFunctionArgument(self.ast_arguments_list.kwarg.arg, PythonArgumentKind.KWARGS, None, self.ast_arguments_list.kwarg.annotation.id))
|
||||||
|
|
||||||
|
return arguments
|
||||||
@@ -29,3 +29,35 @@ class NamespaceError(ValueError):
|
|||||||
class InvalidNamespaceError(NamespaceError): ...
|
class InvalidNamespaceError(NamespaceError): ...
|
||||||
class NamespaceNotFoundError(NamespaceError): ...
|
class NamespaceNotFoundError(NamespaceError): ...
|
||||||
class NamespaceExistsError(NamespaceError): ...
|
class NamespaceExistsError(NamespaceError): ...
|
||||||
|
|
||||||
|
class PythonIdentifierError(ValueError):
|
||||||
|
'''
|
||||||
|
Base class for Python identifier errors.
|
||||||
|
'''
|
||||||
|
def __init__(self, *args: object, identifier: str = '') -> None:
|
||||||
|
super().__init__(*args)
|
||||||
|
self.identifier = identifier
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if not self.args:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return f'{self.args[0]}{": " if self.identifier and self.args[0] else ""}{self.identifier if self.args[0] else ""}'
|
||||||
|
|
||||||
|
class InvalidPythonIdentifierError(PythonIdentifierError): ...
|
||||||
|
|
||||||
|
class PythonAnnotationError(ValueError):
|
||||||
|
'''
|
||||||
|
Base class for Python annotation errors.
|
||||||
|
'''
|
||||||
|
def __init__(self, *args: object, annotation: str = '') -> None:
|
||||||
|
super().__init__(*args)
|
||||||
|
self.annotation = annotation
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if not self.args:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return f'{self.args[0]}{": " if self.annotation and self.args[0] else ""}{self.annotation if self.args[0] else ""}'
|
||||||
|
|
||||||
|
class InvalidPythonAnnotationError(PythonAnnotationError): ...
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# Copyright 2026 jCloud Services GbR
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import PythonASTArgumentsListParser, PythonFunctionArgument, PythonArgumentKind
|
||||||
|
import pytest
|
||||||
|
import ast
|
||||||
|
|
||||||
|
def argument_list(argument_list: str) -> ast.arguments:
|
||||||
|
'''
|
||||||
|
Returns an AST argument list from the argument list source code
|
||||||
|
|
||||||
|
:param argument_list: The argument list.
|
||||||
|
:type argument_list: str
|
||||||
|
|
||||||
|
:return: The AST argument list.
|
||||||
|
:rtype: ast.arguments
|
||||||
|
'''
|
||||||
|
|
||||||
|
return ast.parse('def func(' + argument_list + '): ...').body[0].args
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('ast_arguments_list,expected', [
|
||||||
|
(argument_list('a'), [PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None)]),
|
||||||
|
(argument_list('a: str'), [PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str')]),
|
||||||
|
(argument_list('a: str, b'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: str, b: int'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'str'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int')
|
||||||
|
]),
|
||||||
|
(argument_list('a, b: int'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'int')
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, *args'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, b, *args'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a, b: str, *args'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, b: str, *args'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, b: str, *args: float'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, 'str'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float')
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, b, *args: float'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float')
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, *args: float'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'float')
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, /, **kwargs'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, **kwargs: str'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, 'int'),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'str')
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, /, b, **kwargs'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None)
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, /, b'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a: int, /, b, *, c'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, *, c'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, *args, c'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, *args, c, **kwargs'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, /, *, c, **kwargs'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a, b, /, c, *, d, **kwargs'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, None),
|
||||||
|
PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, None),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, None),
|
||||||
|
]),
|
||||||
|
(argument_list('a: str, b: int, /, c: float, d: list, *, e: tuple, f: dict, **kwargs: set'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, 'float'),
|
||||||
|
PythonFunctionArgument('d', PythonArgumentKind.NORMAL, None, 'list'),
|
||||||
|
PythonFunctionArgument('e', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'),
|
||||||
|
PythonFunctionArgument('f', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'),
|
||||||
|
]),
|
||||||
|
(argument_list('a: str, b: int, /, c: float, d: list, *args: bytes, e: tuple, f: dict, **kwargs: set'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.NORMAL, None, 'float'),
|
||||||
|
PythonFunctionArgument('d', PythonArgumentKind.NORMAL, None, 'list'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'bytes'),
|
||||||
|
PythonFunctionArgument('e', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'),
|
||||||
|
PythonFunctionArgument('f', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'),
|
||||||
|
]),
|
||||||
|
(argument_list('a: str, b: int, /, *args: bytes, c: tuple, d: dict, **kwargs: set'), [
|
||||||
|
PythonFunctionArgument('a', PythonArgumentKind.POSITIONAL_ONLY, None, 'str'),
|
||||||
|
PythonFunctionArgument('b', PythonArgumentKind.POSITIONAL_ONLY, None, 'int'),
|
||||||
|
PythonFunctionArgument('args', PythonArgumentKind.VARARG, None, 'bytes'),
|
||||||
|
PythonFunctionArgument('c', PythonArgumentKind.KEYWORD_ONLY, None, 'tuple'),
|
||||||
|
PythonFunctionArgument('d', PythonArgumentKind.KEYWORD_ONLY, None, 'dict'),
|
||||||
|
PythonFunctionArgument('kwargs', PythonArgumentKind.KWARGS, None, 'set'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
def test_PythonASTArgumentsListParser_to_argument_list(ast_arguments_list, expected):
|
||||||
|
assert PythonASTArgumentsListParser(ast_arguments_list).to_argument_list() == expected
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Copyright 2026 jCloud Services GbR
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgument, PythonArgumentKind, PythonFunctionArgumentDefault
|
||||||
|
from src.jcloud_docsgen.exceptions import InvalidPythonIdentifierError, InvalidPythonAnnotationError
|
||||||
|
import ast
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('name,kind,default,annotation,expected_exception,expected_exception_msg', [
|
||||||
|
(None, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'NoneType\''),
|
||||||
|
(1, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'int\''),
|
||||||
|
(42, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'int\''),
|
||||||
|
(4.2, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'float\''),
|
||||||
|
(4.2, PythonArgumentKind.NORMAL, None, None, TypeError, 'expected \'str\', got \'float\''),
|
||||||
|
('a', PythonArgumentKind.NORMAL, 1, None, TypeError, 'expected either \'PythonFunctionArgumentDefault\' or \'NoneType\', got \'int\''),
|
||||||
|
('a', PythonArgumentKind.NORMAL, None, 1, TypeError, 'expected either \'str\' or \'NoneType\', got \'int\''),
|
||||||
|
('a', 0, None, None, TypeError, 'expected \'PythonArgumentKind\', got \'int\''),
|
||||||
|
('a', PythonArgumentKind.NORMAL, None, '', InvalidPythonAnnotationError, 'invalid annotation'),
|
||||||
|
('', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier'),
|
||||||
|
('.', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: .'),
|
||||||
|
('#', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: #'),
|
||||||
|
('1a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1a'),
|
||||||
|
('1_', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1_'),
|
||||||
|
('1,', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: 1,'),
|
||||||
|
(',', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ,'),
|
||||||
|
(':', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: :'),
|
||||||
|
('-', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: -'),
|
||||||
|
(';', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ;'),
|
||||||
|
('|', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: |'),
|
||||||
|
('<', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: <'),
|
||||||
|
('>', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: >'),
|
||||||
|
('a,', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a,'),
|
||||||
|
(',a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ,a'),
|
||||||
|
('a:', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a:'),
|
||||||
|
(':a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: :a'),
|
||||||
|
('a-', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a-'),
|
||||||
|
('-a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: -a'),
|
||||||
|
('a;', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a;'),
|
||||||
|
(';a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: ;a'),
|
||||||
|
('a|', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a|'),
|
||||||
|
('|a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: |a'),
|
||||||
|
('a<', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a<'),
|
||||||
|
('<a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: <a'),
|
||||||
|
('a>', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: a>'),
|
||||||
|
('>a', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: >a'),
|
||||||
|
('class', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: class'),
|
||||||
|
('def', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: def'),
|
||||||
|
('lambda', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: lambda'),
|
||||||
|
('None', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: None'),
|
||||||
|
('True', PythonArgumentKind.NORMAL, None, None, InvalidPythonIdentifierError, 'invalid identifier: True'),
|
||||||
|
])
|
||||||
|
def test_PythonFunctionArgument_exceptions(name, kind, default, annotation, expected_exception, expected_exception_msg):
|
||||||
|
with pytest.raises(expected_exception) as exc_info:
|
||||||
|
PythonFunctionArgument(name, kind, default, annotation)
|
||||||
|
assert str(exc_info.value) == expected_exception_msg
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('name', [
|
||||||
|
'a',
|
||||||
|
'_a',
|
||||||
|
'a1',
|
||||||
|
'a0',
|
||||||
|
'a_',
|
||||||
|
'_',
|
||||||
|
'_abc',
|
||||||
|
'abc',
|
||||||
|
'abc_',
|
||||||
|
'match',
|
||||||
|
'case',
|
||||||
|
'type',
|
||||||
|
'ä',
|
||||||
|
])
|
||||||
|
@pytest.mark.parametrize('kind', [
|
||||||
|
PythonArgumentKind.NORMAL,
|
||||||
|
PythonArgumentKind.POSITIONAL_ONLY,
|
||||||
|
PythonArgumentKind.KEYWORD_ONLY,
|
||||||
|
PythonArgumentKind.VARARG,
|
||||||
|
PythonArgumentKind.KWARGS,
|
||||||
|
])
|
||||||
|
@pytest.mark.parametrize('default', [
|
||||||
|
None,
|
||||||
|
PythonFunctionArgumentDefault(ast.Constant(value = 'x')),
|
||||||
|
PythonFunctionArgumentDefault(ast.Constant(value = 1)),
|
||||||
|
PythonFunctionArgumentDefault(ast.Constant(value = None)),
|
||||||
|
])
|
||||||
|
@pytest.mark.parametrize('annotation', [
|
||||||
|
None,
|
||||||
|
'a',
|
||||||
|
'list[int]',
|
||||||
|
'_',
|
||||||
|
])
|
||||||
|
def test_PythonFunctionArgument(name, kind, default, annotation):
|
||||||
|
PythonFunctionArgument(name, kind, default, annotation)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Copyright 2026 jCloud Services GbR
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from src.jcloud_docsgen.core.python.arguments import PythonFunctionArgumentDefault
|
||||||
|
import pytest
|
||||||
|
import ast
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('default,expected', [
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = '')), ast.Constant),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 'x')), ast.Constant),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = None)), ast.Constant),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 1)), ast.Constant),
|
||||||
|
])
|
||||||
|
def test_PythonFunctionArgumentDefault_value_attribute_type(default, expected):
|
||||||
|
assert isinstance(default.value, expected)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('value,expected_exception_msg', [
|
||||||
|
(None, 'expected \'expr\', got \'NoneType\''),
|
||||||
|
(1, 'expected \'expr\', got \'int\''),
|
||||||
|
('x', 'expected \'expr\', got \'str\''),
|
||||||
|
(42, 'expected \'expr\', got \'int\''),
|
||||||
|
(4.2, 'expected \'expr\', got \'float\''),
|
||||||
|
])
|
||||||
|
def test_PythonFunctionArgumentDefault_raises_TypeError(value, expected_exception_msg):
|
||||||
|
with pytest.raises(TypeError) as exc_info:
|
||||||
|
PythonFunctionArgumentDefault(value)
|
||||||
|
assert str(exc_info.value) == expected_exception_msg
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('default1,default2,expected', [
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 1)), PythonFunctionArgumentDefault(ast.Constant(value = 1)), True),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 1)), PythonFunctionArgumentDefault(ast.Constant(value = 2)), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 'a')), PythonFunctionArgumentDefault(ast.Constant(value = 2)), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 'a')), PythonFunctionArgumentDefault(ast.Constant(value = 'a')), True),
|
||||||
|
(PythonFunctionArgumentDefault(ast.Constant(value = 'a')), PythonFunctionArgumentDefault(ast.Constant(value = 'b')), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [])), PythonFunctionArgumentDefault(ast.List(elts = [])), True),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [1])), PythonFunctionArgumentDefault(ast.List(elts = [])), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [1])), PythonFunctionArgumentDefault(ast.List(elts = [1])), True),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [1])), PythonFunctionArgumentDefault(ast.List(elts = [2])), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [1])), PythonFunctionArgumentDefault(ast.List(elts = [1, 2])), False),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [1, 2])), PythonFunctionArgumentDefault(ast.List(elts = [1, 2])), True),
|
||||||
|
(PythonFunctionArgumentDefault(ast.List(elts = [2, 1])), PythonFunctionArgumentDefault(ast.List(elts = [1, 2])), False),
|
||||||
|
])
|
||||||
|
def test_PythonFunctionArgumentDefault___eq__(default1, default2, expected):
|
||||||
|
assert (default1 == default2) == expected
|
||||||
Reference in New Issue
Block a user