mypy-protobuf icon indicating copy to clipboard operation
mypy-protobuf copied to clipboard

my-protobuf-3.4.0 breaks backward compatibility by silently upgrading to protobuf-4

Open alonbl opened this issue 3 years ago • 10 comments

Hello,

protobuf-3 is still alive, especially since it is the protoc version that is available in stable ubuntu and other distros and providing the native libraries.

Starting from mypy-protobuf-3.4.0 the protobuf major was bumped, without the library major bump, hence breaking protobuf-3 installations.

protobuf-4 is a new major which is incompatible with protobuf-3, a new major per semver.

As far as I know, it is not a good practice to bump major version of breaking dependencies, without bumping the major of the package, hence instead of my-protobuf-3.4.0 it should have been my-protobuf-4.0.0, and the two y-streams should be maintained.

What do you think? Alon

Traceback (most recent call last):
  File "xxx/.tox/test-py310/bin/protoc-gen-mypy", line 5, in <module>
    from mypy_protobuf.main import main
  File "xxx/.tox/test-py310/lib/python3.10/site-packages/mypy_protobuf/main.py", line 25, in <module>
    from . import extensions_pb2
  File "xxx/.tox/test-py310/lib/python3.10/site-packages/mypy_protobuf/extensions_pb2.py", line 5, in <module>
    from google.protobuf.internal import builder as _builder
ImportError: cannot import name 'builder' from 'google.protobuf.internal' (xxx/.tox/test-py310/lib/python3.10/site-packages/google/protobuf/internal/__init__.py)
--mypy_out: protoc-gen-mypy: Plugin failed with status code 1.
Traceback (most recent call last):

alonbl avatar Oct 23 '22 09:10 alonbl

Thanks for the report!

Interestingly, protoc https://github.com/protocolbuffers/protobuf/releases - is still calling itself 3.21.x - but the python package bumped major to 4.21.x

There's some information about this in the release notes here https://github.com/protocolbuffers/protobuf/releases/tag/v21.0

I'll look into this some more.

My thinking is that mypy-protobuf can depend on whichever version of protobuf it wants as long as it generates compatible code. I'm a bit surprised by this crash, so can look into it a bit more to understand. My original thought is that mypy-protobuf depends on its version of python-protobuf, and thus internally should work properly. I'll spend some time trying to repro running a newer mypy-protobuf with an older protoc.

Would appreciate some information from your side if you can provide

protoc --version
protoc-gen-mypy --version

nipunn1313 avatar Oct 24 '22 17:10 nipunn1313

Hi,

Thanks, here are the versions:

protobuf-compiler-3.12.4-1ubuntu7
$ protoc --version
libprotoc 3.12.4
$ protoc-gen-mypy --version
mypy-protobuf 3.4.0

Regards,

alonbl avatar Oct 25 '22 18:10 alonbl

Hi. I've tried to repro and I'm unable. I did this:

mkdir testdir
cd testdir

python3 -m venv env
source env/bin/activate

pip3 install mypy-protobuf==3.4.0
env) ➜ git:(main) ✗ protoc-gen-mypy --version
mypy-protobuf 3.4.0

# Download protoc 3.12.4 from source
(env) ➜ git:(main) ✗ ~/Downloads/protobuf-3.12.4/src/protoc --version
libprotoc 3.12.4

mkdir testout/

(env) ➜ git:(main) ✗ ~/Downloads/protobuf-3.12.4/src/protoc --python_out=testout/ --mypy_out=testout/ ./duration.proto
Writing mypy to duration_pb2.pyi
# success

perhaps something else tricky is going on here? Do you have repro steps?

You mentioned semver incompatibility, however semver allows you to bump a dependency version by a major bump as long as you internalize the associated effects in your library, which is what I believe is going on here.

nipunn1313 avatar Nov 03 '22 15:11 nipunn1313

Hi,

Thank you for testing, it took me a while to doubt my sanity... took me a while to find the root cause.

We have dependency hell...

