neobuilder6 6.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- neobuilder6-6.1.0/LICENSE +22 -0
- neobuilder6-6.1.0/PKG-INFO +231 -0
- neobuilder6-6.1.0/README.md +194 -0
- neobuilder6-6.1.0/neobuilder6/__init__.py +5 -0
- neobuilder6-6.1.0/neobuilder6/cli/__init__.py +0 -0
- neobuilder6-6.1.0/neobuilder6/cli/neobuilder.py +48 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/dataclass.jinja2 +12 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/dataclass_module.jinja2 +34 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/grpc_receiver.jinja2 +21 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/grpc_sender.jinja2 +21 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/init.jinja2 +0 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/interface.jinja2 +22 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/macros/indentclude.jinja2 +2 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/package_aliases.jinja2 +10 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/parts/_enum_map.jinja2 +1 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/parts/_interface_method.jinja2 +2 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/parts/_interface_service.jinja2 +10 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/parts/_nested_messages.jinja2 +1 -0
- neobuilder6-6.1.0/neobuilder6/data/templates/root_init.jinja2 +12 -0
- neobuilder6-6.1.0/neobuilder6/descwrap/__init__.py +2 -0
- neobuilder6-6.1.0/neobuilder6/descwrap/_field.py +188 -0
- neobuilder6-6.1.0/neobuilder6/descwrap/_method.py +57 -0
- neobuilder6-6.1.0/neobuilder6/generators/__init__.py +0 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/__init__.py +27 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/base.py +99 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/grpc_receiver.py +92 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/grpc_sender.py +65 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/grpc_server.py +54 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/implementation.py +49 -0
- neobuilder6-6.1.0/neobuilder6/generators/servicebuilders/interface.py +84 -0
- neobuilder6-6.1.0/neobuilder6/generators/symbols/__init__.py +4 -0
- neobuilder6-6.1.0/neobuilder6/generators/symbols/common.py +68 -0
- neobuilder6-6.1.0/neobuilder6/generators/symbols/dataclass.py +471 -0
- neobuilder6-6.1.0/neobuilder6/generators/symbols/modules.py +232 -0
- neobuilder6-6.1.0/neobuilder6/generators/symbols/service.py +139 -0
- neobuilder6-6.1.0/neobuilder6/neobuilder/__init__.py +455 -0
- neobuilder6-6.1.0/neobuilder6/neobuilder/render/__init__.py +66 -0
- neobuilder6-6.1.0/neobuilder6/neobuilder/render/renderer.py +37 -0
- neobuilder6-6.1.0/neobuilder6/structs/__init__.py +5 -0
- neobuilder6-6.1.0/neobuilder6/structs/_base.py +3 -0
- neobuilder6-6.1.0/neobuilder6/structs/protofile.py +20 -0
- neobuilder6-6.1.0/neobuilder6/structs/pytype.py +91 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/PKG-INFO +231 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/SOURCES.txt +53 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/dependency_links.txt +1 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/entry_points.txt +3 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/requires.txt +5 -0
- neobuilder6-6.1.0/neobuilder6.egg-info/top_level.txt +1 -0
- neobuilder6-6.1.0/pyproject.toml +65 -0
- neobuilder6-6.1.0/setup.cfg +4 -0
- neobuilder6-6.1.0/setup.py +6 -0
- neobuilder6-6.1.0/tests/test_alias_imports.py +70 -0
- neobuilder6-6.1.0/tests/test_neobuilder.py +116 -0
- neobuilder6-6.1.0/tests/test_services.py +277 -0
- neobuilder6-6.1.0/tests/test_zdataclassbuilder.py +1163 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thordur Matthiasson
|
|
4
|
+
Copyright (c) 2022-2024 CCP Games
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neobuilder6
|
|
3
|
+
Version: 6.1.0
|
|
4
|
+
Summary: Builds Neobuf Packages from Protobuf files using Protoplasm6! :D (a continuation fork of CCP Games' neobuilder)
|
|
5
|
+
Author-email: Thordur Matthiasson <doddi79@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/doddi79/neobuilder6
|
|
8
|
+
Project-URL: Documentation, https://github.com/doddi79/neobuilder6/blob/main/README.md
|
|
9
|
+
Project-URL: Repository, https://github.com/doddi79/neobuilder6.git
|
|
10
|
+
Project-URL: Issues, https://github.com/doddi79/neobuilder6/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/doddi79/neobuilder6/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: protobuf,proto,grpc,protoplasm,protoplasm6,neobuf,dataclasses,codegen,tools,utils
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Topic :: Utilities
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: protoplasm6<7,>=6.0
|
|
32
|
+
Requires-Dist: batutils<3,>=2.2
|
|
33
|
+
Requires-Dist: grpcio-tools<2,>=1.80
|
|
34
|
+
Requires-Dist: Jinja2<4,>=3.1
|
|
35
|
+
Requires-Dist: semver<4,>=3.0.2
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# NeoBuilder6
|
|
39
|
+
|
|
40
|
+
Builds Neobuf Packages from Protobuf files using Protoplasm6! :D
|
|
41
|
+
|
|
42
|
+
See the **[Public API index](docs/api-index.md)** for a task-oriented reference of
|
|
43
|
+
the CLI, the Python API and the generated code layout.
|
|
44
|
+
|
|
45
|
+
## Note
|
|
46
|
+
|
|
47
|
+
This is a fork of the [`neobuilder`](https://github.com/ccpgames/neobuilder)
|
|
48
|
+
package (v5.3.1) by CCP Games, made in order to continue its development and
|
|
49
|
+
maintenance. The package _and_ module are now called `neobuilder6` (starting
|
|
50
|
+
at version 6.0.0) and the code it generates uses
|
|
51
|
+
[`protoplasm6`](https://github.com/doddi79/protoplasm6) (the fork of
|
|
52
|
+
`protoplasm`).
|
|
53
|
+
|
|
54
|
+
## What it does
|
|
55
|
+
|
|
56
|
+
Given a directory of `*.proto` files, Neobuilder6 builds a Python package
|
|
57
|
+
("Neobuf package") containing, for each `foo.proto`:
|
|
58
|
+
|
|
59
|
+
| File | What |
|
|
60
|
+
|-------------------------|------------------------------------------------------------------------------------|
|
|
61
|
+
| `foo_pb2.py` | The plain `protoc` generated protobuf code |
|
|
62
|
+
| `foo_pb2.pyi` | Type stubs for `foo_pb2.py` so IDEs and type checkers understand it (skip with `--no-pyi`) |
|
|
63
|
+
| `foo_pb2_grpc.py` | The plain `protoc` generated gRPC code (if `foo.proto` has a `service`) |
|
|
64
|
+
| `foo_dc.py` | Python dataclasses for every message and enum, extending Protoplasm6's `DataclassBase` |
|
|
65
|
+
| `foo_api.py` | Interface classes for every `service` with plain Python methods per RPC |
|
|
66
|
+
| `foo_grpc_receiver.py` | gRPC servicers that forward calls to your implementation of the interface |
|
|
67
|
+
| `foo_grpc_sender.py` | gRPC clients that implement the interface by calling a remote service |
|
|
68
|
+
|
|
69
|
+
...along with `__init__.py` files everywhere, a root `__init__.py` with a
|
|
70
|
+
`__version__` (and the Protoplasm6 and Neobuilder6 versions used), a
|
|
71
|
+
`VERSION` file and an `__everything__.py` module that imports every module
|
|
72
|
+
(call `your_package.load_symbols()` to make sure the protobuf symbol database
|
|
73
|
+
knows every message, e.g. for `Any` unpacking).
|
|
74
|
+
|
|
75
|
+
The `__init__.py` of every directory that matches a proto package also
|
|
76
|
+
"alias imports" everything in that proto package, so e.g.
|
|
77
|
+
`from myshop.orders import *` gets you every message and enum declared with
|
|
78
|
+
`package myshop.orders;`, no matter which `.proto` file they're in (use
|
|
79
|
+
`--no-alias-imports` to skip this).
|
|
80
|
+
|
|
81
|
+
See the [Protoplasm6 README](https://github.com/doddi79/protoplasm6) for how
|
|
82
|
+
to use the generated code.
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
pip install neobuilder6
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This installs Protoplasm6, `grpcio-tools` (which provides `protoc`) and creates
|
|
91
|
+
the `neobuild` command (and its alias `neobuild6`) in Python's `Scripts`/`bin`
|
|
92
|
+
folder. Neobuilder6 requires Python 3.10+.
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
Assuming you've got a package called `sandbox` and you're storing your
|
|
97
|
+
protobuf files in a directory called `proto` (i.e. `./proto/sandbox/**.proto`)
|
|
98
|
+
and you want to spit out the build into the directory `build` just go:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
neobuild sandbox
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The resulting `./build/sandbox` package depends on `protoplasm6` (with the
|
|
105
|
+
same major version and the same or later minor version as the Neobuilder6
|
|
106
|
+
used to build it) and on a `protobuf` runtime matching the `grpcio-tools` that
|
|
107
|
+
built it (the `*_pb2.py` files check this when imported).
|
|
108
|
+
|
|
109
|
+
You can also run it from Python:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from neobuilder6.neobuilder import NeoBuilder
|
|
113
|
+
|
|
114
|
+
NeoBuilder(package='sandbox', protopath='./proto', build_root='./build').build()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Versioning your package
|
|
118
|
+
|
|
119
|
+
If you place a plain text file called `VERSION` in the root of your proto
|
|
120
|
+
package (e.g. `./proto/sandbox/VERSION` from the example above) and skip any
|
|
121
|
+
versioning parameters (`--major`/`-m` or `--patch`/`-p`) Neobuilder6 will use
|
|
122
|
+
that file for versioning.
|
|
123
|
+
|
|
124
|
+
The first line in the file should be the semantic version number (`x.y.z`,
|
|
125
|
+
and if there's a 4th part like in `x.y.z.a` it's ignored) and any following
|
|
126
|
+
lines are treated as a "commit message" that's written to the built package's
|
|
127
|
+
`VERSION` file.
|
|
128
|
+
|
|
129
|
+
Without a `VERSION` file, the version of the previous build (if found in the
|
|
130
|
+
build root) is bumped (minor by default) or `0.1.0` is used for a first build.
|
|
131
|
+
|
|
132
|
+
### Full CLI usage
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
usage: neobuild [-h] [-m | -p] [-b [BUILDROOT]] [-v] [-i | --pyi | --no-pyi]
|
|
136
|
+
[--alias-imports | --no-alias-imports] [-I INCLUDE]
|
|
137
|
+
package [protopath]
|
|
138
|
+
|
|
139
|
+
Builds neobuf packages with protoplasm6.
|
|
140
|
+
|
|
141
|
+
positional arguments:
|
|
142
|
+
package Package name
|
|
143
|
+
protopath Path to the root of the protobuf files (default="./proto")
|
|
144
|
+
|
|
145
|
+
options:
|
|
146
|
+
-h, --help show this help message and exit
|
|
147
|
+
-m, --major Bump the major version number instead of the minor
|
|
148
|
+
-p, --patch Bump the patch version number instead of the minor
|
|
149
|
+
-b, --buildroot [BUILDROOT]
|
|
150
|
+
Path to the root of the output build files (default="./build")
|
|
151
|
+
-v, --verbose Spits out DEBUG level logs
|
|
152
|
+
-i, --pyi, --no-pyi Builds *.pyi type stubs for the pb2 files as well (default=True, use --no-
|
|
153
|
+
pyi to skip)
|
|
154
|
+
--alias-imports, --no-alias-imports
|
|
155
|
+
Alias imports everything in each proto package in the __init__.py of the
|
|
156
|
+
corresponding Python package, e.g. so "from myshop.orders import *" works
|
|
157
|
+
(default=True, use --no-alias-imports to skip)
|
|
158
|
+
-I, --include INCLUDE
|
|
159
|
+
Optional additional proto paths to include (can be used multiple times)
|
|
160
|
+
|
|
161
|
+
Neobuilder6 v6.1.0 - Protoplasm6 v6.1.0
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Neobuilder6 & Protoplasm6 (the chicken and the egg)
|
|
165
|
+
|
|
166
|
+
The two packages depend on each other in a slightly odd way:
|
|
167
|
+
|
|
168
|
+
- **Neobuilder6 depends on Protoplasm6** at build time (it uses some of its
|
|
169
|
+
utilities, e.g. for type casting and method I/O types) and the code it
|
|
170
|
+
generates imports Protoplasm6 at runtime.
|
|
171
|
+
- **Protoplasm6 depends on Neobuilder6 only for its unit tests**, which use
|
|
172
|
+
Neobuilder6 to build the protos they test against. It's installed with
|
|
173
|
+
`--no-deps` there to avoid a circular install.
|
|
174
|
+
|
|
175
|
+
### Versioning
|
|
176
|
+
|
|
177
|
+
The versioning of Neobuilder6 and Protoplasm6 (major and minor versions) go
|
|
178
|
+
hand in hand; i.e. when Protoplasm6's major or minor versions bump, a new
|
|
179
|
+
Neobuilder6 should also be released with the same major and minor version,
|
|
180
|
+
even if there's no actual code change needed in Neobuilder6 to support
|
|
181
|
+
whatever changed in Protoplasm6.
|
|
182
|
+
|
|
183
|
+
Neobuilder6 releases are NOT guaranteed to be backwards compatible with older
|
|
184
|
+
versions of Protoplasm6 and trying to run Neobuilder6 built code with a
|
|
185
|
+
Protoplasm6 release whose major and/or minor versions lag behind will more
|
|
186
|
+
often than not just break. A difference in the patch version should most
|
|
187
|
+
often be fine, as should using a Protoplasm6 release that's one or more minor
|
|
188
|
+
versions ahead of the Neobuilder6 used to build code.
|
|
189
|
+
|
|
190
|
+
In order to keep this synchronous development and release simple, Neobuilder6
|
|
191
|
+
should generally NOT depend on new features added to the Protoplasm6 release
|
|
192
|
+
with the same version, so that it can build code for its corresponding
|
|
193
|
+
Protoplasm6 version without having that latest version actually installed.
|
|
194
|
+
|
|
195
|
+
### Releasing
|
|
196
|
+
|
|
197
|
+
1. Release Protoplasm6 first (its unit tests need Neobuilder6, installed with
|
|
198
|
+
`--no-deps`, e.g. straight from its `main` branch or a sibling checkout)
|
|
199
|
+
2. Then release Neobuilder6 (which can then pull the new Protoplasm6 from
|
|
200
|
+
PyPI)
|
|
201
|
+
|
|
202
|
+
See [CI/CD](#cicd) for how GitHub Actions handle this.
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
With both repos checked out side by side:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
python -m venv .venv
|
|
210
|
+
.venv/Scripts/activate # or: source .venv/bin/activate
|
|
211
|
+
pip install -e ../protoplasm6
|
|
212
|
+
pip install -e .
|
|
213
|
+
python -m unittest discover -v -f ./tests
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### CI/CD
|
|
217
|
+
|
|
218
|
+
GitHub Actions run the unit tests (on Python 3.10 to 3.14) for pushes to
|
|
219
|
+
`main` and for all pull requests, and publish to PyPI (after running the
|
|
220
|
+
tests again and checking that the release tag, e.g. `v6.1.0` or `6.1.0`,
|
|
221
|
+
matches `__version__`) whenever a GitHub release (or pre-release) is created.
|
|
222
|
+
Publishing needs a `PYPI_UPLOAD_TOKEN` repository secret.
|
|
223
|
+
|
|
224
|
+
Since the tests of each package need the other one, CI installs
|
|
225
|
+
Protoplasm6 from the branch with the **same name** in its repo if there is
|
|
226
|
+
one (so changes spanning both repos can be tested together, e.g. with a pull
|
|
227
|
+
request from the same branch name in both) or from `main` otherwise. When
|
|
228
|
+
publishing, Protoplasm6 comes from PyPI if a matching release is there
|
|
229
|
+
(otherwise from `main`).
|
|
230
|
+
|
|
231
|
+
When releasing both packages, release Protoplasm6 first, then Neobuilder6.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# NeoBuilder6
|
|
2
|
+
|
|
3
|
+
Builds Neobuf Packages from Protobuf files using Protoplasm6! :D
|
|
4
|
+
|
|
5
|
+
See the **[Public API index](docs/api-index.md)** for a task-oriented reference of
|
|
6
|
+
the CLI, the Python API and the generated code layout.
|
|
7
|
+
|
|
8
|
+
## Note
|
|
9
|
+
|
|
10
|
+
This is a fork of the [`neobuilder`](https://github.com/ccpgames/neobuilder)
|
|
11
|
+
package (v5.3.1) by CCP Games, made in order to continue its development and
|
|
12
|
+
maintenance. The package _and_ module are now called `neobuilder6` (starting
|
|
13
|
+
at version 6.0.0) and the code it generates uses
|
|
14
|
+
[`protoplasm6`](https://github.com/doddi79/protoplasm6) (the fork of
|
|
15
|
+
`protoplasm`).
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
Given a directory of `*.proto` files, Neobuilder6 builds a Python package
|
|
20
|
+
("Neobuf package") containing, for each `foo.proto`:
|
|
21
|
+
|
|
22
|
+
| File | What |
|
|
23
|
+
|-------------------------|------------------------------------------------------------------------------------|
|
|
24
|
+
| `foo_pb2.py` | The plain `protoc` generated protobuf code |
|
|
25
|
+
| `foo_pb2.pyi` | Type stubs for `foo_pb2.py` so IDEs and type checkers understand it (skip with `--no-pyi`) |
|
|
26
|
+
| `foo_pb2_grpc.py` | The plain `protoc` generated gRPC code (if `foo.proto` has a `service`) |
|
|
27
|
+
| `foo_dc.py` | Python dataclasses for every message and enum, extending Protoplasm6's `DataclassBase` |
|
|
28
|
+
| `foo_api.py` | Interface classes for every `service` with plain Python methods per RPC |
|
|
29
|
+
| `foo_grpc_receiver.py` | gRPC servicers that forward calls to your implementation of the interface |
|
|
30
|
+
| `foo_grpc_sender.py` | gRPC clients that implement the interface by calling a remote service |
|
|
31
|
+
|
|
32
|
+
...along with `__init__.py` files everywhere, a root `__init__.py` with a
|
|
33
|
+
`__version__` (and the Protoplasm6 and Neobuilder6 versions used), a
|
|
34
|
+
`VERSION` file and an `__everything__.py` module that imports every module
|
|
35
|
+
(call `your_package.load_symbols()` to make sure the protobuf symbol database
|
|
36
|
+
knows every message, e.g. for `Any` unpacking).
|
|
37
|
+
|
|
38
|
+
The `__init__.py` of every directory that matches a proto package also
|
|
39
|
+
"alias imports" everything in that proto package, so e.g.
|
|
40
|
+
`from myshop.orders import *` gets you every message and enum declared with
|
|
41
|
+
`package myshop.orders;`, no matter which `.proto` file they're in (use
|
|
42
|
+
`--no-alias-imports` to skip this).
|
|
43
|
+
|
|
44
|
+
See the [Protoplasm6 README](https://github.com/doddi79/protoplasm6) for how
|
|
45
|
+
to use the generated code.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install neobuilder6
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This installs Protoplasm6, `grpcio-tools` (which provides `protoc`) and creates
|
|
54
|
+
the `neobuild` command (and its alias `neobuild6`) in Python's `Scripts`/`bin`
|
|
55
|
+
folder. Neobuilder6 requires Python 3.10+.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Assuming you've got a package called `sandbox` and you're storing your
|
|
60
|
+
protobuf files in a directory called `proto` (i.e. `./proto/sandbox/**.proto`)
|
|
61
|
+
and you want to spit out the build into the directory `build` just go:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
neobuild sandbox
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The resulting `./build/sandbox` package depends on `protoplasm6` (with the
|
|
68
|
+
same major version and the same or later minor version as the Neobuilder6
|
|
69
|
+
used to build it) and on a `protobuf` runtime matching the `grpcio-tools` that
|
|
70
|
+
built it (the `*_pb2.py` files check this when imported).
|
|
71
|
+
|
|
72
|
+
You can also run it from Python:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from neobuilder6.neobuilder import NeoBuilder
|
|
76
|
+
|
|
77
|
+
NeoBuilder(package='sandbox', protopath='./proto', build_root='./build').build()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Versioning your package
|
|
81
|
+
|
|
82
|
+
If you place a plain text file called `VERSION` in the root of your proto
|
|
83
|
+
package (e.g. `./proto/sandbox/VERSION` from the example above) and skip any
|
|
84
|
+
versioning parameters (`--major`/`-m` or `--patch`/`-p`) Neobuilder6 will use
|
|
85
|
+
that file for versioning.
|
|
86
|
+
|
|
87
|
+
The first line in the file should be the semantic version number (`x.y.z`,
|
|
88
|
+
and if there's a 4th part like in `x.y.z.a` it's ignored) and any following
|
|
89
|
+
lines are treated as a "commit message" that's written to the built package's
|
|
90
|
+
`VERSION` file.
|
|
91
|
+
|
|
92
|
+
Without a `VERSION` file, the version of the previous build (if found in the
|
|
93
|
+
build root) is bumped (minor by default) or `0.1.0` is used for a first build.
|
|
94
|
+
|
|
95
|
+
### Full CLI usage
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
usage: neobuild [-h] [-m | -p] [-b [BUILDROOT]] [-v] [-i | --pyi | --no-pyi]
|
|
99
|
+
[--alias-imports | --no-alias-imports] [-I INCLUDE]
|
|
100
|
+
package [protopath]
|
|
101
|
+
|
|
102
|
+
Builds neobuf packages with protoplasm6.
|
|
103
|
+
|
|
104
|
+
positional arguments:
|
|
105
|
+
package Package name
|
|
106
|
+
protopath Path to the root of the protobuf files (default="./proto")
|
|
107
|
+
|
|
108
|
+
options:
|
|
109
|
+
-h, --help show this help message and exit
|
|
110
|
+
-m, --major Bump the major version number instead of the minor
|
|
111
|
+
-p, --patch Bump the patch version number instead of the minor
|
|
112
|
+
-b, --buildroot [BUILDROOT]
|
|
113
|
+
Path to the root of the output build files (default="./build")
|
|
114
|
+
-v, --verbose Spits out DEBUG level logs
|
|
115
|
+
-i, --pyi, --no-pyi Builds *.pyi type stubs for the pb2 files as well (default=True, use --no-
|
|
116
|
+
pyi to skip)
|
|
117
|
+
--alias-imports, --no-alias-imports
|
|
118
|
+
Alias imports everything in each proto package in the __init__.py of the
|
|
119
|
+
corresponding Python package, e.g. so "from myshop.orders import *" works
|
|
120
|
+
(default=True, use --no-alias-imports to skip)
|
|
121
|
+
-I, --include INCLUDE
|
|
122
|
+
Optional additional proto paths to include (can be used multiple times)
|
|
123
|
+
|
|
124
|
+
Neobuilder6 v6.1.0 - Protoplasm6 v6.1.0
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Neobuilder6 & Protoplasm6 (the chicken and the egg)
|
|
128
|
+
|
|
129
|
+
The two packages depend on each other in a slightly odd way:
|
|
130
|
+
|
|
131
|
+
- **Neobuilder6 depends on Protoplasm6** at build time (it uses some of its
|
|
132
|
+
utilities, e.g. for type casting and method I/O types) and the code it
|
|
133
|
+
generates imports Protoplasm6 at runtime.
|
|
134
|
+
- **Protoplasm6 depends on Neobuilder6 only for its unit tests**, which use
|
|
135
|
+
Neobuilder6 to build the protos they test against. It's installed with
|
|
136
|
+
`--no-deps` there to avoid a circular install.
|
|
137
|
+
|
|
138
|
+
### Versioning
|
|
139
|
+
|
|
140
|
+
The versioning of Neobuilder6 and Protoplasm6 (major and minor versions) go
|
|
141
|
+
hand in hand; i.e. when Protoplasm6's major or minor versions bump, a new
|
|
142
|
+
Neobuilder6 should also be released with the same major and minor version,
|
|
143
|
+
even if there's no actual code change needed in Neobuilder6 to support
|
|
144
|
+
whatever changed in Protoplasm6.
|
|
145
|
+
|
|
146
|
+
Neobuilder6 releases are NOT guaranteed to be backwards compatible with older
|
|
147
|
+
versions of Protoplasm6 and trying to run Neobuilder6 built code with a
|
|
148
|
+
Protoplasm6 release whose major and/or minor versions lag behind will more
|
|
149
|
+
often than not just break. A difference in the patch version should most
|
|
150
|
+
often be fine, as should using a Protoplasm6 release that's one or more minor
|
|
151
|
+
versions ahead of the Neobuilder6 used to build code.
|
|
152
|
+
|
|
153
|
+
In order to keep this synchronous development and release simple, Neobuilder6
|
|
154
|
+
should generally NOT depend on new features added to the Protoplasm6 release
|
|
155
|
+
with the same version, so that it can build code for its corresponding
|
|
156
|
+
Protoplasm6 version without having that latest version actually installed.
|
|
157
|
+
|
|
158
|
+
### Releasing
|
|
159
|
+
|
|
160
|
+
1. Release Protoplasm6 first (its unit tests need Neobuilder6, installed with
|
|
161
|
+
`--no-deps`, e.g. straight from its `main` branch or a sibling checkout)
|
|
162
|
+
2. Then release Neobuilder6 (which can then pull the new Protoplasm6 from
|
|
163
|
+
PyPI)
|
|
164
|
+
|
|
165
|
+
See [CI/CD](#cicd) for how GitHub Actions handle this.
|
|
166
|
+
|
|
167
|
+
## Development
|
|
168
|
+
|
|
169
|
+
With both repos checked out side by side:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
python -m venv .venv
|
|
173
|
+
.venv/Scripts/activate # or: source .venv/bin/activate
|
|
174
|
+
pip install -e ../protoplasm6
|
|
175
|
+
pip install -e .
|
|
176
|
+
python -m unittest discover -v -f ./tests
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### CI/CD
|
|
180
|
+
|
|
181
|
+
GitHub Actions run the unit tests (on Python 3.10 to 3.14) for pushes to
|
|
182
|
+
`main` and for all pull requests, and publish to PyPI (after running the
|
|
183
|
+
tests again and checking that the release tag, e.g. `v6.1.0` or `6.1.0`,
|
|
184
|
+
matches `__version__`) whenever a GitHub release (or pre-release) is created.
|
|
185
|
+
Publishing needs a `PYPI_UPLOAD_TOKEN` repository secret.
|
|
186
|
+
|
|
187
|
+
Since the tests of each package need the other one, CI installs
|
|
188
|
+
Protoplasm6 from the branch with the **same name** in its repo if there is
|
|
189
|
+
one (so changes spanning both repos can be tested together, e.g. with a pull
|
|
190
|
+
request from the same branch name in both) or from `main` otherwise. When
|
|
191
|
+
publishing, Protoplasm6 comes from PyPI if a matching release is there
|
|
192
|
+
(otherwise from `main`).
|
|
193
|
+
|
|
194
|
+
When releasing both packages, release Protoplasm6 first, then Neobuilder6.
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from neobuilder6.neobuilder import NeoBuilder
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
parser = argparse.ArgumentParser(prog='neobuild',
|
|
8
|
+
description='Builds neobuf packages with protoplasm6.',
|
|
9
|
+
epilog=(f'Neobuilder6 v{NeoBuilder.neobuilder_version()} - '
|
|
10
|
+
f'Protoplasm6 v{NeoBuilder.protoplasm_version()}'))
|
|
11
|
+
version_group = parser.add_mutually_exclusive_group()
|
|
12
|
+
version_group.add_argument('-m', '--major', action="store_true",
|
|
13
|
+
help='Bump the major version number instead of the minor')
|
|
14
|
+
version_group.add_argument('-p', '--patch', action="store_true",
|
|
15
|
+
help='Bump the patch version number instead of the minor')
|
|
16
|
+
parser.add_argument('package', help='Package name')
|
|
17
|
+
parser.add_argument('protopath', help='Path to the root of the protobuf files (default="./proto")',
|
|
18
|
+
default='./proto', nargs='?')
|
|
19
|
+
parser.add_argument('-b', '--buildroot', help='Path to the root of the output build files (default="./build")',
|
|
20
|
+
default='./build', nargs='?')
|
|
21
|
+
parser.add_argument('-v', '--verbose', action="store_true",
|
|
22
|
+
help='Spits out DEBUG level logs')
|
|
23
|
+
parser.add_argument('-i', '--pyi', action=argparse.BooleanOptionalAction, default=True,
|
|
24
|
+
help='Builds *.pyi type stubs for the pb2 files as well (default=True, use --no-pyi to skip)')
|
|
25
|
+
parser.add_argument('--alias-imports', action=argparse.BooleanOptionalAction, default=True,
|
|
26
|
+
help='Alias imports everything in each proto package in the __init__.py of the '
|
|
27
|
+
'corresponding Python package, e.g. so "from myshop.orders import *" works '
|
|
28
|
+
'(default=True, use --no-alias-imports to skip)')
|
|
29
|
+
parser.add_argument('-I', '--include', action='append', help="Optional additional proto paths to include (can be used multiple times)", default=[])
|
|
30
|
+
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
|
|
33
|
+
n = NeoBuilder(
|
|
34
|
+
package=args.package,
|
|
35
|
+
protopath=args.protopath,
|
|
36
|
+
build_root=args.buildroot,
|
|
37
|
+
major=args.major,
|
|
38
|
+
patch=args.patch,
|
|
39
|
+
verbose=args.verbose,
|
|
40
|
+
include_pyi=args.pyi,
|
|
41
|
+
extra_includes=args.include,
|
|
42
|
+
alias_imports=args.alias_imports,
|
|
43
|
+
)
|
|
44
|
+
n.build()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == '__main__':
|
|
48
|
+
main()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
@dataclasses.dataclass
|
|
3
|
+
class {{ class_name }}(bases.DataclassBase):
|
|
4
|
+
__proto_cls__ = pb2.{{ proto_cls_name }}
|
|
5
|
+
|
|
6
|
+
{{ indentclude('parts/_nested_messages.jinja2', 4) }}
|
|
7
|
+
|
|
8
|
+
{{ indentclude('parts/_enum_map.jinja2', 4) }}
|
|
9
|
+
|
|
10
|
+
{% for pf in field_map %}
|
|
11
|
+
{{ pf }}
|
|
12
|
+
{% endfor %}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_list %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import dataclasses
|
|
11
|
+
import datetime
|
|
12
|
+
import enum
|
|
13
|
+
import typing
|
|
14
|
+
from protoplasm6.casting import dictators
|
|
15
|
+
from protoplasm6 import plasm
|
|
16
|
+
{{ imports }}
|
|
17
|
+
{{ proto_import }}
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
{% if enum_list %}
|
|
24
|
+
{% for enum in enum_list %}
|
|
25
|
+
{{ enum }}
|
|
26
|
+
|
|
27
|
+
{% endfor %}
|
|
28
|
+
{% endif %}
|
|
29
|
+
{% if message_list %}
|
|
30
|
+
{% for msg in message_list %}
|
|
31
|
+
{{ msg }}
|
|
32
|
+
|
|
33
|
+
{% endfor -%}
|
|
34
|
+
{%- endif -%}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
__all__ = [
|
|
5
|
+
{% for a in all_list %}
|
|
6
|
+
'{{ a }}',
|
|
7
|
+
{% endfor %}
|
|
8
|
+
]
|
|
9
|
+
import datetime
|
|
10
|
+
import typing
|
|
11
|
+
from protoplasm6 import plasm
|
|
12
|
+
{{ imports }}
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from grpc import ServicerContext
|
|
15
|
+
{{ api_import }}
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
{{ services }}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Source module: {{ module_full_name }}
|
|
3
|
+
# Generated at: {{ iso_timestamp }}
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_list %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import datetime
|
|
11
|
+
import typing
|
|
12
|
+
from protoplasm6 import plasm
|
|
13
|
+
{{ imports }}
|
|
14
|
+
if typing.TYPE_CHECKING:
|
|
15
|
+
from grpc import ChannelCredentials
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
{{ services }}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
3
|
+
# Source module: {{ module_full_name }}
|
|
4
|
+
# Generated at: {{ iso_timestamp }}
|
|
5
|
+
__all__ = [
|
|
6
|
+
{% for a in all_clause %}
|
|
7
|
+
'{{ a }}',
|
|
8
|
+
{% endfor %}
|
|
9
|
+
]
|
|
10
|
+
import datetime
|
|
11
|
+
import typing
|
|
12
|
+
from protoplasm6 import plasm
|
|
13
|
+
|
|
14
|
+
{% for import_line in import_lines %}
|
|
15
|
+
{{ import_line }}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
{{ services }}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Auto-Generated file - DO NOT EDIT!
|
|
2
|
+
# Proto package alias imports, i.e. everything in the `{{ proto_package }}` proto package
|
|
3
|
+
__all__ = [
|
|
4
|
+
{% for name in names %}
|
|
5
|
+
'{{ name }}',
|
|
6
|
+
{% endfor %}
|
|
7
|
+
]
|
|
8
|
+
{% for module in modules %}
|
|
9
|
+
from {{ module }} import *
|
|
10
|
+
{% endfor %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# ENUMS GO HERE...
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% from 'macros/indentclude.jinja2' import indentclude with context %}
|
|
2
|
+
class {{ service_name }}Interface:
|
|
3
|
+
{% if is_grpc %}
|
|
4
|
+
__servicer_cls__ = {{ service_name }}GrpcServicer
|
|
5
|
+
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% for method in method_list %}
|
|
8
|
+
{{ indentclude('parts/_interface_method.jinja2', (indent_spaces + 4), method) }}
|
|
9
|
+
|
|
10
|
+
{% endfor %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# NESTED MESSAGES GO HERE...
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
__version__ = '{{ version }}'{% if version == '' %} # NO VERSION NUMBERS FOUND?!? :({% endif +%}
|
|
2
|
+
__protoplasm_version__ = '{{ protoplasm_version }}'
|
|
3
|
+
__neobuilder_version__ = '{{ neobuilder_version }}'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def load_symbols():
|
|
7
|
+
from {{ package_name }} import __everything__{% if alias_init %}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
{{ alias_init }}
|
|
12
|
+
{%- endif %}
|