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"""

Reply via email to