Skip to content

Conversation

@lukasbindreiter
Copy link

Implementation of the outcome of the discussion in #339

Some notes on what this does:

  • Added bufbuild-protovalidate-protocolbuffers as a subpackage (and uv workspace member)
  • The protovalidate-python package now depends on it (see: https://docs.astral.sh/uv/concepts/projects/dependencies/#workspace-member)
  • I got rid of the gen folder entirely, since I think it causes confusion.
  • Instead the protobuf generation is split in two parts (also in the Makefile)

bufbuild-protovalidate-protocolbuffers

  • Is a new python package part of this repo now.
  • make generate-protovalidate-pypi-package generates the gencode for buf.build/bufbuild/protovalidate gencode into this new package
  • Not part of the PR yet, but this package is intended to be published to pypi
  • It utilizes the wandb approach to support multiple major protobuf runtime versions.
  • make fills in the $(PROTOVALIDATE_VERSION) from the Makefile into the pyproject.toml of the subpackage

gencode for unit tests

  • make generate-protobuf-tests generates the required gencode for the unit tests
  • it writes it into test/gen
  • for cel.expr it needs to update the import path to test.gen.cel.expr which I do a bit hacky with sed here, since I think buf doesn't allow me to specify that in python unfortunately.

With those changes for me:
make test or uv run pytest work again as expected.

@stefanvanburen what do you think of this package architecture? would appreciate an initial review before I continue further with the package build / publish step.

@CLAassistant
Copy link

CLAassistant commented Aug 28, 2025

CLA assistant check
All committers have signed the CLA.

@stefanvanburen
Copy link
Member

hey @lukasbindreiter, thanks for the PR! looks good to me from the outset.

gencode for unit tests

Appreciate you moving these over and getting them to work; I don't care for the sed trick although I know that's just the way with python & protobuf :(.

I put up an alternative approach of moving just the test dependencies to using generated SDKs in #368, thoughts? Not sure if having the index declared in pyproject.toml would cause issues for your downstream packaging (although I doubt it?).

bufbuild-protovalidate-protocolbuffers

Looks good to me so far.

  • Not part of the PR yet, but this package is intended to be published to pypi

What's the plan for versioning the package?

@lukasbindreiter
Copy link
Author

lukasbindreiter commented Aug 29, 2025

I put up an alternative approach of moving just the test dependencies to using generated SDKs in #368, thoughts?

Oh that's a great idea, didn't think of that but I like it a lot, the buf.build index is a perfect fit in this case for the dev dependencies.

if having the index declared in pyproject.toml would cause issues for your downstream packaging

Nope, that shouldn't be an issue I think.

What's the plan for versioning the package?

Since the package anyway includes multiple plugin versions, the idea was to just mirror the tag from the protofiles, e.g. the latest version would be 0.14.2

What's still missing though is the Github Action which checks if a new tagged version exists, and if so builds and pushes it to pypi.
Potentially that will make it a bit tricky though to have a dependency on it as a workspace=true member with uv, since that will always be the version that's currently checked in into git, which would require manual updates / commits as well to keep this up to date.

My idea for that is to eventually have the main protovalidate-python just depend on the pypi.org version of bufbuild-protovalidate-protocolbuffers instead, then it can also properly specify version ranges in case of breaking changes in the proto files.

@stefanvanburen
Copy link
Member

What's still missing though is the Github Action which checks if a new tagged version exists, and if so builds and pushes it to pypi.
Potentially that will make it a bit tricky though to have a dependency on it as a workspace=true member with uv, since that will always be the version that's currently checked in into git, which would require manual updates / commits as well to keep this up to date.

yep, seems reasonable. I think if we add the functionality here, we could bootstrap the package with a single workflow run & then dep on it locally, and remove the workspace member? (I'm not super familiar with uv workspaces yet, but assuming we'd want to drop that once we have the proper dep?)

@lukasbindreiter
Copy link
Author

@stefanvanburen I've now pushed a first version to Test PyPI: https://test.pypi.org/project/bufbuild-protovalidate-protocolbuffers/

And now have bootstrapped protovalidate-python by specifying a dependency on that testpypi version.
As soon as everything is ready we'll change that to point to the real pypi version.

Could you take a look at the workflow in protovalidate-gencode-pypi-sync.yaml?
It's configured as a daily CRON job, which right now just builds one specific version (v0.11.0 and pushes it to testpypi).
I want to see if that works as expected, and as soon as that is the case I'll add a script that fetches the available versions from pypi and builds all missing ones.

I'm not sure what's required for the workflow to run now, @stefanvanburen could you trigger it somehow manually as repo maintainer?

@stefanvanburen
Copy link
Member

I'm not sure what's required for the workflow to run now, @stefanvanburen could you trigger it somehow manually as repo maintainer?

Not sure if there's a way for me to run actions until they land on main, I'm afraid. But it looks close enough that we can iterate on it separately using test-pypi until it's right.

I want to see if that works as expected, and as soon as that is the case I'll add a script that fetches the available versions from pypi and builds all missing ones.

makes sense, yeah I think that's probably the last bit we want to get working here (besides the test failures). From there I think I have enough where I can probably pull out the subdirectory into a separate repo & get it working fairly quickly.

Ideally we'd publish all of the pre-existing protovalidate versions, but if that's not feasible for whatever reason we could also just publish them moving forward (since e.g. we'll only add this dep for presumably v0.15.0 of this package, depending on the latest protovalidate version, I think?).

@stefanvanburen
Copy link
Member

Lets revert df3d2b0; I don't think we need to format the generated code.

@lukasbindreiter
Copy link
Author

lukasbindreiter commented Sep 10, 2025

Lets revert df3d2b0; I don't think we need to format the generated code.

Sure, I reverted it now, and added the paths to the exclude list of ruff instead.

besides the test failures

Got everything running just now.

Not sure if there's a way for me to run actions until they land on main, I'm afraid. But it looks close enough that we can iterate on it separately using test-pypi until it's right.

@stefanvanburen Can you take another quick look at the bufbuild-protovalidate-protocolbuffers package? I added a py.typed marker, since that was a mypy issue I got when using it, but from my point of view I think its ready to publish it to the real pypi too. Because then I'd suggest as soon as I have the publishing script ready for testing it, we change the dependency of protovalidate to point to the pypi version, and can then already merge this PR if everything else looks good too.

This means for now the publishing process is manual still, but it does allow us to then test the publishing workflow against testpypi.

Ideally we'd publish all of the pre-existing protovalidate versions, but if that's not feasible for whatever reason we could also just publish them moving forward

My idea was to write a script that fetches all versions from buf and from pypi, and then does a diff and publishes all missing ones. So the first time this script runs that should then also include all historical versions.

@stefanvanburen
Copy link
Member

My idea was to write a script that fetches all versions from buf and from pypi, and then does a diff and publishes all missing ones. So the first time this script runs that should then also include all historical versions.

yep! I think that's the last bit I'd like to see in this PR, and then I can pretty much move forward and get it integrated. Would be great if you could include that, otherwise I can try to get to it in the next couple days. Otherwise, agree with next steps in terms of getting it integrated as a dependency here.

@lukasbindreiter
Copy link
Author

lukasbindreiter commented Oct 20, 2025

Hi @stefanvanburen

took me a while to get around to it, but I just now added the script:

https://github.com/bufbuild/protovalidate-python/pull/367/files#diff-0ed29b7ace1bcdaeafe361df35be414b4a7e33b1b1e2c3b4b3bb213db1379c64R1

The way I query the tags from buf is by sending a POST reques to:

https://buf.build/buf.registry.module.v1beta1.LabelService/ListLabels

with the following request data

{
    "pageSize": 100,
    "resourceRef": {"name": {"owner": "bufbuild", "module": "protovalidate"}},
    "order": "ORDER_UPDATE_TIME_DESC",
    "archiveFilter": "ARCHIVE_FILTER_UNARCHIVED_ONLY",
}

I think for a small script like that it's easier to just do that rather than including the LabelService grpc and messages as dependency here. But feel free to change that if another way of fetching the tags is preferred.

Usage of the script:

> cd bufbuild-protovalidate-protocolbuffers
> uv run sync_to_pypi.py --pypi-url https://test.pypi.org

2025-10-21 09:56:13 | INFO | Querying pypi for existing versions of bufbuild-protovalidate-protocolbuffers (on pypi https://test.pypi.org)
2025-10-21 09:56:13 | INFO | Found 1 versions on pypi: ['0.14.1']
2025-10-21 09:56:13 | INFO | Querying buf.build for existing tags of bufbuild/protovalidate
2025-10-21 09:56:14 | INFO | Found 71 tags on buf.build: ['0.1.0', '0.1.10', '0.1.11', '0.1.12', '0.1.13', '0.1.14', '0.1.15', '0.1.2', '0.1.3', '0.1.4', '0.1.5', '0.1.6', '0.1.7', '0.1.8', '0.1.9', '0.10.0', '0.10.1', '0.10.2', '0.10.3', '0.10.4', '0.10.5', '0.10.6', '0.10.7', '0.11.0', '0.11.1', '0.12.0', '0.13.0', '0.13.1', '0.13.2', '0.13.3', '0.13.4', '0.14.0', '0.14.1', '0.14.2', '0.2.0', '0.2.1', '0.2.2', '0.2.3', '0.2.4', '0.2.5', '0.2.6', '0.2.7', '0.2.8', '0.3.0', '0.3.1', '0.4.0', '0.4.1', '0.4.2', '0.4.3', '0.4.4', '0.5.0', '0.5.1', '0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6', '0.6.0', '0.6.1', '0.6.2', '0.6.3', '0.6.4', '0.7.0', '0.7.1', '0.8.0', '0.8.1', '0.8.2', '0.9.0', '1.0.0', '1.0.0-rc.1', '1.0.0-rc.2']
2025-10-21 09:56:14 | INFO | Found 1 existing versions: ['0.14.1']
2025-10-21 09:56:14 | INFO | Found 70 versions to generate: ['0.1.0', '0.1.10', '0.1.11', '0.1.12', '0.1.13', '0.1.14', '0.1.15', '0.1.2', '0.1.3', '0.1.4', '0.1.5', '0.1.6', '0.1.7', '0.1.8', '0.1.9', '0.10.0', '0.10.1', '0.10.2', '0.10.3', '0.10.4', '0.10.5', '0.10.6', '0.10.7', '0.11.0', '0.11.1', '0.12.0', '0.13.0', '0.13.1', '0.13.2', '0.13.3', '0.13.4', '0.14.0', '0.14.2', '0.2.0', '0.2.1', '0.2.2', '0.2.3', '0.2.4', '0.2.5', '0.2.6', '0.2.7', '0.2.8', '0.3.0', '0.3.1', '0.4.0', '0.4.1', '0.4.2', '0.4.3', '0.4.4', '0.5.0', '0.5.1', '0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6', '0.6.0', '0.6.1', '0.6.2', '0.6.3', '0.6.4', '0.7.0', '0.7.1', '0.8.0', '0.8.1', '0.8.2', '0.9.0', '1.0.0', '1.0.0-rc.1', '1.0.0-rc.2']
2025-10-21 09:56:14 | INFO | Generating package for version 0.1.0
warning: `VIRTUAL_ENV=/Users/lukasbindreiter/Library/Caches/uv/environments-v2/sync-to-pypi-130f735fd931e805` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
Resolved 25 packages in 10ms
      Built bufbuild-protovalidate-protocolbuffers @ file:///private/var/folders/54/
Prepared 1 package in 199ms
Uninstalled 1 package in 0.71ms
Installed 1 package in 1ms
 - bufbuild-protovalidate-protocolbuffers==0.14.0 (from file:///Users/lukasbindreiter/Documents/tilebox/protovalidate-python/bufbuild-protovalidate-protocolbuffers)
 + bufbuild-protovalidate-protocolbuffers==0.1.0 (from file:///private/var/folders/54/_ns7r5yn0j1bqrwh3hmds3w40000gn/T/tmpj4rks__d/protovalidate-python/bufbuild-protovalidate-protocolbuffers)
bufbuild-protovalidate-protocolbuffers 0.14.0 => 0.1.0
Building source distribution...
Building wheel from source distribution...
Successfully built dist/bufbuild_protovalidate_protocolbuffers-0.1.0.tar.gz
Successfully built dist/bufbuild_protovalidate_protocolbuffers-0.1.0-py3-none-any.whl
2025-10-21 09:56:17 | INFO | Generating package for version 0.1.10
warning: `VIRTUAL_ENV=/Users/lukasbindreiter/Library/Caches/uv/environments-v2/sync-to-pypi-130f735fd931e805` does not match the project environment path `.venv` and will be ignored; use `--active` to target the active environment instead
Resolved 25 packages in 9ms
      Built bufbuild-protovalidate-protocolbuffers @ file:///private/var/folders/54/
Prepared 1 package in 207ms
Uninstalled 1 package in 0.87ms
Installed 1 package in 1ms
 - bufbuild-protovalidate-protocolbuffers==0.14.0 (from file:///Users/lukasbindreiter/Documents/tilebox/protovalidate-python/bufbuild-protovalidate-protocolbuffers)
 + bufbuild-protovalidate-protocolbuffers==0.1.10 (from file:///private/var/folders/54/_ns7r5yn0j1bqrwh3hmds3w40000gn/T/tmphsjedvy5/protovalidate-python/bufbuild-protovalidate-protocolbuffers)
bufbuild-protovalidate-protocolbuffers 0.14.0 => 0.1.10
Building source distribution...
Building wheel from source distribution...
Successfully built dist/bufbuild_protovalidate_protocolbuffers-0.1.10.tar.gz
Successfully built dist/bufbuild_protovalidate_protocolbuffers-0.1.10-py3-none-any.whl
2025-10-21 09:56:21 | INFO | Generated 2 packages, stopping for now to test the sync script.

Afterwards dist contains the built packages:

dist
├── bufbuild_protovalidate_protocolbuffers-0.1.3-py3-none-any.whl
├── bufbuild_protovalidate_protocolbuffers-0.1.3.tar.gz
├── bufbuild_protovalidate_protocolbuffers-0.8.1-py3-none-any.whl
└── bufbuild_protovalidate_protocolbuffers-0.8.1.tar.gz

which the pypa/gh-action-pypi-publish@release/v1 will then hopefully publish

I've limited the script to building a maximum of 2 packages for now at once, so we can test it over time, and also to avoid rate limiting hopefully.

Tomorrow at 12:15 UTC the GH Action should run in my fork, let's see if that works as expected.

@lukasbindreiter
Copy link
Author

The sync CRON job just completed successfully, pushing the first two versions to testpypi. (I hardcoded a limit of maximum of 2 for now per run)

Here is the logs of that job:
https://github.com/lukasbindreiter/protovalidate-python/actions/runs/18749203709/job/53484015684

Here are the releases:
https://test.pypi.org/project/bufbuild-protovalidate-protocolbuffers/#history

@lukasbindreiter
Copy link
Author

@stefanvanburen quick ping for visibility, would appreciate a quick review if you have some time, so that we can get this over the finish line and hopefully merged soon, especially with the 1.0.0 release that's out now already. Thank you!

@stefanvanburen
Copy link
Member

@stefanvanburen quick ping for visibility, would appreciate a quick review if you have some time, so that we can get this over the finish line and hopefully merged soon, especially with the 1.0.0 release that's out now already. Thank you!

hey @lukasbindreiter, yep will definitely take a look as soon as I can :). Should be able to check this out by EOW or early next week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants