diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e62ad9fddb..90e7b69d33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,12 +63,13 @@ jobs: uses: actions/setup-python@v5 with: python-version: | - 3.7 3.8 3.9 3.10 3.11 3.12 + 3.13 + allow-prereleases: true if: matrix.os != 'macos-latest' - name: Setup Python Versions uses: actions/setup-python@v5 @@ -77,6 +78,8 @@ jobs: 3.10 3.11 3.12 + 3.13 + allow-prereleases: true if: matrix.os == 'macos-latest' - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/docs/usage/project.md b/docs/usage/project.md index 55aa04ae93..e3ededcb0d 100644 --- a/docs/usage/project.md +++ b/docs/usage/project.md @@ -18,7 +18,7 @@ will be stored in `.pdm-python` and used by subsequent commands. You can also ch Alternatively, you can specify the Python interpreter path via `PDM_PYTHON` environment variable. When it is set, the path saved in `.pdm-python` will be ignored. !!! warning "Using an existing environment" - If you choose to use an existing environment, such as reusing an environment created by `conda`, please note that PDM will *remove* dependencies not listed in `pyproject.toml` or `pdm.lock` when running `pdm sync --clean` or `pdm remove`. This may lead to destructive consequences. Therefore, try not to share environment among multiple projects. +If you choose to use an existing environment, such as reusing an environment created by `conda`, please note that PDM will _remove_ dependencies not listed in `pyproject.toml` or `pdm.lock` when running `pdm sync --clean` or `pdm remove`. This may lead to destructive consequences. Therefore, try not to share environment among multiple projects. ### Install Python interpreters with PDM @@ -99,7 +99,7 @@ In `pyproject.toml`, there is a field `distribution` under the `[tool.pdm]` tabl ## Specify `requires-python` -You need to set an appropriate `requires-python` value for your project. This is an important property that affects how dependencies are resolved. Basically, each package's `requires-python` must *cover* the project's `requires-python` range. For example, consider the following setup: +You need to set an appropriate `requires-python` value for your project. This is an important property that affects how dependencies are resolved. Basically, each package's `requires-python` must _cover_ the project's `requires-python` range. For example, consider the following setup: - Project: `requires-python = ">=3.9"` - Package `foo`: `requires-python = ">=3.7,<3.11"` @@ -111,7 +111,7 @@ Unable to find a resolution because the following dependencies don't work on all Python versions defined by the project's `requires-python` ``` -Because the dependency's `requires-python` is `>=3.7,<3.11`, it *doesn't* cover the project's `requires-python` range of `>=3.9`. In other words, the project promises to work on Python 3.9, 3.10, 3.11 (and so on), but the dependency doesn't support Python 3.11 (or any higher). Since PDM creates a cross-platform lockfile that should work on all Python versions within the `requires-python` range, it can't find a valid resolution. +Because the dependency's `requires-python` is `>=3.7,<3.11`, it _doesn't_ cover the project's `requires-python` range of `>=3.9`. In other words, the project promises to work on Python 3.9, 3.10, 3.11 (and so on), but the dependency doesn't support Python 3.11 (or any higher). Since PDM creates a cross-platform lockfile that should work on all Python versions within the `requires-python` range, it can't find a valid resolution. To fix this, you need add a maximum version to `requires-python`, like `>=3.9,<3.11`. The value of `requires-python` is a [version specifier as defined in PEP 440](https://peps.python.org/pep-0440/#version-specifiers). Here are some examples: @@ -124,6 +124,10 @@ The value of `requires-python` is a [version specifier as defined in PEP 440](ht ## Working with older Python versions +--- 2.19.0 + + PDM now supports 3.8 and above as the python version of projects. + Although PDM run on Python 3.8 and above, you can still have lower Python versions for your **working project**. But remember, if your project is a library, which needs to be built, published or installed, you make sure the PEP 517 build backend being used supports the lowest Python version you need. For instance, the default backend `pdm-backend` only works on Python 3.7+, so if you run [`pdm build`](../reference/cli.md#build) on a project with Python 3.6, you will get an error. Most modern build backends have dropped the support for Python 3.6 and lower, so it is highly recommended to upgrade the Python version to 3.7+. Here are the supported Python range for some commonly used build backends, we only list those that support PEP 621 since otherwise PDM can't work with them. | Backend | Supported Python | Support PEP 621 | @@ -151,7 +155,7 @@ PDM provides `import` command so that you don't have to initialize the project m Also, when you are executing [`pdm init`](../reference/cli.md#init) or [`pdm install`](../reference/cli.md#install), PDM can auto-detect possible files to import if your PDM project has not been initialized yet. !!! info - Converting a `setup.py` will execute the file with the project interpreter. Make sure `setuptools` is installed with the interpreter and the `setup.py` is trusted. +Converting a `setup.py` will execute the file with the project interpreter. Make sure `setuptools` is installed with the interpreter and the `setup.py` is trusted. ## Working with version control diff --git a/news/3176.breaking.md b/news/3176.breaking.md new file mode 100644 index 0000000000..1743fea79c --- /dev/null +++ b/news/3176.breaking.md @@ -0,0 +1 @@ +The minimum supported Python version of projects using PDM has been bumped to 3.8. diff --git a/news/3176.bugfix.md b/news/3176.bugfix.md new file mode 100644 index 0000000000..40557f0bb3 --- /dev/null +++ b/news/3176.bugfix.md @@ -0,0 +1 @@ +Call functions from shared library in the in-process `env_spec.py` script. diff --git a/pdm.lock b/pdm.lock index 8fb3de8d9d..f5229bd717 100644 --- a/pdm.lock +++ b/pdm.lock @@ -575,7 +575,7 @@ files = [ [[package]] name = "dep-logic" -version = "0.4.6" +version = "0.4.8" requires_python = ">=3.8" summary = "Python dependency specifications supporting logical operations" groups = ["default"] @@ -583,8 +583,8 @@ dependencies = [ "packaging>=22", ] files = [ - {file = "dep_logic-0.4.6-py3-none-any.whl", hash = "sha256:837a890bff8c8eeef0aa54ece388d423a83f48e8697f25b74549d90f65f7da72"}, - {file = "dep_logic-0.4.6.tar.gz", hash = "sha256:673d45402e9f11c4e501b08ebaea1efaa5e9bc6f69410684a9e448f8f5b26d6a"}, + {file = "dep_logic-0.4.8-py3-none-any.whl", hash = "sha256:0d436d493bbe6bec45588d8c432e318bd388a67a0965f7b527b2721f5c70b01b"}, + {file = "dep_logic-0.4.8.tar.gz", hash = "sha256:6be51b8a51893e67961a5c10ed7b8dd95fffe3ee42982fbb99f68218221a6ad2"}, ] [[package]] diff --git a/src/pdm/models/in_process/__init__.py b/src/pdm/models/in_process/__init__.py index c29da68823..12128a116a 100644 --- a/src/pdm/models/in_process/__init__.py +++ b/src/pdm/models/in_process/__init__.py @@ -9,6 +9,7 @@ import json import os import subprocess +import sysconfig import tempfile from typing import Any, Generator @@ -47,5 +48,6 @@ def parse_setup_py(executable: str, path: str) -> dict[str, Any]: @functools.lru_cache def get_env_spec(executable: str) -> EnvSpec: """Get the environment spec of the python interpreter""" + shared_lib = sysconfig.get_path("purelib") with _in_process_script("env_spec.py") as script: - return EnvSpec.from_spec(**json.loads(subprocess.check_output([executable, "-Es", script]))) + return EnvSpec.from_spec(**json.loads(subprocess.check_output([executable, "-EsS", script, shared_lib]))) diff --git a/src/pdm/models/in_process/env_spec.py b/src/pdm/models/in_process/env_spec.py index 5a41fe24b6..b167e2c027 100644 --- a/src/pdm/models/in_process/env_spec.py +++ b/src/pdm/models/in_process/env_spec.py @@ -1,66 +1,24 @@ +from __future__ import annotations + import json import platform +import site import sys import sysconfig -class EnvError(Exception): - pass - - -def get_arch() -> str: - arch = platform.machine().lower() - if arch in ("i386", "i686"): - return "x86" - if arch == "amd64": - if platform.architecture()[0] == "32bit": - return "x86" - return "x86_64" - if arch == "arm64": - return "aarch64" - return arch - - -def get_platform() -> str: - """Return the current platform.""" - - system = platform.system() - arch = get_arch() - if system == "Linux": - libc_ver = platform.libc_ver()[1] - if libc_ver: - parts = libc_ver.split(".") - return f"manylinux_{parts[0]}_{parts[1]}_{arch}" - else: # musl - # There is no easy way to retrieve the musl version, so we just assume it's 1.2 - return f"musllinux_1_2_{arch}" - elif system == "Windows": - if arch == "aarch64": - return "windows_arm64" - if arch == "x86_64": - return "windows_amd64" - return f"windows_{arch}" - elif system == "Darwin": - mac_ver = platform.mac_ver()[0].split(".") - if arch == "aarch64": - arch = "arm64" - major, minor = int(mac_ver[0]), int(mac_ver[1]) - if major >= 11: - minor = 0 - return f"macos_{major}_{minor}_{arch}" - else: - raise EnvError("Unsupported platform") - +def get_current_env_spec(shared_lib: str) -> dict[str, str | bool]: + site.addsitedir(shared_lib) + from dep_logic.tags import Platform -def get_current_env_spec(): python_version = f"{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}" return { "requires_python": f"=={python_version}", - "platform": get_platform(), + "platform": str(Platform.current()), "implementation": platform.python_implementation().lower(), "gil_disabled": sysconfig.get_config_var("Py_GIL_DISABLED") or False, } if __name__ == "__main__": - print(json.dumps(get_current_env_spec(), indent=2)) + print(json.dumps(get_current_env_spec(sys.argv[1]), indent=2)) diff --git a/tests/test_integration.py b/tests/test_integration.py index 5fd342498b..55deccd534 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -3,7 +3,7 @@ from pdm.utils import cd -PYTHON_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] +PYTHON_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] PYPROJECT = { "project": {"name": "test-project", "version": "0.1.0", "requires-python": ">=3.7"}, "build-system": {"requires": ["pdm-backend"], "build-backend": "pdm.backend"}, diff --git a/tox.ini b/tox.ini index e12ae8eb9d..9a7d00c628 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ # https://pypi.org/project/tox-pdm/ is needed to run this tox configuration [tox] -envlist = py3{8,9,10,11,12} +envlist = py3{8,9,10,11,12,13} passenv = LD_PRELOAD isolated_build = True