From e6d9615cab26f246d1252825adb553c1857a60df Mon Sep 17 00:00:00 2001 From: James McKinney Date: Mon, 11 Jun 2018 18:59:10 -0400 Subject: [PATCH 1/6] Add changelog, set version number --- CHANGELOG.md | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2e3461d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.0.1 (2018-06-11) + +First release. diff --git a/setup.py b/setup.py index aa985f7..aad9382 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='ocdsextensionregistry', - version='0.0.0', + version='0.0.1', author='James McKinney', author_email='james@slashpoundbang.com', url='https://github.com/open-contracting/extension_registry.py', From 68f1010d445bd89656c22adc8167d9607ad8941b Mon Sep 17 00:00:00 2001 From: James McKinney Date: Mon, 11 Jun 2018 19:11:46 -0400 Subject: [PATCH 2/6] Ignore dist --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aeb8ec2..32833f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.cache /.coverage +/dist /*.egg-info From 812ca99311bd9f17b7a32280c6dd600c8895123d Mon Sep 17 00:00:00 2001 From: James McKinney Date: Mon, 11 Jun 2018 23:21:47 -0400 Subject: [PATCH 3/6] Make `ExtensionRegistry` iterable. Remove `all()` method from `ExtensionRegistry`. --- CHANGELOG.md | 5 +++++ ocdsextensionregistry/__init__.py | 8 +++----- tests/test_extension_registry.py | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e3461d..9b78fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.0.2 (2018-06-11) + +* Make `ExtensionRegistry` iterable. +* Remove `all()` method from `ExtensionRegistry`. + ## 0.0.1 (2018-06-11) First release. diff --git a/ocdsextensionregistry/__init__.py b/ocdsextensionregistry/__init__.py index ae6df8d..b37a7d3 100644 --- a/ocdsextensionregistry/__init__.py +++ b/ocdsextensionregistry/__init__.py @@ -42,11 +42,9 @@ def filter(self, **kwargs): else: raise - def all(self): - """ - Returns all the extension versions in the registry. - """ - return self.versions + def __iter__(self): + for version in self.versions: + yield version def _resolve(self, data_or_url): parsed = urlparse(data_or_url) diff --git a/tests/test_extension_registry.py b/tests/test_extension_registry.py index 6877ddd..7067c34 100644 --- a/tests/test_extension_registry.py +++ b/tests/test_extension_registry.py @@ -97,8 +97,9 @@ def test_filter_without_extensions(): assert str(excinfo.value) == 'You must initialize ExtensionRegistry with two arguments.' -def test_all(): +def test_iter(): obj = ExtensionRegistry(extension_versions_data) - result = obj.all() + for i, version in enumerate(obj, 1): + pass - assert len(result) == 14 + assert i == 14 From 11d9158adad65bed1104b6e445ab875997a6d0c3 Mon Sep 17 00:00:00 2001 From: James McKinney Date: Tue, 12 Jun 2018 00:18:53 -0400 Subject: [PATCH 4/6] Add `get(**kwargs)` method to `ExtensionRegistry`. --- CHANGELOG.md | 3 ++- ocdsextensionregistry/__init__.py | 3 +++ tests/test_extension_registry.py | 44 +++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b78fb3..ee50bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## 0.0.2 (2018-06-11) +## 0.0.2 (2018-06-12) * Make `ExtensionRegistry` iterable. * Remove `all()` method from `ExtensionRegistry`. +* Add `get(**kwargs)` method to `ExtensionRegistry`. ## 0.0.1 (2018-06-11) diff --git a/ocdsextensionregistry/__init__.py b/ocdsextensionregistry/__init__.py index b37a7d3..4dcbe89 100644 --- a/ocdsextensionregistry/__init__.py +++ b/ocdsextensionregistry/__init__.py @@ -42,6 +42,9 @@ def filter(self, **kwargs): else: raise + def get(self, **kwargs): + return next(ver for ver in self.versions if all(getattr(ver, k) == v for k, v in kwargs.items())) + def __iter__(self): for version in self.versions: yield version diff --git a/tests/test_extension_registry.py b/tests/test_extension_registry.py index 7067c34..2e7ee6d 100644 --- a/tests/test_extension_registry.py +++ b/tests/test_extension_registry.py @@ -34,6 +34,7 @@ def test_init_with_url(): obj = ExtensionRegistry(extension_versions_url, extensions_url) assert len(obj.versions) > 50 + # Skip testing data, as the data changes over time. def test_init_with_data(): @@ -49,6 +50,7 @@ def test_init_with_data(): 'category': 'ppp', 'core': False, } + # Assume intermediate data is correctly parsed. assert obj.versions[-1].__dict__ == { 'id': 'lots', 'date': '2018-01-30', @@ -71,6 +73,7 @@ def test_init_with_versions_only(): 'base_url': 'https://raw.githubusercontent.com/open-contracting/ocds_charges_extension/master/', 'download_url': 'https://github.com/open-contracting/ocds_charges_extension/archive/master.zip', } + # Assume intermediate data is correctly parsed. assert obj.versions[-1].__dict__ == { 'id': 'lots', 'date': '2018-01-30', @@ -85,8 +88,24 @@ def test_filter(): result = obj.filter(core=True, version='v1.1.3', category='tender') assert len(result) == 2 - assert result[0].id == 'enquiries' - assert result[1].id == 'lots' + assert result[0].__dict__ == { + 'id': 'enquiries', + 'date': '2018-02-01', + 'version': 'v1.1.3', + 'base_url': 'https://raw.githubusercontent.com/open-contracting/ocds_enquiry_extension/v1.1.3/', + 'download_url': 'https://api.github.com/repos/open-contracting/ocds_enquiry_extension/zipball/v1.1.3', + 'category': 'tender', + 'core': True, + } + assert result[1].__dict__ == { + 'id': 'lots', + 'date': '2018-01-30', + 'version': 'v1.1.3', + 'base_url': 'https://raw.githubusercontent.com/open-contracting/ocds_lots_extension/v1.1.3/', + 'download_url': 'https://api.github.com/repos/open-contracting/ocds_lots_extension/zipball/v1.1.3', + 'category': 'tender', + 'core': True, + } def test_filter_without_extensions(): @@ -97,6 +116,27 @@ def test_filter_without_extensions(): assert str(excinfo.value) == 'You must initialize ExtensionRegistry with two arguments.' +def test_get(): + obj = ExtensionRegistry(extension_versions_data) + result = obj.get(id='lots', version='v1.1.3') + + assert result.__dict__ == { + 'id': 'lots', + 'date': '2018-01-30', + 'version': 'v1.1.3', + 'base_url': 'https://raw.githubusercontent.com/open-contracting/ocds_lots_extension/v1.1.3/', + 'download_url': 'https://api.github.com/repos/open-contracting/ocds_lots_extension/zipball/v1.1.3', + } + + +def test_get_no_match(): + obj = ExtensionRegistry(extension_versions_data) + with pytest.raises(StopIteration) as excinfo: + obj.get(id='nonexistent') + + assert str(excinfo.value) == '' + + def test_iter(): obj = ExtensionRegistry(extension_versions_data) for i, version in enumerate(obj, 1): From b1aa8ec6d089c9b95beb4d3ba9671b810e7d3a8c Mon Sep 17 00:00:00 2001 From: James McKinney Date: Tue, 12 Jun 2018 15:12:39 -0400 Subject: [PATCH 5/6] Add package-specific exceptions. --- CHANGELOG.md | 1 + ocdsextensionregistry/__init__.py | 33 ++++++++++++++++++++++++++----- tests/test_extension_registry.py | 18 ++++++++++++----- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee50bf0..17868fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Make `ExtensionRegistry` iterable. * Remove `all()` method from `ExtensionRegistry`. * Add `get(**kwargs)` method to `ExtensionRegistry`. +* Add package-specific exceptions. ## 0.0.1 (2018-06-11) diff --git a/ocdsextensionregistry/__init__.py b/ocdsextensionregistry/__init__.py index 4dcbe89..cc54d73 100644 --- a/ocdsextensionregistry/__init__.py +++ b/ocdsextensionregistry/__init__.py @@ -6,6 +6,18 @@ import requests +class OCDSExtensionRegistryError(Exception): + """Base class for exceptions from within this package""" + + +class DoesNotExist(OCDSExtensionRegistryError): + """Raised if an object wasn't found for the given parameters""" + + +class MissingExtensionMetadata(OCDSExtensionRegistryError): + """Raised if a method call requires extensions metadata, with which the extension registry was not initialized""" + + class ExtensionRegistry: def __init__(self, extension_versions_data, extensions_data=None): """ @@ -37,13 +49,18 @@ def filter(self, **kwargs): try: return list(filter(lambda ver: all(getattr(ver, k) == v for k, v in kwargs.items()), self.versions)) except AttributeError as e: - if "'category'" in str(e.args) or "'core'" in str(e.args): - raise Exception('You must initialize ExtensionRegistry with two arguments.') from e - else: - raise + self._handle_attribute_error(e) def get(self, **kwargs): - return next(ver for ver in self.versions if all(getattr(ver, k) == v for k, v in kwargs.items())) + """ + Returns the first extension version in the registry that matches the keyword arguments. + """ + try: + return next(ver for ver in self.versions if all(getattr(ver, k) == v for k, v in kwargs.items())) + except StopIteration: + raise DoesNotExist('Extension version matching {} does not exist.'.format(repr(kwargs))) + except AttributeError as e: + self._handle_attribute_error(e) def __iter__(self): for version in self.versions: @@ -56,6 +73,12 @@ def _resolve(self, data_or_url): else: return data_or_url + def _handle_attribute_error(self, e): + if "'category'" in str(e.args) or "'core'" in str(e.args): + raise MissingExtensionMetadata('ExtensionRegistry must be initialized with extensions data.') from e + else: + raise + class Extension: def __init__(self, data): diff --git a/tests/test_extension_registry.py b/tests/test_extension_registry.py index 2e7ee6d..dd51bf6 100644 --- a/tests/test_extension_registry.py +++ b/tests/test_extension_registry.py @@ -1,6 +1,6 @@ import pytest -from ocdsextensionregistry import ExtensionRegistry +from ocdsextensionregistry import ExtensionRegistry, DoesNotExist, MissingExtensionMetadata extensions_url = 'https://raw.githubusercontent.com/open-contracting/extension_registry/master/extensions.csv' extension_versions_url = 'https://raw.githubusercontent.com/open-contracting/extension_registry/master/extension_versions.csv' # noqa @@ -110,10 +110,10 @@ def test_filter(): def test_filter_without_extensions(): obj = ExtensionRegistry(extension_versions_data) - with pytest.raises(Exception) as excinfo: + with pytest.raises(MissingExtensionMetadata) as excinfo: obj.filter(category='tender') - assert str(excinfo.value) == 'You must initialize ExtensionRegistry with two arguments.' + assert str(excinfo.value) == 'ExtensionRegistry must be initialized with extensions data.' def test_get(): @@ -131,10 +131,18 @@ def test_get(): def test_get_no_match(): obj = ExtensionRegistry(extension_versions_data) - with pytest.raises(StopIteration) as excinfo: + with pytest.raises(DoesNotExist) as excinfo: obj.get(id='nonexistent') - assert str(excinfo.value) == '' + assert str(excinfo.value) == "Extension version matching {'id': 'nonexistent'} does not exist." + + +def test_get_without_extensions(): + obj = ExtensionRegistry(extension_versions_data) + with pytest.raises(MissingExtensionMetadata) as excinfo: + obj.get(category='tender') + + assert str(excinfo.value) == 'ExtensionRegistry must be initialized with extensions data.' def test_iter(): From 50aabe01b9e0ce21ea2c0576479fdf90bbb02905 Mon Sep 17 00:00:00 2001 From: James McKinney Date: Tue, 12 Jun 2018 18:16:37 -0400 Subject: [PATCH 6/6] Iterate the version number --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aad9382..4db9e67 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='ocdsextensionregistry', - version='0.0.1', + version='0.0.2', author='James McKinney', author_email='james@slashpoundbang.com', url='https://github.com/open-contracting/extension_registry.py',