generated from jCloud/repository-template
260 lines
7.3 KiB
Python
260 lines
7.3 KiB
Python
# 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.
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import keyword
|
|
import types
|
|
|
|
from ...exceptions import InvalidPythonIdentifierError
|
|
from ...utils import assert_that_is_instance
|
|
from .arguments import PythonASTArgumentsListParser, PythonFunctionArgument
|
|
from .docstrings import PythonDocstring
|
|
|
|
__all__ = [
|
|
'PythonDefinition',
|
|
'PythonFunctionDefinition',
|
|
'PythonAsyncFunctionDefinition',
|
|
'PythonClassDefinition',
|
|
]
|
|
|
|
|
|
def _python_docstring(doc: str | None) -> PythonDocstring | None:
|
|
"""
|
|
Returns the PythonDocstring object if the docstring is a string and
|
|
not ``None``.
|
|
|
|
:param doc: The docstring.
|
|
:type doc: str | None
|
|
|
|
:return: The PythonDocstring object or ``None``.
|
|
:rtype: PythonDocstring | None
|
|
"""
|
|
|
|
return PythonDocstring(doc) if doc is not None else None
|
|
|
|
|
|
class PythonDefinition:
|
|
"""
|
|
A base class for Python definition (a class, function or asynchronous
|
|
function definition) classes.
|
|
|
|
:param name: The name of the definition.
|
|
:type name: str
|
|
:param doc: The docstring.
|
|
:type doc: PythonDocstring | None
|
|
:param decorators: The definition decorators.
|
|
:type decorators: list[str]
|
|
|
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
|
Python identifier.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
doc: PythonDocstring | None,
|
|
decorators: list[str],
|
|
) -> None:
|
|
if keyword.iskeyword(name) or not name.isidentifier() or not name:
|
|
raise InvalidPythonIdentifierError(
|
|
'invalid identifier',
|
|
identifier=name,
|
|
)
|
|
|
|
assert_that_is_instance(doc, (PythonDocstring, types.NoneType))
|
|
|
|
self.name = name
|
|
self.doc = doc
|
|
self.decorators = decorators
|
|
|
|
def _arg_list(self) -> tuple:
|
|
return (
|
|
self.name,
|
|
self.doc,
|
|
self.decorators,
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return type(self).__name__ + repr(self._arg_list())
|
|
|
|
@classmethod
|
|
def from_node(cls, node: ast.stmt) -> PythonDefinition:
|
|
"""
|
|
From an AST node.
|
|
|
|
:param node: The AST node.
|
|
:type node: ast.stmt
|
|
"""
|
|
|
|
return cls(
|
|
node.name,
|
|
_python_docstring(ast.get_docstring(node)),
|
|
[ast.unparse(d) for d in node.decorator_list],
|
|
)
|
|
|
|
def __eq__(self, value: object) -> bool:
|
|
if not isinstance(value, PythonDefinition):
|
|
return False
|
|
return self._arg_list() == value._arg_list()
|
|
|
|
|
|
class PythonFunctionDefinition(PythonDefinition):
|
|
"""
|
|
A PythonDefinition subclass representing a Python function
|
|
definition.
|
|
|
|
:param name: The name of the definition.
|
|
:type name: str
|
|
:param args: The arguments.
|
|
:type args: list[PythonFunctionArgument]
|
|
:param returns: The return type of the function or class.
|
|
:type returns: str | None
|
|
:param doc: The docstring.
|
|
:type doc: PythonDocstring | None
|
|
:param decorators: The definition decorators.
|
|
:type decorators: list[str]
|
|
|
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
|
Python identifier.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
args: list[PythonFunctionArgument],
|
|
returns: str | None,
|
|
doc: PythonDocstring | None,
|
|
decorators: list[str],
|
|
) -> None:
|
|
super().__init__(name, doc, decorators)
|
|
self.args = args
|
|
self.returns = returns
|
|
|
|
def _arg_list(self) -> tuple:
|
|
return (
|
|
self.name,
|
|
self.args,
|
|
self.returns,
|
|
self.doc,
|
|
self.decorators,
|
|
)
|
|
|
|
@classmethod
|
|
def from_node(cls, node: ast.stmt) -> PythonFunctionDefinition:
|
|
"""
|
|
From an AST node.
|
|
|
|
:param node: The AST node.
|
|
:type node: ast.stmt
|
|
"""
|
|
|
|
return cls(
|
|
node.name,
|
|
PythonASTArgumentsListParser(node.args).to_argument_list(),
|
|
ast.unparse(node.returns) if node.returns is not None else None,
|
|
_python_docstring(ast.get_docstring(node)),
|
|
[ast.unparse(d) for d in node.decorator_list],
|
|
)
|
|
|
|
|
|
class PythonAsyncFunctionDefinition(PythonFunctionDefinition):
|
|
"""
|
|
A PythonDefinition subclass representing an asynchronous Python function
|
|
definition.
|
|
|
|
:param name: The name of the definition.
|
|
:type name: str
|
|
:param args: The arguments.
|
|
:type args: list[PythonFunctionArgument]
|
|
:param returns: The return type of the function or class.
|
|
:type returns: str | None
|
|
:param doc: The docstring.
|
|
:type doc: PythonDocstring | None
|
|
:param decorators: The definition decorators.
|
|
:type decorators: list[str]
|
|
|
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
|
Python identifier.
|
|
"""
|
|
|
|
|
|
class PythonClassDefinition(PythonDefinition):
|
|
"""
|
|
A PythonDefinition subclass representing a Python class definition.
|
|
|
|
:param name: The name of the definition.
|
|
:type name: str
|
|
:param bases: The base classes.
|
|
:type bases: list[str]
|
|
:param doc: The docstring.
|
|
:type doc: PythonDocstring | None
|
|
:param decorators: The definition decorators.
|
|
:type decorators: list[str]
|
|
:param body:
|
|
:type body: list[PythonDefinition]
|
|
|
|
:raises InvalidPythonIdentifierError: If the name is not a valid
|
|
Python identifier.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
bases: list[str],
|
|
doc: PythonDocstring | None,
|
|
decorators: list[str],
|
|
body: list[PythonDefinition],
|
|
) -> None:
|
|
super().__init__(name, doc, decorators)
|
|
self.bases = bases
|
|
self.body = body
|
|
|
|
def _arg_list(self) -> tuple:
|
|
return (
|
|
self.name,
|
|
self.bases,
|
|
self.doc,
|
|
self.decorators,
|
|
self.body,
|
|
)
|
|
|
|
@classmethod
|
|
def from_node(cls, node: ast.stmt) -> PythonClassDefinition:
|
|
"""
|
|
From an AST node.
|
|
|
|
:param node: The AST node.
|
|
:type node: ast.stmt
|
|
"""
|
|
|
|
return cls(
|
|
node.name,
|
|
[ast.unparse(b) for b in node.bases],
|
|
_python_docstring(ast.get_docstring(node)),
|
|
[ast.unparse(d) for d in node.decorator_list],
|
|
[
|
|
PythonFunctionDefinition.from_node(expr)
|
|
if isinstance(expr, ast.FunctionDef)
|
|
else PythonAsyncFunctionDefinition.from_node(expr)
|
|
if isinstance(expr, ast.AsyncFunctionDef)
|
|
else PythonClassDefinition.from_node(expr)
|
|
for expr in node.body
|
|
if isinstance(
|
|
expr, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
|
|
)
|
|
],
|
|
)
|