diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index abe4a57..8cae894 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks default_language_version: - python: python3 + python: python3.8 repos: - repo: https://github.com/pre-commit/pre-commit-hooks @@ -22,7 +22,7 @@ repos: hooks: - id: reorder-python-imports - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.812 + rev: v0.940 hooks: - id: mypy - args: ['--config-file', './mypy.ini'] + args: [--install-types, --non-interactive] diff --git a/docs/grpc_argument_validator/argument_validator_config.html b/docs/grpc_argument_validator/argument_validator_config.html index 725e0e3..c115569 100644 --- a/docs/grpc_argument_validator/argument_validator_config.html +++ b/docs/grpc_argument_validator/argument_validator_config.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.argument_validator_config API documentation @@ -34,7 +34,7 @@

Module grpc_argument_validator.argument_validator_config _use_rich_grpc_errors = False @classmethod - def set_rich_grpc_errors(cls, enabled: bool = True): + def set_rich_grpc_errors(cls, enabled: bool = True) -> None: """ Set the option to use rich gRPC errors """ @@ -74,7 +74,7 @@

Classes

_use_rich_grpc_errors = False @classmethod - def set_rich_grpc_errors(cls, enabled: bool = True): + def set_rich_grpc_errors(cls, enabled: bool = True) -> None: """ Set the option to use rich gRPC errors """ @@ -90,7 +90,7 @@

Classes

Static methods

-def set_rich_grpc_errors(enabled: bool = True) +def set_rich_grpc_errors(enabled: bool = True) ‑> None

Set the option to use rich gRPC errors

@@ -99,7 +99,7 @@

Static methods

Expand source code
@classmethod
-def set_rich_grpc_errors(cls, enabled: bool = True):
+def set_rich_grpc_errors(cls, enabled: bool = True) -> None:
     """
     Set the option to use rich gRPC errors
     """
@@ -154,7 +154,7 @@ 

-

Generated by pdoc 0.9.2.

+

Generated by pdoc 0.10.0.

diff --git a/docs/grpc_argument_validator/argument_validators.html b/docs/grpc_argument_validator/argument_validators.html index 1afb6ba..160e792 100644 --- a/docs/grpc_argument_validator/argument_validators.html +++ b/docs/grpc_argument_validator/argument_validators.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.argument_validators API documentation @@ -393,7 +393,7 @@

-

Generated by pdoc 0.9.2.

+

Generated by pdoc 0.10.0.

diff --git a/docs/grpc_argument_validator/field_path.html b/docs/grpc_argument_validator/field_path.html deleted file mode 100644 index 6b00c78..0000000 --- a/docs/grpc_argument_validator/field_path.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - -grpc_argument_validator.field_path API documentation - - - - - - - - - - - -
-
-
-

Module grpc_argument_validator.field_path

-
-
-
- -Expand source code - -
import re
-
-
-def is_valid_field_path(path: str):
-    return re.match(r"^(?:\.|\.?(?:[a-zA-Z][a-zA-Z_0-9]*\.)*(?:[a-zA-Z][a-zA-Z_0-9]*)(?:\[\])?)$", path) is not None
-
-
-
-
-
-
-
-

Functions

-
-
-def is_valid_field_path(path: str) -
-
-
-
- -Expand source code - -
def is_valid_field_path(path: str):
-    return re.match(r"^(?:\.|\.?(?:[a-zA-Z][a-zA-Z_0-9]*\.)*(?:[a-zA-Z][a-zA-Z_0-9]*)(?:\[\])?)$", path) is not None
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/grpc_argument_validator/fields.html b/docs/grpc_argument_validator/fields.html index 3329f6a..49930c4 100644 --- a/docs/grpc_argument_validator/fields.html +++ b/docs/grpc_argument_validator/fields.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.fields API documentation @@ -41,7 +41,7 @@

Module grpc_argument_validator.fields

return re.match(r"^(?:\.|\.?(?:[a-zA-Z][a-zA-Z_0-9]*\.)*(?:[a-zA-Z][a-zA-Z_0-9]*)(?:\[\])?)$", path) is not None -def validate_field_names(field_names: typing.Iterable[str]): +def validate_field_names(field_names: typing.Iterable[str]) -> None: """ Validates that all field names adhere to the Protobuf 3 language specification @@ -92,7 +92,7 @@

Functions

-def validate_field_names(field_names: Iterable[str]) +def validate_field_names(field_names: Iterable[str]) ‑> None

Validates that all field names adhere to the Protobuf 3 language specification

@@ -106,7 +106,7 @@

Functions

Expand source code -
def validate_field_names(field_names: typing.Iterable[str]):
+
def validate_field_names(field_names: typing.Iterable[str]) -> None:
     """
     Validates that all field names adhere to the Protobuf 3 language specification
 
@@ -151,7 +151,7 @@ 

Index

diff --git a/docs/grpc_argument_validator/index.html b/docs/grpc_argument_validator/index.html index bf828e5..6cf453b 100644 --- a/docs/grpc_argument_validator/index.html +++ b/docs/grpc_argument_validator/index.html @@ -3,7 +3,7 @@ - + grpc_argument_validator API documentation @@ -35,7 +35,7 @@

Getting Started

To get a local copy up and running follow these simple example steps.

Installation

From PyPI

-
pip install grpc-argument-validator
+
pip install grpc-argument-validator
 

From source

    @@ -46,22 +46,22 @@

    From source

    Clone repo

-
git clone https://github.com/messagebird/python-grpc-argument-validator.git
+
git clone https://github.com/messagebird/python-grpc-argument-validator.git
 
  • Install packages
-
cd python-grpc-argument-validator && poetry install
+
cd python-grpc-argument-validator && poetry install
 
  • Run the tests
-
cd src/tests
+
cd src/tests
 poetry run python -m unittest
 

Quick Example

-
from google.protobuf.descriptor import FieldDescriptor
+
from google.protobuf.descriptor import FieldDescriptor
 from grpc_argument_validator import validate_args
 from grpc_argument_validator import AbstractArgumentValidator, ValidationResult, ValidationContext
 
@@ -87,7 +87,7 @@ 

Argument field syntax

To specify which argument field should be validated, grpc-argument-validator expects strings that match the field names as defined in the protobufs. To access nested fields, use a dot (.).

