diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7da71521..93d7308a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,10 +48,6 @@ jobs: matrix: python: [3.6, 3.7, 3.8, 3.9] variant: ['', cpp] - # Note: as of 2021-02-09, there are no 3.9 python wheels for protobuf/grpc - exclude: - - python: 3.9 - variant: cpp steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.9.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf77f1a..d5243704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +### [1.19.1](https://www.github.com/googleapis/proto-plus-python/compare/v1.19.0...v1.19.1) (2021-09-29) + + +### Bug Fixes + +* ensure enums are incomparable w other enum types ([#248](https://www.github.com/googleapis/proto-plus-python/issues/248)) ([5927c14](https://www.github.com/googleapis/proto-plus-python/commit/5927c1400f400b3213c9b92e7a37c3c3a1abd681)) + ## [1.19.0](https://www.github.com/googleapis/proto-plus-python/compare/v1.18.1...v1.19.0) (2021-06-29) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..46b2a08e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,43 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6272489d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code Reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows [Google's Open Source Community +Guidelines](https://opensource.google/conduct/). diff --git a/noxfile.py b/noxfile.py index 0ed6caa7..1ca07cec 100644 --- a/noxfile.py +++ b/noxfile.py @@ -54,7 +54,7 @@ def unit(session, proto="python"): # Check if protobuf has released wheels for new python versions # https://pypi.org/project/protobuf/#files # This list will generally be shorter than 'unit' -@nox.session(python=["3.6", "3.7", "3.8"]) +@nox.session(python=["3.6", "3.7", "3.8", "3.9"]) def unitcpp(session): return unit(session, proto="cpp") diff --git a/proto/enums.py b/proto/enums.py index 1fe8746f..067d9674 100644 --- a/proto/enums.py +++ b/proto/enums.py @@ -108,7 +108,45 @@ def __new__(mcls, name, bases, attrs): class Enum(enum.IntEnum, metaclass=ProtoEnumMeta): """A enum object that also builds a protobuf enum descriptor.""" - pass + def _comparable(self, other): + # Avoid 'isinstance' to prevent other IntEnums from matching + return type(other) in (type(self), int) + + def __eq__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value == int(other) + + def __ne__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value != int(other) + + def __lt__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value < int(other) + + def __le__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value <= int(other) + + def __ge__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value >= int(other) + + def __gt__(self, other): + if not self._comparable(other): + return NotImplemented + + return self.value > int(other) class _EnumInfo: diff --git a/setup.py b/setup.py index b6bc503a..a49c3762 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ from setuptools import find_packages, setup -version = "1.19.0" +version = "1.19.1" PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/enums_test.py b/tests/enums_test.py new file mode 100644 index 00000000..6639fbf9 --- /dev/null +++ b/tests/enums_test.py @@ -0,0 +1,28 @@ +# Copyright (C) 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import proto + +__protobuf__ = proto.module(package="test.proto", manifest={"Enums",},) + + +class OneEnum(proto.Enum): + UNSPECIFIED = 0 + SOME_VALUE = 1 + + +class OtherEnum(proto.Enum): + UNSPECIFIED = 0 + APPLE = 1 + BANANA = 2 diff --git a/tests/test_enum_total_ordering.py b/tests/test_enum_total_ordering.py new file mode 100644 index 00000000..a459e6b0 --- /dev/null +++ b/tests/test_enum_total_ordering.py @@ -0,0 +1,87 @@ +# Copyright 2021, Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import enums_test + + +def test_total_ordering_w_same_enum_type(): + to_compare = enums_test.OneEnum.SOME_VALUE + + for item in enums_test.OneEnum: + if item.value < to_compare.value: + assert not to_compare == item + assert to_compare != item + assert not to_compare < item + assert not to_compare <= item + assert to_compare > item + assert to_compare >= item + elif item.value > to_compare.value: + assert not to_compare == item + assert to_compare != item + assert to_compare < item + assert to_compare <= item + assert not to_compare > item + assert not to_compare >= item + else: # item.value == to_compare.value: + assert to_compare == item + assert not to_compare != item + assert not to_compare < item + assert to_compare <= item + assert not to_compare > item + assert to_compare >= item + + +def test_total_ordering_w_other_enum_type(): + to_compare = enums_test.OneEnum.SOME_VALUE + + for item in enums_test.OtherEnum: + assert not to_compare == item + assert to_compare.SOME_VALUE != item + with pytest.raises(TypeError): + assert not to_compare < item + with pytest.raises(TypeError): + assert not to_compare <= item + with pytest.raises(TypeError): + assert not to_compare > item + with pytest.raises(TypeError): + assert not to_compare >= item + + +@pytest.mark.parametrize("int_val", range(-1, 3)) +def test_total_ordering_w_int(int_val): + to_compare = enums_test.OneEnum.SOME_VALUE + + if int_val < to_compare.value: + assert not to_compare == int_val + assert to_compare != int_val + assert not to_compare < int_val + assert not to_compare <= int_val + assert to_compare > int_val + assert to_compare >= int_val + elif int_val > to_compare.value: + assert not to_compare == int_val + assert to_compare != int_val + assert to_compare < int_val + assert to_compare <= int_val + assert not to_compare > int_val + assert not to_compare >= int_val + else: # int_val == to_compare.value: + assert to_compare == int_val + assert not to_compare != int_val + assert not to_compare < int_val + assert to_compare <= int_val + assert not to_compare > int_val + assert to_compare >= int_val