Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pdm for openSUSE:Factory checked in at 2022-10-06 07:42:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pdm (Old) and /work/SRC/openSUSE:Factory/.python-pdm.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pdm" Thu Oct 6 07:42:17 2022 rev:5 rq:1008183 version:2.1.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pdm/python-pdm.changes 2022-08-20 20:28:32.997333843 +0200 +++ /work/SRC/openSUSE:Factory/.python-pdm.new.2275/python-pdm.changes 2022-10-06 07:42:28.824724214 +0200 @@ -1,0 +2,37 @@ +Wed Oct 5 09:47:36 UTC 2022 - Daniel Garcia <daniel.gar...@suse.com> + +- Update to v2.1.5 + * Ensure pypi.[ca,client]_cert[s] config items are passed to distribution + builder install steps to allow for custom PyPI index sources with self + signed certificates. #1396 + * Fix a crash issue when depending on editable packages with extras. #1401 + * Do not save the python path when using non-interactive mode in pdm init. + #1410 + * Restrict importlib-metadata (<5.0.0) for Python <3.8 #1411 + +------------------------------------------------------------------- +Tue Oct 4 22:54:50 UTC 2022 - Yogalakshmi Arunachalam <yarunacha...@suse.com> + +- Update to v2.1.4 (2022-09-17) + Bug Fixes + Fix a lock failure when depending on self with URL requirements. #1347 + Ensure list to concatenate args for composite scripts. #1359 + Fix an error in pdm lock --refresh if some packages has URLs. #1361 + Fix unnecessary package downloads and VCS clones for certain commands. #1370 + Fix a conversion error when converting a list of conditional dependencies from a Poetry format. #1383 + Documentation + Adds a section to the docs on how to correctly work with PDM and version control systems. #1364 + +- Update to v2.1.3 (2022-08-30) + Features & Improvements + When adding a package to (or removing from) a group, enhance the formatting of the group name in the printed message. #1329 + Bug Fixes + Fix a bug of missing hashes for packages with file:// links the first time they are added. #1325 + Ignore invalid values of data-requires-python when parsing package links. #1334 + Leave an incomplete project metadata if PDM fails to parse the project files, but emit a warning. #1337 + Fix the bug that editables package isn't installed for self package. #1344 + Fix a decoding error for non-ASCII characters in package description when publishing it. #1345 + Documentation + Clarify documentation explaining setup-script, run-setuptools, and is-purelib. #1327 + +------------------------------------------------------------------- Old: ---- pdm-2.1.2.tar.gz New: ---- pdm-2.1.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pdm.spec ++++++ --- /var/tmp/diff_new_pack.4GIbav/_old 2022-10-06 07:42:29.332725345 +0200 +++ /var/tmp/diff_new_pack.4GIbav/_new 2022-10-06 07:42:29.336725354 +0200 @@ -26,7 +26,7 @@ %bcond_with test %endif Name: python-pdm%{psuffix} -Version: 2.1.2 +Version: 2.1.5 Release: 0 Summary: Python Development Master License: MIT @@ -104,6 +104,7 @@ donttest="network" # mock testing finds the wrong python versions in our multiflavor setup donttest="$donttest or test_project_packages_path or test_conda_backend_create" +donttest="$donttest or test_init_non_interactive" %pytest -k "not ($donttest)" %endif ++++++ pdm-2.1.2.tar.gz -> pdm-2.1.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/CHANGELOG.md new/pdm-2.1.5/CHANGELOG.md --- old/pdm-2.1.2/CHANGELOG.md 2022-08-15 10:29:18.304720900 +0200 +++ new/pdm-2.1.5/CHANGELOG.md 2022-10-05 11:07:33.125920000 +0200 @@ -1,3 +1,50 @@ +Release v2.1.5 (2022-10-05) +--------------------------- + +### Bug Fixes + +- Ensure `pypi.[ca,client]_cert[s]` config items are passed to distribution builder install steps to allow for custom PyPI index sources with self signed certificates. [#1396](https://github.com/pdm-project/pdm/issues/1396) +- Fix a crash issue when depending on editable packages with extras. [#1401](https://github.com/pdm-project/pdm/issues/1401) +- Do not save the python path when using non-interactive mode in `pdm init`. [#1410](https://github.com/pdm-project/pdm/issues/1410) +- Restrict importlib-metadata (<5.0.0) for Python <3.8 [#1411](https://github.com/pdm-project/pdm/issues/1411) + + +Release v2.1.4 (2022-09-17) +--------------------------- + +### Bug Fixes + +- Fix a lock failure when depending on self with URL requirements. [#1347](https://github.com/pdm-project/pdm/issues/1347) +- Ensure list to concatenate args for composite scripts. [#1359](https://github.com/pdm-project/pdm/issues/1359) +- Fix an error in `pdm lock --refresh` if some packages has URLs. [#1361](https://github.com/pdm-project/pdm/issues/1361) +- Fix unnecessary package downloads and VCS clones for certain commands. [#1370](https://github.com/pdm-project/pdm/issues/1370) +- Fix a conversion error when converting a list of conditional dependencies from a Poetry format. [#1383](https://github.com/pdm-project/pdm/issues/1383) + +### Documentation + +- Adds a section to the docs on how to correctly work with PDM and version control systems. [#1364](https://github.com/pdm-project/pdm/issues/1364) + + +Release v2.1.3 (2022-08-30) +--------------------------- + +### Features & Improvements + +- When adding a package to (or removing from) a group, enhance the formatting of the group name in the printed message. [#1329](https://github.com/pdm-project/pdm/issues/1329) + +### Bug Fixes + +- Fix a bug of missing hashes for packages with `file://` links the first time they are added. [#1325](https://github.com/pdm-project/pdm/issues/1325) +- Ignore invalid values of `data-requires-python` when parsing package links. [#1334](https://github.com/pdm-project/pdm/issues/1334) +- Leave an incomplete project metadata if PDM fails to parse the project files, but emit a warning. [#1337](https://github.com/pdm-project/pdm/issues/1337) +- Fix the bug that `editables` package isn't installed for self package. [#1344](https://github.com/pdm-project/pdm/issues/1344) +- Fix a decoding error for non-ASCII characters in package description when publishing it. [#1345](https://github.com/pdm-project/pdm/issues/1345) + +### Documentation + +- Clarify documentation explaining `setup-script`, `run-setuptools`, and `is-purelib`. [#1327](https://github.com/pdm-project/pdm/issues/1327) + + Release v2.1.2 (2022-08-15) --------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/PKG-INFO new/pdm-2.1.5/PKG-INFO --- old/pdm-2.1.2/PKG-INFO 2022-08-15 10:29:30.096685600 +0200 +++ new/pdm-2.1.5/PKG-INFO 2022-10-05 11:07:43.114076600 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pdm -Version: 2.1.2 +Version: 2.1.5 Summary: A modern Python package and dependency manager supporting the latest PEP standards License: MIT Keywords: packaging,dependency,workflow diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/pyproject.toml new/pdm-2.1.5/pyproject.toml --- old/pdm-2.1.2/pyproject.toml 2022-08-15 10:29:18.308720800 +0200 +++ new/pdm-2.1.5/pyproject.toml 2022-10-05 11:07:33.129920000 +0200 @@ -23,7 +23,7 @@ "cachecontrol[filecache]>=0.12.11", "tomli>=1.1.0; python_version < \"3.11\"", "typing-extensions; python_version < \"3.8\"", - "importlib-metadata; python_version < \"3.8\"", + "importlib-metadata<5.0.0; python_version < \"3.8\"", "certifi>=2022.6.15", ] name = "pdm" @@ -42,7 +42,7 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ] -version = "2.1.2" +version = "2.1.5" [project.license] text = "MIT" @@ -96,6 +96,7 @@ "pytest-cov", "pytest-mock", "pytest-xdist>=1.31.0", + "pytest-rerunfailures>=10.2", ] tox = [ "tox", @@ -168,7 +169,7 @@ "path: Tests that compare with the system paths", "deprecated: Tests about deprecated features", ] -addopts = "-ra" +addopts = "-r aR" testpaths = [ "tests/", ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/builders/base.py new/pdm-2.1.5/src/pdm/builders/base.py --- old/pdm-2.1.2/src/pdm/builders/base.py 2022-08-15 10:29:18.308720800 +0200 +++ new/pdm-2.1.5/src/pdm/builders/base.py 2022-10-05 11:07:33.129920000 +0200 @@ -289,6 +289,12 @@ "--prefix", path, ] + ca_certs = self._env.project.config.get("pypi.ca_certs") + if ca_certs is not None: + cmd.extend(["--cert", ca_certs]) + client_cert = self._env.project.config.get("pypi.client_cert") + if client_cert is not None: + cmd.extend(["--client-cert", client_cert]) cmd.extend(prepare_pip_source_args(self._env.project.sources)) cmd.extend(["-r", req_file.name]) self.subprocess_runner(cmd, isolated=False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/actions.py new/pdm-2.1.5/src/pdm/cli/actions.py --- old/pdm-2.1.2/src/pdm/cli/actions.py 2022-08-15 10:29:18.308720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/actions.py 2022-10-05 11:07:33.129920000 +0200 @@ -75,9 +75,8 @@ reqs, python_requires, summary = locked_repo.candidate_info[key] candidate.summary = summary candidate.requires_python = python_requires - dep_key = cast("tuple[str, str | None]", key[:2]) - mapping[dep_key[0]] = candidate - dependencies[dep_key] = list(map(parse_requirement, reqs)) + mapping[candidate.identify()] = candidate + dependencies[candidate.dep_key] = list(map(parse_requirement, reqs)) fetch_hashes(repo, mapping) lockfile = format_lockfile(project, mapping, dependencies) project.write_lockfile(lockfile) @@ -265,7 +264,8 @@ tracked_names.add(key) requirements[key] = r project.core.ui.echo( - f"Adding packages to {group} {'dev-' if dev else ''}dependencies: " + f"Adding packages to [bold cyan]{group}[/] " + f"{'dev-' if dev else ''}dependencies: " + ", ".join(f"[bold green]{r.as_line()}[/]" for r in requirements.values()) ) all_dependencies = project.all_dependencies @@ -358,7 +358,7 @@ ) if not matched_name: raise ProjectError( - f"[bold green]{name}[/] does not exist in {group} " + f"[bold green]{name}[/] does not exist in [bold cyan]{group}[/] " f"{'dev-' if dev else ''}dependencies." ) dependencies[matched_name].prerelease = prerelease @@ -434,7 +434,8 @@ deps = project.get_pyproject_dependencies(group, dev) project.core.ui.echo( - f"Removing packages from {group} {'dev-' if dev else ''}dependencies: " + f"Removing packages from [bold cyan]{group}[/] " + f"{'dev-' if dev else ''}dependencies: " + ", ".join(f"[bold green]{name}[/]" for name in packages) ) for name in packages: @@ -444,7 +445,8 @@ ) if not matched_indexes: raise ProjectError( - f"[bold green]{name}[/] does not exist in {group} dependencies." + f"[bold green]{name}[/] does not exist in " + f"[bold cyan]{group}[/] dependencies." ) for i in matched_indexes: del deps[i] @@ -606,6 +608,7 @@ first: bool = False, ignore_remembered: bool = False, ignore_requires_python: bool = False, + save: bool = True, hooks: HookManager | None = None, ) -> PythonInfo: """Use the specified python version and save in project config. @@ -683,8 +686,13 @@ if not selected_python.valid: path = str(selected_python.executable) raise InvalidPyVersion(f"Invalid Python interpreter: {path}") - - old_python = project.python if "python.path" in project.config else None + if not save: + return selected_python + old_python = ( + PythonInfo.from_path(project.config["python.path"]) + if "python.path" in project.config + else None + ) project.core.ui.echo( "Using Python interpreter: " f"[green]{str(selected_python.executable)}[/] " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/commands/init.py new/pdm-2.1.5/src/pdm/cli/commands/init.py --- old/pdm-2.1.2/src/pdm/cli/commands/init.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/commands/init.py 2022-10-05 11:07:33.129920000 +0200 @@ -60,7 +60,9 @@ ): try: path = project._create_virtualenv() - project.python = PythonInfo.from_path(get_venv_python(path)) + python = project.python = PythonInfo.from_path( + get_venv_python(path) + ) except Exception as e: # pragma: no cover project.core.ui.echo( f"Error occurred when creating virtualenv: {e}\n" @@ -69,8 +71,10 @@ err=True, ) else: - actions.do_use(project, "3", True, ignore_requires_python=True, hooks=hooks) - if get_venv_like_prefix(project.python.executable) is None: + python = actions.do_use( + project, "3", True, ignore_requires_python=True, save=False, hooks=hooks + ) + if get_venv_like_prefix(python.executable) is None: project.core.ui.echo( "You are using the PEP 582 mode, no virtualenv is created.\n" "For more info, please visit https://peps.python.org/pep-0582/", @@ -92,7 +96,7 @@ git_user, git_email = get_user_email_from_git() author = self.ask("Author name", git_user) email = self.ask("Author email", git_email) - python_version = f"{project.python.major}.{project.python.minor}" + python_version = f"{python.major}.{python.minor}" python_requires = self.ask( "Python requires('*' to allow any)", f">={python_version}" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/commands/publish/package.py new/pdm-2.1.5/src/pdm/cli/commands/publish/package.py --- old/pdm-2.1.2/src/pdm/cli/commands/publish/package.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/commands/publish/package.py 2022-10-05 11:07:33.129920000 +0200 @@ -4,6 +4,7 @@ import email.message import email.policy import hashlib +import io import os import re import subprocess @@ -30,7 +31,12 @@ \.whl|\.dist-info)$""", re.VERBOSE, ) -message_policy = email.policy.default.clone(utf8=True) + + +def parse_metadata(fp: IO[bytes]) -> email.message.Message: + return email.message_from_file( + io.TextIOWrapper(fp, encoding="utf-8", errors="surrogateescape") + ) @dataclass @@ -105,9 +111,7 @@ for m in members: fn = split_leading_dir(m.name)[1] if has_leading else m.name if fn == "PKG-INFO": - return email.message_from_binary_file( - cast(IO[bytes], tar.extractfile(m)), policy=message_policy - ) + return parse_metadata(cast(IO[bytes], tar.extractfile(m))) raise ProjectError(f"No PKG-INFO found in {filename}") @staticmethod @@ -118,9 +122,7 @@ for name in filenames: fn = split_leading_dir(name)[1] if has_leading else name if fn == "PKG-INFO": - return email.message_from_binary_file( - zip.open(name), policy=message_policy - ) + return parse_metadata(zip.open(name)) raise ProjectError(f"No PKG-INFO found in {filename}") @staticmethod @@ -128,9 +130,7 @@ with zipfile.ZipFile(filename, allowZip64=True) as zip: for fn in zip.namelist(): if fn.replace("\\", "/").endswith(".dist-info/METADATA"): - return email.message_from_binary_file( - zip.open(fn), policy=message_policy - ) + return parse_metadata(zip.open(fn)) raise ProjectError(f"No egg-info is found in {filename}") def add_gpg_signature(self, filename: str, signature_name: str) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/commands/run.py new/pdm-2.1.5/src/pdm/cli/commands/run.py --- old/pdm-2.1.2/src/pdm/cli/commands/run.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/commands/run.py 2022-10-05 11:07:33.129920000 +0200 @@ -243,6 +243,7 @@ ] + list(args) elif kind == "composite": assert isinstance(value, list) + args = list(args) self.project.core.ui.echo( f"Running {task}: [green]{str(args)}[/]", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/hooks.py new/pdm-2.1.5/src/pdm/cli/hooks.py --- old/pdm-2.1.2/src/pdm/cli/hooks.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/hooks.py 2022-10-05 11:07:33.129920000 +0200 @@ -15,9 +15,6 @@ class HookManager: - projet: Project - skip: list[str] - def __init__(self, project: Project, skip: list[str] | None = None): self.project = project self.skip = skip or [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/cli/options.py new/pdm-2.1.5/src/pdm/cli/options.py --- old/pdm-2.1.2/src/pdm/cli/options.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/cli/options.py 2022-10-05 11:07:33.129920000 +0200 @@ -59,7 +59,7 @@ def __init__( self, - name: str = None, + name: str, is_mutually_exclusive: bool = False, required: bool = None, ) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/formats/poetry.py new/pdm-2.1.5/src/pdm/formats/poetry.py --- old/pdm-2.1.2/src/pdm/formats/poetry.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/formats/poetry.py 2022-10-05 11:07:33.133920200 +0200 @@ -6,7 +6,7 @@ import re from argparse import Namespace from pathlib import Path -from typing import TYPE_CHECKING, Any, Mapping, cast +from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, cast from pdm._types import RequirementDict, Source from pdm.compat import tomllib @@ -64,31 +64,36 @@ return functools.reduce(operator.or_, parts) -def _convert_req(name: str, req_dict: RequirementDict) -> str: - if not getattr(req_dict, "items", None): - assert isinstance(req_dict, str) - return Requirement.from_req_dict(name, _convert_specifier(req_dict)).as_line() - assert isinstance(req_dict, dict) - req_dict = dict(req_dict) - req_dict.pop("optional", None) # Ignore the 'optional' key - if "version" in req_dict: - req_dict["version"] = _convert_specifier(str(req_dict["version"])) - markers: list[Marker] = [] - if "markers" in req_dict: - markers.append(Marker(req_dict.pop("markers"))) # type: ignore - if "python" in req_dict: - markers.append( - Marker(_convert_python(str(req_dict.pop("python"))).as_marker_string()) - ) - if markers: - req_dict["marker"] = str(functools.reduce(operator.and_, markers)).replace( - '"', "'" - ) - if "rev" in req_dict or "branch" in req_dict or "tag" in req_dict: - req_dict["ref"] = req_dict.pop( - "rev", req_dict.pop("tag", req_dict.pop("branch", None)) # type: ignore - ) - return Requirement.from_req_dict(name, req_dict).as_line() +def _convert_req( + name: str, req_dict: RequirementDict | List[RequirementDict] +) -> Iterable[str]: + if isinstance(req_dict, list): + for req in req_dict: + yield from _convert_req(name, req) + elif isinstance(req_dict, str): + yield Requirement.from_req_dict(name, _convert_specifier(req_dict)).as_line() + else: + assert isinstance(req_dict, dict) + req_dict = dict(req_dict) + req_dict.pop("optional", None) # Ignore the 'optional' key + if "version" in req_dict: + req_dict["version"] = _convert_specifier(str(req_dict["version"])) + markers: list[Marker] = [] + if "markers" in req_dict: + markers.append(Marker(req_dict.pop("markers"))) # type: ignore + if "python" in req_dict: + markers.append( + Marker(_convert_python(str(req_dict.pop("python"))).as_marker_string()) + ) + if markers: + req_dict["marker"] = str(functools.reduce(operator.and_, markers)).replace( + '"', "'" + ) + if "rev" in req_dict or "branch" in req_dict or "tag" in req_dict: + req_dict["ref"] = req_dict.pop( + "rev", req_dict.pop("tag", req_dict.pop("branch", None)) # type: ignore + ) + yield Requirement.from_req_dict(name, req_dict).as_line() class PoetryMetaConverter(MetaConverter): @@ -134,15 +139,15 @@ optional = getattr(req_dict, "items", None) and req_dict.pop( "optional", False ) - req = _convert_req(key, req_dict) - if optional: - extra = next((k for k, v in extras.items() if key in v), None) - if extra: - self._data.setdefault("optional-dependencies", {}).setdefault( - extra, [] - ).append(req) - else: - rv.append(req) + for req in _convert_req(key, req_dict): + if optional: + extra = next((k for k, v in extras.items() if key in v), None) + if extra: + self._data.setdefault("optional-dependencies", {}).setdefault( + extra, [] + ).append(req) + else: + rv.append(req) del source["dependencies"] return make_array(rv, True) @@ -150,7 +155,7 @@ def dev_dependencies(self, value: dict) -> None: self.settings["dev-dependencies"] = { "dev": make_array( - [_convert_req(key, req) for key, req in value.items()], True + [r for key, req in value.items() for r in _convert_req(key, req)], True ), } raise Unset() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/installers/synchronizers.py new/pdm-2.1.5/src/pdm/installers/synchronizers.py --- old/pdm-2.1.2/src/pdm/installers/synchronizers.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/installers/synchronizers.py 2022-10-05 11:07:33.133920200 +0200 @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses import functools import multiprocessing import traceback @@ -11,7 +12,7 @@ from pdm import termui from pdm.exceptions import InstallationError from pdm.installers.manager import InstallManager -from pdm.models.candidates import Candidate +from pdm.models.candidates import Candidate, make_candidate from pdm.models.environment import Environment from pdm.models.requirements import parse_requirement, strip_extras from pdm.utils import is_editable @@ -129,7 +130,9 @@ keys = [] if ( self.install_self - and getattr(self.environment.project.meta, "editable_backend", "path") + and getattr( + self.environment.project.meta.config, "editable_backend", "path" + ) == "editables" and "editables" not in candidates ): @@ -138,8 +141,13 @@ if editables is not None: candidates["editables"] = editables for key in keys: - if key in candidates: - candidates[key].req.editable = False + if key in candidates and candidates[key].req.editable: + # We do not do in-place update, which will break the caches + candidate = candidates[key] + req = dataclasses.replace(candidate.req, editable=False) + candidates[key] = make_candidate( + req, candidate.name, candidate.version, candidate.link + ) self.candidates = candidates self._manager: InstallManager | None = None @@ -330,6 +338,8 @@ ) if lines: self.ui.echo("\n".join(lines)) + else: + self.ui.echo("All packages are synced to date, nothing to do.") def synchronize(self) -> None: """Synchronize the working set with pinned candidates.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/models/caches.py new/pdm-2.1.5/src/pdm/models/caches.py --- old/pdm-2.1.2/src/pdm/models/caches.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/models/caches.py 2022-10-05 11:07:33.133920200 +0200 @@ -86,7 +86,7 @@ # Name and version are set when dependencies are resolved, # so use them for cache key. Local directories won't be cached. if not obj.name or not obj.version: - raise KeyError + raise KeyError("The package is missing a name or version") extras = ( "[{}]".format(",".join(sorted(obj.req.extras))) if obj.req.extras else "" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/models/candidates.py new/pdm-2.1.5/src/pdm/models/candidates.py --- old/pdm-2.1.2/src/pdm/models/candidates.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/models/candidates.py 2022-10-05 11:07:33.133920200 +0200 @@ -2,6 +2,7 @@ import os import re +import warnings from functools import lru_cache from pathlib import Path from tempfile import TemporaryDirectory @@ -14,7 +15,7 @@ from pdm import termui from pdm.builders import EditableBuilder, WheelBuilder from pdm.compat import importlib_metadata as im -from pdm.exceptions import BuildError, CandidateNotFound +from pdm.exceptions import BuildError, CandidateNotFound, InvalidPyVersion from pdm.models.requirements import ( FileRequirement, Requirement, @@ -23,6 +24,7 @@ filter_requirements_with_extras, ) from pdm.models.setup import Setup +from pdm.models.specifiers import PySpecSet from pdm.project.metadata import MutableMetadata, SetupDistribution from pdm.utils import ( cached_property, @@ -198,9 +200,15 @@ return self._requires_python if self.link: requires_python = self.link.requires_python - if requires_python and requires_python.isdigit(): - requires_python = f">={requires_python},<{int(requires_python) + 1}" - self._requires_python = requires_python + if requires_python is not None: + if requires_python.isdigit(): + requires_python = f">={requires_python},<{int(requires_python) + 1}" + try: # ensure the specifier is valid + PySpecSet(requires_python) + except InvalidPyVersion: + pass + else: + self._requires_python = requires_python return self._requires_python or "" @requires_python.setter @@ -483,7 +491,15 @@ ).prepare_metadata(metadir_parent) except BuildError: termui.logger.warn("Failed to build package, try parsing project files.") - setup = Setup.from_directory(self._unpacked_dir) + try: + setup = Setup.from_directory(self._unpacked_dir) + except Exception: + message = ( + "Failed to parse the project files, dependencies may be missing" + ) + termui.logger.warn(message) + warnings.warn(message, RuntimeWarning) + setup = Setup() return SetupDistribution(setup) else: return im.PathDistribution(Path(self._metadata_dir)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/models/python_max_versions.json new/pdm-2.1.5/src/pdm/models/python_max_versions.json --- old/pdm-2.1.2/src/pdm/models/python_max_versions.json 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/models/python_max_versions.json 2022-10-05 11:07:33.133920200 +0200 @@ -11,13 +11,13 @@ "3": 10, "3.0": 1, "3.1": 5, - "3.10": 6, + "3.10": 7, "3.2": 6, "3.3": 7, "3.4": 10, "3.5": 10, "3.6": 15, - "3.7": 13, - "3.8": 13, - "3.9": 13 + "3.7": 14, + "3.8": 14, + "3.9": 14 } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/models/repositories.py new/pdm-2.1.5/src/pdm/models/repositories.py --- old/pdm-2.1.2/src/pdm/models/repositories.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/models/repositories.py 2022-10-05 11:07:33.133920200 +0200 @@ -75,7 +75,12 @@ else: if last_ext_info is not None: raise last_ext_info[1].with_traceback(last_ext_info[2]) # type: ignore - reqs = [parse_requirement(line) for line in requirements] + reqs: list[Requirement] = [] + for line in requirements: + if line.startswith("-e "): + reqs.append(parse_requirement(line[3:], True)) + else: + reqs.append(parse_requirement(line)) if candidate.req.extras: # XXX: If the requirement has extras, add the original candidate # (without extras) as its dependency. This ensures the same package with @@ -226,9 +231,7 @@ if ( candidate.req.is_vcs or candidate.req.is_file_or_url - and candidate.req.is_local # type: ignore - or candidate.link - and candidate.link.is_file + and candidate.req.is_local_dir # type: ignore ): return None if candidate.hashes: @@ -244,7 +247,12 @@ assert c.link is not None # Prepare the candidate to replace vars in the link URL prepared_link = c.prepare(self.environment).link - if not prepared_link or prepared_link.is_vcs: + if ( + not prepared_link + or prepared_link.is_vcs + or prepared_link.is_file + and prepared_link.file_path.is_dir() + ): continue result[c.link] = self._hash_cache.get_hash( prepared_link, finder.session diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/models/requirements.py new/pdm-2.1.5/src/pdm/models/requirements.py --- old/pdm-2.1.2/src/pdm/models/requirements.py 2022-08-15 10:29:18.312720800 +0200 +++ new/pdm-2.1.5/src/pdm/models/requirements.py 2022-10-05 11:07:33.133920200 +0200 @@ -60,7 +60,12 @@ return name, extras -@dataclasses.dataclass +@functools.lru_cache(maxsize=None) +def _get_random_key(req: Requirement) -> str: + return f":empty:{secrets.token_urlsafe(8)}" + + +@dataclasses.dataclass(eq=False) class Requirement: """Base class of a package requirement. A requirement is a (virtual) specification of a package which contains @@ -119,12 +124,11 @@ return hash(self._hash_key()) def __eq__(self, o: object) -> bool: - return isinstance(o, Requirement) and hash(self) == hash(o) + return isinstance(o, Requirement) and self._hash_key() == o._hash_key() - @functools.lru_cache(maxsize=None) def identify(self) -> str: if not self.key: - return f":empty:{secrets.token_urlsafe(8)}" + return _get_random_key(self) extras = "[{}]".format(",".join(sorted(self.extras))) if self.extras else "" return self.key + extras @@ -233,17 +237,14 @@ return "" -@dataclasses.dataclass +@dataclasses.dataclass(eq=False) class NamedRequirement(Requirement): - def __hash__(self) -> int: - return hash(self._hash_key()) - def as_line(self) -> str: extras = f"[{','.join(sorted(self.extras))}]" if self.extras else "" return f"{self.project_name}{extras}{self.specifier}{self._format_marker()}" -@dataclasses.dataclass +@dataclasses.dataclass(eq=False) class FileRequirement(Requirement): url: str = "" path: Path | None = None @@ -260,9 +261,6 @@ def _hash_key(self) -> tuple: return super()._hash_key() + (self.url, self.editable) - def __hash__(self) -> int: - return hash(self._hash_key()) - @classmethod def create(cls: type[T], **kwargs: Any) -> T: if kwargs.get("path"): @@ -329,6 +327,8 @@ else "" ) marker = self._format_marker() + if marker: + marker = f" {marker}" url = self.get_full_url() fragments = [] if self.subdirectory: @@ -349,7 +349,8 @@ egg_info = urlparse.unquote(fragments["egg"]) name, extras = strip_extras(egg_info) self.name = name - self.extras = extras + if not self.extras: + self.extras = extras if not self.name and not self.is_vcs: filename = os.path.basename( urlparse.unquote(url_without_fragments(self.url)) @@ -375,18 +376,16 @@ ): raise RequirementError(f"The local path '{self.path}' is not installable.") result = Setup.from_directory(self.path.absolute()) - self.name = result.name + if result.name: + self.name = result.name -@dataclasses.dataclass +@dataclasses.dataclass(eq=False) class VcsRequirement(FileRequirement): vcs: str = "" ref: str | None = None revision: str | None = None - def __hash__(self) -> int: - return hash(self._hash_key()) - def __post_init__(self) -> None: super().__post_init__() if not self.vcs: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/src/pdm/resolver/providers.py new/pdm-2.1.5/src/pdm/resolver/providers.py --- old/pdm-2.1.2/src/pdm/resolver/providers.py 2022-08-15 10:29:18.316720700 +0200 +++ new/pdm-2.1.5/src/pdm/resolver/providers.py 2022-10-05 11:07:33.133920200 +0200 @@ -6,6 +6,7 @@ from resolvelib import AbstractProvider from pdm.models.candidates import Candidate, make_candidate +from pdm.models.repositories import LockedRepository from pdm.models.requirements import parse_requirement, strip_extras from pdm.resolver.python import ( PythonCandidate, @@ -120,9 +121,12 @@ return self._find_candidates(parse_requirement(req)) def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: - if not requirement.is_named: + if not requirement.is_named and not isinstance( + self.repository, LockedRepository + ): can = make_candidate(requirement) - can.prepare(self.repository.environment).metadata + if not can.name: + can.prepare(self.repository.environment).metadata return [can] else: return self.repository.find_candidates( @@ -165,6 +169,9 @@ requirement.url # type: ignore ) version = candidate.version + if version is None: + # This should be a URL candidate, consider it to be matching + return True # Allow prereleases if: 1) it is not specified in the tool settings or # 2) the candidate doesn't come from PyPI index. allow_prereleases = ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/cli/test_add.py new/pdm-2.1.5/tests/cli/test_add.py --- old/pdm-2.1.2/tests/cli/test_add.py 2022-08-15 10:29:18.316720700 +0200 +++ new/pdm-2.1.5/tests/cli/test_add.py 2022-10-05 11:07:33.133920200 +0200 @@ -6,6 +6,7 @@ from pdm.cli import actions from pdm.exceptions import PdmUsageError from pdm.models.specifiers import PySpecSet +from pdm.utils import path_to_url from tests import FIXTURES @@ -294,3 +295,22 @@ actions.do_add(project, packages=["urllib3"], prerelease=True) assert working_set["urllib3"].version == "1.23b0" assert project.meta.dependencies[0] == "urllib3<2,>=1.23b0" + + +@pytest.mark.usefixtures("repository") +def test_add_editable_package_with_extras(project, working_set): + project.environment.python_requires = PySpecSet(">=3.6") + dep_path = FIXTURES.joinpath("projects/demo").as_posix() + actions.do_add( + project, + dev=True, + group="dev", + editables=[f"{dep_path}[security]"], + ) + assert ( + f"-e {path_to_url(dep_path)}#egg=demo[security]" + in project.get_pyproject_dependencies("dev", True) + ) + assert "demo" in working_set + assert "requests" in working_set + assert "urllib3" in working_set diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/cli/test_init.py new/pdm-2.1.5/tests/cli/test_init.py --- old/pdm-2.1.2/tests/cli/test_init.py 2022-08-15 10:29:18.316720700 +0200 +++ new/pdm-2.1.5/tests/cli/test_init.py 2022-10-05 11:07:33.133920200 +0200 @@ -1,8 +1,10 @@ +import sys from unittest.mock import ANY import pytest from pdm.cli import actions +from pdm.models.python import PythonInfo def test_init_validate_python_requires(project_no_init): @@ -63,7 +65,9 @@ return_value=("Testing", "m...@example.org"), ) do_init = mocker.patch.object(actions, "do_init") - do_use = mocker.patch.object(actions, "do_use") + do_use = mocker.patch.object( + actions, "do_use", return_value=PythonInfo.from_path(sys.executable) + ) result = invoke(["init", "-n"], obj=project_no_init) assert result.exit_code == 0 python_version = f"{project_no_init.python.major}.{project_no_init.python.minor}" @@ -72,6 +76,7 @@ ANY, True, ignore_requires_python=True, + save=False, hooks=ANY, ) do_init.assert_called_with( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/cli/test_publish.py new/pdm-2.1.5/tests/cli/test_publish.py --- old/pdm-2.1.2/tests/cli/test_publish.py 2022-08-15 10:29:18.316720700 +0200 +++ new/pdm-2.1.5/tests/cli/test_publish.py 2022-10-05 11:07:33.133920200 +0200 @@ -34,6 +34,15 @@ assert meta["filetype"] == "sdist" +def test_parse_metadata_with_non_ascii_chars(): + fullpath = FIXTURES / "artifacts" / "caj2pdf-restructured-0.1.0a6.tar.gz" + package = PackageFile.from_filename(str(fullpath), None) + meta = package.metadata_dict + assert meta["summary"] == "caj2pdf ????????????????????????????????????" + assert meta["author_email"] == "?????? <s...@zhang.me>" + assert meta["description"].strip() == "# caj2pdf\n\n??????????????????" + + def test_package_add_signature(tmp_path): package = PackageFile.from_filename( str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None Binary files old/pdm-2.1.2/tests/fixtures/artifacts/caj2pdf-restructured-0.1.0a6.tar.gz and new/pdm-2.1.5/tests/fixtures/artifacts/caj2pdf-restructured-0.1.0a6.tar.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/fixtures/index/wheel.html new/pdm-2.1.5/tests/fixtures/index/wheel.html --- old/pdm-2.1.2/tests/fixtures/index/wheel.html 1970-01-01 01:00:00.000000000 +0100 +++ new/pdm-2.1.5/tests/fixtures/index/wheel.html 2022-10-05 11:07:33.149920500 +0200 @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> + <body> + <h1>pep345-legacy</h1> + <a + href="http://fixtures.test/artifacts/wheel-0.37.1-py2.py3-none-any.whl" + data-requires-python=">=3.1.*'" + > + pep345_legacy-0.0.1-py2.py3-none-any.whl + </a> + </body> +</html> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/models/test_candidates.py new/pdm-2.1.5/tests/models/test_candidates.py --- old/pdm-2.1.2/tests/models/test_candidates.py 2022-08-15 10:29:18.336720700 +0200 +++ new/pdm-2.1.5/tests/models/test_candidates.py 2022-10-05 11:07:33.153920400 +0200 @@ -323,6 +323,14 @@ assert candidate.requires_python == ">=3,<4" +def test_ignore_invalid_py_version(project): + project.project_config["pypi.url"] = "https://my.pypi.org/simple" + req = parse_requirement("wheel") + repo = project.get_repository() + candidate = next(iter(repo.find_candidates(req))) + assert not candidate.requires_python + + def test_find_candidates_from_find_links(project): repo = project.get_repository() repo.sources = [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/models/test_requirements.py new/pdm-2.1.5/tests/models/test_requirements.py --- old/pdm-2.1.2/tests/models/test_requirements.py 2022-08-15 10:29:18.336720700 +0200 +++ new/pdm-2.1.5/tests/models/test_requirements.py 2022-10-05 11:07:33.153920400 +0200 @@ -21,7 +21,7 @@ ), ( 'pip @ https://github.com/pypa/pip/archive/1.3.1.zip ; python_version > "3.4"', - 'pip @ https://github.com/pypa/pip/archive/1.3.1.zip; python_version > "3.4"', + None, ), ( "git+http://git.example.com/MyProject.git@master#egg=MyProject", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pdm-2.1.2/tests/test_integration.py new/pdm-2.1.5/tests/test_integration.py --- old/pdm-2.1.2/tests/test_integration.py 2022-08-15 10:29:18.336720700 +0200 +++ new/pdm-2.1.5/tests/test_integration.py 2022-10-05 11:07:33.153920400 +0200 @@ -28,6 +28,7 @@ @pytest.mark.integration @pytest.mark.network +@pytest.mark.flaky(reruns=3) @pytest.mark.parametrize("python_version", get_python_versions()) def test_basic_integration(python_version, core, tmp_path, invoke): """An e2e test case to ensure PDM works on all supported Python versions"""