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 2026-03-31 15:22:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pdm (Old)
 and      /work/SRC/openSUSE:Factory/.python-pdm.new.1999 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pdm"

Tue Mar 31 15:22:49 2026 rev:20 rq:1343756 version:2.26.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pdm/python-pdm.changes    2026-02-12 
17:29:54.773769249 +0100
+++ /work/SRC/openSUSE:Factory/.python-pdm.new.1999/python-pdm.changes  
2026-03-31 15:23:55.864424180 +0200
@@ -1,0 +2,10 @@
+Mon Mar 30 16:33:27 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2.26.7:
+  * Speed up dependency resolution when there are complex
+    conflicts.
+  * Switch to Zensical as docs generator.
+  * Add comprehensive tests for completion, show, search, and
+    info commands to improve test coverage
+
+-------------------------------------------------------------------

Old:
----
  pdm-2.26.6.tar.gz

New:
----
  pdm-2.26.7.tar.gz

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

Other differences:
------------------
++++++ python-pdm.spec ++++++
--- /var/tmp/diff_new_pack.wbWvMf/_old  2026-03-31 15:23:56.536452176 +0200
+++ /var/tmp/diff_new_pack.wbWvMf/_new  2026-03-31 15:23:56.536452176 +0200
@@ -27,7 +27,7 @@
 %endif
 %{?sle15_python_module_pythons}
 Name:           python-pdm%{psuffix}
-Version:        2.26.6
+Version:        2.26.7
 Release:        0
 Summary:        Python Development Master
 License:        MIT