Prepare

Environment: ubuntu-22.04

$ sudo apt install protobuf-compiler
$ python3 -m venv /tmp/env.test1
$ . /tmp/env.test1/bin/activate
$ cat > test.proto << __EOF__
syntax = "proto3";

message test {
  int64 n = 1;
}
__EOF__

Default mypy-protobuf installation

We cannot use the default protobuf-4.*, as it is incopatible with ubuntu-22.04 protoc:

(env.test1) $ pip install mypy-protobuf
Collecting mypy-protobuf
  Using cached mypy_protobuf-3.4.0-py3-none-any.whl (15 kB)
Collecting protobuf>=4.21.8
  Using cached protobuf-4.21.9-cp37-abi3-manylinux2014_x86_64.whl (408 kB)
Collecting types-protobuf>=3.20.4
  Using cached types_protobuf-3.20.4.2-py3-none-any.whl (60 kB)
Installing collected packages: types-protobuf, protobuf, mypy-protobuf
Successfully installed mypy-protobuf-3.4.0 protobuf-4.21.9 types-protobuf-3.20.4.2
(env.test1) $ protoc --python_out . --mypy_out . test.proto 
Writing mypy to test_pb2.pyi
$ python3 test_pb2.py 
Traceback (most recent call last):
  File "/home/alonbl/exodigo/exodigo-python-skel/test_project/x/test_pb2.py", line 36, in <module>
    _descriptor.FieldDescriptor(
  File "/tmp/env.test1/lib/python3.10/site-packages/google/protobuf/descriptor.py", line 560, in __new__
    _message.Message._CheckCalledFromGeneratedFile()
TypeError: Descriptors cannot not be created directly.
If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc >= 3.19.0.
If you cannot immediately regenerate your protos, some other possible workarounds are:
 1. Downgrade the protobuf package to 3.20.x or lower.
 2. Set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python (but this will use pure-Python parsing and will be much slower).

More information: https://developers.google.com/protocol-buffers/docs/news/2022-05-06#python-updates
$ protoc --version
libprotoc 3.12.4

Force protobuf==3.*

With protobuf==3.* the output is supported by native libraries of ubuntu-22.04, and for some reason it also works with mypy-protobuf-3.4.0.

(env.test1) $ pip uninstall protobuf
(env.test1) $ pip install 'protobuf==3.*'
Collecting protobuf==3.*
  Using cached protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.1 MB)
Installing collected packages: protobuf
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
mypy-protobuf 3.4.0 requires protobuf>=4.21.8, but you have protobuf 3.20.3 which is incompatible.
Successfully installed protobuf-3.20.3
(env.test1) $ protoc --python_out . --mypy_out . test.proto 
Writing mypy to test_pb2.pyi
$ python3 test_pb2.py
<success>

Force protobuf<3.20

There are dependeices which force this such as tensorboard [https://pypi.org/project/tensorboard/] which has:

Requires-Dist: protobuf (<3.20,>=3.9.2)

When tox pulls everything it installs: mypy-protobuf==3.4.0,...,protobuf==3.19.6,...,tensorboard==2.10.1, there is obviosly a bug in pip as well, in tox log I see:

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
mypy-protobuf 3.4.0 requires protobuf>=4.21.8, but you have protobuf 3.19.6 which is incompatible.

When invoking manually to reproduce:

(env.test1) $ pip uninstall protobuf
$ pip3 install 'protobuf<3.20'
Collecting protobuf<3.20
  Using cached protobuf-3.19.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB)
Installing collected packages: protobuf
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
mypy-protobuf 3.4.0 requires protobuf>=4.21.8, but you have protobuf 3.19.6 which is incompatible.
Successfully installed protobuf-3.19.6
(env.test1) $ protoc --python_out . --mypy_out . test.proto 
Traceback (most recent call last):
  File "/tmp/env.test1/bin/protoc-gen-mypy", line 5, in <module>
    from mypy_protobuf.main import main
  File "/tmp/env.test1/lib/python3.10/site-packages/mypy_protobuf/main.py", line 25, in <module>
    from . import extensions_pb2
  File "/tmp/env.test1/lib/python3.10/site-packages/mypy_protobuf/extensions_pb2.py", line 5, in <module>
    from google.protobuf.internal import builder as _builder
