From 83dab4af4f54904a1d96f1575e30c7ad3481b2fa Mon Sep 17 00:00:00 2001 From: ccp_zeulix Date: Mon, 23 Sep 2024 12:35:47 +0000 Subject: [PATCH] Version 5.2.0 - Protobuf Struct Update ### Added - Support for the `google.protobuf.Struct` message --- CHANGELOG.md | 7 +++++ README.md | 31 +++++++++++++++++++ _sandbox.py | 2 +- neobuilder/__init__.py | 6 +++- neobuilder/descwrap/_field.py | 3 ++ neobuilder/generators/symbols/dataclass.py | 17 ++++++++-- tests/res/expected/sandbox/__everything__.py | 2 ++ .../expected/sandbox/test/googlestruct_dc.py | 26 ++++++++++++++++ .../expected/sandbox/test/googlestruct_pb2.py | 27 ++++++++++++++++ .../res/proto/sandbox/test/googlestruct.proto | 10 ++++++ .../proto/sandbox/test/googlestruct.proto | 10 ++++++ tests/test_neobuilder.py | 2 +- 12 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 tests/res/expected/sandbox/test/googlestruct_dc.py create mode 100644 tests/res/expected/sandbox/test/googlestruct_pb2.py create mode 100644 tests/res/proto/sandbox/test/googlestruct.proto create mode 100644 tests/res2/proto/sandbox/test/googlestruct.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ff1ac..bd5b6d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.2.0] - 2024-09-23 + +### Added + +- Support for the `google.protobuf.Struct` message + + ## [5.1.2] - 2024-07-02 ### Fixed diff --git a/README.md b/README.md index 8f9aa90..618c712 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,37 @@ Builds Neobuf Packages from Protobuf files using Protoplasm! :D +## Super Important Info + +Neobuilder releases are NOT guaranteed to be backwards compatible with older +versions of Protoplasm and trying to run Neobuilder built code in a Protoplasm +release who's major and/or minor versions lag behind will more often than not +just break. + +A difference in the patch version should most often be fine, as should using a +Protoplasm release that's one or more minor versions ahead of the Neobuilder +used to build code. + +## Versioning + +The versioning of Neobuilder and Protoplasm (major and minor versions) go hand +in hand; i.e. when Protoplasm's major of minor versions bump, a new NeoBuilder +should also be released with the same major and minor version, even if there's +no actual code change needed in NeoBuilder to support whatever changed in +Protoplasm. + +Note that in order to simplify and ease this synchronous development, release +and versioning, the NeoBuilder package for the latest Protoplasm should +generally NOT be dependent on any new features added to that package, so that +the NeoBuilder package can be built and released FIRST and it can build code for +its corresponding Protoplasm version, withing having that latest version +actually installed. + +This is done in order to not create a circular dependency on features between +the two but instead have NeoBuilder only depend on the previous version of +Protoplasm, especially for its pre-release unit-tests, and instead allow +Protoplasm to fully depend on the latest NeoBuilder for its unit-tests. + ## Useful info Installing this package creates a command line executable called `neobuild` (or diff --git a/_sandbox.py b/_sandbox.py index 7907868..5858231 100644 --- a/_sandbox.py +++ b/_sandbox.py @@ -10,7 +10,7 @@ #) n = NeoBuilder( package='sandbox', - protopath=r'D:\Code\github\ccpgames\neobuilder\tests\res\proto', + protopath=r'D:\Code\github\ccpgames\neobuilder\tests\res2\proto', build_root='./_sandbox/build/', verbose=True, ) diff --git a/neobuilder/__init__.py b/neobuilder/__init__.py index deda00e..207eefa 100644 --- a/neobuilder/__init__.py +++ b/neobuilder/__init__.py @@ -1 +1,5 @@ -__version__ = '5.1.2' +__version__ = '5.2.0-rc.1' + +__author__ = 'Thordur Matthiasson ' +__license__ = 'MIT License' +__copyright__ = 'Copyright 2019-2024 - CCP Games ehf' diff --git a/neobuilder/descwrap/_field.py b/neobuilder/descwrap/_field.py index 4eca3c4..626e08a 100644 --- a/neobuilder/descwrap/_field.py +++ b/neobuilder/descwrap/_field.py @@ -27,6 +27,7 @@ class FieldKind(enum.IntFlag): SPECIAL_DURATION = 0x0800 SPECIAL_ANY = 0x1000 SPECIAL_EMPTY = 0x2000 + SPECIAL_STRUCT = 0x4000 SPECIAL_KEYWORD = 0x10000000 @@ -124,6 +125,8 @@ def _check_field_kind(self): self.kind |= FieldKind.SPECIAL_ANY elif self.value_msg.full_name == 'google.protobuf.Empty': self.kind |= FieldKind.SPECIAL_EMPTY + elif self.value_msg.full_name == 'google.protobuf.Struct': + self.kind |= FieldKind.SPECIAL_STRUCT else: self.kind |= FieldKind.DATA_MESSAGE elif self.value_field.type == ProtoType.ENUM: # Enum diff --git a/neobuilder/generators/symbols/dataclass.py b/neobuilder/generators/symbols/dataclass.py index fd951ec..7b6fb21 100644 --- a/neobuilder/generators/symbols/dataclass.py +++ b/neobuilder/generators/symbols/dataclass.py @@ -226,7 +226,9 @@ def render_arg_py(self, used_in_file: json_format.descriptor.FileDescriptor = No return f'{self.py_name()}: {self.get_type_hint(used_in_file)} = None' def get_field_options(self): - if self.is_map(): + if self.is_struct(): + return f"default_factory=dict, metadata={{{self.get_metadata()}}}" + elif self.is_map(): return f"default_factory=dict, metadata={{{self.get_metadata()}}}" elif self.is_list(): return f"default_factory=list, metadata={{{self.get_metadata()}}}" @@ -277,7 +279,9 @@ def get_py_type_name(self, used_in_file: json_format.descriptor.FileDescriptor = return self.field_descriptor.py_type.name def get_type_hint(self, used_in_file: json_format.descriptor.FileDescriptor = None) -> str: - if self.is_map(): + if self.is_struct(): + return f'Dict[str, Any]' + elif self.is_map(): return f'Dict[{self.field_descriptor.key_py_type.name}, {self.get_py_type_name(used_in_file)}]' elif self.is_list(): return f'List[{self.get_py_type_name(used_in_file)}]' @@ -300,7 +304,9 @@ def get_metadata(self): elif self.is_list(): buf.append("'is_list': True") - if self.is_message(): + if self.is_struct(): + buf.append("'is_struct': True") + elif self.is_message(): buf.append("'is_obj': True") elif self.is_enum(): buf.append("'is_enum': True") @@ -325,6 +331,8 @@ def get_dictator_name(self): return 'EnumDictator' if self.is_long(): return 'LongDictator' + if self.is_struct(): + return 'StructDictator' return 'BaseDictator' def is_long(self): @@ -333,6 +341,9 @@ def is_long(self): def is_timestamp(self): return bool(self.field_descriptor.kind & FieldKind.SPECIAL_TIMESTAMP) + def is_struct(self): + return bool(self.field_descriptor.kind & FieldKind.SPECIAL_STRUCT) + def is_duration(self): return bool(self.field_descriptor.kind & FieldKind.SPECIAL_DURATION) diff --git a/tests/res/expected/sandbox/__everything__.py b/tests/res/expected/sandbox/__everything__.py index 0b62191..2cde883 100644 --- a/tests/res/expected/sandbox/__everything__.py +++ b/tests/res/expected/sandbox/__everything__.py @@ -14,6 +14,8 @@ import sandbox.test.delta_pb2 import sandbox.test.enums_dc import sandbox.test.enums_pb2 +import sandbox.test.googlestruct_dc +import sandbox.test.googlestruct_pb2 import sandbox.test.illnamedfields_dc import sandbox.test.illnamedfields_pb2 import sandbox.test.illnamedservice_api diff --git a/tests/res/expected/sandbox/test/googlestruct_dc.py b/tests/res/expected/sandbox/test/googlestruct_dc.py new file mode 100644 index 0000000..5fd739d --- /dev/null +++ b/tests/res/expected/sandbox/test/googlestruct_dc.py @@ -0,0 +1,26 @@ +# md5: 7b314132a19d0dc0845666a4261161ed +# Auto-Generated file - DO NOT EDIT! +# Source module: sandbox.test.googlestruct_pb2 +# Generated at: 2024-09-19T12:21:16.292717 +from __future__ import annotations +__all__ = [ + 'StructMessage', +] +import dataclasses +import datetime +import enum +from typing import * +from protoplasm.casting import dictators +from protoplasm import plasm + +from sandbox.test import googlestruct_pb2 as pb2 + +import logging +log = logging.getLogger(__name__) + + +@dataclasses.dataclass +class StructMessage(plasm.DataclassBase): + __proto_cls__ = pb2.StructMessage + my_struct: Dict[str, Any] = dataclasses.field(default_factory=dict, metadata={'dictator': dictators.StructDictator, 'is_struct': True}) + diff --git a/tests/res/expected/sandbox/test/googlestruct_pb2.py b/tests/res/expected/sandbox/test/googlestruct_pb2.py new file mode 100644 index 0000000..57d10ba --- /dev/null +++ b/tests/res/expected/sandbox/test/googlestruct_pb2.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: sandbox/test/googlestruct.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fsandbox/test/googlestruct.proto\x12\x0csandbox.test\x1a\x1cgoogle/protobuf/struct.proto\";\n\rStructMessage\x12*\n\tmy_struct\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Structb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'sandbox.test.googlestruct_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_STRUCTMESSAGE']._serialized_start=79 + _globals['_STRUCTMESSAGE']._serialized_end=138 +# @@protoc_insertion_point(module_scope) diff --git a/tests/res/proto/sandbox/test/googlestruct.proto b/tests/res/proto/sandbox/test/googlestruct.proto new file mode 100644 index 0000000..4830578 --- /dev/null +++ b/tests/res/proto/sandbox/test/googlestruct.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package sandbox.test; + +import "google/protobuf/struct.proto"; + + +message StructMessage { + google.protobuf.Struct my_struct = 1; +} diff --git a/tests/res2/proto/sandbox/test/googlestruct.proto b/tests/res2/proto/sandbox/test/googlestruct.proto new file mode 100644 index 0000000..4830578 --- /dev/null +++ b/tests/res2/proto/sandbox/test/googlestruct.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package sandbox.test; + +import "google/protobuf/struct.proto"; + + +message StructMessage { + google.protobuf.Struct my_struct = 1; +} diff --git a/tests/test_neobuilder.py b/tests/test_neobuilder.py index 242446e..eb39c26 100644 --- a/tests/test_neobuilder.py +++ b/tests/test_neobuilder.py @@ -15,7 +15,7 @@ BUILD_ROOT = os.path.join(HERE, 'res', 'build') EXPECTED_ROOT = os.path.join(HERE, 'res', 'expected') -EXPECTED_NUMBER_OF_FILES_CHECKED = 38 +EXPECTED_NUMBER_OF_FILES_CHECKED = 39 from neobuilder import __version__ as neobuilder_version from protoplasm import __version__ as protoplasm_version