++++++ pdm-2.26.6.tar.gz -> pdm-2.26.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/CHANGELOG.md new/pdm-2.26.7/CHANGELOG.md
--- old/pdm-2.26.6/CHANGELOG.md 2026-01-22 10:47:17.162061200 +0100
+++ new/pdm-2.26.7/CHANGELOG.md 2026-03-24 08:21:54.119341600 +0100
@@ -1,3 +1,18 @@
+## Release v2.26.7 (2026-03-24)
+
+### Features & Improvements
+
+- Speed up dependency resolution when there are complex conflicts. 
([#3751](https://github.com/pdm-project/pdm/issues/3751))
+
+### Documentation
+
+- Switch to Zensical as docs generator. 
([#3752](https://github.com/pdm-project/pdm/issues/3752))
+
+### Miscellany
+
+- Add comprehensive tests for completion, show, search, and info commands to 
improve test coverage ([#3541](https://github.com/pdm-project/pdm/issues/3541))
+
+
 ## Release v2.26.6 (2026-01-22)
 
 ### Bug Fixes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/PKG-INFO new/pdm-2.26.7/PKG-INFO
--- old/pdm-2.26.6/PKG-INFO     1970-01-01 01:00:00.000000000 +0100
+++ new/pdm-2.26.7/PKG-INFO     1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: pdm
-Version: 2.26.6
+Version: 2.26.7
 Summary: A modern Python package and dependency manager supporting the latest 
PEP standards
 Keywords: packaging,dependency,workflow
 Author-Email: Frost Ming <[email protected]>
@@ -124,7 +124,7 @@
 
 ![PDM 
logo](https://raw.githubusercontent.com/pdm-project/pdm/main/docs/assets/logo_big.png)
 
-[![Docs](https://img.shields.io/badge/Docs-mkdocs-blue?style=for-the-badge)](https://pdm-project.org)
+[![Docs](https://img.shields.io/badge/Docs-zensical-blue?style=for-the-badge)](https://pdm-project.org)
 [![Twitter 
Follow](https://img.shields.io/twitter/follow/pdm_project?label=get%20updates&logo=twitter&style=for-the-badge)](https://twitter.com/pdm_project)
 
[![Discord](https://img.shields.io/discord/824472774965329931?label=discord&logo=discord&style=for-the-badge)](https://discord.gg/Phn8smztpv)
 
@@ -248,6 +248,22 @@
 
 [Awesome PDM](https://github.com/pdm-project/awesome-pdm) is a curated list of 
awesome PDM plugins and resources.
 
+## Experimental
+
+Enable [PEP 582](https://peps.python.org/pep-0582/) for a project:
+
+    pdm config python.use_venv False
+
+This makes PDM install packages into a local project folder instead of a venv 
(similar to how npm installs into node_modules).
+    
+Enable [uv](https://github.com/astral-sh/uv) integration:
+
+    pdm config use_uv true
+
+uv is a very fast Python package installer written in Rust.
+    
+Note: `uv` does not work with `PEP 582`.
+
 ## Sponsors
 
 <p align="center">
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/README.md new/pdm-2.26.7/README.md
--- old/pdm-2.26.6/README.md    2026-01-22 10:47:17.162061200 +0100
+++ new/pdm-2.26.7/README.md    2026-03-24 08:21:54.119341600 +0100
@@ -7,7 +7,7 @@
 
 ![PDM 
logo](https://raw.githubusercontent.com/pdm-project/pdm/main/docs/assets/logo_big.png)
 
-[![Docs](https://img.shields.io/badge/Docs-mkdocs-blue?style=for-the-badge)](https://pdm-project.org)
+[![Docs](https://img.shields.io/badge/Docs-zensical-blue?style=for-the-badge)](https://pdm-project.org)
 [![Twitter 
Follow](https://img.shields.io/twitter/follow/pdm_project?label=get%20updates&logo=twitter&style=for-the-badge)](https://twitter.com/pdm_project)
 
[![Discord](https://img.shields.io/discord/824472774965329931?label=discord&logo=discord&style=for-the-badge)](https://discord.gg/Phn8smztpv)
 
@@ -131,6 +131,22 @@
 
 [Awesome PDM](https://github.com/pdm-project/awesome-pdm) is a curated list of 
awesome PDM plugins and resources.
 
+## Experimental
+
+Enable [PEP 582](https://peps.python.org/pep-0582/) for a project:
+
+    pdm config python.use_venv False
+
+This makes PDM install packages into a local project folder instead of a venv 
(similar to how npm installs into node_modules).
+    
+Enable [uv](https://github.com/astral-sh/uv) integration:
+
+    pdm config use_uv true
+
+uv is a very fast Python package installer written in Rust.
+    
+Note: `uv` does not work with `PEP 582`.
+
 ## Sponsors
 
 <p align="center">
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/pyproject.toml 
new/pdm-2.26.7/pyproject.toml
--- old/pdm-2.26.6/pyproject.toml       2026-01-22 10:47:23.148037000 +0100
+++ new/pdm-2.26.7/pyproject.toml       2026-03-24 08:21:59.608373600 +0100
@@ -58,7 +58,7 @@
     "Programming Language :: Python :: 3.12",
     "Programming Language :: Python :: 3.13",
 ]
-version = "2.26.6"
+version = "2.26.7"
 
 [project.urls]
 Homepage = "https://pdm-project.org";
@@ -164,14 +164,9 @@
     "tox-pdm>=0.5",
 ]
 doc = [
-    "mkdocs>=1.1",
-    "mkdocs-material>=7.3",
+    "zensical>=0.0.28; python_version >= '3.10'",
     "mkdocstrings[python]>=0.18",
     "setuptools>=62.3.3",
-    "markdown-exec>=0.7.0",
-    "mkdocs-redirects>=1.2.0",
-    "mkdocs-version-annotations>=1.0.0",
-    "mkdocs-llmstxt>=0.2.0",
 ]
 workflow = [
     "parver>=0.3.1",
@@ -323,15 +318,23 @@
 release = "python tasks/release.py"
 test = "pytest"
 tox = "tox"
+pre_doc = "python tasks/render_reference_docs.py"
 lint = "prek run --all-files"
 
 [tool.pdm.scripts.coverage]
 shell = "python -m pytest --verbosity=3 --cov=src/pdm --cov-branch 
--cov-report term-missing tests/\n                    "
 
 [tool.pdm.scripts.doc]
-cmd = "mkdocs serve"
+cmd = "zensical serve"
 help = "Start the dev server for docs preview"
 
+[tool.pdm.scripts.doc-build]
+composite = [
+    "pre_doc",
+    "zensical build",
+]
+help = "Build the documentation site"
+
 [tool.pdm.scripts.complete]
 call = "tasks.complete:main"
 help = "Create autocomplete files for bash and fish"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/VERSION 
new/pdm-2.26.7/src/pdm/VERSION
--- old/pdm-2.26.6/src/pdm/VERSION      2026-01-22 10:47:23.074037300 +0100
+++ new/pdm-2.26.7/src/pdm/VERSION      2026-03-24 08:21:59.535711300 +0100
@@ -1 +1 @@
-2.26.6
+2.26.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/cli/utils.py 
new/pdm-2.26.7/src/pdm/cli/utils.py
--- old/pdm-2.26.6/src/pdm/cli/utils.py 2026-01-22 10:47:17.169061200 +0100
+++ new/pdm-2.26.7/src/pdm/cli/utils.py 2026-03-24 08:21:54.127488000 +0100
@@ -125,9 +125,8 @@
 
     def __init__(self, *args: Any, **kwargs: Any) -> None:
         if sys.version_info >= (3, 14):
-            kwargs["formatter_class"] = argparse.RawDescriptionHelpFormatter
-        else:
-            kwargs["formatter_class"] = PdmFormatter
+            kwargs["color"] = False
+        kwargs["formatter_class"] = PdmFormatter
         kwargs["add_help"] = False
         super().__init__(*args, **kwargs)
         self.add_argument(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/models/python_max_versions.json 
new/pdm-2.26.7/src/pdm/models/python_max_versions.json
--- old/pdm-2.26.6/src/pdm/models/python_max_versions.json      2026-01-22 
10:47:17.172061000 +0100
+++ new/pdm-2.26.7/src/pdm/models/python_max_versions.json      2026-03-24 
08:21:54.129763600 +0100
@@ -11,11 +11,11 @@
     "3": 14,
     "3.0": 1,
     "3.1": 5,
-    "3.10": 19,
-    "3.11": 14,
-    "3.12": 12,
-    "3.13": 11,
-    "3.14": 2,
+    "3.10": 20,
+    "3.11": 15,
+    "3.12": 13,
+    "3.13": 12,
+    "3.14": 3,
     "3.2": 6,
     "3.3": 7,
     "3.4": 10,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/project/core.py 
new/pdm-2.26.7/src/pdm/project/core.py
--- old/pdm-2.26.6/src/pdm/project/core.py      2026-01-22 10:47:17.173061100 
+0100
+++ new/pdm-2.26.7/src/pdm/project/core.py      2026-03-24 08:21:54.131075000 
+0100
@@ -714,9 +714,11 @@
         deps_setter = [
             (
                 metadata.get("optional-dependencies", {}),
-                lambda x: metadata.setdefault("optional-dependencies", 
{}).__setitem__(group, x)
-                if x
-                else metadata.setdefault("optional-dependencies", 
{}).pop(group, None),
+                lambda x: (
+                    metadata.setdefault("optional-dependencies", 
{}).__setitem__(group, x)
+                    if x
+                    else metadata.setdefault("optional-dependencies", 
{}).pop(group, None)
+                ),
             ),
             (dev_dependencies, update_dev_dependencies),
         ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/pytest.py 
new/pdm-2.26.7/src/pdm/pytest.py
--- old/pdm-2.26.6/src/pdm/pytest.py    2026-01-22 10:47:17.173061100 +0100
+++ new/pdm-2.26.7/src/pdm/pytest.py    2026-03-24 08:21:54.131433200 +0100
@@ -653,6 +653,10 @@
                 os.environ.update(old_env)
                 if cleanup:
                     core.exit_stack.close()
+                    # Clear the build directory cache to avoid stale references
+                    from pdm.models.candidates import PreparedCandidate
+
+                    PreparedCandidate._build_dir_cache.clear()
 
         result = RunResult(exit_code, stdout.getvalue(), stderr.getvalue(), 
exception)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/resolver/providers.py 
new/pdm-2.26.7/src/pdm/resolver/providers.py
--- old/pdm-2.26.6/src/pdm/resolver/providers.py        2026-01-22 
10:47:17.174061000 +0100
+++ new/pdm-2.26.7/src/pdm/resolver/providers.py        2026-03-24 
08:21:54.131875300 +0100
@@ -2,6 +2,7 @@
 
 import dataclasses
 import os
+from collections import defaultdict
 from functools import cached_property
 from typing import TYPE_CHECKING, Callable
 
@@ -39,6 +40,7 @@
 
 
 _PROVIDER_REGISTRY: dict[str, type[BaseProvider]] = {}
+_CONFLICT_PRIORITY_THRESHOLD = 5
 
 
 def get_provider(strategy: str) -> type[BaseProvider]:
@@ -79,6 +81,8 @@
         self.excludes = {normalize_name(k) for k in 
project.pyproject.resolution.get("excludes", [])}
         self.direct_minimal_versions = direct_minimal_versions
         self.locked_repository = locked_repository
+        self._conflict_counts: defaultdict[str, int] = defaultdict(int)
+        self._conflict_promoted: set[str] = set()
 
     def requirement_preference(self, requirement: Requirement) -> Comparable:
         """Return the preference of a requirement to find candidates.
@@ -97,12 +101,49 @@
     def identify(self, requirement_or_candidate: Requirement | Candidate) -> 
str:
         return requirement_or_candidate.identify()
 
+    def narrow_requirement_selection(
+        self,
+        identifiers: Iterable[str],
+        resolutions: Mapping[str, Candidate],
+        candidates: Mapping[str, Iterator[Candidate]],
+        information: Mapping[str, Iterator[RequirementInformation]],
+        backtrack_causes: Sequence[RequirementInformation],
+    ) -> Iterable[str]:
+        backtrack_identifiers: set[str] = set()
+        for requirement, parent in backtrack_causes:
+            names = [requirement.identify()]
+            if parent is not None:
+                names.append(parent.identify())
+            for name in names:
+                backtrack_identifiers.add(name)
+                if name not in resolutions:
+                    self._conflict_counts[name] += 1
+                    if self._conflict_counts[name] >= 
_CONFLICT_PRIORITY_THRESHOLD:
+                        self._conflict_promoted.add(name)
+
+        current_backtrack_causes: list[str] = []
+        promoted: list[str] = []
+        for identifier in identifiers:
+            if identifier == "python":
+                return [identifier]
+            if identifier in backtrack_identifiers:
+                current_backtrack_causes.append(identifier)
+                continue
+            if identifier in self._conflict_promoted:
+                promoted.append(identifier)
+
+        if current_backtrack_causes:
+            return current_backtrack_causes
+        if promoted:
+            return promoted
+        return identifiers
+
     def get_preference(
         self,
         identifier: str,
-        resolutions: dict[str, Candidate],
-        candidates: dict[str, Iterator[Candidate]],
-        information: dict[str, Iterator[RequirementInformation]],
+        resolutions: Mapping[str, Candidate],
+        candidates: Mapping[str, Iterator[Candidate]],
+        information: Mapping[str, Iterator[RequirementInformation]],
         backtrack_causes: Sequence[RequirementInformation],
     ) -> tuple[Comparable, ...]:
         is_top = any(parent is None for _, parent in information[identifier])
@@ -123,9 +164,11 @@
         is_python = identifier == "python"
         is_pinned = any(op[:2] == "==" for op in operators)
         constraints = len(operators)
+        is_conflict_promoted = identifier in self._conflict_promoted
         return (
             not is_python,
             not is_top,
+            not is_conflict_promoted,
             not is_file_or_url,
             not is_pinned,
             not is_backtrack_cause,
@@ -458,9 +501,9 @@
     def get_preference(
         self,
         identifier: str,
-        resolutions: dict[str, Candidate],
-        candidates: dict[str, Iterator[Candidate]],
-        information: dict[str, Iterator[RequirementInformation]],
+        resolutions: Mapping[str, Candidate],
+        candidates: Mapping[str, Iterator[Candidate]],
+        information: Mapping[str, Iterator[RequirementInformation]],
         backtrack_causes: Sequence[RequirementInformation],
     ) -> tuple[Comparable, ...]:
         # Resolve tracking packages so we have a chance to unpin them first.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/src/pdm/utils.py 
new/pdm-2.26.7/src/pdm/utils.py
--- old/pdm-2.26.6/src/pdm/utils.py     2026-01-22 10:47:17.174061000 +0100
+++ new/pdm-2.26.7/src/pdm/utils.py     2026-03-24 08:21:54.131875300 +0100
@@ -352,17 +352,27 @@
         if hasattr(parsed, "__replace__"):  # packaging >= 26
             parsed = parsed.__replace__(local=None)
         else:
+            # packaging < 26 does not have __replace__ method
+            # In this version, we need to manually update _version and 
recompute _key
+            # Note: In packaging >= 26, _key is a read-only property, but this 
else branch
+            # only executes on packaging < 26 where _key is a regular 
attribute that can be
+            # assigned. We use object.__setattr__() instead of direct 
assignment to satisfy
+            # type checkers that analyze based on the current packaging 
version.
             from packaging.version import _cmpkey
 
             parsed._version = parsed._version._replace(local=None)
 
-            parsed._key = _cmpkey(
-                parsed._version.epoch,
-                parsed._version.release,
-                parsed._version.pre,
-                parsed._version.post,
-                parsed._version.dev,
-                parsed._version.local,
+            object.__setattr__(
+                parsed,
+                "_key",
+                _cmpkey(
+                    parsed._version.epoch,
+                    parsed._version.release,
+                    parsed._version.pre,
+                    parsed._version.post,
+                    parsed._version.dev,
+                    parsed._version.local,
+                ),
             )
 
     return parsed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/cli/test_completion.py 
new/pdm-2.26.7/tests/cli/test_completion.py
--- old/pdm-2.26.6/tests/cli/test_completion.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/pdm-2.26.7/tests/cli/test_completion.py 2026-03-24 08:21:54.132939800 
+0100
@@ -0,0 +1,63 @@
+"""Tests for the completion command"""
+
+
+def test_completion_bash(pdm):
+    """Test completion for bash shell"""
+    result = pdm(["completion", "bash"])
+    assert result.exit_code == 0
+    assert "BASH completion script for pdm" in result.output
+
+
+def test_completion_zsh(pdm):
+    """Test completion for zsh shell"""
+    result = pdm(["completion", "zsh"])
+    assert result.exit_code == 0
+    assert "#compdef pdm" in result.output
+
+
+def test_completion_fish(pdm):
+    """Test completion for fish shell"""
+    result = pdm(["completion", "fish"])
+    assert result.exit_code == 0
+    assert "FISH completion script for pdm" in result.output
+
+
+def test_completion_powershell(pdm):
+    """Test completion for powershell"""
+    result = pdm(["completion", "powershell"])
+    assert result.exit_code == 0
+    assert "Powershell completion script for pdm" in result.output
+
+
+def test_completion_pwsh(pdm):
+    """Test completion for pwsh (PowerShell Core)"""
+    result = pdm(["completion", "pwsh"])
+    assert result.exit_code == 0
+    assert "Powershell completion script for pdm" in result.output
+
+
+def test_completion_unsupported_shell(pdm):
+    """Test completion with unsupported shell raises error"""
+    result = pdm(["completion", "unsupported_shell"])
+    assert result.exit_code != 0
+    assert "Unsupported shell" in result.stderr
+
+
+def test_completion_auto_detect(pdm, monkeypatch):
+    """Test completion with auto-detected shell"""
+    import shellingham
+
+    monkeypatch.setattr(shellingham, "detect_shell", lambda: ("bash", 
"/bin/bash"))
+    result = pdm(["completion"])
+    assert result.exit_code == 0
+    assert "BASH completion script for pdm" in result.output
+
+
+def test_completion_auto_detect_unsupported(pdm, monkeypatch):
+    """Test completion with auto-detected unsupported shell"""
+    import shellingham
+
+    monkeypatch.setattr(shellingham, "detect_shell", lambda: ("csh", 
"/bin/csh"))
+    result = pdm(["completion"])
+    assert result.exit_code != 0
+    assert "Unsupported shell" in result.stderr
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/cli/test_info.py 
new/pdm-2.26.7/tests/cli/test_info.py
--- old/pdm-2.26.6/tests/cli/test_info.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/pdm-2.26.7/tests/cli/test_info.py       2026-03-24 08:21:54.132939800 
+0100
@@ -0,0 +1,112 @@
+"""Additional tests for the info command to improve coverage"""
+
+import json
+
+
+def test_info_command_packages_option(project, pdm):
+    """Test info command with --packages option"""
+    result = pdm(["info", "--packages"], obj=project)
+    assert result.exit_code == 0
+    # Should show packages path
+    assert result.output.strip() != ""
+
+
+def test_info_command_all_options_mutually_exclusive(project, pdm):
+    """Test that info command options are mutually exclusive"""
+    # Only one field option should work at a time
+    result = pdm(["info", "--python"], obj=project)
+    assert result.exit_code == 0
+
+    result = pdm(["info", "--where"], obj=project)
+    assert result.exit_code == 0
+
+    result = pdm(["info", "--packages"], obj=project)
+    assert result.exit_code == 0
+
+    result = pdm(["info", "--env"], obj=project)
+    assert result.exit_code == 0
+
+
+def test_info_command_env_output_format(project, pdm):
+    """Test that --env outputs valid JSON"""
+    result = pdm(["info", "--env"], obj=project, strict=True)
+    # Try to parse as JSON
+    try:
+        data = json.loads(result.output)
+        assert isinstance(data, dict)
+    except json.JSONDecodeError:
+        # If it's not JSON, it should at least contain marker info
+        assert "python_version" in result.output or "implementation" in 
result.output
+
+
+def test_info_command_json_contains_all_fields(project, pdm):
+    """Test that --json output contains all expected fields"""
+    result = pdm(["info", "--json"], obj=project, strict=True)
+
+    data = json.loads(result.output)
+
+    # Check pdm section
+    assert "pdm" in data
+    assert "version" in data["pdm"]
+
+    # Check python section
+    assert "python" in data
+    assert "interpreter" in data["python"]
+    assert "version" in data["python"]
+    assert "markers" in data["python"]
+    assert isinstance(data["python"]["markers"], dict)
+
+    # Check project section
+    assert "project" in data
+    assert "root" in data["project"]
+    assert "pypackages" in data["project"]
+
+
+def test_info_command_default_output(project, pdm):
+    """Test info command with no options shows formatted output"""
+    result = pdm(["info"], obj=project, strict=True)
+
+    # Should contain all major sections
+    assert "PDM version" in result.output or "PDM" in result.output
+    assert "Python Interpreter" in result.output or "Interpreter" in 
result.output
+    assert "Project Root" in result.output or "Root" in result.output
+    assert "Local Packages" in result.output or "Packages" in result.output
+
+
+def test_info_command_with_global_project(pdm, tmp_path, monkeypatch):
+    """Test info command with global project"""
+    monkeypatch.chdir(tmp_path)
+
+    result = pdm(["info", "-g", "--python"])
+    assert result.exit_code == 0
+
+
+def test_info_command_shows_venv_info(project, pdm):
+    """Test info command shows virtual environment information when 
applicable"""
+    # Create a venv
+    project.global_config["python.use_venv"] = True
+
+    result = pdm(["info"], obj=project)
+    assert result.exit_code == 0
+
+
+def test_info_command_non_local_environment(project, pdm, mocker):
+    """Test info command with non-local environment shows site-packages"""
+    # Mock the environment to be non-local
+    project.environment.is_local = False
+
+    # Mock get_paths to return purelib
+    project.environment.get_paths = mocker.Mock(return_value={"purelib": 
"/fake/purelib"})
+
+    result = pdm(["info", "--packages"], obj=project)
+    assert result.exit_code == 0
+    assert "/fake/purelib" in result.output or "purelib" in result.output
+
+
+def test_info_command_global_project_prefix(project, pdm):
+    """Test that global project shows 'Global' prefix in output"""
+    result = pdm(["info", "-g"], obj=project)
+
+    # If it's a global project, should show "Global" prefix
+    # This test checks if the code path exists
+    assert result.exit_code == 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/cli/test_lock.py 
new/pdm-2.26.7/tests/cli/test_lock.py
--- old/pdm-2.26.6/tests/cli/test_lock.py       2026-01-22 10:47:17.175061000 
+0100
+++ new/pdm-2.26.7/tests/cli/test_lock.py       2026-03-24 08:21:54.132939800 
+0100
@@ -60,9 +60,11 @@
         core.repository_class,
         "get_hashes",
         side_effect=(
-            lambda c: [{"url": url, "file": Link(url).filename, "hash": hash} 
for url, hash in url_hashes.items()]
-            if c.identify() == "requests"
-            else []
+            lambda c: (
+                [{"url": url, "file": Link(url).filename, "hash": hash} for 
url, hash in url_hashes.items()]
+                if c.identify() == "requests"
+                else []
+            )
         ),
     )
     assert not project.is_lockfile_hash_match()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/cli/test_search.py 
new/pdm-2.26.7/tests/cli/test_search.py
--- old/pdm-2.26.6/tests/cli/test_search.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/pdm-2.26.7/tests/cli/test_search.py     2026-03-24 08:21:54.133341600 
+0100
@@ -0,0 +1,103 @@
+"""Tests for the search command utilities"""
+
+from pdm.cli.commands.search import print_results
+
+
+def test_print_results_empty_hits(mocker):
+    """Test print_results with empty hits returns early"""
+    ui = mocker.Mock()
+    working_set = mocker.Mock()
+
+    # Should return early without calling echo
+    print_results(ui, [], working_set)
+
+    ui.echo.assert_not_called()
+
+
+def test_print_results_with_hits(mocker):
+    """Test print_results with search hits"""
+    ui = mocker.Mock()
+    working_set = {}
+
+    # Create mock search hits
+    hit1 = mocker.Mock()
+    hit1.name = "test-package"
+    hit1.version = "1.0.0"
+    hit1.summary = "A test package"
+
+    hit2 = mocker.Mock()
+    hit2.name = "another-package"
+    hit2.version = "2.0.0"
+    hit2.summary = "Another test package"
+
+    hits = [hit1, hit2]
+
+    print_results(ui, hits, working_set)
+
+    # Should call echo for each hit
+    assert ui.echo.call_count >= 2
+
+
+def test_print_results_with_installed_package(mocker):
+    """Test print_results shows INSTALLED for packages in working set"""
+    ui = mocker.Mock()
+    working_set = mocker.Mock()
+
+    # Mock a package that's installed
+    hit = mocker.Mock()
+    hit.name = "installed-package"
+    hit.version = "1.0.0"
+    hit.summary = "An installed package"
+
+    # Mock working set to return a distribution
+    dist = mocker.Mock()
+    dist.version = "1.0.0"
+    working_set.__contains__ = mocker.Mock(return_value=True)
+    working_set.__getitem__ = mocker.Mock(return_value=dist)
+
+    print_results(ui, [hit], working_set)
+
+    # Should show INSTALLED label
+    calls = [str(call) for call in ui.echo.call_args_list]
+    assert any("INSTALLED" in str(call) for call in calls)
+
+
+def test_print_results_with_terminal_width(mocker):
+    """Test print_results respects terminal width for wrapping"""
+    ui = mocker.Mock()
+    working_set = {}
+
+    hit = mocker.Mock()
+    hit.name = "test-package"
+    hit.version = "1.0.0"
+    hit.summary = "This is a very long summary that should be wrapped when 
terminal width is specified"
+
+    print_results(ui, [hit], working_set, terminal_width=40)
+
+    # Should call echo
+    ui.echo.assert_called()
+
+
+def test_print_results_unicode_error(mocker):
+    """Test print_results handles UnicodeEncodeError gracefully"""
+    ui = mocker.Mock()
+    working_set = {}
+
+    hit = mocker.Mock()
+    hit.name = "test-package"
+    hit.version = "1.0.0"
+    hit.summary = "Test summary"
+
+    # Make echo raise UnicodeEncodeError
+    ui.echo.side_effect = UnicodeEncodeError("utf-8", "", 0, 1, "test")
+
+    # Should not raise exception
+    print_results(ui, [hit], working_set)
+
+
+def test_search_command_deprecation_warning(pdm):
+    """Test that search command shows deprecation warning"""
+    result = pdm(["search", "test"])
+    # Command should succeed but show warning
+    assert result.exit_code == 0
+    assert "deprecated" in result.stderr.lower() or "deprecated" in 
result.output.lower()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/cli/test_show.py 
new/pdm-2.26.7/tests/cli/test_show.py
--- old/pdm-2.26.6/tests/cli/test_show.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/pdm-2.26.7/tests/cli/test_show.py       2026-03-24 08:21:54.133341600 
+0100
@@ -0,0 +1,71 @@
+"""Additional tests for the show command"""
+
+import pytest
+
+from pdm.cli.commands.show import filter_stable
+
+
+def test_filter_stable_with_stable_version(mocker):
+    """Test filter_stable returns True for stable versions"""
+    # Mock package with stable version
+    package = mocker.Mock()
+    package.version = "1.0.0"
+    assert filter_stable(package) is True
+
+
+def test_filter_stable_with_prerelease_alpha(mocker):
+    """Test filter_stable returns False for alpha prereleases"""
+    package = mocker.Mock()
+    package.version = "1.0.0a1"
+    assert filter_stable(package) is False
+
+
+def test_filter_stable_with_prerelease_beta(mocker):
+    """Test filter_stable returns False for beta prereleases"""
+    package = mocker.Mock()
+    package.version = "2.0.0b2"
+    assert filter_stable(package) is False
+
+
+def test_filter_stable_with_prerelease_rc(mocker):
+    """Test filter_stable returns False for release candidates"""
+    package = mocker.Mock()
+    package.version = "3.0.0rc1"
+    assert filter_stable(package) is False
+
+
+def test_filter_stable_with_dev_version(mocker):
+    """Test filter_stable returns False for dev versions"""
+    package = mocker.Mock()
+    package.version = "1.0.0.dev1"
+    assert filter_stable(package) is False
+
+
[email protected]
+def test_show_command_with_specific_metadata_keys(pdm):
+    """Test show command with specific metadata keys"""
+    result = pdm(["show", "requests", "--name"])
+    assert result.exit_code == 0
+    assert "requests" in result.output.lower()
+
+    result = pdm(["show", "requests", "--version"])
+    assert result.exit_code == 0
+    # Should contain a version number
+
+
[email protected]
+def test_show_command_with_multiple_metadata_keys(pdm):
+    """Test show command with multiple metadata keys only shows selected 
ones"""
+    result = pdm(["show", "requests", "--name", "--version"])
+    assert result.exit_code == 0
+    # Should only show name and version, not full metadata
+
+
+def test_show_command_non_distribution_project(project, pdm):
+    """Test show command on a non-distribution project raises error"""
+    # Mark the project as non-distribution
+    project.pyproject.settings["distribution"] = False
+
+    result = pdm(["show"], obj=project)
+    assert result.exit_code != 0
+    assert "not a library" in result.stderr
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pdm-2.26.6/tests/resolver/test_providers.py 
new/pdm-2.26.7/tests/resolver/test_providers.py
--- old/pdm-2.26.6/tests/resolver/test_providers.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/pdm-2.26.7/tests/resolver/test_providers.py     2026-03-24 
08:21:54.150679000 +0100
@@ -0,0 +1,68 @@
+from __future__ import annotations
+
+from collections.abc import Iterator
+
+from resolvelib.resolvers import RequirementInformation
+
+from pdm.models.candidates import Candidate
+from pdm.models.requirements import parse_requirement
+from pdm.resolver.providers import _CONFLICT_PRIORITY_THRESHOLD
+
+
+def _build_candidates(identifier: str) -> dict[str, Iterator[Candidate]]:
+    requirement = parse_requirement(identifier)
+    candidate = Candidate(requirement, name=requirement.project_name, 
version="1.0")
+    return {identifier: iter([candidate])}
+
+
+def _build_information(identifier: str) -> dict[str, 
Iterator[RequirementInformation]]:
+    requirement = parse_requirement(identifier)
+    return {identifier: iter([RequirementInformation(requirement, None)])}
+
+
+def test_narrow_requirement_selection_promotes_repeated_conflicts(project, 
repository):
+    repository.add_candidate("conflict-pkg", "1.0")
+    repository.add_candidate("other-pkg", "1.0")
+
+    provider = project.get_provider()
+    narrow = provider.narrow_requirement_selection
+    causes = [RequirementInformation(parse_requirement("conflict-pkg"), None)]
+
+    for _ in range(1, _CONFLICT_PRIORITY_THRESHOLD):
+        result = list(narrow(["other-pkg"], {}, {}, {}, causes))
+        assert result == ["other-pkg"]
+
+    result = list(narrow(["other-pkg", "conflict-pkg"], {}, {}, {}, causes))
+    assert result == ["conflict-pkg"]
+
+    result = list(narrow(["other-pkg", "conflict-pkg"], {}, {}, {}, []))
+    assert result == ["conflict-pkg"]
+
+    other_causes = [RequirementInformation(parse_requirement("other-pkg"), 
None)]
+    result = list(narrow(["other-pkg", "conflict-pkg"], {}, {}, {}, 
other_causes))
+    assert result == ["other-pkg"]
+
+
+def test_get_preference_prioritizes_promoted_conflicts(project, repository):
+    repository.add_candidate("promoted-pkg", "1.0")
+    repository.add_candidate("normal-pkg", "1.0")
+
+    provider = project.get_provider()
+    provider._conflict_promoted.add("promoted-pkg")
+
+    promoted_preference = provider.get_preference(
+        "promoted-pkg",
+        {},
+        _build_candidates("promoted-pkg"),
+        _build_information("promoted-pkg"),
+        [],
+    )
+    normal_preference = provider.get_preference(
+        "normal-pkg",
+        {},
+        _build_candidates("normal-pkg"),
+        _build_information("normal-pkg"),
+        [],
+    )
+
+    assert promoted_preference < normal_preference

Reply via email to