Merge branch 'refs/heads/master_gh'
# Conflicts: # pyproject.toml
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -28,7 +28,7 @@ jobs: | |||||||
|       - name: Get full Python version |       - name: Get full Python version | ||||||
|         id: full-python-version |         id: full-python-version | ||||||
|         shell: bash |         shell: bash | ||||||
|         run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))") |         run: echo "version=$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")" >> "$GITHUB_OUTPUT" | ||||||
|  |  | ||||||
|       - name: Install poetry |       - name: Install poetry | ||||||
|         shell: bash |         shell: bash | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							| @@ -277,7 +277,22 @@ message Test { | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| You can use `betterproto.which_one_of(message, group_name)` to determine which of the fields was set. It returns a tuple of the field name and value, or a blank string and `None` if unset. | On Python 3.10 and later, you can use a `match` statement to access the provided one-of field, which supports type-checking: | ||||||
|  |  | ||||||
|  | ```py | ||||||
|  | test = Test() | ||||||
|  | match test: | ||||||
|  |     case Test(on=value): | ||||||
|  |         print(value)  # value: bool | ||||||
|  |     case Test(count=value): | ||||||
|  |         print(value)  # value: int | ||||||
|  |     case Test(name=value): | ||||||
|  |         print(value)  # value: str | ||||||
|  |     case _: | ||||||
|  |         print("No value provided") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | You can also use `betterproto.which_one_of(message, group_name)` to determine which of the fields was set. It returns a tuple of the field name and value, or a blank string and `None` if unset. | ||||||
|  |  | ||||||
| ```py | ```py | ||||||
| >>> test = Test() | >>> test = Test() | ||||||
| @@ -292,17 +307,11 @@ You can use `betterproto.which_one_of(message, group_name)` to determine which o | |||||||
| >>> test.count = 57 | >>> test.count = 57 | ||||||
| >>> betterproto.which_one_of(test, "foo") | >>> betterproto.which_one_of(test, "foo") | ||||||
| ["count", 57] | ["count", 57] | ||||||
| >>> test.on |  | ||||||
| False |  | ||||||
|  |  | ||||||
| # Default (zero) values also work. | # Default (zero) values also work. | ||||||
| >>> test.name = "" | >>> test.name = "" | ||||||
| >>> betterproto.which_one_of(test, "foo") | >>> betterproto.which_one_of(test, "foo") | ||||||
| ["name", ""] | ["name", ""] | ||||||
| >>> test.count |  | ||||||
| 0 |  | ||||||
| >>> test.on |  | ||||||
| False |  | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Again this is a little different than the official Google code generator: | Again this is a little different than the official Google code generator: | ||||||
|   | |||||||
| @@ -85,17 +85,19 @@ wrappers used to provide optional zero value support. Each of these has a specia | |||||||
| representation and is handled a little differently from normal messages. The Python | representation and is handled a little differently from normal messages. The Python | ||||||
| mapping for these is as follows: | mapping for these is as follows: | ||||||
|  |  | ||||||
| +-------------------------------+-----------------------------------------------+--------------------------+ | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
| | ``Google Message``            | ``Python Type``                                 | ``Default``              | | | ``Google Message``            | ``Python Type``                                 | ``Default``              | | ||||||
| +===============================+===============================================+==========================+ | +===============================+=================================================+==========================+ | ||||||
| | ``google.protobuf.duration``  | :class:`datetime.timedelta`                     | ``0``                    | | | ``google.protobuf.duration``  | :class:`datetime.timedelta`                     | ``0``                    | | ||||||
| +-------------------------------+-----------------------------------------------+--------------------------+ | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
| | ``google.protobuf.timestamp`` | ``Timezone-aware`` :class:`datetime.datetime`   | ``1970-01-01T00:00:00Z`` | | | ``google.protobuf.timestamp`` | ``Timezone-aware`` :class:`datetime.datetime`   | ``1970-01-01T00:00:00Z`` | | ||||||
| +-------------------------------+-----------------------------------------------+--------------------------+ | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
| | ``google.protobuf.*Value``    | ``Optional[...]``/``None``                      | ``None``                 | | | ``google.protobuf.*Value``    | ``Optional[...]``/``None``                      | ``None``                 | | ||||||
| +-------------------------------+-----------------------------------------------+--------------------------+ | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
| | ``google.protobuf.*``         | ``betterproto.lib.google.protobuf.*``         | ``None``                 | | | ``google.protobuf.*``         | ``betterproto.lib.std.google.protobuf.*``       | ``None``                 | | ||||||
| +-------------------------------+-----------------------------------------------+--------------------------+ | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
|  | | ``google.protobuf.*``         | ``betterproto.lib.pydantic.google.protobuf.*``  | ``None``                 | | ||||||
|  | +-------------------------------+-------------------------------------------------+--------------------------+ | ||||||
|  |  | ||||||
|  |  | ||||||
| For the wrapper types, the Python type corresponds to the wrapped type, e.g. | For the wrapper types, the Python type corresponds to the wrapped type, e.g. | ||||||
|   | |||||||
| @@ -95,11 +95,11 @@ cmd = """ | |||||||
| protoc | protoc | ||||||
|     --plugin=protoc-gen-custom=src/betterproto/plugin/main.py |     --plugin=protoc-gen-custom=src/betterproto/plugin/main.py | ||||||
|     --custom_opt=INCLUDE_GOOGLE |     --custom_opt=INCLUDE_GOOGLE | ||||||
|     --custom_out=src/betterproto/lib |     --custom_out=src/betterproto/lib/std | ||||||
|     -I C:\\work\\include |     -I C:\\work\\include | ||||||
|     C:\\work\\include\\google\\protobuf\\**\\*.proto |     C:\\work\\include\\google\\protobuf\\**\\*.proto | ||||||
| """ | """ | ||||||
| help = "Regenerate the types in betterproto.lib.google" | help = "Regenerate the types in betterproto.lib.std.google" | ||||||
|  |  | ||||||
| # CI tasks | # CI tasks | ||||||
|  |  | ||||||
|   | |||||||
| @@ -749,7 +749,7 @@ class Message(ABC): | |||||||
|                 group_current.setdefault(meta.group) |                 group_current.setdefault(meta.group) | ||||||
|  |  | ||||||
|             value = self.__raw_get(field_name) |             value = self.__raw_get(field_name) | ||||||
|             if value != PLACEHOLDER and not (meta.optional and value is None): |             if value is not PLACEHOLDER and not (meta.optional and value is None): | ||||||
|                 # Found a non-sentinel value |                 # Found a non-sentinel value | ||||||
|                 all_sentinel = False |                 all_sentinel = False | ||||||
|  |  | ||||||
|   | |||||||
| @@ -43,7 +43,12 @@ def parse_source_type_name(field_type_name: str) -> Tuple[str, str]: | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_type_reference( | def get_type_reference( | ||||||
|     *, package: str, imports: set, source_type: str, unwrap: bool = True |     *, | ||||||
|  |     package: str, | ||||||
|  |     imports: set, | ||||||
|  |     source_type: str, | ||||||
|  |     unwrap: bool = True, | ||||||
|  |     pydantic: bool = False, | ||||||
| ) -> 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 | ||||||
| @@ -69,7 +74,9 @@ def get_type_reference( | |||||||
|     compiling_google_protobuf = current_package == ["google", "protobuf"] |     compiling_google_protobuf = current_package == ["google", "protobuf"] | ||||||
|     importing_google_protobuf = py_package == ["google", "protobuf"] |     importing_google_protobuf = py_package == ["google", "protobuf"] | ||||||
|     if importing_google_protobuf and not compiling_google_protobuf: |     if importing_google_protobuf and not compiling_google_protobuf: | ||||||
|         py_package = ["betterproto", "lib"] + py_package |         py_package = ( | ||||||
|  |             ["betterproto", "lib"] + (["pydantic"] if pydantic else []) + py_package | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     if py_package[:1] == ["betterproto"]: |     if py_package[:1] == ["betterproto"]: | ||||||
|         return reference_absolute(imports, py_package, py_type) |         return reference_absolute(imports, py_package, py_type) | ||||||
|   | |||||||
| @@ -156,6 +156,12 @@ class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType): | |||||||
|             f"{self.__class__.__name__} Cannot delete a member's attributes." |             f"{self.__class__.__name__} Cannot delete a member's attributes." | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     def __copy__(self) -> Self: | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def __deepcopy__(self, memo: Any) -> Self: | ||||||
|  |         return self | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def try_value(cls, value: int = 0) -> Self: |     def try_value(cls, value: int = 0) -> Self: | ||||||
|         """Return the value which corresponds to the value. |         """Return the value which corresponds to the value. | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,152 +1 @@ | |||||||
| # Generated by the protocol buffer compiler.  DO NOT EDIT! | from betterproto.lib.std.google.protobuf.compiler import * | ||||||
| # sources: google/protobuf/compiler/plugin.proto |  | ||||||
| # plugin: python-betterproto |  | ||||||
| # This file has been @generated |  | ||||||
| from dataclasses import dataclass |  | ||||||
| from typing import List |  | ||||||
|  |  | ||||||
| import betterproto |  | ||||||
| import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class CodeGeneratorResponseFeature(betterproto.Enum): |  | ||||||
|     """Sync with code_generator.h.""" |  | ||||||
|  |  | ||||||
|     FEATURE_NONE = 0 |  | ||||||
|     FEATURE_PROTO3_OPTIONAL = 1 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass(eq=False, repr=False) |  | ||||||
| class Version(betterproto.Message): |  | ||||||
|     """The version number of protocol compiler.""" |  | ||||||
|  |  | ||||||
|     major: int = betterproto.int32_field(1) |  | ||||||
|     minor: int = betterproto.int32_field(2) |  | ||||||
|     patch: int = betterproto.int32_field(3) |  | ||||||
|     suffix: str = betterproto.string_field(4) |  | ||||||
|     """ |  | ||||||
|     A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should |  | ||||||
|     be empty for mainline stable releases. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass(eq=False, repr=False) |  | ||||||
| class CodeGeneratorRequest(betterproto.Message): |  | ||||||
|     """An encoded CodeGeneratorRequest is written to the plugin's stdin.""" |  | ||||||
|  |  | ||||||
|     file_to_generate: List[str] = betterproto.string_field(1) |  | ||||||
|     """ |  | ||||||
|     The .proto files that were explicitly listed on the command-line.  The code |  | ||||||
|     generator should generate code only for these files.  Each file's |  | ||||||
|     descriptor will be included in proto_file, below. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     parameter: str = betterproto.string_field(2) |  | ||||||
|     """The generator parameter passed on the command-line.""" |  | ||||||
|  |  | ||||||
|     proto_file: List[ |  | ||||||
|         "betterproto_lib_google_protobuf.FileDescriptorProto" |  | ||||||
|     ] = betterproto.message_field(15) |  | ||||||
|     """ |  | ||||||
|     FileDescriptorProtos for all files in files_to_generate and everything they |  | ||||||
|     import.  The files will appear in topological order, so each file appears |  | ||||||
|     before any file that imports it. protoc guarantees that all proto_files |  | ||||||
|     will be written after the fields above, even though this is not technically |  | ||||||
|     guaranteed by the protobuf wire format.  This theoretically could allow a |  | ||||||
|     plugin to stream in the FileDescriptorProtos and handle them one by one |  | ||||||
|     rather than read the entire set into memory at once.  However, as of this |  | ||||||
|     writing, this is not similarly optimized on protoc's end -- it will store |  | ||||||
|     all fields in memory at once before sending them to the plugin. Type names |  | ||||||
|     of fields and extensions in the FileDescriptorProto are always fully |  | ||||||
|     qualified. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     compiler_version: "Version" = betterproto.message_field(3) |  | ||||||
|     """The version number of protocol compiler.""" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass(eq=False, repr=False) |  | ||||||
| class CodeGeneratorResponse(betterproto.Message): |  | ||||||
|     """The plugin writes an encoded CodeGeneratorResponse to stdout.""" |  | ||||||
|  |  | ||||||
|     error: str = betterproto.string_field(1) |  | ||||||
|     """ |  | ||||||
|     Error message.  If non-empty, code generation failed.  The plugin process |  | ||||||
|     should exit with status code zero even if it reports an error in this way. |  | ||||||
|     This should be used to indicate errors in .proto files which prevent the |  | ||||||
|     code generator from generating correct code.  Errors which indicate a |  | ||||||
|     problem in protoc itself -- such as the input CodeGeneratorRequest being |  | ||||||
|     unparseable -- should be reported by writing a message to stderr and |  | ||||||
|     exiting with a non-zero status code. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     supported_features: int = betterproto.uint64_field(2) |  | ||||||
|     """ |  | ||||||
|     A bitmask of supported features that the code generator supports. This is a |  | ||||||
|     bitwise "or" of values from the Feature enum. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     file: List["CodeGeneratorResponseFile"] = betterproto.message_field(15) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass(eq=False, repr=False) |  | ||||||
| class CodeGeneratorResponseFile(betterproto.Message): |  | ||||||
|     """Represents a single generated file.""" |  | ||||||
|  |  | ||||||
|     name: str = betterproto.string_field(1) |  | ||||||
|     """ |  | ||||||
|     The file name, relative to the output directory.  The name must not contain |  | ||||||
|     "." or ".." components and must be relative, not be absolute (so, the file |  | ||||||
|     cannot lie outside the output directory).  "/" must be used as the path |  | ||||||
|     separator, not "\". If the name is omitted, the content will be appended to |  | ||||||
|     the previous file.  This allows the generator to break large files into |  | ||||||
|     small chunks, and allows the generated text to be streamed back to protoc |  | ||||||
|     so that large files need not reside completely in memory at one time.  Note |  | ||||||
|     that as of this writing protoc does not optimize for this -- it will read |  | ||||||
|     the entire CodeGeneratorResponse before writing files to disk. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     insertion_point: str = betterproto.string_field(2) |  | ||||||
|     """ |  | ||||||
|     If non-empty, indicates that the named file should already exist, and the |  | ||||||
|     content here is to be inserted into that file at a defined insertion point. |  | ||||||
|     This feature allows a code generator to extend the output produced by |  | ||||||
|     another code generator.  The original generator may provide insertion |  | ||||||
|     points by placing special annotations in the file that look like: |  | ||||||
|     @@protoc_insertion_point(NAME) The annotation can have arbitrary text |  | ||||||
|     before and after it on the line, which allows it to be placed in a comment. |  | ||||||
|     NAME should be replaced with an identifier naming the point -- this is what |  | ||||||
|     other generators will use as the insertion_point.  Code inserted at this |  | ||||||
|     point will be placed immediately above the line containing the insertion |  | ||||||
|     point (thus multiple insertions to the same point will come out in the |  | ||||||
|     order they were added). The double-@ is intended to make it unlikely that |  | ||||||
|     the generated code could contain things that look like insertion points by |  | ||||||
|     accident. For example, the C++ code generator places the following line in |  | ||||||
|     the .pb.h files that it generates:   // |  | ||||||
|     @@protoc_insertion_point(namespace_scope) This line appears within the |  | ||||||
|     scope of the file's package namespace, but outside of any particular class. |  | ||||||
|     Another plugin can then specify the insertion_point "namespace_scope" to |  | ||||||
|     generate additional classes or other declarations that should be placed in |  | ||||||
|     this scope. Note that if the line containing the insertion point begins |  | ||||||
|     with whitespace, the same whitespace will be added to every line of the |  | ||||||
|     inserted text.  This is useful for languages like Python, where indentation |  | ||||||
|     matters.  In these languages, the insertion point comment should be |  | ||||||
|     indented the same amount as any inserted code will need to be in order to |  | ||||||
|     work correctly in that context. The code generator that generates the |  | ||||||
|     initial file and the one which inserts into it must both run as part of a |  | ||||||
|     single invocation of protoc. Code generators are executed in the order in |  | ||||||
|     which they appear on the command line. If |insertion_point| is present, |  | ||||||
|     |name| must also be present. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     content: str = betterproto.string_field(15) |  | ||||||
|     """The file contents.""" |  | ||||||
|  |  | ||||||
|     generated_code_info: "betterproto_lib_google_protobuf.GeneratedCodeInfo" = ( |  | ||||||
|         betterproto.message_field(16) |  | ||||||
|     ) |  | ||||||
|     """ |  | ||||||
|     Information describing the file content being inserted. If an insertion |  | ||||||
|     point is used, this information will be appropriately offset and inserted |  | ||||||
|     into the code generation metadata for the generated files. |  | ||||||
|     """ |  | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								src/betterproto/lib/pydantic/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/betterproto/lib/pydantic/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								src/betterproto/lib/pydantic/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/betterproto/lib/pydantic/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2589
									
								
								src/betterproto/lib/pydantic/google/protobuf/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2589
									
								
								src/betterproto/lib/pydantic/google/protobuf/__init__.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | # Generated by the protocol buffer compiler.  DO NOT EDIT! | ||||||
|  | # sources: google/protobuf/compiler/plugin.proto | ||||||
|  | # plugin: python-betterproto | ||||||
|  | # This file has been @generated | ||||||
|  |  | ||||||
|  | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from dataclasses import dataclass | ||||||
|  | else: | ||||||
|  |     from pydantic.dataclasses import dataclass | ||||||
|  |  | ||||||
|  | from typing import List | ||||||
|  |  | ||||||
|  | import betterproto | ||||||
|  | import betterproto.lib.pydantic.google.protobuf as betterproto_lib_pydantic_google_protobuf | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class CodeGeneratorResponseFeature(betterproto.Enum): | ||||||
|  |     """Sync with code_generator.h.""" | ||||||
|  |  | ||||||
|  |     FEATURE_NONE = 0 | ||||||
|  |     FEATURE_PROTO3_OPTIONAL = 1 | ||||||
|  |     FEATURE_SUPPORTS_EDITIONS = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class Version(betterproto.Message): | ||||||
|  |     """The version number of protocol compiler.""" | ||||||
|  |  | ||||||
|  |     major: int = betterproto.int32_field(1) | ||||||
|  |     minor: int = betterproto.int32_field(2) | ||||||
|  |     patch: int = betterproto.int32_field(3) | ||||||
|  |     suffix: str = betterproto.string_field(4) | ||||||
|  |     """ | ||||||
|  |     A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should | ||||||
|  |      be empty for mainline stable releases. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorRequest(betterproto.Message): | ||||||
|  |     """An encoded CodeGeneratorRequest is written to the plugin's stdin.""" | ||||||
|  |  | ||||||
|  |     file_to_generate: List[str] = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     The .proto files that were explicitly listed on the command-line.  The | ||||||
|  |      code generator should generate code only for these files.  Each file's | ||||||
|  |      descriptor will be included in proto_file, below. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     parameter: str = betterproto.string_field(2) | ||||||
|  |     """The generator parameter passed on the command-line.""" | ||||||
|  |  | ||||||
|  |     proto_file: List[ | ||||||
|  |         "betterproto_lib_pydantic_google_protobuf.FileDescriptorProto" | ||||||
|  |     ] = betterproto.message_field(15) | ||||||
|  |     """ | ||||||
|  |     FileDescriptorProtos for all files in files_to_generate and everything | ||||||
|  |      they import.  The files will appear in topological order, so each file | ||||||
|  |      appears before any file that imports it. | ||||||
|  |      | ||||||
|  |      Note: the files listed in files_to_generate will include runtime-retention | ||||||
|  |      options only, but all other files will include source-retention options. | ||||||
|  |      The source_file_descriptors field below is available in case you need | ||||||
|  |      source-retention options for files_to_generate. | ||||||
|  |      | ||||||
|  |      protoc guarantees that all proto_files will be written after | ||||||
|  |      the fields above, even though this is not technically guaranteed by the | ||||||
|  |      protobuf wire format.  This theoretically could allow a plugin to stream | ||||||
|  |      in the FileDescriptorProtos and handle them one by one rather than read | ||||||
|  |      the entire set into memory at once.  However, as of this writing, this | ||||||
|  |      is not similarly optimized on protoc's end -- it will store all fields in | ||||||
|  |      memory at once before sending them to the plugin. | ||||||
|  |      | ||||||
|  |      Type names of fields and extensions in the FileDescriptorProto are always | ||||||
|  |      fully qualified. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     source_file_descriptors: List[ | ||||||
|  |         "betterproto_lib_pydantic_google_protobuf.FileDescriptorProto" | ||||||
|  |     ] = betterproto.message_field(17) | ||||||
|  |     """ | ||||||
|  |     File descriptors with all options, including source-retention options. | ||||||
|  |      These descriptors are only provided for the files listed in | ||||||
|  |      files_to_generate. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     compiler_version: "Version" = betterproto.message_field(3) | ||||||
|  |     """The version number of protocol compiler.""" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorResponse(betterproto.Message): | ||||||
|  |     """The plugin writes an encoded CodeGeneratorResponse to stdout.""" | ||||||
|  |  | ||||||
|  |     error: str = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     Error message.  If non-empty, code generation failed.  The plugin process | ||||||
|  |      should exit with status code zero even if it reports an error in this way. | ||||||
|  |      | ||||||
|  |      This should be used to indicate errors in .proto files which prevent the | ||||||
|  |      code generator from generating correct code.  Errors which indicate a | ||||||
|  |      problem in protoc itself -- such as the input CodeGeneratorRequest being | ||||||
|  |      unparseable -- should be reported by writing a message to stderr and | ||||||
|  |      exiting with a non-zero status code. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     supported_features: int = betterproto.uint64_field(2) | ||||||
|  |     """ | ||||||
|  |     A bitmask of supported features that the code generator supports. | ||||||
|  |      This is a bitwise "or" of values from the Feature enum. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     minimum_edition: int = betterproto.int32_field(3) | ||||||
|  |     """ | ||||||
|  |     The minimum edition this plugin supports.  This will be treated as an | ||||||
|  |      Edition enum, but we want to allow unknown values.  It should be specified | ||||||
|  |      according the edition enum value, *not* the edition number.  Only takes | ||||||
|  |      effect for plugins that have FEATURE_SUPPORTS_EDITIONS set. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     maximum_edition: int = betterproto.int32_field(4) | ||||||
|  |     """ | ||||||
|  |     The maximum edition this plugin supports.  This will be treated as an | ||||||
|  |      Edition enum, but we want to allow unknown values.  It should be specified | ||||||
|  |      according the edition enum value, *not* the edition number.  Only takes | ||||||
|  |      effect for plugins that have FEATURE_SUPPORTS_EDITIONS set. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     file: List["CodeGeneratorResponseFile"] = betterproto.message_field(15) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorResponseFile(betterproto.Message): | ||||||
|  |     """Represents a single generated file.""" | ||||||
|  |  | ||||||
|  |     name: str = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     The file name, relative to the output directory.  The name must not | ||||||
|  |      contain "." or ".." components and must be relative, not be absolute (so, | ||||||
|  |      the file cannot lie outside the output directory).  "/" must be used as | ||||||
|  |      the path separator, not "\". | ||||||
|  |      | ||||||
|  |      If the name is omitted, the content will be appended to the previous | ||||||
|  |      file.  This allows the generator to break large files into small chunks, | ||||||
|  |      and allows the generated text to be streamed back to protoc so that large | ||||||
|  |      files need not reside completely in memory at one time.  Note that as of | ||||||
|  |      this writing protoc does not optimize for this -- it will read the entire | ||||||
|  |      CodeGeneratorResponse before writing files to disk. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     insertion_point: str = betterproto.string_field(2) | ||||||
|  |     """ | ||||||
|  |     If non-empty, indicates that the named file should already exist, and the | ||||||
|  |      content here is to be inserted into that file at a defined insertion | ||||||
|  |      point.  This feature allows a code generator to extend the output | ||||||
|  |      produced by another code generator.  The original generator may provide | ||||||
|  |      insertion points by placing special annotations in the file that look | ||||||
|  |      like: | ||||||
|  |        @@protoc_insertion_point(NAME) | ||||||
|  |      The annotation can have arbitrary text before and after it on the line, | ||||||
|  |      which allows it to be placed in a comment.  NAME should be replaced with | ||||||
|  |      an identifier naming the point -- this is what other generators will use | ||||||
|  |      as the insertion_point.  Code inserted at this point will be placed | ||||||
|  |      immediately above the line containing the insertion point (thus multiple | ||||||
|  |      insertions to the same point will come out in the order they were added). | ||||||
|  |      The double-@ is intended to make it unlikely that the generated code | ||||||
|  |      could contain things that look like insertion points by accident. | ||||||
|  |      | ||||||
|  |      For example, the C++ code generator places the following line in the | ||||||
|  |      .pb.h files that it generates: | ||||||
|  |        // @@protoc_insertion_point(namespace_scope) | ||||||
|  |      This line appears within the scope of the file's package namespace, but | ||||||
|  |      outside of any particular class.  Another plugin can then specify the | ||||||
|  |      insertion_point "namespace_scope" to generate additional classes or | ||||||
|  |      other declarations that should be placed in this scope. | ||||||
|  |      | ||||||
|  |      Note that if the line containing the insertion point begins with | ||||||
|  |      whitespace, the same whitespace will be added to every line of the | ||||||
|  |      inserted text.  This is useful for languages like Python, where | ||||||
|  |      indentation matters.  In these languages, the insertion point comment | ||||||
|  |      should be indented the same amount as any inserted code will need to be | ||||||
|  |      in order to work correctly in that context. | ||||||
|  |      | ||||||
|  |      The code generator that generates the initial file and the one which | ||||||
|  |      inserts into it must both run as part of a single invocation of protoc. | ||||||
|  |      Code generators are executed in the order in which they appear on the | ||||||
|  |      command line. | ||||||
|  |      | ||||||
|  |      If |insertion_point| is present, |name| must also be present. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     content: str = betterproto.string_field(15) | ||||||
|  |     """The file contents.""" | ||||||
|  |  | ||||||
|  |     generated_code_info: ( | ||||||
|  |         "betterproto_lib_pydantic_google_protobuf.GeneratedCodeInfo" | ||||||
|  |     ) = betterproto.message_field(16) | ||||||
|  |     """ | ||||||
|  |     Information describing the file content being inserted. If an insertion | ||||||
|  |      point is used, this information will be appropriately offset and inserted | ||||||
|  |      into the code generation metadata for the generated files. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CodeGeneratorRequest.__pydantic_model__.update_forward_refs()  # type: ignore | ||||||
|  | CodeGeneratorResponse.__pydantic_model__.update_forward_refs()  # type: ignore | ||||||
|  | CodeGeneratorResponseFile.__pydantic_model__.update_forward_refs()  # type: ignore | ||||||
							
								
								
									
										0
									
								
								src/betterproto/lib/std/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/betterproto/lib/std/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								src/betterproto/lib/std/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/betterproto/lib/std/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2526
									
								
								src/betterproto/lib/std/google/protobuf/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2526
									
								
								src/betterproto/lib/std/google/protobuf/__init__.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										198
									
								
								src/betterproto/lib/std/google/protobuf/compiler/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/betterproto/lib/std/google/protobuf/compiler/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | |||||||
|  | # Generated by the protocol buffer compiler.  DO NOT EDIT! | ||||||
|  | # sources: google/protobuf/compiler/plugin.proto | ||||||
|  | # plugin: python-betterproto | ||||||
|  | # This file has been @generated | ||||||
|  |  | ||||||
|  | from dataclasses import dataclass | ||||||
|  | from typing import List | ||||||
|  |  | ||||||
|  | import betterproto | ||||||
|  | import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class CodeGeneratorResponseFeature(betterproto.Enum): | ||||||
|  |     """Sync with code_generator.h.""" | ||||||
|  |  | ||||||
|  |     FEATURE_NONE = 0 | ||||||
|  |     FEATURE_PROTO3_OPTIONAL = 1 | ||||||
|  |     FEATURE_SUPPORTS_EDITIONS = 2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class Version(betterproto.Message): | ||||||
|  |     """The version number of protocol compiler.""" | ||||||
|  |  | ||||||
|  |     major: int = betterproto.int32_field(1) | ||||||
|  |     minor: int = betterproto.int32_field(2) | ||||||
|  |     patch: int = betterproto.int32_field(3) | ||||||
|  |     suffix: str = betterproto.string_field(4) | ||||||
|  |     """ | ||||||
|  |     A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should | ||||||
|  |      be empty for mainline stable releases. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorRequest(betterproto.Message): | ||||||
|  |     """An encoded CodeGeneratorRequest is written to the plugin's stdin.""" | ||||||
|  |  | ||||||
|  |     file_to_generate: List[str] = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     The .proto files that were explicitly listed on the command-line.  The | ||||||
|  |      code generator should generate code only for these files.  Each file's | ||||||
|  |      descriptor will be included in proto_file, below. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     parameter: str = betterproto.string_field(2) | ||||||
|  |     """The generator parameter passed on the command-line.""" | ||||||
|  |  | ||||||
|  |     proto_file: List[ | ||||||
|  |         "betterproto_lib_google_protobuf.FileDescriptorProto" | ||||||
|  |     ] = betterproto.message_field(15) | ||||||
|  |     """ | ||||||
|  |     FileDescriptorProtos for all files in files_to_generate and everything | ||||||
|  |      they import.  The files will appear in topological order, so each file | ||||||
|  |      appears before any file that imports it. | ||||||
|  |      | ||||||
|  |      Note: the files listed in files_to_generate will include runtime-retention | ||||||
|  |      options only, but all other files will include source-retention options. | ||||||
|  |      The source_file_descriptors field below is available in case you need | ||||||
|  |      source-retention options for files_to_generate. | ||||||
|  |      | ||||||
|  |      protoc guarantees that all proto_files will be written after | ||||||
|  |      the fields above, even though this is not technically guaranteed by the | ||||||
|  |      protobuf wire format.  This theoretically could allow a plugin to stream | ||||||
|  |      in the FileDescriptorProtos and handle them one by one rather than read | ||||||
|  |      the entire set into memory at once.  However, as of this writing, this | ||||||
|  |      is not similarly optimized on protoc's end -- it will store all fields in | ||||||
|  |      memory at once before sending them to the plugin. | ||||||
|  |      | ||||||
|  |      Type names of fields and extensions in the FileDescriptorProto are always | ||||||
|  |      fully qualified. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     source_file_descriptors: List[ | ||||||
|  |         "betterproto_lib_google_protobuf.FileDescriptorProto" | ||||||
|  |     ] = betterproto.message_field(17) | ||||||
|  |     """ | ||||||
|  |     File descriptors with all options, including source-retention options. | ||||||
|  |      These descriptors are only provided for the files listed in | ||||||
|  |      files_to_generate. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     compiler_version: "Version" = betterproto.message_field(3) | ||||||
|  |     """The version number of protocol compiler.""" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorResponse(betterproto.Message): | ||||||
|  |     """The plugin writes an encoded CodeGeneratorResponse to stdout.""" | ||||||
|  |  | ||||||
|  |     error: str = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     Error message.  If non-empty, code generation failed.  The plugin process | ||||||
|  |      should exit with status code zero even if it reports an error in this way. | ||||||
|  |      | ||||||
|  |      This should be used to indicate errors in .proto files which prevent the | ||||||
|  |      code generator from generating correct code.  Errors which indicate a | ||||||
|  |      problem in protoc itself -- such as the input CodeGeneratorRequest being | ||||||
|  |      unparseable -- should be reported by writing a message to stderr and | ||||||
|  |      exiting with a non-zero status code. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     supported_features: int = betterproto.uint64_field(2) | ||||||
|  |     """ | ||||||
|  |     A bitmask of supported features that the code generator supports. | ||||||
|  |      This is a bitwise "or" of values from the Feature enum. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     minimum_edition: int = betterproto.int32_field(3) | ||||||
|  |     """ | ||||||
|  |     The minimum edition this plugin supports.  This will be treated as an | ||||||
|  |      Edition enum, but we want to allow unknown values.  It should be specified | ||||||
|  |      according the edition enum value, *not* the edition number.  Only takes | ||||||
|  |      effect for plugins that have FEATURE_SUPPORTS_EDITIONS set. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     maximum_edition: int = betterproto.int32_field(4) | ||||||
|  |     """ | ||||||
|  |     The maximum edition this plugin supports.  This will be treated as an | ||||||
|  |      Edition enum, but we want to allow unknown values.  It should be specified | ||||||
|  |      according the edition enum value, *not* the edition number.  Only takes | ||||||
|  |      effect for plugins that have FEATURE_SUPPORTS_EDITIONS set. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     file: List["CodeGeneratorResponseFile"] = betterproto.message_field(15) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass(eq=False, repr=False) | ||||||
|  | class CodeGeneratorResponseFile(betterproto.Message): | ||||||
|  |     """Represents a single generated file.""" | ||||||
|  |  | ||||||
|  |     name: str = betterproto.string_field(1) | ||||||
|  |     """ | ||||||
|  |     The file name, relative to the output directory.  The name must not | ||||||
|  |      contain "." or ".." components and must be relative, not be absolute (so, | ||||||
|  |      the file cannot lie outside the output directory).  "/" must be used as | ||||||
|  |      the path separator, not "\". | ||||||
|  |      | ||||||
|  |      If the name is omitted, the content will be appended to the previous | ||||||
|  |      file.  This allows the generator to break large files into small chunks, | ||||||
|  |      and allows the generated text to be streamed back to protoc so that large | ||||||
|  |      files need not reside completely in memory at one time.  Note that as of | ||||||
|  |      this writing protoc does not optimize for this -- it will read the entire | ||||||
|  |      CodeGeneratorResponse before writing files to disk. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     insertion_point: str = betterproto.string_field(2) | ||||||
|  |     """ | ||||||
|  |     If non-empty, indicates that the named file should already exist, and the | ||||||
|  |      content here is to be inserted into that file at a defined insertion | ||||||
|  |      point.  This feature allows a code generator to extend the output | ||||||
|  |      produced by another code generator.  The original generator may provide | ||||||
|  |      insertion points by placing special annotations in the file that look | ||||||
|  |      like: | ||||||
|  |        @@protoc_insertion_point(NAME) | ||||||
|  |      The annotation can have arbitrary text before and after it on the line, | ||||||
|  |      which allows it to be placed in a comment.  NAME should be replaced with | ||||||
|  |      an identifier naming the point -- this is what other generators will use | ||||||
|  |      as the insertion_point.  Code inserted at this point will be placed | ||||||
|  |      immediately above the line containing the insertion point (thus multiple | ||||||
|  |      insertions to the same point will come out in the order they were added). | ||||||
|  |      The double-@ is intended to make it unlikely that the generated code | ||||||
|  |      could contain things that look like insertion points by accident. | ||||||
|  |      | ||||||
|  |      For example, the C++ code generator places the following line in the | ||||||
|  |      .pb.h files that it generates: | ||||||
|  |        // @@protoc_insertion_point(namespace_scope) | ||||||
|  |      This line appears within the scope of the file's package namespace, but | ||||||
|  |      outside of any particular class.  Another plugin can then specify the | ||||||
|  |      insertion_point "namespace_scope" to generate additional classes or | ||||||
|  |      other declarations that should be placed in this scope. | ||||||
|  |      | ||||||
|  |      Note that if the line containing the insertion point begins with | ||||||
|  |      whitespace, the same whitespace will be added to every line of the | ||||||
|  |      inserted text.  This is useful for languages like Python, where | ||||||
|  |      indentation matters.  In these languages, the insertion point comment | ||||||
|  |      should be indented the same amount as any inserted code will need to be | ||||||
|  |      in order to work correctly in that context. | ||||||
|  |      | ||||||
|  |      The code generator that generates the initial file and the one which | ||||||
|  |      inserts into it must both run as part of a single invocation of protoc. | ||||||
|  |      Code generators are executed in the order in which they appear on the | ||||||
|  |      command line. | ||||||
|  |      | ||||||
|  |      If |insertion_point| is present, |name| must also be present. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     content: str = betterproto.string_field(15) | ||||||
|  |     """The file contents.""" | ||||||
|  |  | ||||||
|  |     generated_code_info: "betterproto_lib_google_protobuf.GeneratedCodeInfo" = ( | ||||||
|  |         betterproto.message_field(16) | ||||||
|  |     ) | ||||||
|  |     """ | ||||||
|  |     Information describing the file content being inserted. If an insertion | ||||||
|  |      point is used, this information will be appropriately offset and inserted | ||||||
|  |      into the code generation metadata for the generated files. | ||||||
|  |     """ | ||||||
| @@ -562,6 +562,7 @@ class FieldCompiler(MessageCompiler): | |||||||
|                 package=self.output_file.package, |                 package=self.output_file.package, | ||||||
|                 imports=self.output_file.imports, |                 imports=self.output_file.imports, | ||||||
|                 source_type=self.proto_obj.type_name, |                 source_type=self.proto_obj.type_name, | ||||||
|  |                 pydantic=self.output_file.pydantic_dataclasses, | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             raise NotImplementedError(f"Unknown type {self.proto_obj.type}") |             raise NotImplementedError(f"Unknown type {self.proto_obj.type}") | ||||||
| @@ -806,6 +807,7 @@ class ServiceMethodCompiler(ProtoContentBase): | |||||||
|             imports=self.output_file.imports, |             imports=self.output_file.imports, | ||||||
|             source_type=self.proto_obj.input_type, |             source_type=self.proto_obj.input_type, | ||||||
|             unwrap=False, |             unwrap=False, | ||||||
|  |             pydantic=self.output_file.pydantic_dataclasses, | ||||||
|         ).strip('"') |         ).strip('"') | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -834,6 +836,7 @@ class ServiceMethodCompiler(ProtoContentBase): | |||||||
|             imports=self.output_file.imports, |             imports=self.output_file.imports, | ||||||
|             source_type=self.proto_obj.output_type, |             source_type=self.proto_obj.output_type, | ||||||
|             unwrap=False, |             unwrap=False, | ||||||
|  |             pydantic=self.output_file.pydantic_dataclasses, | ||||||
|         ).strip('"') |         ).strip('"') | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|   | |||||||
| @@ -35,7 +35,48 @@ def test_reference_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_type_reference(package="", imports=imports, source_type=google_type) |     name = get_type_reference( | ||||||
|  |         package="", imports=imports, source_type=google_type, pydantic=False | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     assert name == expected_name | ||||||
|  |     assert imports.__contains__( | ||||||
|  |         expected_import | ||||||
|  |     ), f"{expected_import} not found in {imports}" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.parametrize( | ||||||
|  |     ["google_type", "expected_name", "expected_import"], | ||||||
|  |     [ | ||||||
|  |         ( | ||||||
|  |             ".google.protobuf.Empty", | ||||||
|  |             '"betterproto_lib_pydantic_google_protobuf.Empty"', | ||||||
|  |             "import betterproto.lib.pydantic.google.protobuf as betterproto_lib_pydantic_google_protobuf", | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             ".google.protobuf.Struct", | ||||||
|  |             '"betterproto_lib_pydantic_google_protobuf.Struct"', | ||||||
|  |             "import betterproto.lib.pydantic.google.protobuf as betterproto_lib_pydantic_google_protobuf", | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             ".google.protobuf.ListValue", | ||||||
|  |             '"betterproto_lib_pydantic_google_protobuf.ListValue"', | ||||||
|  |             "import betterproto.lib.pydantic.google.protobuf as betterproto_lib_pydantic_google_protobuf", | ||||||
|  |         ), | ||||||
|  |         ( | ||||||
|  |             ".google.protobuf.Value", | ||||||
|  |             '"betterproto_lib_pydantic_google_protobuf.Value"', | ||||||
|  |             "import betterproto.lib.pydantic.google.protobuf as betterproto_lib_pydantic_google_protobuf", | ||||||
|  |         ), | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  | def test_reference_google_wellknown_types_non_wrappers_pydantic( | ||||||
|  |     google_type: str, expected_name: str, expected_import: str | ||||||
|  | ): | ||||||
|  |     imports = set() | ||||||
|  |     name = get_type_reference( | ||||||
|  |         package="", imports=imports, source_type=google_type, pydantic=True | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     assert name == expected_name |     assert name == expected_name | ||||||
|     assert imports.__contains__( |     assert imports.__contains__( | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import json | import json | ||||||
|  |  | ||||||
| from betterproto.lib.google.protobuf import Struct | from betterproto.lib.google.protobuf import Struct | ||||||
|  | from betterproto.lib.pydantic.google.protobuf import Struct as StructPydantic | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_struct_roundtrip(): | def test_struct_roundtrip(): | ||||||
| @@ -22,3 +23,14 @@ def test_struct_roundtrip(): | |||||||
|     assert struct_from_json.to_dict() == data |     assert struct_from_json.to_dict() == data | ||||||
|     assert struct_from_json == struct_from_dict |     assert struct_from_json == struct_from_dict | ||||||
|     assert struct_from_json.to_json() == data_json |     assert struct_from_json.to_json() == data_json | ||||||
|  |  | ||||||
|  |     struct_pyd_from_dict = StructPydantic(fields={}).from_dict(data) | ||||||
|  |     assert struct_pyd_from_dict.fields == data | ||||||
|  |     assert struct_pyd_from_dict.to_dict() == data | ||||||
|  |     assert struct_pyd_from_dict.to_json() == data_json | ||||||
|  |  | ||||||
|  |     struct_pyd_from_dict = StructPydantic(fields={}).from_json(data_json) | ||||||
|  |     assert struct_pyd_from_dict.fields == data | ||||||
|  |     assert struct_pyd_from_dict.to_dict() == data | ||||||
|  |     assert struct_pyd_from_dict == struct_pyd_from_dict | ||||||
|  |     assert struct_pyd_from_dict.to_json() == data_json | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user