Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-devpi-client for openSUSE:Factory checked in at 2023-04-20 15:14:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-devpi-client (Old) and /work/SRC/openSUSE:Factory/.python-devpi-client.new.2023 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-devpi-client" Thu Apr 20 15:14:14 2023 rev:8 rq:1080284 version:6.0.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-devpi-client/python-devpi-client.changes 2023-04-10 21:24:49.540743825 +0200 +++ /work/SRC/openSUSE:Factory/.python-devpi-client.new.2023/python-devpi-client.changes 2023-04-20 15:15:13.730169163 +0200 @@ -1,0 +2,14 @@ +Wed Apr 19 09:33:04 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 6.0.4: + * Fix precedence of URL from command line over DEVPI_INDEX + environment variable for ``devpi use``. + * Fix relative DEVPI_INDEX environment variable with user and + index causing an invalid URL in some cases. + * Fix persistence of username when DEVPI_INDEX environment + variable is used with ``devpi login``. + * Fix precedence of ``--sdist`` and ``--wheel`` over + ``formats`` setting from setup.cfg ``[devpi:upload]`` + section. + +------------------------------------------------------------------- Old: ---- devpi-client-6.0.3.tar.gz New: ---- devpi-client-6.0.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-devpi-client.spec ++++++ --- /var/tmp/diff_new_pack.cStjZO/_old 2023-04-20 15:15:14.286172936 +0200 +++ /var/tmp/diff_new_pack.cStjZO/_new 2023-04-20 15:15:14.290172963 +0200 @@ -17,7 +17,7 @@ Name: python-devpi-client -Version: 6.0.3 +Version: 6.0.4 Release: 0 Summary: Client for devpi License: MIT ++++++ devpi-client-6.0.3.tar.gz -> devpi-client-6.0.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/CHANGELOG new/devpi-client-6.0.4/CHANGELOG --- old/devpi-client-6.0.3/CHANGELOG 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/CHANGELOG 2023-04-13 07:14:01.000000000 +0200 @@ -2,6 +2,21 @@ .. towncrier release notes start +6.0.4 (2023-04-13) +================== + +Bug Fixes +--------- + +- Fix precedence of URL from command line over DEVPI_INDEX environment variable for ``devpi use``. + +- Fix relative DEVPI_INDEX environment variable with user and index causing an invalid URL in some cases. + +- Fix persistence of username when DEVPI_INDEX environment variable is used with ``devpi login``. + +- Fix precedence of ``--sdist`` and ``--wheel`` over ``formats`` setting from setup.cfg ``[devpi:upload]`` section. + + 6.0.3 (2023-02-20) ================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/PKG-INFO new/devpi-client-6.0.4/PKG-INFO --- old/devpi-client-6.0.3/PKG-INFO 2023-02-20 10:11:16.122493500 +0100 +++ new/devpi-client-6.0.4/PKG-INFO 2023-04-13 07:14:10.173057600 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: devpi-client -Version: 6.0.3 +Version: 6.0.4 Summary: devpi upload/install/... workflow commands for Python developers Home-page: https://devpi.net Maintainer: Florian Schulze @@ -61,6 +61,21 @@ .. towncrier release notes start +6.0.4 (2023-04-13) +================== + +Bug Fixes +--------- + +- Fix precedence of URL from command line over DEVPI_INDEX environment variable for ``devpi use``. + +- Fix relative DEVPI_INDEX environment variable with user and index causing an invalid URL in some cases. + +- Fix persistence of username when DEVPI_INDEX environment variable is used with ``devpi login``. + +- Fix precedence of ``--sdist`` and ``--wheel`` over ``formats`` setting from setup.cfg ``[devpi:upload]`` section. + + 6.0.3 (2023-02-20) ================== @@ -166,12 +181,3 @@ - When there is no json error message only the HTML error code and reason is printed now, to get the full HTML output use the ``--debug`` flag. - -5.2.3 (2021-11-15) -================== - -Bug Fixes ---------- - -- Bump upper version limit on pluggy to <2.0. - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/devpi/__init__.py new/devpi-client-6.0.4/devpi/__init__.py --- old/devpi-client-6.0.3/devpi/__init__.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/devpi/__init__.py 2023-04-13 07:14:01.000000000 +0200 @@ -1,2 +1,2 @@ -__version__ = '6.0.3' +__version__ = '6.0.4' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/devpi/main.py new/devpi-client-6.0.4/devpi/main.py --- old/devpi-client-6.0.3/devpi/main.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/devpi/main.py 2023-04-13 07:14:01.000000000 +0200 @@ -258,30 +258,65 @@ finally: rmtree(workdir.strpath, onerror=remove_readonly) - @cached_property - def current(self): + def get_current(self, args_url=None): self.clientdir.ensure(dir=1) current = PersistentCurrent(self.auth_path, self.current_path) - url = getattr(self.args, "index", None) + index_url = getattr(self.args, "index", None) if "DEVPI_INDEX" in os.environ: - if url is None: - if current.index is None: + devpi_index = os.environ["DEVPI_INDEX"] + self.debug("Got DEVPI_INDEX from environment: %s", devpi_index) + if args_url is not None: + self.info( + "Using index URL from command line instead of " + "DEVPI_INDEX (%s) from environment." % devpi_index) + # cache in case get_current was called directly + self.__dict__['current'] = current + return current + if URL(devpi_index).is_valid_http_url(): + # switch to full DEVPI_INDEX URL, so possible relative + # --index switches work + current.persist_index = False + current.configure_fromurl(self, devpi_index) + if index_url is None: + if current.index is None or '/' in devpi_index: url = current.root_url else: url = current.index_url + url = url.joinpath(devpi_index) + if not current.root_url.is_valid_http_url() and not url.is_valid_http_url(): + self.fatal( + "No server set and DEVPI_INDEX from environment is not a " + "full valid URL: %s" % devpi_index) + self.info("Using DEVPI_INDEX from environment: %s" % devpi_index) else: - url = URL(url) - devpi_index = os.environ["DEVPI_INDEX"] - url = url.joinpath(devpi_index) - if not current.root_url.is_valid_http_url() and not url.is_valid_http_url(): - self.fatal( - "No server set and DEVPI_INDEX from environment is not a " - "full valid URL: %s" % devpi_index) - self.info("Using DEVPI_INDEX from environment: %s" % devpi_index) + url = URL(index_url) + if not url.is_valid_http_url(): + if not current.root_url.is_valid_http_url(): + if not URL(devpi_index).is_valid_http_url(): + self.fatal( + "No server set and neither the --index URL (%s) " + "nor the DEVPI_INDEX from environment (%s) is a " + "full valid URL." % (index_url, devpi_index)) + url = URL(devpi_index) + self.info("Using DEVPI_INDEX from environment: %s" % devpi_index) + elif current.index is None or '/' in index_url: + url = current.root_url + else: + url = current.index_url + url = url.joinpath(index_url) + else: + url = index_url if url is not None: - current = current.switch_to_local(self, URL(url).url, None) + current.persist_index = False + current.configure_fromurl(self, URL(url).url) + # cache in case get_current was called directly + self.__dict__['current'] = current return current + @cached_property + def current(self): + return self.get_current() + def get_existing_file(self, arg): p = py.path.local(arg, expanduser=True) if not p.exists(): @@ -1151,7 +1186,7 @@ help="Install from the given requirements file.") parser.add_argument("pkgspecs", metavar="pkg", type=str, action="store", default=None, nargs="*", - help="uri or package file for installation from current index. """) + help="uri or package file for installation from current index. ") @subcommand("devpi.refresh") @@ -1165,7 +1200,7 @@ help="index to refresh (defaults to current index)") parser.add_argument( "pkgnames", metavar="pkg", type=str, action="store", nargs="+", - help="package name to refresh.""") + help="package name to refresh.") def verify_reply_version(hub, reply): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/devpi/upload.py new/devpi-client-6.0.4/devpi/upload.py --- old/devpi-client-6.0.3/devpi/upload.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/devpi/upload.py 2023-04-13 07:14:01.000000000 +0200 @@ -139,7 +139,7 @@ dic = meta.copy() pypi_action = action dic[":action"] = pypi_action - dic["protocol_version"] = "1", + dic["protocol_version"] = "1" headers = {} auth = hub.current.get_auth() if auth: @@ -405,10 +405,12 @@ def setup_build(self, default_formats=None): deprecated_formats = [] + sdist = self.args.sdist + wheel = self.args.wheel formats = self.args.formats if formats is None: formats = default_formats - if formats: + if formats and not sdist and not wheel: sdist = None wheel = None for format in formats.split(","): @@ -447,9 +449,6 @@ "The --formats option is deprecated, " "you can remove it to get the default sdist and wheel " "releases you get with your currently specified formats.") - else: - sdist = self.args.sdist - wheel = self.args.wheel cmds = [] if sdist is not None or wheel is not None: @@ -559,6 +558,7 @@ setup_cfg = path.join("setup.cfg") if setup_cfg.exists(): cfg = iniconfig.IniConfig(setup_cfg) - hub.line("detected devpi:upload section in %s" % setup_cfg, bold=True) - return cfg.sections.get("devpi:upload", {}) + if 'devpi:upload' in cfg.sections: + hub.line("detected devpi:upload section in %s" % setup_cfg, bold=True) + return cfg.sections["devpi:upload"] return {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/devpi/use.py new/devpi-client-6.0.4/devpi/use.py --- old/devpi-client-6.0.3/devpi/use.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/devpi/use.py 2023-04-13 07:14:01.000000000 +0200 @@ -19,31 +19,36 @@ devpi_data_keys = ["features"] -def currentproperty(name): - def propget(self): - return self._currentdict.get(name, None) +class baseproperty(object): + def __init__(self, name): + self.name = name - def propset(self, val): - self._currentdict[name] = val + def __get__(self, inst, owner=None): + if inst is None: + return self + return getattr(inst, self.dict_name).get(self.name) - return property(propget, propset) + def __set__(self, inst, value): + getattr(inst, self.dict_name)[self.name] = value -def authproperty(name): - def propget(self): - return self._authdict.get(name, None) +class authproperty(baseproperty): + dict_name = '_authdict' - def propset(self, val): - self._authdict[name] = val - return property(propget, propset) +class currentproperty(baseproperty): + dict_name = '_currentdict' + + +class indexproperty(baseproperty): + dict_name = '_currentdict' class Current(object): - index = currentproperty("index") - simpleindex = currentproperty("simpleindex") - pypisubmit = currentproperty("pypisubmit") - login = currentproperty("login") + index = indexproperty("index") + simpleindex = indexproperty("simpleindex") + pypisubmit = indexproperty("pypisubmit") + login = indexproperty("login") username = currentproperty("username") venvdir = currentproperty("venvdir") _auth = authproperty("auth") @@ -381,19 +386,29 @@ indexname=indexname).addpath(name, asdir=1) +def _load_json(path, dest): + if path is None: + return + if not path.check(): + return + raw = path.read().strip() + if not raw: + return + data = json.loads(raw) + if not isinstance(data, dict): + return + dest.update(data) + + class PersistentCurrent(Current): + persist_index = True + def __init__(self, auth_path, current_path): Current.__init__(self) self.auth_path = auth_path self.current_path = current_path - if self.auth_path.check(): - auth_data = self.auth_path.read().strip() - if auth_data: - self._authdict.update(json.loads(auth_data)) - if self.current_path and self.current_path.check(): - current_data = self.current_path.read().strip() - if current_data: - self._currentdict.update(json.loads(current_data)) + _load_json(self.auth_path, self._authdict) + _load_json(self.current_path, self._currentdict) def exists(self): return self.current_path and self.current_path.check() @@ -416,9 +431,16 @@ def reconfigure(self, data, force_write=False): Current.reconfigure(self, data) self._persist(self._authdict, self.auth_path, force_write=force_write) + currentdict = {} + _load_json(self.current_path, currentdict) + for key, value in self._currentdict.items(): + prop = getattr(self.__class__, key) + if isinstance(prop, indexproperty) and not self.persist_index: + continue + currentdict[key] = value # we make sure to remove legacy auth data - self._currentdict.pop("auth", None) - self._persist(self._currentdict, self.current_path, force_write=force_write) + currentdict.pop("auth", None) + self._persist(currentdict, self.current_path, force_write=force_write) def out_index_list(hub, data): @@ -445,7 +467,7 @@ hub, current.index, hub.local_current_path) # now store existing data in new location current.reconfigure({}, force_write=True) - current = hub.current + current = hub.get_current(args.url) if args.delete: if not hub.current.exists(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/devpi_client.egg-info/PKG-INFO new/devpi-client-6.0.4/devpi_client.egg-info/PKG-INFO --- old/devpi-client-6.0.3/devpi_client.egg-info/PKG-INFO 2023-02-20 10:11:16.000000000 +0100 +++ new/devpi-client-6.0.4/devpi_client.egg-info/PKG-INFO 2023-04-13 07:14:10.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: devpi-client -Version: 6.0.3 +Version: 6.0.4 Summary: devpi upload/install/... workflow commands for Python developers Home-page: https://devpi.net Maintainer: Florian Schulze @@ -61,6 +61,21 @@ .. towncrier release notes start +6.0.4 (2023-04-13) +================== + +Bug Fixes +--------- + +- Fix precedence of URL from command line over DEVPI_INDEX environment variable for ``devpi use``. + +- Fix relative DEVPI_INDEX environment variable with user and index causing an invalid URL in some cases. + +- Fix persistence of username when DEVPI_INDEX environment variable is used with ``devpi login``. + +- Fix precedence of ``--sdist`` and ``--wheel`` over ``formats`` setting from setup.cfg ``[devpi:upload]`` section. + + 6.0.3 (2023-02-20) ================== @@ -166,12 +181,3 @@ - When there is no json error message only the HTML error code and reason is printed now, to get the full HTML output use the ``--debug`` flag. - -5.2.3 (2021-11-15) -================== - -Bug Fixes ---------- - -- Bump upper version limit on pluggy to <2.0. - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/pyproject.toml new/devpi-client-6.0.4/pyproject.toml --- old/devpi-client-6.0.3/pyproject.toml 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/pyproject.toml 2023-04-13 07:14:01.000000000 +0200 @@ -3,7 +3,6 @@ filename = "CHANGELOG" directory = "news/" title_format = "{version} ({project_date})" -template = "news/_template.rst" [[tool.towncrier.type]] directory = "removal" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/setup.py new/devpi-client-6.0.4/setup.py --- old/devpi-client-6.0.3/setup.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/setup.py 2023-04-13 07:14:01.000000000 +0200 @@ -44,7 +44,7 @@ description="devpi upload/install/... workflow commands for Python " "developers", long_description="\n\n".join([README, CHANGELOG]), - version='6.0.3', + version='6.0.4', packages=['devpi'], install_requires=install_requires, extras_require=extras_require, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/conftest.py new/devpi-client-6.0.4/testing/conftest.py --- old/devpi-client-6.0.3/testing/conftest.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/conftest.py 2023-04-13 07:14:01.000000000 +0200 @@ -742,12 +742,10 @@ @pytest.fixture -def mock_http_api(monkeypatch): +def mock_http_api(monkeypatch, reqmock): # noqa """ mock out all Hub.http_api calls and return an object offering 'set' and 'add' to fake replies. """ from devpi import main - from requests.sessions import Session - monkeypatch.setattr(Session, "request", None) class MockHTTPAPI: def __init__(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/functional.py new/devpi-client-6.0.4/testing/functional.py --- old/devpi-client-6.0.3/testing/functional.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/functional.py 2023-04-13 07:14:01.000000000 +0200 @@ -13,6 +13,12 @@ def __init__(self, d): self.__dict__ = d + def __repr__(self): + cls = self.__class__ + name = "%s.%s" % (cls.__module__, cls.__name__) + return "<%s %r>" % ( + name, self.__dict__.get('stagename', 'uninitialized')) + class MappMixin: _usercount = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/reqmock.py new/devpi-client-6.0.4/testing/reqmock.py --- old/devpi-client-6.0.3/testing/reqmock.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/reqmock.py 2023-04-13 07:14:01.000000000 +0200 @@ -55,7 +55,7 @@ if fnmatch.fnmatch(request.url, name): break else: - raise Exception("not mocked call to %s" % url) + raise Exception("not mocked call to %s" % url) # noqa: TRY002 response.add_request(request) r = HTTPAdapter().build_response(request, response) return r diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/simpypi.py new/devpi-client-6.0.4/testing/simpypi.py --- old/devpi-client-6.0.3/testing/simpypi.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/simpypi.py 2023-04-13 07:14:01.000000000 +0200 @@ -116,6 +116,9 @@ self.simpleurl = "%s/simple" % self.baseurl self.projects = {} self.files = {} + self.clear() + + def clear(self): self.clear_log() self.clear_requests() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/test_login.py new/devpi-client-6.0.4/testing/test_login.py --- old/devpi-client-6.0.3/testing/test_login.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/test_login.py 2023-04-13 07:14:01.000000000 +0200 @@ -87,3 +87,45 @@ out = hub._out.getvalue() assert "logged in 'user'" in out assert "credentials valid for 10.00 hours" in out + + +@pytest.mark.skipif("config.option.fast") +def test_login_with_index_environment(capfd, devpi, devpi_username, monkeypatch, tmpdir, url_of_liveserver): + # remove persisted client for fresh start + clientdir = tmpdir.join("client") + clientdir.remove() + monkeypatch.setenv("DEVPI_INDEX", url_of_liveserver.url) + devpi("use") + (out, err) = capfd.readouterr() + assert "using server: %s/ (not logged in)" % url_of_liveserver.url in out + devpi("login", devpi_username, "--password", "123") + (out, err) = capfd.readouterr() + assert 'credentials valid for' in out + devpi("use") + (out, err) = capfd.readouterr() + msg = "using server: %s/ (logged in as %s)" % ( + url_of_liveserver.url, + devpi_username) + assert msg in out + + +@pytest.mark.skipif("config.option.fast") +def test_login_with_relative_index_environment(capfd, devpi, devpi_username, monkeypatch, tmpdir, url_of_liveserver): + # remove persisted client for fresh start + clientdir = tmpdir.join("client") + clientdir.remove() + devpi_index = "%s/dev" % devpi_username + monkeypatch.setenv("DEVPI_INDEX", devpi_index) + devpi("use", url_of_liveserver.url) + (out, err) = capfd.readouterr() + assert "using server: %s/ (not logged in)" % url_of_liveserver.url in out + devpi("login", devpi_username, "--password", "123") + (out, err) = capfd.readouterr() + assert 'credentials valid for' in out + devpi("use") + (out, err) = capfd.readouterr() + url = url_of_liveserver.joinpath(devpi_index).url + msg = "current devpi index: %s (logged in as %s)" % ( + url, + devpi_username) + assert msg in out diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/test_main.py new/devpi-client-6.0.4/testing/test_main.py --- old/devpi-client-6.0.3/testing/test_main.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/test_main.py 2023-04-13 07:14:01.000000000 +0200 @@ -84,7 +84,7 @@ try: import importlib.metadata as importlib_metadata except ImportError: - import importlib_metadata as importlib_metadata + import importlib_metadata ver = devpi.__version__ assert importlib_metadata.version("devpi-client") == ver @@ -112,3 +112,88 @@ names = [x.strip().split()[0] for x in lines] assert 'devpi-client' in names assert 'devpi-server' in names + + +@pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) +def test_index_option_with_environment_relative_root_current( + capfd, cmd_devpi, devpi_index, initproj, + mock_http_api, monkeypatch, reqmock): + mock_http_api.set( + "http://devpi/+api", 200, result=dict( + login="http://devpi/+login", + authstatus=["noauth", "", []])) + cmd_devpi("use", "http://devpi") + monkeypatch.setenv("DEVPI_INDEX", "foo/bar") + initproj("hello1.1") + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + (out, err) = capfd.readouterr() + reqmock.mockresponse("http://devpi/user/dev/", 200) + cmd_devpi("upload", "--no-isolation", "--index", devpi_index) + (out, err) = capfd.readouterr() + assert "DEVPI_INDEX" not in out + assert "foo/bar" not in out + assert "file_upload of hello1.1-0.1.tar.gz to http://devpi/user/dev/" in out.splitlines() + + +@pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) +def test_index_option_with_environment_relative_user_current( + capfd, cmd_devpi, devpi_index, initproj, + mock_http_api, monkeypatch, reqmock): + mock_http_api.set( + "http://devpi/user/+api", 200, result=dict( + login="http://devpi/+login", + authstatus=["noauth", "", []])) + cmd_devpi("use", "http://devpi/user") + monkeypatch.setenv("DEVPI_INDEX", "foo/bar") + initproj("hello1.1") + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + (out, err) = capfd.readouterr() + reqmock.mockresponse("http://devpi/user/dev/", 200) + cmd_devpi("upload", "--no-isolation", "--index", devpi_index) + (out, err) = capfd.readouterr() + assert "DEVPI_INDEX" not in out + assert "foo/bar" not in out + assert "file_upload of hello1.1-0.1.tar.gz to http://devpi/user/dev/" in out.splitlines() + + +@pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) +def test_index_option_with_environment_relative( + capfd, cmd_devpi, devpi_index, initproj, + mock_http_api, monkeypatch, reqmock): + mock_http_api.set( + "http://devpi/user/foo/+api", 200, result=dict( + pypisubmit="http://devpi/user/foo/", + simpleindex="http://devpi/user/foo/+simple/", + index="http://devpi/user/foo", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://devpi/user/foo?no_projects=", 200, result=dict()) + cmd_devpi("use", "http://devpi/user/foo") + monkeypatch.setenv("DEVPI_INDEX", "foo/bar") + initproj("hello1.1") + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + (out, err) = capfd.readouterr() + reqmock.mockresponse("http://devpi/user/dev/", 200) + cmd_devpi("upload", "--no-isolation", "--index", devpi_index) + (out, err) = capfd.readouterr() + assert "DEVPI_INDEX" not in out + assert "foo/bar" not in out + assert "file_upload of hello1.1-0.1.tar.gz to http://devpi/user/dev/" in out.splitlines() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/test_push.py new/devpi-client-6.0.4/testing/test_push.py --- old/devpi-client-6.0.3/testing/test_push.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/test_push.py 2023-04-13 07:14:01.000000000 +0200 @@ -90,6 +90,31 @@ assert len(mock_http_api.called) == 1 +def test_push_devpi_index_option_with_environment(loghub, monkeypatch, mock_http_api): + loghub.args.target = "user/name" + loghub.args.index = "src/dev" + monkeypatch.setenv("DEVPI_INDEX", "http://devpi/user/dev") + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + mock_http_api.set( + "http://devpi/src/dev/+api", 200, result=dict( + pypisubmit="http://devpi/src/dev/", + simpleindex="http://devpi/src/dev/+simple/", + index="http://devpi/src/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + pusher = parse_target(loghub, loghub.args) + mock_http_api.set("http://devpi/src/dev", 200, result={}) + pusher.execute(loghub, "pytest", "2.3.5") + dict(name="pytest", version="2.3.5", targetindex="user/name") + assert len(mock_http_api.called) == 3 + + @pytest.mark.parametrize("spec", ("pkg==1.0", "pkg-1.0")) def test_main_push_pypi(capsys, monkeypatch, tmpdir, spec): from devpi.push import main @@ -244,7 +269,7 @@ msgs.append(msg) hub = MockHub() hub.derive_token = Hub.derive_token.__get__(hub) - hub.derive_token("%s-foo" % prefix, None) == "%s-foo" % prefix + assert hub.derive_token("%s-foo" % prefix, None) == "%s-foo" % prefix (msg,) = msgs assert "can not parse it" in msg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/test_upload.py new/devpi-client-6.0.4/testing/test_upload.py --- old/devpi-client-6.0.3/testing/test_upload.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/test_upload.py 2023-04-13 07:14:01.000000000 +0200 @@ -96,7 +96,7 @@ runproc("git commit -m message") return repo - def test_vcs_export(self, uploadhub, repo, setupdir, tmpdir, monkeypatch): + def test_vcs_export(self, uploadhub, repo, setupdir, tmpdir): checkout = Checkout(uploadhub, uploadhub.args, setupdir) assert checkout.rootpath == repo newrepo = tmpdir.mkdir("newrepo") @@ -145,8 +145,7 @@ exported = checkout.export(tmpdir) assert exported.rootpath == checkout.setupdir - def test_vcs_export_verify_setup(self, uploadhub, setupdir, - tmpdir, monkeypatch): + def test_vcs_export_verify_setup(self, uploadhub, setupdir, tmpdir): subdir = setupdir.mkdir("subdir") subdir.ensure("setup.py") checkout = Checkout(uploadhub, uploadhub.args, subdir) @@ -154,7 +153,7 @@ exported = checkout.export(wc) assert not exported.rootpath.join("setup.py").check() - def test_export_attributes(self, uploadhub, setupdir, tmpdir, monkeypatch): + def test_export_attributes(self, uploadhub, setupdir, tmpdir): checkout = Checkout(uploadhub, uploadhub.args, setupdir) setupdir.join("setup.py").write(dedent(""" from setuptools import setup @@ -167,7 +166,7 @@ assert name == "xyz" assert version == "1.2.3" - def test_setup_build_docs(self, uploadhub, setupdir, tmpdir, monkeypatch): + def test_setup_build_docs(self, uploadhub, setupdir, tmpdir): checkout = Checkout(uploadhub, uploadhub.args, setupdir) setupdir.join("setup.py").write(dedent(""" from setuptools import setup @@ -229,7 +228,7 @@ @pytest.mark.skipif("config.option.fast") -def test_post_includes_auth_info(initproj, monkeypatch, uploadhub): +def test_post_includes_auth_info(initproj, uploadhub): class Session: posts = [] @@ -241,7 +240,9 @@ class args: dryrun = None - formats = "sdist,bdist_wheel" + sdist = False + wheel = False + formats = None index = None no_isolation = True novcs = None @@ -277,9 +278,63 @@ assert "X-Devpi-Auth" in upload2[1]["headers"] +@pytest.mark.skipif("sys.version_info < (3,)") +@pytest.mark.skipif("config.option.fast") +def test_post_data(initproj, monkeypatch, reqmock, uploadhub): + import email + + class args: + dryrun = None + sdist = False + wheel = False + formats = None + index = None + no_isolation = True + novcs = None + only_latest = None + onlydocs = None + path = None + python = None + setupdironly = None + verbose = 0 + withdocs = None + + class Response: + status_code = 200 + + sent = [] + + def send(req, **kw): + sent.append((req, kw)) + return Response() + + initproj("pkg-1.0") + tmpdir = py.path.local() + uploadhub.cwd = tmpdir + uploadhub.current.reconfigure(dict( + index="http://devpi/foo/bar", + login="http://devpi/+login", + pypisubmit="http://devpi/foo/bar")) + monkeypatch.setattr(uploadhub.http, "send", send) + main(uploadhub, args) + # convert POST data to Message + msg = email.message_from_bytes( + b"MIME-Version: 1.0\nContent-Type: %s\n\n%s" % ( + sent[0][0].headers['Content-Type'].encode('ascii'), + sent[0][0].body)) + # get the data + data = { + x.get_param("name", header="Content-Disposition"): x.get_payload() + for x in msg.get_payload()} + assert data[":action"] == "file_upload" + assert data["name"] == "pkg" + assert data["protocol_version"] == "1" + assert data["version"] == "1.0" + + @pytest.mark.skipif("sys.version_info < (3, 7)") @pytest.mark.skipif("config.option.fast") -def test_post_derived_devpi_token(initproj, monkeypatch, uploadhub): +def test_post_derived_devpi_token(initproj, uploadhub): from base64 import b64decode import pypitoken @@ -587,6 +642,27 @@ out = out_devpi("upload", "--no-isolation", "--index", "%s/dev" % user, "--dry-run") out.stdout.fnmatch_lines_random("skipped: file_upload*to*/%s/dev*" % user) + @pytest.mark.parametrize("other_index", ["root/pypi", "/"]) + def test_index_option_with_environment_relative( + self, devpi, initproj, monkeypatch, out_devpi, + other_index, projname_version): + initproj(projname_version.rsplit("-", 1), {"doc": { + "conf.py": "#nothing", + "contents.rst": "", + "index.html": "<html/>"}}) + assert py.path.local("setup.py").check() + # remember username + out = out_devpi("use") + user = re.search(r'\(logged in as (.+?)\)', out.stdout.str()).group(1) + + # go to other index + out = out_devpi("use", other_index) + + monkeypatch.setenv("DEVPI_INDEX", "user/dev") + # --index option + out = out_devpi("upload", "--no-isolation", "--index", "%s/dev" % user, "--dry-run") + out.stdout.fnmatch_lines_random("skipped: file_upload*to*/%s/dev*" % user) + def test_logout(self, capfd, devpi, out_devpi, projname_version): # logoff then upload out = out_devpi("logoff") @@ -601,7 +677,7 @@ assert isinstance(res.sysex, SystemExit) assert res.sysex.args == (1,) - def test_fromdir(self, initproj, devpi, out_devpi, runproc, monkeypatch): + def test_fromdir(self, initproj, devpi, out_devpi, runproc): initproj("hello-1.1", {"doc": { "conf.py": "", "index.html": "<html/>"}}) @@ -653,6 +729,36 @@ assert links["releasefile"] == "%s.zip" % name_version_str assert links["doczip"] == "%s.doc.zip" % name_version_str + def test_cli_sdist_precedence(self, initproj, devpi, out_devpi): + initproj("pkg-1.0") + tmpdir = py.path.local() + tmpdir.join("setup.cfg").write(dedent(""" + [devpi:upload] + formats=bdist_wheel,sdist.zip""")) + hub = devpi("upload", "--sdist", "--no-isolation") + url = hub.current.get_index_url().url + 'pkg/1.0/' + out = out_devpi("getjson", url) + data = json.loads(out.stdout.str()) + vv = ViewLinkStore(url, data["result"]) + assert len(vv.get_links()) == 1 + assert vv.get_links()[0].basename in ('pkg-1.0.tar.gz', 'pkg-1.0.zip') + + def test_cli_wheel_precedence(self, initproj, devpi, out_devpi): + initproj("pkg-1.0") + tmpdir = py.path.local() + tmpdir.join("setup.cfg").write(dedent(""" + [devpi:upload] + formats=bdist_wheel,sdist.zip""")) + hub = devpi("upload", "--wheel", "--no-isolation") + url = hub.current.get_index_url().url + 'pkg/1.0/' + out = out_devpi("getjson", url) + data = json.loads(out.stdout.str()) + vv = ViewLinkStore(url, data["result"]) + assert len(vv.get_links()) == 1 + assert vv.get_links()[0].basename in ( + 'pkg-1.0-py2-none-any.whl', + 'pkg-1.0-py3-none-any.whl') + def test_getpkginfo(datadir): info = get_pkginfo(datadir.join("dddttt-0.1.dev45-py27-none-any.whl")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/testing/test_use.py new/devpi-client-6.0.4/testing/test_use.py --- old/devpi-client-6.0.3/testing/test_use.py 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/testing/test_use.py 2023-04-13 07:14:01.000000000 +0200 @@ -802,6 +802,70 @@ assert "login: http://devpi/+login" in out @pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) + def test_environment_relative_with_current( + self, capfd, cmd_devpi, devpi_index, mock_http_api, monkeypatch): + mock_http_api.set( + "http://world/user/dev/+api", 200, result=dict( + pypisubmit="http://world/user/dev/", + simpleindex="http://world/user/dev/+simple/", + index="http://world/user/dev", + login="http://world/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://world/user/dev?no_projects=", 200, result=dict()) + cmd_devpi("use", "http://world/user/dev") + (out, err) = capfd.readouterr() + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "index: http://world/user/dev" in out + assert "simpleindex: http://world/user/dev/+simple/" in out + assert "pypisubmit: http://world/user/dev/" in out + assert "login: http://world/+login" in out + monkeypatch.setenv("DEVPI_INDEX", devpi_index) + mock_http_api.set( + "http://world/user/dev/+api", 200, result=dict( + pypisubmit="http://world/user/dev/", + simpleindex="http://world/user/dev/+simple/", + index="http://world/user/dev", + login="http://world/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://world/user/dev?no_projects=", 200, result=dict()) + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "Using DEVPI_INDEX from environment: %s\n" % devpi_index in out + assert "index: http://world/user/dev" in out + assert "simpleindex: http://world/user/dev/+simple/" in out + assert "pypisubmit: http://world/user/dev/" in out + assert "login: http://world/+login" in out + + def test_environment_with_root_current(self, capfd, cmd_devpi, mock_http_api, monkeypatch): + mock_http_api.set( + "http://world/+api", 200, result=dict( + login="http://world/+login", + authstatus=["noauth", "", []])) + cmd_devpi("use", "http://world/") + (out, err) = capfd.readouterr() + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "using server: http://world/ (not logged in)" in out + assert "no current index" in out + monkeypatch.setenv("DEVPI_INDEX", "http://devpi/user/dev") + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://devpi/user/dev?no_projects=", 200, result=dict()) + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "Using DEVPI_INDEX from environment: http://devpi/user/dev\n" in out + assert "index: http://devpi/user/dev" in out + assert "simpleindex: http://devpi/user/dev/+simple/" in out + assert "pypisubmit: http://devpi/user/dev/" in out + assert "login: http://devpi/+login" in out + + @pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) def test_environment_relative_with_root_current( self, capfd, cmd_devpi, devpi_index, mock_http_api, monkeypatch): mock_http_api.set( @@ -831,6 +895,81 @@ assert "pypisubmit: http://world/user/dev/" in out assert "login: http://world/+login" in out + def test_environment_with_url_from_commandline( + self, capfd, cmd_devpi, mock_http_api, monkeypatch): + monkeypatch.setenv("DEVPI_INDEX", "http://devpi/user/dev") + mock_http_api.set( + "http://world/user/foo/+api", 200, result=dict( + pypisubmit="http://world/user/foo/", + simpleindex="http://world/user/foo/+simple/", + index="http://world/user/foo", + login="http://world/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://world/user/foo?no_projects=", 200, result=dict()) + cmd_devpi("use", "http://world/user/foo") + (out, err) = capfd.readouterr() + assert "Using index URL from command line" in out + mock_http_api.set( + "http://devpi/user/dev/+api", 200, result=dict( + pypisubmit="http://devpi/user/dev/", + simpleindex="http://devpi/user/dev/+simple/", + index="http://devpi/user/dev", + login="http://devpi/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://devpi/user/dev?no_projects=", 200, result=dict()) + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "Using DEVPI_INDEX from environment: http://devpi/user/dev\n" in out + assert "index: http://devpi/user/dev" in out + assert "simpleindex: http://devpi/user/dev/+simple/" in out + assert "pypisubmit: http://devpi/user/dev/" in out + assert "login: http://devpi/+login" in out + cmd_devpi("use", "http://world/user/foo", "--urls") + (out, err) = capfd.readouterr() + assert "Using index URL from command line" in out + assert "index: http://world/user/foo" in out + assert "simpleindex: http://world/user/foo/+simple/" in out + assert "pypisubmit: http://world/user/foo/" in out + assert "login: http://world/+login" in out + + @pytest.mark.parametrize("devpi_index", ["user/dev", "/user/dev"]) + def test_environment_relative_with_url_from_commandline( + self, capfd, cmd_devpi, devpi_index, mock_http_api, monkeypatch): + monkeypatch.setenv("DEVPI_INDEX", devpi_index) + mock_http_api.set( + "http://world/user/foo/+api", 200, result=dict( + pypisubmit="http://world/user/foo/", + simpleindex="http://world/user/foo/+simple/", + index="http://world/user/foo", + login="http://world/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://world/user/foo?no_projects=", 200, result=dict()) + cmd_devpi("use", "http://world/user/foo") + (out, err) = capfd.readouterr() + assert "Using index URL from command line" in out + mock_http_api.set( + "http://world/user/dev/+api", 200, result=dict( + pypisubmit="http://world/user/dev/", + simpleindex="http://world/user/dev/+simple/", + index="http://world/user/dev", + login="http://world/+login", + authstatus=["noauth", "", []])) + mock_http_api.set("http://world/user/dev?no_projects=", 200, result=dict()) + cmd_devpi("use", "--urls") + (out, err) = capfd.readouterr() + assert "Using DEVPI_INDEX from environment: %s\n" % devpi_index in out + assert "index: http://world/user/dev" in out + assert "simpleindex: http://world/user/dev/+simple/" in out + assert "pypisubmit: http://world/user/dev/" in out + assert "login: http://world/+login" in out + cmd_devpi("use", "http://world/user/foo", "--urls") + (out, err) = capfd.readouterr() + assert "Using index URL from command line" in out + assert "index: http://world/user/foo" in out + assert "simpleindex: http://world/user/foo/+simple/" in out + assert "pypisubmit: http://world/user/foo/" in out + assert "login: http://world/+login" in out + def test_getparse_keyvalues_invalid(): with pytest.raises(ValueError): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/devpi-client-6.0.3/tox.ini new/devpi-client-6.0.4/tox.ini --- old/devpi-client-6.0.3/tox.ini 2023-02-20 10:11:07.000000000 +0100 +++ new/devpi-client-6.0.4/tox.ini 2023-04-13 07:14:01.000000000 +0200 @@ -15,12 +15,13 @@ envlist = py27-server520,py27-version,py27,py35,py38,pypy,pypy3 [testenv] -passenv = LANG, PIP_INDEX_URL +passenv = GITHUB_ACTIONS, LANG, PIP_INDEX_URL deps = pytest pytest-flake8 < 1.1.0;python_version=="2.7" pytest-flake8;python_version!="2.7" flake8<5 + pytest-github-actions-annotate-failures pytest-instafail pytest-timeout devpi-server;python_version!="2.7"