From e3cb782e5992ba306de59ba96dfb6f125720cd06 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 17 Dec 2018 13:23:57 -0800 Subject: [PATCH] Allow josepy to be accessed through acme.jose. (#6592) When working on an update to our packages in Ubuntu Xenial, @NCommander noticed that importing code through acme.jose no longer works since josepy became a separate package and remembers having to fix up some code that was using acme.jose himself. This PR should fix that problem by making all of josepy accessible through acme.jose. This is primarily beneficial to our OS package maintainers who want to avoid subtle API changes when updating packages in stable repositories. They will likely backport this change, but I figure we might as well add it ourselves to minimize divergences in our OS packages in the future and avoid problems on the off chance someone hasn't upgraded acme and was relying on this feature. --- CHANGELOG.md | 8 ++++++-- acme/acme/__init__.py | 15 +++++++++++++++ acme/acme/jose_test.py | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 acme/acme/jose_test.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c6f0eb2..c6fac76fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,13 +17,17 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). ### Fixed -* +* Older modules in the josepy library can now be accessed through acme.jose + like it could in previous versions of acme. This is only done to preserve + backwards compatibility and support for doing this with new modules in josepy + will not be added. Users of the acme library should switch to using josepy + directly if they haven't done so already. Despite us having broken lockstep, we are continuing to release new versions of all Certbot components during releases for the time being, however, the only package with changes other than its version number was: -* +* acme More details about these changes can be found on our GitHub repo. diff --git a/acme/acme/__init__.py b/acme/acme/__init__.py index e8a0b16a8..d2cb1ee06 100644 --- a/acme/acme/__init__.py +++ b/acme/acme/__init__.py @@ -10,3 +10,18 @@ supported version: `draft-ietf-acme-01`_. https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01 """ +import sys + +import josepy + +# This code exists to keep backwards compatibility with people using acme.jose +# before it became the standalone josepy package. +# +# It is based on +# https://github.com/requests/requests/blob/1278ecdf71a312dc2268f3bfc0aabfab3c006dcf/requests/packages.py + +for mod in list(sys.modules): + # This traversal is apparently necessary such that the identities are + # preserved (acme.jose.* is josepy.*) + if mod == 'josepy' or mod.startswith('josepy.'): + sys.modules['acme.' + mod.replace('josepy', 'jose', 1)] = sys.modules[mod] diff --git a/acme/acme/jose_test.py b/acme/acme/jose_test.py new file mode 100644 index 000000000..ecd483662 --- /dev/null +++ b/acme/acme/jose_test.py @@ -0,0 +1,43 @@ +"""Tests for acme.jose shim.""" +import importlib +import unittest + +class JoseTest(unittest.TestCase): + """Tests for acme.jose shim.""" + + def _test_it(self, submodule, attribute): + if submodule: + acme_jose_path = 'acme.jose.' + submodule + josepy_path = 'josepy.' + submodule + else: + acme_jose_path = 'acme.jose' + josepy_path = 'josepy' + acme_jose = importlib.import_module(acme_jose_path) + josepy = importlib.import_module(josepy_path) + + self.assertIs(acme_jose, josepy) + self.assertIs(getattr(acme_jose, attribute), getattr(josepy, attribute)) + + def test_top_level(self): + self._test_it('', 'RS512') + + def test_submodules(self): + # This test ensures that the modules in josepy that were + # available at the time it was moved into its own package are + # available under acme.jose. Backwards compatibility with new + # modules or testing code is not maintained. + mods_and_attrs = [('b64', 'b64decode',), + ('errors', 'Error',), + ('interfaces', 'JSONDeSerializable',), + ('json_util', 'Field',), + ('jwa', 'HS256',), + ('jwk', 'JWK',), + ('jws', 'JWS',), + ('util', 'ImmutableMap',),] + + for mod, attr in mods_and_attrs: + self._test_it(mod, attr) + + +if __name__ == '__main__': + unittest.main() # pragma: no cover