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 2021-11-03 17:26:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-osc-tiny (Old)
 and      /work/SRC/openSUSE:Factory/.python-osc-tiny.new.1890 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-osc-tiny"

Wed Nov  3 17:26:21 2021 rev:10 rq:928959 version:0.4.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-osc-tiny/python-osc-tiny.changes  
2021-03-02 12:46:10.284392588 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-osc-tiny.new.1890/python-osc-tiny.changes    
    2021-11-03 17:27:17.481370874 +0100
@@ -1,0 +2,11 @@
+Wed Sep 22 10:45:07 UTC 2021 - Andreas Hasenkopf <ahasenk...@suse.com>
+
+- Release 0.4.1
+  * Bugfix for configuration utilities
+
+- Release 0.4.0
+  * Added support to parse `osc` configuration
+  * Bugfix for `origin` extension
+  
+
+-------------------------------------------------------------------

Old:
----
  osc-tiny-0.3.3.tar.gz

New:
----
  osc-tiny-0.4.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-osc-tiny.spec ++++++
--- /var/tmp/diff_new_pack.ZIVeAq/_old  2021-11-03 17:27:17.921371114 +0100
+++ /var/tmp/diff_new_pack.ZIVeAq/_new  2021-11-03 17:27:17.925371116 +0100
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-osc-tiny
-Version:        0.3.3
+Version:        0.4.1
 Release:        0
 Summary:        Client API for openSUSE BuildService
 License:        MIT

++++++ osc-tiny-0.3.3.tar.gz -> osc-tiny-0.4.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/PKG-INFO new/osc-tiny-0.4.1/PKG-INFO
--- old/osc-tiny-0.3.3/PKG-INFO 2021-02-24 11:35:58.000000000 +0100
+++ new/osc-tiny-0.4.1/PKG-INFO 2021-09-22 11:55:15.028164000 +0200
@@ -1,58 +1,12 @@
 Metadata-Version: 2.1
 Name: osc-tiny
-Version: 0.3.3
+Version: 0.4.1
 Summary: Client API for openSUSE BuildService
 Home-page: http://github.com/crazyscientist/osc-tiny
 Author: Andreas Hasenkopf
 Author-email: ahasenk...@suse.com
 License: MIT
 Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master
