From b68f1063f574b6610bbe7fac18de171d7acb73fc Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Wed, 28 Aug 2019 10:08:26 -0400 Subject: [PATCH 01/16] Added search_distributor method to client Client has a method to search distributors as well. It will return a Page with Distributor objects. Added the field repo_type to Distributor object and updated the schema to validate it. --- pubtools/pulplib/_impl/client/client.py | 35 ++++++++++++++++--- pubtools/pulplib/_impl/model/distributor.py | 3 ++ pubtools/pulplib/_impl/schema/repository.yaml | 3 ++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/pubtools/pulplib/_impl/client/client.py b/pubtools/pulplib/_impl/client/client.py index 3de9e083..46397f19 100644 --- a/pubtools/pulplib/_impl/client/client.py +++ b/pubtools/pulplib/_impl/client/client.py @@ -10,7 +10,7 @@ from ..page import Page from ..criteria import Criteria -from ..model import Repository +from ..model import Repository, Distributor from .search import filters_for_criteria from .errors import PulpException from .poller import TaskPoller @@ -165,20 +165,45 @@ def search_repository(self, criteria=None): Each page will contain a collection of :class:`~pubtools.pulplib.Repository` objects. """ + search_options = {"distributors": True} + return self._search( + Repository, "repositories", criteria=criteria, search_options=search_options + ) + + def search_distributor(self, criteria=None): + """Search the distributors matching the given criteria + + Args: + criteria (:class:`~pubtools.pulplib.Criteria`) + A criteria object used for this search. + If None, search for all repositories. + + Returns: + Future[:class:`~pubtools.pulplib.Page`] + A future representing the first page of results. + + Each page will contain a collection of + :class:`~pubtools.pulplib.Distributor` objects. + """ + return self._search(Distributor, "distributors", criteria=criteria) + + def _search(self, return_type, resource_type, criteria=None, search_options=None): pulp_crit = { "skip": 0, "limit": self._PAGE_SIZE, - "filters": filters_for_criteria(criteria, Repository), + "filters": filters_for_criteria(criteria, return_type), } - search = {"criteria": pulp_crit, "distributors": True} + search = {"criteria": pulp_crit} + if search_options: + search.update(search_options) - response_f = self._do_search("repositories", search) + response_f = self._do_search(resource_type, search) # When this request is resolved, we'll have the first page of data. # We'll need to convert that into a page and also keep going with # the search if there's more to be done. return f_map( - response_f, lambda data: self._handle_page(Repository, search, data) + response_f, lambda data: self._handle_page(return_type, search, data) ) def _do_upload_file(self, upload_id, file_obj, name): diff --git a/pubtools/pulplib/_impl/model/distributor.py b/pubtools/pulplib/_impl/model/distributor.py index 804f82c0..1fcf6744 100644 --- a/pubtools/pulplib/_impl/model/distributor.py +++ b/pubtools/pulplib/_impl/model/distributor.py @@ -26,6 +26,9 @@ class Distributor(PulpObject): of type `yum_distributor` may be used to create yum repositories. """ + repo_id = pulp_attrib(type=str, default=None, pulp_field="repo_id") + """The :class:`pubtools.pulplib.Repository` ID this distributor is attached to.""" + last_publish = pulp_attrib( default=None, type=datetime.datetime, pulp_field="last_publish" ) diff --git a/pubtools/pulplib/_impl/schema/repository.yaml b/pubtools/pulplib/_impl/schema/repository.yaml index 986d0e4b..e13a733b 100644 --- a/pubtools/pulplib/_impl/schema/repository.yaml +++ b/pubtools/pulplib/_impl/schema/repository.yaml @@ -17,6 +17,9 @@ definitions: distributor_type_id: # String ID of distributor's type, e.g. "yum_distributor" type: string + repo_id: + # String ID of the repository the distributor is attached + type: string config: # Config dict for this distributor, different per distributor type. # We won't mandate which config keys are used with each distributor, From e27d8ba79308cd3d1ac3a9ea04ee1ce56116c7cf Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 29 Aug 2019 13:39:09 -0400 Subject: [PATCH 02/16] Added tests and fake client for search_distributor --- pubtools/pulplib/_impl/fake/client.py | 30 ++++++++++++++++++++++----- tests/client/test_client.py | 19 ++++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/pubtools/pulplib/_impl/fake/client.py b/pubtools/pulplib/_impl/fake/client.py index ae44d8cb..c26b017e 100644 --- a/pubtools/pulplib/_impl/fake/client.py +++ b/pubtools/pulplib/_impl/fake/client.py @@ -8,7 +8,7 @@ import six from more_executors.futures import f_return, f_return_error, f_flat_map -from pubtools.pulplib import Page, PulpException, Criteria, Task, Repository +from pubtools.pulplib import Page, PulpException, Criteria, Task, Repository, Distributor from pubtools.pulplib._impl.client.search import filters_for_criteria from .. import compat_attr as attr @@ -57,14 +57,34 @@ def search_repository(self, criteria=None): # callers should not make any assumption about the order of returned # values. Encourage that by returning output in unpredictable order random.shuffle(repos) + return self._prepare_pages(repos) - # Split it into pages + def search_distributor(self, criteria=None): + criteria = criteria or Criteria.true() + distributors = [] + + filters_for_criteria(criteria, Distributor) + + try: + for repo in self._repositories: + for distributor in repo.distributors: + if match_object(criteria, distributor): + distributors.append(distributor) + except Exception as ex: # pylint: disable=broad-except + return f_return_error(ex) + + random.shuffle(distributors) + return self._prepare_pages(distributors) + + def _prepare_pages(self, resource_list): + # Split resource_list into pages + # resource_list: list of objects that paginated page_data = [] current_page_data = [] - while repos: - next_elem = repos.pop() + while resource_list: + next_elem = resource_list.pop() current_page_data.append(next_elem) - if len(current_page_data) == self._PAGE_SIZE and repos: + if len(current_page_data) == self._PAGE_SIZE and resource_list: page_data.append(current_page_data) current_page_data = [] diff --git a/tests/client/test_client.py b/tests/client/test_client.py index f0e46a75..9c45d521 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -1,7 +1,7 @@ import logging import pytest import requests_mock -from pubtools.pulplib import Client, Criteria, Repository, PulpException +from pubtools.pulplib import Client, Criteria, Repository, PulpException, Distributor def test_can_construct(requests_mocker): @@ -44,6 +44,23 @@ def test_can_search(client, requests_mocker): assert requests_mocker.call_count == 1 +def test_can_search_distributor(client, requests_mocker): + """search_distributor issues distributors/search POST request as expected.""" + requests_mocker.post( + "https://pulp.example.com/pulp/api/v2/distributors/search/", + json=[{"id": "yum_distributor", "distributor_type_id": "yum_distributor", "repo_id": "test_rpm"}, + {"id": "cdn_distributor", "distributor_type_id": "rpm_rsync_distributor"} + ] + ) + + distributors_f = client.search_distributor() + distributors = [dist for dist in distributors_f.result().as_iter()] + # distributor objects are returned + assert sorted(distributors) == [Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor"), Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="test_rpm")] + # api is called once + assert requests_mocker.call_count == 1 + + def test_search_retries(client, requests_mocker, caplog): """search_repository retries operations on failure.""" logging.getLogger().setLevel(logging.WARNING) From 8385fdc2f4c83d828e22454e6f80b66bc8377c9f Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 29 Aug 2019 13:43:37 -0400 Subject: [PATCH 03/16] Added less_than matcher for the criteria less_than matcher returns the objects whose field values is less than the matcher value. Dates can also be compared with this matcher. It requires the dates to be in a specific ISO format so as to identify it as a date and generate the criteria accordingly. Also, the fake client is updated for this new matcher. Corresponding tests were added as well. --- pubtools/pulplib/_impl/client/search.py | 10 +++++ pubtools/pulplib/_impl/criteria.py | 30 +++++++++++++++ pubtools/pulplib/_impl/fake/match.py | 17 +++++++++ pubtools/pulplib/_impl/util.py | 11 ++++++ tests/client/test_search.py | 11 ++++++ tests/fake/test_fake_search.py | 50 +++++++++++++++++++++++-- 6 files changed, 125 insertions(+), 4 deletions(-) diff --git a/pubtools/pulplib/_impl/client/search.py b/pubtools/pulplib/_impl/client/search.py index ab84e92b..6d3e76a8 100644 --- a/pubtools/pulplib/_impl/client/search.py +++ b/pubtools/pulplib/_impl/client/search.py @@ -7,10 +7,12 @@ EqMatcher, InMatcher, ExistsMatcher, + LessThanMatcher ) from pubtools.pulplib._impl import compat_attr as attr from pubtools.pulplib._impl.model.attr import PULP2_FIELD, PY_PULP2_CONVERTER +from pubtools.pulplib._impl.util import _is_iso_date_format def all_subclasses(klass): @@ -87,4 +89,12 @@ def field_match(to_match): if isinstance(to_match, ExistsMatcher): return {"$exists": True} + if isinstance(to_match, LessThanMatcher): + value = to_match._value + # only value of the format YYYY-mm-ddTHH:MM:SSZ is + # treated as date + if _is_iso_date_format(value): + return {"$lt": {"$date": value}} + return {"$lt": value} + raise TypeError("Not a matcher: %s" % repr(to_match)) diff --git a/pubtools/pulplib/_impl/criteria.py b/pubtools/pulplib/_impl/criteria.py index 8eeb3886..427bc3f0 100644 --- a/pubtools/pulplib/_impl/criteria.py +++ b/pubtools/pulplib/_impl/criteria.py @@ -219,6 +219,28 @@ def in_(cls, values): """ return InMatcher(values) + @classmethod + def less_than(cls, value): + """ + Returns a matcher for a field whose value is less than the specified input + value + + Arguments: + values (object) + An object to match against the field + + Example: + .. code-block:: python + + # would match where last_publish is before "2019-08-27T00:00:00Z + # date is reqired to be in this format + crit = Criteria.with_field( + 'last_publish', + Matcher.less_than("2019-08-27T00:00:00Z") + ) + """ + return LessThanMatcher(value) + def _map(self, _fn): # Internal-only: return self with matched value mapped through # the given function. Intended to be overridden in subclasses @@ -265,6 +287,14 @@ class ExistsMatcher(Matcher): pass +@attr.s +class LessThanMatcher(Matcher): + _value = attr.ib() + + def _map(self, fn): + return attr.evolve(self, value=fn(self._value)) + + def coerce_to_matcher(value): if isinstance(value, Matcher): return value diff --git a/pubtools/pulplib/_impl/fake/match.py b/pubtools/pulplib/_impl/fake/match.py index dc2190fb..0531f2bc 100644 --- a/pubtools/pulplib/_impl/fake/match.py +++ b/pubtools/pulplib/_impl/fake/match.py @@ -13,8 +13,10 @@ InMatcher, RegexMatcher, ExistsMatcher, + LessThanMatcher ) from pubtools.pulplib._impl.model.common import PULP2_FIELD +from pubtools.pulplib._impl.util import _is_iso_date_format ABSENT = object() CLASS_MATCHERS = [] @@ -124,6 +126,21 @@ def match_in(matcher, field, obj): return False +@visit(LessThanMatcher) +def match_less(matcher, field, obj): + # for datetime.dateime fields, obj_value is returned as pulp_value(iso format) + # if the model filed name is same as pulp filed name else returns a obj_value + # as datetime.datetime object + obj_value = get_field(field, obj) + if _is_iso_date_format(obj_value): + obj_value = datetime.datetime.strptime(obj_value, "%Y-%m-%dT%H:%M:%SZ") + matcher_value = matcher._value + if _is_iso_date_format(matcher_value): + matcher_value = datetime.datetime.strptime(matcher_value, "%Y-%m-%dT%H:%M:%SZ") + + return obj_value < matcher_value + + def pulp_value(pulp_field, obj): # Given a Pulp 'field' name and a PulpObject instance, # returns the value on the object corresponding to that Pulp field. diff --git a/pubtools/pulplib/_impl/util.py b/pubtools/pulplib/_impl/util.py index 026a1762..7f5646f9 100644 --- a/pubtools/pulplib/_impl/util.py +++ b/pubtools/pulplib/_impl/util.py @@ -1,3 +1,5 @@ +from datetime import datetime + ABSENT = object() @@ -23,3 +25,12 @@ def lookup(value, key, default=ABSENT): # it's present return value + +def _is_iso_date_format(str_date): + # checks if the input is of the format YYYY-mm-ddTHH:MM:SSZ + # to qualify as a date type field value + try: + datetime.strptime(str_date, "%Y-%m-%dT%H:%M:%SZ") + return True + except (ValueError, TypeError): + return False diff --git a/tests/client/test_search.py b/tests/client/test_search.py index 3c5cc87a..528f3f49 100644 --- a/tests/client/test_search.py +++ b/tests/client/test_search.py @@ -58,6 +58,17 @@ def test_field_regex_criteria(): ) == {"some.field": {"$regex": "abc"}} +def test_field_less_than_criteria(): + """with_filed with less_than is translated as expected for + date and non-date types + """ + c1 = Criteria.with_field("num_field", Matcher.less_than("5")) + c2= Criteria.with_field("date_field", Matcher.less_than("2019-08-27T00:00:00Z")) + + assert filters_for_criteria(c1) == {"num_field": {"$lt": "5"}} + assert filters_for_criteria(c2) == {"date_field": {"$lt": {"$date": "2019-08-27T00:00:00Z"}}} + + def test_non_matcher(): """field_match raises if invoked with something other than a matcher. diff --git a/tests/fake/test_fake_search.py b/tests/fake/test_fake_search.py index c0aab8a7..550b3d2d 100644 --- a/tests/fake/test_fake_search.py +++ b/tests/fake/test_fake_search.py @@ -2,7 +2,7 @@ import pytest -from pubtools.pulplib import FakeController, Repository, Criteria, Matcher +from pubtools.pulplib import FakeController, Repository, Criteria, Matcher, Distributor def test_can_search_id(): @@ -136,7 +136,8 @@ def test_search_null_and(): """Search with an empty AND gives an error.""" controller = FakeController() - repo1 = Repository(id="repo1") + dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1") + repo1 = Repository(id="repo1", distributors=[dist1]) controller.insert_repository(repo1) @@ -145,6 +146,9 @@ def test_search_null_and(): assert "Invalid AND in search query" in str( client.search_repository(crit).exception() ) + assert "Invalid AND in search query" in str( + client.search_distributor(crit).exception() + ) def test_search_null_or(): @@ -172,10 +176,14 @@ def test_search_bad_criteria(): client = controller.client - with pytest.raises(Exception) as exc: + with pytest.raises(Exception) as repo_exc: client.search_repository("not a valid criteria") - assert "Not a criteria" in str(exc.value) + with pytest.raises(Exception) as dist_exc: + client.search_distributor("invalid criteria") + + assert "Not a criteria" in str(repo_exc.value) + assert "Not a criteria" in str(dist_exc.value) def test_search_created_timestamp(): @@ -312,3 +320,37 @@ def test_search_paginates(): # All repos should have been found assert sorted(found_repos) == sorted(repos) + +def test_search_distributor(): + controller = FakeController() + + dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1") + dist2 = Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor", repo_id="repo1") + repo1 = Repository(id="repo1", distributors=(dist1, dist2)) + + controller.insert_repository(repo1) + + client = controller.client + crit = Criteria.true() + + found = client.search_distributor(crit).result().data + + assert sorted(found) == [dist2, dist1] + +def test_search_mapped_field_less_than(): + controller = FakeController() + + dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1", + last_publish=datetime.datetime(2019, 8, 23, 2, 5, 0, tzinfo=None)) + dist2 = Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor", repo_id="repo1", + last_publish=datetime.datetime(2019, 8, 27, 2, 5, 0, tzinfo=None)) + repo1 = Repository(id="repo1", distributors=(dist1, dist2)) + + controller.insert_repository(repo1) + + client = controller.client + crit = Criteria.with_field("last_publish", Matcher.less_than("2019-08-24T00:00:00Z")) + + found = client.search_distributor(crit).result().data + + assert found == [dist1] From 24d4ffe4c40cc4057c2297f5fbddb97bc3bb66c5 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 29 Aug 2019 14:12:58 -0400 Subject: [PATCH 04/16] Updated CHANGELOG.md for search_distributor and Matcher.less_than --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index accd3547..f65ce988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -n/a +### Added +- A `search_distributor` API to search distributors on defined `Criteria` +- `Matcher.less_than()` matcher to find the results with fields less than + the given value ## [1.3.0] - 2019-08-15 From 3ad0b0edf6ea48e63236139f5b59f09ee50c3067 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 29 Aug 2019 14:20:51 -0400 Subject: [PATCH 05/16] Code formatted with Black --- pubtools/pulplib/_impl/client/search.py | 2 +- pubtools/pulplib/_impl/fake/client.py | 9 ++++++- pubtools/pulplib/_impl/fake/match.py | 2 +- pubtools/pulplib/_impl/util.py | 1 + tests/client/test_client.py | 18 ++++++++++--- tests/client/test_search.py | 6 +++-- tests/fake/test_fake_search.py | 34 +++++++++++++++++++------ 7 files changed, 55 insertions(+), 17 deletions(-) diff --git a/pubtools/pulplib/_impl/client/search.py b/pubtools/pulplib/_impl/client/search.py index 6d3e76a8..35fa5081 100644 --- a/pubtools/pulplib/_impl/client/search.py +++ b/pubtools/pulplib/_impl/client/search.py @@ -7,7 +7,7 @@ EqMatcher, InMatcher, ExistsMatcher, - LessThanMatcher + LessThanMatcher, ) from pubtools.pulplib._impl import compat_attr as attr diff --git a/pubtools/pulplib/_impl/fake/client.py b/pubtools/pulplib/_impl/fake/client.py index c26b017e..2e56099d 100644 --- a/pubtools/pulplib/_impl/fake/client.py +++ b/pubtools/pulplib/_impl/fake/client.py @@ -8,7 +8,14 @@ import six from more_executors.futures import f_return, f_return_error, f_flat_map -from pubtools.pulplib import Page, PulpException, Criteria, Task, Repository, Distributor +from pubtools.pulplib import ( + Page, + PulpException, + Criteria, + Task, + Repository, + Distributor, +) from pubtools.pulplib._impl.client.search import filters_for_criteria from .. import compat_attr as attr diff --git a/pubtools/pulplib/_impl/fake/match.py b/pubtools/pulplib/_impl/fake/match.py index 0531f2bc..e790b0e6 100644 --- a/pubtools/pulplib/_impl/fake/match.py +++ b/pubtools/pulplib/_impl/fake/match.py @@ -13,7 +13,7 @@ InMatcher, RegexMatcher, ExistsMatcher, - LessThanMatcher + LessThanMatcher, ) from pubtools.pulplib._impl.model.common import PULP2_FIELD from pubtools.pulplib._impl.util import _is_iso_date_format diff --git a/pubtools/pulplib/_impl/util.py b/pubtools/pulplib/_impl/util.py index 7f5646f9..efa80cea 100644 --- a/pubtools/pulplib/_impl/util.py +++ b/pubtools/pulplib/_impl/util.py @@ -26,6 +26,7 @@ def lookup(value, key, default=ABSENT): # it's present return value + def _is_iso_date_format(str_date): # checks if the input is of the format YYYY-mm-ddTHH:MM:SSZ # to qualify as a date type field value diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 9c45d521..6780147a 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -48,15 +48,25 @@ def test_can_search_distributor(client, requests_mocker): """search_distributor issues distributors/search POST request as expected.""" requests_mocker.post( "https://pulp.example.com/pulp/api/v2/distributors/search/", - json=[{"id": "yum_distributor", "distributor_type_id": "yum_distributor", "repo_id": "test_rpm"}, - {"id": "cdn_distributor", "distributor_type_id": "rpm_rsync_distributor"} - ] + json=[ + { + "id": "yum_distributor", + "distributor_type_id": "yum_distributor", + "repo_id": "test_rpm", + }, + {"id": "cdn_distributor", "distributor_type_id": "rpm_rsync_distributor"}, + ], ) distributors_f = client.search_distributor() distributors = [dist for dist in distributors_f.result().as_iter()] # distributor objects are returned - assert sorted(distributors) == [Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor"), Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="test_rpm")] + assert sorted(distributors) == [ + Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor"), + Distributor( + id="yum_distributor", type_id="yum_distributor", repo_id="test_rpm" + ), + ] # api is called once assert requests_mocker.call_count == 1 diff --git a/tests/client/test_search.py b/tests/client/test_search.py index 528f3f49..e933a009 100644 --- a/tests/client/test_search.py +++ b/tests/client/test_search.py @@ -63,10 +63,12 @@ def test_field_less_than_criteria(): date and non-date types """ c1 = Criteria.with_field("num_field", Matcher.less_than("5")) - c2= Criteria.with_field("date_field", Matcher.less_than("2019-08-27T00:00:00Z")) + c2 = Criteria.with_field("date_field", Matcher.less_than("2019-08-27T00:00:00Z")) assert filters_for_criteria(c1) == {"num_field": {"$lt": "5"}} - assert filters_for_criteria(c2) == {"date_field": {"$lt": {"$date": "2019-08-27T00:00:00Z"}}} + assert filters_for_criteria(c2) == { + "date_field": {"$lt": {"$date": "2019-08-27T00:00:00Z"}} + } def test_non_matcher(): diff --git a/tests/fake/test_fake_search.py b/tests/fake/test_fake_search.py index 550b3d2d..b14abc20 100644 --- a/tests/fake/test_fake_search.py +++ b/tests/fake/test_fake_search.py @@ -136,7 +136,9 @@ def test_search_null_and(): """Search with an empty AND gives an error.""" controller = FakeController() - dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1") + dist1 = Distributor( + id="yum_distributor", type_id="yum_distributor", repo_id="repo1" + ) repo1 = Repository(id="repo1", distributors=[dist1]) controller.insert_repository(repo1) @@ -321,11 +323,16 @@ def test_search_paginates(): # All repos should have been found assert sorted(found_repos) == sorted(repos) + def test_search_distributor(): controller = FakeController() - dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1") - dist2 = Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor", repo_id="repo1") + dist1 = Distributor( + id="yum_distributor", type_id="yum_distributor", repo_id="repo1" + ) + dist2 = Distributor( + id="cdn_distributor", type_id="rpm_rsync_distributor", repo_id="repo1" + ) repo1 = Repository(id="repo1", distributors=(dist1, dist2)) controller.insert_repository(repo1) @@ -337,19 +344,30 @@ def test_search_distributor(): assert sorted(found) == [dist2, dist1] + def test_search_mapped_field_less_than(): controller = FakeController() - dist1 = Distributor(id="yum_distributor", type_id="yum_distributor", repo_id="repo1", - last_publish=datetime.datetime(2019, 8, 23, 2, 5, 0, tzinfo=None)) - dist2 = Distributor(id="cdn_distributor", type_id="rpm_rsync_distributor", repo_id="repo1", - last_publish=datetime.datetime(2019, 8, 27, 2, 5, 0, tzinfo=None)) + dist1 = Distributor( + id="yum_distributor", + type_id="yum_distributor", + repo_id="repo1", + last_publish=datetime.datetime(2019, 8, 23, 2, 5, 0, tzinfo=None), + ) + dist2 = Distributor( + id="cdn_distributor", + type_id="rpm_rsync_distributor", + repo_id="repo1", + last_publish=datetime.datetime(2019, 8, 27, 2, 5, 0, tzinfo=None), + ) repo1 = Repository(id="repo1", distributors=(dist1, dist2)) controller.insert_repository(repo1) client = controller.client - crit = Criteria.with_field("last_publish", Matcher.less_than("2019-08-24T00:00:00Z")) + crit = Criteria.with_field( + "last_publish", Matcher.less_than("2019-08-24T00:00:00Z") + ) found = client.search_distributor(crit).result().data From 28c9bd4c00bc485bcc9c84b156204071239b8ea1 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 29 Aug 2019 14:34:34 -0400 Subject: [PATCH 06/16] Updated version 1.3.0 to 1.4.0 Added a new api and matcher. So incermented the version number --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 53fbeda0..aeb35c93 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def get_requirements(): setup( name="pubtools-pulplib", - version="1.3.0", + version="1.4.0", packages=find_packages(exclude=["tests"]), package_data={"pubtools.pulplib._impl.schema": ["*.yaml"]}, url="https://github.com/release-engineering/pubtools-pulplib", From 79f754b3de2064d5168c70c48787a564e56715d1 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Tue, 3 Sep 2019 12:20:56 -0400 Subject: [PATCH 07/16] Updated on review to use datetime obj for date comparisons --- pubtools/pulplib/_impl/client/client.py | 7 +++--- pubtools/pulplib/_impl/client/search.py | 22 +++++++++++-------- pubtools/pulplib/_impl/criteria.py | 10 ++++----- pubtools/pulplib/_impl/fake/match.py | 15 ++++++------- pubtools/pulplib/_impl/schema/repository.yaml | 2 +- pubtools/pulplib/_impl/util.py | 12 ---------- tests/client/test_search.py | 10 +++++---- tests/fake/test_fake_search.py | 3 +-- 8 files changed, 36 insertions(+), 45 deletions(-) diff --git a/pubtools/pulplib/_impl/client/client.py b/pubtools/pulplib/_impl/client/client.py index 4fb67434..9341725e 100644 --- a/pubtools/pulplib/_impl/client/client.py +++ b/pubtools/pulplib/_impl/client/client.py @@ -171,12 +171,12 @@ def search_repository(self, criteria=None): ) def search_distributor(self, criteria=None): - """Search the distributors matching the given criteria + """Search the distributors matching the given criteria. Args: criteria (:class:`~pubtools.pulplib.Criteria`) A criteria object used for this search. - If None, search for all repositories. + If None, search for all distributors. Returns: Future[:class:`~pubtools.pulplib.Page`] @@ -194,8 +194,7 @@ def _search(self, return_type, resource_type, criteria=None, search_options=None "filters": filters_for_criteria(criteria, return_type), } search = {"criteria": pulp_crit} - if search_options: - search.update(search_options) + search.update(search_options or {}) response_f = self._do_search(resource_type, search) diff --git a/pubtools/pulplib/_impl/client/search.py b/pubtools/pulplib/_impl/client/search.py index 35fa5081..46659cf8 100644 --- a/pubtools/pulplib/_impl/client/search.py +++ b/pubtools/pulplib/_impl/client/search.py @@ -1,3 +1,4 @@ +import datetime from pubtools.pulplib._impl.criteria import ( AndCriteria, OrCriteria, @@ -12,7 +13,6 @@ from pubtools.pulplib._impl import compat_attr as attr from pubtools.pulplib._impl.model.attr import PULP2_FIELD, PY_PULP2_CONVERTER -from pubtools.pulplib._impl.util import _is_iso_date_format def all_subclasses(klass): @@ -23,6 +23,15 @@ def all_subclasses(klass): return out +def to_mongo_json(value): + # Return a value converted to the format expected for a mongo JSON + # expression. Only a handful of special types need explicit conversions. + if isinstance(value, datetime.datetime): + return {"$date": value.strftime("%Y-%m-%dT%H:%M:%SZ")} + + return value + + def map_field_for_type(field_name, matcher, type_hint): if not type_hint: return (field_name, matcher) @@ -81,20 +90,15 @@ def field_match(to_match): return {"$regex": to_match._pattern} if isinstance(to_match, EqMatcher): - return {"$eq": to_match._value} + return {"$eq": to_mongo_json(to_match._value)} if isinstance(to_match, InMatcher): - return {"$in": to_match._values} + return {"$in": to_mongo_json(to_match._values)} if isinstance(to_match, ExistsMatcher): return {"$exists": True} if isinstance(to_match, LessThanMatcher): - value = to_match._value - # only value of the format YYYY-mm-ddTHH:MM:SSZ is - # treated as date - if _is_iso_date_format(value): - return {"$lt": {"$date": value}} - return {"$lt": value} + return {"$lt": to_mongo_json(to_match._value)} raise TypeError("Not a matcher: %s" % repr(to_match)) diff --git a/pubtools/pulplib/_impl/criteria.py b/pubtools/pulplib/_impl/criteria.py index 427bc3f0..602d1fcd 100644 --- a/pubtools/pulplib/_impl/criteria.py +++ b/pubtools/pulplib/_impl/criteria.py @@ -223,20 +223,20 @@ def in_(cls, values): def less_than(cls, value): """ Returns a matcher for a field whose value is less than the specified input - value + value. Arguments: - values (object) + value (object) An object to match against the field Example: .. code-block:: python - # would match where last_publish is before "2019-08-27T00:00:00Z - # date is reqired to be in this format + # would match where last_publish is before "2019-08-27T00:00:00Z" + # date comparison requries a dateime.datetime object crit = Criteria.with_field( 'last_publish', - Matcher.less_than("2019-08-27T00:00:00Z") + Matcher.less_than(datetime.datetime(2019, 8, 27, 0, 0, 0)) ) """ return LessThanMatcher(value) diff --git a/pubtools/pulplib/_impl/fake/match.py b/pubtools/pulplib/_impl/fake/match.py index e790b0e6..52dc9bbb 100644 --- a/pubtools/pulplib/_impl/fake/match.py +++ b/pubtools/pulplib/_impl/fake/match.py @@ -16,7 +16,6 @@ LessThanMatcher, ) from pubtools.pulplib._impl.model.common import PULP2_FIELD -from pubtools.pulplib._impl.util import _is_iso_date_format ABSENT = object() CLASS_MATCHERS = [] @@ -129,16 +128,16 @@ def match_in(matcher, field, obj): @visit(LessThanMatcher) def match_less(matcher, field, obj): # for datetime.dateime fields, obj_value is returned as pulp_value(iso format) - # if the model filed name is same as pulp filed name else returns a obj_value - # as datetime.datetime object + # if the model field name is same as pulp field name else returns a obj_value + # as datetime.datetime object. So obj_value is converted to datetime.datetime + # for any expected date comparison obj_value = get_field(field, obj) - if _is_iso_date_format(obj_value): + if isinstance(matcher._value, datetime.datetime) and not isinstance( + obj_value, datetime.datetime + ): obj_value = datetime.datetime.strptime(obj_value, "%Y-%m-%dT%H:%M:%SZ") - matcher_value = matcher._value - if _is_iso_date_format(matcher_value): - matcher_value = datetime.datetime.strptime(matcher_value, "%Y-%m-%dT%H:%M:%SZ") - return obj_value < matcher_value + return obj_value < matcher._value def pulp_value(pulp_field, obj): diff --git a/pubtools/pulplib/_impl/schema/repository.yaml b/pubtools/pulplib/_impl/schema/repository.yaml index e13a733b..affaecbe 100644 --- a/pubtools/pulplib/_impl/schema/repository.yaml +++ b/pubtools/pulplib/_impl/schema/repository.yaml @@ -18,7 +18,7 @@ definitions: # String ID of distributor's type, e.g. "yum_distributor" type: string repo_id: - # String ID of the repository the distributor is attached + # String ID of distributor's repository type: string config: # Config dict for this distributor, different per distributor type. diff --git a/pubtools/pulplib/_impl/util.py b/pubtools/pulplib/_impl/util.py index efa80cea..026a1762 100644 --- a/pubtools/pulplib/_impl/util.py +++ b/pubtools/pulplib/_impl/util.py @@ -1,5 +1,3 @@ -from datetime import datetime - ABSENT = object() @@ -25,13 +23,3 @@ def lookup(value, key, default=ABSENT): # it's present return value - - -def _is_iso_date_format(str_date): - # checks if the input is of the format YYYY-mm-ddTHH:MM:SSZ - # to qualify as a date type field value - try: - datetime.strptime(str_date, "%Y-%m-%dT%H:%M:%SZ") - return True - except (ValueError, TypeError): - return False diff --git a/tests/client/test_search.py b/tests/client/test_search.py index e933a009..47eeca18 100644 --- a/tests/client/test_search.py +++ b/tests/client/test_search.py @@ -1,4 +1,5 @@ import pytest +import datetime from pubtools.pulplib import Criteria, Matcher @@ -59,13 +60,14 @@ def test_field_regex_criteria(): def test_field_less_than_criteria(): - """with_filed with less_than is translated as expected for + """with_field with less_than is translated as expected for date and non-date types """ - c1 = Criteria.with_field("num_field", Matcher.less_than("5")) - c2 = Criteria.with_field("date_field", Matcher.less_than("2019-08-27T00:00:00Z")) + publish_date = datetime.datetime(2019, 8, 27, 0, 0, 0) + c1 = Criteria.with_field("num_field", Matcher.less_than(5)) + c2 = Criteria.with_field("date_field", Matcher.less_than(publish_date)) - assert filters_for_criteria(c1) == {"num_field": {"$lt": "5"}} + assert filters_for_criteria(c1) == {"num_field": {"$lt": 5}} assert filters_for_criteria(c2) == { "date_field": {"$lt": {"$date": "2019-08-27T00:00:00Z"}} } diff --git a/tests/fake/test_fake_search.py b/tests/fake/test_fake_search.py index b14abc20..444f4f34 100644 --- a/tests/fake/test_fake_search.py +++ b/tests/fake/test_fake_search.py @@ -366,9 +366,8 @@ def test_search_mapped_field_less_than(): client = controller.client crit = Criteria.with_field( - "last_publish", Matcher.less_than("2019-08-24T00:00:00Z") + "last_publish", Matcher.less_than(datetime.datetime(2019, 8, 24, 0, 0, 0)) ) - found = client.search_distributor(crit).result().data assert found == [dist1] From ff0bd76ff58b9e0dc1a68c63af665341d34ef890 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Tue, 3 Sep 2019 13:33:35 -0400 Subject: [PATCH 08/16] Bump version to 1.6.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1eef41c5..8ee49321 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def get_requirements(): setup( name="pubtools-pulplib", - version="1.5.0", + version="1.6.0", packages=find_packages(exclude=["tests"]), package_data={"pubtools.pulplib._impl.schema": ["*.yaml"]}, url="https://github.com/release-engineering/pubtools-pulplib", From bf154296612cc9809ad7452697eafc1df4004a50 Mon Sep 17 00:00:00 2001 From: Rohan McGovern Date: Wed, 4 Sep 2019 09:00:17 +0800 Subject: [PATCH 09/16] Fix bogus field conversions in fake client In the fake client, our logic for deciding whether we had found a model field or a raw Pulp field was: using_model_field = mapped_field is not field But that clearly doesn't work if the model field and the Pulp field happen to have the same name. This would lead to fields wrongly using a Pulp conversion where none is required. Up to now, it seems no fields hit the conditions where this mattered, which is a field having: - the same name on model and in Pulp - and a (nontrivial) conversion required With the addition of Distributor.last_publish as a datetime, this bug now matters and must be fixed. Instead of trying to reuse a single piece of data to mean multiple things, let's just define the function as returning None if no mapping occurred. --- pubtools/pulplib/_impl/client/search.py | 8 +++++--- pubtools/pulplib/_impl/fake/match.py | 19 ++++--------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/pubtools/pulplib/_impl/client/search.py b/pubtools/pulplib/_impl/client/search.py index 46659cf8..68a8f300 100644 --- a/pubtools/pulplib/_impl/client/search.py +++ b/pubtools/pulplib/_impl/client/search.py @@ -34,7 +34,7 @@ def to_mongo_json(value): def map_field_for_type(field_name, matcher, type_hint): if not type_hint: - return (field_name, matcher) + return None attrs_classes = all_subclasses(type_hint) attrs_classes = [cls for cls in attrs_classes if attr.has(cls)] @@ -55,7 +55,7 @@ def map_field_for_type(field_name, matcher, type_hint): raise NotImplementedError("Searching on field %s is not supported" % field_name) # No match => no change, search exactly what was requested - return (field_name, matcher) + return None def filters_for_criteria(criteria, type_hint=None): @@ -78,7 +78,9 @@ def filters_for_criteria(criteria, type_hint=None): field = criteria._field matcher = criteria._matcher - field, matcher = map_field_for_type(field, matcher, type_hint) + mapped = map_field_for_type(field, matcher, type_hint) + if mapped: + field, matcher = mapped return {field: field_match(matcher)} diff --git a/pubtools/pulplib/_impl/fake/match.py b/pubtools/pulplib/_impl/fake/match.py index 52dc9bbb..5052f041 100644 --- a/pubtools/pulplib/_impl/fake/match.py +++ b/pubtools/pulplib/_impl/fake/match.py @@ -46,11 +46,9 @@ def get_field(field, obj): # - If it's a field on the model, no conversion is needed since we already # are storing plain objects from the model # - If it's a Pulp field, conversion will be handled in pulp_value - mapped_field, _ = map_field_for_type(field, matcher=None, type_hint=obj.__class__) + using_model_field = map_field_for_type(field, matcher=None, type_hint=obj.__class__) # Are we looking for a field on our model, or a raw Pulp field? - using_model_field = mapped_field is not field - if using_model_field: # If matching a field on the model, we can simply grab and compare # the attribute directly. @@ -126,18 +124,9 @@ def match_in(matcher, field, obj): @visit(LessThanMatcher) -def match_less(matcher, field, obj): - # for datetime.dateime fields, obj_value is returned as pulp_value(iso format) - # if the model field name is same as pulp field name else returns a obj_value - # as datetime.datetime object. So obj_value is converted to datetime.datetime - # for any expected date comparison - obj_value = get_field(field, obj) - if isinstance(matcher._value, datetime.datetime) and not isinstance( - obj_value, datetime.datetime - ): - obj_value = datetime.datetime.strptime(obj_value, "%Y-%m-%dT%H:%M:%SZ") - - return obj_value < matcher._value +def match_field_less(matcher, field, obj): + value = get_field(field, obj) + return value < matcher._value def pulp_value(pulp_field, obj): From 584b6f309d587ded7480bd8d0996a4c29b6bb51a Mon Sep 17 00:00:00 2001 From: Rohan McGovern Date: Thu, 5 Sep 2019 10:23:09 +0800 Subject: [PATCH 10/16] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d86e596..11b7b552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- A `search_distributor` API to search distributors on defined `Criteria` +- `Matcher.less_than()` matcher to find the results with fields less than + the given value + ### Changed - **API break**: types of fields on model objects are now strictly validated during construction. @@ -39,9 +44,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support querying and updating maintenance mode of Pulp repositories - Introduced ``Client.get_content_type_ids`` method to retrieve supported content types. -- A `search_distributor` API to search distributors on defined `Criteria` -- `Matcher.less_than()` matcher to find the results with fields less than - the given value ### Fixed - Fixed a crash in `upload_file` when passed a file object opened in text mode From edc58883139f7593ecb00c972a563fb0f8146478 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 5 Sep 2019 15:42:53 -0400 Subject: [PATCH 11/16] added translation of list and dict values in to_mongo_json some values like datetime.datetime requires transaltion to iso format. this translation was there for an object. this patch extends that to translate the values in list or dict type matcher values too. --- pubtools/pulplib/_impl/client/search.py | 9 +++++++++ tests/client/test_search.py | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/pubtools/pulplib/_impl/client/search.py b/pubtools/pulplib/_impl/client/search.py index 68a8f300..640fb5a4 100644 --- a/pubtools/pulplib/_impl/client/search.py +++ b/pubtools/pulplib/_impl/client/search.py @@ -29,6 +29,15 @@ def to_mongo_json(value): if isinstance(value, datetime.datetime): return {"$date": value.strftime("%Y-%m-%dT%H:%M:%SZ")} + if isinstance(value, (list, tuple)): + return [to_mongo_json(elem) for elem in value] + + if isinstance(value, dict): + out = {} + for (key, val) in value.items(): + out[key] = to_mongo_json(val) + return out + return value diff --git a/tests/client/test_search.py b/tests/client/test_search.py index 47eeca18..b3cfaeb6 100644 --- a/tests/client/test_search.py +++ b/tests/client/test_search.py @@ -84,3 +84,16 @@ def test_non_matcher(): field_match("oops not a matcher") assert "Not a matcher" in str(exc_info.value) + + +def test_dict_matcher_value(): + """criteria using a dict as matcher value""" + + crit = Criteria.with_field( + "created", + Matcher.less_than({"created_date": datetime.datetime(2019, 9, 4, 0, 0, 0)}), + ) + + assert filters_for_criteria(crit) == { + "created": {"$lt": {"created_date": {"$date": "2019-09-04T00:00:00Z"}}} + } From 2dd209f5d50f5371ee95b25f4ba372dad56de91e Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 5 Sep 2019 15:46:10 -0400 Subject: [PATCH 12/16] corrected indentation of code block to generated docs --- pubtools/pulplib/_impl/criteria.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pubtools/pulplib/_impl/criteria.py b/pubtools/pulplib/_impl/criteria.py index c3f53ebf..2178b35c 100644 --- a/pubtools/pulplib/_impl/criteria.py +++ b/pubtools/pulplib/_impl/criteria.py @@ -232,12 +232,12 @@ def less_than(cls, value): Example: .. code-block:: python - # would match where last_publish is before "2019-08-27T00:00:00Z" - # date comparison requries a dateime.datetime object - crit = Criteria.with_field( - 'last_publish', - Matcher.less_than(datetime.datetime(2019, 8, 27, 0, 0, 0)) - ) + # would match where last_publish is before "2019-08-27T00:00:00Z" + # date comparison requires a datetime.datetime object + crit = Criteria.with_field( + 'last_publish', + Matcher.less_than(datetime.datetime(2019, 8, 27, 0, 0, 0)) + ) """ return LessThanMatcher(value) From 5b380e1add43a4c664cc0a88cbedcf8a3c45dd33 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 5 Sep 2019 15:47:53 -0400 Subject: [PATCH 13/16] Added a validation on distributors field in Repository distributor object has a field repo_id with the name of the repository the distributor is attached to. the validation checks if the distributor.repo_id is same as the repo.id of the repo this distributor is being attached to else raises an exception. --- pubtools/pulplib/_impl/fake/client.py | 2 +- pubtools/pulplib/_impl/model/repository/base.py | 14 ++++++++++++++ tests/repository/test_repository_from_data.py | 10 ++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pubtools/pulplib/_impl/fake/client.py b/pubtools/pulplib/_impl/fake/client.py index 75a70111..97719d84 100644 --- a/pubtools/pulplib/_impl/fake/client.py +++ b/pubtools/pulplib/_impl/fake/client.py @@ -99,7 +99,7 @@ def search_distributor(self, criteria=None): for repo in self._repositories: for distributor in repo.distributors: if match_object(criteria, distributor): - distributors.append(distributor) + distributors.append(attr.evolve(distributor, repo_id=repo.id)) except Exception as ex: # pylint: disable=broad-except return f_return_error(ex) diff --git a/pubtools/pulplib/_impl/model/repository/base.py b/pubtools/pulplib/_impl/model/repository/base.py index f9373c7d..9d7998e1 100644 --- a/pubtools/pulplib/_impl/model/repository/base.py +++ b/pubtools/pulplib/_impl/model/repository/base.py @@ -159,6 +159,20 @@ class Repository(PulpObject): _client = attr.ib(default=None, init=False, repr=False, cmp=False, hash=False) # hidden attribute for client attached to this object + @distributors.validator + def check_repo_id(self, _, value): + # checks if distributor's repository id is same as the repository it + # is attached to + for distributor in value: + if not distributor.repo_id: + return + elif distributor.repo_id == self.id: + return + raise ValueError( + "repo_id doesn't match for %s. repo_id: %s, distributor.repo_id: %s" + % (distributor.id, self.id, distributor.repo_id) + ) + @property def _distributors_by_id(self): out = {} diff --git a/tests/repository/test_repository_from_data.py b/tests/repository/test_repository_from_data.py index cd33f242..ec9d0507 100644 --- a/tests/repository/test_repository_from_data.py +++ b/tests/repository/test_repository_from_data.py @@ -106,3 +106,13 @@ def test_distributors_last_publish_null(): ) assert repo.distributor("dist1").last_publish is None + + +def test_invalid_distributor_repo_id(): + """distributor's repo id being different from repo it's attached is invalid""" + + dist = Distributor(id="dist", type_id="yum_distributor", repo_id="repo") + with pytest.raises(ValueError) as ex: + repo = Repository(id="test_repo", distributors=[dist]) + + assert "repo_id doesn't match for dist" in ex.value.message From 4fa7f3432e511b740c9ae4cb079ba36d0322989c Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Thu, 5 Sep 2019 16:19:09 -0400 Subject: [PATCH 14/16] refactored assert ValueError for no message attribute --- tests/repository/test_repository_from_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/repository/test_repository_from_data.py b/tests/repository/test_repository_from_data.py index ec9d0507..71600bb8 100644 --- a/tests/repository/test_repository_from_data.py +++ b/tests/repository/test_repository_from_data.py @@ -115,4 +115,4 @@ def test_invalid_distributor_repo_id(): with pytest.raises(ValueError) as ex: repo = Repository(id="test_repo", distributors=[dist]) - assert "repo_id doesn't match for dist" in ex.value.message + assert "repo_id doesn't match for dist" in str(ex.value) From a283c55f8e4c19de5242aa9760854f43a72566f2 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Mon, 9 Sep 2019 08:24:41 -0400 Subject: [PATCH 15/16] make distributor.repo_id validation 'check_repo_id' private --- pubtools/pulplib/_impl/model/repository/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubtools/pulplib/_impl/model/repository/base.py b/pubtools/pulplib/_impl/model/repository/base.py index 9d7998e1..c1b99067 100644 --- a/pubtools/pulplib/_impl/model/repository/base.py +++ b/pubtools/pulplib/_impl/model/repository/base.py @@ -160,7 +160,7 @@ class Repository(PulpObject): # hidden attribute for client attached to this object @distributors.validator - def check_repo_id(self, _, value): + def _check_repo_id(self, _, value): # checks if distributor's repository id is same as the repository it # is attached to for distributor in value: From c0f5d12e6c7e13d277c2e36c02ca399a89116f93 Mon Sep 17 00:00:00 2001 From: Rajul Kumar Date: Mon, 9 Sep 2019 09:06:24 -0400 Subject: [PATCH 16/16] bump version to 2.1.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 32f45961..579c9d97 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def get_requirements(): setup( name="pubtools-pulplib", - version="2.0.0", + version="2.1.0", packages=find_packages(exclude=["tests"]), package_data={"pubtools.pulplib._impl.schema": ["*.yaml"]}, url="https://github.com/release-engineering/pubtools-pulplib",