neobuilder6 6.1.0__py3-none-any.whl
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.
- neobuilder6/__init__.py +5 -0
- neobuilder6/cli/__init__.py +0 -0
- neobuilder6/cli/neobuilder.py +48 -0
- neobuilder6/data/templates/dataclass.jinja2 +12 -0
- neobuilder6/data/templates/dataclass_module.jinja2 +34 -0
- neobuilder6/data/templates/grpc_receiver.jinja2 +21 -0
- neobuilder6/data/templates/grpc_sender.jinja2 +21 -0
- neobuilder6/data/templates/init.jinja2 +0 -0
- neobuilder6/data/templates/interface.jinja2 +22 -0
- neobuilder6/data/templates/macros/indentclude.jinja2 +2 -0
- neobuilder6/data/templates/package_aliases.jinja2 +10 -0
- neobuilder6/data/templates/parts/_enum_map.jinja2 +1 -0
- neobuilder6/data/templates/parts/_interface_method.jinja2 +2 -0
- neobuilder6/data/templates/parts/_interface_service.jinja2 +10 -0
- neobuilder6/data/templates/parts/_nested_messages.jinja2 +1 -0
- neobuilder6/data/templates/root_init.jinja2 +12 -0
- neobuilder6/descwrap/__init__.py +2 -0
- neobuilder6/descwrap/_field.py +188 -0
- neobuilder6/descwrap/_method.py +57 -0
- neobuilder6/generators/__init__.py +0 -0
- neobuilder6/generators/servicebuilders/__init__.py +27 -0
- neobuilder6/generators/servicebuilders/base.py +99 -0
- neobuilder6/generators/servicebuilders/grpc_receiver.py +92 -0
- neobuilder6/generators/servicebuilders/grpc_sender.py +65 -0
- neobuilder6/generators/servicebuilders/grpc_server.py +54 -0
- neobuilder6/generators/servicebuilders/implementation.py +49 -0
- neobuilder6/generators/servicebuilders/interface.py +84 -0
- neobuilder6/generators/symbols/__init__.py +4 -0
- neobuilder6/generators/symbols/common.py +68 -0
- neobuilder6/generators/symbols/dataclass.py +471 -0
- neobuilder6/generators/symbols/modules.py +232 -0
- neobuilder6/generators/symbols/service.py +139 -0
- neobuilder6/neobuilder/__init__.py +455 -0
- neobuilder6/neobuilder/render/__init__.py +66 -0
- neobuilder6/neobuilder/render/renderer.py +37 -0
- neobuilder6/structs/__init__.py +5 -0
- neobuilder6/structs/_base.py +3 -0
- neobuilder6/structs/protofile.py +20 -0
- neobuilder6/structs/pytype.py +91 -0
- neobuilder6-6.1.0.dist-info/METADATA +231 -0
- neobuilder6-6.1.0.dist-info/RECORD +45 -0
- neobuilder6-6.1.0.dist-info/WHEEL +5 -0
- neobuilder6-6.1.0.dist-info/entry_points.txt +3 -0
- neobuilder6-6.1.0.dist-info/licenses/LICENSE +22 -0
- neobuilder6-6.1.0.dist-info/top_level.txt +1 -0
neobuilder6/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from neobuilder6.neobuilder import NeoBuilder
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
parser = argparse.ArgumentParser(prog='neobuild',
|
|
8
|
+
description='Builds neobuf packages with protoplasm6.',
|
|
9
|
+
epilog=(f'Neobuilder6 v{NeoBuilder.neobuilder_version()} - '
|
|
10
|
+
f'Protoplasm6 v{NeoBuilder.protoplasm_version()}'))
|
|
11
|
+
version_group = parser.add_mutually_exclusive_group()
|
|
12
|
+
version_group.add_argument('-m', '--major', action="store_true",
|
|
13
|
+
help='Bump the major version number instead of the minor')
|
|
14
|
+
version_group.add_argument('-p', '--patch', action="store_true",
|
|
15
|
+
help='Bump the patch version number instead of the minor')
|
|
16
|
+
parser.add_argument('package', help='Package name')
|
|
17
|
+
parser.add_argument('protopath', help='Path to the root of the protobuf files (default="./proto")',
|
|
18
|
+
default='./proto', nargs='?')
|
|
19
|
+
parser.add_argument('-b', '--buildroot', help='Path to the root of the output build files (default="./build")',
|
|
20
|
+
default='./build', nargs='?')
|
|
21
|
+
parser.add_argument('-v', '--verbose', action="store_true",
|
|
22
|
+
help='Spits out DEBUG level logs')
|
|
23
|
+
parser.add_argument('-i', '--pyi', action=argparse.BooleanOptionalAction, default=True,
|
|
24
|
+
help='Builds *.pyi type stubs for the pb2 files as well (default=True, use --no-pyi to skip)')
|
|
25
|
+
parser.add_argument('--alias-imports', action=argparse.BooleanOptionalAction, default=True,
|
|
26
|
+
help='Alias imports everything in each proto package in the __init__.py of the '
|
|
27
|
+
'corresponding Python package, e.g. so "from myshop.orders import *" works '
|
|
28
|
+
'(default=True, use --no-alias-imports to skip)')
|
|
29
|
+
parser.add_argument('-I', '--include', action='append', help="Optional additional proto paths to include (can be used multiple times)", default=[])
|
|
30
|
+
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
|
|
33
|
+
n = NeoBuilder(
|
|
34
|
+
package=args.package,
|
|
35
|
+
protopath=args.protopath,
|
|
36
|
+
build_root=args.buildroot,
|
|
37
|
+
major=args.major,
|
|
38
|
+
patch=args.patch,
|
|
39
|
+
verbose=args.verbose,
|
|
40
|
+
include_pyi=args.pyi,
|
|
41
|
+
extra_includes=args.include,
|
|
42
|
+
alias_imports=args.alias_imports,
|
|
43
|
+
)
|
|
44
|
+
n.build()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == '__main__':
|
|
48
|
+
main()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
@dataclasses.dataclass
|
|
3
|
+
class {{ class_name }}(bases.DataclassBase):
|
|
4
|
+
__proto_cls__ = pb2.{{ proto_cls_name }}
|
|
5
|
+
|
|
6
|
+
{{ indentclude('parts/_nested_messages.jinja2', 4) }}
|
|
7
|
+
|
|
8
|
+
{{ indentclude('parts/_enum_map.jinja2', 4) }}
|
|
9
|
+
|
|
10
|
+
{% for pf in field_map %}
|
|
11
|
+
{{ pf }}
|
|
12
|
+
{% endfor %}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_list %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import dataclasses
|
|
11
|
+
import datetime
|
|
12
|
+
import enum
|
|
13
|
+
import typing
|
|
14
|
+
from protoplasm6.casting import dictators
|
|
15
|
+
from protoplasm6 import plasm
|
|
16
|
+
{{ imports }}
|
|
17
|
+
{{ proto_import }}
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
{% if enum_list %}
|
|
24
|
+
{% for enum in enum_list %}
|
|
25
|
+
{{ enum }}
|
|
26
|
+
|
|
27
|
+
{% endfor %}
|
|
28
|
+
{% endif %}
|
|
29
|
+
{% if message_list %}
|
|
30
|
+
{% for msg in message_list %}
|
|
31
|
+
{{ msg }}
|
|
32
|
+
|
|
33
|
+
{% endfor -%}
|
|
34
|
+
{%- endif -%}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
__all__ = [
|
|
5
|
+
{% for a in all_list %}
|
|
6
|
+
'{{ a }}',
|
|
7
|
+
{% endfor %}
|
|
8
|
+
]
|
|
9
|
+
import datetime
|
|
10
|
+
import typing
|
|
11
|
+
from protoplasm6 import plasm
|
|
12
|
+
{{ imports }}
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from grpc import ServicerContext
|
|
15
|
+
{{ api_import }}
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
{{ services }}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_list %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import datetime
|
|
11
|
+
import typing
|
|
12
|
+
from protoplasm6 import plasm
|
|
13
|
+
{{ imports }}
|
|
14
|
+
if typing.TYPE_CHECKING:
|
|
15
|
+
from grpc import ChannelCredentials
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
{{ services }}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
3
|
+
# Source module: {{ module_full_name }}
|
|
4
|
+
# Generated at: {{ iso_timestamp }}
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_clause %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import datetime
|
|
11
|
+
import typing
|
|
12
|
+
from protoplasm6 import plasm
|
|
13
|
+
|
|
14
|
+
{% for import_line in import_lines %}
|
|
15
|
+
{{ import_line }}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
{{ services }}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Proto package alias imports, i.e. everything in the `{{ proto_package }}` proto package
|
|
3
|
+
__all__ = [
|
|
4
|
+
{% for name in names %}
|
|
5
|
+
'{{ name }}',
|
|
6
|
+
{% endfor %}
|
|
7
|
+
]
|
|
8
|
+
{% for module in modules %}
|
|
9
|
+
from {{ module }} import *
|
|
10
|
+
{% endfor %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# ENUMS GO HERE...
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
class {{ service_name }}Interface:
|
|
3
|
+
{% if is_grpc %}
|
|
4
|
+
__servicer_cls__ = {{ service_name }}GrpcServicer
|
|
5
|
+
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% for method in method_list %}
|
|
8
|
+
{{ indentclude('parts/_interface_method.jinja2', (indent_spaces + 4), method) }}
|
|
9
|
+
|
|
10
|
+
{% endfor %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# NESTED MESSAGES GO HERE...
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
__version__ = '{{ version }}'{% if version == '' %} # NO VERSION NUMBERS FOUND?!? :({% endif +%}
|
|
2
|
+
__protoplasm_version__ = '{{ protoplasm_version }}'
|
|
3
|
+
__neobuilder_version__ = '{{ neobuilder_version }}'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def load_symbols():
|
|
7
|
+
from {{ package_name }} import __everything__{% if alias_init %}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
{{ alias_init }}
|
|
12
|
+
{%- endif %}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'FieldKind',
|
|
3
|
+
'FieldDescriptorWrapper',
|
|
4
|
+
]
|
|
5
|
+
import dataclasses
|
|
6
|
+
import enum
|
|
7
|
+
from typing import *
|
|
8
|
+
import keyword
|
|
9
|
+
from google.protobuf.json_format import descriptor
|
|
10
|
+
from google.protobuf import json_format
|
|
11
|
+
from neobuilder6.structs.pytype import *
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FieldKind(enum.IntFlag):
|
|
15
|
+
UNKNOWN = 0x0000
|
|
16
|
+
DATA_SCALAR = 0x0001
|
|
17
|
+
DATA_MESSAGE = 0x0002
|
|
18
|
+
DATA_ENUM = 0x0004
|
|
19
|
+
|
|
20
|
+
KIND_SINGLE = 0x0010
|
|
21
|
+
KIND_LIST = 0x0020
|
|
22
|
+
KIND_MAP = 0x0040
|
|
23
|
+
|
|
24
|
+
SPECIAL_TIMESTAMP = 0x00100
|
|
25
|
+
SPECIAL_BYTES = 0x00200
|
|
26
|
+
SPECIAL_LONG = 0x00400
|
|
27
|
+
SPECIAL_DURATION = 0x00800
|
|
28
|
+
SPECIAL_ANY = 0x01000
|
|
29
|
+
SPECIAL_EMPTY = 0x02000
|
|
30
|
+
SPECIAL_STRUCT = 0x04000
|
|
31
|
+
SPECIAL_VALUE = 0x08000
|
|
32
|
+
SPECIAL_LIST_VALUE = 0x10000
|
|
33
|
+
SPECIAL_NULL_VALUE = 0x20000
|
|
34
|
+
|
|
35
|
+
SPECIAL_KEYWORD = 0x10000000
|
|
36
|
+
|
|
37
|
+
# scalar
|
|
38
|
+
FIELD_SIMPLE = DATA_SCALAR | KIND_SINGLE
|
|
39
|
+
# Message
|
|
40
|
+
FIELD_MESSAGE = DATA_MESSAGE | KIND_SINGLE
|
|
41
|
+
# Enum
|
|
42
|
+
FIELD_ENUM = DATA_ENUM | KIND_SINGLE
|
|
43
|
+
|
|
44
|
+
# repeated scalar
|
|
45
|
+
FIELD_SIMPLE_LIST = DATA_SCALAR | KIND_LIST
|
|
46
|
+
# repeated Message
|
|
47
|
+
FIELD_MESSAGE_LIST = DATA_MESSAGE | KIND_LIST
|
|
48
|
+
# repeated Enum
|
|
49
|
+
FIELD_ENUM_LIST = DATA_ENUM | KIND_LIST
|
|
50
|
+
|
|
51
|
+
# map<scalar, scalar>
|
|
52
|
+
FIELD_SIMPLE_MAP = DATA_SCALAR | KIND_MAP
|
|
53
|
+
# map<scalar, Message>
|
|
54
|
+
FIELD_MESSAGE_MAP = DATA_MESSAGE | KIND_MAP
|
|
55
|
+
# map<scalar, Enum>
|
|
56
|
+
FIELD_ENUM_MAP = DATA_ENUM | KIND_MAP
|
|
57
|
+
|
|
58
|
+
FIELD_STRUCT_VALUES = SPECIAL_STRUCT | SPECIAL_VALUE | SPECIAL_LIST_VALUE | SPECIAL_NULL_VALUE
|
|
59
|
+
|
|
60
|
+
def __repr__(self) -> str:
|
|
61
|
+
return self.name or str(self.value)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclasses.dataclass
|
|
65
|
+
class FieldDescriptorWrapper:
|
|
66
|
+
"""This is a wrapper around Google's protobuf FieldDescriptor.
|
|
67
|
+
|
|
68
|
+
Its point is twofold.
|
|
69
|
+
|
|
70
|
+
1. To mask complex lookups and evaluations behind a simpler facade to use
|
|
71
|
+
in the rest of the code.
|
|
72
|
+
2. To abstract away the exact structure of Google's descriptor to guard
|
|
73
|
+
against future code changes, especially in internal parts that aren't
|
|
74
|
+
part of the official API.
|
|
75
|
+
"""
|
|
76
|
+
real_descriptor: descriptor.FieldDescriptor = dataclasses.field(repr=False)
|
|
77
|
+
|
|
78
|
+
name: str = dataclasses.field(default='', init=False)
|
|
79
|
+
|
|
80
|
+
kind: FieldKind = dataclasses.field(default=FieldKind.UNKNOWN, init=False)
|
|
81
|
+
|
|
82
|
+
options: Optional[Any] = dataclasses.field(default=None, init=False, repr=False)
|
|
83
|
+
|
|
84
|
+
key_field: Optional[descriptor.FieldDescriptor] = dataclasses.field(default=None, init=False, repr=False)
|
|
85
|
+
value_field: Optional[descriptor.FieldDescriptor] = dataclasses.field(default=None, init=False, repr=False)
|
|
86
|
+
value_msg: Optional[descriptor.Descriptor] = dataclasses.field(default=None, init=False, repr=False)
|
|
87
|
+
|
|
88
|
+
py_type: PyType = dataclasses.field(default=PY_TYPE_MAP[ProtoType.UNKNOWN], init=False)
|
|
89
|
+
key_py_type: PyType = dataclasses.field(default=PY_TYPE_MAP[ProtoType.UNKNOWN], init=False)
|
|
90
|
+
|
|
91
|
+
is_proto_repeated: bool = dataclasses.field(default=False, init=False)
|
|
92
|
+
|
|
93
|
+
def __post_init__(self):
|
|
94
|
+
self.name = self.real_descriptor.name
|
|
95
|
+
self.is_proto_repeated = self.real_descriptor.is_repeated
|
|
96
|
+
self._check_field_kind()
|
|
97
|
+
|
|
98
|
+
def _load_options(self):
|
|
99
|
+
self.options = None
|
|
100
|
+
if self.real_descriptor.type == ProtoType.MESSAGE:
|
|
101
|
+
if self.real_descriptor.message_type.has_options:
|
|
102
|
+
self.options = self.real_descriptor.message_type.GetOptions()
|
|
103
|
+
|
|
104
|
+
def _check_field_kind(self):
|
|
105
|
+
self._load_options()
|
|
106
|
+
|
|
107
|
+
if self.options and self.options.map_entry: # Message that is a map...
|
|
108
|
+
self.key_field = self.real_descriptor.message_type.fields_by_name['key']
|
|
109
|
+
self.key_py_type = PY_TYPE_MAP[self.key_field.type]
|
|
110
|
+
self.value_field = self.real_descriptor.message_type.fields_by_name['value']
|
|
111
|
+
self.kind |= FieldKind.KIND_MAP
|
|
112
|
+
else:
|
|
113
|
+
self.value_field = self.real_descriptor
|
|
114
|
+
if self.is_proto_repeated:
|
|
115
|
+
self.kind |= FieldKind.KIND_LIST
|
|
116
|
+
else:
|
|
117
|
+
self.kind |= FieldKind.KIND_SINGLE
|
|
118
|
+
|
|
119
|
+
if self.value_field.type == ProtoType.MESSAGE: # Message
|
|
120
|
+
self.value_msg = self.value_field.message_type
|
|
121
|
+
|
|
122
|
+
if self.value_msg.full_name == 'google.protobuf.Timestamp':
|
|
123
|
+
self.kind |= FieldKind.DATA_SCALAR
|
|
124
|
+
self.kind |= FieldKind.SPECIAL_TIMESTAMP
|
|
125
|
+
elif self.value_msg.full_name == 'google.protobuf.Duration':
|
|
126
|
+
self.kind |= FieldKind.DATA_SCALAR
|
|
127
|
+
self.kind |= FieldKind.SPECIAL_DURATION
|
|
128
|
+
elif self.value_msg.full_name == 'google.protobuf.Any':
|
|
129
|
+
self.kind |= FieldKind.DATA_SCALAR
|
|
130
|
+
self.kind |= FieldKind.SPECIAL_ANY
|
|
131
|
+
elif self.value_msg.full_name == 'google.protobuf.Empty':
|
|
132
|
+
self.kind |= FieldKind.SPECIAL_EMPTY
|
|
133
|
+
elif self.value_msg.full_name == 'google.protobuf.Struct':
|
|
134
|
+
self.kind |= FieldKind.SPECIAL_STRUCT
|
|
135
|
+
elif self.value_msg.full_name == 'google.protobuf.Value':
|
|
136
|
+
self.kind |= FieldKind.SPECIAL_VALUE
|
|
137
|
+
elif self.value_msg.full_name == 'google.protobuf.ListValue':
|
|
138
|
+
self.kind |= FieldKind.SPECIAL_LIST_VALUE
|
|
139
|
+
elif self.value_msg.full_name == 'google.protobuf.NullValue':
|
|
140
|
+
self.kind |= FieldKind.SPECIAL_NULL_VALUE
|
|
141
|
+
else:
|
|
142
|
+
self.kind |= FieldKind.DATA_MESSAGE
|
|
143
|
+
elif self.value_field.type == ProtoType.ENUM: # Enum
|
|
144
|
+
self.kind |= FieldKind.DATA_ENUM
|
|
145
|
+
|
|
146
|
+
else: # Scalar
|
|
147
|
+
self.kind |= FieldKind.DATA_SCALAR
|
|
148
|
+
|
|
149
|
+
if self.value_field.type == ProtoType.BYTES:
|
|
150
|
+
self.kind |= FieldKind.SPECIAL_BYTES
|
|
151
|
+
else:
|
|
152
|
+
if self.value_field.cpp_type in json_format._INT64_TYPES:
|
|
153
|
+
self.kind |= FieldKind.SPECIAL_LONG
|
|
154
|
+
|
|
155
|
+
self.py_type = PY_TYPE_MAP[self.value_field.type]
|
|
156
|
+
|
|
157
|
+
if keyword.iskeyword(self.name):
|
|
158
|
+
self.kind |= FieldKind.SPECIAL_KEYWORD
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def camelcase_name(self) -> str:
|
|
162
|
+
return self.real_descriptor.camelcase_name
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def json_name(self) -> str:
|
|
166
|
+
return self.real_descriptor.json_name
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def default_value(self) -> Any:
|
|
170
|
+
return self.real_descriptor.default_value
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def full_name(self) -> Any:
|
|
174
|
+
return self.real_descriptor.full_name
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# def main():
|
|
178
|
+
# fields = []
|
|
179
|
+
# from sandbox.test import RainbowMessage
|
|
180
|
+
# for f in RainbowMessage.DESCRIPTOR.fields:
|
|
181
|
+
# ff = FieldDescriptorWrapper(f)
|
|
182
|
+
# print(ff)
|
|
183
|
+
# fields.append(ff)
|
|
184
|
+
#
|
|
185
|
+
# # print(fields)
|
|
186
|
+
#
|
|
187
|
+
# if __name__ == '__main__':
|
|
188
|
+
# main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MethodDescriptorWrapper',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
|
|
7
|
+
from neobuilder6.structs import *
|
|
8
|
+
from google.protobuf.json_format import descriptor
|
|
9
|
+
from protoplasm6.casting import castutils
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclasses.dataclass
|
|
13
|
+
class MethodDescriptorWrapper:
|
|
14
|
+
"""This is a wrapper around Google's protobuf MethodDescriptor.
|
|
15
|
+
|
|
16
|
+
Its point is twofold.
|
|
17
|
+
|
|
18
|
+
1. To mask complex lookups and evaluations behind a simpler facade to use
|
|
19
|
+
in the rest of the code.
|
|
20
|
+
2. To abstract away the exact structure of Google's descriptor to guard
|
|
21
|
+
against future code changes, especially in internal parts that aren't
|
|
22
|
+
part of the official API.
|
|
23
|
+
"""
|
|
24
|
+
real_descriptor: descriptor.MethodDescriptor = dataclasses.field(repr=False)
|
|
25
|
+
|
|
26
|
+
name: str = dataclasses.field(default='', init=False)
|
|
27
|
+
under_name: str = dataclasses.field(default='', init=False)
|
|
28
|
+
|
|
29
|
+
client_streaming: bool = False
|
|
30
|
+
server_streaming: bool = False
|
|
31
|
+
|
|
32
|
+
input_type: Optional[descriptor.Descriptor] = dataclasses.field(default=None, init=False, repr=False)
|
|
33
|
+
output_type: Optional[descriptor.Descriptor] = dataclasses.field(default=None, init=False, repr=False)
|
|
34
|
+
|
|
35
|
+
io_type: MethodIoType = dataclasses.field(default=MethodIoType._UNKNOWN, init=False)
|
|
36
|
+
|
|
37
|
+
service_descriptor: descriptor.ServiceDescriptor = dataclasses.field(default=None, init=False, repr=False)
|
|
38
|
+
file_descriptor: descriptor.FileDescriptor = dataclasses.field(default=None, init=False, repr=False)
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
self.name = self.real_descriptor.name
|
|
42
|
+
self.under_name = castutils.humps_to_under(self.name)
|
|
43
|
+
self.input_type = self.real_descriptor.input_type
|
|
44
|
+
self.output_type = self.real_descriptor.output_type
|
|
45
|
+
self.client_streaming = self.real_descriptor.client_streaming
|
|
46
|
+
self.server_streaming = self.real_descriptor.server_streaming
|
|
47
|
+
if self.client_streaming:
|
|
48
|
+
self.io_type |= MethodIoType._INPUT_STREAM
|
|
49
|
+
else:
|
|
50
|
+
self.io_type |= MethodIoType._INPUT_UNARY
|
|
51
|
+
if self.server_streaming:
|
|
52
|
+
self.io_type |= MethodIoType._OUTPUT_STREAM
|
|
53
|
+
else:
|
|
54
|
+
self.io_type |= MethodIoType._OUTPUT_UNARY
|
|
55
|
+
|
|
56
|
+
self.service_descriptor = self.real_descriptor.containing_service
|
|
57
|
+
self.file_descriptor = self.service_descriptor.file
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from . import base
|
|
3
|
+
from batutils.tpu import strimp
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
_CACHE = {}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_builder_module(implementation_name: str):
|
|
13
|
+
if implementation_name not in _CACHE:
|
|
14
|
+
_CACHE[implementation_name] = strimp.get_module(f'{__package__}.{implementation_name}', logger=log, reraise=True)
|
|
15
|
+
return _CACHE[implementation_name]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_module_builder(implementation_name: str) -> typing.Type[base.AbstractModuleBuilder]:
|
|
19
|
+
return getattr(get_builder_module(implementation_name), 'ModuleBuilder')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_service_builder(implementation_name: str) -> typing.Type[base.AbstractServiceBuilder]:
|
|
23
|
+
return getattr(get_builder_module(implementation_name), 'ServiceBuilder')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_method_builder(implementation_name: str) -> typing.Type[base.AbstractMethodBuilder]:
|
|
27
|
+
return getattr(get_builder_module(implementation_name), 'MethodBuilder')
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from neobuilder6.generators.symbols import service as symbols_service
|
|
3
|
+
from neobuilder6.generators.symbols import modules
|
|
4
|
+
import datetime
|
|
5
|
+
from typing import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AbstractModuleBuilder(object):
|
|
9
|
+
builder_name = ''
|
|
10
|
+
|
|
11
|
+
def __init__(self, module: modules.ProtoModule):
|
|
12
|
+
self.module = module
|
|
13
|
+
self.indent_str = self.module.indent
|
|
14
|
+
self.service_joiner = '\n\n'
|
|
15
|
+
|
|
16
|
+
def get_template_context(self) -> Dict:
|
|
17
|
+
return {
|
|
18
|
+
'module_full_name': self.module.get_module_full_name(),
|
|
19
|
+
'iso_timestamp': datetime.datetime.now().isoformat(),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def render_top(self) -> str:
|
|
23
|
+
# TODO(thordurm@ccpgames.com) 2022-06-24: Remove... why?
|
|
24
|
+
return ('# Auto-Generated file - DO NOT EDIT!\n'
|
|
25
|
+
f'# Source module: {self.module.get_module_full_name()}\n'
|
|
26
|
+
f'# Generated at: {datetime.datetime.now().isoformat()}\n')
|
|
27
|
+
|
|
28
|
+
def render_service_list(self) -> List[str]:
|
|
29
|
+
return [svc.render(self.builder_name) for svc in self.module.service_map.values()]
|
|
30
|
+
|
|
31
|
+
def render_services(self) -> str:
|
|
32
|
+
return '\n'.join(self.render_service_list())
|
|
33
|
+
|
|
34
|
+
def get_render_filename(self) -> str:
|
|
35
|
+
raise NotImplementedError('AbstractModuleBuilder not implemented')
|
|
36
|
+
|
|
37
|
+
def render(self) -> str:
|
|
38
|
+
raise NotImplementedError('AbstractModuleBuilder not implemented')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AbstractServiceBuilder(object):
|
|
42
|
+
builder_name = ''
|
|
43
|
+
|
|
44
|
+
def __init__(self, service: symbols_service.ProtoService, indent_level: int = 0):
|
|
45
|
+
self.service = service
|
|
46
|
+
self.indent_level = indent_level
|
|
47
|
+
self.indent_str = self.service.indent
|
|
48
|
+
self.base_indent = self.indent_str * indent_level
|
|
49
|
+
|
|
50
|
+
def get_template_context(self) -> Dict:
|
|
51
|
+
return {
|
|
52
|
+
'is_grpc': self.service.module.is_grpc_file(),
|
|
53
|
+
'service': self.service,
|
|
54
|
+
'service_name': self.service.service_descriptor.name,
|
|
55
|
+
'_indent_level': self.indent_level,
|
|
56
|
+
'_indent_str': self.indent_str,
|
|
57
|
+
'_base_indent': self.base_indent,
|
|
58
|
+
'method_list': [self.get_method_context(m) for m in self.service.method_map.values()],
|
|
59
|
+
'indent_spaces': self.indent_level * 4,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def get_method_context(self, method: symbols_service.ProtoMethod) -> Dict[str, Any]:
|
|
63
|
+
return {
|
|
64
|
+
'method': method,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def render_methods(self) -> str:
|
|
68
|
+
____ = self.indent_str
|
|
69
|
+
i = self.base_indent
|
|
70
|
+
buf = []
|
|
71
|
+
if self.service.method_map:
|
|
72
|
+
buf.append(
|
|
73
|
+
'\n'.join([m.render(self.builder_name, self.indent_level + 1) for m in self.service.method_map.values()]))
|
|
74
|
+
else:
|
|
75
|
+
buf.append(f'{i}pass\n')
|
|
76
|
+
return ''.join(buf)
|
|
77
|
+
|
|
78
|
+
def render(self) -> str:
|
|
79
|
+
raise NotImplementedError('AbstractServiceBuilder not implemented')
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AbstractMethodBuilder(object):
|
|
83
|
+
def __init__(self, method: symbols_service.ProtoMethod, indent_level: int = 0):
|
|
84
|
+
self.method = method
|
|
85
|
+
self.indent_level = indent_level
|
|
86
|
+
self.indent_str = self.method.indent
|
|
87
|
+
self.base_indent = self.indent_str * indent_level
|
|
88
|
+
|
|
89
|
+
def get_template_context(self) -> Dict:
|
|
90
|
+
return {
|
|
91
|
+
'method': self.method,
|
|
92
|
+
'_indent_level': self.indent_level,
|
|
93
|
+
'_indent_str': self.indent_str,
|
|
94
|
+
'_base_indent': self.base_indent,
|
|
95
|
+
'indent_spaces': self.indent_level * 4,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def render(self) -> str:
|
|
99
|
+
raise NotImplementedError('AbstractServiceMethod not implemented')
|