-Description: OSC Tiny
-        ========
-        
-        [![Build 
Status](https://travis-ci.com/crazyscientist/osc-tiny.svg?branch=master)](https://travis-ci.com/crazyscientist/osc-tiny)
-        [![Coverage 
Status](https://coveralls.io/repos/github/crazyscientist/osc-tiny/badge.svg)](https://coveralls.io/github/crazyscientist/osc-tiny)
-        [![PyPI 
version](https://badge.fury.io/py/osc-tiny.svg)](https://badge.fury.io/py/osc-tiny)
-        
-        This project aims to provide a minimalistic and transparent client for 
accessing
-        the [OpenBuildService](https://openbuildservice.org/) 
-        [API](https://build.opensuse.org/apidocs/index).
-        
-        Usage
-        -----
-        
-        This is a very basic example:
-        
-        ```python
-        from osctiny import Osc
-        
-        osc = Osc(
-            url="https://api.opensuse.org";,
-            username="foobar",
-            password="helloworld",
-        )
-        
-        # This returns an LXML object
-        osc.requests.get(request_id=1)
-        
-        # This returns an LXML object
-        osc.search.request(xpath="state/@name='new'")
-        ```
-        
-        For more documentation see https://osc-tiny.readthedocs.io/en/latest/
-        
-        Contributing
-        ------------
-        
-        Any contributions are welcome.
-        
-        Links
-        -----
-        
-        * https://osc-tiny.readthedocs.io/en/latest/
-        * https://openbuildservice.org/
-        * https://build.opensuse.org/apidocs/index
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
@@ -64,3 +18,52 @@
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Description-Content-Type: text/markdown
+License-File: LICENSE
+
+OSC Tiny
+========
+
+[![Build 
Status](https://travis-ci.com/crazyscientist/osc-tiny.svg?branch=master)](https://travis-ci.com/crazyscientist/osc-tiny)
+[![Coverage 
Status](https://coveralls.io/repos/github/crazyscientist/osc-tiny/badge.svg)](https://coveralls.io/github/crazyscientist/osc-tiny)
+[![PyPI 
version](https://badge.fury.io/py/osc-tiny.svg)](https://badge.fury.io/py/osc-tiny)
+
+This project aims to provide a minimalistic and transparent client for 
accessing
+the [OpenBuildService](https://openbuildservice.org/) 
+[API](https://build.opensuse.org/apidocs/index).
+
+Usage
+-----
+
+This is a very basic example:
+
+```python
+from osctiny import Osc
+
+osc = Osc(
+    url="https://api.opensuse.org";,
+    username="foobar",
+    password="helloworld",
+)
+
+# This returns an LXML object
+osc.requests.get(request_id=1)
+
+# This returns an LXML object
+osc.search.request(xpath="state/@name='new'")
+```
+
+For more documentation see https://osc-tiny.readthedocs.io/en/latest/
+
+Contributing
+------------
+
+Any contributions are welcome.
+
+Links
+-----
+
+* https://osc-tiny.readthedocs.io/en/latest/
+* https://openbuildservice.org/
+* https://build.opensuse.org/apidocs/index
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osc_tiny.egg-info/PKG-INFO 
new/osc-tiny-0.4.1/osc_tiny.egg-info/PKG-INFO
--- old/osc-tiny-0.3.3/osc_tiny.egg-info/PKG-INFO       2021-02-24 
11:35:58.000000000 +0100
+++ new/osc-tiny-0.4.1/osc_tiny.egg-info/PKG-INFO       2021-09-22 
11:55:15.000000000 +0200
@@ -1,58 +1,12 @@
 Metadata-Version: 2.1
 Name: osc-tiny
-Version: 0.3.3
+Version: 0.4.1
 Summary: Client API for openSUSE BuildService
 Home-page: http://github.com/crazyscientist/osc-tiny
 Author: Andreas Hasenkopf
 Author-email: ahasenk...@suse.com
 License: MIT
 Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master
-Description: OSC Tiny
-        ========
-        
-        [![Build 
Status](https://travis-ci.com/crazyscientist/osc-tiny.svg?branch=master)](https://travis-ci.com/crazyscientist/osc-tiny)
-        [![Coverage 
Status](https://coveralls.io/repos/github/crazyscientist/osc-tiny/badge.svg)](https://coveralls.io/github/crazyscientist/osc-tiny)
-        [![PyPI 
version](https://badge.fury.io/py/osc-tiny.svg)](https://badge.fury.io/py/osc-tiny)
-        
-        This project aims to provide a minimalistic and transparent client for 
accessing
-        the [OpenBuildService](https://openbuildservice.org/) 
-        [API](https://build.opensuse.org/apidocs/index).
-        
-        Usage
-        -----
-        
-        This is a very basic example:
-        
-        ```python
-        from osctiny import Osc
-        
-        osc = Osc(
-            url="https://api.opensuse.org";,
-            username="foobar",
-            password="helloworld",
-        )
-        
-        # This returns an LXML object
-        osc.requests.get(request_id=1)
-        
-        # This returns an LXML object
-        osc.search.request(xpath="state/@name='new'")
-        ```
-        
-        For more documentation see https://osc-tiny.readthedocs.io/en/latest/
-        
-        Contributing
-        ------------
-        
-        Any contributions are welcome.
-        
-        Links
-        -----
-        
-        * https://osc-tiny.readthedocs.io/en/latest/
-        * https://openbuildservice.org/
-        * https://build.opensuse.org/apidocs/index
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
@@ -64,3 +18,52 @@
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Description-Content-Type: text/markdown
+License-File: LICENSE
+
+OSC Tiny
+========
+
+[![Build 
Status](https://travis-ci.com/crazyscientist/osc-tiny.svg?branch=master)](https://travis-ci.com/crazyscientist/osc-tiny)
+[![Coverage 
Status](https://coveralls.io/repos/github/crazyscientist/osc-tiny/badge.svg)](https://coveralls.io/github/crazyscientist/osc-tiny)
+[![PyPI 
version](https://badge.fury.io/py/osc-tiny.svg)](https://badge.fury.io/py/osc-tiny)
+
+This project aims to provide a minimalistic and transparent client for 
accessing
+the [OpenBuildService](https://openbuildservice.org/) 
+[API](https://build.opensuse.org/apidocs/index).
+
+Usage
+-----
+
+This is a very basic example:
+
+```python
+from osctiny import Osc
+
+osc = Osc(
+    url="https://api.opensuse.org";,
+    username="foobar",
+    password="helloworld",
+)
+
+# This returns an LXML object
+osc.requests.get(request_id=1)
+
+# This returns an LXML object
+osc.search.request(xpath="state/@name='new'")
+```
+
+For more documentation see https://osc-tiny.readthedocs.io/en/latest/
+
+Contributing
+------------
+
+Any contributions are welcome.
+
+Links
+-----
+
+* https://osc-tiny.readthedocs.io/en/latest/
+* https://openbuildservice.org/
+* https://build.opensuse.org/apidocs/index
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osc_tiny.egg-info/SOURCES.txt 
new/osc-tiny-0.4.1/osc_tiny.egg-info/SOURCES.txt
--- old/osc-tiny-0.3.3/osc_tiny.egg-info/SOURCES.txt    2021-02-24 
11:35:58.000000000 +0100
+++ new/osc-tiny-0.4.1/osc_tiny.egg-info/SOURCES.txt    2021-09-22 
11:55:15.000000000 +0200
@@ -37,9 +37,12 @@
 osctiny/tests/test_requests.py
 osctiny/tests/test_search.py
 osctiny/tests/test_utils.py
+osctiny/tests/osc/__init__.py
+osctiny/tests/osc/conf.py
 osctiny/utils/__init__.py
 osctiny/utils/backports.py
 osctiny/utils/base.py
 osctiny/utils/changelog.py
+osctiny/utils/conf.py
 osctiny/utils/errors.py
 osctiny/utils/mapping.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/__init__.py 
new/osc-tiny-0.4.1/osctiny/__init__.py
--- old/osc-tiny-0.3.3/osctiny/__init__.py      2021-02-24 11:35:35.000000000 
+0100
+++ new/osc-tiny-0.4.1/osctiny/__init__.py      2021-09-22 11:55:07.000000000 
+0200
@@ -6,4 +6,4 @@
 
 __all__ = ['Osc', 'bs_requests', 'buildresults', 'comments', 'packages',
            'projects', 'search', 'users']
-__version__ = "0.3.3"
+__version__ = "0.4.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/extensions/origin.py 
new/osc-tiny-0.4.1/osctiny/extensions/origin.py
--- old/osc-tiny-0.3.3/osctiny/extensions/origin.py     2021-02-24 
11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/extensions/origin.py     2021-09-22 
11:55:07.000000000 +0200
@@ -25,7 +25,6 @@
 import re
 from warnings import warn
 
-from lxml.objectify import ObjectifiedElement
 from yaml import load
 
 from ..utils.backports import lru_cache
@@ -280,10 +279,15 @@
 
         This project is important, because it's meta can provide the list of 
maintained projects.
 
-        :return: Objectified XML element or ``None``
-        :rtype: lxml.objectify.ObjectifiedElement
+        :return: Project name or ``None``
+        :rtype: str or None
+
+        .. versionchanged:: 0.4.0
+
+            Return string instead of XML object
         """
-        response = 
self.osc.search.project(xpath="attribute/@name='OBS:MaintenanceProject'")
+        response = self.osc.search.search(path="project/id",
+                                          
xpath="attribute/@name='OBS:MaintenanceProject'")
         projects = getattr(response, "project", [])
         if len(projects) < 1:
             warn("The build service defines no maintenance projects!")
@@ -291,25 +295,26 @@
         if len(projects) > 1:
             warn("The build service defines multiple maintenance projects!")
 
-        return projects[0]
+        return projects[0].get("name")
 
     @cached_property
-    def maintained_projects(self, from_project=None):
+    def maintained_projects(self):
         """
         Get the list of maintained projects
 
-        By default this property uses :py:meth:`maintenance_project` and 
allows usage of a specified
-        ``from_project`` instead.
+        Maintained projects are identified by the presence of the 
``OBS:Maintained`` attribute.
+
+        :return: Project names
+        :rtype: List of str
 
-        :param from_project: The maintenance project to query
-        :return: Objectified XML element
-        :rtype: lxml.objectify.ObjectifiedElement
-        """
-        from_project = from_project or self.maintenance_project
-        if not isinstance(from_project, ObjectifiedElement):
-            from_project = self.osc.projects.get_meta(project=from_project)
+        .. versionchanged:: 0.4.0
 
-        return [m.get("project") for m in 
from_project.xpath("maintenance/maintains")]
+            Search maintained projects via the ``OBS:Maintained`` attribute 
and not via the
+            maintenance project.
+        """
+        response = self.osc.search.search(path="project/id",
+                                          
xpath="attribute/@name='OBS:Maintained'")
+        return [project.get("name") for project in getattr(response, 
"project", [])]
 
     @lru_cache(maxsize=16)
     def get_project_origin_config(self, project):
@@ -529,6 +534,10 @@
         :return: Generator of pairs: ('pkg', 'origin')
         :rtype: generator
         """
+        if project not in self.expanded_origins:
+            warn("Project {} has no origin definition".format(project))
+            return
+
         packages = self.osc.projects.get_files(project, expand='1')
 
         for package in getattr(packages, "entry", []):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/osc.py 
new/osc-tiny-0.4.1/osctiny/osc.py
--- old/osc-tiny-0.3.3/osctiny/osc.py   2021-02-24 11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/osc.py   2021-09-22 11:55:07.000000000 +0200
@@ -26,6 +26,8 @@
 from .extensions.bs_requests import Request as BsRequest
 from .extensions.search import Search
 from .extensions.users import Group, Person
+from .utils.conf import get_credentials
+from .utils.errors import OscError
 
 try:
     from cachecontrol import CacheControl
@@ -78,6 +80,7 @@
     :param password: Password for login
     :param verify: See `SSL Cert Verification`_ for more details
     :param cache: Store API responses in a cache
+    :raises osctiny.errors.OscError: if no credentials are provided
 
     .. versionadded:: 0.1.1
         The ``cache`` parameter and the ``build`` extension
@@ -94,6 +97,9 @@
     .. versionadded:: 0.3.0
         The ``origins`` extension
 
+    .. versionchanged:: 0.4.0
+        Raises an exception when no credentials are provided
+
     .. _SSL Cert Verification:
         http://docs.python-requests.org/en/master/user/advanced/
         #ssl-cert-verification
@@ -113,6 +119,13 @@
         self.url = url or self.url
         self.username = username or self.username
         self.password = password or self.password
+
+        if not self.username and not self.password:
+            try:
+                self.username, self.password = get_credentials(self.url)
+            except (ValueError, NotImplementedError, FileNotFoundError) as 
error:
+                raise OscError from error
+
         self._session = Session()
         self._session.verify = verify or get_default_verify_paths().capath
         self.auth = HTTPBasicAuth(self.username, self.password)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/tests/osc/__init__.py 
new/osc-tiny-0.4.1/osctiny/tests/osc/__init__.py
--- old/osc-tiny-0.3.3/osctiny/tests/osc/__init__.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/tests/osc/__init__.py    2021-09-22 
11:55:07.000000000 +0200
@@ -0,0 +1,5 @@
+"""
+Mocking of the `osc` package
+
+Without the presence of a dummy module like this one, mocking on Python 3.6 
fails.
+"""
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/tests/test_origin.py 
new/osc-tiny-0.4.1/osctiny/tests/test_origin.py
--- old/osc-tiny-0.3.3/osctiny/tests/test_origin.py     2021-02-24 
11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/tests/test_origin.py     2021-09-22 
11:55:07.000000000 +0200
@@ -85,30 +85,29 @@
                 status = 200
                 body = """
                 <collection matches="1">
-                    <project name="openSUSE:Maintenance" kind="maintenance">
-                      <title>The openSUSE Maintenance project</title>
-                      <description/>
-                      <group groupid="maintenance-opensuse.org" 
role="maintainer"/>
-                      <maintenance>
-                        <maintains project="openSUSE:Leap:15.1:Update"/>
-                        <maintains project="openSUSE:Leap:15.2:Update"/>
-                      </maintenance>
-                    </project>
-                    </collection>
+                    <project name="openSUSE:Maintenance"/>
+                </collection>
+                """
+            elif "OBS:Maintained" in "".join(params.get("match", [])):
+                status = 200
+                body = """
+                <collection matches="2">
+                    <project name="openSUSE:Leap:15.1:Update"/>
+                    <project name="openSUSE:Leap:15.2:Update"/>
+                </collection>
                 """
 
             return status, headers, body
 
         self.mock_request(
             method=responses.GET,
-            url=re.compile(self.osc.url + "/search/project/?"),
+            url=re.compile(self.osc.url + "/search/project/id"),
             callback=CallbackFactory(callback)
         )
 
         with self.subTest("Maintenance Project"):
             maint_project = self.osc.origins.maintenance_project
-            self.assertEqual(maint_project.get("name"), "openSUSE:Maintenance")
-            self.assertEqual(maint_project.get("kind"), "maintenance")
+            self.assertEqual(maint_project, "openSUSE:Maintenance")
 
         with self.subTest("Maintained Projects"):
             self.assertEqual(self.osc.origins.maintained_projects,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/tests/test_utils.py 
new/osc-tiny-0.4.1/osctiny/tests/test_utils.py
--- old/osc-tiny-0.3.3/osctiny/tests/test_utils.py      2021-02-24 
11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/tests/test_utils.py      2021-09-22 
11:55:07.000000000 +0200
@@ -1,8 +1,12 @@
 # -*- coding: utf-8 -*-
+from base64 import b64encode
+from bz2 import compress
 from unittest import TestCase, mock
 from datetime import datetime
 from io import StringIO
-from os import remove
+import os
+from pathlib import Path
+import sys
 from tempfile import mkstemp
 from types import GeneratorType
 
@@ -10,7 +14,9 @@
 from pytz import _UTC, timezone
 
 from ..utils.changelog import ChangeLog, Entry
+from ..utils.conf import get_config_path, get_credentials
 
+sys.path.append(os.path.dirname(__file__))
 
 SAMPLE_CHANGES = """
 -------------------------------------------------------------------
@@ -167,7 +173,7 @@
                 self.assertIsInstance(cl.entries, list)
                 self.assertEqual(len(cl.entries), 2)
         finally:
-            remove(path)
+            os.remove(path)
 
     def test_parse_generative(self):
         with mock.patch("osctiny.utils.changelog.open",
@@ -309,3 +315,62 @@
 
         self.assertEqual(wmock.call_count, 1)
         self.assertIn("Cannot parse changelog entry", wmock.call_args[0][0])
+
+
+@mock.patch("osc.conf", side_effect=ImportError, create=True)
+@mock.patch("pathlib.Path.is_file", return_value=True)
+class TestConfig(TestCase):
+    def test_get_config_path(self, *_):
+        with self.subTest("No env vars"):
+            with mock.patch.dict(os.environ, values={}, clear=True):
+                self.assertEqual(Path.home().joinpath(".oscrc"), 
get_config_path())
+
+        with self.subTest("OSC_CONFIG"):
+            osc_config = "/foo/bar/oscrc"
+            with mock.patch.dict(os.environ, values={'OSC_CONFIG': 
osc_config}, clear=True):
+                self.assertEqual(Path(osc_config), get_config_path())
+
+    def test_get_credentials(self, *_):
+        _, path1 = mkstemp()
+        _, path2 = mkstemp()
+
+        expected_insecure_credentials = ("my-dummy-user", 
"my-insecure-dummy-password")
+        expected_secure_credentials = ('my-dummy-user', 
'my-secure-dummy-password')
+
+        with open(path1, "w") as handle:
+            handle.write("[http://api.dummy-bs.org]\n";)
+            
handle.write("user={}\npass={}\n".format(*expected_insecure_credentials))
+
+        with open(path2, "w") as handle:
+            handle.write("[general]\n")
+            handle.write("apiurl=http://api.dummy-bs.org\n";)
+            handle.write("[http://api.dummy-bs.org]\n";)
+            handle.write("user={}\n".format(expected_secure_credentials[0]))
+            handle.write("passx={}\n".format(b64encode(compress(
+                expected_secure_credentials[1].encode("ascii")
+            )).decode("ascii")))
+
+        try:
+            with self.subTest("No URL, no default"):
+                with mock.patch.dict(os.environ, values={'OSC_CONFIG': path1}, 
clear=True):
+                    self.assertRaises(ValueError, get_credentials)
+
+            with self.subTest("Wrong URL"):
+                with mock.patch.dict(os.environ, values={'OSC_CONFIG': path1}, 
clear=True):
+                    self.assertRaises(ValueError, get_credentials, 
"http://google.de";)
+
+            with self.subTest("Wrong scheme"):
+                with mock.patch.dict(os.environ, values={'OSC_CONFIG': path1}, 
clear=True):
+                    self.assertRaises(ValueError, get_credentials, 
"https://api.dummy-bs.org";)
+
+            with self.subTest("Correct URL"):
+                with mock.patch.dict(os.environ, values={'OSC_CONFIG': path1}, 
clear=True):
+                    credentials = get_credentials("http://api.dummy-bs.org";)
+                self.assertEqual(expected_insecure_credentials, credentials)
+
+            with self.subTest("No URL"):
+                with mock.patch.dict(os.environ, values={'OSC_CONFIG': path2}, 
clear=True):
+                    self.assertEqual(expected_secure_credentials, 
get_credentials())
+        finally:
+            os.remove(path1)
+            os.remove(path2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/utils/changelog.py 
new/osc-tiny-0.4.1/osctiny/utils/changelog.py
--- old/osc-tiny-0.3.3/osctiny/utils/changelog.py       2021-02-24 
11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/osctiny/utils/changelog.py       2021-09-22 
11:55:07.000000000 +0200
@@ -149,13 +149,13 @@
         :param handle: An open and iterable (file) handle
         :type handle: Any derived object of :py:class:`io.IOBase`
         """
-        # pylint: disable=too-many-branches
+        # pylint: disable=too-many-branches,consider-using-with
         entry = self.entry_factory()
 
         if isinstance(handle, TextIOBase):
             handle.seek(0)
         elif isinstance(handle, (str, bytes)):
-            handle = open(handle, "r")
+            handle = open(handle, "r")  # pylint: disable=consider-using-with
         else:
             raise TypeError("Unexpected type for 'path': {}".format(
                 type(handle)))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/osctiny/utils/conf.py 
new/osc-tiny-0.4.1/osctiny/utils/conf.py
--- old/osc-tiny-0.3.3/osctiny/utils/conf.py    1970-01-01 01:00:00.000000000 
+0100
+++ new/osc-tiny-0.4.1/osctiny/utils/conf.py    2021-09-22 11:55:07.000000000 
+0200
@@ -0,0 +1,107 @@
+"""
+Configuration utilities
+^^^^^^^^^^^^^^^^^^^^^^^
+
+This module provides a collection of utilities to access the configuration of
+`osc <https://github.com/openSUSE/osc>`_ in order to make it easier to create 
command line tools
+with OSC Tiny.
+
+.. versionadded:: 0.4.0
+"""
+from base64 import b64decode
+from bz2 import decompress
+from configparser import ConfigParser, NoSectionError
+import os
+from pathlib import Path
+import warnings
+
+try:
+    from osc import conf as _conf
+except ImportError:
+    _conf = None
+
+
+def get_config_path():
+    """
+    Return path of ``osc`` configuration file
+
+    :return: Path
+    :raises FileNotFoundError: if no config file found
+    """
+    env_path = os.environ.get("OSC_CONFIG", None)
+    conf_path = os.environ.get('XDG_CONFIG_HOME', '~/.config')
+    if env_path:
+        path = Path(env_path)
+        if path.is_file():
+            return path
+
+    for path in (Path.home().joinpath(".oscrc"),
+                 Path(conf_path).joinpath("osc/oscrc").expanduser()):
+        if path.is_file():
+            return path
+
+    raise FileNotFoundError("No `osc` configuration file found")
+
+
+def get_credentials(url=None):
+    """
+    Get credentials for Build Service instance identified by ``url``
+
+    .. important::
+
+        If the ``osc`` package is not installed, this function will only try 
to extract the username
+        and password from the configuration file.
+
+        Any credentials stored on a keyring will not be accessible!
+
+    :param str url: URL of Build Service instance (including schema). If not 
specified, the value
+                    from the ``apiurl`` parameter in the config file will be 
used.
+    :return: (username, password)
+    :raises ValueError: if config provides no credentials
+    """
+    if _conf is not None:
+        # pylint: disable=protected-access
+        parser = _conf.get_configParser()
+        try:
+            if url is None:
+                url = parser["general"].get("apiurl", url)
+            cred_mgr = _conf._get_credentials_manager(url, parser)
+            username = _conf._extract_user_compat(parser, url, cred_mgr)
+        except (KeyError, NoSectionError) as error:
+            raise ValueError("`osc` config does not provide the default API 
URL") from error
+
+        if not username:
+            raise ValueError("`osc` config provides no username for URL 
{}".format(url))
+        password = cred_mgr.get_password(url, username, defer=False)
+        if not password:
+            raise ValueError("`osc` config provides no password for URL 
{}".format(url))
+        return username, password
+
+    warnings.warn("`osc` is not installed. Not all configuration backends of 
`osc` will be "
+                  "available.")
+    parser = ConfigParser()
+    path = get_config_path()
+    parser.read((path))
+    try:
+        if url is None:
+            url = parser["general"].get("apiurl", url)
+    except (KeyError, NoSectionError) as error:
+        raise ValueError("`osc` config does not provide the default API URL") 
from error
+
+    if url not in parser.sections():
+        raise ValueError("`osc` config has no section for URL {}".format(url))
+
+    username = parser[url].get("user", None)
+    if not username:
+        raise ValueError("`osc` config provides no username for URL 
{}".format(url))
+
+    password = parser[url].get("pass", None)
+    if not password:
+        password = parser[url].get("passx", None)
+        if password:
+            password = 
decompress(b64decode(password.encode("ascii"))).decode("ascii")
+
+    if not password:
+        raise ValueError("`osc` config provides no password for URL 
{}".format(url))
+
+    return username, password
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osc-tiny-0.3.3/setup.py new/osc-tiny-0.4.1/setup.py
--- old/osc-tiny-0.3.3/setup.py 2021-02-24 11:35:35.000000000 +0100
+++ new/osc-tiny-0.4.1/setup.py 2021-09-22 11:55:07.000000000 +0200
@@ -19,7 +19,7 @@
 
 setup(
     name='osc-tiny',
-    version='0.3.3',
+    version='0.4.1',
     description='Client API for openSUSE BuildService',
     long_description=long_description,
     long_description_content_type="text/markdown",

Reply via email to