Support nested messages, fix casing. Support test-cases in packages.
This commit is contained in:
parent
d8abb850f8
commit
f7c2fd1194
1
Pipfile
1
Pipfile
@ -15,7 +15,6 @@ rope = "*"
|
|||||||
protobuf = "*"
|
protobuf = "*"
|
||||||
jinja2 = "*"
|
jinja2 = "*"
|
||||||
grpclib = "*"
|
grpclib = "*"
|
||||||
stringcase = "*"
|
|
||||||
black = "*"
|
black = "*"
|
||||||
backports-datetime-fromisoformat = "*"
|
backports-datetime-fromisoformat = "*"
|
||||||
dataclasses = "*"
|
dataclasses = "*"
|
||||||
|
@ -30,7 +30,7 @@ from typing import (
|
|||||||
import grpclib.const
|
import grpclib.const
|
||||||
import stringcase
|
import stringcase
|
||||||
|
|
||||||
from .casing import safe_snake_case
|
from .casing import safe_snake_case, snake_case
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from grpclib._protocols import IProtoMessage
|
from grpclib._protocols import IProtoMessage
|
||||||
@ -132,7 +132,7 @@ class Casing(enum.Enum):
|
|||||||
"""Casing constants for serialization."""
|
"""Casing constants for serialization."""
|
||||||
|
|
||||||
CAMEL = stringcase.camelcase
|
CAMEL = stringcase.camelcase
|
||||||
SNAKE = stringcase.snakecase
|
SNAKE = snake_case
|
||||||
|
|
||||||
|
|
||||||
class _PLACEHOLDER:
|
class _PLACEHOLDER:
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
import stringcase
|
import re
|
||||||
|
|
||||||
|
# Word delimiters and symbols that will not be preserved when re-casing.
|
||||||
|
# language=PythonRegExp
|
||||||
|
SYMBOLS = "[^a-zA-Z0-9]*"
|
||||||
|
|
||||||
|
# Optionally capitalized word.
|
||||||
|
# language=PythonRegExp
|
||||||
|
WORD = "[A-Z]*[a-z]*[0-9]*"
|
||||||
|
|
||||||
|
# Uppercase word, not followed by lowercase letters.
|
||||||
|
# language=PythonRegExp
|
||||||
|
WORD_UPPER = "[A-Z]+(?![a-z])[0-9]*"
|
||||||
|
|
||||||
|
|
||||||
def safe_snake_case(value: str) -> str:
|
def safe_snake_case(value: str) -> str:
|
||||||
"""Snake case a value taking into account Python keywords."""
|
"""Snake case a value taking into account Python keywords."""
|
||||||
value = stringcase.snakecase(value)
|
value = snake_case(value)
|
||||||
if value in [
|
if value in [
|
||||||
"and",
|
"and",
|
||||||
"as",
|
"as",
|
||||||
@ -39,3 +51,33 @@ def safe_snake_case(value: str) -> str:
|
|||||||
# https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles
|
# https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles
|
||||||
value += "_"
|
value += "_"
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def snake_case(value: str):
|
||||||
|
"""
|
||||||
|
Join words with an underscore into lowercase and remove symbols.
|
||||||
|
"""
|
||||||
|
snake = re.sub(
|
||||||
|
f"{SYMBOLS}({WORD_UPPER}|{WORD})", lambda groups: "_" + groups[1].lower(), value
|
||||||
|
)
|
||||||
|
return snake.strip("_")
|
||||||
|
|
||||||
|
|
||||||
|
def pascal_case(value: str):
|
||||||
|
"""
|
||||||
|
Capitalize each word and remove symbols.
|
||||||
|
"""
|
||||||
|
return re.sub(
|
||||||
|
f"{SYMBOLS}({WORD_UPPER}|{WORD})", lambda groups: groups[1].capitalize(), value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def camel_case(value: str):
|
||||||
|
"""
|
||||||
|
Capitalize all words except first and remove symbols.
|
||||||
|
"""
|
||||||
|
return capitalize_first(pascal_case(value))
|
||||||
|
|
||||||
|
|
||||||
|
def capitalize_first(value: str):
|
||||||
|
return value[0:1].lower() + value[1:]
|
||||||
|
@ -1,59 +1,72 @@
|
|||||||
from functools import reduce
|
import re
|
||||||
from typing import Dict, List, Type
|
from typing import Dict, List, Type
|
||||||
|
|
||||||
import stringcase
|
|
||||||
|
|
||||||
from betterproto import safe_snake_case
|
from betterproto import safe_snake_case
|
||||||
|
from betterproto.compile.naming import pythonize_class_name
|
||||||
from betterproto.lib.google import protobuf as google_protobuf
|
from betterproto.lib.google import protobuf as google_protobuf
|
||||||
|
|
||||||
WRAPPER_TYPES: Dict[str, Type] = {
|
WRAPPER_TYPES: Dict[str, Type] = {
|
||||||
"google.protobuf.DoubleValue": google_protobuf.DoubleValue,
|
".google.protobuf.DoubleValue": google_protobuf.DoubleValue,
|
||||||
"google.protobuf.FloatValue": google_protobuf.FloatValue,
|
".google.protobuf.FloatValue": google_protobuf.FloatValue,
|
||||||
"google.protobuf.Int32Value": google_protobuf.Int32Value,
|
".google.protobuf.Int32Value": google_protobuf.Int32Value,
|
||||||
"google.protobuf.Int64Value": google_protobuf.Int64Value,
|
".google.protobuf.Int64Value": google_protobuf.Int64Value,
|
||||||
"google.protobuf.UInt32Value": google_protobuf.UInt32Value,
|
".google.protobuf.UInt32Value": google_protobuf.UInt32Value,
|
||||||
"google.protobuf.UInt64Value": google_protobuf.UInt64Value,
|
".google.protobuf.UInt64Value": google_protobuf.UInt64Value,
|
||||||
"google.protobuf.BoolValue": google_protobuf.BoolValue,
|
".google.protobuf.BoolValue": google_protobuf.BoolValue,
|
||||||
"google.protobuf.StringValue": google_protobuf.StringValue,
|
".google.protobuf.StringValue": google_protobuf.StringValue,
|
||||||
"google.protobuf.BytesValue": google_protobuf.BytesValue,
|
".google.protobuf.BytesValue": google_protobuf.BytesValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_source_type_name(field_type_name):
|
||||||
|
"""
|
||||||
|
Split full source type name into package and type name.
|
||||||
|
E.g. 'root.package.Message' -> ('root.package', 'Message')
|
||||||
|
'root.Message.SomeEnum' -> ('root', 'Message.SomeEnum')
|
||||||
|
"""
|
||||||
|
package_match = re.match(r"^\.?([^A-Z]+)\.(.+)", field_type_name)
|
||||||
|
if package_match:
|
||||||
|
package = package_match.group(1)
|
||||||
|
name = package_match.group(2)
|
||||||
|
else:
|
||||||
|
package = ""
|
||||||
|
name = field_type_name.lstrip(".")
|
||||||
|
return package, name
|
||||||
|
|
||||||
|
|
||||||
def get_ref_type(
|
def get_ref_type(
|
||||||
package: str, imports: set, type_name: str, unwrap: bool = True
|
package: str, imports: set, source_type: str, unwrap: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Return a Python type name for a proto type reference. Adds the import if
|
Return a Python type name for a proto type reference. Adds the import if
|
||||||
necessary. Unwraps well known type if required.
|
necessary. Unwraps well known type if required.
|
||||||
"""
|
"""
|
||||||
# If the package name is a blank string, then this should still work
|
is_wrapper = source_type in WRAPPER_TYPES
|
||||||
# because by convention packages are lowercase and message/enum types are
|
|
||||||
# pascal-cased. May require refactoring in the future.
|
|
||||||
type_name = type_name.lstrip(".")
|
|
||||||
|
|
||||||
is_wrapper = type_name in WRAPPER_TYPES
|
|
||||||
|
|
||||||
if unwrap:
|
if unwrap:
|
||||||
if is_wrapper:
|
if is_wrapper:
|
||||||
wrapped_type = type(WRAPPER_TYPES[type_name]().value)
|
wrapped_type = type(WRAPPER_TYPES[source_type]().value)
|
||||||
return f"Optional[{wrapped_type.__name__}]"
|
return f"Optional[{wrapped_type.__name__}]"
|
||||||
|
|
||||||
if type_name == "google.protobuf.Duration":
|
if source_type == ".google.protobuf.Duration":
|
||||||
return "timedelta"
|
return "timedelta"
|
||||||
|
|
||||||
if type_name == "google.protobuf.Timestamp":
|
if source_type == ".google.protobuf.Timestamp":
|
||||||
return "datetime"
|
return "datetime"
|
||||||
|
|
||||||
# Use precompiled classes for google.protobuf.* objects
|
source_package, source_type = parse_source_type_name(source_type)
|
||||||
if type_name.startswith("google.protobuf.") and type_name.count(".") == 2:
|
|
||||||
type_name = type_name.rsplit(".", maxsplit=1)[1]
|
# Use precompiled classes for google.protobuf.* objects
|
||||||
import_package = "betterproto.lib.google.protobuf"
|
if source_package == "google.protobuf":
|
||||||
import_alias = safe_snake_case(import_package)
|
string_import = f"betterproto.lib.{source_package}"
|
||||||
imports.add(f"import {import_package} as {import_alias}")
|
py_type = source_type
|
||||||
return f"{import_alias}.{type_name}"
|
string_alias = safe_snake_case(string_import)
|
||||||
|
imports.add(f"import {string_import} as {string_alias}")
|
||||||
|
return f"{string_alias}.{py_type}"
|
||||||
|
|
||||||
|
py_package: List[str] = source_package.split(".") if source_package else []
|
||||||
|
py_type: str = pythonize_class_name(source_type)
|
||||||
|
|
||||||
importing_package: List[str] = type_name.split(".")
|
|
||||||
importing_type: str = stringcase.pascalcase(importing_package.pop())
|
|
||||||
current_package: List[str] = package.split(".") if package else []
|
current_package: List[str] = package.split(".") if package else []
|
||||||
|
|
||||||
# importing sibling
|
# importing sibling
|
||||||
@ -67,9 +80,8 @@ def get_ref_type(
|
|||||||
package = foo.bar
|
package = foo.bar
|
||||||
name = foo.bar.Baz
|
name = foo.bar.Baz
|
||||||
"""
|
"""
|
||||||
if importing_package == current_package:
|
if py_package == current_package:
|
||||||
imports.add(f"from . import {importing_type}")
|
return f'"{py_type}"'
|
||||||
return importing_type
|
|
||||||
|
|
||||||
# importing child & descendent:
|
# importing child & descendent:
|
||||||
"""
|
"""
|
||||||
@ -79,18 +91,18 @@ def get_ref_type(
|
|||||||
package =
|
package =
|
||||||
name = foo.bar.Baz
|
name = foo.bar.Baz
|
||||||
"""
|
"""
|
||||||
if importing_package[0 : len(current_package)] == current_package:
|
if py_package[0 : len(current_package)] == current_package:
|
||||||
importing_descendent = importing_package[len(current_package) :]
|
importing_descendent = py_package[len(current_package) :]
|
||||||
string_from = ".".join(importing_descendent[0:-1])
|
string_from = ".".join(importing_descendent[0:-1])
|
||||||
string_import = importing_descendent[-1]
|
string_import = importing_descendent[-1]
|
||||||
|
|
||||||
if string_from:
|
if string_from:
|
||||||
string_alias = "_".join(importing_descendent)
|
string_alias = "_".join(importing_descendent)
|
||||||
imports.add(f"from .{string_from} import {string_import} as {string_alias}")
|
imports.add(f"from .{string_from} import {string_import} as {string_alias}")
|
||||||
return f"{string_alias}.{importing_type}"
|
return f"{string_alias}.{py_type}"
|
||||||
else:
|
else:
|
||||||
imports.add(f"from . import {string_import}")
|
imports.add(f"from . import {string_import}")
|
||||||
return f"{string_import}.{importing_type}"
|
return f"{string_import}.{py_type}"
|
||||||
|
|
||||||
# importing parent & ancestor
|
# importing parent & ancestor
|
||||||
"""
|
"""
|
||||||
@ -103,10 +115,10 @@ def get_ref_type(
|
|||||||
package = foo.bar.baz
|
package = foo.bar.baz
|
||||||
name = Bar
|
name = Bar
|
||||||
"""
|
"""
|
||||||
if current_package[0 : len(importing_package)] == importing_package:
|
if current_package[0 : len(py_package)] == py_package:
|
||||||
distance_up = len(current_package) - len(importing_package)
|
distance_up = len(current_package) - len(py_package)
|
||||||
imports.add(f"from .{'.' * distance_up} import {importing_type}")
|
imports.add(f"from .{'.' * distance_up} import {py_type}")
|
||||||
return importing_type
|
return py_type
|
||||||
|
|
||||||
# importing unrelated or cousin
|
# importing unrelated or cousin
|
||||||
"""
|
"""
|
||||||
@ -116,20 +128,16 @@ def get_ref_type(
|
|||||||
package = foo.bar.baz
|
package = foo.bar.baz
|
||||||
name = foo.example.Bar
|
name = foo.example.Bar
|
||||||
"""
|
"""
|
||||||
|
|
||||||
shared_ancestory = [
|
shared_ancestory = [
|
||||||
pair[0]
|
pair[0] for pair in zip(current_package, py_package) if pair[0] == pair[1]
|
||||||
for pair in zip(current_package, importing_package)
|
|
||||||
if pair[0] == pair[1]
|
|
||||||
]
|
]
|
||||||
distance_up = len(current_package) - len(shared_ancestory)
|
distance_up = len(current_package) - len(shared_ancestory)
|
||||||
|
|
||||||
string_from = f".{'.' * distance_up}" + ".".join(
|
string_from = f".{'.' * distance_up}" + ".".join(
|
||||||
importing_package[len(shared_ancestory) : -1]
|
py_package[len(shared_ancestory) : -1]
|
||||||
)
|
)
|
||||||
string_import = importing_package[-1]
|
string_import = py_package[-1]
|
||||||
string_alias = f"{'_' * distance_up}" + safe_snake_case(
|
alias = f"{'_' * distance_up}" + safe_snake_case(
|
||||||
".".join(importing_package[len(shared_ancestory) :])
|
".".join(py_package[len(shared_ancestory) :])
|
||||||
)
|
)
|
||||||
imports.add(f"from {string_from} import {string_import} as {string_alias}")
|
imports.add(f"from {string_from} import {string_import} as {alias}")
|
||||||
return f"{string_alias}.{importing_type}"
|
return f"{alias}.{py_type}"
|
||||||
|
13
betterproto/compile/naming.py
Normal file
13
betterproto/compile/naming.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from betterproto import casing
|
||||||
|
|
||||||
|
|
||||||
|
def pythonize_class_name(name):
|
||||||
|
return casing.pascal_case(name)
|
||||||
|
|
||||||
|
|
||||||
|
def pythonize_field_name(name: str):
|
||||||
|
return casing.safe_snake_case(name)
|
||||||
|
|
||||||
|
|
||||||
|
def pythonize_method_name(name: str):
|
||||||
|
return casing.safe_snake_case(name)
|
@ -10,6 +10,11 @@ from typing import List
|
|||||||
from betterproto.casing import safe_snake_case
|
from betterproto.casing import safe_snake_case
|
||||||
from betterproto.compile.importing import get_ref_type
|
from betterproto.compile.importing import get_ref_type
|
||||||
import betterproto
|
import betterproto
|
||||||
|
from betterproto.compile.naming import (
|
||||||
|
pythonize_class_name,
|
||||||
|
pythonize_field_name,
|
||||||
|
pythonize_method_name,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# betterproto[compiler] specific dependencies
|
# betterproto[compiler] specific dependencies
|
||||||
@ -35,27 +40,22 @@ except ImportError as err:
|
|||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
def py_type(
|
def py_type(package: str, imports: set, field: FieldDescriptorProto) -> str:
|
||||||
package: str,
|
if field.type in [1, 2, 6, 7, 15, 16]:
|
||||||
imports: set,
|
|
||||||
message: DescriptorProto,
|
|
||||||
descriptor: FieldDescriptorProto,
|
|
||||||
) -> str:
|
|
||||||
if descriptor.type in [1, 2, 6, 7, 15, 16]:
|
|
||||||
return "float"
|
return "float"
|
||||||
elif descriptor.type in [3, 4, 5, 13, 17, 18]:
|
elif field.type in [3, 4, 5, 13, 17, 18]:
|
||||||
return "int"
|
return "int"
|
||||||
elif descriptor.type == 8:
|
elif field.type == 8:
|
||||||
return "bool"
|
return "bool"
|
||||||
elif descriptor.type == 9:
|
elif field.type == 9:
|
||||||
return "str"
|
return "str"
|
||||||
elif descriptor.type in [11, 14]:
|
elif field.type in [11, 14]:
|
||||||
# Type referencing another defined Message or a named enum
|
# Type referencing another defined Message or a named enum
|
||||||
return get_ref_type(package, imports, descriptor.type_name)
|
return get_ref_type(package, imports, field.type_name)
|
||||||
elif descriptor.type == 12:
|
elif field.type == 12:
|
||||||
return "bytes"
|
return "bytes"
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Unknown type {descriptor.type}")
|
raise NotImplementedError(f"Unknown type {field.type}")
|
||||||
|
|
||||||
|
|
||||||
def get_py_zero(type_num: int) -> str:
|
def get_py_zero(type_num: int) -> str:
|
||||||
@ -160,17 +160,10 @@ def generate_code(request, response):
|
|||||||
"services": [],
|
"services": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
type_mapping = {}
|
|
||||||
|
|
||||||
for proto_file in options["files"]:
|
for proto_file in options["files"]:
|
||||||
# print(proto_file.message_type, file=sys.stderr)
|
item: DescriptorProto
|
||||||
# print(proto_file.service, file=sys.stderr)
|
|
||||||
# print(proto_file.source_code_info, file=sys.stderr)
|
|
||||||
|
|
||||||
for item, path in traverse(proto_file):
|
for item, path in traverse(proto_file):
|
||||||
# print(item, file=sys.stderr)
|
data = {"name": item.name, "py_name": pythonize_class_name(item.name)}
|
||||||
# print(path, file=sys.stderr)
|
|
||||||
data = {"name": item.name, "py_name": stringcase.pascalcase(item.name)}
|
|
||||||
|
|
||||||
if isinstance(item, DescriptorProto):
|
if isinstance(item, DescriptorProto):
|
||||||
# print(item, file=sys.stderr)
|
# print(item, file=sys.stderr)
|
||||||
@ -187,7 +180,7 @@ def generate_code(request, response):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for i, f in enumerate(item.field):
|
for i, f in enumerate(item.field):
|
||||||
t = py_type(package, output["imports"], item, f)
|
t = py_type(package, output["imports"], f)
|
||||||
zero = get_py_zero(f.type)
|
zero = get_py_zero(f.type)
|
||||||
|
|
||||||
repeated = False
|
repeated = False
|
||||||
@ -222,13 +215,11 @@ def generate_code(request, response):
|
|||||||
k = py_type(
|
k = py_type(
|
||||||
package,
|
package,
|
||||||
output["imports"],
|
output["imports"],
|
||||||
item,
|
|
||||||
nested.field[0],
|
nested.field[0],
|
||||||
)
|
)
|
||||||
v = py_type(
|
v = py_type(
|
||||||
package,
|
package,
|
||||||
output["imports"],
|
output["imports"],
|
||||||
item,
|
|
||||||
nested.field[1],
|
nested.field[1],
|
||||||
)
|
)
|
||||||
t = f"Dict[{k}, {v}]"
|
t = f"Dict[{k}, {v}]"
|
||||||
@ -264,7 +255,7 @@ def generate_code(request, response):
|
|||||||
data["properties"].append(
|
data["properties"].append(
|
||||||
{
|
{
|
||||||
"name": f.name,
|
"name": f.name,
|
||||||
"py_name": safe_snake_case(f.name),
|
"py_name": pythonize_field_name(f.name),
|
||||||
"number": f.number,
|
"number": f.number,
|
||||||
"comment": get_comment(proto_file, path + [2, i]),
|
"comment": get_comment(proto_file, path + [2, i]),
|
||||||
"proto_type": int(f.type),
|
"proto_type": int(f.type),
|
||||||
@ -305,7 +296,7 @@ def generate_code(request, response):
|
|||||||
|
|
||||||
data = {
|
data = {
|
||||||
"name": service.name,
|
"name": service.name,
|
||||||
"py_name": stringcase.pascalcase(service.name),
|
"py_name": pythonize_class_name(service.name),
|
||||||
"comment": get_comment(proto_file, [6, i]),
|
"comment": get_comment(proto_file, [6, i]),
|
||||||
"methods": [],
|
"methods": [],
|
||||||
}
|
}
|
||||||
@ -329,7 +320,7 @@ def generate_code(request, response):
|
|||||||
data["methods"].append(
|
data["methods"].append(
|
||||||
{
|
{
|
||||||
"name": method.name,
|
"name": method.name,
|
||||||
"py_name": stringcase.snakecase(method.name),
|
"py_name": pythonize_method_name(method.name),
|
||||||
"comment": get_comment(proto_file, [6, i, 2, j], indent=8),
|
"comment": get_comment(proto_file, [6, i, 2, j], indent=8),
|
||||||
"route": f"/{package}.{service.name}/{method.name}",
|
"route": f"/{package}.{service.name}/{method.name}",
|
||||||
"input": get_ref_type(
|
"input": get_ref_type(
|
||||||
|
@ -10,6 +10,7 @@ message Test {
|
|||||||
int32 camelCase = 1;
|
int32 camelCase = 1;
|
||||||
my_enum snake_case = 2;
|
my_enum snake_case = 2;
|
||||||
snake_case_message snake_case_message = 3;
|
snake_case_message snake_case_message = 3;
|
||||||
|
int32 UPPERCASE = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message snake_case_message {
|
message snake_case_message {
|
||||||
|
@ -8,6 +8,7 @@ def test_message_attributes():
|
|||||||
message, "snake_case_message"
|
message, "snake_case_message"
|
||||||
), "snake_case field name is same in python"
|
), "snake_case field name is same in python"
|
||||||
assert hasattr(message, "camel_case"), "CamelCase field is snake_case in python"
|
assert hasattr(message, "camel_case"), "CamelCase field is snake_case in python"
|
||||||
|
assert hasattr(message, "uppercase"), "UPPERCASE field is lowercase in python"
|
||||||
|
|
||||||
|
|
||||||
def test_message_casing():
|
def test_message_casing():
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
# Test cases that are expected to fail, e.g. unimplemented features or bug-fixes.
|
# Test cases that are expected to fail, e.g. unimplemented features or bug-fixes.
|
||||||
# Remove from list when fixed.
|
# Remove from list when fixed.
|
||||||
tests = {
|
tests = {
|
||||||
"import_root_sibling", # 61
|
"import_circular_dependency",
|
||||||
"import_child_package_from_package", # 58
|
|
||||||
"import_root_package_from_child", # 60
|
|
||||||
"import_parent_package_from_child", # 59
|
|
||||||
"import_circular_dependency", # failing because of other bugs now
|
|
||||||
"import_packages_same_name", # 25
|
|
||||||
"oneof_enum", # 63
|
"oneof_enum", # 63
|
||||||
"casing_message_field_uppercase", # 11
|
"casing_message_field_uppercase", # 11
|
||||||
"namespace_keywords", # 70
|
"namespace_keywords", # 70
|
||||||
@ -15,6 +10,16 @@ tests = {
|
|||||||
"googletypes_value", # 9
|
"googletypes_value", # 9
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Defines where the main package for this test resides.
|
||||||
|
# Needed to test relative package imports.
|
||||||
|
packages = {
|
||||||
|
"import_root_package_from_child": ".child",
|
||||||
|
"import_parent_package_from_child": ".parent.child",
|
||||||
|
"repeatedmessage": ".repeatedmessage",
|
||||||
|
"service": ".service",
|
||||||
|
}
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
"googletypes_response",
|
"googletypes_response",
|
||||||
"googletypes_response_embedded",
|
"googletypes_response_embedded",
|
||||||
|
@ -3,9 +3,9 @@ syntax = "proto3";
|
|||||||
import "root.proto";
|
import "root.proto";
|
||||||
import "other.proto";
|
import "other.proto";
|
||||||
|
|
||||||
// This test-case verifies that future implementations will support circular dependencies in the generated python files.
|
// This test-case verifies support for circular dependencies in the generated python files.
|
||||||
//
|
//
|
||||||
// This becomes important when generating 1 python file/module per package, rather than 1 file per proto file.
|
// This is important because we generate 1 python file/module per package, rather than 1 file per proto file.
|
||||||
//
|
//
|
||||||
// Scenario:
|
// Scenario:
|
||||||
//
|
//
|
||||||
@ -24,5 +24,5 @@ import "other.proto";
|
|||||||
// (root: Test & RootPackageMessage) <-------> (other: OtherPackageMessage)
|
// (root: Test & RootPackageMessage) <-------> (other: OtherPackageMessage)
|
||||||
message Test {
|
message Test {
|
||||||
RootPackageMessage message = 1;
|
RootPackageMessage message = 1;
|
||||||
other.OtherPackageMessage other =2;
|
other.OtherPackageMessage other = 2;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package child;
|
||||||
|
|
||||||
|
import "root.proto";
|
||||||
|
|
||||||
|
// Verify that we can import root message from child package
|
||||||
|
|
||||||
|
message Test {
|
||||||
|
RootMessage message = 1;
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
import "root.proto";
|
|
||||||
|
|
||||||
package child;
|
|
||||||
|
|
||||||
// Tests generated imports when a message inside a child-package refers to a message defined in the root.
|
|
||||||
|
|
||||||
message Test {
|
|
||||||
RootMessage message = 1;
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ message Test {
|
|||||||
|
|
||||||
Nested nested = 1;
|
Nested nested = 1;
|
||||||
Sibling sibling = 2;
|
Sibling sibling = 2;
|
||||||
Sibling sibling2 = 3;
|
// Sibling sibling2 = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Sibling {
|
message Sibling {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"root": {
|
"top": {
|
||||||
"name": "double-nested",
|
"name": "double-nested",
|
||||||
"parent": {
|
"middle": {
|
||||||
"child": [{"foo": "hello"}],
|
"bottom": [{"foo": "hello"}],
|
||||||
"enumChild": ["A"],
|
"enumBottom": ["A"],
|
||||||
"rootParentChild": [{"a": "hello"}],
|
"topMiddleBottom": [{"a": "hello"}],
|
||||||
"bar": true
|
"bar": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
message Test {
|
message Test {
|
||||||
message Root {
|
message Top {
|
||||||
message Parent {
|
message Middle {
|
||||||
message RootParentChild {
|
message TopMiddleBottom {
|
||||||
string a = 1;
|
string a = 1;
|
||||||
}
|
}
|
||||||
enum EnumChild{
|
enum EnumBottom{
|
||||||
A = 0;
|
A = 0;
|
||||||
B = 1;
|
B = 1;
|
||||||
}
|
}
|
||||||
message Child {
|
message Bottom {
|
||||||
string foo = 1;
|
string foo = 1;
|
||||||
}
|
}
|
||||||
reserved 1;
|
reserved 1;
|
||||||
repeated Child child = 2;
|
repeated Bottom bottom = 2;
|
||||||
repeated EnumChild enumChild=3;
|
repeated EnumBottom enumBottom=3;
|
||||||
repeated RootParentChild rootParentChild=4;
|
repeated TopMiddleBottom topMiddleBottom=4;
|
||||||
bool bar = 5;
|
bool bar = 5;
|
||||||
}
|
}
|
||||||
string name = 1;
|
string name = 1;
|
||||||
Parent parent = 2;
|
Middle middle = 2;
|
||||||
}
|
}
|
||||||
Root root = 1;
|
Top top = 1;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package ref;
|
|
||||||
|
|
||||||
import "repeatedmessage.proto";
|
import "repeatedmessage.proto";
|
||||||
|
|
||||||
message Test {
|
message Test {
|
||||||
|
89
betterproto/tests/test_casing.py
Normal file
89
betterproto/tests/test_casing.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from betterproto.casing import camel_case, pascal_case, snake_case
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
["value", "expected"],
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("a", "A"),
|
||||||
|
("foobar", "Foobar"),
|
||||||
|
("FooBar", "FooBar"),
|
||||||
|
("foo.bar", "FooBar"),
|
||||||
|
("foo_bar", "FooBar"),
|
||||||
|
("FOOBAR", "Foobar"),
|
||||||
|
("FOOBar", "FooBar"),
|
||||||
|
("UInt32", "UInt32"),
|
||||||
|
("FOO_BAR", "FooBar"),
|
||||||
|
("FOOBAR1", "Foobar1"),
|
||||||
|
("FOOBAR_1", "Foobar1"),
|
||||||
|
("FOO1BAR2", "Foo1Bar2"),
|
||||||
|
("foo__bar", "FooBar"),
|
||||||
|
("_foobar", "Foobar"),
|
||||||
|
("foobaR", "FoobaR"),
|
||||||
|
("foo~bar", "FooBar"),
|
||||||
|
("foo:bar", "FooBar"),
|
||||||
|
("1foobar", "1Foobar"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_pascal_case(value, expected):
|
||||||
|
actual = pascal_case(value)
|
||||||
|
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
["value", "expected"],
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("a", "a"),
|
||||||
|
("foobar", "foobar"),
|
||||||
|
("FooBar", "fooBar"),
|
||||||
|
("foo.bar", "fooBar"),
|
||||||
|
("foo_bar", "fooBar"),
|
||||||
|
("FOOBAR", "foobar"),
|
||||||
|
("FOO_BAR", "fooBar"),
|
||||||
|
("FOOBAR1", "foobar1"),
|
||||||
|
("FOOBAR_1", "foobar1"),
|
||||||
|
("FOO1BAR2", "foo1Bar2"),
|
||||||
|
("foo__bar", "fooBar"),
|
||||||
|
("_foobar", "foobar"),
|
||||||
|
("foobaR", "foobaR"),
|
||||||
|
("foo~bar", "fooBar"),
|
||||||
|
("foo:bar", "fooBar"),
|
||||||
|
("1foobar", "1Foobar"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_camel_case(value, expected):
|
||||||
|
actual = camel_case(value)
|
||||||
|
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
["value", "expected"],
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("a", "a"),
|
||||||
|
("foobar", "foobar"),
|
||||||
|
("FooBar", "foo_bar"),
|
||||||
|
("foo.bar", "foo_bar"),
|
||||||
|
("foo_bar", "foo_bar"),
|
||||||
|
("FOOBAR", "foobar"),
|
||||||
|
("FOOBar", "foo_bar"),
|
||||||
|
("UInt32", "u_int32"),
|
||||||
|
("FOO_BAR", "foo_bar"),
|
||||||
|
("FOOBAR1", "foobar1"),
|
||||||
|
("FOOBAR_1", "foobar_1"),
|
||||||
|
("FOOBAR_123", "foobar_123"),
|
||||||
|
("FOO1BAR2", "foo1_bar2"),
|
||||||
|
("foo__bar", "foo_bar"),
|
||||||
|
("_foobar", "foobar"),
|
||||||
|
("foobaR", "fooba_r"),
|
||||||
|
("foo~bar", "foo_bar"),
|
||||||
|
("foo:bar", "foo_bar"),
|
||||||
|
("1foobar", "1_foobar"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_snake_case(value, expected):
|
||||||
|
actual = snake_case(value)
|
||||||
|
assert actual == expected, f"{value} => {expected} (actual: {actual})"
|
@ -1,9 +1,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..compile.importing import get_ref_type
|
from ..compile.importing import get_ref_type, parse_source_type_name
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
["google_type", "expected_name", "expected_import"],
|
["google_type", "expected_name", "expected_import"],
|
||||||
[
|
[
|
||||||
@ -33,13 +32,14 @@ def test_import_google_wellknown_types_non_wrappers(
|
|||||||
google_type: str, expected_name: str, expected_import: str
|
google_type: str, expected_name: str, expected_import: str
|
||||||
):
|
):
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="", imports=imports, type_name=google_type)
|
name = get_ref_type(package="", imports=imports, source_type=google_type)
|
||||||
|
|
||||||
assert name == expected_name
|
assert name == expected_name
|
||||||
assert imports.__contains__(expected_import)
|
assert imports.__contains__(
|
||||||
|
expected_import
|
||||||
|
), f"{expected_import} not found in {imports}"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
["google_type", "expected_name"],
|
["google_type", "expected_name"],
|
||||||
[
|
[
|
||||||
@ -56,13 +56,12 @@ def test_import_google_wellknown_types_non_wrappers(
|
|||||||
)
|
)
|
||||||
def test_importing_google_wrappers_unwraps_them(google_type: str, expected_name: str):
|
def test_importing_google_wrappers_unwraps_them(google_type: str, expected_name: str):
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="", imports=imports, type_name=google_type)
|
name = get_ref_type(package="", imports=imports, source_type=google_type)
|
||||||
|
|
||||||
assert name == expected_name
|
assert name == expected_name
|
||||||
assert imports == set()
|
assert imports == set()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
["google_type", "expected_name"],
|
["google_type", "expected_name"],
|
||||||
[
|
[
|
||||||
@ -80,7 +79,9 @@ def test_importing_google_wrappers_unwraps_them(google_type: str, expected_name:
|
|||||||
def test_importing_google_wrappers_without_unwrapping(
|
def test_importing_google_wrappers_without_unwrapping(
|
||||||
google_type: str, expected_name: str
|
google_type: str, expected_name: str
|
||||||
):
|
):
|
||||||
name = get_ref_type(package="", imports=set(), type_name=google_type, unwrap=False)
|
name = get_ref_type(
|
||||||
|
package="", imports=set(), source_type=google_type, unwrap=False
|
||||||
|
)
|
||||||
|
|
||||||
assert name == expected_name
|
assert name == expected_name
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ def test_importing_google_wrappers_without_unwrapping(
|
|||||||
def test_import_child_package_from_package():
|
def test_import_child_package_from_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="package", imports=imports, type_name="package.child.Message"
|
package="package", imports=imports, source_type="package.child.Message"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from . import child"}
|
assert imports == {"from . import child"}
|
||||||
@ -97,7 +98,7 @@ def test_import_child_package_from_package():
|
|||||||
|
|
||||||
def test_import_child_package_from_root():
|
def test_import_child_package_from_root():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="", imports=imports, type_name="child.Message")
|
name = get_ref_type(package="", imports=imports, source_type="child.Message")
|
||||||
|
|
||||||
assert imports == {"from . import child"}
|
assert imports == {"from . import child"}
|
||||||
assert name == "child.Message"
|
assert name == "child.Message"
|
||||||
@ -106,7 +107,7 @@ def test_import_child_package_from_root():
|
|||||||
def test_import_camel_cased():
|
def test_import_camel_cased():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="", imports=imports, type_name="child_package.example_message"
|
package="", imports=imports, source_type="child_package.example_message"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from . import child_package"}
|
assert imports == {"from . import child_package"}
|
||||||
@ -115,7 +116,7 @@ def test_import_camel_cased():
|
|||||||
|
|
||||||
def test_import_nested_child_from_root():
|
def test_import_nested_child_from_root():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="", imports=imports, type_name="nested.child.Message")
|
name = get_ref_type(package="", imports=imports, source_type="nested.child.Message")
|
||||||
|
|
||||||
assert imports == {"from .nested import child as nested_child"}
|
assert imports == {"from .nested import child as nested_child"}
|
||||||
assert name == "nested_child.Message"
|
assert name == "nested_child.Message"
|
||||||
@ -124,7 +125,7 @@ def test_import_nested_child_from_root():
|
|||||||
def test_import_deeply_nested_child_from_root():
|
def test_import_deeply_nested_child_from_root():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="", imports=imports, type_name="deeply.nested.child.Message"
|
package="", imports=imports, source_type="deeply.nested.child.Message"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
||||||
@ -136,7 +137,7 @@ def test_import_deeply_nested_child_from_package():
|
|||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="package",
|
package="package",
|
||||||
imports=imports,
|
imports=imports,
|
||||||
type_name="package.deeply.nested.child.Message",
|
source_type="package.deeply.nested.child.Message",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
assert imports == {"from .deeply.nested import child as deeply_nested_child"}
|
||||||
@ -145,32 +146,32 @@ def test_import_deeply_nested_child_from_package():
|
|||||||
|
|
||||||
def test_import_root_sibling():
|
def test_import_root_sibling():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="", imports=imports, type_name="Message")
|
name = get_ref_type(package="", imports=imports, source_type="Message")
|
||||||
|
|
||||||
assert imports == {"from . import Message"}
|
assert imports == set()
|
||||||
assert name == "Message"
|
assert name == '"Message"'
|
||||||
|
|
||||||
|
|
||||||
def test_import_nested_siblings():
|
def test_import_nested_siblings():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="foo", imports=imports, type_name="foo.Message")
|
name = get_ref_type(package="foo", imports=imports, source_type="foo.Message")
|
||||||
|
|
||||||
assert imports == {"from . import Message"}
|
assert name == '"Message"'
|
||||||
assert name == "Message"
|
|
||||||
|
|
||||||
|
|
||||||
def test_import_deeply_nested_siblings():
|
def test_import_deeply_nested_siblings():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="foo.bar", imports=imports, type_name="foo.bar.Message")
|
name = get_ref_type(
|
||||||
|
package="foo.bar", imports=imports, source_type="foo.bar.Message"
|
||||||
|
)
|
||||||
|
|
||||||
assert imports == {"from . import Message"}
|
assert name == '"Message"'
|
||||||
assert name == "Message"
|
|
||||||
|
|
||||||
|
|
||||||
def test_import_parent_package_from_child():
|
def test_import_parent_package_from_child():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="package.child", imports=imports, type_name="package.Message"
|
package="package.child", imports=imports, source_type="package.Message"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from .. import Message"}
|
assert imports == {"from .. import Message"}
|
||||||
@ -182,7 +183,7 @@ def test_import_parent_package_from_deeply_nested_child():
|
|||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="package.deeply.nested.child",
|
package="package.deeply.nested.child",
|
||||||
imports=imports,
|
imports=imports,
|
||||||
type_name="package.deeply.nested.Message",
|
source_type="package.deeply.nested.Message",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from .. import Message"}
|
assert imports == {"from .. import Message"}
|
||||||
@ -191,7 +192,7 @@ def test_import_parent_package_from_deeply_nested_child():
|
|||||||
|
|
||||||
def test_import_root_package_from_child():
|
def test_import_root_package_from_child():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="package.child", imports=imports, type_name="Message")
|
name = get_ref_type(package="package.child", imports=imports, source_type="Message")
|
||||||
|
|
||||||
assert imports == {"from ... import Message"}
|
assert imports == {"from ... import Message"}
|
||||||
assert name == "Message"
|
assert name == "Message"
|
||||||
@ -200,7 +201,7 @@ def test_import_root_package_from_child():
|
|||||||
def test_import_root_package_from_deeply_nested_child():
|
def test_import_root_package_from_deeply_nested_child():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(
|
name = get_ref_type(
|
||||||
package="package.deeply.nested.child", imports=imports, type_name="Message"
|
package="package.deeply.nested.child", imports=imports, source_type="Message"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert imports == {"from ..... import Message"}
|
assert imports == {"from ..... import Message"}
|
||||||
@ -209,7 +210,7 @@ def test_import_root_package_from_deeply_nested_child():
|
|||||||
|
|
||||||
def test_import_unrelated_package():
|
def test_import_unrelated_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a", imports=imports, type_name="p.Message")
|
name = get_ref_type(package="a", imports=imports, source_type="p.Message")
|
||||||
|
|
||||||
assert imports == {"from .. import p as _p"}
|
assert imports == {"from .. import p as _p"}
|
||||||
assert name == "_p.Message"
|
assert name == "_p.Message"
|
||||||
@ -217,7 +218,7 @@ def test_import_unrelated_package():
|
|||||||
|
|
||||||
def test_import_unrelated_nested_package():
|
def test_import_unrelated_nested_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a.b", imports=imports, type_name="p.q.Message")
|
name = get_ref_type(package="a.b", imports=imports, source_type="p.q.Message")
|
||||||
|
|
||||||
assert imports == {"from ...p import q as __p_q"}
|
assert imports == {"from ...p import q as __p_q"}
|
||||||
assert name == "__p_q.Message"
|
assert name == "__p_q.Message"
|
||||||
@ -225,7 +226,9 @@ def test_import_unrelated_nested_package():
|
|||||||
|
|
||||||
def test_import_unrelated_deeply_nested_package():
|
def test_import_unrelated_deeply_nested_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a.b.c.d", imports=imports, type_name="p.q.r.s.Message")
|
name = get_ref_type(
|
||||||
|
package="a.b.c.d", imports=imports, source_type="p.q.r.s.Message"
|
||||||
|
)
|
||||||
|
|
||||||
assert imports == {"from .....p.q.r import s as ____p_q_r_s"}
|
assert imports == {"from .....p.q.r import s as ____p_q_r_s"}
|
||||||
assert name == "____p_q_r_s.Message"
|
assert name == "____p_q_r_s.Message"
|
||||||
@ -233,7 +236,7 @@ def test_import_unrelated_deeply_nested_package():
|
|||||||
|
|
||||||
def test_import_cousin_package():
|
def test_import_cousin_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a.x", imports=imports, type_name="a.y.Message")
|
name = get_ref_type(package="a.x", imports=imports, source_type="a.y.Message")
|
||||||
|
|
||||||
assert imports == {"from .. import y as _y"}
|
assert imports == {"from .. import y as _y"}
|
||||||
assert name == "_y.Message"
|
assert name == "_y.Message"
|
||||||
@ -241,7 +244,7 @@ def test_import_cousin_package():
|
|||||||
|
|
||||||
def test_import_far_cousin_package():
|
def test_import_far_cousin_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a.x.y", imports=imports, type_name="a.b.c.Message")
|
name = get_ref_type(package="a.x.y", imports=imports, source_type="a.b.c.Message")
|
||||||
|
|
||||||
assert imports == {"from ...b import c as __b_c"}
|
assert imports == {"from ...b import c as __b_c"}
|
||||||
assert name == "__b_c.Message"
|
assert name == "__b_c.Message"
|
||||||
@ -249,7 +252,22 @@ def test_import_far_cousin_package():
|
|||||||
|
|
||||||
def test_import_far_far_cousin_package():
|
def test_import_far_far_cousin_package():
|
||||||
imports = set()
|
imports = set()
|
||||||
name = get_ref_type(package="a.x.y.z", imports=imports, type_name="a.b.c.d.Message")
|
name = get_ref_type(
|
||||||
|
package="a.x.y.z", imports=imports, source_type="a.b.c.d.Message"
|
||||||
|
)
|
||||||
|
|
||||||
assert imports == {"from ....b.c import d as ___b_c_d"}
|
assert imports == {"from ....b.c import d as ___b_c_d"}
|
||||||
assert name == "___b_c_d.Message"
|
assert name == "___b_c_d.Message"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
["full_name", "expected_output"],
|
||||||
|
[
|
||||||
|
("package.SomeMessage.NestedType", ("package", "SomeMessage.NestedType")),
|
||||||
|
(".package.SomeMessage.NestedType", ("package", "SomeMessage.NestedType")),
|
||||||
|
(".service.ExampleRequest", ("service", "ExampleRequest")),
|
||||||
|
(".package.lower_case_message", ("package", "lower_case_message")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_parse_field_type_name(full_name, expected_output):
|
||||||
|
assert parse_source_type_name(full_name) == expected_output
|
||||||
|
@ -57,7 +57,9 @@ plugin_output_package = "betterproto.tests.output_betterproto"
|
|||||||
reference_output_package = "betterproto.tests.output_reference"
|
reference_output_package = "betterproto.tests.output_reference"
|
||||||
|
|
||||||
|
|
||||||
TestData = namedtuple("TestData", "plugin_module, reference_module, json_data")
|
TestData = namedtuple(
|
||||||
|
"TestData", ["plugin_module", "reference_module", "json_data", "entry_point"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -75,15 +77,18 @@ def test_data(request):
|
|||||||
|
|
||||||
sys.path.append(reference_module_root)
|
sys.path.append(reference_module_root)
|
||||||
|
|
||||||
|
test_package = test_case_name + test_input_config.packages.get(test_case_name, "")
|
||||||
|
|
||||||
yield (
|
yield (
|
||||||
TestData(
|
TestData(
|
||||||
plugin_module=importlib.import_module(
|
plugin_module=importlib.import_module(
|
||||||
f"{plugin_output_package}.{test_case_name}.{test_case_name}"
|
f"{plugin_output_package}.{test_package}"
|
||||||
),
|
),
|
||||||
reference_module=lambda: importlib.import_module(
|
reference_module=lambda: importlib.import_module(
|
||||||
f"{reference_output_package}.{test_case_name}.{test_case_name}_pb2"
|
f"{reference_output_package}.{test_case_name}.{test_case_name}_pb2"
|
||||||
),
|
),
|
||||||
json_data=get_test_case_json_data(test_case_name),
|
json_data=get_test_case_json_data(test_case_name),
|
||||||
|
entry_point=test_package,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,7 +111,7 @@ def test_message_equality(test_data: TestData) -> None:
|
|||||||
|
|
||||||
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
||||||
def test_message_json(repeat, test_data: TestData) -> None:
|
def test_message_json(repeat, test_data: TestData) -> None:
|
||||||
plugin_module, _, json_data = test_data
|
plugin_module, _, json_data, entry_point = test_data
|
||||||
|
|
||||||
for _ in range(repeat):
|
for _ in range(repeat):
|
||||||
message: betterproto.Message = plugin_module.Test()
|
message: betterproto.Message = plugin_module.Test()
|
||||||
@ -119,13 +124,13 @@ def test_message_json(repeat, test_data: TestData) -> None:
|
|||||||
|
|
||||||
@pytest.mark.parametrize("test_data", test_cases.services, indirect=True)
|
@pytest.mark.parametrize("test_data", test_cases.services, indirect=True)
|
||||||
def test_service_can_be_instantiated(test_data: TestData) -> None:
|
def test_service_can_be_instantiated(test_data: TestData) -> None:
|
||||||
plugin_module, _, json_data = test_data
|
plugin_module, _, json_data, entry_point = test_data
|
||||||
plugin_module.TestStub(MockChannel())
|
plugin_module.TestStub(MockChannel())
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
@pytest.mark.parametrize("test_data", test_cases.messages_with_json, indirect=True)
|
||||||
def test_binary_compatibility(repeat, test_data: TestData) -> None:
|
def test_binary_compatibility(repeat, test_data: TestData) -> None:
|
||||||
plugin_module, reference_module, json_data = test_data
|
plugin_module, reference_module, json_data, entry_point = test_data
|
||||||
|
|
||||||
reference_instance = Parse(json_data, reference_module().Test())
|
reference_instance = Parse(json_data, reference_module().Test())
|
||||||
reference_binary_output = reference_instance.SerializeToString()
|
reference_binary_output = reference_instance.SerializeToString()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user