betterproto2-compiler 0.0.1__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.
- betterproto2_compiler/__init__.py +0 -0
- betterproto2_compiler/_types.py +13 -0
- betterproto2_compiler/casing.py +140 -0
- betterproto2_compiler/compile/__init__.py +0 -0
- betterproto2_compiler/compile/importing.py +193 -0
- betterproto2_compiler/compile/naming.py +21 -0
- betterproto2_compiler/enum.py +180 -0
- betterproto2_compiler/grpc/__init__.py +0 -0
- betterproto2_compiler/grpc/grpclib_client.py +172 -0
- betterproto2_compiler/grpc/grpclib_server.py +32 -0
- betterproto2_compiler/grpc/util/__init__.py +0 -0
- betterproto2_compiler/grpc/util/async_channel.py +190 -0
- betterproto2_compiler/lib/__init__.py +0 -0
- betterproto2_compiler/lib/google/__init__.py +0 -0
- betterproto2_compiler/lib/google/protobuf/__init__.py +1 -0
- betterproto2_compiler/lib/google/protobuf/compiler/__init__.py +1 -0
- betterproto2_compiler/lib/pydantic/__init__.py +0 -0
- betterproto2_compiler/lib/pydantic/google/__init__.py +0 -0
- betterproto2_compiler/lib/pydantic/google/protobuf/__init__.py +2690 -0
- betterproto2_compiler/lib/pydantic/google/protobuf/compiler/__init__.py +209 -0
- betterproto2_compiler/lib/std/__init__.py +0 -0
- betterproto2_compiler/lib/std/google/__init__.py +0 -0
- betterproto2_compiler/lib/std/google/protobuf/__init__.py +2517 -0
- betterproto2_compiler/lib/std/google/protobuf/compiler/__init__.py +197 -0
- betterproto2_compiler/plugin/__init__.py +3 -0
- betterproto2_compiler/plugin/__main__.py +3 -0
- betterproto2_compiler/plugin/compiler.py +59 -0
- betterproto2_compiler/plugin/main.py +52 -0
- betterproto2_compiler/plugin/models.py +709 -0
- betterproto2_compiler/plugin/module_validation.py +161 -0
- betterproto2_compiler/plugin/parser.py +263 -0
- betterproto2_compiler/plugin/plugin.bat +2 -0
- betterproto2_compiler/plugin/typing_compiler.py +167 -0
- betterproto2_compiler/py.typed +0 -0
- betterproto2_compiler/templates/header.py.j2 +50 -0
- betterproto2_compiler/templates/template.py.j2 +243 -0
- betterproto2_compiler-0.0.1.dist-info/LICENSE.md +22 -0
- betterproto2_compiler-0.0.1.dist-info/METADATA +35 -0
- betterproto2_compiler-0.0.1.dist-info/RECORD +41 -0
- betterproto2_compiler-0.0.1.dist-info/WHEEL +4 -0
- betterproto2_compiler-0.0.1.dist-info/entry_points.txt +3 -0
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from typing import (
|
2
|
+
TYPE_CHECKING,
|
3
|
+
TypeVar,
|
4
|
+
)
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from grpclib._typing import IProtoMessage
|
8
|
+
|
9
|
+
from . import Message
|
10
|
+
|
11
|
+
# Bound type variable to allow methods to return `self` of subclasses
|
12
|
+
T = TypeVar("T", bound="Message")
|
13
|
+
ST = TypeVar("ST", bound="IProtoMessage")
|
@@ -0,0 +1,140 @@
|
|
1
|
+
import keyword
|
2
|
+
import re
|
3
|
+
|
4
|
+
# Word delimiters and symbols that will not be preserved when re-casing.
|
5
|
+
# language=PythonRegExp
|
6
|
+
SYMBOLS = "[^a-zA-Z0-9]*"
|
7
|
+
|
8
|
+
# Optionally capitalized word.
|
9
|
+
# language=PythonRegExp
|
10
|
+
WORD = "[A-Z]*[a-z]*[0-9]*"
|
11
|
+
|
12
|
+
# Uppercase word, not followed by lowercase letters.
|
13
|
+
# language=PythonRegExp
|
14
|
+
WORD_UPPER = "[A-Z]+(?![a-z])[0-9]*"
|
15
|
+
|
16
|
+
|
17
|
+
def safe_snake_case(value: str) -> str:
|
18
|
+
"""Snake case a value taking into account Python keywords."""
|
19
|
+
value = snake_case(value)
|
20
|
+
value = sanitize_name(value)
|
21
|
+
return value
|
22
|
+
|
23
|
+
|
24
|
+
def snake_case(value: str, strict: bool = True) -> str:
|
25
|
+
"""
|
26
|
+
Join words with an underscore into lowercase and remove symbols.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
-----------
|
30
|
+
value: :class:`str`
|
31
|
+
The value to convert.
|
32
|
+
strict: :class:`bool`
|
33
|
+
Whether or not to force single underscores.
|
34
|
+
|
35
|
+
Returns
|
36
|
+
--------
|
37
|
+
:class:`str`
|
38
|
+
The value in snake_case.
|
39
|
+
"""
|
40
|
+
|
41
|
+
def substitute_word(symbols: str, word: str, is_start: bool) -> str:
|
42
|
+
if not word:
|
43
|
+
return ""
|
44
|
+
if strict:
|
45
|
+
delimiter_count = 0 if is_start else 1 # Single underscore if strict.
|
46
|
+
elif is_start:
|
47
|
+
delimiter_count = len(symbols)
|
48
|
+
elif word.isupper() or word.islower():
|
49
|
+
delimiter_count = max(1, len(symbols)) # Preserve all delimiters if not strict.
|
50
|
+
else:
|
51
|
+
delimiter_count = len(symbols) + 1 # Extra underscore for leading capital.
|
52
|
+
|
53
|
+
return ("_" * delimiter_count) + word.lower()
|
54
|
+
|
55
|
+
snake = re.sub(
|
56
|
+
f"(^)?({SYMBOLS})({WORD_UPPER}|{WORD})",
|
57
|
+
lambda groups: substitute_word(groups[2], groups[3], groups[1] is not None),
|
58
|
+
value,
|
59
|
+
)
|
60
|
+
return snake
|
61
|
+
|
62
|
+
|
63
|
+
def pascal_case(value: str, strict: bool = True) -> str:
|
64
|
+
"""
|
65
|
+
Capitalize each word and remove symbols.
|
66
|
+
|
67
|
+
Parameters
|
68
|
+
-----------
|
69
|
+
value: :class:`str`
|
70
|
+
The value to convert.
|
71
|
+
strict: :class:`bool`
|
72
|
+
Whether or not to output only alphanumeric characters.
|
73
|
+
|
74
|
+
Returns
|
75
|
+
--------
|
76
|
+
:class:`str`
|
77
|
+
The value in PascalCase.
|
78
|
+
"""
|
79
|
+
|
80
|
+
def substitute_word(symbols, word):
|
81
|
+
if strict:
|
82
|
+
return word.capitalize() # Remove all delimiters
|
83
|
+
|
84
|
+
if word.islower():
|
85
|
+
delimiter_length = len(symbols[:-1]) # Lose one delimiter
|
86
|
+
else:
|
87
|
+
delimiter_length = len(symbols) # Preserve all delimiters
|
88
|
+
|
89
|
+
return ("_" * delimiter_length) + word.capitalize()
|
90
|
+
|
91
|
+
return re.sub(
|
92
|
+
f"({SYMBOLS})({WORD_UPPER}|{WORD})",
|
93
|
+
lambda groups: substitute_word(groups[1], groups[2]),
|
94
|
+
value,
|
95
|
+
)
|
96
|
+
|
97
|
+
|
98
|
+
def camel_case(value: str, strict: bool = True) -> str:
|
99
|
+
"""
|
100
|
+
Capitalize all words except first and remove symbols.
|
101
|
+
|
102
|
+
Parameters
|
103
|
+
-----------
|
104
|
+
value: :class:`str`
|
105
|
+
The value to convert.
|
106
|
+
strict: :class:`bool`
|
107
|
+
Whether or not to output only alphanumeric characters.
|
108
|
+
|
109
|
+
Returns
|
110
|
+
--------
|
111
|
+
:class:`str`
|
112
|
+
The value in camelCase.
|
113
|
+
"""
|
114
|
+
return lowercase_first(pascal_case(value, strict=strict))
|
115
|
+
|
116
|
+
|
117
|
+
def lowercase_first(value: str) -> str:
|
118
|
+
"""
|
119
|
+
Lower cases the first character of the value.
|
120
|
+
|
121
|
+
Parameters
|
122
|
+
----------
|
123
|
+
value: :class:`str`
|
124
|
+
The value to lower case.
|
125
|
+
|
126
|
+
Returns
|
127
|
+
-------
|
128
|
+
:class:`str`
|
129
|
+
The lower cased string.
|
130
|
+
"""
|
131
|
+
return value[0:1].lower() + value[1:]
|
132
|
+
|
133
|
+
|
134
|
+
def sanitize_name(value: str) -> str:
|
135
|
+
# https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles
|
136
|
+
if keyword.iskeyword(value):
|
137
|
+
return f"{value}_"
|
138
|
+
if not value.isidentifier():
|
139
|
+
return f"_{value}"
|
140
|
+
return value
|
File without changes
|
@@ -0,0 +1,193 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import os
|
4
|
+
from typing import (
|
5
|
+
TYPE_CHECKING,
|
6
|
+
Dict,
|
7
|
+
List,
|
8
|
+
Set,
|
9
|
+
Tuple,
|
10
|
+
Type,
|
11
|
+
)
|
12
|
+
|
13
|
+
from ..casing import safe_snake_case
|
14
|
+
from ..lib.google import protobuf as google_protobuf
|
15
|
+
from .naming import pythonize_class_name
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from ..plugin.models import PluginRequestCompiler
|
19
|
+
from ..plugin.typing_compiler import TypingCompiler
|
20
|
+
|
21
|
+
WRAPPER_TYPES: Dict[str, Type] = {
|
22
|
+
".google.protobuf.DoubleValue": google_protobuf.DoubleValue,
|
23
|
+
".google.protobuf.FloatValue": google_protobuf.FloatValue,
|
24
|
+
".google.protobuf.Int32Value": google_protobuf.Int32Value,
|
25
|
+
".google.protobuf.Int64Value": google_protobuf.Int64Value,
|
26
|
+
".google.protobuf.UInt32Value": google_protobuf.UInt32Value,
|
27
|
+
".google.protobuf.UInt64Value": google_protobuf.UInt64Value,
|
28
|
+
".google.protobuf.BoolValue": google_protobuf.BoolValue,
|
29
|
+
".google.protobuf.StringValue": google_protobuf.StringValue,
|
30
|
+
".google.protobuf.BytesValue": google_protobuf.BytesValue,
|
31
|
+
}
|
32
|
+
|
33
|
+
|
34
|
+
def parse_source_type_name(field_type_name: str, request: "PluginRequestCompiler") -> Tuple[str, str]:
|
35
|
+
"""
|
36
|
+
Split full source type name into package and type name.
|
37
|
+
E.g. 'root.package.Message' -> ('root.package', 'Message')
|
38
|
+
'root.Message.SomeEnum' -> ('root', 'Message.SomeEnum')
|
39
|
+
|
40
|
+
The function goes through the symbols that have been defined (names, enums,
|
41
|
+
packages) to find the actual package and name of the object that is referenced.
|
42
|
+
"""
|
43
|
+
if field_type_name[0] != ".":
|
44
|
+
raise RuntimeError("relative names are not supported")
|
45
|
+
field_type_name = field_type_name[1:]
|
46
|
+
parts = field_type_name.split(".")
|
47
|
+
|
48
|
+
answer = None
|
49
|
+
|
50
|
+
# a.b.c:
|
51
|
+
# i=0: "", "a.b.c"
|
52
|
+
# i=1: "a", "b.c"
|
53
|
+
# i=2: "a.b", "c"
|
54
|
+
for i in range(len(parts)):
|
55
|
+
package_name, object_name = ".".join(parts[:i]), ".".join(parts[i:])
|
56
|
+
|
57
|
+
package = request.output_packages.get(package_name)
|
58
|
+
|
59
|
+
if not package:
|
60
|
+
continue
|
61
|
+
|
62
|
+
if object_name in package.messages or object_name in package.enums:
|
63
|
+
if answer:
|
64
|
+
# This should have already been handeled by protoc
|
65
|
+
raise ValueError(f"ambiguous definition: {field_type_name}")
|
66
|
+
answer = package_name, object_name
|
67
|
+
|
68
|
+
if answer:
|
69
|
+
return answer
|
70
|
+
|
71
|
+
raise ValueError(f"can't find type name: {field_type_name}")
|
72
|
+
|
73
|
+
|
74
|
+
def get_type_reference(
|
75
|
+
*,
|
76
|
+
package: str,
|
77
|
+
imports: set,
|
78
|
+
source_type: str,
|
79
|
+
typing_compiler: TypingCompiler,
|
80
|
+
request: "PluginRequestCompiler",
|
81
|
+
unwrap: bool = True,
|
82
|
+
pydantic: bool = False,
|
83
|
+
) -> str:
|
84
|
+
"""
|
85
|
+
Return a Python type name for a proto type reference. Adds the import if
|
86
|
+
necessary. Unwraps well known type if required.
|
87
|
+
"""
|
88
|
+
if unwrap:
|
89
|
+
if source_type in WRAPPER_TYPES:
|
90
|
+
wrapped_type = type(WRAPPER_TYPES[source_type]().value)
|
91
|
+
return typing_compiler.optional(wrapped_type.__name__)
|
92
|
+
|
93
|
+
if source_type == ".google.protobuf.Duration":
|
94
|
+
return "datetime.timedelta"
|
95
|
+
|
96
|
+
elif source_type == ".google.protobuf.Timestamp":
|
97
|
+
return "datetime.datetime"
|
98
|
+
|
99
|
+
source_package, source_type = parse_source_type_name(source_type, request)
|
100
|
+
|
101
|
+
current_package: List[str] = package.split(".") if package else []
|
102
|
+
py_package: List[str] = source_package.split(".") if source_package else []
|
103
|
+
py_type: str = pythonize_class_name(source_type)
|
104
|
+
|
105
|
+
compiling_google_protobuf = current_package == ["google", "protobuf"]
|
106
|
+
importing_google_protobuf = py_package == ["google", "protobuf"]
|
107
|
+
if importing_google_protobuf and not compiling_google_protobuf:
|
108
|
+
py_package = ["betterproto", "lib"] + (["pydantic"] if pydantic else []) + py_package
|
109
|
+
|
110
|
+
if py_package[:1] == ["betterproto"]:
|
111
|
+
return reference_absolute(imports, py_package, py_type)
|
112
|
+
|
113
|
+
if py_package == current_package:
|
114
|
+
return reference_sibling(py_type)
|
115
|
+
|
116
|
+
if py_package[: len(current_package)] == current_package:
|
117
|
+
return reference_descendent(current_package, imports, py_package, py_type)
|
118
|
+
|
119
|
+
if current_package[: len(py_package)] == py_package:
|
120
|
+
return reference_ancestor(current_package, imports, py_package, py_type)
|
121
|
+
|
122
|
+
return reference_cousin(current_package, imports, py_package, py_type)
|
123
|
+
|
124
|
+
|
125
|
+
def reference_absolute(imports: Set[str], py_package: List[str], py_type: str) -> str:
|
126
|
+
"""
|
127
|
+
Returns a reference to a python type located in the root, i.e. sys.path.
|
128
|
+
"""
|
129
|
+
string_import = ".".join(py_package)
|
130
|
+
string_alias = safe_snake_case(string_import)
|
131
|
+
imports.add(f"import {string_import} as {string_alias}")
|
132
|
+
return f"{string_alias}.{py_type}"
|
133
|
+
|
134
|
+
|
135
|
+
def reference_sibling(py_type: str) -> str:
|
136
|
+
"""
|
137
|
+
Returns a reference to a python type within the same package as the current package.
|
138
|
+
"""
|
139
|
+
return f"{py_type}"
|
140
|
+
|
141
|
+
|
142
|
+
def reference_descendent(current_package: List[str], imports: Set[str], py_package: List[str], py_type: str) -> str:
|
143
|
+
"""
|
144
|
+
Returns a reference to a python type in a package that is a descendent of the
|
145
|
+
current package, and adds the required import that is aliased to avoid name
|
146
|
+
conflicts.
|
147
|
+
"""
|
148
|
+
importing_descendent = py_package[len(current_package) :]
|
149
|
+
string_from = ".".join(importing_descendent[:-1])
|
150
|
+
string_import = importing_descendent[-1]
|
151
|
+
if string_from:
|
152
|
+
string_alias = "_".join(importing_descendent)
|
153
|
+
imports.add(f"from .{string_from} import {string_import} as {string_alias}")
|
154
|
+
return f"{string_alias}.{py_type}"
|
155
|
+
else:
|
156
|
+
imports.add(f"from . import {string_import}")
|
157
|
+
return f"{string_import}.{py_type}"
|
158
|
+
|
159
|
+
|
160
|
+
def reference_ancestor(current_package: List[str], imports: Set[str], py_package: List[str], py_type: str) -> str:
|
161
|
+
"""
|
162
|
+
Returns a reference to a python type in a package which is an ancestor to the
|
163
|
+
current package, and adds the required import that is aliased (if possible) to avoid
|
164
|
+
name conflicts.
|
165
|
+
|
166
|
+
Adds trailing __ to avoid name mangling (python.org/dev/peps/pep-0008/#id34).
|
167
|
+
"""
|
168
|
+
distance_up = len(current_package) - len(py_package)
|
169
|
+
if py_package:
|
170
|
+
string_import = py_package[-1]
|
171
|
+
string_alias = f"_{'_' * distance_up}{string_import}__"
|
172
|
+
string_from = f"..{'.' * distance_up}"
|
173
|
+
imports.add(f"from {string_from} import {string_import} as {string_alias}")
|
174
|
+
return f"{string_alias}.{py_type}"
|
175
|
+
else:
|
176
|
+
string_alias = f"{'_' * distance_up}{py_type}__"
|
177
|
+
imports.add(f"from .{'.' * distance_up} import {py_type} as {string_alias}")
|
178
|
+
return string_alias
|
179
|
+
|
180
|
+
|
181
|
+
def reference_cousin(current_package: List[str], imports: Set[str], py_package: List[str], py_type: str) -> str:
|
182
|
+
"""
|
183
|
+
Returns a reference to a python type in a package that is not descendent, ancestor
|
184
|
+
or sibling, and adds the required import that is aliased to avoid name conflicts.
|
185
|
+
"""
|
186
|
+
shared_ancestry = os.path.commonprefix([current_package, py_package]) # type: ignore
|
187
|
+
distance_up = len(current_package) - len(shared_ancestry)
|
188
|
+
string_from = f".{'.' * distance_up}" + ".".join(py_package[len(shared_ancestry) : -1])
|
189
|
+
string_import = py_package[-1]
|
190
|
+
# Add trailing __ to avoid name mangling (python.org/dev/peps/pep-0008/#id34)
|
191
|
+
string_alias = f"{'_' * distance_up}" + safe_snake_case(".".join(py_package[len(shared_ancestry) :])) + "__"
|
192
|
+
imports.add(f"from {string_from} import {string_import} as {string_alias}")
|
193
|
+
return f"{string_alias}.{py_type}"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from betterproto2_compiler import casing
|
2
|
+
|
3
|
+
|
4
|
+
def pythonize_class_name(name: str) -> str:
|
5
|
+
return casing.pascal_case(name)
|
6
|
+
|
7
|
+
|
8
|
+
def pythonize_field_name(name: str) -> str:
|
9
|
+
return casing.safe_snake_case(name)
|
10
|
+
|
11
|
+
|
12
|
+
def pythonize_method_name(name: str) -> str:
|
13
|
+
return casing.safe_snake_case(name)
|
14
|
+
|
15
|
+
|
16
|
+
def pythonize_enum_member_name(name: str, enum_name: str) -> str:
|
17
|
+
enum_name = casing.snake_case(enum_name).upper()
|
18
|
+
find = name.find(enum_name)
|
19
|
+
if find != -1:
|
20
|
+
name = name[find + len(enum_name) :].strip("_")
|
21
|
+
return casing.sanitize_name(name)
|
@@ -0,0 +1,180 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from enum import (
|
4
|
+
EnumMeta,
|
5
|
+
IntEnum,
|
6
|
+
)
|
7
|
+
from types import MappingProxyType
|
8
|
+
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
|
+
Any,
|
11
|
+
Dict,
|
12
|
+
Optional,
|
13
|
+
Tuple,
|
14
|
+
)
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from collections.abc import (
|
18
|
+
Generator,
|
19
|
+
Mapping,
|
20
|
+
)
|
21
|
+
|
22
|
+
from typing_extensions import (
|
23
|
+
Never,
|
24
|
+
Self,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
def _is_descriptor(obj: object) -> bool:
|
29
|
+
return hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__")
|
30
|
+
|
31
|
+
|
32
|
+
class EnumType(EnumMeta if TYPE_CHECKING else type):
|
33
|
+
_value_map_: Mapping[int, Enum]
|
34
|
+
_member_map_: Mapping[str, Enum]
|
35
|
+
|
36
|
+
def __new__(mcs, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> Self:
|
37
|
+
value_map = {}
|
38
|
+
member_map = {}
|
39
|
+
|
40
|
+
new_mcs = type(
|
41
|
+
f"{name}Type",
|
42
|
+
tuple(
|
43
|
+
dict.fromkeys([base.__class__ for base in bases if base.__class__ is not type] + [EnumType, type])
|
44
|
+
), # reorder the bases so EnumType and type are last to avoid conflicts
|
45
|
+
{"_value_map_": value_map, "_member_map_": member_map},
|
46
|
+
)
|
47
|
+
|
48
|
+
members = {
|
49
|
+
name: value for name, value in namespace.items() if not _is_descriptor(value) and not name.startswith("__")
|
50
|
+
}
|
51
|
+
|
52
|
+
cls = type.__new__(
|
53
|
+
new_mcs,
|
54
|
+
name,
|
55
|
+
bases,
|
56
|
+
{key: value for key, value in namespace.items() if key not in members},
|
57
|
+
)
|
58
|
+
# this allows us to disallow member access from other members as
|
59
|
+
# members become proper class variables
|
60
|
+
|
61
|
+
for name, value in members.items():
|
62
|
+
member = value_map.get(value)
|
63
|
+
if member is None:
|
64
|
+
member = cls.__new__(cls, name=name, value=value) # type: ignore
|
65
|
+
value_map[value] = member
|
66
|
+
member_map[name] = member
|
67
|
+
type.__setattr__(new_mcs, name, member)
|
68
|
+
|
69
|
+
return cls
|
70
|
+
|
71
|
+
if not TYPE_CHECKING:
|
72
|
+
|
73
|
+
def __call__(cls, value: int) -> Enum:
|
74
|
+
try:
|
75
|
+
return cls._value_map_[value]
|
76
|
+
except (KeyError, TypeError):
|
77
|
+
raise ValueError(f"{value!r} is not a valid {cls.__name__}") from None
|
78
|
+
|
79
|
+
def __iter__(cls) -> Generator[Enum, None, None]:
|
80
|
+
yield from cls._member_map_.values()
|
81
|
+
|
82
|
+
def __reversed__(cls) -> Generator[Enum, None, None]:
|
83
|
+
yield from reversed(cls._member_map_.values())
|
84
|
+
|
85
|
+
def __getitem__(cls, key: str) -> Enum:
|
86
|
+
return cls._member_map_[key]
|
87
|
+
|
88
|
+
@property
|
89
|
+
def __members__(cls) -> MappingProxyType[str, Enum]:
|
90
|
+
return MappingProxyType(cls._member_map_)
|
91
|
+
|
92
|
+
def __repr__(cls) -> str:
|
93
|
+
return f"<enum {cls.__name__!r}>"
|
94
|
+
|
95
|
+
def __len__(cls) -> int:
|
96
|
+
return len(cls._member_map_)
|
97
|
+
|
98
|
+
def __setattr__(cls, name: str, value: Any) -> Never:
|
99
|
+
raise AttributeError(f"{cls.__name__}: cannot reassign Enum members.")
|
100
|
+
|
101
|
+
def __delattr__(cls, name: str) -> Never:
|
102
|
+
raise AttributeError(f"{cls.__name__}: cannot delete Enum members.")
|
103
|
+
|
104
|
+
def __contains__(cls, member: object) -> bool:
|
105
|
+
return isinstance(member, cls) and member.name in cls._member_map_
|
106
|
+
|
107
|
+
|
108
|
+
class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType):
|
109
|
+
"""
|
110
|
+
The base class for protobuf enumerations, all generated enumerations will
|
111
|
+
inherit from this. Emulates `enum.IntEnum`.
|
112
|
+
"""
|
113
|
+
|
114
|
+
name: Optional[str]
|
115
|
+
value: int
|
116
|
+
|
117
|
+
if not TYPE_CHECKING:
|
118
|
+
|
119
|
+
def __new__(cls, *, name: Optional[str], value: int) -> Self:
|
120
|
+
self = super().__new__(cls, value)
|
121
|
+
super().__setattr__(self, "name", name)
|
122
|
+
super().__setattr__(self, "value", value)
|
123
|
+
return self
|
124
|
+
|
125
|
+
def __str__(self) -> str:
|
126
|
+
return self.name or "None"
|
127
|
+
|
128
|
+
def __repr__(self) -> str:
|
129
|
+
return f"{self.__class__.__name__}.{self.name}"
|
130
|
+
|
131
|
+
def __setattr__(self, key: str, value: Any) -> Never:
|
132
|
+
raise AttributeError(f"{self.__class__.__name__} Cannot reassign a member's attributes.")
|
133
|
+
|
134
|
+
def __delattr__(self, item: Any) -> Never:
|
135
|
+
raise AttributeError(f"{self.__class__.__name__} Cannot delete a member's attributes.")
|
136
|
+
|
137
|
+
def __copy__(self) -> Self:
|
138
|
+
return self
|
139
|
+
|
140
|
+
def __deepcopy__(self, memo: Any) -> Self:
|
141
|
+
return self
|
142
|
+
|
143
|
+
@classmethod
|
144
|
+
def try_value(cls, value: int = 0) -> Self:
|
145
|
+
"""Return the value which corresponds to the value.
|
146
|
+
|
147
|
+
Parameters
|
148
|
+
-----------
|
149
|
+
value: :class:`int`
|
150
|
+
The value of the enum member to get.
|
151
|
+
|
152
|
+
Returns
|
153
|
+
-------
|
154
|
+
:class:`Enum`
|
155
|
+
The corresponding member or a new instance of the enum if
|
156
|
+
``value`` isn't actually a member.
|
157
|
+
"""
|
158
|
+
try:
|
159
|
+
return cls._value_map_[value]
|
160
|
+
except (KeyError, TypeError):
|
161
|
+
return cls.__new__(cls, name=None, value=value)
|
162
|
+
|
163
|
+
@classmethod
|
164
|
+
def from_string(cls, name: str) -> Self:
|
165
|
+
"""Return the value which corresponds to the string name.
|
166
|
+
|
167
|
+
Parameters
|
168
|
+
-----------
|
169
|
+
name: :class:`str`
|
170
|
+
The name of the enum member to get.
|
171
|
+
|
172
|
+
Raises
|
173
|
+
-------
|
174
|
+
:exc:`ValueError`
|
175
|
+
The member was not found in the Enum.
|
176
|
+
"""
|
177
|
+
try:
|
178
|
+
return cls._member_map_[name]
|
179
|
+
except KeyError as e:
|
180
|
+
raise ValueError(f"Unknown value {name} for enum {cls.__name__}") from e
|
File without changes
|