python-introspect 0.2.3__tar.gz → 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_introspect-0.2.3/src/python_introspect.egg-info → python_introspect-0.2.5}/PKG-INFO +2 -1
- {python_introspect-0.2.3 → python_introspect-0.2.5}/pyproject.toml +2 -1
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/__init__.py +1 -1
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/annotation_types.py +31 -6
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/signature_analyzer.py +27 -14
- {python_introspect-0.2.3 → python_introspect-0.2.5/src/python_introspect.egg-info}/PKG-INFO +2 -1
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_dataclass_projection.py +35 -1
- {python_introspect-0.2.3 → python_introspect-0.2.5}/LICENSE +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/README.md +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/setup.cfg +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/callable_declaration.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/choices.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/dataclass_projection.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/docstring_annotations.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/enableable.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/environment_projection.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/exceptions.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/jsonable.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/public_api.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/runtime_parameter.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/unified_parameter_analyzer.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/validation.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/SOURCES.txt +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/dependency_links.txt +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/requires.txt +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/top_level.txt +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_annotation_choices.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_annotation_types.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_callable_declaration.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_dataclass_source_preparation.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_environment_projection.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_exceptions.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_init.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_jsonable.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_public_api.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_release_readiness.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_runtime_parameter.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_signature_analyzer.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_unified_parameter_analyzer.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_validate_python_release_action.py +0 -0
- {python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-introspect
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Pure Python introspection toolkit for function signatures, dataclasses, and type hints
|
|
5
5
|
Author-email: Tristan Simas <tristan.simas@mail.mcgill.ca>
|
|
6
6
|
License: MIT
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
20
|
Classifier: Topic :: Utilities
|
|
20
21
|
Requires-Python: >=3.10
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-introspect"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
8
8
|
description = "Pure Python introspection toolkit for function signatures, dataclasses, and type hints"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -21,6 +21,7 @@ classifiers = [
|
|
|
21
21
|
"Programming Language :: Python :: 3.10",
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|
|
23
23
|
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
24
25
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
26
|
"Topic :: Utilities",
|
|
26
27
|
]
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/annotation_types.py
RENAMED
|
@@ -5,7 +5,15 @@ from __future__ import annotations
|
|
|
5
5
|
import sys
|
|
6
6
|
import types
|
|
7
7
|
from enum import Enum
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import (
|
|
9
|
+
Annotated,
|
|
10
|
+
ForwardRef,
|
|
11
|
+
Literal,
|
|
12
|
+
Union,
|
|
13
|
+
get_args,
|
|
14
|
+
get_origin,
|
|
15
|
+
get_type_hints,
|
|
16
|
+
)
|
|
9
17
|
|
|
10
18
|
|
|
11
19
|
def resolved_class_annotations(owner: type) -> dict[str, object]:
|
|
@@ -13,6 +21,9 @@ def resolved_class_annotations(owner: type) -> dict[str, object]:
|
|
|
13
21
|
|
|
14
22
|
Python 3.10 leaves strings inside PEP 585 aliases unresolved. Complete
|
|
15
23
|
those declarations without interpreting Literal values or Annotated metadata.
|
|
24
|
+
A recursive alias such as ``V = int | list["V"]`` names itself; the inner
|
|
25
|
+
reference is left as a ``ForwardRef``, the form ``get_type_hints`` produces
|
|
26
|
+
for it on Python 3.11 and later.
|
|
16
27
|
"""
|
|
17
28
|
annotations = get_type_hints(owner, include_extras=True)
|
|
18
29
|
unresolved = set(annotations)
|
|
@@ -29,20 +40,34 @@ def resolved_class_annotations(owner: type) -> dict[str, object]:
|
|
|
29
40
|
return annotations
|
|
30
41
|
|
|
31
42
|
|
|
32
|
-
def _resolve_nested_type_strings(
|
|
43
|
+
def _resolve_nested_type_strings(
|
|
44
|
+
annotation: object,
|
|
45
|
+
globalns: dict,
|
|
46
|
+
localns: dict,
|
|
47
|
+
expanding: frozenset[str] = frozenset(),
|
|
48
|
+
) -> object:
|
|
33
49
|
if isinstance(annotation, str):
|
|
34
|
-
|
|
50
|
+
if annotation in expanding:
|
|
51
|
+
return ForwardRef(annotation)
|
|
52
|
+
return _resolve_nested_type_strings(
|
|
53
|
+
eval(annotation, globalns, localns), globalns, localns, expanding | {annotation}
|
|
54
|
+
)
|
|
35
55
|
if isinstance(annotation, list):
|
|
36
|
-
resolved = [
|
|
56
|
+
resolved = [
|
|
57
|
+
_resolve_nested_type_strings(member, globalns, localns, expanding)
|
|
58
|
+
for member in annotation
|
|
59
|
+
]
|
|
37
60
|
return annotation if all(a is b for a, b in zip(resolved, annotation)) else resolved
|
|
38
61
|
origin = get_origin(annotation)
|
|
39
62
|
members = get_args(annotation)
|
|
40
63
|
if not members or origin is Literal:
|
|
41
64
|
return annotation
|
|
42
65
|
if origin is Annotated:
|
|
43
|
-
base = _resolve_nested_type_strings(members[0], globalns, localns)
|
|
66
|
+
base = _resolve_nested_type_strings(members[0], globalns, localns, expanding)
|
|
44
67
|
return annotation if base is members[0] else Annotated[(base, *members[1:])]
|
|
45
|
-
resolved = tuple(
|
|
68
|
+
resolved = tuple(
|
|
69
|
+
_resolve_nested_type_strings(member, globalns, localns, expanding) for member in members
|
|
70
|
+
)
|
|
46
71
|
if all(member is original for member, original in zip(resolved, members)):
|
|
47
72
|
return annotation
|
|
48
73
|
if isinstance(annotation, types.GenericAlias):
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/signature_analyzer.py
RENAMED
|
@@ -771,6 +771,26 @@ class CallableAnalysisContext:
|
|
|
771
771
|
)
|
|
772
772
|
|
|
773
773
|
|
|
774
|
+
def _class_node_for_qualname(tree: ast.AST, qualname: str) -> Optional[ast.ClassDef]:
|
|
775
|
+
"""Return the class statement whose qualified name is ``qualname``.
|
|
776
|
+
|
|
777
|
+
Function bodies contribute ``<locals>`` exactly as ``__qualname__`` does.
|
|
778
|
+
"""
|
|
779
|
+
pending = [(node, ()) for node in reversed(getattr(tree, 'body', ()))]
|
|
780
|
+
while pending:
|
|
781
|
+
node, scope = pending.pop()
|
|
782
|
+
if isinstance(node, ast.ClassDef):
|
|
783
|
+
node_scope = (*scope, node.name)
|
|
784
|
+
if '.'.join(node_scope) == qualname:
|
|
785
|
+
return node
|
|
786
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
787
|
+
node_scope = (*scope, node.name, '<locals>')
|
|
788
|
+
else:
|
|
789
|
+
node_scope = scope
|
|
790
|
+
pending.extend((child, node_scope) for child in reversed(list(ast.iter_child_nodes(node))))
|
|
791
|
+
return None
|
|
792
|
+
|
|
793
|
+
|
|
774
794
|
class ClassSourceDeclaration(NamedTuple):
|
|
775
795
|
"""Immutable located class source and its source-only documentation views."""
|
|
776
796
|
|
|
@@ -1196,20 +1216,13 @@ class SignatureAnalyzer:
|
|
|
1196
1216
|
def _qualified_class_source(source: str, qualname: str) -> Optional[ClassSourceDeclaration]:
|
|
1197
1217
|
"""Use Python's qualified class/decorator locator on current source content."""
|
|
1198
1218
|
tree = SignatureAnalyzer._module_source_tree(source)
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
class_node = next(
|
|
1207
|
-
node for node in ast.walk(tree)
|
|
1208
|
-
if isinstance(node, ast.ClassDef)
|
|
1209
|
-
and min([node.lineno, *(decorator.lineno for decorator in node.decorator_list)]) == first_line
|
|
1210
|
-
)
|
|
1211
|
-
return ClassSourceDeclaration.from_block(block, class_node, first_line)
|
|
1212
|
-
return None
|
|
1219
|
+
class_node = _class_node_for_qualname(tree, qualname)
|
|
1220
|
+
if class_node is None:
|
|
1221
|
+
return None
|
|
1222
|
+
first_line = min([class_node.lineno, *(decorator.lineno for decorator in class_node.decorator_list)])
|
|
1223
|
+
lines = source.splitlines(keepends=True)
|
|
1224
|
+
block = ''.join(inspect.getblock(lines[first_line - 1:]))
|
|
1225
|
+
return ClassSourceDeclaration.from_block(block, class_node, first_line)
|
|
1213
1226
|
|
|
1214
1227
|
@staticmethod
|
|
1215
1228
|
def _extract_inline_field_docs(dataclass_type: type) -> Dict[str, str]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-introspect
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Pure Python introspection toolkit for function signatures, dataclasses, and type hints
|
|
5
5
|
Author-email: Tristan Simas <tristan.simas@mail.mcgill.ca>
|
|
6
6
|
License: MIT
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
20
|
Classifier: Topic :: Utilities
|
|
20
21
|
Requires-Python: >=3.10
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass, field
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Annotated, Literal
|
|
4
|
-
from collections.abc import Sequence
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
@@ -297,3 +297,37 @@ def test_inherited_nested_types_use_the_declaring_module_namespace(monkeypatch):
|
|
|
297
297
|
)
|
|
298
298
|
result = dataclass_from_mapping(child_module.Child, {"children": [{"name": "child"}]})
|
|
299
299
|
assert isinstance(result.children[0], base_module.Leaf)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
WireValue = (
|
|
303
|
+
str
|
|
304
|
+
| int
|
|
305
|
+
| float
|
|
306
|
+
| bool
|
|
307
|
+
| None
|
|
308
|
+
| list["WireValue"]
|
|
309
|
+
| tuple["WireValue", ...]
|
|
310
|
+
| dict[str, "WireValue"]
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@dataclass(frozen=True)
|
|
315
|
+
class WireEnvelope:
|
|
316
|
+
sequence: int
|
|
317
|
+
event: Mapping[str, WireValue] | None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_recursive_type_alias_decodes_nested_values() -> None:
|
|
321
|
+
# Python 3.10 keeps "WireValue" as a string inside the PEP 585 aliases;
|
|
322
|
+
# resolving it must stop where the alias names itself.
|
|
323
|
+
payload = {
|
|
324
|
+
"sequence": 1,
|
|
325
|
+
"event": {
|
|
326
|
+
"phase": "compile",
|
|
327
|
+
"percent": 50.0,
|
|
328
|
+
"context": {"axes": ["A01"], "details": [{"ready": True, "error": None}]},
|
|
329
|
+
},
|
|
330
|
+
}
|
|
331
|
+
result = dataclass_from_mapping(WireEnvelope, payload)
|
|
332
|
+
assert result == WireEnvelope(1, payload["event"])
|
|
333
|
+
validate_annotated_dataclass(result)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/callable_declaration.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/dataclass_projection.py
RENAMED
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/docstring_annotations.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/environment_projection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect/runtime_parameter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/requires.txt
RENAMED
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/src/python_introspect.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_dataclass_source_preparation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_unified_parameter_analyzer.py
RENAMED
|
File without changes
|
{python_introspect-0.2.3 → python_introspect-0.2.5}/tests/test_validate_python_release_action.py
RENAMED
|
File without changes
|
|
File without changes
|