Consider the following protobuf definition:

-
syntax = "proto3";
+
syntax = "proto3";
 
 package routeguide;
 
@@ -146,7 +146,7 @@ 

Argument field syntax

To clarify this, let's say that we know that both planet and name.value should have non-default values. We can then decorate a method in our gRPC server as follows:

-
import grpc
+
import grpc
 from google.protobuf.empty_pb2 import Empty
 from grpc_argument_validator import validate_args
 from tests.route_guide_protos.route_guide_pb2 import Route
@@ -173,14 +173,14 @@ 

'Has' validator

True. This of course works in combination with nested fields.

In the example below, calling the Create endpoint without setting Route.name would result in an INVALID_ARGUMENT status.

-

+

 class RouteServiceImpl(RouteServiceServicer):
     @validate_args(has=["name"])
     def CreateRoute(self, request: Route, context: grpc.ServicerContext):
         return Empty()
 

Run this on a local machine and make a request with an invalid argument:

-
with grpc.insecure_channel("127.0.0.1:50051") as c:
+
with grpc.insecure_channel("127.0.0.1:50051") as c:
     route_client = RouteServiceStub(channel=c)
     try:
         route_client.CreateRoute(Route(tags=["tag"]))
@@ -193,13 +193,13 @@ 

'Has' validator

UUID validator

Another common use-case is the validation of UUIDs. You can enlist the fields that should be UUIDs (represented as 16 bytes) with the uuids argument:

-
class RouteServiceImpl(RouteServiceServicer):
+
class RouteServiceImpl(RouteServiceServicer):
     @validate_args(uuids=["uuid.value"])
     def CreateArea(self, request: Area, context: grpc.ServicerContext):
         return Empty()
 

The client side might violate the UUID requirement as follows:

-
with grpc.insecure_channel("127.0.0.1:50051") as c:
+
with grpc.insecure_channel("127.0.0.1:50051") as c:
     route_client = RouteServiceStub(channel=c)
     try:
         route_client.CreateArea(Area(uuid=BytesValue(value="not a uuid".encode())))
@@ -211,13 +211,13 @@ 

UUID validator

Non-default validator

For fields that should have a non-default value, such as enums, we have provided the non_default argument:

-
class RouteServiceImpl(RouteServiceServicer):
+
class RouteServiceImpl(RouteServiceServicer):
     @validate_args(non_default=["planet.value"])
     def CreateRoute(self, request: Route, context: grpc.ServicerContext):
         return Empty()
 

The client side may violate this as follows:

-
with grpc.insecure_channel("127.0.0.1:50051") as c:
+
with grpc.insecure_channel("127.0.0.1:50051") as c:
     route_client = RouteServiceStub(channel=c)
     try:
         route_client.CreateRoute(Route(planet=PlanetValue()))
@@ -228,13 +228,13 @@ 

Non-default validator

Which will print 'planet.value' must have non-default value.

Non-empty validator

We provide a 'non-'empty validator which can be used to ensure that a repeated field has more than zero elements.

-
class RouteServiceImpl(RouteServiceServicer):
+
class RouteServiceImpl(RouteServiceServicer):
     @validate_args(non_empty=["tags"])
     def CreateRoute(self, request: Route, context: grpc.ServicerContext):
         return Empty()
 

Which can be violated as follows:

-
with grpc.insecure_channel("127.0.0.1:50051") as c:
+
with grpc.insecure_channel("127.0.0.1:50051") as c:
     route_client = RouteServiceStub(channel=c)
     try:
         route_client.CreateRoute(Route(tags=[]))
@@ -245,12 +245,12 @@ 

Non-empty validator

Which will print 'tags' must be non-empty.

Regexp validator

Finally, we have the regexp validator that can be used to check whether a string field matches a regular expression.

-
class RouteServiceImpl(RouteServiceServicer):
+
class RouteServiceImpl(RouteServiceServicer):
     @validate_args(validators={"message.value": RegexpValidator(pattern=r"\d+")})
     def CreateArea(self, request: Area, context: grpc.ServicerContext):
         return Empty()
 
-
with grpc.insecure_channel("127.0.0.1:50051") as c:
+
with grpc.insecure_channel("127.0.0.1:50051") as c:
     route_client = RouteServiceStub(channel=c)
     try:
         route_client.CreateArea(Area(message=StringValue(value="hello world")))
@@ -264,7 +264,7 @@ 

Custom validators

AbstractArgumentValidator and implement its check method. The example below shows how to implement a simple validator for checking that a path has 5 points. You can provide such custom validators through a dict that maps a field path to a validator:

-
from grpc_argument_validator import AbstractArgumentValidator
+
from grpc_argument_validator import AbstractArgumentValidator
 from grpc_argument_validator import ValidationContext
 from grpc_argument_validator import ValidationResult
 from google.protobuf.descriptor import FieldDescriptor
@@ -303,7 +303,7 @@ 

Streaming requests

single stream (e.g. the first request might have metadata describing the remainder of the stream), we provide a streaming request index in a ValidationContext that is passed to an AbstractArgumentValidator.

Here's an example of how that could be used:

-
class StreamingPathValidator(AbstractArgumentValidator):
+
class StreamingPathValidator(AbstractArgumentValidator):
     def __init__(self, first_number_of_points: int, second_number_of_points: int):
         self._first_number_of_points = first_number_of_points
         self._second_number_of_points = second_number_of_points
@@ -328,12 +328,12 @@ 

Enabling rich error details

To enable richer error responses where each violation is contained in a BadRequest proto, you can use

-
from grpc_argument_validator import ArgumentValidatorConfig
+
from grpc_argument_validator import ArgumentValidatorConfig
 
 ArgumentValidatorConfig.set_rich_grpc_errors(enabled=True)
 

Now, your client-side can parse the error details as follows:

-
def extract_error_details(err):
+
def extract_error_details(err):
     status_proto = status_pb2.Status()
 
     for metadatum in err.trailing_metadata():
@@ -369,7 +369,7 @@ 

Contributing

Generating HTML Documentation

Generate the docs by running:

-
pdoc --html -o docs src/grpc_argument_validator
+
pdoc --html -o docs src/grpc_argument_validator
 

License

