Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-osc-tiny for openSUSE:Factory checked in at 2022-09-19 16:03:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-osc-tiny (Old) and /work/SRC/openSUSE:Factory/.python-osc-tiny.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-osc-tiny" Mon Sep 19 16:03:39 2022 rev:21 rq:1004700 version:0.7.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-osc-tiny/python-osc-tiny.changes 2022-08-17 18:25:19.387461136 +0200 +++ /work/SRC/openSUSE:Factory/.python-osc-tiny.new.2083/python-osc-tiny.changes 2022-09-19 16:03:47.586223507 +0200 @@ -1,0 +2,19 @@ +Mon Sep 19 12:11:43 UTC 2022 - Andreas Hasenkopf <ahasenk...@suse.com> + +- Release 0.7.3 + * Consider "boolean" parameters of specific API endpoints (fixes #97) + Workaround for + https://github.com/openSUSE/open-build-service/issues/9715 + +------------------------------------------------------------------- +Mon Sep 19 07:45:03 UTC 2022 - Andreas Hasenkopf <ahasenk...@suse.com> + +- Release 0.7.2 + * Suppress logging of response content when streaming + * Fixed error in docstring + * Removed Python2 support libararies (closes #81) + * Removed warning about boolean query params + * Fixed issues in `Project.put_meta` (fixes #84) + * Deprecated `projects.put_meta` in favor of using `set_meta` (fixes #85) + +------------------------------------------------------------------- Old: ---- osc-tiny-0.7.1.tar.gz New: ---- osc-tiny-0.7.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-osc-tiny.spec ++++++ --- /var/tmp/diff_new_pack.fklWdO/_old 2022-09-19 16:03:48.942227129 +0200 +++ /var/tmp/diff_new_pack.fklWdO/_new 2022-09-19 16:03:48.946227140 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-osc-tiny -Version: 0.7.1 +Version: 0.7.3 Release: 0 Summary: Client API for openSUSE BuildService License: MIT ++++++ osc-tiny-0.7.1.tar.gz -> osc-tiny-0.7.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/PKG-INFO new/osc-tiny-0.7.3/PKG-INFO --- old/osc-tiny-0.7.1/PKG-INFO 2022-08-16 14:32:49.240917400 +0200 +++ new/osc-tiny-0.7.3/PKG-INFO 2022-09-19 14:08:24.194599400 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: osc-tiny -Version: 0.7.1 +Version: 0.7.3 Summary: Client API for openSUSE BuildService Home-page: http://github.com/crazyscientist/osc-tiny Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master @@ -23,9 +23,10 @@ OSC Tiny ======== -] +  [](https://badge.fury.io/py/osc-tiny) +[](https://github.com/crazyscientist/osc-tiny/tree/python-coverage-comment-action-data) This project aims to provide a minimalistic and transparent client for accessing the [OpenBuildService](https://openbuildservice.org/) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/README.md new/osc-tiny-0.7.3/README.md --- old/osc-tiny-0.7.1/README.md 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/README.md 2022-09-19 14:08:14.000000000 +0200 @@ -1,9 +1,10 @@ OSC Tiny ======== -] +  [](https://badge.fury.io/py/osc-tiny) +[](https://github.com/crazyscientist/osc-tiny/tree/python-coverage-comment-action-data) This project aims to provide a minimalistic and transparent client for accessing the [OpenBuildService](https://openbuildservice.org/) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osc_tiny.egg-info/PKG-INFO new/osc-tiny-0.7.3/osc_tiny.egg-info/PKG-INFO --- old/osc-tiny-0.7.1/osc_tiny.egg-info/PKG-INFO 2022-08-16 14:32:49.000000000 +0200 +++ new/osc-tiny-0.7.3/osc_tiny.egg-info/PKG-INFO 2022-09-19 14:08:24.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: osc-tiny -Version: 0.7.1 +Version: 0.7.3 Summary: Client API for openSUSE BuildService Home-page: http://github.com/crazyscientist/osc-tiny Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master @@ -23,9 +23,10 @@ OSC Tiny ======== -] +  [](https://badge.fury.io/py/osc-tiny) +[](https://github.com/crazyscientist/osc-tiny/tree/python-coverage-comment-action-data) This project aims to provide a minimalistic and transparent client for accessing the [OpenBuildService](https://openbuildservice.org/) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/__init__.py new/osc-tiny-0.7.3/osctiny/__init__.py --- old/osc-tiny-0.7.1/osctiny/__init__.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/__init__.py 2022-09-19 14:08:14.000000000 +0200 @@ -6,4 +6,4 @@ __all__ = ['Osc', 'bs_requests', 'buildresults', 'comments', 'packages', 'projects', 'search', 'users'] -__version__ = "0.7.1" +__version__ = "0.7.3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/bs_requests.py new/osc-tiny-0.7.3/osctiny/extensions/bs_requests.py --- old/osc-tiny-0.7.1/osctiny/extensions/bs_requests.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/bs_requests.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,9 +2,7 @@ Requests extension ------------------ """ -from __future__ import unicode_literals -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin from ..utils.base import ExtensionBase @@ -17,7 +15,7 @@ @staticmethod def _validate_id(request_id): - request_id = text_type(request_id) + request_id = str(request_id) if not request_id.isnumeric(): raise ValueError( "Request ID must be numeric! Got instead: {}".format(request_id) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/buildresults.py new/osc-tiny-0.7.3/osctiny/extensions/buildresults.py --- old/osc-tiny-0.7.1/osctiny/extensions/buildresults.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/buildresults.py 2022-09-19 14:08:14.000000000 +0200 @@ -3,8 +3,7 @@ ---------------------- """ # pylint: disable=too-few-public-methods -from __future__ import unicode_literals -from six.moves.urllib.parse import urljoin +from urllib.parse import urljoin from ..utils.base import ExtensionBase diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/comments.py new/osc-tiny-0.7.3/osctiny/extensions/comments.py --- old/osc-tiny-0.7.1/osctiny/extensions/comments.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/comments.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,10 +2,8 @@ Comments extension ------------------ """ -from __future__ import unicode_literals import os -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin from ..utils.base import ExtensionBase @@ -53,7 +51,7 @@ response = self.osc.request( url=urljoin(self.osc.url, os.path.join(*([self.base_path, obj_type] - + [text_type(x) for x in ids]))), + + [str(x) for x in ids]))), method="GET", ) return self.osc.get_objectified_xml(response) @@ -81,9 +79,9 @@ """ self._validate(obj_type, ids) url = urljoin(self.osc.url, - os.path.join(*([self.base_path, obj_type] + [text_type(x) for x in ids]))) + os.path.join(*([self.base_path, obj_type] + [str(x) for x in ids]))) params = {} - if parent_id and text_type(parent_id).isnumeric(): + if parent_id and str(parent_id).isnumeric(): params["parent_id"] = parent_id response = self.osc.request( @@ -109,7 +107,7 @@ :return: ``True``, if successful. Otherwise API response :rtype: bool or lxml.objectify.ObjectifiedElement """ - url = urljoin(self.osc.url, '/comment/' + text_type(comment_id)) + url = urljoin(self.osc.url, '/comment/' + str(comment_id)) response = self.osc.request( url=url, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/distributions.py new/osc-tiny-0.7.3/osctiny/extensions/distributions.py --- old/osc-tiny-0.7.1/osctiny/extensions/distributions.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/distributions.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,10 +2,7 @@ Distributions extension ----------------------- """ -from __future__ import unicode_literals - -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin from ..utils.base import ExtensionBase @@ -49,7 +46,7 @@ response = self.osc.request( url=urljoin( self.osc.url, - "/".join((self.base_path, text_type(distribution_id))) + "/".join((self.base_path, str(distribution_id))) ), method="GET" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/issues.py new/osc-tiny-0.7.3/osctiny/extensions/issues.py --- old/osc-tiny-0.7.1/osctiny/extensions/issues.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/issues.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,11 +2,9 @@ Issues extension ---------------- """ -from __future__ import unicode_literals import os -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin from ..utils.backports import lru_cache from ..utils.base import ExtensionBase @@ -22,7 +20,7 @@ @staticmethod def _validate(value): - return text_type(value) + return str(value) @lru_cache(maxsize=1) def get_trackers(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/origin.py new/osc-tiny-0.7.3/osctiny/extensions/origin.py --- old/osc-tiny-0.7.1/osctiny/extensions/origin.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/origin.py 2022-09-19 14:08:14.000000000 +0200 @@ -27,17 +27,11 @@ from yaml import load -from ..utils.backports import lru_cache +from ..utils.backports import lru_cache, cached_property from ..utils.base import ExtensionBase from ..utils.mapping import LazyOscMappable try: - from functools import cached_property -except ImportError: - # Support for Python3 prior 3.8 - from cached_property import cached_property - -try: from yaml import CSafeLoader as SafeLoader except ImportError: # If libyaml installed, we should fall back to the SafeLoader diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/packages.py new/osc-tiny-0.7.3/osctiny/extensions/packages.py --- old/osc-tiny-0.7.1/osctiny/extensions/packages.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/packages.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,10 +2,8 @@ Packages extension ------------------ """ -from __future__ import unicode_literals import os -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin from lxml.etree import tounicode, SubElement, Element from lxml.objectify import fromstring @@ -105,7 +103,7 @@ :type meta: str or lxml.objectify.ObjectifiedElement :return: """ - if isinstance(meta, text_type): + if isinstance(meta, (str, bytes)): meta = fromstring(meta) if meta is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/projects.py new/osc-tiny-0.7.3/osctiny/extensions/projects.py --- old/osc-tiny-0.7.1/osctiny/extensions/projects.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/projects.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,10 +2,9 @@ Projects extension ------------------ """ -from __future__ import unicode_literals import re -from six.moves.urllib.parse import urljoin -from six import text_type +from urllib.parse import urljoin +from warnings import warn from lxml.etree import tounicode from lxml.objectify import fromstring, SubElement @@ -15,8 +14,6 @@ TEMPLATE_CREATE_ATTR = "<attributes><attribute namespace='' name=''></attribute></attributes>" TEMPLATE_META = "<project name=''><title></title><description></description>" \ - "<person userid='' role='bugowner'/>" \ - "<person userid='' role='maintainer'/>" \ "<build><enable/></build><publish><disable/></publish>" \ "<debuginfo><enable/></debuginfo></project>" @@ -73,6 +70,33 @@ .. versionadded:: 0.1.5 + .. deprecated:: 0.7.2 + Use :meth:`set_meta` instead + :param project: name of project + :param metafile: Complete metafile + :type metafile: str or ElementTree + :param title: Title for meta file + :param description: Description for meta file + :param bugowner: Bugowner for meta file + :param maintainer: Maintainer for meta file + :return: ``True``, if successful. Otherwise API response + :rtype: bool or lxml.objectify.ObjectifiedElement + """ + warn("Deprecated. Use projects.set_meta instead") + + return self.set_meta(project, metafile, title, description, bugowner, + maintainer) + + # pylint: disable=too-many-arguments + def set_meta(self, project, metafile=None, title=None, description=None, + bugowner=None, maintainer=None): + """ + Edit project meta data or create a new project + + If no ``metafile`` is provided, a default template is used. + + .. versionadded:: 0.7.2 + :param project: name of project :param metafile: Complete metafile :type metafile: str or ElementTree @@ -86,24 +110,34 @@ if metafile is None: metafile = TEMPLATE_META - if isinstance(metafile, text_type): + if isinstance(metafile, (str, bytes)): metafile = fromstring(metafile) metafile.set("name", project) - # pylint: disable=protected-access - if title: - metafile.title._setText(title) - if description: - metafile.description._setText(description) + for required_field, text in (('title', title), ('description', description)): + elem = metafile.find(required_field) + if elem is None: + elem = SubElement(metafile, required_field) + + if text is not None: + # pylint: disable=protected-access + elem._setText(text) + + def add_person(role: str, userid: str) -> None: + person = metafile.xpath(f"person[@role='{role}']") + if not person: + person = [SubElement(metafile, 'person', role=role)] + person[0].set("userid", userid) + if bugowner: - person = metafile.xpath("person[@role='bugowner']") - if person: - person[0].set("userid", bugowner) + add_person("bugowner", bugowner) + if maintainer: - person = metafile.xpath("person[@role='maintainer']") - if person: - person[0].set("userid", maintainer) + add_person("maintainer", maintainer) + + metafile.insert(0, metafile.title) + metafile.insert(1, metafile.description) response = self.osc.request( url=urljoin(self.osc.url, @@ -134,7 +168,7 @@ """ kwargs["meta"] = meta if rev: - kwargs["rev"] = text_type(rev) + kwargs["rev"] = str(rev) response = self.osc.request( url=urljoin( self.osc.url, @@ -207,7 +241,7 @@ for val in value: elem = SubElement(attr_xml.attribute, "value") # pylint: disable=protected-access - elem._setText(text_type(val)) + elem._setText(str(val)) response = self.osc.request( url=url, @@ -352,7 +386,7 @@ .. versionadded:: 0.1.2 :param project: Project name - :return: ``True``, if package exists, otherwise ``False`` + :return: ``True``, if project exists, otherwise ``False`` """ response = self.osc.request( url=urljoin( @@ -365,4 +399,4 @@ return response.status_code == 200 - create = put_meta + create = set_meta diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/search.py new/osc-tiny-0.7.3/osctiny/extensions/search.py --- old/osc-tiny-0.7.1/osctiny/extensions/search.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/search.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,8 +2,7 @@ Search extension ---------------- """ -from __future__ import unicode_literals -from six.moves.urllib.parse import urljoin +from urllib.parse import urljoin from ..utils.base import ExtensionBase diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/extensions/users.py new/osc-tiny-0.7.3/osctiny/extensions/users.py --- old/osc-tiny-0.7.1/osctiny/extensions/users.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/extensions/users.py 2022-09-19 14:08:14.000000000 +0200 @@ -2,8 +2,7 @@ Persons and groups extension ---------------------------- """ -from __future__ import unicode_literals -from six.moves.urllib.parse import urljoin +from urllib.parse import urljoin from ..utils.base import ExtensionBase diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/osc.py new/osc-tiny-0.7.3/osctiny/osc.py --- old/osc-tiny-0.7.1/osctiny/osc.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/osc.py 2022-09-19 14:08:14.000000000 +0200 @@ -36,6 +36,7 @@ from .extensions.search import Search from .extensions.users import Group, Person from .utils.auth import HttpSignatureAuth +from .utils.backports import cached_property from .utils.conf import BOOLEAN_PARAMS, get_credentials from .utils.errors import OscError @@ -129,7 +130,7 @@ default_connection_retries = 5 default_retry_timeout = 5 - def __init__(self, url: str = None, username: typing.Optional[str] = None, + def __init__(self, url: typing.Optional[str] = None, username: typing.Optional[str] = None, password: typing.Optional[str] = None, verify: typing.Optional[str] = None, cache: bool = False, ssh_key_file: typing.Optional[typing.Union[Path, str]] = None): @@ -301,8 +302,8 @@ req = Request( method, url.replace("#", quote("#")).replace("?", quote("?")), - data=self.handle_params(data), - params=self.handle_params(params) + data=self.handle_params(url=url, method=method, params=data), + params=self.handle_params(url=url, method=method, params=params) ) prepped_req = session.prepare_request(req) prepped_req.headers['Content-Type'] = "application/octet-stream" @@ -341,15 +342,38 @@ logger.info("Server replied with status %d", response.status_code) logger.debug("Response headers:\n%s\n---", "\n".join(f"{k}: {v}" for k, v in response.headers.items())) - logger.debug("Response content:\n%s\n---", response.text) + if not stream: + # The response content must not be accessed when streaming + logger.debug("Response content:\n%s\n---", response.text) if raise_for_status: response.raise_for_status() return response return None - @staticmethod - def handle_params(params): + @cached_property + def _boolean_param_map(self) -> typing.Dict[typing.Pattern, typing.Dict[str, + typing.Tuple[str]]]: + """ + Return mapping table to identify boolean parameters for a given API endpoint + """ + return {re.compile(url): data for url, data in BOOLEAN_PARAMS.items()} + + def get_boolean_params(self, url: str, method: str) -> typing.Tuple[str]: + """ + Get the actual boolean parameter for ``url`` and ``method`` + + .. versionadded:: 0.7.3 + """ + parsed_url = urlparse(url) + for pattern, boolean_params_for_url in self._boolean_param_map.items(): + match = pattern.match(parsed_url.path) + if match: + return boolean_params_for_url.get(method, ()) + + return () + + def handle_params(self, url, method, params): """ Translate request parameters to API conform format @@ -366,8 +390,16 @@ :param params: Request parameter :type params: dict or str or io.BufferedReader + :param url: URL to which the parameters will be sent + :type url: str + :param method: HTTP method to send request + :type method: str :return: converted data ready to be used in HTTP request :rtype: dict or bytes + + .. versionchanged:: 0.7.3 + + Added the ``url`` and ``method`` parameters """ if isinstance(params, bytes): return params @@ -391,22 +423,21 @@ # See: https://github.com/openSUSE/open-build-service/issues/9715 # Also, there are parameters giving the impression that they are boolean, but actually are # not. + boolean_params = self.get_boolean_params(url=url, method=method) unexpected_bools = {key for key, value in params.items() - if isinstance(value, bool) and key not in BOOLEAN_PARAMS} + if isinstance(value, bool) and key not in boolean_params} if unexpected_bools: - warnings.warn(f"Received boolean query params, which are not expected to be: " - f"{', '.join(unexpected_bools)}") for key in unexpected_bools: params[key] = '1' if params[key] else '0' return "&".join( quote(str(key)) - if key in BOOLEAN_PARAMS + if key in boolean_params else f"{quote(str(key))}={quote(str(value))}" for key, value in ( (key, value) for key, value in params.items() - if not (key in BOOLEAN_PARAMS and value in [False, "0", 0, None, ""]) + if not (key in boolean_params and value in [False, "0", 0, None, ""]) ) if value is not None ).encode() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/tests/test_basic.py new/osc-tiny-0.7.3/osctiny/tests/test_basic.py --- old/osc-tiny-0.7.1/osctiny/tests/test_basic.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/tests/test_basic.py 2022-09-19 14:08:14.000000000 +0200 @@ -49,8 +49,9 @@ tmpfile2.unlink() def test_handle_params(self): - def _run(data, expected): - handled = self.osc.handle_params(data) + def _run(data, expected, url="https://api.example.com/source/PROJECT/PACKAGE", + method="GET"): + handled = self.osc.handle_params(url=url, method=method, params=data) self.assertEqual(handled, expected) data = ( @@ -79,7 +80,7 @@ {"view": "xml", "withissues": None}, b"view=xml" ), - # 'deleted' is a boolean param in the API + # 'deleted' is a boolean param in the project endpoint ( {"view": "xml", "deleted": 1}, b"view=xml&deleted" @@ -104,11 +105,33 @@ {"view": "xml", "deleted": True}, b"view=xml&deleted" ), + # 'expand' is no boolean param in the project endpoint + ( + {"expand": True}, + b"expand=1", + "https://api.example.com/source/PROJECT", + ), + ( + {"expand": False}, + b"expand=0", + "https://api.example.com/source/PROJECT", + ), + # 'expand' is a boolean param in the package endpoint + ( + {"expand": False}, + b"", + "https://api.example.com/source/PROJECT/PACKAGE", + ), + ( + {"expand": True}, + b"expand", + "https://api.example.com/source/PROJECT/PACKAGE", + ), ) - for params, expected in data: - with self.subTest(params): - _run(params, expected) + for dataset in data: + with self.subTest(dataset[0]): + _run(*dataset) def test_attrib_regexp(self): def _run(attr, expected): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/tests/test_projects.py new/osc-tiny-0.7.3/osctiny/tests/test_projects.py --- old/osc-tiny-0.7.1/osctiny/tests/test_projects.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/tests/test_projects.py 2022-09-19 14:08:14.000000000 +0200 @@ -1,7 +1,7 @@ import re from urllib.parse import urlparse, parse_qs -from lxml.objectify import fromstring +from lxml.objectify import fromstring, Element from requests.exceptions import HTTPError import responses @@ -25,7 +25,7 @@ """ parsed = urlparse(request.url) params.update(parse_qs(parsed.query, keep_blank_values=True)) - if "deleted" in params: + if params["deleted"] == ["1"]: status = 403 body = """<status code="no_permission_for_deleted"> <summary>only admins can see deleted projects</summary> @@ -113,7 +113,7 @@ ) @responses.activate - def test_put_meta(self): + def test_set_meta(self): def callback(headers, params, request): status = 500 body = "<status code='error'></status>" @@ -122,9 +122,10 @@ except Exception: pass else: - match = re.search("source/(?P<project>[^/]+)/_meta", - request.url) - if match and meta.get("name") == match.group("project"): + match = re.search("source/(?P<project>[^/]+)/_meta", request.url) + children = meta.getchildren() + if match and meta.get("name") == match.group("project") \ + and children[0].tag == "title": status = 200 body = """<status code='ok'></status>""" @@ -138,7 +139,7 @@ ) with self.subTest("Valid request"): - self.assertTrue(self.osc.projects.put_meta( + self.assertTrue(self.osc.projects.set_meta( project="test", title="foo bar", description="lorem ipsum dolor sit amet ..." @@ -153,6 +154,29 @@ data=TEMPLATE_META ) + with self.subTest("Valid Metafile"): + meta = fromstring(TEMPLATE_META) + meta.set("name", "project:foo") + meta.title._setText("Hello World") + self.assertTrue(self.osc.projects.set_meta(project="project:foo", metafile=meta)) + sent_meta = fromstring(responses.calls[-1].request.body.decode()) + self.assertEqual(sent_meta.title.text, "Hello World") + + with self.subTest("Bare Metafile"): + meta = Element("project") + self.assertTrue(self.osc.projects.set_meta( + project="project:foo", + metafile=meta, + title="test title", + description="test description", + bugowner="bugowner", + maintainer="maintainer" + )) + sent_meta = fromstring(responses.calls[-1].request.body.decode()) + self.assertEqual(sent_meta.title.text, "test title") + self.assertEqual(sent_meta.description.text, "test description") + self.assertEqual(len(sent_meta.xpath("person")), 2) + @responses.activate def test_get_files(self): def callback(headers, params, request): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/utils/backports.py new/osc-tiny-0.7.3/osctiny/utils/backports.py --- old/osc-tiny-0.7.1/osctiny/utils/backports.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/utils/backports.py 2022-09-19 14:08:14.000000000 +0200 @@ -16,3 +16,11 @@ return fun return wrapper + + +try: + # pylint: disable=unused-import + from functools import cached_property +except ImportError: + # Support for Python3 prior 3.8 + from cached_property import cached_property diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/osctiny/utils/conf.py new/osc-tiny-0.7.3/osctiny/utils/conf.py --- old/osc-tiny-0.7.1/osctiny/utils/conf.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/osctiny/utils/conf.py 2022-09-19 14:08:14.000000000 +0200 @@ -23,27 +23,22 @@ # Query parameters that are considered to be boolean by the build service -BOOLEAN_PARAMS = ( - "add_repositories", - "deleted", - "emptylink", - "expand", - "extend_package_names", - "extend_package_names", - "ignoredevel", - "keeplink", - "lastbuild", - "lastworking", - "locallink", - "meta", - "multibuild", - "noaccess", - "parse", - "repairlink", - "update_path_elements", - "withdownloadurl", - "withlinked", -) +BOOLEAN_PARAMS = { + "^/source/[^/]+/[^/]+/?$": { + 'GET': ('emptylink', 'expand', 'meta', 'lastworking', 'withlinked', 'deleted', 'parse'), + 'POST': ('ignoredevel', 'add_repositories', 'noaccess', 'update_path_elements', + 'extend_package_names', 'extend_package_names', 'keeplink', 'repairlink') + }, + "^/source/[^/]+/[^/]+/[^/]+$": { + 'PUT': ('keeplink',) + }, + "^/build/[^/]+/_result$": { + 'GET': ('lastbuild', 'locallink', 'multibuild') + }, + "search/published/(binary|repoinfo|pattern)/id$": { + 'GET': ('withdownloadurl',) + } +} def get_config_path() -> Path: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.7.1/setup.py new/osc-tiny-0.7.3/setup.py --- old/osc-tiny-0.7.1/setup.py 2022-08-16 14:32:35.000000000 +0200 +++ new/osc-tiny-0.7.3/setup.py 2022-09-19 14:08:14.000000000 +0200 @@ -26,7 +26,7 @@ setup( name='osc-tiny', - version='0.7.1', + version='0.7.3', description='Client API for openSUSE BuildService', long_description=long_description, long_description_content_type="text/markdown",