ImportError: cannot import name 'builder' from 'google.protobuf.internal' (/tmp/env.test1/lib/python3.10/site-packages/google/protobuf/internal/__init__.py)
--mypy_out: protoc-gen-mypy: Plugin failed with status code 1.

alonbl avatar Nov 04 '22 00:11 alonbl

Hi. Does it work if you run them in their own environments? To test my hypothesis.

In your system environment.

protoc --python_out .

In your virtualenv with mypy-protobuf installed

protoc --mypy_out .

Then see if your python out runs, and your pyi files typecheck.

My hypothesis is that protoc --python_out requires that the installed version of python-protobuf exactly matches that of protoc (which is reasonable).

mypy-protobuf, which releases much less often, requires its own version of python-protobuf, however the generated pyi files would still be valid.

First I want to see if this hypothesis is valid (with your help). If so, then we can determine what to do from there.

nipunn1313 avatar Nov 26 '22 03:11 nipunn1313

A bit of a pile on, but it would be really nice to still have support for older versions of protobuf as tensorflow still only supports protobuf <4 [ref].

Not sure how difficult this would be though.

aucampia avatar Jan 11 '23 10:01 aucampia

Hi @aucampia - can you further describe what issue you're seeing?

I believe that mypy_protobuf should work by installing mypy_protobuf in a separate venv.

I think it's somewhat impossible to have mypy_protobuf work in the same venv with all possible versions of protobuf. mypy_protobuf has an internal dependency on protobuf in order to run.

The generated pyi files should be compatible with a broad variety of protobuf versions - you just need to generate them inside a venv that's more specific. Does that make sense?

If you have specific ideas on how to improve the status quo, I am interested.

nipunn1313 avatar Jan 11 '23 20:01 nipunn1313

I believe that mypy_protobuf should work by installing mypy_protobuf in a separate venv.

That is an option, at the moment I'm using poetry to manage my dev tools though and I would really like to keep doing that but I will look at some other options.

aucampia avatar Jan 12 '23 13:01 aucampia

bit of a hack I guess, but I created a tox environment for mypy-protobuf now:

# https://tox.wiki/en/latest/config.html
# https://tox.wiki/en/latest/user_guide.html
[tox]
envlist =
isolated_build = True


[testenv:tool-mypy-protobuf]
skip_install = True
base = void
deps =
    -rdevtools/requirements-mypy-protobuf.txt

And wrote two wrapper scripts:

$ cat devtools/protoc-gen-mypy
#!/usr/bin/env bash

exec "$(tox -qq -e tool-mypy-protobuf exec -- python -c 'import shutil; print(shutil.which("protoc-gen-mypy"))')" "${@}"
$ cat devtools/protoc-gen-mypy_grpc 
#!/usr/bin/env bash

exec "$(tox -qq -e tool-mypy-protobuf exec -- python -c 'import shutil; print(shutil.which("protoc-gen-mypy_grpc"))')" "${@}"

And specified the paths to these wrapper scripts in buf.gen.yaml:

version: v1
managed:
  enabled: true
plugins:
  - name: python
    out: generated/proto
  - name: mypy
    path: devtools/protoc-gen-mypy
    out: generated/proto

aucampia avatar Jan 12 '23 14:01 aucampia

If there are specific versions of protobuf that we want to work with - I can try to document exactly which versions of mypy-protobuf work properly in the same venv with those versions of mypy-protobuf and try to have the mapping be sensible.

Does that seem reasonable?

It will always work in a venv, but maybe for these special case versions of protobuf that are in LTSes - we can make sure there's a known good setup.

nipunn1313 avatar Jan 12 '23 21:01 nipunn1313