@@ -479,7 +479,7 @@

Index

diff --git a/docs/grpc_argument_validator/streaming_argument_validators.html b/docs/grpc_argument_validator/streaming_argument_validators.html deleted file mode 100644 index 8d411bf..0000000 --- a/docs/grpc_argument_validator/streaming_argument_validators.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - -grpc_argument_validator.streaming_argument_validators API documentation - - - - - - - - - - - -
-
-
-

Module grpc_argument_validator.streaming_argument_validators

-
-
-
- -Expand source code - -
import abc
-import re
-import typing
-import uuid
-
-from google.protobuf.descriptor import FieldDescriptor
-from google.protobuf.message import Message
-
-from .validation_result import ValidationResult
-
-
-class AbstractStreamingArgumentValidator(abc.ABC):
-    """
-    An abstract class that is the base for all streaming argument validators
-    """
-
-    @abc.abstractmethod
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        """
-        Returns a validation bool of the given value and field_descriptor
-
-            Parameters:
-                message_index: The index of the message in the streaming request
-                name (str): Name for the field to be used in invalid reason messages.
-                value (typing.Any): The value to be validated
-                field_descriptor (FieldDescriptor): The protobuf field descriptor of the given value
-
-            Returns:
-                result (ValidationResult): validation bool for the given value
-        """
-        pass
-
-
-class StreamingUUIDBytesValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are valid UUIDs"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        try:
-            uuid.UUID(bytes=value)
-        except (ValueError, TypeError):
-            return ValidationResult(False, f"{name} must be a valid UUID in message request index {message_index}",)
-        return ValidationResult(True)
-
-
-class StreamingNonDefaultValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are non-empty not the default value for this field type"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if value != field_descriptor.default_value:
-            return ValidationResult(True)
-        return ValidationResult(False, f"{name} must have non-default value in message request index {message_index}")
-
-
-class StreamingNonEmptyValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are non-empty"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if len(value) > 0:
-            return ValidationResult(True)
-        return ValidationResult(False, f"{name} must be non-empty in message request index {message_index}",)
-
-
-class StreamingRegexpValidator(AbstractStreamingArgumentValidator):
-    """
-    Matches all input values in the stream against the provided regex.
-
-    Parameters:
-        pattern (str): Regexp pattern to match.
-    """
-
-    def __init__(self, pattern: str):
-        self._pattern = pattern
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if re.match(self._pattern, value) is not None:
-            return ValidationResult(True)
-        return ValidationResult(
-            False, f"{name} must match regexp pattern: {self._pattern} in message request index {message_index}"
-        )
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class AbstractStreamingArgumentValidator -
-
-

An abstract class that is the base for all streaming argument validators

-
- -Expand source code - -
class AbstractStreamingArgumentValidator(abc.ABC):
-    """
-    An abstract class that is the base for all streaming argument validators
-    """
-
-    @abc.abstractmethod
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        """
-        Returns a validation bool of the given value and field_descriptor
-
-            Parameters:
-                message_index: The index of the message in the streaming request
-                name (str): Name for the field to be used in invalid reason messages.
-                value (typing.Any): The value to be validated
-                field_descriptor (FieldDescriptor): The protobuf field descriptor of the given value
-
-            Returns:
-                result (ValidationResult): validation bool for the given value
-        """
-        pass
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def check(self, message_index: int, name: str, value: Any, field_descriptor: google.protobuf.descriptor.FieldDescriptor) ‑> ValidationResult -
-
-

Returns a validation bool of the given value and field_descriptor

-
Parameters:
-    message_index: The index of the message in the streaming request
-    name (str): Name for the field to be used in invalid reason messages.
-    value (typing.Any): The value to be validated
-    field_descriptor (FieldDescriptor): The protobuf field descriptor of the given value
-
-Returns:
-    result (ValidationResult): validation bool for the given value
-
-
- -Expand source code - -
@abc.abstractmethod
-def check(
-    self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-) -> ValidationResult:
-    """
-    Returns a validation bool of the given value and field_descriptor
-
-        Parameters:
-            message_index: The index of the message in the streaming request
-            name (str): Name for the field to be used in invalid reason messages.
-            value (typing.Any): The value to be validated
-            field_descriptor (FieldDescriptor): The protobuf field descriptor of the given value
-
-        Returns:
-            result (ValidationResult): validation bool for the given value
-    """
-    pass
-
-
-
-
-
-class StreamingNonDefaultValidator -
-
-

Ensures all the provided values in the stream are non-empty not the default value for this field type

-
- -Expand source code - -
class StreamingNonDefaultValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are non-empty not the default value for this field type"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if value != field_descriptor.default_value:
-            return ValidationResult(True)
-        return ValidationResult(False, f"{name} must have non-default value in message request index {message_index}")
-
-

Ancestors

- -

Inherited members

- -
-
-class StreamingNonEmptyValidator -
-
-

Ensures all the provided values in the stream are non-empty

-
- -Expand source code - -
class StreamingNonEmptyValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are non-empty"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if len(value) > 0:
-            return ValidationResult(True)
-        return ValidationResult(False, f"{name} must be non-empty in message request index {message_index}",)
-
-

Ancestors

- -

Inherited members

- -
-
-class StreamingRegexpValidator -(pattern: str) -
-
-

Matches all input values in the stream against the provided regex.

-

Parameters

-

pattern (str): Regexp pattern to match.

-
- -Expand source code - -
class StreamingRegexpValidator(AbstractStreamingArgumentValidator):
-    """
-    Matches all input values in the stream against the provided regex.
-
-    Parameters:
-        pattern (str): Regexp pattern to match.
-    """
-
-    def __init__(self, pattern: str):
-        self._pattern = pattern
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        if re.match(self._pattern, value) is not None:
-            return ValidationResult(True)
-        return ValidationResult(
-            False, f"{name} must match regexp pattern: {self._pattern} in message request index {message_index}"
-        )
-
-

Ancestors

- -

Inherited members

- -
-
-class StreamingUUIDBytesValidator -
-
-

Ensures all the provided values in the stream are valid UUIDs

-
- -Expand source code - -
class StreamingUUIDBytesValidator(AbstractStreamingArgumentValidator):
-    """Ensures all the provided values in the stream are valid UUIDs"""
-
-    def check(
-        self, message_index: int, name: str, value: typing.Any, field_descriptor: FieldDescriptor
-    ) -> ValidationResult:
-        try:
-            uuid.UUID(bytes=value)
-        except (ValueError, TypeError):
-            return ValidationResult(False, f"{name} must be a valid UUID in message request index {message_index}",)
-        return ValidationResult(True)
-
-

Ancestors

- -

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/grpc_argument_validator/validate_args_decorator.html b/docs/grpc_argument_validator/validate_args_decorator.html index 118489f..516a690 100644 --- a/docs/grpc_argument_validator/validate_args_decorator.html +++ b/docs/grpc_argument_validator/validate_args_decorator.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.validate_args_decorator API documentation @@ -32,9 +32,11 @@

Module grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorModule grpc_argument_validator.validate_args_decoratorFunctions

-def validate_args(has: Union[List[str], NoneType] = None, uuids: Union[List[str], NoneType] = None, non_default: Union[List[str], NoneType] = None, non_empty: Union[List[str], NoneType] = None, optional_uuids: Union[List[str], NoneType] = None, optional_non_empty: Union[List[str], NoneType] = None, optional_non_default: Union[List[str], NoneType] = None, validators: Union[Dict[str, AbstractArgumentValidator], NoneType] = None, optional_validators: Union[Dict[str, AbstractArgumentValidator], NoneType] = None) ‑> Callable +def validate_args(has: Optional[List[str]] = None, uuids: Optional[List[str]] = None, non_default: Optional[List[str]] = None, non_empty: Optional[List[str]] = None, optional_uuids: Optional[List[str]] = None, optional_non_empty: Optional[List[str]] = None, optional_non_default: Optional[List[str]] = None, validators: Optional[Dict[str, AbstractArgumentValidator]] = None, optional_validators: Optional[Dict[str, AbstractArgumentValidator]] = None) ‑> Callable[[Callable[..., ~R]], Callable[..., ~R]]

Decorator to validate Message type arguments for gRPC methods.

@@ -363,7 +371,7 @@

Functions

