asyncapi-python 0.1.2__tar.gz → 0.1.4__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.
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/PKG-INFO +9 -2
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/README.md +7 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/pyproject.toml +2 -2
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/consumer.py +2 -2
- asyncapi_python-0.1.4/src/asyncapi_python_codegen/document/utils.py +101 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/generate.py +24 -9
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/LICENSE +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/base_application.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/connection.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/message_handler.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/message_handler_params.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/producer.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/utils.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/py.typed +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/base.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/bindings/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/bindings/amqp.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/components.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/document.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/document_context.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/ref.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/__init__.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/templates/__init__.py.j2 +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/templates/application.py.j2 +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/utils.py +0 -0
- {asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncapi-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Easily generate type-safe and async Python applications from AsyncAPI 3 specifications.
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Yaroslav Petrov
|
|
@@ -16,7 +16,7 @@ Provides-Extra: amqp
|
|
|
16
16
|
Provides-Extra: codegen
|
|
17
17
|
Requires-Dist: aio-pika ; extra == "amqp"
|
|
18
18
|
Requires-Dist: black ; extra == "codegen"
|
|
19
|
-
Requires-Dist: datamodel-code-generator[http] (>=0.26.
|
|
19
|
+
Requires-Dist: datamodel-code-generator[http] (>=0.26.4,<0.27.0) ; extra == "codegen"
|
|
20
20
|
Requires-Dist: jinja2 (>=3.1.4,<4.0.0) ; extra == "codegen"
|
|
21
21
|
Requires-Dist: pydantic (>=2)
|
|
22
22
|
Requires-Dist: pytz
|
|
@@ -25,6 +25,13 @@ Requires-Dist: typer[all] (>=0.12.5,<0.13.0) ; extra == "codegen"
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
|
|
27
27
|
# AsyncAPI Python Code Generator
|
|
28
|
+
> [!IMPORTANT]
|
|
29
|
+
> Although commits to dev branch might seem infrequent, the project is under active development **as of December 2024**.
|
|
30
|
+
>
|
|
31
|
+
> We currently produce only those changes that are required to satisfy our personal use cases.
|
|
32
|
+
>
|
|
33
|
+
> The number of commits will grow as we see increase in popularity of it, so do not hesitate
|
|
34
|
+
> to star this repo, open issues, and pull requests.
|
|
28
35
|
|
|
29
36
|
[Link to this github repository](https://github.com/G-USI/asyncapi-python)
|
|
30
37
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
# AsyncAPI Python Code Generator
|
|
2
|
+
> [!IMPORTANT]
|
|
3
|
+
> Although commits to dev branch might seem infrequent, the project is under active development **as of December 2024**.
|
|
4
|
+
>
|
|
5
|
+
> We currently produce only those changes that are required to satisfy our personal use cases.
|
|
6
|
+
>
|
|
7
|
+
> The number of commits will grow as we see increase in popularity of it, so do not hesitate
|
|
8
|
+
> to star this repo, open issues, and pull requests.
|
|
2
9
|
|
|
3
10
|
[Link to this github repository](https://github.com/G-USI/asyncapi-python)
|
|
4
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "asyncapi-python"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
license = "Apache-2.0"
|
|
5
5
|
description = "Easily generate type-safe and async Python applications from AsyncAPI 3 specifications."
|
|
6
6
|
authors = ["Yaroslav Petrov <yaroslav.v.petrov@gmail.com>"]
|
|
@@ -32,7 +32,7 @@ jinja2 = { version = "^3.1.4", optional = true }
|
|
|
32
32
|
typer = { extras = ["all"], version = "^0.12.5", optional = true }
|
|
33
33
|
datamodel-code-generator = { extras = [
|
|
34
34
|
"http",
|
|
35
|
-
], version = "^0.26.
|
|
35
|
+
], version = "^0.26.4", optional = true }
|
|
36
36
|
aio-pika = { version = "*", optional = true }
|
|
37
37
|
pyyaml = { version = "*", optional = true }
|
|
38
38
|
black = { version = "*", optional = true }
|
|
@@ -68,7 +68,7 @@ class Consumer:
|
|
|
68
68
|
name=params.root.name,
|
|
69
69
|
callback=callback,
|
|
70
70
|
decode_message=lambda x: decode_message(
|
|
71
|
-
x, union_model(input_types)
|
|
71
|
+
x, union_model(tuple(input_types))
|
|
72
72
|
).root,
|
|
73
73
|
)
|
|
74
74
|
else:
|
|
@@ -78,7 +78,7 @@ class Consumer:
|
|
|
78
78
|
reply_callback=self._reply_callback,
|
|
79
79
|
encode_message=encode_message,
|
|
80
80
|
decode_message=lambda x: decode_message(
|
|
81
|
-
x, union_model(input_types)
|
|
81
|
+
x, union_model(tuple(input_types))
|
|
82
82
|
).root,
|
|
83
83
|
)
|
|
84
84
|
self._handlers[params] = handler
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Copyright 2024 Yaroslav Petrov <yaroslav.v.petrov@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Union
|
|
17
|
+
import yaml
|
|
18
|
+
|
|
19
|
+
from .document_context import set_current_doc_path
|
|
20
|
+
from .ref import Ref
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Reference = Union[None, tuple[Path, tuple[str, ...]]]
|
|
25
|
+
"""A reference type"""
|
|
26
|
+
|
|
27
|
+
ReferenceCounter = defaultdict[Reference, set[Reference]]
|
|
28
|
+
"""A reference counter"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def populate_jsonschema_defs(schema: Any) -> Any:
|
|
32
|
+
"""Given a $defs element of the JsonSchema
|
|
33
|
+
1. Constructs back references map for all links
|
|
34
|
+
2. Populates types by copying its body into parent $def (if there is only one reference)
|
|
35
|
+
3. Adds a new $defs object (if there is more than one reference), and rewrites $refs
|
|
36
|
+
4. Returns a huge jsonschema $defs object containing all structs that have been referenced by the structs
|
|
37
|
+
from the original schema
|
|
38
|
+
"""
|
|
39
|
+
counter: ReferenceCounter = defaultdict(lambda: set())
|
|
40
|
+
shared_schemas: dict[str, Any] = {}
|
|
41
|
+
_count_references(schema, None, counter)
|
|
42
|
+
res = _populate_jsonschema_recur(schema, counter, shared_schemas)
|
|
43
|
+
return {**res, **shared_schemas}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _count_references(schema: Any, this: Reference, counter: ReferenceCounter):
|
|
47
|
+
"""Recursively constructs back references within the JsonSchema"""
|
|
48
|
+
if not isinstance(schema, dict):
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
if "$ref" in schema:
|
|
52
|
+
ref: Ref[Any] = Ref.model_validate(schema)
|
|
53
|
+
with set_current_doc_path(ref.filepath):
|
|
54
|
+
ref = ref.flatten()
|
|
55
|
+
with ref.filepath.open() as f:
|
|
56
|
+
doc = yaml.safe_load(f)
|
|
57
|
+
for p in ref.doc_path:
|
|
58
|
+
doc = doc[p]
|
|
59
|
+
child = (ref.filepath, ref.doc_path)
|
|
60
|
+
counter[child].add(this)
|
|
61
|
+
with set_current_doc_path(ref.filepath):
|
|
62
|
+
return _count_references(doc, child, counter)
|
|
63
|
+
|
|
64
|
+
for v in schema.values():
|
|
65
|
+
_count_references(v, this, counter)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _populate_jsonschema_recur(
|
|
69
|
+
schema: Any,
|
|
70
|
+
counter: ReferenceCounter,
|
|
71
|
+
shared_schemas: dict[str, Any],
|
|
72
|
+
ignore_shared: bool = False,
|
|
73
|
+
) -> Any:
|
|
74
|
+
"""Recursively populates JsonSchema $defs object"""
|
|
75
|
+
if not isinstance(schema, dict):
|
|
76
|
+
return schema
|
|
77
|
+
|
|
78
|
+
if "$ref" in schema:
|
|
79
|
+
ref: Ref[Any] = Ref.model_validate(schema)
|
|
80
|
+
with set_current_doc_path(ref.filepath):
|
|
81
|
+
ref = ref.flatten()
|
|
82
|
+
|
|
83
|
+
back_refs = counter[(ref.filepath, ref.doc_path)]
|
|
84
|
+
if len(back_refs) > 1 and not ignore_shared:
|
|
85
|
+
ref_struct_name = ref.doc_path[-1]
|
|
86
|
+
shared_schemas[ref_struct_name] = _populate_jsonschema_recur(
|
|
87
|
+
schema, counter, shared_schemas, True
|
|
88
|
+
)
|
|
89
|
+
return {"$ref": f"#/$defs/{ref_struct_name}"}
|
|
90
|
+
|
|
91
|
+
with ref.filepath.open() as f:
|
|
92
|
+
doc = yaml.safe_load(f)
|
|
93
|
+
for p in ref.doc_path:
|
|
94
|
+
doc = doc[p]
|
|
95
|
+
with set_current_doc_path(ref.filepath):
|
|
96
|
+
return _populate_jsonschema_recur(doc, counter, shared_schemas)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
k: _populate_jsonschema_recur(v, counter, shared_schemas)
|
|
100
|
+
for k, v in schema.items()
|
|
101
|
+
}
|
|
@@ -24,6 +24,8 @@ from asyncapi_python_codegen import document as d
|
|
|
24
24
|
from itertools import chain
|
|
25
25
|
from datamodel_code_generator.__main__ import main as datamodel_codegen
|
|
26
26
|
|
|
27
|
+
from asyncapi_python_codegen.document.utils import populate_jsonschema_defs
|
|
28
|
+
|
|
27
29
|
from .utils import snake_case
|
|
28
30
|
|
|
29
31
|
|
|
@@ -47,7 +49,10 @@ def generate(
|
|
|
47
49
|
doc.info.description,
|
|
48
50
|
doc.info.version,
|
|
49
51
|
).items()
|
|
50
|
-
} | {
|
|
52
|
+
} | {
|
|
53
|
+
output_path / "models.py": generate_models(ops, doc.filepath.parent),
|
|
54
|
+
output_path / "py.typed": "",
|
|
55
|
+
}
|
|
51
56
|
|
|
52
57
|
|
|
53
58
|
def generate_application(
|
|
@@ -69,14 +74,16 @@ def generate_application(
|
|
|
69
74
|
def generate_models(schemas: list[Operation], cwd: Path) -> str:
|
|
70
75
|
inp = {
|
|
71
76
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
72
|
-
"$defs":
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
"$defs": populate_jsonschema_defs(
|
|
78
|
+
{
|
|
79
|
+
type_name: {"$ref": type_schema}
|
|
80
|
+
for s in schemas
|
|
81
|
+
for type_name, type_schema in chain(
|
|
82
|
+
zip(s["input_types"], s["input_schemas"]),
|
|
83
|
+
zip(s["output_types"], s["output_schemas"]),
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
),
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
with tempfile.TemporaryDirectory() as dir:
|
|
@@ -88,6 +95,14 @@ def generate_models(schemas: list[Operation], cwd: Path) -> str:
|
|
|
88
95
|
--output { str(models_path.absolute()) }
|
|
89
96
|
--output-model-type pydantic_v2.BaseModel
|
|
90
97
|
--input-file-type jsonschema
|
|
98
|
+
--reuse-model
|
|
99
|
+
--allow-extra-fields
|
|
100
|
+
--collapse-root-models
|
|
101
|
+
--target-python-version 3.9
|
|
102
|
+
--use-title-as-name
|
|
103
|
+
--capitalize-enum-members
|
|
104
|
+
--snake-case-field
|
|
105
|
+
--allow-population-by-field-name
|
|
91
106
|
""".split()
|
|
92
107
|
|
|
93
108
|
with schema_path.open("w") as schema:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/base_application.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python/amqp/message_handler_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/__init__.py
RENAMED
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/components.py
RENAMED
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/document/document.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{asyncapi_python-0.1.2 → asyncapi_python-0.1.4}/src/asyncapi_python_codegen/generators/amqp/utils.py
RENAMED
|
File without changes
|
|
File without changes
|