optional_non_default: Optional[List[str]] = None, validators: Optional[Dict[str, AbstractArgumentValidator]] = None, optional_validators: Optional[Dict[str, AbstractArgumentValidator]] = None, -) -> Callable: +) -> Callable[[Callable[..., R]], Callable[..., R]]: """ Decorator to validate Message type arguments for gRPC methods. @@ -439,8 +447,10 @@

Functions

if mandatory_fields.intersection(optional_fields): raise ValueError("Overlap in mandatory and optional fields") - def decorating_function(func): - def validate_message(request: Message, context: grpc.ServicerContext, validation_context: ValidationContext): + def decorating_function(func: Callable[..., R]) -> Callable[..., R]: + def validate_message( + request: Message, context: grpc.ServicerContext, validation_context: ValidationContext + ) -> None: field_violations = [] for field_name in field_names: field_validators: List[AbstractArgumentValidator] = [] @@ -479,13 +489,13 @@

Functions

grpc.StatusCode.INVALID_ARGUMENT, ", ".join([e.reason for e in field_violations])[:1000] ) - def validate_streaming(requests: Iterable[Message], context: grpc.ServicerContext): + def validate_streaming(requests: Iterable[M], context: grpc.ServicerContext) -> Generator[M, None, None]: for i, req in enumerate(requests): validate_message(req, context, ValidationContext(is_streaming=True, streaming_message_index=i)) yield req @functools.wraps(func) - def validate_wrapper(self, request: Union[Message, Iterable[Message]], context: grpc.ServicerContext): + def validate_wrapper(self: Any, request: Union[M, Iterable[M]], context: grpc.ServicerContext) -> R: if isinstance(request, Iterable): return func(self, validate_streaming(request, context), context) else: @@ -522,7 +532,7 @@

Index

diff --git a/docs/grpc_argument_validator/validate_streaming_args_decorator.html b/docs/grpc_argument_validator/validate_streaming_args_decorator.html deleted file mode 100644 index 939f676..0000000 --- a/docs/grpc_argument_validator/validate_streaming_args_decorator.html +++ /dev/null @@ -1,458 +0,0 @@ - - - - - - -grpc_argument_validator.validate_streaming_args_decorator API documentation - - - - - - - - - - - -
-
-
-

Module grpc_argument_validator.validate_streaming_args_decorator

-
-
-
- -Expand source code - -
import functools
-import itertools
-from typing import Callable
-from typing import Dict
-from typing import Iterable
-from typing import List
-from typing import Optional
-
-import grpc
-from google.protobuf.descriptor import FieldDescriptor
-from google.protobuf.message import Message
-from grpc_argument_validator import AbstractStreamingArgumentValidator
-from grpc_argument_validator.fields import validate_field_names
-from grpc_argument_validator.streaming_argument_validators import StreamingNonDefaultValidator
-from grpc_argument_validator.streaming_argument_validators import StreamingNonEmptyValidator
-from grpc_argument_validator.streaming_argument_validators import StreamingUUIDBytesValidator
-
-
-def validate_streaming_args(
-    has: Optional[List[str]] = None,
-    uuids: Optional[List[str]] = None,
-    non_default: Optional[List[str]] = None,
-    non_empty: Optional[List[str]] = None,
-    optional_uuids: Optional[List[str]] = None,
-    optional_non_empty: Optional[List[str]] = None,
-    optional_non_default: Optional[List[str]] = None,
-    validators: Optional[Dict[str, AbstractStreamingArgumentValidator]] = None,
-    optional_validators: Optional[Dict[str, AbstractStreamingArgumentValidator]] = None,
-) -> Callable:
-    """
-    Decorator to validate Message type arguments for gRPC methods.
-
-    Subfields can be separated by a `.`.
-
-    E.g. `foo.bar` where bar is a property of the Message in foo.
-
-
-    For lists the same notation can be used, for clarity `[]` can be added optionally. Both `foo.bar` and `foo[].bar`
-    can be used, where bar is a property of the Message in the list foo.
-
-        Parameters:
-            has (Optional[List[str]]):
-                Fields the Message should contain
-            uuids (Optional[List[str]]):
-                Fields to be validated for UUIDs
-            non_default (Optional[List[str]]):
-                Fields that should not have the default value
-            non_empty (Optional[List[str]]):
-                Fields that should not be empty
-            optional_uuids (Optional[List[str]]):
-                Fields that can be None or a valid UUID
-            optional_non_empty (Optional[List[str]]):
-                Fields that can be None or non-empty
-            optional_non_default (Optional[List[str]]):
-                Fields that can be None or non-default
-            validators (Optional[Dict[str, AbstractArgumentValidator]]):
-                Dict mapping field names to validators
-            optional_validators (Optional[Dict[str, AbstractArgumentValidator]]):
-                Dict mapping field names to validators, the fields can be None or validated using the specified
-                validator
-
-        Returns:
-            decorating_function (func): the decorating function wrapping the gRPC method function
-    """
-    if all(arg is None for arg in locals().values()):
-        raise ValueError("Should provide at least one field to validate")
-    has_value = has or []
-
-    optional_uuids_value = optional_uuids or []
-    optional_non_empty_value = optional_non_empty or []
-    optional_non_default_value = optional_non_default or []
-    optional_validators_value: Dict[str, AbstractStreamingArgumentValidator] = optional_validators or dict()
-
-    uuids_value = uuids or []
-    non_empty_value = non_empty or []
-    non_default_value = non_default or []
-    validators_value = validators or dict()
-    field_names = list(
-        itertools.chain(
-            has_value,
-            uuids_value,
-            optional_uuids_value,
-            non_empty_value,
-            optional_non_empty_value,
-            non_default_value,
-            optional_non_default_value,
-            validators_value.keys(),
-            optional_validators_value.keys(),
-        )
-    )
-
-    validate_field_names(field_names)
-
-    mandatory_fields = set(uuids_value + non_empty_value + non_default_value + list(validators_value.keys()))
-    optional_fields = set(
-        optional_uuids_value
-        + optional_non_empty_value
-        + optional_non_default_value
-        + list(optional_validators_value.keys())
-    )
-
-    if mandatory_fields.intersection(optional_fields):
-        raise ValueError("Overlap in mandatory and optional fields")
-
-    def decorating_function(func):
-        @functools.wraps(func)
-        def validate_wrapper(self, request: Iterable[Message], context: grpc.ServicerContext):
-            def validate_message(message_index: int, request: Message) -> Message:
-                errors = []
-
-                for field_name in field_names:
-                    field_validators: List[AbstractStreamingArgumentValidator] = []
-                    is_optional = (
-                        field_name in optional_non_empty_value
-                        or field_name in optional_uuids_value
-                        or field_name in optional_non_default_value
-                        or field_name in optional_validators_value
-                    )
-                    if field_name in uuids_value + optional_uuids_value:
-                        field_validators.append(StreamingUUIDBytesValidator())
-                    if field_name in non_empty_value + optional_non_empty_value:
-                        field_validators.append(StreamingNonEmptyValidator())
-                    if field_name in non_default_value + optional_non_default_value:
-                        field_validators.append(StreamingNonDefaultValidator())
-                    if field_name in itertools.chain(validators_value.keys(), optional_validators_value.keys()):
-                        validator = {**validators_value, **optional_validators_value}.get(field_name)
-                        if validator is not None:
-                            field_validators.append(validator)
-
-                    errors.extend(
-                        _recurse_validate(
-                            message_index,
-                            request,
-                            name=field_name,
-                            validators=field_validators,
-                            is_optional=is_optional,
-                        )
-                    )
-                if len(errors) > 0:
-                    context.abort(grpc.StatusCode.INVALID_ARGUMENT, ", ".join(errors)[:1000])
-                return request
-
-            def request_validator(request):
-                for i, r in enumerate(request):
-                    yield validate_message(i, r)
-
-            return func(self, request_validator(request), context)
-
-        return validate_wrapper
-
-    return decorating_function
-
-
-def _recurse_validate(
-    message_index: int,
-    message: Message,
-    name: str,
-    validators: List[AbstractStreamingArgumentValidator],
-    leading_parts_name: str = None,
-    is_optional: bool = False,
-):
-    errors = []
-    field_name_raw, *remaining_fields = name.split(".")
-    field_name = field_name_raw.rstrip("[]")
-
-    remaining_fields = [f for f in remaining_fields if f != ""]
-
-    if leading_parts_name is None and field_name == "":
-        field_value = message
-        field_descriptor: FieldDescriptor = message.DESCRIPTOR  # type: ignore
-        full_name = message.DESCRIPTOR.name
-    else:
-        field_descriptor = message.DESCRIPTOR.fields_by_name[field_name]
-
-        full_name = field_name if leading_parts_name is None else f"{leading_parts_name}.{field_name}"
-        if (
-            field_descriptor.label != FieldDescriptor.LABEL_REPEATED
-            and field_descriptor.type == FieldDescriptor.TYPE_MESSAGE
-            and not message.HasField(field_name)
-        ):
-            if is_optional:
-                return []
-            return [f"request must have {full_name}"]
-
-        field_value = getattr(message, field_name)
-
-    if remaining_fields:
-        if field_descriptor.label == FieldDescriptor.LABEL_REPEATED:
-            for i, elem in enumerate(field_value):  # type: ignore
-                errors.extend(
-                    _recurse_validate(
-                        message_index=message_index,
-                        message=elem,
-                        name=".".join(remaining_fields),
-                        leading_parts_name=f"{full_name}[{i}]",
-                        validators=validators,
-                    )
-                )
-        else:
-            errors.extend(
-                _recurse_validate(
-                    message_index=message_index,
-                    message=field_value,
-                    name=".".join(remaining_fields),
-                    leading_parts_name=full_name,
-                    validators=validators,
-                )
-            )
-    else:
-        for v in validators:
-            if field_name_raw.endswith("[]") and field_descriptor.label == FieldDescriptor.LABEL_REPEATED:
-                for i, field_value_elem in enumerate(field_value):  # type: ignore
-                    validation_result = v.check(message_index, f"{full_name}[{i}]", field_value_elem, field_descriptor)
-                    if not validation_result.valid:
-                        errors.append(validation_result.invalid_reason)
-            else:
-                validation_result = v.check(message_index, full_name, field_value, field_descriptor)
-                if not validation_result.valid:
-                    errors.append(validation_result.invalid_reason)
-    return errors
-
-
-
-
-
-
-
-

Functions

-
-
-def validate_streaming_args(has: Union[List[str], NoneType] = None, uuids: Union[List[str], NoneType] = None, non_default: Union[List[str], NoneType] = None, non_empty: Union[List[str], NoneType] = None, optional_uuids: Union[List[str], NoneType] = None, optional_non_empty: Union[List[str], NoneType] = None, optional_non_default: Union[List[str], NoneType] = None, validators: Union[Dict[str, AbstractStreamingArgumentValidator], NoneType] = None, optional_validators: Union[Dict[str, AbstractStreamingArgumentValidator], NoneType] = None) ‑> Callable -
-
-

Decorator to validate Message type arguments for gRPC methods.

-

Subfields can be separated by a ..

-

E.g. foo.bar where bar is a property of the Message in foo.

-

For lists the same notation can be used, for clarity [] can be added optionally. Both foo.bar and foo[].bar -can be used, where bar is a property of the Message in the list foo.

-
Parameters:
-    has (Optional[List[str]]):
-        Fields the Message should contain
-    uuids (Optional[List[str]]):
-        Fields to be validated for UUIDs
-    non_default (Optional[List[str]]):
-        Fields that should not have the default value
-    non_empty (Optional[List[str]]):
-        Fields that should not be empty
-    optional_uuids (Optional[List[str]]):
-        Fields that can be None or a valid UUID
-    optional_non_empty (Optional[List[str]]):
-        Fields that can be None or non-empty
-    optional_non_default (Optional[List[str]]):
-        Fields that can be None or non-default
-    validators (Optional[Dict[str, AbstractArgumentValidator]]):
-        Dict mapping field names to validators
-    optional_validators (Optional[Dict[str, AbstractArgumentValidator]]):
-        Dict mapping field names to validators, the fields can be None or validated using the specified
-        validator
-
-Returns:
-    decorating_function (func): the decorating function wrapping the gRPC method function
-
-
- -Expand source code - -
def validate_streaming_args(
-    has: Optional[List[str]] = None,
-    uuids: Optional[List[str]] = None,
-    non_default: Optional[List[str]] = None,
-    non_empty: Optional[List[str]] = None,
-    optional_uuids: Optional[List[str]] = None,
-    optional_non_empty: Optional[List[str]] = None,
-    optional_non_default: Optional[List[str]] = None,
-    validators: Optional[Dict[str, AbstractStreamingArgumentValidator]] = None,
-    optional_validators: Optional[Dict[str, AbstractStreamingArgumentValidator]] = None,
-) -> Callable:
-    """
-    Decorator to validate Message type arguments for gRPC methods.
-
-    Subfields can be separated by a `.`.
-
-    E.g. `foo.bar` where bar is a property of the Message in foo.
-
-
-    For lists the same notation can be used, for clarity `[]` can be added optionally. Both `foo.bar` and `foo[].bar`
-    can be used, where bar is a property of the Message in the list foo.
-
-        Parameters:
-            has (Optional[List[str]]):
-                Fields the Message should contain
-            uuids (Optional[List[str]]):
-                Fields to be validated for UUIDs
-            non_default (Optional[List[str]]):
-                Fields that should not have the default value
-            non_empty (Optional[List[str]]):
-                Fields that should not be empty
-            optional_uuids (Optional[List[str]]):
-                Fields that can be None or a valid UUID
-            optional_non_empty (Optional[List[str]]):
-                Fields that can be None or non-empty
-            optional_non_default (Optional[List[str]]):
-                Fields that can be None or non-default
-            validators (Optional[Dict[str, AbstractArgumentValidator]]):
-                Dict mapping field names to validators
-            optional_validators (Optional[Dict[str, AbstractArgumentValidator]]):
-                Dict mapping field names to validators, the fields can be None or validated using the specified
-                validator
-
-        Returns:
-            decorating_function (func): the decorating function wrapping the gRPC method function
-    """
-    if all(arg is None for arg in locals().values()):
-        raise ValueError("Should provide at least one field to validate")
-    has_value = has or []
-
-    optional_uuids_value = optional_uuids or []
-    optional_non_empty_value = optional_non_empty or []
-    optional_non_default_value = optional_non_default or []
-    optional_validators_value: Dict[str, AbstractStreamingArgumentValidator] = optional_validators or dict()
-
-    uuids_value = uuids or []
-    non_empty_value = non_empty or []
-    non_default_value = non_default or []
-    validators_value = validators or dict()
-    field_names = list(
-        itertools.chain(
-            has_value,
-            uuids_value,
-            optional_uuids_value,
-            non_empty_value,
-            optional_non_empty_value,
-            non_default_value,
-            optional_non_default_value,
-            validators_value.keys(),
-            optional_validators_value.keys(),
-        )
-    )
-
-    validate_field_names(field_names)
-
-    mandatory_fields = set(uuids_value + non_empty_value + non_default_value + list(validators_value.keys()))
-    optional_fields = set(
-        optional_uuids_value
-        + optional_non_empty_value
-        + optional_non_default_value
-        + list(optional_validators_value.keys())
-    )
-
-    if mandatory_fields.intersection(optional_fields):
-        raise ValueError("Overlap in mandatory and optional fields")
-
-    def decorating_function(func):
-        @functools.wraps(func)
-        def validate_wrapper(self, request: Iterable[Message], context: grpc.ServicerContext):
-            def validate_message(message_index: int, request: Message) -> Message:
-                errors = []
-
-                for field_name in field_names:
-                    field_validators: List[AbstractStreamingArgumentValidator] = []
-                    is_optional = (
-                        field_name in optional_non_empty_value
-                        or field_name in optional_uuids_value
-                        or field_name in optional_non_default_value
-                        or field_name in optional_validators_value
-                    )
-                    if field_name in uuids_value + optional_uuids_value:
-                        field_validators.append(StreamingUUIDBytesValidator())
-                    if field_name in non_empty_value + optional_non_empty_value:
-                        field_validators.append(StreamingNonEmptyValidator())
-                    if field_name in non_default_value + optional_non_default_value:
-                        field_validators.append(StreamingNonDefaultValidator())
-                    if field_name in itertools.chain(validators_value.keys(), optional_validators_value.keys()):
-                        validator = {**validators_value, **optional_validators_value}.get(field_name)
-                        if validator is not None:
-                            field_validators.append(validator)
-
-                    errors.extend(
-                        _recurse_validate(
-                            message_index,
-                            request,
-                            name=field_name,
-                            validators=field_validators,
-                            is_optional=is_optional,
-                        )
-                    )
-                if len(errors) > 0:
-                    context.abort(grpc.StatusCode.INVALID_ARGUMENT, ", ".join(errors)[:1000])
-                return request
-
-            def request_validator(request):
-                for i, r in enumerate(request):
-                    yield validate_message(i, r)
-
-            return func(self, request_validator(request), context)
-
-        return validate_wrapper
-
-    return decorating_function
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/grpc_argument_validator/validation_context.html b/docs/grpc_argument_validator/validation_context.html index c0c16c6..bf34f26 100644 --- a/docs/grpc_argument_validator/validation_context.html +++ b/docs/grpc_argument_validator/validation_context.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.validation_context API documentation @@ -54,7 +54,7 @@

Classes

class ValidationContext -(is_streaming: bool = False, streaming_message_index: Union[int, NoneType] = None) +(is_streaming: bool = False, streaming_message_index: Optional[int] = None)

Contains extra information about the request while validating.

@@ -79,7 +79,7 @@

Class variables

Whether the request is part of a streaming request.

-
var streaming_message_index : Union[int, NoneType]
+
var streaming_message_index : Optional[int]

If the request is a streaming request, the index of the current streamed message

@@ -114,7 +114,7 @@

-

Generated by pdoc 0.9.2.

+

Generated by pdoc 0.10.0.

diff --git a/docs/grpc_argument_validator/validation_result.html b/docs/grpc_argument_validator/validation_result.html index e0831de..ccba4b9 100644 --- a/docs/grpc_argument_validator/validation_result.html +++ b/docs/grpc_argument_validator/validation_result.html @@ -3,7 +3,7 @@ - + grpc_argument_validator.validation_result API documentation @@ -54,7 +54,7 @@

Classes

class ValidationResult -(valid: bool, invalid_reason: Union[str, NoneType] = None) +(valid: bool, invalid_reason: Optional[str] = None)

Contains results for validation check.

@@ -75,7 +75,7 @@

Classes

Class variables

-
var invalid_reason : Union[str, NoneType]
+
var invalid_reason : Optional[str]

Reason for invalidity of the argument. Will be None if valid.

@@ -114,7 +114,7 @@

diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 7840685..0000000 --- a/docs/index.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 27b2f42..0000000 --- a/mypy.ini +++ /dev/null @@ -1,9 +0,0 @@ -[mypy] -python_version = 3.7 -ignore_missing_imports = True - -[mypy-example] -ignore_errors = True - -[mypy-tests.route_guide_protos.*] -ignore_errors = True diff --git a/pyproject.toml b/pyproject.toml index 6faa015..f48ac71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,3 +33,19 @@ types-protobuf = "*" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.mypy] +python_version = 3.7 +ignore_missing_imports = true +check_untyped_defs = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_incomplete_defs = true + +[[tool.mypy.overrides]] +module = "example" +ignore_errors = true + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true diff --git a/src/grpc_argument_validator/argument_validator_config.py b/src/grpc_argument_validator/argument_validator_config.py index 4f4d853..5240edf 100644 --- a/src/grpc_argument_validator/argument_validator_config.py +++ b/src/grpc_argument_validator/argument_validator_config.py @@ -6,7 +6,7 @@ class ArgumentValidatorConfig: _use_rich_grpc_errors = False @classmethod - def set_rich_grpc_errors(cls, enabled: bool = True): + def set_rich_grpc_errors(cls, enabled: bool = True) -> None: """ Set the option to use rich gRPC errors """ diff --git a/src/grpc_argument_validator/fields.py b/src/grpc_argument_validator/fields.py index 95eb85f..94a5d65 100644 --- a/src/grpc_argument_validator/fields.py +++ b/src/grpc_argument_validator/fields.py @@ -13,7 +13,7 @@ def is_valid_field_path(path: str) -> bool: return re.match(r"^(?:\.|\.?(?:[a-zA-Z][a-zA-Z_0-9]*\.)*(?:[a-zA-Z][a-zA-Z_0-9]*)(?:\[\])?)$", path) is not None -def validate_field_names(field_names: typing.Iterable[str]): +def validate_field_names(field_names: typing.Iterable[str]) -> None: """ Validates that all field names adhere to the Protobuf 3 language specification diff --git a/src/grpc_argument_validator/py.typed b/src/grpc_argument_validator/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/grpc_argument_validator/validate_args_decorator.py b/src/grpc_argument_validator/validate_args_decorator.py index 847145b..93b5d73 100644 --- a/src/grpc_argument_validator/validate_args_decorator.py +++ b/src/grpc_argument_validator/validate_args_decorator.py @@ -4,9 +4,11 @@ from typing import Any from typing import Callable from typing import Dict +from typing import Generator from typing import Iterable from typing import List from typing import Optional +from typing import TypeVar from typing import Union import grpc @@ -32,10 +34,14 @@ class _FieldViolation: reason: str -def _none_or_empty(x: Optional[List[Any]]): +def _none_or_empty(x: Optional[List[Any]]) -> bool: return x is None or len(x) == 0 +M = TypeVar("M", bound=Message) +R = TypeVar("R") + + def validate_args( has: Optional[List[str]] = None, uuids: Optional[List[str]] = None, @@ -46,7 +52,7 @@ def validate_args( optional_non_default: Optional[List[str]] = None, validators: Optional[Dict[str, AbstractArgumentValidator]] = None, optional_validators: Optional[Dict[str, AbstractArgumentValidator]] = None, -) -> Callable: +) -> Callable[[Callable[..., R]], Callable[..., R]]: """ Decorator to validate Message type arguments for gRPC methods. @@ -122,8 +128,10 @@ def validate_args( if mandatory_fields.intersection(optional_fields): raise ValueError("Overlap in mandatory and optional fields") - def decorating_function(func): - def validate_message(request: Message, context: grpc.ServicerContext, validation_context: ValidationContext): + def decorating_function(func: Callable[..., R]) -> Callable[..., R]: + def validate_message( + request: Message, context: grpc.ServicerContext, validation_context: ValidationContext + ) -> None: field_violations = [] for field_name in field_names: field_validators: List[AbstractArgumentValidator] = [] @@ -162,13 +170,13 @@ def validate_message(request: Message, context: grpc.ServicerContext, validation grpc.StatusCode.INVALID_ARGUMENT, ", ".join([e.reason for e in field_violations])[:1000] ) - def validate_streaming(requests: Iterable[Message], context: grpc.ServicerContext): + def validate_streaming(requests: Iterable[M], context: grpc.ServicerContext) -> Generator[M, None, None]: for i, req in enumerate(requests): validate_message(req, context, ValidationContext(is_streaming=True, streaming_message_index=i)) yield req @functools.wraps(func) - def validate_wrapper(self, request: Union[Message, Iterable[Message]], context: grpc.ServicerContext): + def validate_wrapper(self: Any, request: Union[M, Iterable[M]], context: grpc.ServicerContext) -> R: if isinstance(request, Iterable): return func(self, validate_streaming(request, context), context) else: @@ -180,7 +188,7 @@ def validate_wrapper(self, request: Union[Message, Iterable[Message]], context: return decorating_function -def _create_rich_validation_error(field_violations: List[_FieldViolation]): +def _create_rich_validation_error(field_violations: List[_FieldViolation]) -> status_pb2.Status: detail = any_pb2.Any() detail.Pack( error_details_pb2.BadRequest( @@ -202,7 +210,7 @@ def _recurse_validate( name: str, validation_context: ValidationContext, validators: List[AbstractArgumentValidator], - leading_parts_name: str = None, + leading_parts_name: Optional[str] = None, is_optional: bool = False, ) -> List[_FieldViolation]: field_violations: List[_FieldViolation] = [] diff --git a/src/tests/route_guide_protos/route_guide_pb2.pyi b/src/tests/route_guide_protos/route_guide_pb2.pyi deleted file mode 100644 index d9c8b15..0000000 --- a/src/tests/route_guide_protos/route_guide_pb2.pyi +++ /dev/null @@ -1,135 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import google.protobuf.wrappers_pb2 -import typing -import typing_extensions - -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor = ... - -class Planet(_Planet, metaclass=_PlanetEnumTypeWrapper): - pass -class _Planet: - V = typing.NewType('V', builtins.int) -class _PlanetEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Planet.V], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor = ... - PLANET_INVALID = Planet.V(0) - PLANET_EARTH = Planet.V(1) - PLANET_MARS = Planet.V(2) - -PLANET_INVALID = Planet.V(0) -PLANET_EARTH = Planet.V(1) -PLANET_MARS = Planet.V(2) -global___Planet = Planet - - -class Point(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - X_FIELD_NUMBER: builtins.int - Y_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - x: builtins.int = ... - y: builtins.int = ... - @property - def name(self) -> google.protobuf.wrappers_pb2.StringValue: ... - def __init__(self, - *, - x : builtins.int = ..., - y : builtins.int = ..., - name : typing.Optional[google.protobuf.wrappers_pb2.StringValue] = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal[u"name",b"name"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal[u"name",b"name",u"x",b"x",u"y",b"y"]) -> None: ... -global___Point = Point - -class Rectangle(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - LO_FIELD_NUMBER: builtins.int - HI_FIELD_NUMBER: builtins.int - @property - def lo(self) -> global___Point: ... - @property - def hi(self) -> global___Point: ... - def __init__(self, - *, - lo : typing.Optional[global___Point] = ..., - hi : typing.Optional[global___Point] = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal[u"hi",b"hi",u"lo",b"lo"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal[u"hi",b"hi",u"lo",b"lo"]) -> None: ... -global___Rectangle = Rectangle - -class Area(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - RECTANGLE_FIELD_NUMBER: builtins.int - MESSAGE_FIELD_NUMBER: builtins.int - UUID_FIELD_NUMBER: builtins.int - @property - def rectangle(self) -> global___Rectangle: ... - @property - def message(self) -> google.protobuf.wrappers_pb2.StringValue: ... - @property - def uuid(self) -> google.protobuf.wrappers_pb2.BytesValue: ... - def __init__(self, - *, - rectangle : typing.Optional[global___Rectangle] = ..., - message : typing.Optional[google.protobuf.wrappers_pb2.StringValue] = ..., - uuid : typing.Optional[google.protobuf.wrappers_pb2.BytesValue] = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal[u"message",b"message",u"rectangle",b"rectangle",u"uuid",b"uuid"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal[u"message",b"message",u"rectangle",b"rectangle",u"uuid",b"uuid"]) -> None: ... -global___Area = Area - -class Path(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - POINTS_FIELD_NUMBER: builtins.int - @property - def points(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Point]: ... - def __init__(self, - *, - points : typing.Optional[typing.Iterable[global___Point]] = ..., - ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal[u"points",b"points"]) -> None: ... -global___Path = Path - -class PlanetValue(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - VALUE_FIELD_NUMBER: builtins.int - value: global___Planet.V = ... - def __init__(self, - *, - value : global___Planet.V = ..., - ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal[u"value",b"value"]) -> None: ... -global___PlanetValue = PlanetValue - -class Route(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... - PATH_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - PLANET_FIELD_NUMBER: builtins.int - TAGS_FIELD_NUMBER: builtins.int - @property - def path(self) -> global___Path: ... - @property - def name(self) -> google.protobuf.wrappers_pb2.StringValue: ... - @property - def planet(self) -> global___PlanetValue: ... - @property - def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ... - def __init__(self, - *, - path : typing.Optional[global___Path] = ..., - name : typing.Optional[google.protobuf.wrappers_pb2.StringValue] = ..., - planet : typing.Optional[global___PlanetValue] = ..., - tags : typing.Optional[typing.Iterable[typing.Text]] = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal[u"name",b"name",u"path",b"path",u"planet",b"planet"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal[u"name",b"name",u"path",b"path",u"planet",b"planet",u"tags",b"tags"]) -> None: ... -global___Route = Route