diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f96b3300e..eb7a497a21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,12 @@ ci: autoupdate_schedule: monthly repos: + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: 'v0.0.246' + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - repo: https://github.com/psf/black rev: 23.1.0 hooks: @@ -11,18 +17,6 @@ repos: hooks: - id: codespell # See setup.cfg for args - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear - - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.991 hooks: diff --git a/install-pdm.py b/install-pdm.py index 27aff2bc15..ed6bd2e337 100644 --- a/install-pdm.py +++ b/install-pdm.py @@ -39,9 +39,7 @@ def _call_subprocess(args: list[str]) -> int: try: - return subprocess.run( - args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True - ).returncode + return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True).returncode except subprocess.CalledProcessError as e: print(f"An error occurred when executing {args}:", file=sys.stderr) print(e.output.decode("utf-8"), file=sys.stderr) @@ -100,7 +98,7 @@ def _get_win_folder_from_registry(csidl_name: str) -> str: return dir try: - from ctypes import windll # noqa + from ctypes import windll # noqa: F401 _get_win_folder = _get_win_folder_with_ctypes except ImportError: @@ -110,14 +108,10 @@ def _remove_path_windows(target: Path) -> None: value = os.path.normcase(target) with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: - with winreg.OpenKey( - root, "Environment", 0, winreg.KEY_ALL_ACCESS - ) as env_key: + with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as env_key: try: old_value, type_ = winreg.QueryValueEx(env_key, "PATH") - paths = [ - os.path.normcase(item) for item in old_value.split(os.pathsep) - ] + paths = [os.path.normcase(item) for item in old_value.split(os.pathsep)] if value not in paths: return @@ -132,14 +126,10 @@ def _add_to_path(target: Path) -> None: if WINDOWS: with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as root: - with winreg.OpenKey( - root, "Environment", 0, winreg.KEY_ALL_ACCESS - ) as env_key: + with winreg.OpenKey(root, "Environment", 0, winreg.KEY_ALL_ACCESS) as env_key: try: old_value, type_ = winreg.QueryValueEx(env_key, "PATH") - if value in [ - os.path.normcase(item) for item in old_value.split(os.pathsep) - ]: + if value in [os.path.normcase(item) for item in old_value.split(os.pathsep)]: return except FileNotFoundError: old_value, type_ = "", winreg.REG_EXPAND_SZ @@ -224,9 +214,7 @@ def sort_version(v: str) -> tuple: parts.append(rest) return tuple(parts) - releases = sorted( - filter(version_okay, metadata["releases"]), key=sort_version, reverse=True - ) + releases = sorted(filter(version_okay, metadata["releases"]), key=sort_version, reverse=True) return releases[0] @@ -266,16 +254,11 @@ def _make_env(self) -> Path: import virtualenv except ModuleNotFoundError: python_version = f"{sys.version_info.major}.{sys.version_info.minor}" - url = ( - "https://bootstrap.pypa.io/virtualenv/" - f"{python_version}/virtualenv.pyz" - ) + url = f"https://bootstrap.pypa.io/virtualenv/{python_version}/virtualenv.pyz" with TemporaryDirectory(prefix="pdm-installer-") as tempdir: virtualenv_zip = Path(tempdir) / "virtualenv.pyz" urllib.request.urlretrieve(url, virtualenv_zip) - _call_subprocess( - [sys.executable, str(virtualenv_zip), str(venv_path)] - ) + _call_subprocess([sys.executable, str(virtualenv_zip), str(venv_path)]) else: virtualenv.cli_run([str(venv_path)]) @@ -311,7 +294,7 @@ def _install(self, venv_path: Path) -> None: else: req = "pdm" args = [req] + [d for d in self.additional_deps if d] - pip_cmd = [str(venv_python), "-m", "pip", "install"] + args + pip_cmd = [str(venv_python), "-m", "pip", "install", *args] _call_subprocess(pip_cmd) def _make_bin(self, venv_path: Path) -> Path: @@ -370,8 +353,7 @@ def _write_output(self, venv_path: Path, script: Path) -> None: output = { "pdm_version": self.version, "pdm_bin": str(script), - "install_python_version": f"{sys.version_info.major}." - f"{sys.version_info.minor}.{sys.version_info.micro}", + "install_python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", "install_location": str(venv_path), } with open(self.output_path, "w") as f: @@ -416,8 +398,7 @@ def main(): parser.add_argument( "-v", "--version", - help="Specify the version to be installed, " - "or HEAD to install from the main branch", + help="Specify the version to be installed, or HEAD to install from the main branch", default=os.getenv("PDM_VERSION"), ) parser.add_argument( @@ -451,9 +432,7 @@ def main(): help="Do not add binary to the PATH.", default=os.getenv("PDM_SKIP_ADD_TO_PATH"), ) - parser.add_argument( - "-o", "--output", help="Output file to write the installation info to" - ) + parser.add_argument("-o", "--output", help="Output file to write the installation info to") options = parser.parse_args() installer = Installer( diff --git a/news/1715.misc.md b/news/1715.misc.md new file mode 100644 index 0000000000..9a39f52026 --- /dev/null +++ b/news/1715.misc.md @@ -0,0 +1 @@ +Use `ruff` as the linter. diff --git a/pyproject.toml b/pyproject.toml index 7bea0c2d85..9c2a6ac1c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,22 +106,33 @@ workflow = [ ] [tool.black] -line-length = 88 -exclude = ''' -/( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - | tests/fixtures -)/ -''' +line-length = 120 +target-version = ["py37", "py38", "py39", "py310"] + + +[tool.ruff] +line-length = 120 +select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "PGH", # pygrep-hooks + "RUF", # ruff + "W", # pycodestyle + "W", # pycodestyle + "YTT", # flake8-2020 +] +extend-ignore = ["B019"] +src = ["src"] +extend-exclude = ["tests/fixtures"] +target-version = "py37" + +[tool.ruff.mccabe] +max-complexity = 10 + +[tool.ruff.isort] +known-first-party = ["pdm"] [tool.towncrier] package = "pdm" @@ -171,22 +182,6 @@ underlines = "-~^" requires = ["pdm-pep517>=1.0"] build-backend = "pdm.pep517.api" -[tool.isort] -profile = "black" -atomic = true -skip_glob = ["*/setup.py"] -filter_files = true -known_first_party = ["pdm"] -known_third_party = [ - "platformdirs", - "packaging", - "pytest", - "findpython", - "tomlkit", - "unearth", - "requests", -] - [tool.pytest.ini_options] filterwarnings = [ "ignore::DeprecationWarning" diff --git a/setup.cfg b/setup.cfg index 34a971fa7e..f70ca2c3f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,20 +1,3 @@ -[flake8] -extend-exclude = - tests/fixtures/*, - env, - dist, - build, - __pypackages__, - temp_script.py -max_complexity = 22 -max_line_length = 88 -ignore = - B019 - B020 - B028 - E203 - W503 - [codespell] ignore-words-list = ba,overriden,te diff --git a/src/pdm/_types.py b/src/pdm/_types.py index e8ed27a40b..0d9a5b9fc6 100644 --- a/src/pdm/_types.py +++ b/src/pdm/_types.py @@ -9,7 +9,7 @@ class Source(TypedDict, total=False): url: str verify_ssl: bool name: str - type: Literal["index", "find_links"] + type: Literal["index", "find_links"] # noqa: F821 username: str password: str diff --git a/src/pdm/builders/base.py b/src/pdm/builders/base.py index 8e580ffc26..e72aa8357c 100644 --- a/src/pdm/builders/base.py +++ b/src/pdm/builders/base.py @@ -114,9 +114,7 @@ def __init__(self, executable: str, shared: str, overlay: str) -> None: self.bin_dirs: list[str] = [] self.lib_dirs: list[str] = [] for path in (overlay, shared): - paths = get_sys_config_paths( - executable, vars={"base": path, "platbase": path}, kind="prefix" - ) + paths = get_sys_config_paths(executable, vars={"base": path, "platbase": path}, kind="prefix") self.bin_dirs.append(paths["scripts"]) self.lib_dirs.extend([paths["platlib"], paths["purelib"]]) self.site_dir = os.path.join(overlay, "site") @@ -176,9 +174,7 @@ def get_shared_env(cls, key: int) -> str: @classmethod def get_overlay_env(cls, key: str) -> str: if key not in cls._overlay_envs: - cls._overlay_envs[key] = create_tracked_tempdir( - "-overlay", "pdm-build-env-" - ) + cls._overlay_envs[key] = create_tracked_tempdir("-overlay", "pdm-build-env-") return cls._overlay_envs[key] def __init__(self, src_dir: str | Path, environment: Environment) -> None: @@ -240,7 +236,7 @@ def _env_vars(self) -> dict[str, str]: ) else: project_libs = self._env.get_paths()["purelib"] - pythonpath = self._prefix.lib_dirs + [project_libs] + pythonpath = [*self._prefix.lib_dirs, project_libs] if "PYTHONPATH" in os.environ: pythonpath.append(os.getenv("PYTHONPATH", "")) env.update( @@ -304,9 +300,7 @@ def install(self, requirements: Iterable[str], shared: bool = False) -> None: if key not in self._shared_envs: self._shared_envs[key] = path - def prepare_metadata( - self, out_dir: str, config_settings: Mapping[str, Any] | None = None - ) -> str: + def prepare_metadata(self, out_dir: str, config_settings: Mapping[str, Any] | None = None) -> str: """Prepare metadata and store in the out_dir. Some backends doesn't provide that API, in that case the metadata will be retrieved from the built result. diff --git a/src/pdm/builders/editable.py b/src/pdm/builders/editable.py index 1ea84fe5fb..a53662ecd9 100644 --- a/src/pdm/builders/editable.py +++ b/src/pdm/builders/editable.py @@ -21,22 +21,16 @@ class EditableBuilder(EnvBuilder): def __init__(self, src_dir: str | Path, environment: Environment) -> None: super().__init__(src_dir, environment) - if self._hook.build_backend.startswith( - "pdm.pep517" - ) and environment.interpreter.version_tuple < (3, 6): + if self._hook.build_backend.startswith("pdm.pep517") and environment.interpreter.version_tuple < (3, 6): # pdm.pep517 backend is not available on Python 2, use the fallback backend self.init_build_system(self.FALLBACK_BACKEND) - def prepare_metadata( - self, out_dir: str, config_settings: Mapping[str, Any] | None = None - ) -> str: + def prepare_metadata(self, out_dir: str, config_settings: Mapping[str, Any] | None = None) -> str: self.install(self._requires, shared=True) try: requires = self._hook.get_requires_for_build_editable(config_settings) self.install(requires) - filename = self._hook.prepare_metadata_for_build_editable( - out_dir, config_settings - ) + filename = self._hook.prepare_metadata_for_build_editable(out_dir, config_settings) except HookMissing: self.init_build_system(self.FALLBACK_BACKEND) return self.prepare_metadata(out_dir, config_settings) @@ -52,14 +46,9 @@ def build( try: requires = self._hook.get_requires_for_build_editable(config_settings) self.install(requires) - filename = self._hook.build_editable( - out_dir, config_settings, metadata_directory - ) + filename = self._hook.build_editable(out_dir, config_settings, metadata_directory) except HookMissing: - logger.warning( - "The build backend doesn't support PEP 660, falling back to " - "setuptools-pep660" - ) + logger.warning("The build backend doesn't support PEP 660, falling back to setuptools-pep660") self.init_build_system(self.FALLBACK_BACKEND) return self.build(out_dir, config_settings, metadata_directory) return os.path.join(out_dir, filename) diff --git a/src/pdm/builders/wheel.py b/src/pdm/builders/wheel.py index 09a35cacde..6e4209dfbd 100644 --- a/src/pdm/builders/wheel.py +++ b/src/pdm/builders/wheel.py @@ -9,9 +9,7 @@ class WheelBuilder(EnvBuilder): """Build wheel in isolated env with managed Python.""" - def prepare_metadata( - self, out_dir: str, config_settings: Mapping[str, Any] | None = None - ) -> str: + def prepare_metadata(self, out_dir: str, config_settings: Mapping[str, Any] | None = None) -> str: self.install(self._requires, shared=True) requires = self._hook.get_requires_for_build_wheel(config_settings) self.install(requires) diff --git a/src/pdm/cli/actions.py b/src/pdm/cli/actions.py index 3b2ce83e67..bbcdc56ec8 100644 --- a/src/pdm/cli/actions.py +++ b/src/pdm/cli/actions.py @@ -80,9 +80,7 @@ def do_lock( # TODO: multiple dependency definitions for the same package. provider = project.get_provider(strategy, tracked_names) if not requirements: - requirements = [ - r for deps in project.all_dependencies.values() for r in deps.values() - ] + requirements = [r for deps in project.all_dependencies.values() for r in deps.values()] resolve_max_rounds = int(project.config["strategy.resolve_max_rounds"]) ui = project.core.ui with ui.logging("lock"): @@ -124,15 +122,11 @@ def do_lock( return mapping -def resolve_candidates_from_lockfile( - project: Project, requirements: Iterable[Requirement] -) -> dict[str, Candidate]: +def resolve_candidates_from_lockfile(project: Project, requirements: Iterable[Requirement]) -> dict[str, Candidate]: ui = project.core.ui resolve_max_rounds = int(project.config["strategy.resolve_max_rounds"]) reqs = [ - req - for req in requirements - if not req.marker or req.marker.evaluate(project.environment.marker_environment) + req for req in requirements if not req.marker or req.marker.evaluate(project.environment.marker_environment) ] with ui.logging("install-resolve"): with ui.open_spinner("Resolving packages from lockfile...") as spinner: @@ -199,9 +193,7 @@ def do_sync( requirements.extend(project.get_dependencies(group).values()) candidates = resolve_candidates_from_lockfile(project, requirements) if tracked_names and dry_run: - candidates = { - name: c for name, c in candidates.items() if name in tracked_names - } + candidates = {name: c for name, c in candidates.items() if name in tracked_names} handler = project.core.synchronizer_class( candidates, project.environment, @@ -245,18 +237,10 @@ def do_add( group = "dev" if dev else "default" tracked_names: set[str] = set() requirements: dict[str, Requirement] = {} - if ( - group == "default" - or not dev - and group not in project.pyproject.settings.get("dev-dependencies", {}) - ): + if group == "default" or not dev and group not in project.pyproject.settings.get("dev-dependencies", {}): if editables: - raise PdmUsageError( - "Cannot add editables to the default or optional dependency group" - ) - for r in [parse_requirement(line, True) for line in editables] + [ - parse_requirement(line) for line in packages - ]: + raise PdmUsageError("Cannot add editables to the default or optional dependency group") + for r in [parse_requirement(line, True) for line in editables] + [parse_requirement(line) for line in packages]: if project.name and normalize_name(project.name) == r.key and not r.extras: project.core.ui.echo( f"Package [req]{project.name}[/] is the project itself.", @@ -265,7 +249,7 @@ def do_add( ) continue if r.is_file_or_url: - r.relocate(project.backend) # type: ignore + r.relocate(project.backend) # type: ignore[attr-defined] key = r.identify() r.prerelease = prerelease tracked_names.add(key) @@ -274,8 +258,7 @@ def do_add( return project.core.ui.echo( f"Adding packages to [primary]{group}[/] " - f"{'dev-' if dev else ''}dependencies: " - + ", ".join(f"[req]{r.as_line()}[/]" for r in requirements.values()) + f"{'dev-' if dev else ''}dependencies: " + ", ".join(f"[req]{r.as_line()}[/]" for r in requirements.values()) ) all_dependencies = project.all_dependencies group_deps = all_dependencies.setdefault(group, {}) @@ -285,9 +268,7 @@ def do_add( group_deps.update(requirements) reqs = [r for deps in all_dependencies.values() for r in deps.values()] with hooks.skipping("post_lock"): - resolved = do_lock( - project, strategy, tracked_names, reqs, dry_run=dry_run, hooks=hooks - ) + resolved = do_lock(project, strategy, tracked_names, reqs, dry_run=dry_run, hooks=hooks) # Update dependency specifiers and lockfile hash. deps_to_update = group_deps if unconstrained else requirements @@ -340,10 +321,7 @@ def do_update( hooks = hooks or HookManager(project) check_project_file(project) if len(packages) > 0 and (top or len(groups) > 1 or not default): - raise PdmUsageError( - "packages argument can't be used together with multiple -G or " - "--no-default and --top." - ) + raise PdmUsageError("packages argument can't be used together with multiple -G or --no-default and --top.") all_dependencies = project.all_dependencies updated_deps: dict[str, dict[str, Requirement]] = defaultdict(dict) install_dev = True if dev is None else dev @@ -358,25 +336,18 @@ def do_update( dependencies = all_dependencies[group] for name in packages: matched_name = next( - ( - k - for k in dependencies - if normalize_name(strip_extras(k)[0]) == normalize_name(name) - ), + (k for k in dependencies if normalize_name(strip_extras(k)[0]) == normalize_name(name)), None, ) if not matched_name: raise ProjectError( - f"[req]{name}[/] does not exist in [primary]{group}[/] " - f"{'dev-' if dev else ''}dependencies." + f"[req]{name}[/] does not exist in [primary]{group}[/] {'dev-' if dev else ''}dependencies." ) dependencies[matched_name].prerelease = prerelease updated_deps[group][matched_name] = dependencies[matched_name] project.core.ui.echo( "Updating packages: {}.".format( - ", ".join( - f"[req]{v}[/]" for v in chain.from_iterable(updated_deps.values()) - ) + ", ".join(f"[req]{v}[/]" for v in chain.from_iterable(updated_deps.values())) ) ) if unconstrained: @@ -409,9 +380,7 @@ def do_update( clean=False, dry_run=dry_run, requirements=[r for deps in updated_deps.values() for r in deps.values()], - tracked_names=list(chain.from_iterable(updated_deps.values())) - if top - else None, + tracked_names=list(chain.from_iterable(updated_deps.values())) if top else None, no_editable=no_editable, no_self=no_self, hooks=hooks, @@ -442,20 +411,14 @@ def do_remove( deps, _ = project.get_pyproject_dependencies(group, dev) project.core.ui.echo( f"Removing packages from [primary]{group}[/] " - f"{'dev-' if dev else ''}dependencies: " - + ", ".join(f"[req]{name}[/]" for name in packages) + f"{'dev-' if dev else ''}dependencies: " + ", ".join(f"[req]{name}[/]" for name in packages) ) with cd(project.root): for name in packages: req = parse_requirement(name) - matched_indexes = sorted( - (i for i, r in enumerate(deps) if req.matches(r)), reverse=True - ) + matched_indexes = sorted((i for i, r in enumerate(deps) if req.matches(r)), reverse=True) if not matched_indexes: - raise ProjectError( - f"[req]{name}[/] does not exist in " - f"[primary]{group}[/] dependencies." - ) + raise ProjectError(f"[req]{name}[/] does not exist in [primary]{group}[/] dependencies.") for i in matched_indexes: del deps[i] cast(Array, deps).multiline(True) @@ -506,16 +469,12 @@ def do_build( with project.core.ui.logging("build"): if sdist: project.core.ui.echo("Building sdist...") - loc = SdistBuilder(project.root, project.environment).build( - dest, config_settings - ) + loc = SdistBuilder(project.root, project.environment).build(dest, config_settings) project.core.ui.echo(f"Built sdist at {loc}") artifacts.append(loc) if wheel: project.core.ui.echo("Building wheel...") - loc = WheelBuilder(project.root, project.environment).build( - dest, config_settings - ) + loc = WheelBuilder(project.root, project.environment).build(dest, config_settings) project.core.ui.echo(f"Built wheel at {loc}") artifacts.append(loc) hooks.try_emit("post_build", artifacts=artifacts, config_settings=config_settings) @@ -548,13 +507,13 @@ def do_init( if build_backend is not None: data["build-system"] = build_backend.build_system() if python_requires and python_requires != "*": - data["project"]["requires-python"] = python_requires # type: ignore + data["project"]["requires-python"] = python_requires if name and version: readme = next(project.root.glob("README*"), None) if readme is None: readme = project.root.joinpath("README.md") readme.write_text(f"# {name}\n\n{description}\n") - data["project"]["readme"] = readme.name # type: ignore + data["project"]["readme"] = readme.name get_specifier(python_requires) project.pyproject._data.update(data) project.pyproject.write() @@ -580,15 +539,12 @@ def do_use( def version_matcher(py_version: PythonInfo) -> bool: return py_version.valid and ( - ignore_requires_python - or project.python_requires.contains(str(py_version.version), True) + ignore_requires_python or project.python_requires.contains(str(py_version.version), True) ) if not project.cache_dir.exists(): project.cache_dir.mkdir(parents=True) - use_cache: JSONFileCache[str, str] = JSONFileCache( - project.cache_dir / "use_cache.json" - ) + use_cache: JSONFileCache[str, str] = JSONFileCache(project.cache_dir / "use_cache.json") selected_python: PythonInfo | None = None if python and not ignore_remembered: if python in use_cache: @@ -612,27 +568,21 @@ def version_matcher(py_version: PythonInfo) -> bool: found_interpreters = list(dict.fromkeys(project.find_interpreters(python))) matching_interpreters = list(filter(version_matcher, found_interpreters)) if not found_interpreters: - raise NoPythonVersion( - f"No Python interpreter matching [success]{python}[/] is found." - ) + raise NoPythonVersion(f"No Python interpreter matching [success]{python}[/] is found.") if not matching_interpreters: project.core.ui.echo("Interpreters found but not matching:", err=True) for py in found_interpreters: info = py.identifier if py.valid else "Invalid" project.core.ui.echo(f" - {py.path} ({info})", err=True) raise NoPythonVersion( - "No python is found meeting the requirement " - f"[success]python {str(project.python_requires)}[/]" + f"No python is found meeting the requirement [success]python {str(project.python_requires)}[/]" ) if first or len(matching_interpreters) == 1: selected_python = matching_interpreters[0] else: project.core.ui.echo("Please enter the Python interpreter to use") for i, py_version in enumerate(matching_interpreters): - project.core.ui.echo( - f"{i}. [success]{str(py_version.path)}[/] " - f"({py_version.identifier})" - ) + project.core.ui.echo(f"{i}. [success]{str(py_version.path)}[/] ({py_version.identifier})") selection = termui.ask( "Please select", default="0", @@ -646,22 +596,12 @@ def version_matcher(py_version: PythonInfo) -> bool: if not save: return selected_python - old_python = ( - PythonInfo.from_path(project.config["python.path"]) - if "python.path" in project.config - else None - ) + 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"[success]{str(selected_python.path)}[/] " - f"({selected_python.identifier})" + f"Using Python interpreter: [success]{str(selected_python.path)}[/] ({selected_python.identifier})" ) project.python = selected_python - if ( - old_python - and old_python.executable != selected_python.executable - and not project.environment.is_global - ): + if old_python and old_python.executable != selected_python.executable and not project.environment.is_global: project.core.ui.echo("Updating executable scripts...", style="primary") project.environment.update_shebangs(selected_python.executable.as_posix()) hooks.try_emit("post_use", python=selected_python) @@ -687,8 +627,7 @@ def do_import( break else: raise PdmUsageError( - "Can't derive the file format automatically, " - "please specify it via '-f/--format' option." + "Can't derive the file format automatically, please specify it via '-f/--format' option." ) else: key = format @@ -697,26 +636,20 @@ def do_import( project_data, settings = FORMATS[key].convert(project, filename, options) pyproject = project.pyproject._data - if "tool" not in pyproject or "pdm" not in pyproject["tool"]: # type: ignore + if "tool" not in pyproject or "pdm" not in pyproject["tool"]: pyproject.setdefault("tool", {})["pdm"] = tomlkit.table() - if "build" in pyproject["tool"]["pdm"] and isinstance( - pyproject["tool"]["pdm"]["build"], str - ): + if "build" in pyproject["tool"]["pdm"] and isinstance(pyproject["tool"]["pdm"]["build"], str): pyproject["tool"]["pdm"]["build"] = { "setup-script": pyproject["tool"]["pdm"]["build"], "run-setuptools": True, } if "project" not in pyproject: - pyproject.add("project", tomlkit.table()) # type: ignore - pyproject["project"].add( # type: ignore - tomlkit.comment("PEP 621 project metadata") - ) - pyproject["project"].add( # type: ignore - tomlkit.comment("See https://www.python.org/dev/peps/pep-0621/") - ) + pyproject.add("project", tomlkit.table()) + pyproject["project"].add(tomlkit.comment("PEP 621 project metadata")) + pyproject["project"].add(tomlkit.comment("See https://www.python.org/dev/peps/pep-0621/")) - merge_dictionary(pyproject["project"], project_data) # type: ignore - merge_dictionary(pyproject["tool"]["pdm"], settings) # type: ignore + merge_dictionary(pyproject["project"], project_data) + merge_dictionary(pyproject["tool"]["pdm"], settings) pyproject["build-system"] = { "requires": ["pdm-pep517>=1.0.0"], "build-backend": "pdm.pep517.api", @@ -737,14 +670,10 @@ def ask_for_import(project: Project) -> None: importable_files = list(find_importable_files(project)) if not importable_files: return - project.core.ui.echo( - "Found following files from other formats that you may import:", style="primary" - ) + project.core.ui.echo("Found following files from other formats that you may import:", style="primary") for i, (key, filepath) in enumerate(importable_files): project.core.ui.echo(f"{i}. [success]{filepath.as_posix()}[/] ({key})") - project.core.ui.echo( - f"{len(importable_files)}. [warning]don't do anything, I will import later.[/]" - ) + project.core.ui.echo(f"{len(importable_files)}. [warning]don't do anything, I will import later.[/]") choice = termui.ask( "Please select", prompt_type=int, @@ -774,8 +703,7 @@ def print_pep582_command(project: Project, shell: str = "AUTO") -> None: err=True, ) ui.echo( - "The environment variable has been saved, " - "please restart the session to take effect.", + "The environment variable has been saved, please restart the session to take effect.", style="success", ) return @@ -818,16 +746,11 @@ def print_pep582_command(project: Project, shell: str = "AUTO") -> None: """ ).strip() else: - raise PdmUsageError( - f"Unsupported shell: {shell}, please specify another shell " - "via `--pep582 `" - ) + raise PdmUsageError(f"Unsupported shell: {shell}, please specify another shell via `--pep582 `") ui.echo(result) -def get_latest_pdm_version_from_pypi( - project: Project, prereleases: bool = False -) -> str | None: +def get_latest_pdm_version_from_pypi(project: Project, prereleases: bool = False) -> str | None: """Get the latest version of PDM from PyPI.""" environment = BareEnvironment(project) with environment.get_finder([project.default_source]) as finder: @@ -843,10 +766,7 @@ def get_latest_version(project: Project) -> str | None: with contextlib.suppress(OSError): state = json.loads(cache_file.read_text()) current_time = datetime.datetime.utcnow().timestamp() - if ( - state.get("last-check") - and current_time - state["last-check"] < 60 * 60 * 24 * 7 - ): + if state.get("last-check") and current_time - state["last-check"] < 60 * 60 * 24 * 7: return cast(str, state["latest-version"]) try: latest_version = get_latest_pdm_version_from_pypi(project) @@ -867,9 +787,7 @@ def check_update(project: Project) -> None: this_version = project.core.version latest_version = get_latest_version(project) - if latest_version is None or parse_version(this_version) >= parse_version( - latest_version - ): + if latest_version is None or parse_version(this_version) >= parse_version(latest_version): return install_command = "pdm self update" disable_command = "pdm config check_update false" diff --git a/src/pdm/cli/commands/add.py b/src/pdm/cli/commands/add.py index 3ae1bd92a6..cfff4111c1 100644 --- a/src/pdm/cli/commands/add.py +++ b/src/pdm/cli/commands/add.py @@ -21,7 +21,8 @@ class Command(BaseCommand): """Add package(s) to pyproject.toml and install them""" - arguments = BaseCommand.arguments + [ + arguments = [ + *BaseCommand.arguments, lockfile_option, save_strategy_group, update_strategy_group, @@ -41,9 +42,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_true", help="Add packages into dev dependencies", ) - parser.add_argument( - "-G", "--group", help="Specify the target dependency group to add into" - ) + parser.add_argument("-G", "--group", help="Specify the target dependency group to add into") parser.add_argument( "--no-sync", dest="sync", diff --git a/src/pdm/cli/commands/base.py b/src/pdm/cli/commands/base.py index a60327648a..c5961a27fb 100644 --- a/src/pdm/cli/commands/base.py +++ b/src/pdm/cli/commands/base.py @@ -26,9 +26,7 @@ def __init__(self, parser: argparse.ArgumentParser) -> None: self.add_arguments(parser) @classmethod - def register_to( - cls, subparsers: _SubParsersAction, name: str | None = None, **kwargs: Any - ) -> None: + def register_to(cls, subparsers: _SubParsersAction, name: str | None = None, **kwargs: Any) -> None: """Register a subcommand to the subparsers, with an optional name of the subcommand. """ diff --git a/src/pdm/cli/commands/build.py b/src/pdm/cli/commands/build.py index bf04735e30..7df67563a3 100644 --- a/src/pdm/cli/commands/build.py +++ b/src/pdm/cli/commands/build.py @@ -32,9 +32,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_false", help="Don't build wheels", ) - parser.add_argument( - "-d", "--dest", default="dist", help="Target directory to put artifacts" - ) + parser.add_argument("-d", "--dest", default="dist", help="Target directory to put artifacts") parser.add_argument( "--no-clean", dest="clean", diff --git a/src/pdm/cli/commands/cache.py b/src/pdm/cli/commands/cache.py index dd15cebc70..55c28c18fb 100644 --- a/src/pdm/cli/commands/cache.py +++ b/src/pdm/cli/commands/cache.py @@ -111,16 +111,12 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: if not options.type: pass elif options.type not in self.CACHE_TYPES: - raise PdmUsageError( - f"Invalid cache type {options.type}, should one of {self.CACHE_TYPES}" - ) + raise PdmUsageError(f"Invalid cache type {options.type}, should one of {self.CACHE_TYPES}") else: types = (str(options.type),) packages = files = 0 - with project.core.ui.open_spinner( - f"Clearing {options.type or 'all'} caches..." - ): + with project.core.ui.open_spinner(f"Clearing {options.type or 'all'} caches..."): if not options.type: packages, files = 0, self._clear_files(project.cache_dir) else: @@ -159,14 +155,11 @@ class ListCommand(BaseCommand): arguments = [verbose_option] def add_arguments(self, parser: argparse.ArgumentParser) -> None: - parser.add_argument( - "pattern", nargs="?", default="*", help="The pattern to list" - ) + parser.add_argument("pattern", nargs="?", default="*", help="The pattern to list") def handle(self, project: Project, options: argparse.Namespace) -> None: rows = [ - (format_size(file_size(file)), file.name) - for file in find_files(project.cache("wheels"), options.pattern) + (format_size(file_size(file)), file.name) for file in find_files(project.cache("wheels"), options.pattern) ] project.core.ui.display_columns(rows, [">Size", "Filename"]) diff --git a/src/pdm/cli/commands/config.py b/src/pdm/cli/commands/config.py index a8b678c37b..e2de8c4a54 100644 --- a/src/pdm/cli/commands/config.py +++ b/src/pdm/cli/commands/config.py @@ -25,9 +25,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_true", help="Set config in the project's local configuration file", ) - parser.add_argument( - "-d", "--delete", action="store_true", help="Unset a configuration key" - ) + parser.add_argument("-d", "--delete", action="store_true", help="Unset a configuration key") parser.add_argument("key", help="Config key", nargs="?") parser.add_argument("value", help="Config value", nargs="?") @@ -44,8 +42,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: def _get_config(self, project: Project, options: argparse.Namespace) -> None: if options.key in project.project_config.deprecated: # pragma: no cover project.core.ui.echo( - "DEPRECATED: the config has been renamed to " - f"{project.project_config.deprecated[options.key]}", + f"DEPRECATED: the config has been renamed to {project.project_config.deprecated[options.key]}", style="warning", err=True, ) @@ -60,16 +57,13 @@ def _set_config(self, project: Project, options: argparse.Namespace) -> None: config = project.project_config if options.local else project.global_config if options.key in config.deprecated: # pragma: no cover project.core.ui.echo( - "DEPRECATED: the config has been renamed to " - f"{config.deprecated[options.key]}", + f"DEPRECATED: the config has been renamed to {config.deprecated[options.key]}", style="warning", err=True, ) config[options.key] = options.value - def _show_config( - self, config: Mapping[str, Any], supersedes: Mapping[str, Any] - ) -> None: + def _show_config(self, config: Mapping[str, Any], supersedes: Mapping[str, Any]) -> None: assert Config.site is not None for key in sorted(config): deprecated = "" @@ -80,9 +74,7 @@ def _show_config( if canonical_key in supersedes: superseded = True deprecated = f"[error](deprecating: {key})[/]" - elif key not in Config._config_map and not ( - key.startswith("pypi.") or key.startswith(REPOSITORY) - ): + elif key not in Config._config_map and not (key.startswith("pypi.") or key.startswith(REPOSITORY)): continue extra_style = "dim" if superseded else None if canonical_key not in Config._config_map: @@ -104,11 +96,7 @@ def _show_config( repository = dict(config[key][item]) if "url" not in repository and item in DEFAULT_REPOSITORIES: repository["url"] = DEFAULT_REPOSITORIES[item].url - self.ui.echo( - RepositoryConfig( - **repository, config_prefix=f"{key}.{item}" - ) - ) + self.ui.echo(RepositoryConfig(**repository, config_prefix=f"{key}.{item}")) continue config_item = Config._config_map[canonical_key] self.ui.echo( @@ -138,13 +126,10 @@ def _list_config(self, project: Project, options: argparse.Namespace) -> None: f"\nHome configuration ([success]{project.global_config.config_file}[/]):", style="bold", ) - self._show_config( - project.global_config.self_data, project.project_config.self_data - ) + self._show_config(project.global_config.self_data, project.project_config.self_data) self.ui.echo( - "\nProject configuration ([success]" - f"{project.project_config.config_file}[/]):", + f"\nProject configuration ([success]{project.project_config.config_file}[/]):", style="bold", ) self._show_config(project.project_config.self_data, {}) @@ -153,8 +138,7 @@ def _delete_config(self, project: Project, options: argparse.Namespace) -> None: config = project.project_config if options.local else project.global_config if options.key in config.deprecated: # pragma: no cover project.core.ui.echo( - "DEPRECATED: the config has been renamed to " - f"{config.deprecated[options.key]}", + f"DEPRECATED: the config has been renamed to {config.deprecated[options.key]}", style="warning", err=True, ) diff --git a/src/pdm/cli/commands/export.py b/src/pdm/cli/commands/export.py index 3e3fb22d05..6afd408ce8 100644 --- a/src/pdm/cli/commands/export.py +++ b/src/pdm/cli/commands/export.py @@ -68,20 +68,12 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: style="warning", err=True, ) - candidates = resolve_candidates_from_lockfile( - project, requirements.values() - ) + candidates = resolve_candidates_from_lockfile(project, requirements.values()) # Remove candidates with [extras] because the bare candidates are already # included - packages = ( - candidate - for candidate in candidates.values() - if not candidate.req.extras - ) + packages = (candidate for candidate in candidates.values() if not candidate.req.extras) - content = FORMATS[options.format].export( - project, packages, options - ) # type: ignore + content = FORMATS[options.format].export(project, packages, options) if options.output: Path(options.output).write_text(content) else: diff --git a/src/pdm/cli/commands/import_cmd.py b/src/pdm/cli/commands/import_cmd.py index f966f52145..a922f48273 100644 --- a/src/pdm/cli/commands/import_cmd.py +++ b/src/pdm/cli/commands/import_cmd.py @@ -19,9 +19,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_true", help="import packages into dev dependencies", ) - parser.add_argument( - "-G", "--group", help="Specify the target dependency group to import into" - ) + parser.add_argument("-G", "--group", help="Specify the target dependency group to import into") parser.add_argument( "-f", "--format", diff --git a/src/pdm/cli/commands/info.py b/src/pdm/cli/commands/info.py index 91e3d33c99..5cedeadbea 100644 --- a/src/pdm/cli/commands/info.py +++ b/src/pdm/cli/commands/info.py @@ -12,21 +12,15 @@ class Command(BaseCommand): def add_arguments(self, parser: argparse.ArgumentParser) -> None: group = ArgumentGroup("fields", is_mutually_exclusive=True) - group.add_argument( - "--python", action="store_true", help="Show the interpreter path" - ) + group.add_argument("--python", action="store_true", help="Show the interpreter path") group.add_argument( "--where", dest="where", action="store_true", help="Show the project root path", ) - group.add_argument( - "--packages", action="store_true", help="Show the packages root" - ) - group.add_argument( - "--env", action="store_true", help="Show PEP 508 environment markers" - ) + group.add_argument("--packages", action="store_true", help="Show the packages root") + group.add_argument("--env", action="store_true", help="Show PEP 508 environment markers") group.add_to_parser(parser) def handle(self, project: Project, options: argparse.Namespace) -> None: @@ -39,9 +33,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: elif options.packages: project.core.ui.echo(str(project.environment.packages_path)) elif options.env: - project.core.ui.echo( - json.dumps(project.environment.marker_environment, indent=2) - ) + project.core.ui.echo(json.dumps(project.environment.marker_environment, indent=2)) else: for name, value in zip( [ diff --git a/src/pdm/cli/commands/init.py b/src/pdm/cli/commands/init.py index 22c86a53b7..2e736cb320 100644 --- a/src/pdm/cli/commands/init.py +++ b/src/pdm/cli/commands/init.py @@ -37,9 +37,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: help="Don't ask questions but use default values", ) parser.add_argument("--python", help="Specify the Python version/path to use") - parser.add_argument( - "--backend", choices=list(_BACKENDS), help="Specify the build backend" - ) + parser.add_argument("--backend", choices=list(_BACKENDS), help="Specify the build backend") parser.set_defaults(search_parent=False) def handle(self, project: Project, options: argparse.Namespace) -> None: @@ -47,13 +45,9 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: hooks = HookManager(project, options.skip) if project.pyproject.exists: - project.core.ui.echo( - "pyproject.toml already exists, update it now.", style="primary" - ) + project.core.ui.echo("pyproject.toml already exists, update it now.", style="primary") else: - project.core.ui.echo( - "Creating a pyproject.toml for PDM...", style="primary" - ) + project.core.ui.echo("Creating a pyproject.toml for PDM...", style="primary") self.set_interactive(not options.non_interactive) if self.interactive: @@ -65,24 +59,17 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: ignore_requires_python=True, hooks=hooks, ) - if ( - project.config["python.use_venv"] - and get_venv_like_prefix(python.executable) is None - ): + if project.config["python.use_venv"] and get_venv_like_prefix(python.executable) is None: if termui.confirm( - "Would you like to create a virtualenv with " - f"[success]{python.executable}[/]?", + f"Would you like to create a virtualenv with [success]{python.executable}[/]?", default=True, ): try: path = project._create_virtualenv() - python = 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" - "Please fix it and create later.", + f"Error occurred when creating virtualenv: {e}\nPlease fix it and create later.", style="error", err=True, ) @@ -141,9 +128,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: author = self.ask("Author name", git_user) email = self.ask("Author email", git_email) python_version = f"{python.major}.{python.minor}" - python_requires = self.ask( - "Python requires('*' to allow any)", f">={python_version}" - ) + python_requires = self.ask("Python requires('*' to allow any)", f">={python_version}") actions.do_init( project, diff --git a/src/pdm/cli/commands/install.py b/src/pdm/cli/commands/install.py index cec13039ea..b685d1a945 100644 --- a/src/pdm/cli/commands/install.py +++ b/src/pdm/cli/commands/install.py @@ -52,12 +52,8 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: ) sys.exit(1) if options.lock: - project.core.ui.echo( - "Updating the lock file...", style="success", err=True - ) - actions.do_lock( - project, strategy=strategy, dry_run=options.dry_run, hooks=hooks - ) + project.core.ui.echo("Updating the lock file...", style="success", err=True) + actions.do_lock(project, strategy=strategy, dry_run=options.dry_run, hooks=hooks) actions.do_sync( project, diff --git a/src/pdm/cli/commands/list.py b/src/pdm/cli/commands/list.py index 1fd7e7fa53..84dd02e7a5 100644 --- a/src/pdm/cli/commands/list.py +++ b/src/pdm/cli/commands/list.py @@ -40,20 +40,15 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: help="Show the installed dependencies in pip's requirements.txt format", ) - graph.add_argument( - "--graph", action="store_true", help="Display a graph of dependencies" - ) + graph.add_argument("--graph", action="store_true", help="Display a graph of dependencies") - parser.add_argument( - "-r", "--reverse", action="store_true", help="Reverse the dependency graph" - ) + parser.add_argument("-r", "--reverse", action="store_true", help="Reverse the dependency graph") parser.add_argument( "--resolve", action="store_true", default=False, - help="Resolve all requirements to output licenses " - "(instead of just showing those currently installed)", + help="Resolve all requirements to output licenses (instead of just showing those currently installed)", ) parser.add_argument( @@ -87,15 +82,13 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: list_formats.add_argument( "--markdown", action="store_true", - help="Output dependencies and legal notices in markdown " - "document format - best effort basis", + help="Output dependencies and legal notices in markdown document format - best effort basis", ) parser.add_argument( "--include", default="", - help="Dependency groups to include in the output. By default " - "all are included", + help="Dependency groups to include in the output. By default all are included", ) parser.add_argument( @@ -124,25 +117,13 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: # Set up `--include` and `--exclude` dep groups. # Include everything by default (*) then exclude after. # Check to make sure that only valid dep group names are given. - valid_groups = set([g for g in project.iter_groups()] + [SUBDEP_GROUP_LABEL]) - include = set( - parse_comma_separated_string( - options.include, lowercase=False, asterisk_values=valid_groups - ) - ) + valid_groups = {*list(project.iter_groups()), SUBDEP_GROUP_LABEL} + include = set(parse_comma_separated_string(options.include, lowercase=False, asterisk_values=valid_groups)) if not all(g in valid_groups for g in include): - raise PdmUsageError( - f"--include groups must be selected from: {valid_groups}" - ) - exclude = set( - parse_comma_separated_string( - options.exclude, lowercase=False, asterisk_values=valid_groups - ) - ) + raise PdmUsageError(f"--include groups must be selected from: {valid_groups}") + exclude = set(parse_comma_separated_string(options.exclude, lowercase=False, asterisk_values=valid_groups)) if exclude and not all(g in valid_groups for g in exclude): - raise PdmUsageError( - f"--exclude groups must be selected from: {valid_groups}" - ) + raise PdmUsageError(f"--exclude groups must be selected from: {valid_groups}") # Include selects only certain groups when set, but always selects :sub # unless it is explicitly unset. @@ -153,16 +134,12 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: # Requirements as importtools distributions (eg packages). # Resolve all the requirements. Map the candidates to distributions. requirements = [ - r - for g in selected_groups - if g != SUBDEP_GROUP_LABEL - for r in project.get_dependencies(g).values() + r for g in selected_groups if g != SUBDEP_GROUP_LABEL for r in project.get_dependencies(g).values() ] if options.resolve: candidates = actions.resolve_candidates_from_lockfile(project, requirements) packages: Mapping[str, im.Distribution] = { - k: c.prepare(project.environment).metadata - for k, c in candidates.items() + k: c.prepare(project.environment).metadata for k, c in candidates.items() } # Use requirements from the working set (currently installed). @@ -225,9 +202,7 @@ def handle_graph( if options.sort: raise PdmUsageError("--sort cannot be used with --graph") - show_dependency_graph( - project, dep_graph, reverse=options.reverse, json=options.json - ) + show_dependency_graph(project, dep_graph, reverse=options.reverse, json=options.json) def handle_list( self, @@ -240,13 +215,9 @@ def handle_list( raise PdmUsageError("--reverse cannot be used without --graph") # Check the fields are specified OK. - fields = parse_comma_separated_string( - options.fields, asterisk_values=Listable.KEYS - ) + fields = parse_comma_separated_string(options.fields, asterisk_values=Listable.KEYS) if not all(field in Listable.KEYS for field in fields): - raise PdmUsageError( - f"--fields must specify one or more of: {Listable.KEYS}" - ) + raise PdmUsageError(f"--fields must specify one or more of: {Listable.KEYS}") # Wrap each distribution with a Listable (and a groups pairing) # to make it easier to filter on later. @@ -260,9 +231,7 @@ def _group_of(name: str) -> set[str]: if options.sort: keys = parse_comma_separated_string(options.sort) if not all(key in Listable.KEYS for key in keys): - raise PdmUsageError( - f"--sort key must be one of: {','.join(Listable.KEYS)}" - ) + raise PdmUsageError(f"--sort key must be one of: {','.join(Listable.KEYS)}") records.sort(key=lambda d: tuple(d[key] for key in keys)) # Write CSV @@ -293,12 +262,8 @@ def _group_of(name: str) -> set[str]: err=True, style="error", ) - ui.echo( - text_body.encode().decode("ascii", errors="ignore"), highlight=True - ) - ui.echo( - "**Problem decoding file as UTF-8. Some characters may be omit.**" - ) + ui.echo(text_body.encode().decode("ascii", errors="ignore"), highlight=True) + ui.echo("**Problem decoding file as UTF-8. Some characters may be omit.**") # Write nice table format. else: @@ -352,11 +317,7 @@ def __init__(self, dist: im.Distribution, groups: set[str]): self.licenses: str | None = dist.metadata["License"] self.licenses = None if self.licenses == "UNKNOWN" else self.licenses if not self.licenses: - classifier_licenses = [ - v - for v in dist.metadata.get_all("Classifier", []) # type: ignore - if v.startswith("License") - ] + classifier_licenses = [v for v in dist.metadata.get_all("Classifier", []) if v.startswith("License")] alternatives = [parts.split("::") for parts in classifier_licenses] alternatives = [part[-1].strip() for part in alternatives if part] self.licenses = "|".join(alternatives) diff --git a/src/pdm/cli/commands/lock.py b/src/pdm/cli/commands/lock.py index 9f283a94a6..f2a9d5d102 100644 --- a/src/pdm/cli/commands/lock.py +++ b/src/pdm/cli/commands/lock.py @@ -12,11 +12,7 @@ class Command(BaseCommand): """Resolve and lock dependencies""" - arguments = BaseCommand.arguments + [ - lockfile_option, - no_isolation_option, - skip_option, - ] + arguments = [*BaseCommand.arguments, lockfile_option, no_isolation_option, skip_option] def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( @@ -43,8 +39,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: sys.exit(1) else: project.core.ui.echo( - f"[success]{termui.Emoji.SUCC}[/] Lockfile is " - "[success]up to date[/].", + f"[success]{termui.Emoji.SUCC}[/] Lockfile is [success]up to date[/].", err=True, verbosity=termui.Verbosity.DETAIL, ) diff --git a/src/pdm/cli/commands/publish/__init__.py b/src/pdm/cli/commands/publish/__init__.py index 9385834715..4503f3bffa 100644 --- a/src/pdm/cli/commands/publish/__init__.py +++ b/src/pdm/cli/commands/publish/__init__.py @@ -31,20 +31,17 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "-r", "--repository", - help="The repository name or url to publish the package to" - " [env var: PDM_PUBLISH_REPO]", + help="The repository name or url to publish the package to [env var: PDM_PUBLISH_REPO]", ) parser.add_argument( "-u", "--username", - help="The username to access the repository" - " [env var: PDM_PUBLISH_USERNAME]", + help="The username to access the repository [env var: PDM_PUBLISH_USERNAME]", ) parser.add_argument( "-P", "--password", - help="The password to access the repository" - " [env var: PDM_PUBLISH_PASSWORD]", + help="The password to access the repository [env var: PDM_PUBLISH_PASSWORD]", ) parser.add_argument( "-S", @@ -76,9 +73,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: ) @staticmethod - def _make_package( - filename: str, signatures: dict[str, str], options: argparse.Namespace - ) -> PackageFile: + def _make_package(filename: str, signatures: dict[str, str], options: argparse.Namespace) -> PackageFile: p = PackageFile.from_filename(filename, options.comment) if p.base_filename in signatures: p.add_gpg_signature(signatures[p.base_filename], p.base_filename + ".asc") @@ -96,10 +91,7 @@ def _check_response(response: requests.Response) -> None: "(or https://test.pypi.org/legacy/) instead." ) elif response.status_code == 405 and "pypi.org" in response.url: - message = ( - "It appears you're trying to upload to pypi.org but have an " - "invalid URL." - ) + message = "It appears you're trying to upload to pypi.org but have an invalid URL." else: try: response.raise_for_status() @@ -124,9 +116,7 @@ def get_repository(project: Project, options: argparse.Namespace) -> Repository: config.password = password if ca_certs is not None: config.ca_certs = ca_certs - return Repository( - project, config.url, config.username, config.password, config.ca_certs - ) + return Repository(project, config.url, config.username, config.password, config.ca_certs) def handle(self, project: Project, options: argparse.Namespace) -> None: hooks = HookManager(project, options.skip) @@ -136,16 +126,8 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: if options.build: actions.do_build(project, hooks=hooks) - package_files = [ - str(p) - for p in project.root.joinpath("dist").iterdir() - if not p.name.endswith(".asc") - ] - signatures = { - p.stem: str(p) - for p in project.root.joinpath("dist").iterdir() - if p.name.endswith(".asc") - } + package_files = [str(p) for p in project.root.joinpath("dist").iterdir() if not p.name.endswith(".asc")] + signatures = {p.stem: str(p) for p in project.root.joinpath("dist").iterdir() if p.name.endswith(".asc")} repository = self.get_repository(project, options) uploaded: list[PackageFile] = [] @@ -168,9 +150,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: ) for package in packages: resp = repository.upload(package, progress) - logger.debug( - "Response from %s:\n%s %s", resp.url, resp.status_code, resp.reason - ) + logger.debug("Response from %s:\n%s %s", resp.url, resp.status_code, resp.reason) self._check_response(resp) uploaded.append(package) diff --git a/src/pdm/cli/commands/publish/package.py b/src/pdm/cli/commands/publish/package.py index e5ad2c5a56..65ba0a156d 100644 --- a/src/pdm/cli/commands/publish/package.py +++ b/src/pdm/cli/commands/publish/package.py @@ -34,9 +34,7 @@ def parse_metadata(fp: IO[bytes]) -> email.message.Message: - return email.message_from_file( - io.TextIOWrapper(fp, encoding="utf-8", errors="surrogateescape") - ) + return email.message_from_file(io.TextIOWrapper(fp, encoding="utf-8", errors="surrogateescape")) @dataclass @@ -164,7 +162,7 @@ def _run_gpg(gpg_args: list[str]) -> None: "'gpg' or 'gpg2' executables not available.\n" "Try installing one of these or specifying an executable " "with the --sign-with flag." - ) + ) from None @property def metadata_dict(self) -> dict[str, Any]: diff --git a/src/pdm/cli/commands/publish/repository.py b/src/pdm/cli/commands/publish/repository.py index b2e2dfbd34..9b9e8d90c4 100644 --- a/src/pdm/cli/commands/publish/repository.py +++ b/src/pdm/cli/commands/publish/repository.py @@ -16,9 +16,9 @@ from pdm.project.config import DEFAULT_REPOSITORIES try: - import keyring # type: ignore + import keyring except ImportError: - keyring = None # type: ignore + keyring = None class Repository: @@ -40,26 +40,18 @@ def __init__( weakref.finalize(self, self.session.close) self.ui = project.core.ui - def _ensure_credentials( - self, username: str | None, password: str | None - ) -> tuple[str, str]: + def _ensure_credentials(self, username: str | None, password: str | None) -> tuple[str, str]: netloc = urlparse(self.url).netloc if username and password: return username, password if not termui.is_interactive(): raise PdmUsageError("Username and password are required") username, password, save = self._prompt_for_credentials(netloc, username) - if ( - save - and keyring is not None - and termui.confirm("Save credentials to keyring?") - ): + if save and keyring is not None and termui.confirm("Save credentials to keyring?"): self._credentials_to_save = (netloc, username, password) return username, password - def _prompt_for_credentials( - self, service: str, username: str | None - ) -> tuple[str, str, bool]: + def _prompt_for_credentials(self, service: str, username: str | None) -> tuple[str, str, bool]: if keyring is not None: cred = keyring.get_credential(service, username) if cred is not None: @@ -91,14 +83,9 @@ def get_release_urls(self, packages: list[PackageFile]) -> Iterable[str]: base = "https://test.pypi.org/" else: return set() - return { - f"{base}project/{package.metadata['name']}/{package.metadata['version']}/" - for package in packages - } - - def upload( - self, package: PackageFile, progress: rich.progress.Progress - ) -> requests.Response: + return {f"{base}project/{package.metadata['name']}/{package.metadata['version']}/" for package in packages} + + def upload(self, package: PackageFile, progress: rich.progress.Progress) -> requests.Response: payload = package.metadata_dict payload.update( { @@ -111,16 +98,12 @@ def upload( progress.live.console.print(f"Uploading [success]{package.base_filename}") with open(package.filename, "rb") as fp: - field_parts.append( - ("content", (package.base_filename, fp, "application/octet-stream")) - ) + field_parts.append(("content", (package.base_filename, fp, "application/octet-stream"))) def on_upload(monitor: requests_toolbelt.MultipartEncoderMonitor) -> None: progress.update(job, completed=monitor.bytes_read) - monitor = requests_toolbelt.MultipartEncoderMonitor.from_fields( - field_parts, callback=on_upload - ) + monitor = requests_toolbelt.MultipartEncoderMonitor.from_fields(field_parts, callback=on_upload) job = progress.add_task("", total=monitor.len) resp = self.session.post( self.url, diff --git a/src/pdm/cli/commands/remove.py b/src/pdm/cli/commands/remove.py index adf79cd4ae..3f27c8a2aa 100644 --- a/src/pdm/cli/commands/remove.py +++ b/src/pdm/cli/commands/remove.py @@ -22,9 +22,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_true", help="Remove packages from dev dependencies", ) - parser.add_argument( - "-G", "--group", help="Specify the target dependency group to remove from" - ) + parser.add_argument("-G", "--group", help="Specify the target dependency group to remove from") parser.add_argument( "--no-sync", dest="sync", @@ -32,9 +30,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_false", help="Only write pyproject.toml and do not uninstall packages", ) - parser.add_argument( - "packages", nargs="+", help="Specify the packages to remove" - ) + parser.add_argument("packages", nargs="+", help="Specify the packages to remove") def handle(self, project: Project, options: argparse.Namespace) -> None: actions.do_remove( diff --git a/src/pdm/cli/commands/run.py b/src/pdm/cli/commands/run.py index e6879ed4ef..2e6060fa1e 100644 --- a/src/pdm/cli/commands/run.py +++ b/src/pdm/cli/commands/run.py @@ -36,13 +36,7 @@ class TaskOptions(TypedDict, total=False): def exec_opts(*options: TaskOptions | None) -> dict[str, Any]: return dict( env={k: v for opts in options if opts for k, v in opts.get("env", {}).items()}, - **{ - k: v - for opts in options - if opts - for k, v in opts.items() - if k not in ("env", "help") - }, + **{k: v for opts in options if opts for k, v in opts.items() if k not in ("env", "help")}, ) @@ -77,12 +71,8 @@ def short_description(self) -> str: if self.kind == "composite": fallback = f" {termui.Emoji.ARROW_SEPARATOR} ".join(self.args) else: - lines = [ - line.strip() for line in str(self.args).splitlines() if line.strip() - ] - fallback = ( - f"{lines[0]}{termui.Emoji.ELLIPSIS}" if len(lines) > 1 else lines[0] - ) + lines = [line.strip() for line in str(self.args).splitlines() if line.strip()] + fallback = f"{lines[0]}{termui.Emoji.ELLIPSIS}" if len(lines) > 1 else lines[0] return self.options.get("help", fallback) @@ -104,9 +94,7 @@ def __init__(self, project: Project, hooks: HookManager) -> None: def get_task(self, script_name: str) -> Task | None: if script_name not in self.project.scripts: return None - script = cast( - "str | Sequence[str] | Mapping[str,Any]", self.project.scripts[script_name] - ) + script = cast("str | Sequence[str] | Mapping[str,Any]", self.project.scripts[script_name]) if not isinstance(script, Mapping): # Regard as the same as {cmd = ... } kind = "cmd" @@ -120,15 +108,11 @@ def get_task(self, script_name: str) -> Task | None: value = cast("str | Sequence[str]", script.pop(key)) break else: - raise PdmUsageError( - f"Script type must be one of ({', '.join(self.TYPES)})" - ) + raise PdmUsageError(f"Script type must be one of ({', '.join(self.TYPES)})") options = script.copy() unknown_options = set(options) - set(self.OPTIONS) if unknown_options: - raise PdmUsageError( - f"Unknown options for task {script_name}: {', '.join(unknown_options)}" - ) + raise PdmUsageError(f"Unknown options for task {script_name}: {', '.join(unknown_options)}") return Task(kind, script_name, value, cast(TaskOptions, options)) def _run_process( @@ -164,15 +148,11 @@ def _run_process( else: process_env = {**dotenv_env, **process_env} pythonpath = process_env.get("PYTHONPATH", "").split(os.pathsep) - pythonpath = [get_pep582_path(project)] + [ - p for p in pythonpath if "pdm/pep582" not in p.replace("\\", "/") - ] + pythonpath = [get_pep582_path(project)] + [p for p in pythonpath if "pdm/pep582" not in p.replace("\\", "/")] project_env = project.environment this_path = project_env.get_paths()["scripts"] python_root = os.path.dirname(project.python.executable) - new_path = os.pathsep.join( - [this_path, process_env.get("PATH", ""), python_root] - ) + new_path = os.pathsep.join([this_path, process_env.get("PATH", ""), python_root]) process_env.update( { "PYTHONPATH": os.pathsep.join(pythonpath), @@ -193,18 +173,14 @@ def _run_process( assert isinstance(args, Sequence) command, *args = args if command.endswith(".py"): - args = [command] + args + args = [command, *args] command = str(project.python.executable) expanded_command = project_env.which(command) if not expanded_command: - raise PdmUsageError( - f"Command [success]'{command}'[/] is not found in your PATH." - ) + raise PdmUsageError(f"Command [success]'{command}'[/] is not found in your PATH.") expanded_command = os.path.expanduser(os.path.expandvars(expanded_command)) real_command = os.path.realpath(expanded_command) - expanded_args = [ - os.path.expandvars(arg) for arg in [expanded_command] + args - ] + expanded_args = [os.path.expandvars(arg) for arg in [expanded_command, *args]] if ( not project_env.is_global and not site_packages @@ -226,17 +202,13 @@ def forward_signal(signum: int, frame: FrameType | None) -> None: handle_term = signal.signal(signal.SIGTERM, forward_signal) handle_int = signal.signal(signal.SIGINT, forward_signal) - process = subprocess.Popen( - expanded_args, cwd=cwd, env=process_env, shell=shell, bufsize=0 - ) + process = subprocess.Popen(expanded_args, cwd=cwd, env=process_env, shell=shell, bufsize=0) process.wait() signal.signal(signal.SIGTERM, handle_term) signal.signal(signal.SIGINT, handle_int) return process.returncode - def run_task( - self, task: Task, args: Sequence[str] = (), opts: TaskOptions | None = None - ) -> int: + def run_task(self, task: Task, args: Sequence[str] = (), opts: TaskOptions | None = None) -> int: kind, _, value, options = task shell = False if kind == "cmd": @@ -248,8 +220,7 @@ def run_task( interpolated = any(row[1] for row in agg) # In case of multiple default, we need to split the resulting string. parts: Iterator[list[str]] = ( - shlex.split(part) if interpolated else [part] - for part, interpolated in agg + shlex.split(part) if interpolated else [part] for part, interpolated in agg ) # We flatten the nested list to obtain a list of arguments value = list(itertools.chain(*parts)) @@ -263,18 +234,11 @@ def run_task( assert isinstance(value, str) module, _, func = value.partition(":") if not module or not func: - raise PdmUsageError( - "Python callable must be in the form :" - ) + raise PdmUsageError("Python callable must be in the form :") short_name = "_1" if re.search(r"\(.*?\)", func) is None: func += "()" - args = [ - "python", - "-c", - f"import sys, {module} as {short_name};" - f"sys.exit({short_name}.{func})", - ] + list(args) + args = ["python", "-c", f"import sys, {module} as {short_name};sys.exit({short_name}.{func})", *list(args)] elif kind == "composite": assert isinstance(value, list) @@ -285,9 +249,7 @@ def run_task( ) if kind == "composite": args = list(args) - should_interpolate = any( - (RE_ARGS_PLACEHOLDER.search(script) for script in value) - ) + should_interpolate = any(RE_ARGS_PLACEHOLDER.search(script) for script in value) for script in value: if should_interpolate: script, _ = interpolate(script, args) @@ -305,9 +267,7 @@ def run_task( **exec_opts(self.global_options, options, opts), ) - def run( - self, command: str, args: list[str], opts: TaskOptions | None = None - ) -> int: + def run(self, command: str, args: list[str], opts: TaskOptions | None = None) -> int: if command in self.hooks.skip: return 0 task = self.get_task(command) @@ -328,7 +288,7 @@ def run( return code else: return self._run_process( - [command] + args, + [command, *args], **exec_opts(self.global_options, opts), ) diff --git a/src/pdm/cli/commands/search.py b/src/pdm/cli/commands/search.py index 5e855de0a6..77aa7e4cb3 100644 --- a/src/pdm/cli/commands/search.py +++ b/src/pdm/cli/commands/search.py @@ -32,9 +32,7 @@ def print_results( target_width = terminal_width - name_column_width - 5 if target_width > 10: # wrap and indent summary to fit terminal - summary = ("\n" + " " * (name_column_width + 2)).join( - textwrap.wrap(summary, target_width) - ) + summary = ("\n" + " " * (name_column_width + 2)).join(textwrap.wrap(summary, target_width)) current_width = len(name) + len(latest) + 4 spaces = " " * (name_column_width - current_width) line = f"[req]{name}[/] ([warning]{latest}[/]){spaces} - {summary}" diff --git a/src/pdm/cli/commands/self_cmd.py b/src/pdm/cli/commands/self_cmd.py index 25f9239fb9..318ed89a6d 100644 --- a/src/pdm/cli/commands/self_cmd.py +++ b/src/pdm/cli/commands/self_cmd.py @@ -28,9 +28,7 @@ def _get_distributions() -> Iterable[Distribution]: def list_distributions(plugin_only: bool = False) -> list[Distribution]: result: list[Distribution] = [] for dist in _get_distributions(): - if not plugin_only or any( - ep.group in ("pdm", "pdm.plugin") for ep in dist.entry_points - ): + if not plugin_only or any(ep.group in ("pdm", "pdm.plugin") for ep in dist.entry_points): result.append(dist) return sorted(result, key=lambda d: d.metadata["Name"] or "UNKNOWN") @@ -123,25 +121,18 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( "packages", nargs="+", - help="Specify one or many package names, " - "each package can have a version specifier", + help="Specify one or many package names, each package can have a version specifier", ) def handle(self, project: Project, options: argparse.Namespace) -> None: - pip_args = ["install"] + shlex.split(options.pip_args) + options.packages + pip_args = ["install", *shlex.split(options.pip_args), *options.packages] - project.core.ui.echo( - f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL - ) + project.core.ui.echo(f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL) try: - with project.core.ui.open_spinner( - f"Installing packages: {options.packages}" - ): + with project.core.ui.open_spinner(f"Installing packages: {options.packages}"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: - project.core.ui.echo( - "[error]Installation failed:[/]\n" + e.output, err=True - ) + project.core.ui.echo("[error]Installation failed:[/]\n" + e.output, err=True) sys.exit(1) else: project.core.ui.echo("[success]Installation succeeds.[/]") @@ -159,12 +150,8 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: help="Arguments that will be passed to pip uninstall", default="", ) - parser.add_argument( - "-y", "--yes", action="store_true", help="Answer yes on the question" - ) - parser.add_argument( - "packages", nargs="+", help="Specify one or many package names" - ) + parser.add_argument("-y", "--yes", action="store_true", help="Answer yes on the question") + parser.add_argument("packages", nargs="+", help="Specify one or many package names") def _resolve_dependencies_to_remove(self, packages: list[str]) -> list[str]: """Perform a BFS to find all unneeded dependencies""" @@ -198,29 +185,16 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: if not packages_to_remove: project.core.ui.echo("No package to remove.", err=True) sys.exit(1) - if not ( - options.yes - or termui.confirm( - f"Will remove: {packages_to_remove}, continue?", default=True - ) - ): + if not (options.yes or termui.confirm(f"Will remove: {packages_to_remove}, continue?", default=True)): return - pip_args = ( - ["uninstall", "-y"] + shlex.split(options.pip_args) + packages_to_remove - ) + pip_args = ["uninstall", "-y", *shlex.split(options.pip_args), *packages_to_remove] - project.core.ui.echo( - f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL - ) + project.core.ui.echo(f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL) try: - with project.core.ui.open_spinner( - f"Uninstalling packages: [success]{', '.join(options.packages)}[/]" - ): + with project.core.ui.open_spinner(f"Uninstalling packages: [success]{', '.join(options.packages)}[/]"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: - project.core.ui.echo( - "[error]Uninstallation failed:[/]\n" + e.output, err=True - ) + project.core.ui.echo("[error]Uninstallation failed:[/]\n" + e.output, err=True) sys.exit(1) else: project.core.ui.echo("[success]Uninstallation succeeds.[/]") @@ -262,25 +236,18 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: project.core.ui.echo(f"Already up-to-date: [primary]{__version__}[/]") return package = f"pdm=={version}" - pip_args = ["install", "--upgrade"] + shlex.split(options.pip_args) + [package] - project.core.ui.echo( - f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL - ) + pip_args = ["install", "--upgrade", *shlex.split(options.pip_args)] + [package] + project.core.ui.echo(f"Running pip command: {pip_args}", verbosity=termui.Verbosity.DETAIL) try: - with project.core.ui.open_spinner( - f"Updating pdm to version [primary]{version}[/]" - ): + with project.core.ui.open_spinner(f"Updating pdm to version [primary]{version}[/]"): run_pip(project, pip_args) except subprocess.CalledProcessError as e: project.core.ui.echo( - f"[error]Installing version [primary]{version}[/] failed:[/]\n" - + e.output, + f"[error]Installing version [primary]{version}[/] failed:[/]\n" + e.output, err=True, ) sys.exit(1) else: - project.core.ui.echo( - f"[success]Installing version [primary]{version}[/] succeeds.[/]" - ) + project.core.ui.echo(f"[success]Installing version [primary]{version}[/] succeeds.[/]") # Update the version value to avoid check update print wrong message project.core.version = read_version() diff --git a/src/pdm/cli/commands/show.py b/src/pdm/cli/commands/show.py index 52e959669d..f373324cdd 100644 --- a/src/pdm/cli/commands/show.py +++ b/src/pdm/cli/commands/show.py @@ -29,9 +29,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: help="Specify the package name, or show this package if not given", ) for option in self.metadata_keys: - parser.add_argument( - f"--{option}", action="store_true", help=f"Show {option}" - ) + parser.add_argument(f"--{option}", action="store_true", help=f"Show {option}") def handle(self, project: Project, options: argparse.Namespace) -> None: package = options.package @@ -54,9 +52,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: if not project.name: raise PdmUsageError("This project is not a package") package = normalize_name(project.name) - metadata = ( - project.make_self_candidate().prepare(project.environment).metadata - ) + metadata = project.make_self_candidate().prepare(project.environment).metadata latest_stable = None assert metadata project_info = ProjectInfo(metadata) diff --git a/src/pdm/cli/commands/update.py b/src/pdm/cli/commands/update.py index acc86b34b6..08845375ac 100644 --- a/src/pdm/cli/commands/update.py +++ b/src/pdm/cli/commands/update.py @@ -19,7 +19,8 @@ class Command(BaseCommand): """Update package(s) in pyproject.toml""" - arguments = BaseCommand.arguments + [ + arguments = [ + *BaseCommand.arguments, groups_group, install_group, lockfile_option, @@ -51,9 +52,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_false", help="Only update lock file but do not sync packages", ) - parser.add_argument( - "packages", nargs="*", help="If packages are given, only update them" - ) + parser.add_argument("packages", nargs="*", help="If packages are given, only update them") parser.set_defaults(dev=None) def handle(self, project: Project, options: argparse.Namespace) -> None: diff --git a/src/pdm/cli/commands/use.py b/src/pdm/cli/commands/use.py index 0d8c210963..7c7d6ad19f 100644 --- a/src/pdm/cli/commands/use.py +++ b/src/pdm/cli/commands/use.py @@ -24,9 +24,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: action="store_true", help="Ignore the remembered selection", ) - parser.add_argument( - "python", nargs="?", help="Specify the Python version or path", default="" - ) + parser.add_argument("python", nargs="?", help="Specify the Python version or path", default="") def handle(self, project: Project, options: argparse.Namespace) -> None: actions.do_use( diff --git a/src/pdm/cli/commands/venv/activate.py b/src/pdm/cli/commands/venv/activate.py index 79f9d28a08..7c0395c88a 100644 --- a/src/pdm/cli/commands/venv/activate.py +++ b/src/pdm/cli/commands/venv/activate.py @@ -21,9 +21,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: def handle(self, project: Project, options: argparse.Namespace) -> None: if options.env: - venv = next( - (venv for key, venv in iter_venvs(project) if key == options.env), None - ) + venv = next((venv for key, venv in iter_venvs(project) if key == options.env), None) if not venv: project.core.ui.echo( f"No virtualenv with key [success]{options.env}[/] is found", @@ -36,8 +34,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: interpreter = project.project_config.get("python.path") if not interpreter: project.core.ui.echo( - "The project doesn't have a saved python.path. " - "Run [success]pdm use[/] to pick one.", + "The project doesn't have a saved python.path. Run [success]pdm use[/] to pick one.", style="warning", err=True, ) diff --git a/src/pdm/cli/commands/venv/backends.py b/src/pdm/cli/commands/venv/backends.py index 02033c4b45..829e9bc5dc 100644 --- a/src/pdm/cli/commands/venv/backends.py +++ b/src/pdm/cli/commands/venv/backends.py @@ -37,11 +37,7 @@ def _resolved_interpreter(self) -> PythonInfo: if saved_python: return PythonInfo.from_path(saved_python) for py_version in self.project.find_interpreters(self.python): - if ( - self.python - or py_version.valid - and self.project.python_requires.contains(py_version.version, True) - ): + if self.python or py_version.valid and self.project.python_requires.contains(py_version.version, True): return py_version python = f" {self.python}" if self.python else "" @@ -67,9 +63,7 @@ def subprocess_call(self, cmd: list[str], **kwargs: Any) -> None: try: subprocess.check_call( cmd, - stdout=subprocess.DEVNULL - if self.project.core.ui.verbosity < termui.Verbosity.DETAIL - else None, + stdout=subprocess.DEVNULL if self.project.core.ui.verbosity < termui.Verbosity.DETAIL else None, ) except subprocess.CalledProcessError as e: # pragma: no cover raise VirtualenvCreateError(e) from None @@ -78,16 +72,12 @@ def _ensure_clean(self, location: Path, force: bool = False) -> None: if not location.exists(): return if not force: - raise VirtualenvCreateError( - f"The location {location} is not empty, add --force to overwrite it." - ) + raise VirtualenvCreateError(f"The location {location} is not empty, add --force to overwrite it.") if location.is_file(): self.project.core.ui.echo(f"Removing existing file {location}", err=True) location.unlink() else: - self.project.core.ui.echo( - f"Cleaning existing target directory {location}", err=True - ) + self.project.core.ui.echo(f"Cleaning existing target directory {location}", err=True) shutil.rmtree(location) def get_location(self, name: str | None) -> Path: diff --git a/src/pdm/cli/commands/venv/create.py b/src/pdm/cli/commands/venv/create.py index eaa2070e29..ddb41e3ccc 100644 --- a/src/pdm/cli/commands/venv/create.py +++ b/src/pdm/cli/commands/venv/create.py @@ -30,9 +30,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: help="Recreate if the virtualenv already exists", ) parser.add_argument("-n", "--name", help="Specify the name of the virtualenv") - parser.add_argument( - "--with-pip", action="store_true", help="Install pip with the virtualenv" - ) + parser.add_argument("--with-pip", action="store_true", help="Install pip with the virtualenv") parser.add_argument( "python", nargs="?", @@ -49,9 +47,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: prompt = project.config["venv.prompt"] backend: str = options.backend or project.config["venv.backend"] venv_backend = BACKENDS[backend](project, options.python) - with project.core.ui.open_spinner( - f"Creating virtualenv using [success]{backend}[/]..." - ): + with project.core.ui.open_spinner(f"Creating virtualenv using [success]{backend}[/]..."): path = venv_backend.create( options.name, options.venv_args, diff --git a/src/pdm/cli/commands/venv/purge.py b/src/pdm/cli/commands/venv/purge.py index 8f6370377c..25388bdfb9 100644 --- a/src/pdm/cli/commands/venv/purge.py +++ b/src/pdm/cli/commands/venv/purge.py @@ -34,9 +34,7 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: return if not options.force: - project.core.ui.echo( - "The following Virtualenvs will be purged:", style="error" - ) + project.core.ui.echo("The following Virtualenvs will be purged:", style="error") for i, venv in enumerate(all_central_venvs): project.core.ui.echo(f"{i}. [success]{venv[0]}[/]") diff --git a/src/pdm/cli/commands/venv/remove.py b/src/pdm/cli/commands/venv/remove.py index 231814772e..76ae66dde1 100644 --- a/src/pdm/cli/commands/venv/remove.py +++ b/src/pdm/cli/commands/venv/remove.py @@ -27,14 +27,11 @@ def handle(self, project: Project, options: argparse.Namespace) -> None: project.core.ui.echo("Virtualenvs created with this project:") for ident, venv in iter_venvs(project): if ident == options.env: - if options.yes or termui.confirm( - f"[warning]Will remove: [success]{venv}[/], continue?", default=True - ): + if options.yes or termui.confirm(f"[warning]Will remove: [success]{venv}[/], continue?", default=True): shutil.rmtree(venv) if ( project.project_config.get("python.path") - and Path(project.project_config["python.path"]).parent.parent - == venv + and Path(project.project_config["python.path"]).parent.parent == venv ): del project.project_config["python.path"] project.core.ui.echo("Removed successfully!") diff --git a/src/pdm/cli/commands/venv/utils.py b/src/pdm/cli/commands/venv/utils.py index 31451bccef..900121b562 100644 --- a/src/pdm/cli/commands/venv/utils.py +++ b/src/pdm/cli/commands/venv/utils.py @@ -17,9 +17,7 @@ def hash_path(path: str) -> str: """Generate a hash for the given path.""" - return base64.urlsafe_b64encode( - hashlib.new("md5", path.encode(), usedforsecurity=False).digest() - ).decode()[:8] + return base64.urlsafe_b64encode(hashlib.new("md5", path.encode(), usedforsecurity=False).digest()).decode()[:8] def get_in_project_venv_python(root: Path) -> Path | None: diff --git a/src/pdm/cli/hooks.py b/src/pdm/cli/hooks.py index c66ed38a25..570b2c1f10 100644 --- a/src/pdm/cli/hooks.py +++ b/src/pdm/cli/hooks.py @@ -8,9 +8,7 @@ from pdm import signals from pdm.project.core import Project -KNOWN_HOOKS = tuple( - name for name, obj in signals.__dict__.items() if isinstance(obj, Signal) -) +KNOWN_HOOKS = tuple(name for name, obj in signals.__dict__.items() if isinstance(obj, Signal)) class HookManager: diff --git a/src/pdm/cli/options.py b/src/pdm/cli/options.py index 1332178b36..af6034f698 100644 --- a/src/pdm/cli/options.py +++ b/src/pdm/cli/options.py @@ -101,9 +101,7 @@ def __call__( if not isinstance(values, str): return split = getattr(args, self.dest) or [] - split.extend( - value.strip() for value in values.split(separator) if value.strip() - ) + split.extend(value.strip() for value in values.split(separator) if value.strip()) setattr(args, self.dest, split) return SplitList @@ -291,8 +289,7 @@ def no_isolation_callback( "-p", "--project", dest="project_path", - help="Specify another path as the project root, " - "which changes the base of pyproject.toml and __pypackages__", + help="Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__", ) @@ -305,20 +302,12 @@ def no_isolation_callback( ) clean_group = ArgumentGroup("clean", is_mutually_exclusive=True) -clean_group.add_argument( - "--clean", action="store_true", help="clean packages not in the lockfile" -) -clean_group.add_argument( - "--only-keep", action="store_true", help="only keep the selected packages" -) +clean_group.add_argument("--clean", action="store_true", help="clean packages not in the lockfile") +clean_group.add_argument("--only-keep", action="store_true", help="only keep the selected packages") sync_group = ArgumentGroup("sync", is_mutually_exclusive=True) -sync_group.add_argument( - "--sync", action="store_true", dest="sync", help="sync packages" -) -sync_group.add_argument( - "--no-sync", action="store_false", dest="sync", help="don't sync packages" -) +sync_group.add_argument("--sync", action="store_true", dest="sync", help="sync packages") +sync_group.add_argument("--no-sync", action="store_false", dest="sync", help="don't sync packages") packages_group = ArgumentGroup("Package Arguments") packages_group.add_argument( diff --git a/src/pdm/cli/utils.py b/src/pdm/cli/utils.py index 4c4504e9f6..1481e42cf9 100644 --- a/src/pdm/cli/utils.py +++ b/src/pdm/cli/utils.py @@ -74,9 +74,7 @@ def _parse_known_args( class PdmFormatter(argparse.RawDescriptionHelpFormatter): def start_section(self, heading: str | None) -> None: - return super().start_section( - termui.style(heading.title() if heading else "", style="warning") - ) + return super().start_section(termui.style(heading.title() if heading else "", style="warning")) def _format_usage( self, @@ -106,13 +104,13 @@ def _format_action(self, action: Action) -> str: # short action name; start on the same line and pad two spaces elif len(action_header) <= action_width: - tup = self._current_indent, "", action_width, action_header # type: ignore - action_header = "%*s%-*s " % tup # type: ignore + tup = self._current_indent, "", action_width, action_header # type: ignore[assignment] + action_header = "%*s%-*s " % tup # type: ignore[str-format] indent_first = 0 # long action name; start on the next line else: - tup = self._current_indent, "", action_header # type: ignore + tup = self._current_indent, "", action_header action_header = "%*s%s\n" % tup indent_first = help_position @@ -142,9 +140,7 @@ def _format_action(self, action: Action) -> str: class Package: """An internal class for the convenience of dependency graph building.""" - def __init__( - self, name: str, version: str | None, requirements: dict[str, Requirement] - ) -> None: + def __init__(self, name: str, version: str | None, requirements: dict[str, Requirement]) -> None: self.name = name self.version = version # if version is None, the dist is not installed. self.requirements = requirements @@ -252,9 +248,7 @@ def add_package_to_tree( "[error][ not installed ][/]" if not package.version else f"[error]{package.version}[/]" - if required - and required not in ("Any", "This project") - and not SpecifierSet(required).contains(package.version) + if required and required not in ("Any", "This project") and not SpecifierSet(required).contains(package.version) else f"[warning]{package.version}[/]" ) # escape deps with extras @@ -280,11 +274,7 @@ def add_package_to_reverse_tree( visited: frozenset[str] = frozenset(), ) -> None: """Format one package for output reverse dependency graph.""" - version = ( - "[error][ not installed ][/]" - if not package.version - else f"[warning]{package.version}[/]" - ) + version = "[error][ not installed ][/]" if not package.version else f"[warning]{package.version}[/]" if package.name in visited: version = r"[error]\[circular][/]" requires = ( @@ -302,22 +292,16 @@ def add_package_to_reverse_tree( if package.name in visited: return - parents: list[Package] = sorted( - filter(None, graph.iter_parents(package)), key=lambda p: p.name - ) + parents: list[Package] = sorted(filter(None, graph.iter_parents(package)), key=lambda p: p.name) for parent in parents: requires = specifier_from_requirement(parent.requirements[package.name]) - add_package_to_reverse_tree( - node, graph, parent, package, requires, visited=visited | {package.name} - ) + add_package_to_reverse_tree(node, graph, parent, package, requires, visited=visited | {package.name}) return def package_is_project(package: Package, project: Project) -> bool: return ( - not project.environment.is_global - and project.name is not None - and package.name == normalize_name(project.name) + not project.environment.is_global and project.name is not None and package.name == normalize_name(project.name) ) @@ -337,9 +321,7 @@ def _format_forward_dependency_graph(project: Project, graph: DirectedGraph) -> return root -def _format_reverse_dependency_graph( - project: Project, graph: DirectedGraph[Package | None] -) -> Tree: +def _format_reverse_dependency_graph(project: Project, graph: DirectedGraph[Package | None]) -> Tree: """Format reverse dependency graph for output.""" root = Tree("Dependencies", hide_root=True) leaf_nodes = sorted( @@ -361,11 +343,7 @@ def build_forward_dependency_json_subtree( visited: frozenset[str] = frozenset(), ) -> dict: if not package_is_project(root, project): - requirements = ( - required_by.requirements - if required_by - else ChainMap(*project.all_dependencies.values()) - ) + requirements = required_by.requirements if required_by else ChainMap(*project.all_dependencies.values()) if root.name in requirements: required = specifier_from_requirement(requirements[root.name]) else: @@ -381,9 +359,7 @@ def build_forward_dependency_json_subtree( required=required, dependencies=sorted( ( - build_forward_dependency_json_subtree( - p, project, graph, root, visited | {root.name} - ) + build_forward_dependency_json_subtree(p, project, graph, root, visited | {root.name}) for p in children if p ), @@ -403,14 +379,10 @@ def build_reverse_dependency_json_subtree( return OrderedDict( package=root.name, version=root.version, - requires=specifier_from_requirement(root.requirements[requires.name]) - if requires - else None, + requires=specifier_from_requirement(root.requirements[requires.name]) if requires else None, dependents=sorted( ( - build_reverse_dependency_json_subtree( - p, project, graph, root, visited | {root.name} - ) + build_reverse_dependency_json_subtree(p, project, graph, root, visited | {root.name}) for p in parents if p ), @@ -419,13 +391,9 @@ def build_reverse_dependency_json_subtree( ) -def build_dependency_json_tree( - project: Project, graph: DirectedGraph[Package | None], reverse: bool -) -> list[dict]: +def build_dependency_json_tree(project: Project, graph: DirectedGraph[Package | None], reverse: bool) -> list[dict]: if reverse: - top_level_packages = filter( - lambda n: not list(graph.iter_children(n)), graph - ) # leaf nodes + top_level_packages = filter(lambda n: not list(graph.iter_children(n)), graph) # leaf nodes build_dependency_json_subtree: Callable = build_reverse_dependency_json_subtree else: top_level_packages = graph.iter_children(None) # root nodes @@ -473,33 +441,27 @@ def format_lockfile( file_hashes = tomlkit.table() for k, v in sorted(mapping.items()): base = tomlkit.table() - base.update(v.as_lockfile_entry(project.root)) # type: ignore + base.update(v.as_lockfile_entry(project.root)) base.add("summary", v.summary or "") - deps = make_array( - sorted(r.as_line() for r in fetched_dependencies[v.dep_key]), True - ) + deps = make_array(sorted(r.as_line() for r in fetched_dependencies[v.dep_key]), True) if len(deps) > 0: base.add("dependencies", deps) - packages.append(base) # type: ignore + packages.append(base) if v.hashes: key = f"{strip_extras(k)[0]} {v.version}" if key in file_hashes: continue array = tomlkit.array().multiline(True) - for link, hash_value in sorted( - v.hashes.items(), key=lambda l_h: (l_h[0].url_without_fragment, l_h[1]) - ): - inline = make_inline_table( - {"url": link.url_without_fragment, "hash": hash_value} - ) - array.append(inline) # type: ignore + for link, hash_value in sorted(v.hashes.items(), key=lambda l_h: (l_h[0].url_without_fragment, l_h[1])): + inline = make_inline_table({"url": link.url_without_fragment, "hash": hash_value}) + array.append(inline) if array: file_hashes.add(key, array) doc = tomlkit.document() - doc.add("package", packages) # type: ignore + doc.add("package", packages) metadata = tomlkit.table() metadata.add("files", file_hashes) - doc.add("metadata", metadata) # type: ignore + doc.add("metadata", metadata) return cast(dict, doc) @@ -523,29 +485,22 @@ def candidate_version(c: Candidate) -> Version: for name, r in reqs.items(): if r.is_named and not r.specifier: if save_strategy == "exact": - r.specifier = get_specifier( - f"=={candidate_version(resolved[name])}" - ) + r.specifier = get_specifier(f"=={candidate_version(resolved[name])}") elif save_strategy == "compatible": version = candidate_version(resolved[name]) if version.is_prerelease or version.is_devrelease: r.specifier = get_specifier(f">={version},<{version.major + 1}") else: - r.specifier = get_specifier( - f"~={version.major}.{version.minor}" - ) + r.specifier = get_specifier(f"~={version.major}.{version.minor}") elif save_strategy == "minimum": - r.specifier = get_specifier( - f">={candidate_version(resolved[name])}" - ) + r.specifier = get_specifier(f">={candidate_version(resolved[name])}") def check_project_file(project: Project) -> None: """Check the existence of the project file and throws an error on failure.""" if not project.pyproject.is_valid: raise ProjectError( - "The pyproject.toml has not been initialized yet. You can do this " - "by running [success]`pdm init`[/]." + "The pyproject.toml has not been initialized yet. You can do this by running [success]`pdm init`[/]." ) from None @@ -584,7 +539,7 @@ def set_env_in_reg(env_name: str, value: str) -> None: return except FileNotFoundError: paths, type_ = [], winreg.REG_EXPAND_SZ - new_value = os.pathsep.join([value] + paths) + new_value = os.pathsep.join([value, *paths]) winreg.SetValueEx(env_key, env_name, 0, type_, new_value) @@ -594,15 +549,12 @@ def format_resolution_impossible(err: ResolutionImpossible) -> str: causes: list[RequirementInformation] = err.causes info_lines: set[str] = set() if all(isinstance(cause.requirement, PythonRequirement) for cause in causes): - project_requires: PythonRequirement = next( - cause.requirement for cause in causes if cause.parent is None - ) + project_requires: PythonRequirement = next(cause.requirement for cause in causes if cause.parent is None) pyspec = cast(PySpecSet, project_requires.specifier) conflicting = [ cause for cause in causes - if cause.parent is not None - and not cause.requirement.specifier.is_superset(pyspec) + if cause.parent is not None and not cause.requirement.specifier.is_superset(pyspec) ] result = [ "Unable to find a resolution because the following dependencies don't work " @@ -614,10 +566,7 @@ def format_resolution_impossible(err: ResolutionImpossible) -> str: info_lines.add(f" {req.as_line()} (from {repr(parent)})") result.extend(sorted(info_lines)) if pyspec.is_impossible: - result.append( - "Consider changing the version specifiers of the dependencies to " - "be compatible" - ) + result.append("Consider changing the version specifiers of the dependencies to be compatible") else: result.append( "A possible solution is to change the value of `requires-python` " @@ -648,9 +597,7 @@ def format_resolution_impossible(err: ResolutionImpossible) -> str: return "\n".join(result) -def translate_groups( - project: Project, default: bool, dev: bool, groups: Iterable[str] -) -> list[str]: +def translate_groups(project: Project, default: bool, dev: bool, groups: Iterable[str]) -> list[str]: """Translate default, dev and groups containing ":all" into a list of groups""" optional_groups = set(project.pyproject.metadata.get("optional-dependencies", {})) dev_groups = set(project.pyproject.settings.get("dev-dependencies", {})) @@ -659,9 +606,7 @@ def translate_groups( dev = True if groups_set & dev_groups: if not dev: - raise PdmUsageError( - "--prod is not allowed with dev groups and should be left" - ) + raise PdmUsageError("--prod is not allowed with dev groups and should be left") elif dev: groups_set.update(dev_groups) if ":all" in groups: @@ -681,9 +626,7 @@ def translate_groups( return sorted(groups_set) -def merge_dictionary( - target: MutableMapping[Any, Any], input: Mapping[Any, Any] -) -> None: +def merge_dictionary(target: MutableMapping[Any, Any], input: Mapping[Any, Any]) -> None: """Merge the input dict with the target while preserving the existing values properly. This will update the target dictionary in place. List values will be extended, but only if the value is not already in the list. @@ -719,9 +662,7 @@ def is_homebrew_installation() -> bool: def is_scoop_installation() -> bool: - return os.name == "nt" and is_path_relative_to( - sys.prefix, Path.home() / "scoop/apps/pdm" - ) + return os.name == "nt" and is_path_relative_to(sys.prefix, Path.home() / "scoop/apps/pdm") def get_dist_location(dist: Distribution) -> str: diff --git a/src/pdm/compat.py b/src/pdm/compat.py index d7baa0bae2..dadd448fc9 100644 --- a/src/pdm/compat.py +++ b/src/pdm/compat.py @@ -18,18 +18,12 @@ def resources_open_binary(package: str, resource: str) -> BinaryIO: return (importlib.resources.files(package) / resource).open("rb") - def resources_read_text( - package: str, resource: str, encoding: str = "utf-8", errors: str = "strict" - ) -> str: - with (importlib.resources.files(package) / resource).open( - "r", encoding=encoding, errors=errors - ) as f: + def resources_read_text(package: str, resource: str, encoding: str = "utf-8", errors: str = "strict") -> str: + with (importlib.resources.files(package) / resource).open("r", encoding=encoding, errors=errors) as f: return f.read() def resources_path(package: str, resource: str) -> ContextManager[Path]: - return importlib.resources.as_file( - importlib.resources.files(package) / resource - ) + return importlib.resources.as_file(importlib.resources.files(package) / resource) else: resources_open_binary = importlib.resources.open_binary diff --git a/src/pdm/core.py b/src/pdm/core.py index d380333ac0..85c238f3df 100644 --- a/src/pdm/core.py +++ b/src/pdm/core.py @@ -32,9 +32,7 @@ from pdm.project.config import Config, ConfigItem from pdm.utils import is_in_zipapp -COMMANDS_MODULE_PATH: str = importlib.import_module( - "pdm.cli.commands" -).__path__ # type: ignore +COMMANDS_MODULE_PATH = importlib.import_module("pdm.cli.commands").__path__ class Core: @@ -61,7 +59,7 @@ def init_parser(self) -> None: description=__doc__, formatter_class=PdmFormatter, ) - self.parser.is_root = True # type: ignore + self.parser.is_root = True # type: ignore[attr-defined] self.parser.add_argument( "-V", "--version", @@ -82,13 +80,11 @@ def init_parser(self) -> None: ignore_python_option.add_to_parser(self.parser) pep582_option.add_to_parser(self.parser) - self.subparsers = self.parser.add_subparsers( - parser_class=argparse.ArgumentParser - ) + self.subparsers = self.parser.add_subparsers(parser_class=argparse.ArgumentParser) for _, name, _ in pkgutil.iter_modules(COMMANDS_MODULE_PATH): module = importlib.import_module(f"pdm.cli.commands.{name}", __name__) try: - klass = module.Command # type: ignore + klass = module.Command except AttributeError: continue self.register_command(klass, klass.name or name) @@ -96,21 +92,15 @@ def init_parser(self) -> None: def __call__(self, *args: Any, **kwargs: Any) -> None: return self.main(*args, **kwargs) - def ensure_project( - self, options: argparse.Namespace, obj: Project | None - ) -> Project: + def ensure_project(self, options: argparse.Namespace, obj: Project | None) -> Project: if obj is not None: project = obj else: global_project = bool(getattr(options, "global_project", None)) - default_root = ( - None - if global_project or getattr(options, "search_parent", True) - else "." - ) + default_root = None if global_project or getattr(options, "search_parent", True) else "." project = self.create_project( - getattr(options, "project_path", None) or default_root, # type: ignore + getattr(options, "project_path", None) or default_root, is_global=global_project, global_config=options.config or os.getenv("PDM_CONFIG_FILE"), ) @@ -188,9 +178,9 @@ def main( etype, err, traceback = sys.exc_info() should_show_tb = not isinstance(err, PdmUsageError) if self.ui.verbosity > termui.Verbosity.NORMAL and should_show_tb: - raise cast(Exception, err).with_traceback(traceback) + raise cast(Exception, err).with_traceback(traceback) from None self.ui.echo( - rf"[error]\[{etype.__name__}][/]: {err}", # type: ignore + rf"[error]\[{etype.__name__}][/]: {err}", # type: ignore[union-attr] err=True, ) if should_show_tb: @@ -204,9 +194,7 @@ def main( if project.config["check_update"] and not is_in_zipapp(): check_update(project) - def register_command( - self, command: type[BaseCommand], name: str | None = None - ) -> None: + def register_command(self, command: type[BaseCommand], name: str | None = None) -> None: """Register a subcommand to the subparsers, with an optional name of the subcommand. diff --git a/src/pdm/exceptions.py b/src/pdm/exceptions.py index c8da4df055..79a60e9c57 100644 --- a/src/pdm/exceptions.py +++ b/src/pdm/exceptions.py @@ -36,9 +36,7 @@ class CandidateNotFound(PdmException): class CandidateInfoNotFound(PdmException): def __init__(self, candidate: Candidate) -> None: - message = ( - "No metadata information is available for " f"[success]{str(candidate)}[/]." - ) + message = f"No metadata information is available for [success]{str(candidate)}[/]." self.candidate = candidate super().__init__(message) diff --git a/src/pdm/formats/__init__.py b/src/pdm/formats/__init__.py index cc7f99e47d..5c56605712 100644 --- a/src/pdm/formats/__init__.py +++ b/src/pdm/formats/__init__.py @@ -25,9 +25,7 @@ def convert( ) -> tuple[Mapping, Mapping]: ... - def export( - self, project: Project, candidates: ExportItems, options: Namespace | None - ) -> str: + def export(self, project: Project, candidates: ExportItems, options: Namespace | None) -> str: ... diff --git a/src/pdm/formats/base.py b/src/pdm/formats/base.py index d55e44aea9..b49df56e95 100644 --- a/src/pdm/formats/base.py +++ b/src/pdm/formats/base.py @@ -10,12 +10,10 @@ _T = TypeVar("_T", bound=Callable) -def convert_from( - field: str | None = None, name: str | None = None -) -> Callable[[_T], _T]: +def convert_from(field: str | None = None, name: str | None = None) -> Callable[[_T], _T]: def wrapper(func: _T) -> _T: - func._convert_from = field # type: ignore - func._convert_to = name # type: ignore + func._convert_from = field # type: ignore[attr-defined] + func._convert_to = name # type: ignore[attr-defined] return func return wrapper @@ -50,13 +48,9 @@ def __init__(self, source: dict, ui: termui.UI | None = None) -> None: def convert(self) -> tuple[Mapping[str, Any], Mapping[str, Any]]: source = self.source for key, func in self._converters.items(): - if func._convert_from and func._convert_from not in source: # type: ignore + if func._convert_from and func._convert_from not in source: # type: ignore[attr-defined] continue - value = ( - source - if func._convert_from is None # type: ignore - else source[func._convert_from] # type: ignore - ) + value = source if func._convert_from is None else source[func._convert_from] # type: ignore[attr-defined] try: self._data[key] = func(self, value) except Unset: @@ -64,10 +58,10 @@ def convert(self) -> tuple[Mapping[str, Any], Mapping[str, Any]]: # Delete all used fields for func in self._converters.values(): - if func._convert_from is None: # type: ignore + if func._convert_from is None: # type: ignore[attr-defined] continue try: - del source[func._convert_from] # type: ignore + del source[func._convert_from] # type: ignore[attr-defined] except KeyError: pass # Add remaining items to the data @@ -102,9 +96,7 @@ def parse_name_email(name_email: list[str]) -> list[str]: [ { k: v - for k, v in NAME_EMAIL_RE.match(item) - .groupdict() # type: ignore - .items() + for k, v in NAME_EMAIL_RE.match(item).groupdict().items() # type: ignore[union-attr] if v is not None } for item in name_email diff --git a/src/pdm/formats/flit.py b/src/pdm/formats/flit.py index 5393e1cd13..b4455c9546 100644 --- a/src/pdm/formats/flit.py +++ b/src/pdm/formats/flit.py @@ -65,9 +65,7 @@ def get_docstring_and_version_via_ast( class FlitMetaConverter(MetaConverter): - def warn_against_dynamic_version_or_docstring( - self, source: Path, version: str, description: str - ) -> None: + def warn_against_dynamic_version_or_docstring(self, source: Path, version: str, description: str) -> None: if not self._ui: return dynamic_fields = [] @@ -101,9 +99,7 @@ def name(self, metadata: dict[str, Any]) -> str: description_in_ast, version_in_ast = get_docstring_and_version_via_ast(source) self._data["version"] = version or version_in_ast or "" self._data["description"] = description or description_in_ast or "" - self.warn_against_dynamic_version_or_docstring( - source, self._data["version"], self._data["description"] - ) + self.warn_against_dynamic_version_or_docstring(source, self._data["version"], self._data["description"]) # author and maintainer if "author" in metadata: self._data["authors"] = _get_author(metadata) @@ -129,9 +125,7 @@ def name(self, metadata: dict[str, Any]) -> str: return self._data["name"] @convert_from("entrypoints", name="entry-points") - def entry_points( - self, value: dict[str, dict[str, str]] - ) -> dict[str, dict[str, str]]: + def entry_points(self, value: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]: return value @convert_from("sdist") @@ -142,13 +136,9 @@ def includes(self, value: dict[str, list[str]]) -> None: raise Unset() -def convert( - project: Project | None, filename: PathLike, options: Namespace | None -) -> tuple[Mapping, Mapping]: +def convert(project: Project | None, filename: PathLike, options: Namespace | None) -> tuple[Mapping, Mapping]: with open(filename, "rb") as fp, cd(os.path.dirname(os.path.abspath(filename))): - converter = FlitMetaConverter( - tomllib.load(fp)["tool"]["flit"], project.core.ui if project else None - ) + converter = FlitMetaConverter(tomllib.load(fp)["tool"]["flit"], project.core.ui if project else None) return converter.convert() diff --git a/src/pdm/formats/pipfile.py b/src/pdm/formats/pipfile.py index 6fa999ec76..311102b057 100644 --- a/src/pdm/formats/pipfile.py +++ b/src/pdm/formats/pipfile.py @@ -23,7 +23,7 @@ def convert_pipfile_requirement(name: str, req: RequirementDict) -> str: if isinstance(req, dict): markers: list[Marker] = [] if "markers" in req: - markers.append(Marker(req["markers"])) # type: ignore + markers.append(Marker(req["markers"])) # type: ignore[arg-type] for key in MARKER_KEYS: if key in req: marker = Marker(f"{key}{req[key]}") @@ -40,9 +40,7 @@ def check_fingerprint(project: Project, filename: PathLike) -> bool: return os.path.basename(filename) == "Pipfile" -def convert( - project: Project, filename: PathLike, options: Namespace | None -) -> tuple[dict[str, Any], dict[str, Any]]: +def convert(project: Project, filename: PathLike, options: Namespace | None) -> tuple[dict[str, Any], dict[str, Any]]: with open(filename, "rb") as fp: data = tomllib.load(fp) result = {} @@ -50,25 +48,17 @@ def convert( if "pipenv" in data: settings["allow_prereleases"] = data["pipenv"].get("allow_prereleases", False) if "requires" in data: - python_version = data["requires"].get("python_full_version") or data[ - "requires" - ].get("python_version") + python_version = data["requires"].get("python_full_version") or data["requires"].get("python_version") result["requires-python"] = f">={python_version}" if "source" in data: settings["source"] = data["source"] - result["dependencies"] = make_array( # type: ignore - [ - convert_pipfile_requirement(k, req) - for k, req in data.get("packages", {}).items() - ], + result["dependencies"] = make_array( # type: ignore[assignment] + [convert_pipfile_requirement(k, req) for k, req in data.get("packages", {}).items()], True, ) settings["dev-dependencies"] = { "dev": make_array( - [ - convert_pipfile_requirement(k, req) - for k, req in data.get("dev-packages", {}).items() - ], + [convert_pipfile_requirement(k, req) for k, req in data.get("dev-packages", {}).items()], True, ) } diff --git a/src/pdm/formats/poetry.py b/src/pdm/formats/poetry.py index a230ace577..20c9b06857 100644 --- a/src/pdm/formats/poetry.py +++ b/src/pdm/formats/poetry.py @@ -21,9 +21,10 @@ from pdm.models.specifiers import PySpecSet if TYPE_CHECKING: - from pdm.project.core import Project from argparse import Namespace + from pdm._types import RequirementDict, Source + from pdm.project.core import Project from pdm.utils import cd @@ -45,17 +46,17 @@ def check_fingerprint(project: Project | None, filename: Path | str) -> bool: def _convert_specifier(version: str) -> str: parts = [] - for op, version in VERSION_RE.findall(str(version)): + for op, ver in VERSION_RE.findall(str(version)): if op == "~": op += "=" elif op == "^": - major, *vparts = version.split(".") + major, *vparts = ver.split(".") next_major = ".".join([str(int(major) + 1)] + ["0"] * len(vparts)) - parts.append(f">={version},<{next_major}") + parts.append(f">={ver},<{next_major}") continue elif not op: op = "==" - parts.append(f"{op}{version}") + parts.append(f"{op}{ver}") return ",".join(parts) @@ -66,9 +67,7 @@ def _convert_python(python: str) -> PySpecSet: return functools.reduce(operator.or_, parts) -def _convert_req( - name: str, req_dict: RequirementDict | list[RequirementDict] -) -> Iterable[str]: +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) @@ -82,18 +81,14 @@ def _convert_req( 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 + markers.append(Marker(req_dict.pop("markers"))) # type: ignore[arg-type] if "python" in req_dict: - markers.append( - Marker(_convert_python(str(req_dict.pop("python"))).as_marker_string()) - ) + 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( - '"', "'" - ) + 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 + "rev", req_dict.pop("tag", req_dict.pop("branch", None)) # type: ignore[arg-type] ) yield Requirement.from_req_dict(name, req_dict).as_line() @@ -128,9 +123,7 @@ def urls(self, source: dict[str, Any]) -> dict[str, str]: return rv @convert_from("plugins", name="entry-points") - def entry_points( - self, value: dict[str, dict[str, str]] - ) -> dict[str, dict[str, str]]: + def entry_points(self, value: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]: return value @convert_from() @@ -138,16 +131,12 @@ def dependencies(self, source: dict[str, Any]) -> list[str]: rv = [] value, extras = dict(source["dependencies"]), source.pop("extras", {}) for key, req_dict in value.items(): - optional = getattr(req_dict, "items", None) and req_dict.pop( - "optional", False - ) + optional = getattr(req_dict, "items", None) and req_dict.pop("optional", False) 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) + self._data.setdefault("optional-dependencies", {}).setdefault(extra, []).append(req) else: rv.append(req) del source["dependencies"] @@ -156,9 +145,7 @@ def dependencies(self, source: dict[str, Any]) -> list[str]: @convert_from("dev-dependencies") def dev_dependencies(self, value: dict) -> None: self.settings["dev-dependencies"] = { - "dev": make_array( - [r for key, req in value.items() for r in _convert_req(key, req)], True - ), + "dev": make_array([r for key, req in value.items() for r in _convert_req(key, req)], True), } raise Unset() @@ -187,9 +174,7 @@ def build(self, value: str | dict) -> None: if "generate-setup-file" in value: run_setuptools = cast(bool, value["generate-setup-file"]) value = value["script"] - self.settings.setdefault("build", {}).update( - {"setup-script": value, "run-setuptools": run_setuptools} - ) + self.settings.setdefault("build", {}).update({"setup-script": value, "run-setuptools": run_setuptools}) raise Unset() @convert_from("source") @@ -211,9 +196,7 @@ def convert( options: Namespace | None, ) -> tuple[Mapping[str, Any], Mapping[str, Any]]: with open(filename, "rb") as fp, cd(os.path.dirname(os.path.abspath(filename))): - converter = PoetryMetaConverter( - tomllib.load(fp)["tool"]["poetry"], project.core.ui if project else None - ) + converter = PoetryMetaConverter(tomllib.load(fp)["tool"]["poetry"], project.core.ui if project else None) return converter.convert() diff --git a/src/pdm/formats/requirements.py b/src/pdm/formats/requirements.py index 17b381d16e..f57c8eaf0d 100644 --- a/src/pdm/formats/requirements.py +++ b/src/pdm/formats/requirements.py @@ -109,9 +109,7 @@ def _is_url_trusted(url: str, trusted_hosts: list[str]) -> bool: return False -def convert_url_to_source( - url: str, name: str | None, trusted_hosts: list[str], type: str = "index" -) -> Source: +def convert_url_to_source(url: str, name: str | None, trusted_hosts: list[str], type: str = "index") -> Source: if not name: name = hashlib.sha1(url.encode("utf-8")).hexdigest()[:6] source = { @@ -124,9 +122,7 @@ def convert_url_to_source( return cast("Source", source) -def convert( - project: Project, filename: PathLike, options: Namespace -) -> tuple[Mapping[str, Any], Mapping[str, Any]]: +def convert(project: Project, filename: PathLike, options: Namespace) -> tuple[Mapping[str, Any], Mapping[str, Any]]: parser = RequirementParser() parser.parse(str(filename)) backend = project.backend @@ -152,9 +148,7 @@ def convert( data["dependencies"] = deps sources: list[Source] = [] if parser.index_url and not parser.no_index: - sources.append( - convert_url_to_source(parser.index_url, "pypi", parser.trusted_hosts) - ) + sources.append(convert_url_to_source(parser.index_url, "pypi", parser.trusted_hosts)) if not parser.no_index: for url in parser.extra_index_urls: sources.append(convert_url_to_source(url, None, parser.trusted_hosts)) @@ -169,9 +163,7 @@ def convert( ) ) for url in find_links: - sources.append( - convert_url_to_source(url, None, parser.trusted_hosts, "find_links") - ) + sources.append(convert_url_to_source(url, None, parser.trusted_hosts, "find_links")) if sources: settings["source"] = sources @@ -184,17 +176,15 @@ def export( options: Namespace, ) -> str: lines = [] - for candidate in sorted(candidates, key=lambda x: x.identify()): # type: ignore + for candidate in sorted(candidates, key=lambda x: x.identify()): if isinstance(candidate, Candidate): - req = dataclasses.replace( - candidate.req, specifier=f"=={candidate.version}", marker=None - ) + req = dataclasses.replace(candidate.req, specifier=f"=={candidate.version}", marker=None) else: assert isinstance(candidate, Requirement) req = candidate lines.append(project.backend.expand_line(req.as_line())) if options.hashes and getattr(candidate, "hashes", None): - for item in sorted(set(candidate.hashes.values())): # type: ignore + for item in sorted(set(candidate.hashes.values())): # type: ignore[attr-defined] lines.append(f" \\\n --hash={item}") lines.append("\n") sources = project.pyproject.settings.get("source", []) diff --git a/src/pdm/formats/setup_py.py b/src/pdm/formats/setup_py.py index 316da7d119..a1da081ede 100644 --- a/src/pdm/formats/setup_py.py +++ b/src/pdm/formats/setup_py.py @@ -12,14 +12,10 @@ def check_fingerprint(project: Project, filename: Path) -> bool: return os.path.basename(filename) == "setup.py" -def convert( - project: Project, filename: Path, options: Any | None -) -> tuple[Mapping[str, Any], Mapping[str, Any]]: +def convert(project: Project, filename: Path, options: Any | None) -> tuple[Mapping[str, Any], Mapping[str, Any]]: from pdm.models.in_process import parse_setup_py - parsed = parse_setup_py( - str(project.environment.interpreter.executable), str(filename) - ) + parsed = parse_setup_py(str(project.environment.interpreter.executable), str(filename)) metadata: dict[str, Any] = {} settings: dict[str, Any] = {} for name in [ diff --git a/src/pdm/installers/core.py b/src/pdm/installers/core.py index a02ac77ac4..1faa728c3b 100644 --- a/src/pdm/installers/core.py +++ b/src/pdm/installers/core.py @@ -24,7 +24,7 @@ def install_requirements( backend = project.backend for req in reqs: if req.is_file_or_url: - req.relocate(backend) # type: ignore + req.relocate(backend) # type: ignore[attr-defined] resolved, _ = resolve( resolver, reqs, diff --git a/src/pdm/installers/installers.py b/src/pdm/installers/installers.py index f8063a7edf..a271f2f6a7 100644 --- a/src/pdm/installers/installers.py +++ b/src/pdm/installers/installers.py @@ -117,16 +117,10 @@ def __init__(self, f: zipfile.ZipFile) -> None: def dist_info_dir(self) -> str: namelist = self._zipfile.namelist() try: - return next( - name.split("/")[0] - for name in namelist - if name.split("/")[0].endswith(".dist-info") - ) + return next(name.split("/")[0] for name in namelist if name.split("/")[0].endswith(".dist-info")) except StopIteration: # pragma: no cover canonical_name = super().dist_info_dir - raise InvalidWheelSource( - f"The wheel doesn't contain metadata {canonical_name!r}" - ) + raise InvalidWheelSource(f"The wheel doesn't contain metadata {canonical_name!r}") from None def get_contents(self) -> Iterator[WheelContentElement]: for element in super().get_contents(): @@ -137,15 +131,11 @@ def get_contents(self) -> Iterator[WheelContentElement]: class InstallDestination(SchemeDictionaryDestination): - def __init__( - self, *args: Any, symlink_to: str | None = None, **kwargs: Any - ) -> None: + def __init__(self, *args: Any, symlink_to: str | None = None, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.symlink_to = symlink_to - def write_to_fs( - self, scheme: Scheme, path: str | Path, stream: BinaryIO, is_executable: bool - ) -> RecordEntry: + def write_to_fs(self, scheme: Scheme, path: str | Path, stream: BinaryIO, is_executable: bool) -> RecordEntry: target_path = os.path.join(self.scheme_dict[scheme], path) if os.path.exists(target_path): os.unlink(target_path) @@ -159,9 +149,7 @@ def finalize_installation( ) -> None: if self.symlink_to: # Create symlinks to the cached location - for relpath in _create_symlinks_recursively( - self.symlink_to, self.scheme_dict[scheme] - ): + for relpath in _create_symlinks_recursively(self.symlink_to, self.scheme_dict[scheme]): records = itertools.chain( records, [(scheme, RecordEntry(relpath.replace("\\", "/"), None, None))], @@ -169,28 +157,20 @@ def finalize_installation( return super().finalize_installation(scheme, record_file_path, records) -def install_wheel( - wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None -) -> None: +def install_wheel(wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None) -> None: """Install a normal wheel file into the environment.""" additional_metadata = None if direct_url is not None: - additional_metadata = { - "direct_url.json": json.dumps(direct_url, indent=2).encode() - } + additional_metadata = {"direct_url.json": json.dumps(direct_url, indent=2).encode()} destination = InstallDestination( scheme_dict=environment.get_paths(), interpreter=str(environment.interpreter.executable), script_kind=_get_kind(environment), ) - _install_wheel( - wheel=wheel, destination=destination, additional_metadata=additional_metadata - ) + _install_wheel(wheel=wheel, destination=destination, additional_metadata=additional_metadata) -def install_wheel_with_cache( - wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None -) -> None: +def install_wheel_with_cache(wheel: str, environment: Environment, direct_url: dict[str, Any] | None = None) -> None: """Only create .pth files referring to the cached package. If the cache doesn't exist, create one. """ @@ -199,10 +179,7 @@ def install_wheel_with_cache( package_cache = CachedPackage(cache_path) interpreter = str(environment.interpreter.executable) script_kind = _get_kind(environment) - use_symlink = ( - environment.project.config["install.cache_method"] == "symlink" - and fs_supports_symlink() - ) + use_symlink = environment.project.config["install.cache_method"] == "symlink" and fs_supports_symlink() if not cache_path.is_dir(): logger.info("Installing wheel into cached location %s", cache_path) cache_path.mkdir(exist_ok=True) @@ -216,9 +193,7 @@ def install_wheel_with_cache( additional_metadata = {"REFER_TO": package_cache.path.as_posix().encode()} if direct_url is not None: - additional_metadata["direct_url.json"] = json.dumps( - direct_url, indent=2 - ).encode() + additional_metadata["direct_url.json"] = json.dumps(direct_url, indent=2).encode() def skip_files(source: WheelFile, element: WheelContentElement) -> bool: root_scheme = _process_WHEEL_file(source) @@ -240,9 +215,7 @@ def skip_files(source: WheelFile, element: WheelContentElement) -> bool: filename = "aaa_" + wheel_stem.split("-")[0] + ".pth" # use site.addsitedir() rather than a plain path to properly process .pth files stream = io.BytesIO(f"import site;site.addsitedir({lib_path!r})\n".encode()) - additional_contents.append( - ((filename, "", str(len(stream.getvalue()))), stream, False) - ) + additional_contents.append(((filename, "", str(len(stream.getvalue()))), stream, False)) destination = InstallDestination( scheme_dict=environment.get_paths(), diff --git a/src/pdm/installers/manager.py b/src/pdm/installers/manager.py index 0750a95396..1ec15bd623 100644 --- a/src/pdm/installers/manager.py +++ b/src/pdm/installers/manager.py @@ -19,18 +19,12 @@ class InstallManager: # The packages below are needed to load paths and thus should not be cached. NO_CACHE_PACKAGES = ["editables"] - def __init__( - self, environment: Environment, *, use_install_cache: bool = False - ) -> None: + def __init__(self, environment: Environment, *, use_install_cache: bool = False) -> None: self.environment = environment self.use_install_cache = use_install_cache def install(self, candidate: Candidate) -> None: - if ( - self.use_install_cache - and candidate.req.is_named - and candidate.name not in self.NO_CACHE_PACKAGES - ): + if self.use_install_cache and candidate.req.is_named and candidate.name not in self.NO_CACHE_PACKAGES: # Only cache wheels from PyPI installer = install_wheel_with_cache else: @@ -51,8 +45,6 @@ def uninstall(self, dist: Distribution) -> None: remove_path.remove() remove_path.commit() except OSError as e: - termui.logger.info( - "Error occurred during uninstallation, roll back the changes now." - ) + termui.logger.info("Error occurred during uninstallation, roll back the changes now.") remove_path.rollback() raise UninstallError(e) from e diff --git a/src/pdm/installers/packages.py b/src/pdm/installers/packages.py index f6d722c20d..17414e2363 100644 --- a/src/pdm/installers/packages.py +++ b/src/pdm/installers/packages.py @@ -41,9 +41,7 @@ def add_referrer(self, path: str) -> None: """Add a new referrer""" path = os.path.normcase(os.path.expanduser(os.path.abspath(path))) referrers = self.referrers | {path} - (self.path / "referrers").write_text( - "\n".join(sorted(referrers)) + "\n", "utf8" - ) + (self.path / "referrers").write_text("\n".join(sorted(referrers)) + "\n", "utf8") self._referrers = None def remove_referrer(self, path: str) -> None: @@ -61,7 +59,5 @@ def scheme(self) -> dict[str, str]: return pdm_scheme(str(self.path)) def cleanup(self) -> None: - logger.info( - "Clean up cached package %s since it is not used by any project.", self.path - ) + logger.info("Clean up cached package %s since it is not used by any project.", self.path) shutil.rmtree(self.path) diff --git a/src/pdm/installers/synchronizers.py b/src/pdm/installers/synchronizers.py index fb290e0368..920e7af5c4 100644 --- a/src/pdm/installers/synchronizers.py +++ b/src/pdm/installers/synchronizers.py @@ -123,9 +123,7 @@ def __init__( self.ui = environment.project.core.ui self.self_candidate: Candidate | None = None if self.install_self: - self.self_candidate = self.environment.project.make_self_candidate( - not self.no_editable - ) + self.self_candidate = self.environment.project.make_self_candidate(not self.no_editable) if isinstance(self.no_editable, Collection): keys = self.no_editable @@ -143,9 +141,7 @@ def __init__( # 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 - ) + candidates[key] = make_candidate(req, candidate.name, candidate.version, candidate.link) self.candidates = candidates self._manager: InstallManager | None = None @@ -190,9 +186,7 @@ def _should_update(self, dist: Distribution, can: Candidate) -> bool: return bool(self.no_editable) if not can.req.is_named: dreq = Requirement.from_dist(dist) - return getattr(dreq, "url", None) != backend.expand_line( - can.req.url # type: ignore - ) + return getattr(dreq, "url", None) != backend.expand_line(can.req.url) # type: ignore[attr-defined] specifier = can.req.as_pinned_version(can.version).specifier assert specifier is not None return not specifier.contains(dist.version, prereleases=True) @@ -235,29 +229,21 @@ def install_candidate(self, key: str, progress: Progress) -> Candidate: try: self.manager.install(can) except Exception: - progress.live.console.print( - f" [error]{termui.Emoji.FAIL}[/] Install {can.format()} failed" - ) + progress.live.console.print(f" [error]{termui.Emoji.FAIL}[/] Install {can.format()} failed") raise else: - progress.live.console.print( - f" [success]{termui.Emoji.SUCC}[/] Install {can.format()} successful" - ) + progress.live.console.print(f" [success]{termui.Emoji.SUCC}[/] Install {can.format()} successful") finally: progress.update(job, completed=1, visible=False) return can - def update_candidate( - self, key: str, progress: Progress - ) -> tuple[Distribution, Candidate]: + def update_candidate(self, key: str, progress: Progress) -> tuple[Distribution, Candidate]: """Update candidate""" can = self.candidates[key] dist = self.working_set[strip_extras(key)[0]] dist_version = dist.version job = progress.add_task( - f"Updating [req]{key}[/] " - f"[warning]{dist_version}[/] " - f"-> [warning]{can.version}[/]...", + f"Updating [req]{key}[/] [warning]{dist_version}[/] -> [warning]{can.version}[/]...", total=1, ) try: @@ -287,21 +273,19 @@ def remove_distribution(self, key: str, progress: Progress) -> Distribution: dist_version = dist.version job = progress.add_task( - f"Removing [req]{key}[/] " f"[warning]{dist_version}[/]...", + f"Removing [req]{key}[/] [warning]{dist_version}[/]...", total=1, ) try: self.manager.uninstall(dist) except Exception: progress.live.console.print( - f" [error]{termui.Emoji.FAIL}[/] Remove [req]{key}[/] " - f"[warning]{dist_version}[/] failed", + f" [error]{termui.Emoji.FAIL}[/] Remove [req]{key}[/] [warning]{dist_version}[/] failed", ) raise else: progress.live.console.print( - f" [success]{termui.Emoji.SUCC}[/] Remove [req]{key}[/] " - f"[warning]{dist_version}[/] successful" + f" [success]{termui.Emoji.SUCC}[/] Remove [req]{key}[/] [warning]{dist_version}[/] successful" ) finally: progress.update(job, completed=1, visible=False) @@ -324,9 +308,7 @@ def _show_headline(self, packages: dict[str, list[str]]) -> None: def _show_summary(self, packages: dict[str, list[str]]) -> None: to_add = [self.candidates[key] for key in packages["add"]] - to_update = [ - (self.working_set[key], self.candidates[key]) for key in packages["update"] - ] + to_update = [(self.working_set[key], self.candidates[key]) for key in packages["update"]] to_remove = [self.working_set[key] for key in packages["remove"]] lines = [] if to_add: @@ -336,17 +318,11 @@ def _show_summary(self, packages: dict[str, list[str]]) -> None: if to_update: lines.append("[bold]Packages to update[/]:") for prev, cur in to_update: - lines.append( - f" - [req]{cur.name}[/] " - f"[warning]{prev.version}[/] -> [warning]{cur.version}[/]" - ) + lines.append(f" - [req]{cur.name}[/] [warning]{prev.version}[/] -> [warning]{cur.version}[/]") if to_remove: lines.append("[bold]Packages to remove[/]:") for dist in to_remove: - lines.append( - f" - [req]{dist.metadata['Name']}[/] " - f"[warning]{dist.version}[/]" - ) + lines.append(f" - [req]{dist.metadata['Name']}[/] [warning]{dist.version}[/]") if lines: self.ui.echo("\n".join(lines)) else: @@ -389,10 +365,7 @@ def update_progress(future: Future | DummyFuture, kind: str, key: str) -> None: exc_info = (type(error), error, error.__traceback__) termui.logger.exception("Error occurs: ", exc_info=exc_info) failed_jobs.append((kind, key)) - errors.extend( - [f"{kind} [success]{key}[/] failed:\n"] - + traceback.format_exception(*exc_info) - ) + errors.extend([f"{kind} [success]{key}[/] failed:\n", *traceback.format_exception(*exc_info)]) # get rich progress and live handler to deal with multiple spinners with self.ui.logging("install"), self.ui.make_progress( @@ -407,9 +380,7 @@ def update_progress(future: Future | DummyFuture, kind: str, key: str) -> None: with self.create_executor() as executor: for kind, key in parallel_jobs: future = executor.submit(handlers[kind], key, progress) - future.add_done_callback( - functools.partial(update_progress, kind=kind, key=key) - ) + future.add_done_callback(functools.partial(update_progress, kind=kind, key=key)) if not failed_jobs or i == self.retry_times: break parallel_jobs, failed_jobs = failed_jobs, [] diff --git a/src/pdm/installers/uninstallers.py b/src/pdm/installers/uninstallers.py index 5b54aaba4a..e997eb47ce 100644 --- a/src/pdm/installers/uninstallers.py +++ b/src/pdm/installers/uninstallers.py @@ -137,7 +137,7 @@ def from_dist(cls: type[_T], dist: Distribution, environment: Environment) -> _T """Create an instance from the distribution""" scheme = environment.get_paths() instance = cls(dist, environment) - meta_location = os.path.normcase(dist._path.absolute()) # type: ignore + meta_location = os.path.normcase(dist._path.absolute()) # type: ignore[attr-defined] dist_location = os.path.dirname(meta_location) if is_egg_link(dist): # pragma: no cover egg_link_path = cast("Path | None", getattr(dist, "link_file", None)) @@ -147,9 +147,7 @@ def from_dist(cls: type[_T], dist: Distribution, environment: Environment) -> _T dist.metadata["Name"], ) else: - link_pointer = os.path.normcase( - egg_link_path.open("rb").readline().decode().strip() - ) + link_pointer = os.path.normcase(egg_link_path.open("rb").readline().decode().strip()) if link_pointer != dist_location: raise UninstallError( f"The link pointer in {egg_link_path} doesn't match " @@ -207,9 +205,7 @@ class StashedRemovePaths(BaseRemovePaths): def __init__(self, dist: Distribution, environment: Environment) -> None: super().__init__(dist, environment) - self._pth_file = os.path.join( - self.environment.get_paths()["purelib"], self.PTH_REGISTRY - ) + self._pth_file = os.path.join(self.environment.get_paths()["purelib"], self.PTH_REGISTRY) self._saved_pth: bytes | None = None self._stashed: list[tuple[str, str]] = [] self._tempdirs: dict[str, TemporaryDirectory] = {} @@ -238,18 +234,14 @@ def _stash_files(self) -> None: if not os.path.exists(old_path): continue is_dir = os.path.isdir(old_path) and not os.path.islink(old_path) - termui.logger.debug( - "Removing %s %s", "directory" if is_dir else "file", old_path - ) + termui.logger.debug("Removing %s %s", "directory" if is_dir else "file", old_path) if old_path.endswith(".pyc"): # Don't stash cache files, remove them directly os.unlink(old_path) continue root = _get_file_root(old_path, prefix) if root is None: - termui.logger.debug( - "File path %s is not under packages root %s, skip", old_path, prefix - ) + termui.logger.debug("File path %s is not under packages root %s, skip", old_path, prefix) continue if root not in self._tempdirs: self._tempdirs[root] = TemporaryDirectory("-uninstall", "pdm-") diff --git a/src/pdm/models/auth.py b/src/pdm/models/auth.py index 3a02e08e18..e43dcd4bee 100644 --- a/src/pdm/models/auth.py +++ b/src/pdm/models/auth.py @@ -12,7 +12,7 @@ try: import keyring except ModuleNotFoundError: - keyring = None # type: ignore + keyring = None ui = UI() @@ -44,8 +44,7 @@ def _get_auth_from_index_url(self, netloc: str) -> tuple[MaybeAuth, str | None]: def _prompt_for_password(self, netloc: str) -> tuple[str | None, str | None, bool]: if not self._real_prompting: raise PdmException( - f"The credentials for {netloc} are not provided. " - "Please rerun the command with `-v` option." + f"The credentials for {netloc} are not provided. Please rerun the command with `-v` option." ) return super()._prompt_for_password(netloc) diff --git a/src/pdm/models/backends.py b/src/pdm/models/backends.py index 9f2d5f040a..dfb9c22ef4 100644 --- a/src/pdm/models/backends.py +++ b/src/pdm/models/backends.py @@ -46,9 +46,7 @@ def build_system(cls) -> dict: class PDMBackend(BuildBackend): def expand_line(self, req: str) -> str: - return expand_env_vars(req).replace( - "file:///${PROJECT_ROOT}", path_to_url(self.root.as_posix()) - ) + return expand_env_vars(req).replace("file:///${PROJECT_ROOT}", path_to_url(self.root.as_posix())) def relative_path_to_url(self, path: str) -> str: if os.path.isabs(path): @@ -93,9 +91,7 @@ def __format__(self, __format_spec: str) -> str: if name in os.environ: return os.environ[name] if not sep: - raise ValueError( - f"Nonexistent environment variable must set a default: {name}" - ) + raise ValueError(f"Nonexistent environment variable must set a default: {name}") return default diff --git a/src/pdm/models/caches.py b/src/pdm/models/caches.py index ece1e3e507..f096b6caa8 100644 --- a/src/pdm/models/caches.py +++ b/src/pdm/models/caches.py @@ -87,9 +87,7 @@ def _get_key(cls, obj: Candidate) -> str: # so use them for cache key. Local directories won't be cached. if not obj.name or not obj.version: raise KeyError("The package is missing a name or version") - extras = ( - "[{}]".format(",".join(sorted(obj.req.extras))) if obj.req.extras else "" - ) + extras = "[{}]".format(",".join(sorted(obj.req.extras))) if obj.req.extras else "" return f"{obj.name}{extras}-{obj.version}" @@ -174,9 +172,7 @@ class WheelCache: def __init__(self, directory: Path) -> None: self.directory = directory - def _get_candidates( - self, link: Link, target_python: TargetPython - ) -> Iterable[Path]: + def _get_candidates(self, link: Link, target_python: TargetPython) -> Iterable[Path]: path = self.get_path_for_link(link, target_python) if not path.exists(): return @@ -197,22 +193,16 @@ def get_path_for_link(self, link: Link, target_python: TargetPython) -> Path: if link.hash: hash_key[link.hash_name] = link.hash hashed = hashlib.sha224( - json.dumps( - hash_key, sort_keys=True, separators=(",", ":"), ensure_ascii=True - ).encode("utf-8") + json.dumps(hash_key, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8") ).hexdigest() parts = (hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]) return self.directory.joinpath(*parts) - def get( - self, link: Link, project_name: str | None, target_python: TargetPython - ) -> Path | None: + def get(self, link: Link, project_name: str | None, target_python: TargetPython) -> Path | None: if not project_name: return None canonical_name = canonicalize_name(project_name) - tags_priorities = { - tag: i for i, tag in enumerate(target_python.supported_tags()) - } + tags_priorities = {tag: i for i, tag in enumerate(target_python.supported_tags())} candidates: list[tuple[int, Path]] = [] for candidate in self._get_candidates(link, target_python): try: @@ -222,8 +212,7 @@ def get( continue if canonical_name != canonicalize_name(name): logger.debug( - "Ignoring cached wheel %s with invalid project name %s, " - "expected: %s", + "Ignoring cached wheel %s with invalid project name %s, expected: %s", candidate.name, name, canonical_name, @@ -231,9 +220,7 @@ def get( continue if tags.isdisjoint(tags_priorities): continue - support_min = min( - tags_priorities[tag] for tag in tags if tag in tags_priorities - ) + support_min = min(tags_priorities[tag] for tag in tags if tag in tags_priorities) candidates.append((support_min, candidate)) if not candidates: return None @@ -255,7 +242,7 @@ def _get_cache_path(self, name: str) -> str: # class for backwards-compatibility and to avoid using a non-public # method. hashed = FileCache.encode(name) - parts = list(hashed[:5]) + [hashed] + parts = [*list(hashed[:5]), hashed] return os.path.join(self.directory, *parts) def get(self, key: str) -> bytes | None: diff --git a/src/pdm/models/candidates.py b/src/pdm/models/candidates.py index 0ac8465cf5..bba16a353b 100644 --- a/src/pdm/models/candidates.py +++ b/src/pdm/models/candidates.py @@ -144,7 +144,7 @@ def __init__( self.name = name or self.req.project_name self.version = version if link is None and not req.is_named: - link = req.as_file_link() # type: ignore + link = req.as_file_link() # type: ignore[attr-defined] self.link = link self.summary = "" self.hashes: dict[Link, str] | None = None @@ -178,8 +178,8 @@ def __eq__(self, other: Any) -> bool: def get_revision(self) -> str: if not self.req.is_vcs: raise AttributeError("Non-VCS candidate doesn't have revision attribute") - if self.req.revision: # type: ignore - return self.req.revision # type: ignore + if self.req.revision: # type: ignore[attr-defined] + return self.req.revision # type: ignore[attr-defined] return self._prepared.revision if self._prepared else "unknown" def __repr__(self) -> str: @@ -194,9 +194,7 @@ def __str__(self) -> str: return f"{self.name}@{self.link.url_without_fragment}" @classmethod - def from_installation_candidate( - cls, candidate: Package, req: Requirement - ) -> Candidate: + def from_installation_candidate(cls, candidate: Package, req: Requirement) -> Candidate: """Build a candidate from unearth's find result.""" return cls( req, @@ -301,12 +299,13 @@ def revision(self) -> str: # pulled to local. In this case the link url must contain the full commit # hash which can be taken as the revision safely. # See more info at https://github.com/pdm-project/pdm/issues/349 - rev = get_rev_from_url(self.candidate.link.url) # type: ignore + rev = get_rev_from_url(self.candidate.link.url) # type: ignore[union-attr] if rev: return rev - return vcs_support.get_backend( - self.req.vcs, self.environment.project.core.ui.verbosity # type: ignore - ).get_revision(cast(Path, self._source_dir)) + assert isinstance(self.req, VcsRequirement) + return vcs_support.get_backend(self.req.vcs, self.environment.project.core.ui.verbosity).get_revision( + cast(Path, self._source_dir) + ) def direct_url(self) -> dict[str, Any] | None: """PEP 610 direct_url.json data""" @@ -349,11 +348,7 @@ def direct_url(self) -> dict[str, Any] | None: return _filter_none( { "url": self.link.url_without_fragment, - "archive_info": { - "hash": hash_cache.get_hash( - self.link, finder.session - ).replace(":", "=") - }, + "archive_info": {"hash": hash_cache.get_hash(self.link, finder.session).replace(":", "=")}, "subdirectory": req.subdirectory, } ) @@ -369,7 +364,7 @@ def build(self) -> Path: cached = self._get_cached_wheel() if cached: self.wheel = cached - return self.wheel # type: ignore + return self.wheel assert self._source_dir, "Source directory isn't ready yet" builder_cls = EditableBuilder if self.req.editable else WheelBuilder builder = builder_cls(str(self._unpacked_dir), self.environment) @@ -377,9 +372,7 @@ def build(self) -> Path: if not os.path.exists(build_dir): os.makedirs(build_dir) termui.logger.info("Running PEP 517 backend to build a wheel for %s", self.link) - self.wheel = Path( - builder.build(build_dir, metadata_directory=self._metadata_dir) - ) + self.wheel = Path(builder.build(build_dir, metadata_directory=self._metadata_dir)) return self.wheel def obtain(self, allow_all: bool = False) -> None: @@ -396,16 +389,9 @@ def obtain(self, allow_all: bool = False) -> None: if not allow_all and self.candidate.hashes: hash_options = convert_hashes(self.candidate.hashes) with self.environment.get_finder() as finder: - if ( - not self.link - or self.link.is_wheel - and not self._wheel_compatible(self.link.filename, allow_all) - ): + if not self.link or self.link.is_wheel and not self._wheel_compatible(self.link.filename, allow_all): if self.req.is_file_or_url: - raise CandidateNotFound( - f"The URL requirement {self.req.as_line()} is a wheel but " - "incompatible" - ) + raise CandidateNotFound(f"The URL requirement {self.req.as_line()} is a wheel but incompatible") self.link = self.wheel = None # reset the incompatible wheel self.link = _find_best_match_link( finder, @@ -415,8 +401,7 @@ def obtain(self, allow_all: bool = False) -> None: ) if not self.link: raise CandidateNotFound( - f"No candidate is found for `{self.req.project_name}` " - "that matches the environment or hashes" + f"No candidate is found for `{self.req.project_name}` that matches the environment or hashes" ) if not self.candidate.link: self.candidate.link = self.link @@ -431,9 +416,7 @@ def obtain(self, allow_all: bool = False) -> None: download_dir = build_dir else: download_dir = tmpdir - result = finder.download_and_unpack( - self.link, build_dir, download_dir, hash_options - ) + result = finder.download_and_unpack(self.link, build_dir, download_dir, hash_options) if self.link.is_wheel: self.wheel = result else: @@ -445,9 +428,7 @@ def prepare_metadata(self) -> im.Distribution: metadir_parent = create_tracked_tempdir(prefix="pdm-meta-") if self.wheel: # Get metadata from METADATA inside the wheel - self._metadata_dir = _get_wheel_metadata_from_wheel( - self.wheel, metadir_parent - ) + self._metadata_dir = _get_wheel_metadata_from_wheel(self.wheel, metadir_parent) return im.PathDistribution(Path(self._metadata_dir)) assert self._unpacked_dir, "Source directory isn't ready yet" @@ -488,9 +469,7 @@ def prepare_metadata(self) -> im.Distribution: ), extras_require={ k: list(map(backend.expand_line, v)) - for k, v in metadata.get( - "optional-dependencies", {} - ).items() + for k, v in metadata.get("optional-dependencies", {}).items() }, python_requires=metadata.get("requires-python"), ) @@ -498,20 +477,14 @@ def prepare_metadata(self) -> im.Distribution: # If all fail, try building the source to get the metadata builder = EditableBuilder if self.req.editable else WheelBuilder try: - termui.logger.info( - "Running PEP 517 backend to get metadata for %s", self.link - ) - self._metadata_dir = builder( - self._unpacked_dir, self.environment - ).prepare_metadata(metadir_parent) + termui.logger.info("Running PEP 517 backend to get metadata for %s", self.link) + self._metadata_dir = builder(self._unpacked_dir, self.environment).prepare_metadata(metadir_parent) except BuildError: termui.logger.warn("Failed to build package, try parsing project files.") try: setup = Setup.from_directory(self._unpacked_dir) except Exception: - message = ( - "Failed to parse the project files, dependencies may be missing" - ) + message = "Failed to parse the project files, dependencies may be missing" termui.logger.warn(message) warnings.warn(message, RuntimeWarning) setup = Setup() @@ -528,9 +501,7 @@ def metadata(self) -> im.Distribution: if not self.candidate.version: self.candidate.version = result.version if not self.candidate.requires_python: - self.candidate.requires_python = cast( - str, result.metadata["Requires-Python"] or "" - ) + self.candidate.requires_python = cast(str, result.metadata["Requires-Python"] or "") self._metadata = result return self._metadata @@ -538,7 +509,7 @@ def get_dependencies_from_metadata(self) -> list[str]: """Get the dependencies of a candidate from metadata.""" extras = self.req.extras or () return filter_requirements_with_extras( - self.req.project_name, self.metadata.requires or [], extras # type: ignore + self.req.project_name, self.metadata.requires or [], extras # type: ignore[arg-type] ) def should_cache(self) -> bool: @@ -553,9 +524,7 @@ def should_cache(self) -> bool: # If the candidate isn't prepared, we can't cache it return False assert link - vcs_backend = vcs_support.get_backend( - link.vcs, self.environment.project.core.ui.verbosity - ) + vcs_backend = vcs_support.get_backend(link.vcs, self.environment.project.core.ui.verbosity) return vcs_backend.is_immutable_revision(source_dir, link) if link and not (link.is_file and link.file_path.is_dir()): # Cache if the link contains egg-info like 'foo-1.0' @@ -565,9 +534,7 @@ def should_cache(self) -> bool: def _get_cached_wheel(self) -> Path | None: wheel_cache = self.environment.project.make_wheel_cache() assert self.candidate.link - cache_entry = wheel_cache.get( - self.candidate.link, self.candidate.name, self.environment.target_python - ) + cache_entry = wheel_cache.get(self.candidate.link, self.candidate.name, self.environment.target_python) if cache_entry is not None: termui.logger.info("Using cached wheel: %s", cache_entry) return cache_entry @@ -584,9 +551,7 @@ def _get_build_dir(self) -> str: if self.environment.packages_path: src_dir = self.environment.packages_path / "src" else: - venv_prefix = get_venv_like_prefix( - self.environment.interpreter.executable - ) + venv_prefix = get_venv_like_prefix(self.environment.interpreter.executable) if venv_prefix is not None: src_dir = venv_prefix / "src" else: @@ -611,9 +576,7 @@ def _get_wheel_dir(self) -> str: if self.should_cache(): termui.logger.info("Saving wheel to cache: %s", self.candidate.link) wheel_cache = self.environment.project.make_wheel_cache() - return wheel_cache.get_path_for_link( - self.candidate.link, self.environment.target_python - ).as_posix() + return wheel_cache.get_path_for_link(self.candidate.link, self.environment.target_python).as_posix() else: return create_tracked_tempdir(prefix="pdm-wheel-") diff --git a/src/pdm/models/environment.py b/src/pdm/models/environment.py index 2ca3e3d522..74f458a8af 100644 --- a/src/pdm/models/environment.py +++ b/src/pdm/models/environment.py @@ -102,11 +102,7 @@ def get_paths(self) -> dict[str, str]: @cached_property def packages_path(self) -> Path: """The local packages path.""" - pypackages = ( - self.project.root # type: ignore - / "__pypackages__" - / self.interpreter.identifier - ) + pypackages = self.project.root / "__pypackages__" / self.interpreter.identifier if not pypackages.exists() and "-32" in pypackages.name: compatible_packages = pypackages.with_name(pypackages.name[:-3]) if compatible_packages.exists(): @@ -132,9 +128,7 @@ def target_python(self) -> unearth.TargetPython: python_abi_tag = get_python_abi_tag(str(self.interpreter.executable)) return unearth.TargetPython(python_version, [python_abi_tag]) - def _build_session( - self, index_urls: list[str], trusted_hosts: list[str] - ) -> PDMSession: + def _build_session(self, index_urls: list[str], trusted_hosts: list[str]) -> PDMSession: ca_certs = self.project.config.get("pypi.ca_certs") session = PDMSession( cache_dir=self.project.cache("http"), @@ -182,9 +176,9 @@ def get_finder( ignore_compatibility=ignore_compatibility, no_binary=os.getenv("PDM_NO_BINARY", "").split(","), only_binary=os.getenv("PDM_ONLY_BINARY", "").split(","), - respect_source_order=self.project.pyproject.settings.get( - "resolution", {} - ).get("respect-source-order", False), + respect_source_order=self.project.pyproject.settings.get("resolution", {}).get( + "respect-source-order", False + ), verbosity=self.project.core.ui.verbosity, ) try: @@ -234,9 +228,7 @@ def _download_pip_wheel(self, path: str | Path) -> None: raise download_error with tempfile.TemporaryDirectory(prefix="pip-download-") as dirname: try: - downloaded = finder.download_and_unpack( - best_match.link, dirname, dirname - ) + downloaded = finder.download_and_unpack(best_match.link, dirname, dirname) except unearth.UnpackError as e: raise download_error from e shutil.move(str(downloaded), path) @@ -249,13 +241,11 @@ def pip_command(self) -> list[str]: try: from pip import __file__ as pip_location except ImportError: - pip_location = None # type: ignore + pip_location = None # type: ignore[assignment] python_version = self.interpreter.version executable = str(self.interpreter.executable) - proc = subprocess.run( - [executable, "-Esm", "pip", "--version"], capture_output=True - ) + proc = subprocess.run([executable, "-Esm", "pip", "--version"], capture_output=True) if proc.returncode == 0: # The pip has already been installed with the executable, just use it command = [executable, "-Esm", "pip"] @@ -283,9 +273,7 @@ def get_paths(self) -> dict[str, str]: is_venv = bool(get_venv_like_prefix(self.interpreter.executable)) paths = get_sys_config_paths( str(self.interpreter.executable), - kind="user" - if not is_venv and self.project.global_config["global_project.user_site"] - else "default", + kind="user" if not is_venv and self.project.global_config["global_project.user_site"] else "default", ) if is_venv: python_xy = f"python{self.interpreter.identifier}" @@ -295,7 +283,7 @@ def get_paths(self) -> dict[str, str]: return paths @property - def packages_path(self) -> Path | None: # type: ignore + def packages_path(self) -> Path | None: # type: ignore[override] return None @@ -307,7 +295,7 @@ def __init__(self, project: Project, prefix: str) -> None: self.prefix = prefix @property - def packages_path(self) -> Path | None: # type: ignore + def packages_path(self) -> Path | None: # type: ignore[override] return None def get_paths(self) -> dict[str, str]: diff --git a/src/pdm/models/in_process/__init__.py b/src/pdm/models/in_process/__init__.py index 7ef04a4a2d..7dd5a6aa2a 100644 --- a/src/pdm/models/in_process/__init__.py +++ b/src/pdm/models/in_process/__init__.py @@ -25,9 +25,7 @@ def get_python_abi_tag(executable: str) -> str: return json.loads(subprocess.check_output(args=[executable, "-Es", script])) -def get_sys_config_paths( - executable: str, vars: dict[str, str] | None = None, kind: str = "default" -) -> dict[str, str]: +def get_sys_config_paths(executable: str, vars: dict[str, str] | None = None, kind: str = "default") -> dict[str, str]: """Return the sys_config.get_paths() result for the python interpreter""" env = os.environ.copy() env.pop("__PYVENV_LAUNCHER__", None) diff --git a/src/pdm/models/in_process/get_abi_tag.py b/src/pdm/models/in_process/get_abi_tag.py index 403d2d486d..3c64cf2865 100644 --- a/src/pdm/models/in_process/get_abi_tag.py +++ b/src/pdm/models/in_process/get_abi_tag.py @@ -17,7 +17,7 @@ def get_abbr_impl(): """Returns the name of the running interpreter.""" try: - name = sys.implementation.name # type: ignore + name = sys.implementation.name except AttributeError: # pragma: no cover # Python 2.7 compatibility. name = platform.python_implementation().lower() @@ -32,8 +32,7 @@ def get_flag(var, fallback, expected=True, warn=True): if val is None: if warn: warnings.warn( - "Config variable '{}' is unset, Python ABI tag may " - "be incorrect".format(var), + "Config variable '{}' is unset, Python ABI tag may be incorrect".format(var), RuntimeWarning, 2, ) @@ -54,9 +53,7 @@ def get_flag(var, fallback, expected=True, warn=True): is_cpython = impl == "cp" if get_flag("Py_DEBUG", hasattr(sys, "gettotalrefcount"), warn=False): d = "d" - if python_version < (3, 8) and get_flag( - "WITH_PYMALLOC", is_cpython, warn=False - ): + if python_version < (3, 8) and get_flag("WITH_PYMALLOC", is_cpython, warn=False): m = "m" if python_version < (3, 3) and get_flag( "Py_UNICODE_SIZE", diff --git a/src/pdm/models/in_process/parse_setup.py b/src/pdm/models/in_process/parse_setup.py index a36d0c206f..7e46cba9fd 100644 --- a/src/pdm/models/in_process/parse_setup.py +++ b/src/pdm/models/in_process/parse_setup.py @@ -51,8 +51,7 @@ def _parse_setup_cfg(path: str) -> Dict[str, Any]: if "project_urls" in metadata: result["project_urls"] = dict( - [u.strip() for u in url.split("=", 1)] - for url in metadata["project_urls"].strip().splitlines() + [u.strip() for u in url.split("=", 1)] for url in metadata["project_urls"].strip().splitlines() ) if "long_description" in metadata: @@ -67,14 +66,11 @@ def _parse_setup_cfg(path: str) -> Dict[str, Any]: result["python_requires"] = options["python_requires"] if "install_requires" in options: - result["install_requires"] = ( - options["install_requires"].strip().splitlines() - ) + result["install_requires"] = options["install_requires"].strip().splitlines() if "package_dir" in options: result["package_dir"] = dict( - [p.strip() for p in d.split("=", 1)] - for d in options["package_dir"].strip().splitlines() + [p.strip() for p in d.split("=", 1)] for d in options["package_dir"].strip().splitlines() ) if setup_cfg.has_section("options.extras_require"): @@ -159,9 +155,7 @@ def clean_metadata(metadata: Dict[str, Any]) -> None: for entry_point, definitions in metadata["entry_points"].items(): if isinstance(definitions, str): definitions = [definitions] - definitions = dict( - sorted(d.replace(" ", "").split("=", 1) for d in definitions) - ) + definitions = dict(sorted(d.replace(" ", "").split("=", 1) for d in definitions)) entry_points[entry_point] = definitions if entry_points: diff --git a/src/pdm/models/in_process/sysconfig_get_paths.py b/src/pdm/models/in_process/sysconfig_get_paths.py index b5b56fec21..e91fed11e5 100644 --- a/src/pdm/models/in_process/sysconfig_get_paths.py +++ b/src/pdm/models/in_process/sysconfig_get_paths.py @@ -33,17 +33,10 @@ def get_paths(kind="default", vars=None): if kind == "user" and not running_under_virtualenv(): scheme = _get_user_scheme() if scheme not in scheme_names: - raise ValueError( - "{} is not a valid scheme on the system, " - "or user site may be disabled.".format(scheme) - ) + raise ValueError("{} is not a valid scheme on the system, or user site may be disabled.".format(scheme)) return sysconfig.get_paths(scheme, vars=vars) else: - if ( - sys.platform == "darwin" - and "osx_framework_library" in scheme_names - and kind == "prefix" - ): + if sys.platform == "darwin" and "osx_framework_library" in scheme_names and kind == "prefix": return sysconfig.get_paths("posix_prefix", vars=vars) return sysconfig.get_paths(vars=vars) diff --git a/src/pdm/models/markers.py b/src/pdm/models/markers.py index 0e67c22a61..2be4a01a87 100644 --- a/src/pdm/models/markers.py +++ b/src/pdm/models/markers.py @@ -31,16 +31,8 @@ def split_pyspec(self) -> tuple[Marker | None, PySpecSet]: return None, _build_pyspec_from_marker(self._markers) if "or" in self._markers: return self.copy(), PySpecSet() - py_markers = [ - marker - for marker in self._markers - if marker != "and" and _only_contains_python_keys(marker) - ] - rest = [ - marker - for marker in self._markers - if marker != "and" and marker not in py_markers - ] + py_markers = [marker for marker in self._markers if marker != "and" and _only_contains_python_keys(marker)] + rest = [marker for marker in self._markers if marker != "and" and marker not in py_markers] new_markers = join_list_with(rest, "and") if not new_markers: marker = None @@ -149,13 +141,9 @@ def split_version(version: str) -> list[str]: elif op in ("in", "not in"): version = " ".join(v + ".*" for v in split_version(version)) if op == "in": - pyspec = reduce( - operator.or_, (PySpecSet(f"=={v}") for v in split_version(version)) - ) + pyspec = reduce(operator.or_, (PySpecSet(f"=={v}") for v in split_version(version))) elif op == "not in": - pyspec = reduce( - operator.and_, (PySpecSet(f"!={v}") for v in split_version(version)) - ) + pyspec = reduce(operator.and_, (PySpecSet(f"!={v}") for v in split_version(version))) else: pyspec = PySpecSet(f"{op}{version}") groups[-1] = groups[-1] & pyspec diff --git a/src/pdm/models/project_info.py b/src/pdm/models/project_info.py index d84d4c3827..2567ba42ca 100644 --- a/src/pdm/models/project_info.py +++ b/src/pdm/models/project_info.py @@ -21,10 +21,7 @@ def _parse(self, data: Distribution) -> dict[str, Any]: if "Project-URL" in metadata: project_urls = { - k.strip(): v.strip() - for k, v in ( - row.split(",") for row in metadata.get_all("Project-URL", []) - ) + k.strip(): v.strip() for k, v in (row.split(",") for row in metadata.get_all("Project-URL", [])) } else: project_urls = {} diff --git a/src/pdm/models/repositories.py b/src/pdm/models/repositories.py index 52bbf459eb..5f3bdf1775 100644 --- a/src/pdm/models/repositories.py +++ b/src/pdm/models/repositories.py @@ -34,9 +34,7 @@ T = TypeVar("T", bound="BaseRepository") -def cache_result( - func: Callable[[T, Candidate], CandidateInfo] -) -> Callable[[T, Candidate], CandidateInfo]: +def cache_result(func: Callable[[T, Candidate], CandidateInfo]) -> Callable[[T, Candidate], CandidateInfo]: @wraps(func) def wrapper(self: T, candidate: Candidate) -> CandidateInfo: result = func(self, candidate) @@ -73,9 +71,7 @@ def get_filtered_sources(self, req: Requirement) -> list[Source]: """Get matching sources based on the index attribute.""" return self.sources - def get_dependencies( - self, candidate: Candidate - ) -> tuple[list[Requirement], PySpecSet, str]: + def get_dependencies(self, candidate: Candidate) -> tuple[list[Requirement], PySpecSet, str]: """Get (dependencies, python_specifier, summary) of the candidate.""" requires_python, summary = "", "" requirements: list[str] = [] @@ -89,7 +85,7 @@ def get_dependencies( break else: if last_ext_info is not None: - raise last_ext_info[1].with_traceback(last_ext_info[2]) # type: ignore + raise last_ext_info[1].with_traceback(last_ext_info[2]) # type: ignore[union-attr] reqs: list[Requirement] = [] for line in requirements: if line.startswith("-e "): @@ -111,9 +107,7 @@ def get_dependencies( candidate.summary = summary if not self.ignore_compatibility: pep508_env = self.environment.marker_environment - reqs = [ - req for req in reqs if not req.marker or req.marker.evaluate(pep508_env) - ] + reqs = [req for req in reqs if not req.marker or req.marker.evaluate(pep508_env)] return reqs, PySpecSet(requires_python), summary def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: @@ -122,11 +116,7 @@ def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: def is_this_package(self, requirement: Requirement) -> bool: """Whether the requirement is the same as this package""" project = self.environment.project - return ( - requirement.is_named - and project.name is not None - and requirement.key == normalize_name(project.name) - ) + return requirement.is_named and project.name is not None and requirement.key == normalize_name(project.name) def make_this_candidate(self, requirement: Requirement) -> Candidate: """Make a candidate for this package. @@ -134,7 +124,7 @@ def make_this_candidate(self, requirement: Requirement) -> Candidate: """ project = self.environment.project assert project.name - link = Link.from_path(project.root) # type: ignore + link = Link.from_path(project.root) candidate = make_candidate(requirement, project.name, link=link) candidate.prepare(self.environment).metadata return candidate @@ -158,15 +148,11 @@ def find_candidates( applicable_cans = [ c for c in cans - if requirement.specifier.contains( # type: ignore - c.version, allow_prereleases # type: ignore - ) + if requirement.specifier.contains(c.version, allow_prereleases) # type: ignore[arg-type, union-attr] ] applicable_cans_python_compatible = [ - c - for c in applicable_cans - if ignore_requires_python or requires_python.is_subset(c.requires_python) + c for c in applicable_cans if ignore_requires_python or requires_python.is_subset(c.requires_python) ] # Evaluate data-requires-python attr and discard incompatible candidates # to reduce the number of candidates to resolve. @@ -179,28 +165,20 @@ def find_candidates( if not applicable_cans and allow_prereleases is None: # No non-pre-releases is found, force pre-releases now applicable_cans = [ - c - for c in cans - if requirement.specifier.contains(c.version, True) # type: ignore + c for c in cans if requirement.specifier.contains(c.version, True) # type: ignore[arg-type, union-attr] ] applicable_cans_python_compatible = [ - c - for c in applicable_cans - if ignore_requires_python - or requires_python.is_subset(c.requires_python) + c for c in applicable_cans if ignore_requires_python or requires_python.is_subset(c.requires_python) ] if applicable_cans_python_compatible: applicable_cans = applicable_cans_python_compatible if not applicable_cans: termui.logger.debug( - "\tCould not find any matching candidates even when considering " - "pre-releases.", + "\tCould not find any matching candidates even when considering pre-releases.", ) - def print_candidates( - title: str, candidates: list[Candidate], max_lines: int = 10 - ) -> None: + def print_candidates(title: str, candidates: list[Candidate], max_lines: int = 10) -> None: termui.logger.debug("\t" + title) logged_lines = set() for can in candidates: @@ -208,9 +186,7 @@ def print_candidates( if new_line not in logged_lines: logged_lines.add(new_line) if len(logged_lines) > max_lines: - termui.logger.debug( - f"\t ... [{len(candidates)-max_lines} more candidate(s)]" - ) + termui.logger.debug(f"\t ... [{len(candidates)-max_lines} more candidate(s)]") break else: termui.logger.debug(new_line) @@ -244,7 +220,7 @@ def get_hashes(self, candidate: Candidate) -> dict[Link, str] | None: if ( candidate.req.is_vcs or candidate.req.is_file_or_url - and candidate.req.is_local_dir # type: ignore + and candidate.req.is_local_dir # type: ignore[attr-defined] ): return None if candidate.hashes: @@ -267,9 +243,7 @@ def get_hashes(self, candidate: Candidate) -> dict[Link, str] | None: and prepared_link.file_path.is_dir() ): continue - result[c.link] = self._hash_cache.get_hash( - prepared_link, finder.session - ) + result[c.link] = self._hash_cache.get_hash(prepared_link, finder.session) return result or None def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateInfo]]: @@ -300,10 +274,7 @@ def _get_dependencies_from_json(self, candidate: Candidate) -> CandidateInfo: sources = self.get_filtered_sources(candidate.req) url_prefixes = [ proc_url[:-7] # Strip "/simple". - for proc_url in ( - raw_url.rstrip("/") - for raw_url in (source.get("url", "") for source in sources) - ) + for proc_url in (raw_url.rstrip("/") for raw_url in (source.get("url", "") for source in sources)) if proc_url.endswith("/simple") ] with self.environment.get_finder(sources) as finder: @@ -342,9 +313,7 @@ def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: with self.environment.get_finder(sources, self.ignore_compatibility) as finder: cans = [ Candidate.from_installation_candidate(c, requirement) - for c in finder.find_all_packages( - requirement.project_name, allow_yanked=requirement.is_pinned - ) + for c in finder.find_all_packages(requirement.project_name, allow_yanked=requirement.is_pinned) ] if not cans: raise CandidateNotFound( @@ -362,7 +331,7 @@ def search(self, query: str) -> SearchResult: search_url = pypi_simple + "/search" with self.environment.get_finder() as finder: - session = finder.session # type: ignore + session = finder.session resp = session.get(search_url, params={"q": query}) if resp.status_code == 404: self.environment.project.core.ui.echo( @@ -372,9 +341,7 @@ def search(self, query: str) -> SearchResult: err=True, style="warning", ) - resp = session.get( - f"{self.DEFAULT_INDEX_URL}/search", params={"q": query} - ) + resp = session.get(f"{self.DEFAULT_INDEX_URL}/search", params={"q": query}) parser = SearchResultParser() resp.raise_for_status() parser.feed(resp.text) @@ -406,16 +373,10 @@ def _read_lockfile(self, lockfile: Mapping[str, Any]) -> None: if version: package["version"] = f"=={version}" package_name = package.pop("name") - req_dict = { - k: v - for k, v in package.items() - if k not in ("dependencies", "requires_python", "summary") - } + req_dict = {k: v for k, v in package.items() if k not in ("dependencies", "requires_python", "summary")} req = Requirement.from_req_dict(package_name, req_dict) - if req.is_file_or_url and req.path and not req.url: # type: ignore - req.url = path_to_url( # type: ignore - posixpath.join(root, req.path) # type: ignore - ) + if req.is_file_or_url and req.path and not req.url: # type: ignore[attr-defined] + req.url = path_to_url(posixpath.join(root, req.path)) # type: ignore[attr-defined] can = make_candidate(req, name=package_name, version=version) can_id = self._identify_candidate(can) self.packages[can_id] = can @@ -427,7 +388,7 @@ def _read_lockfile(self, lockfile: Mapping[str, Any]) -> None: self.candidate_info[can_id] = candidate_info for key, hashes in lockfile.get("metadata", {}).get("files", {}).items(): - self.file_hashes[tuple(key.split(None, 1))] = { # type: ignore + self.file_hashes[tuple(key.split(None, 1))] = { # type: ignore[index] Link(item["url"]): item["hash"] for item in hashes if "url" in item } @@ -456,9 +417,7 @@ def _get_dependency_from_local_package(self, candidate: Candidate) -> CandidateI raise CandidateInfoNotFound(candidate) from None reqs = self.environment.project.pyproject.metadata.get("dependencies", []) - optional_dependencies = self.environment.project.pyproject.metadata.get( - "optional-dependencies", {} - ) + optional_dependencies = self.environment.project.pyproject.metadata.get("optional-dependencies", {}) if candidate.req.extras is not None: reqs = sum( (optional_dependencies.get(g, []) for g in candidate.req.extras), @@ -483,13 +442,11 @@ def _matching_keys(self, requirement: Requirement) -> Iterable[tuple]: if key[0] != requirement.identify(): continue elif key[2] is not None: - if key[2] != url_without_fragments( - getattr(requirement, "url", "") # type: ignore - ): + if key[2] != url_without_fragments(getattr(requirement, "url", "")): continue else: can_req = self.packages[key].req - if can_req.path != getattr(requirement, "path", None): # type: ignore + if can_req.path != getattr(requirement, "path", None): # type: ignore[attr-defined] continue yield key @@ -507,9 +464,7 @@ def find_candidates( return for key in self._matching_keys(requirement): info = self.candidate_info[key] - if not PySpecSet(info[1]).contains( - str(self.environment.interpreter.version), True - ): + if not PySpecSet(info[1]).contains(str(self.environment.interpreter.version), True): continue can = self.packages[key] can.requires_python = info[1] @@ -521,6 +476,4 @@ def find_candidates( def get_hashes(self, candidate: Candidate) -> dict[Link, str] | None: assert candidate.name - return self.file_hashes.get( - (normalize_name(candidate.name), candidate.version or "") - ) + return self.file_hashes.get((normalize_name(candidate.name), candidate.version or "")) diff --git a/src/pdm/models/requirements.py b/src/pdm/models/requirements.py index 77206405dc..1c3d5bda71 100644 --- a/src/pdm/models/requirements.py +++ b/src/pdm/models/requirements.py @@ -87,9 +87,7 @@ class Requirement: prerelease: bool = False def __post_init__(self) -> None: - self.requires_python = ( - self.marker.split_pyspec()[1] if self.marker else PySpecSet() - ) + self.requires_python = self.marker.split_pyspec()[1] if self.marker else PySpecSet() @property def project_name(self) -> str | None: @@ -150,19 +148,11 @@ def create(cls: type[T], **kwargs: Any) -> T: except InvalidMarker as e: raise RequirementError("Invalid marker: %s" % str(e)) from None if "extras" in kwargs and isinstance(kwargs["extras"], str): - kwargs["extras"] = tuple( - e.strip() for e in kwargs["extras"][1:-1].split(",") - ) + kwargs["extras"] = tuple(e.strip() for e in kwargs["extras"][1:-1].split(",")) version = kwargs.pop("version", None) if version: kwargs["specifier"] = get_specifier(version) - return cls( - **{ - k: v - for k, v in kwargs.items() - if k in inspect.signature(cls).parameters - } - ) + return cls(**{k: v for k, v in kwargs.items() if k in inspect.signature(cls).parameters}) @classmethod def from_dist(cls, dist: Distribution) -> Requirement: @@ -184,9 +174,7 @@ def from_dist(cls, dist: Distribution) -> Requirement: ) return VcsRequirement.create(**data) return FileRequirement.create(**data) - return NamedRequirement.create( - name=dist.metadata["Name"], version=f"=={dist.version}" - ) + return NamedRequirement.create(name=dist.metadata["Name"], version=f"=={dist.version}") @classmethod def from_req_dict(cls, name: str, req_dict: RequirementDict) -> Requirement: @@ -237,9 +225,9 @@ def from_pkg_requirement(cls, req: PackageRequirement) -> Requirement: if getattr(req, "url", None): link = Link(cast(str, req.url)) klass = VcsRequirement if link.is_vcs else FileRequirement - return klass(url=req.url, **kwargs) # type: ignore + return klass(url=req.url, **kwargs) else: - return NamedRequirement(**kwargs) # type: ignore + return NamedRequirement(**kwargs) # type: ignore[arg-type] def _format_marker(self) -> str: if self.marker: @@ -267,13 +255,13 @@ def __post_init__(self) -> None: self._check_installable() def _hash_key(self) -> tuple: - return super()._hash_key() + (self.get_full_url(), self.editable) + return (*super()._hash_key(), self.get_full_url(), self.editable) @classmethod def create(cls: type[T], **kwargs: Any) -> T: if kwargs.get("path"): kwargs["path"] = Path(kwargs["path"]) - return super().create(**kwargs) # type: ignore + return super().create(**kwargs) @property def str_path(self) -> str | None: @@ -336,11 +324,7 @@ def get_full_url(self) -> str: def as_line(self) -> str: project_name = f"{self.project_name}" if self.project_name else "" - extras = ( - f"[{','.join(sorted(self.extras))}]" - if self.extras and self.project_name - else "" - ) + extras = f"[{','.join(sorted(self.extras))}]" if self.extras and self.project_name else "" marker = self._format_marker() if marker: marker = f" {marker}" @@ -367,9 +351,7 @@ def _parse_name_from_url(self) -> None: 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)) - ) + filename = os.path.basename(urlparse.unquote(url_without_fragments(self.url))) if filename.endswith(".whl"): self.name, *_ = parse_wheel_filename(filename) else: @@ -385,10 +367,7 @@ def _parse_name_from_url(self) -> None: def _check_installable(self) -> None: assert self.path - if not ( - self.path.joinpath("setup.py").exists() - or self.path.joinpath("pyproject.toml").exists() - ): + if not (self.path.joinpath("setup.py").exists() or self.path.joinpath("pyproject.toml").exists()): raise RequirementError(f"The local path '{self.path}' is not installable.") result = Setup.from_directory(self.path.absolute()) if result.name: @@ -430,7 +409,7 @@ def _parse_url(self) -> None: path, ref = parsed.path.split("@", 1) repo = urlparse.urlunparse(parsed._replace(path=path, fragment="")) self.url = f"{vcs}+{repo}" - self.repo, self.ref = repo, ref # type: ignore + self.repo, self.ref = repo, ref def filter_requirements_with_extras( @@ -454,12 +433,7 @@ def filter_requirements_with_extras( _r.marker = Marker(rest) if rest else None else: req_extras = set() - if ( - req_extras - and not req_extras.isdisjoint(extras) - or not req_extras - and (include_default or not extras) - ): + if req_extras and not req_extras.isdisjoint(extras) or not req_extras and (include_default or not extras): result.append(_r.as_line()) extras_not_found = [e for e in extras if e not in extras_in_meta] @@ -527,12 +501,9 @@ def parse_requirement(line: str, editable: bool = False) -> Requirement: r.path = Path(get_relative_path(r.url) or "") if editable: - if r.is_vcs or r.is_file_or_url and r.is_local_dir: # type: ignore + if r.is_vcs or r.is_file_or_url and r.is_local_dir: # type: ignore[attr-defined] assert isinstance(r, FileRequirement) r.editable = True else: - raise RequirementError( - "Editable requirement is only supported for VCS link" - " or local directory." - ) + raise RequirementError("Editable requirement is only supported for VCS link or local directory.") return r diff --git a/src/pdm/models/search.py b/src/pdm/models/search.py index 0a9e401cd2..2df82602f8 100644 --- a/src/pdm/models/search.py +++ b/src/pdm/models/search.py @@ -42,15 +42,9 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None if tag == "span" and self._match_class(attrs, "package-snippet__name"): self._data_callback = functools.partial(setattr, self._current, "name") elif tag == "span" and self._match_class(attrs, "package-snippet__version"): - self._data_callback = functools.partial( - setattr, self._current, "version" - ) - elif tag == "p" and self._match_class( - attrs, "package-snippet__description" - ): - self._data_callback = functools.partial( - setattr, self._current, "description" - ) + self._data_callback = functools.partial(setattr, self._current, "version") + elif tag == "p" and self._match_class(attrs, "package-snippet__description"): + self._data_callback = functools.partial(setattr, self._current, "description") elif tag == "a": self._nest_anchors += 1 diff --git a/src/pdm/models/session.py b/src/pdm/models/session.py index a970f40b9c..92fa7d14fc 100644 --- a/src/pdm/models/session.py +++ b/src/pdm/models/session.py @@ -19,18 +19,12 @@ def __init__(self, *, cache_dir: Path, **kwargs: Any) -> None: cache = SafeFileCache(str(cache_dir)) self.secure_adapter_cls = functools.partial(CacheControlAdapter, cache=cache) - self.insecure_adapter_cls = functools.partial( - InsecureCacheControlAdapter, cache=cache - ) + self.insecure_adapter_cls = functools.partial(InsecureCacheControlAdapter, cache=cache) super().__init__(**kwargs) self.headers["User-Agent"] = self._make_user_agent() def _make_user_agent(self) -> str: - return ( - user_agent.UserAgentBuilder("pdm", __version__) - .include_implementation() - .build() - ) + return user_agent.UserAgentBuilder("pdm", __version__).include_implementation().build() # HACK: make the sessions identical to functools.lru_cache # so that the same index page won't be fetched twice. @@ -39,7 +33,4 @@ def __hash__(self) -> int: return hash(self.headers["User-Agent"]) def __eq__(self, __o: Any) -> bool: - return ( - isinstance(__o, PDMSession) - and self.headers["User-Agent"] == __o.headers["User-Agent"] - ) + return isinstance(__o, PDMSession) and self.headers["User-Agent"] == __o.headers["User-Agent"] diff --git a/src/pdm/models/setup.py b/src/pdm/models/setup.py index 940dea3b39..0450ea14cf 100644 --- a/src/pdm/models/setup.py +++ b/src/pdm/models/setup.py @@ -1,9 +1,9 @@ from __future__ import annotations import ast +import os from configparser import ConfigParser from dataclasses import asdict, dataclass, field, fields -import os from pathlib import Path from typing import Any, Iterable, no_type_check @@ -99,9 +99,7 @@ def read_setup_py(cls, file: Path) -> Setup: version=cls._find_single_string(setup_call, body, "version") or "0.0.0", install_requires=cls._find_install_requires(setup_call, body), extras_require=cls._find_extras_require(setup_call, body), - python_requires=cls._find_single_string( - setup_call, body, "python_requires" - ), + python_requires=cls._find_single_string(setup_call, body, "python_requires"), ) @staticmethod @@ -155,9 +153,7 @@ def read_setup_cfg(file: Path) -> Setup: ) @classmethod - def _find_setup_call( - cls, elements: list[Any] - ) -> tuple[ast.Call | None, list[Any | None]]: + def _find_setup_call(cls, elements: list[Any]) -> tuple[ast.Call | None, list[Any | None]]: funcdefs = [] for i, element in enumerate(elements): if isinstance(element, ast.If) and i == len(elements) - 1: @@ -206,9 +202,7 @@ def _find_setup_call( @no_type_check @classmethod - def _find_sub_setup_call( - cls, elements: list[Any] - ) -> tuple[ast.Call | None, list[Any | None]]: + def _find_sub_setup_call(cls, elements: list[Any]) -> tuple[ast.Call | None, list[Any | None]]: for element in elements: if not isinstance(element, (ast.FunctionDef, ast.If)): continue @@ -267,9 +261,7 @@ def _find_install_requires(cls, call: ast.Call, body: Iterable[Any]) -> list[str @no_type_check @classmethod - def _find_extras_require( - cls, call: ast.Call, body: Iterable[Any] - ) -> dict[str, list[str]]: + def _find_extras_require(cls, call: ast.Call, body: Iterable[Any]) -> dict[str, list[str]]: extras_require: dict[str, list[str]] = {} value = cls._find_in_call(call, "extras_require") if value is None: @@ -320,9 +312,7 @@ def _find_extras_require( return extras_require @classmethod - def _find_single_string( - cls, call: ast.Call, body: list[Any], name: str - ) -> str | None: + def _find_single_string(cls, call: ast.Call, body: list[Any], name: str) -> str | None: value = cls._find_in_call(call, name) if value is None: # Trying to find in kwargs @@ -408,7 +398,7 @@ def locate_file(self, path: str | os.PathLike[str]) -> os.PathLike[str]: return Path() @property - def metadata(self) -> dict[str, Any]: # type: ignore + def metadata(self) -> dict[str, Any]: # type: ignore[override] return { "Name": self._data.name, "Version": self._data.version, @@ -418,8 +408,8 @@ def metadata(self) -> dict[str, Any]: # type: ignore @property def requires(self) -> list[str] | None: - from pdm.models.requirements import parse_requirement from pdm.models.markers import Marker + from pdm.models.requirements import parse_requirement result = self._data.install_requires for extra, reqs in self._data.extras_require.items(): diff --git a/src/pdm/models/specifiers.py b/src/pdm/models/specifiers.py index 82d1b43214..a9c37bb1ae 100644 --- a/src/pdm/models/specifiers.py +++ b/src/pdm/models/specifiers.py @@ -84,13 +84,9 @@ def _analyze_specifiers(self) -> None: if op == "!=": excludes.add(version) elif op[0] == ">": - lower_bound = max( - lower_bound, version if op == ">=" else version.bump() - ) + lower_bound = max(lower_bound, version if op == ">=" else version.bump()) elif op[0] == "<": - upper_bound = min( - upper_bound, version.bump() if op == "<=" else version - ) + upper_bound = min(upper_bound, version.bump() if op == "<=" else version) elif op == "~=": new_lower = version.complete() new_upper = version.bump(-2) @@ -110,15 +106,12 @@ def _merge_bounds_and_excludes( excludes: Iterable[Version], ) -> tuple[Version, Version, list[Version]]: sorted_excludes = sorted(excludes) - wildcard_excludes = { - version[:-1] for version in sorted_excludes if version.is_wildcard - } + wildcard_excludes = {version[:-1] for version in sorted_excludes if version.is_wildcard} # Remove versions that are already excluded by another wildcard exclude. sorted_excludes = [ version for version in sorted_excludes - if version.is_wildcard - or not any(version.startswith(wv) for wv in wildcard_excludes) + if version.is_wildcard or not any(version.startswith(wv) for wv in wildcard_excludes) ] if lower == Version.MIN and upper == Version.MAX: @@ -175,9 +168,7 @@ def _merge_bounds_and_excludes( return lower, upper, sorted_excludes - def _rearrange( - self, lower_bound: Version, upper_bound: Version, excludes: Iterable[Version] - ) -> None: + def _rearrange(self, lower_bound: Version, upper_bound: Version, excludes: Iterable[Version]) -> None: """Rearrange the version bounds with the given inputs.""" ( self._lower_bound, @@ -210,11 +201,7 @@ def is_allow_all(self) -> bool: """Return True if the specifierset accepts all versions.""" if self.is_impossible: return False - return ( - self._lower_bound == Version.MIN - and self._upper_bound == Version.MAX - and not self._excludes - ) + return self._lower_bound == Version.MIN and self._upper_bound == Version.MAX and not self._excludes def __bool__(self) -> bool: return not self.is_allow_all @@ -280,15 +267,11 @@ def __or__(self, other: PySpecSet) -> PySpecSet: lower = left._lower_bound upper = max(left._upper_bound, right._upper_bound) if right._lower_bound > left._upper_bound: # two ranges has no overlap - excludes.update( - self._populate_version_range(left._upper_bound, right._lower_bound) - ) + excludes.update(self._populate_version_range(left._upper_bound, right._lower_bound)) rv._rearrange(lower, upper, excludes) return rv - def _populate_version_range( - self, lower: Version, upper: Version - ) -> Iterable[Version]: + def _populate_version_range(self, lower: Version, upper: Version) -> Iterable[Version]: """Expand the version range to a collection of versions to exclude, taking the released python versions into consideration. """ @@ -306,10 +289,7 @@ def _populate_version_range( if cur <= upper: # It is still within the range yield prev[:2].complete("*") # Exclude X.Y.* prev = ( - prev.bump(0) - if cur.is_py2 - and cast(int, cur[1]) > self.PY_MAX_MINOR_VERSION[cur[:1]] - else cur + prev.bump(0) if cur.is_py2 and cast(int, cur[1]) > self.PY_MAX_MINOR_VERSION[cur[:1]] else cur ) # If prev is 2.7, next is 3.0, otherwise next is X.Y+1.0 continue while prev < upper: @@ -323,12 +303,7 @@ def _populate_version_range( current_max = self.PY_MAX_MINOR_VERSION[prev[:2]] for z in range(cast(int, prev[2]), current_max + 1): yield prev[:2].complete(z) - prev = ( - prev.bump(0) - if cur.is_py2 - and cast(int, cur[1]) > self.PY_MAX_MINOR_VERSION[cur[:1]] - else cur - ) + prev = prev.bump(0) if cur.is_py2 and cast(int, cur[1]) > self.PY_MAX_MINOR_VERSION[cur[:1]] else cur else: # Produce each version from X.Y.Z to X.Y.W while prev < upper: yield prev @@ -346,19 +321,10 @@ def is_superset(self, other: str | SpecifierSet) -> bool: # XXX: narrow down the upper bound to ``MAX_MAJOR_VERSION`` # So that `>=3.6,<4.0` is considered a superset of `>=3.7`, see issues/66 other._upper_bound = self.MAX_MAJOR_VERSION - lower, upper, excludes = self._merge_bounds_and_excludes( - other._lower_bound, other._upper_bound, self._excludes - ) - if ( - self._lower_bound > other._lower_bound - or self._upper_bound < other._upper_bound - ): + lower, upper, excludes = self._merge_bounds_and_excludes(other._lower_bound, other._upper_bound, self._excludes) + if self._lower_bound > other._lower_bound or self._upper_bound < other._upper_bound: return False - return ( - lower <= other._lower_bound - and upper >= other._upper_bound - and set(excludes) <= set(other._excludes) - ) + return lower <= other._lower_bound and upper >= other._upper_bound and set(excludes) <= set(other._excludes) @lru_cache() def is_subset(self, other: str | SpecifierSet) -> bool: @@ -370,19 +336,10 @@ def is_subset(self, other: str | SpecifierSet) -> bool: other._upper_bound = Version.MAX if other.is_allow_all: return True - lower, upper, excludes = self._merge_bounds_and_excludes( - self._lower_bound, self._upper_bound, other._excludes - ) - if ( - self._lower_bound < other._lower_bound - or self._upper_bound > other._upper_bound - ): + lower, upper, excludes = self._merge_bounds_and_excludes(self._lower_bound, self._upper_bound, other._excludes) + if self._lower_bound < other._lower_bound or self._upper_bound > other._upper_bound: return False - return ( - lower <= self._lower_bound - and upper >= self._upper_bound - and set(self._excludes) >= set(excludes) - ) + return lower <= self._lower_bound and upper >= self._upper_bound and set(self._excludes) >= set(excludes) def as_marker_string(self) -> str: if self.is_allow_all: @@ -407,15 +364,9 @@ def as_marker_string(self) -> str: else: result.append(f"{key}{op}{version!r}") if excludes: - result.append( - "python_version not in {!r}".format(", ".join(sorted(excludes))) - ) + result.append("python_version not in {!r}".format(", ".join(sorted(excludes)))) if full_excludes: - result.append( - "python_full_version not in {!r}".format( - ", ".join(sorted(full_excludes)) - ) - ) + result.append("python_full_version not in {!r}".format(", ".join(sorted(full_excludes)))) return " and ".join(result) def supports_py2(self) -> bool: diff --git a/src/pdm/models/versions.py b/src/pdm/models/versions.py index 09b4ef84fb..a88a2a60da 100644 --- a/src/pdm/models/versions.py +++ b/src/pdm/models/versions.py @@ -6,7 +6,7 @@ from pdm.compat import Literal from pdm.exceptions import InvalidPyVersion -VersionBit = Union[int, Literal["*"]] +VersionBit = Union[int, Literal["*"]] # noqa: F722 PRE_RELEASE_SEGMENT_RE = re.compile( r"(?P\d+)(?Pa|b|rc)(?P\d*)", flags=re.IGNORECASE, @@ -49,8 +49,7 @@ def __init__(self, version: tuple[VersionBit, ...] | str) -> None: break # pre release version is only at the end else: raise InvalidPyVersion( - f"{version_str}: postreleases are not supported " - "for python version specifiers." + f"{version_str}: postreleases are not supported for python version specifiers." ) from None version = tuple(bits) self._version: tuple[VersionBit, ...] = version diff --git a/src/pdm/models/working_set.py b/src/pdm/models/working_set.py index c743c09b86..e8f524dae4 100644 --- a/src/pdm/models/working_set.py +++ b/src/pdm/models/working_set.py @@ -13,9 +13,7 @@ class EgglinkFinder(im.DistributionFinder): @classmethod - def find_distributions( - cls, context: im.DistributionFinder.Context = default_context - ) -> Iterable[im.Distribution]: + def find_distributions(cls, context: im.DistributionFinder.Context = default_context) -> Iterable[im.Distribution]: found_links = cls._search_paths(context.name, context.path) # For Py3.7 compatibility, handle both classmethod and instance method meta_finder = im.MetadataPathFinder() @@ -24,17 +22,13 @@ def find_distributions( link_pointer = Path(link.open("rb").readline().decode().strip()) dist = next( iter( - meta_finder.find_distributions( - im.DistributionFinder.Context( - name=name, path=[str(link_pointer)] - ) - ) + meta_finder.find_distributions(im.DistributionFinder.Context(name=name, path=[str(link_pointer)])) ), None, ) if not dist: continue - dist.link_file = link.absolute() # type: ignore + dist.link_file = link.absolute() # type: ignore[attr-defined] yield dist @classmethod @@ -68,10 +62,7 @@ class WorkingSet(Mapping[str, im.Distribution]): def __init__(self, paths: list[str] | None = None): if paths is None: paths = sys.path - self._dist_map = { - normalize_name(dist.metadata["Name"]): dist - for dist in distributions(path=paths) - } + self._dist_map = {normalize_name(dist.metadata["Name"]): dist for dist in distributions(path=paths)} def __getitem__(self, key: str) -> im.Distribution: return self._dist_map[key] diff --git a/src/pdm/pep582/sitecustomize.py b/src/pdm/pep582/sitecustomize.py index e7e36fdb9e..0a600d8884 100644 --- a/src/pdm/pep582/sitecustomize.py +++ b/src/pdm/pep582/sitecustomize.py @@ -75,7 +75,7 @@ def patch_sysconfig(libpath): bin_prefix = "Scripts" if os.name == "nt" else "bin" pep582_base = os.path.dirname(libpath) - pep582_scheme = { # type: ignore + pep582_scheme = { "stdlib": "{pep582_base}/lib", "platstdlib": "{pep582_base}/lib", "purelib": "{pep582_base}/lib", @@ -124,9 +124,7 @@ def main(): site.addusersitepackages(known_paths) site.addsitepackages(known_paths) known_paths = {os.path.normcase(path) for path in known_paths} - original_sys_path = [ - path for path in original_sys_path if os.path.normcase(path) not in known_paths - ] + original_sys_path = [path for path in original_sys_path if os.path.normcase(path) not in known_paths] sys.path[:] = original_sys_path # Second, add lib directories, ensuring .pth file are processed. diff --git a/src/pdm/project/__init__.py b/src/pdm/project/__init__.py index 0eb7d40c2b..c573e0b7f5 100644 --- a/src/pdm/project/__init__.py +++ b/src/pdm/project/__init__.py @@ -1,2 +1,4 @@ -from pdm.project.config import Config, ConfigItem # noqa -from pdm.project.core import Project # noqa +from pdm.project.config import Config, ConfigItem +from pdm.project.core import Project + +__all__ = ["Config", "ConfigItem", "Project"] diff --git a/src/pdm/project/config.py b/src/pdm/project/config.py index 9bfc4908b3..74b2a19e2e 100644 --- a/src/pdm/project/config.py +++ b/src/pdm/project/config.py @@ -29,9 +29,7 @@ class RepositoryConfig: config_prefix: str | None = None def __rich__(self) -> str: - config_prefix = ( - f"{self.config_prefix}." if self.config_prefix is not None else "" - ) + config_prefix = f"{self.config_prefix}." if self.config_prefix is not None else "" lines = [f"[primary]{config_prefix}url[/] = {self.url}"] if self.username: lines.append(f"[primary]{config_prefix}username[/] = {self.username}") @@ -53,9 +51,7 @@ class RegistryConfig: config_prefix: str | None = None def __rich__(self) -> str: - config_prefix = ( - f"{self.config_prefix}." if self.config_prefix is not None else "" - ) + config_prefix = f"{self.config_prefix}." if self.config_prefix is not None else "" lines = [f"[primary]{config_prefix}url[/] = {self.url}"] if self.username: lines.append(f"[primary]{config_prefix}username[/] = {self.username}") @@ -80,22 +76,20 @@ def load_config(file_path: Path) -> dict[str, Any]: E.g. ["python"]["path"] will be loaded as "python.path" key. """ - def get_item(sub_data: Mapping[str, Any]) -> Mapping[str, Any]: + def get_item(sub_data: Mapping[str, Any]) -> dict[str, Any]: result: dict[str, Any] = {} for k, v in sub_data.items(): if k == "pypi": result.update((f"{k}.{sub_k}", sub_v) for sub_k, sub_v in v.items()) elif k != REPOSITORY and isinstance(v, Mapping): - result.update( - {f"{k}.{sub_k}": sub_v for sub_k, sub_v in get_item(v).items()} - ) + result.update({f"{k}.{sub_k}": sub_v for sub_k, sub_v in get_item(v).items()}) else: result.update({k: v}) return result if not file_path.is_file(): return {} - return get_item(dict(tomlkit.parse(file_path.read_text("utf-8")))) # type: ignore + return get_item(dict(tomlkit.parse(file_path.read_text("utf-8")))) def ensure_boolean(val: Any) -> bool: @@ -175,9 +169,7 @@ class Config(MutableMapping[str, str]): platformdirs.user_config_path("pdm") / "global-project", True, ), - "global_project.user_site": ConfigItem( - "Whether to install to user site", False, True, coerce=ensure_boolean - ), + "global_project.user_site": ConfigItem("Whether to install to user site", False, True, coerce=ensure_boolean), "project_max_depth": ConfigItem( "The max depth to search for a project through the parents", 5, @@ -185,12 +177,8 @@ class Config(MutableMapping[str, str]): env_var="PDM_PROJECT_MAX_DEPTH", coerce=int, ), - "strategy.update": ConfigItem( - "The default strategy for updating packages", "reuse", False - ), - "strategy.save": ConfigItem( - "Specify how to save versions when a package is added", "minimum", False - ), + "strategy.update": ConfigItem("The default strategy for updating packages", "reuse", False), + "strategy.save": ConfigItem("Specify how to save versions when a package is added", "minimum", False), "strategy.resolve_max_rounds": ConfigItem( "Specify the max rounds of resolution process", 10000, @@ -213,9 +201,7 @@ class Config(MutableMapping[str, str]): "symlink", ), "python.path": ConfigItem("The Python interpreter path", env_var="PDM_PYTHON"), - "python.use_pyenv": ConfigItem( - "Use the pyenv interpreter", True, coerce=ensure_boolean - ), + "python.use_pyenv": ConfigItem("Use the pyenv interpreter", True, coerce=ensure_boolean), "python.use_venv": ConfigItem( "Install packages into the activated venv site packages instead of PEP 582", True, @@ -227,18 +213,11 @@ class Config(MutableMapping[str, str]): DEFAULT_PYPI_INDEX, env_var="PDM_PYPI_URL", ), - "pypi.verify_ssl": ConfigItem( - "Verify SSL certificate when query PyPI", True, coerce=ensure_boolean - ), - "pypi.username": ConfigItem( - "The username to access PyPI", env_var="PDM_PYPI_USERNAME" - ), - "pypi.password": ConfigItem( - "The password to access PyPI", env_var="PDM_PYPI_PASSWORD" - ), + "pypi.verify_ssl": ConfigItem("Verify SSL certificate when query PyPI", True, coerce=ensure_boolean), + "pypi.username": ConfigItem("The username to access PyPI", env_var="PDM_PYPI_USERNAME"), + "pypi.password": ConfigItem("The password to access PyPI", env_var="PDM_PYPI_PASSWORD"), "pypi.ca_certs": ConfigItem( - "Path to a CA certificate bundle used for verifying the identity " - "of the PyPI server", + "Path to a CA certificate bundle used for verifying the identity of the PyPI server", ), "pypi.ignore_stored_index": ConfigItem( "Ignore the configured indexes", @@ -311,20 +290,14 @@ def add_config(cls, name: str, item: ConfigItem) -> None: def __init__(self, config_file: Path, is_global: bool = False): self.is_global = is_global self.config_file = config_file.resolve() - self.deprecated = { - v.replace: k for k, v in self._config_map.items() if v.replace - } + self.deprecated = {v.replace: k for k, v in self._config_map.items() if v.replace} self._file_data = load_config(self.config_file) - self._data = collections.ChainMap( - self._file_data, self.get_defaults() if is_global else {} - ) + self._data = collections.ChainMap(self._file_data, self.get_defaults() if is_global else {}) def load_theme(self) -> rich.theme.Theme: if not self.is_global: # pragma: no cover raise PdmUsageError("Theme can only be loaded from global config") - return rich.theme.Theme( - {k[6:]: v for k, v in self.items() if k.startswith("theme.")} - ) + return rich.theme.Theme({k[6:]: v for k, v in self.items() if k.startswith("theme.")}) @property def self_data(self) -> dict[str, Any]: @@ -349,7 +322,7 @@ def _save_config(self) -> None: temp[last] = value with self.config_file.open("w", encoding="utf-8") as fp: - tomlkit.dump(toml_data, fp) # type: ignore + tomlkit.dump(toml_data, fp) def __getitem__(self, key: str) -> Any: parts = key.split(".") @@ -371,11 +344,7 @@ def __getitem__(self, key: str) -> Any: source = self._data[index_key] if len(parts) >= 3 and parts[2] == "password": return "" - return ( - source[parts[2]] - if len(parts) >= 3 - else RegistryConfig(**self._data[index_key]) - ) + return source[parts[2]] if len(parts) >= 3 else RegistryConfig(**self._data[index_key]) elif key == "pypi.password": return "" @@ -399,12 +368,8 @@ def __setitem__(self, key: str, value: Any) -> None: parts = key.split(".") if parts[0] == REPOSITORY: if len(parts) < 3: - raise PdmUsageError( - "Set repository config with [success]repository.{name}.{attr}" - ) - self._file_data.setdefault(parts[0], {}).setdefault( - parts[1], {} - ).setdefault(parts[2], value) + raise PdmUsageError("Set repository config with [success]repository.{name}.{attr}") + self._file_data.setdefault(parts[0], {}).setdefault(parts[1], {}).setdefault(parts[2], value) self._save_config() return if parts[0] == "pypi" and key not in self._config_map: @@ -419,16 +384,13 @@ def __setitem__(self, key: str, value: Any) -> None: config_key = self.deprecated.get(key, key) config = self._config_map[config_key] if not self.is_global and config.global_only: - raise ValueError( - f"Config item '{key}' is not allowed to set in project config." - ) + raise ValueError(f"Config item '{key}' is not allowed to set in project config.") value = config.coerce(value) env_var = config.env_var if env_var is not None and env_var in os.environ: ui.echo( - "WARNING: the config is shadowed by env var '{}', " - "the value set won't take effect.".format(env_var), + "WARNING: the config is shadowed by env var '{}', the value set won't take effect.".format(env_var), style="warning", ) self._file_data[config_key] = value @@ -478,8 +440,7 @@ def __delitem__(self, key: str) -> None: env_var = config.env_var if env_var is not None and env_var in os.environ: ui.echo( - "WARNING: the config is shadowed by env var '{}', " - "set value won't take effect.".format(env_var), + "WARNING: the config is shadowed by env var '{}', set value won't take effect.".format(env_var), style="warning", ) self._save_config() @@ -491,9 +452,7 @@ def get_repository_config(self, name_or_url: str) -> RepositoryConfig | None: repositories: Mapping[str, Source] = self._data.get(REPOSITORY, {}) repo: RepositoryConfig | None = None if "://" in name_or_url: - config: Source = next( - (v for v in repositories.values() if v.get("url") == name_or_url), {} - ) + config: Source = next((v for v in repositories.values() if v.get("url") == name_or_url), {}) repo = next( (r for r in DEFAULT_REPOSITORIES.values() if r.url == name_or_url), RepositoryConfig(name_or_url), @@ -506,4 +465,4 @@ def get_repository_config(self, name_or_url: str) -> RepositoryConfig | None: return dataclasses.replace(repo, **config) if not config: return None - return RepositoryConfig(**config) # type: ignore + return RepositoryConfig(**config) # type: ignore[misc] diff --git a/src/pdm/project/core.py b/src/pdm/project/core.py index 3af82f328c..823dbc1523 100644 --- a/src/pdm/project/core.py +++ b/src/pdm/project/core.py @@ -87,11 +87,7 @@ def __init__( if not is_global else global_project ) - if ( - not is_global - and root_path is None - and self.global_config["global_project.fallback"] - ): + if not is_global and root_path is None and self.global_config["global_project.fallback"]: root_path = global_project is_global = True if self.global_config["global_project.fallback_verbose"]: @@ -115,9 +111,7 @@ def pyproject(self) -> PyProject: @property def lockfile(self) -> Lockfile: if self._lockfile is None: - self._lockfile = Lockfile( - self.root / self.LOCKFILE_FILENAME, ui=self.core.ui - ) + self._lockfile = Lockfile(self.root / self.LOCKFILE_FILENAME, ui=self.core.ui) return self._lockfile def set_lockfile(self, path: str | Path) -> None: @@ -132,7 +126,7 @@ def config(self) -> dict[str, Any]: @property def scripts(self) -> dict[str, str | dict[str, str]]: - return self.pyproject.settings.get("scripts", {}) # type: ignore + return self.pyproject.settings.get("scripts", {}) @cached_property def project_config(self) -> Config: @@ -184,10 +178,7 @@ def note(message: str) -> None: if venv_in_env: python = PythonInfo.from_path(get_venv_python(Path(venv_in_env))) if match_version(python): - note( - f"Inside an active virtualenv [success]{venv_in_env}[/], " - "reusing it." - ) + note(f"Inside an active virtualenv [success]{venv_in_env}[/], reusing it.") return python # otherwise, get a venv associated with the project for _, venv in iter_venvs(self): @@ -198,10 +189,7 @@ def note(message: str) -> None: return python if not self.root.joinpath("__pypackages__").exists(): - note( - "python.use_venv is on, creating a virtualenv for this project" - "..." - ) + note("python.use_venv is on, creating a virtualenv for this project...") venv = self._create_virtualenv() self.python = PythonInfo.from_path(get_venv_python(venv)) return self.python @@ -209,17 +197,11 @@ def note(message: str) -> None: for py_version in self.find_interpreters(): if match_version(py_version): if config.get("python.use_venv"): - note( - "[success]__pypackages__[/] is detected, using the PEP 582 mode" - ) + note("[success]__pypackages__[/] is detected, using the PEP 582 mode") self.python = py_version return py_version - raise NoPythonVersion( - "No Python that satisfies {} is found on the system.".format( - self.python_requires - ) - ) + raise NoPythonVersion(f"No Python that satisfies {self.python_requires} is found on the system.") def get_environment(self) -> Environment: """Get the environment selected by this project""" @@ -233,8 +215,7 @@ def get_environment(self) -> Environment: return ( GlobalEnvironment(self) - if self.config["python.use_venv"] - and get_venv_like_prefix(self.python.executable) is not None + if self.config["python.use_venv"] and get_venv_like_prefix(self.python.executable) is not None else Environment(self) ) @@ -248,9 +229,7 @@ def _create_virtualenv(self) -> Path: in_project=self.config["venv.in_project"], prompt=self.config["venv.prompt"], ) - self.core.ui.echo( - f"Virtualenv is created successfully at [success]{path}[/]", err=True - ) + self.core.ui.echo(f"Virtualenv is created successfully at [success]{path}[/]", err=True) return path @property @@ -367,16 +346,11 @@ def sources(self) -> list[Source]: if all(source.get("name") != "pypi" for source in sources): sources.insert(0, self.default_source) stored_sources = dict(self.project_config.iter_sources()) - stored_sources.update( - (k, v) - for k, v in self.global_config.iter_sources() - if k not in stored_sources - ) + stored_sources.update((k, v) for k, v in self.global_config.iter_sources() if k not in stored_sources) # The order is kept as project sources -> global sources sources.extend(stored_sources.values()) expanded_sources = [ - cast("Source", {**source, "url": expand_env_vars_in_auth(source["url"])}) - for source in sources + cast("Source", {**source, "url": expand_env_vars_in_auth(source["url"])}) for source in sources ] return expanded_sources @@ -421,9 +395,7 @@ def get_provider( repository = self.get_repository(ignore_compatibility=ignore_compatibility) allow_prereleases = self.allow_prereleases - overrides = { - normalize_name(k): v for k, v in self.pyproject.resolution_overrides.items() - } + overrides = {normalize_name(k): v for k, v in self.pyproject.resolution_overrides.items()} locked_repository: LockedRepository | None = None if strategy != "all" or for_install: try: @@ -441,9 +413,7 @@ def get_provider( return BaseProvider(repository, allow_prereleases, overrides) if for_install: return BaseProvider(locked_repository, allow_prereleases, overrides) - provider_class = ( - ReusePinProvider if strategy == "reuse" else EagerUpdateProvider - ) + provider_class = ReusePinProvider if strategy == "reuse" else EagerUpdateProvider tracked_names = [strip_extras(name)[0] for name in tracked_names or ()] return provider_class( locked_repository.all_candidates, @@ -481,9 +451,7 @@ def get_lock_metadata(self) -> dict[str, Any]: "content_hash": content_hash, } - def write_lockfile( - self, toml_data: dict, show_message: bool = True, write: bool = True - ) -> None: + def write_lockfile(self, toml_data: dict, show_message: bool = True, write: bool = True) -> None: toml_data["metadata"].update(self.get_lock_metadata()) self.lockfile.set_data(toml_data) @@ -520,9 +488,7 @@ def is_lockfile_compatible(self) -> bool: accepted = get_specifier(f"~={lockfile_version},>={lockfile_version}") return accepted.contains(self.lockfile.spec_version) - def get_pyproject_dependencies( - self, group: str, dev: bool = False - ) -> tuple[list[str], bool]: + def get_pyproject_dependencies(self, group: str, dev: bool = False) -> tuple[list[str], bool]: """Get the dependencies array in the pyproject.toml Return a tuple of two elements, the first is the dependencies array, and the second tells whether it is a dev-dependencies group. @@ -573,9 +539,7 @@ def init_global_project(self) -> None: if not self.is_global or not self.pyproject.empty: return self.root.mkdir(parents=True, exist_ok=True) - self.pyproject.set_data( - {"project": {"dependencies": ["pip", "setuptools", "wheel"]}} - ) + self.pyproject.set_data({"project": {"dependencies": ["pip", "setuptools", "wheel"]}}) self.pyproject.write() @property @@ -599,9 +563,7 @@ def make_wheel_cache(self) -> WheelCache: return WheelCache(self.cache("wheels")) def make_candidate_info_cache(self) -> CandidateInfoCache: - python_hash = hashlib.sha1( - str(self.environment.python_requires).encode() - ).hexdigest() + python_hash = hashlib.sha1(str(self.environment.python_requires).encode()).hexdigest() file_name = f"package_meta_{python_hash}.json" return CandidateInfoCache(self.cache("metadata") / file_name) @@ -664,15 +626,10 @@ def _get_python_finder(self) -> Finder: # compatibility, shouldn't be used directly @property def meta(self) -> dict[str, Any]: - deprecation_warning( - "project.meta is deprecated, use project.pyproject.metadata instead" - ) + deprecation_warning("project.meta is deprecated, use project.pyproject.metadata instead") return self.pyproject.metadata @property def tool_settings(self) -> dict[str, Any]: - deprecation_warning( - "project.tool_settings is deprecated, " - "use project.pyproject.settings instead" - ) + deprecation_warning("project.tool_settings is deprecated, use project.pyproject.settings instead") return self.pyproject.settings diff --git a/src/pdm/project/project_file.py b/src/pdm/project/project_file.py index 3419fb0179..4a11abf5c8 100644 --- a/src/pdm/project/project_file.py +++ b/src/pdm/project/project_file.py @@ -24,9 +24,7 @@ def read(self) -> TOMLDocument: metadata, settings = converter.convert(None, self._path, None) data["project"] = metadata if settings: - data.setdefault("tool", {}).setdefault("pdm", {}).update( - settings - ) + data.setdefault("tool", {}).setdefault("pdm", {}).update(settings) break return data diff --git a/src/pdm/pytest.py b/src/pdm/pytest.py index cc84f37345..a392790087 100644 --- a/src/pdm/pytest.py +++ b/src/pdm/pytest.py @@ -89,9 +89,7 @@ def __init__( strip_suffix: bool = False, ): super().__init__() - self.aliases = sorted( - aliases.items(), key=lambda item: len(item[0]), reverse=True - ) + self.aliases = sorted(aliases.items(), key=lambda item: len(item[0]), reverse=True) self.overrides = overrides if overrides is not None else {} self.strip_suffix = strip_suffix self._opened_files: list[BytesIO | BufferedReader | BinaryIO] = [] @@ -155,9 +153,7 @@ class TestRepository(BaseRepository): A mock repository to ease testing dependencies """ - def __init__( - self, sources: list[Source], environment: Environment, pypi_json: Path - ): + def __init__(self, sources: list[Source], environment: Environment, pypi_json: Path): super().__init__(sources, environment) self._pypi_data: dict[str, Any] = {} self._pypi_json = pypi_json @@ -165,39 +161,27 @@ def __init__( def get_raw_dependencies(self, candidate: Candidate) -> list[str]: try: - pypi_data = self._pypi_data[cast(str, candidate.req.key)][ - cast(str, candidate.version) - ] + pypi_data = self._pypi_data[cast(str, candidate.req.key)][cast(str, candidate.version)] except KeyError: return candidate.prepare(self.environment).metadata.requires or [] else: return pypi_data.get("dependencies", []) def add_candidate(self, name: str, version: str, requires_python: str = "") -> None: - pypi_data = self._pypi_data.setdefault(normalize_name(name), {}).setdefault( - version, {} - ) + pypi_data = self._pypi_data.setdefault(normalize_name(name), {}).setdefault(version, {}) pypi_data["requires_python"] = requires_python - def add_dependencies( - self, name: str, version: str, requirements: list[str] - ) -> None: + def add_dependencies(self, name: str, version: str, requirements: list[str]) -> None: pypi_data = self._pypi_data[normalize_name(name)][version] pypi_data.setdefault("dependencies", []).extend(requirements) - def _get_dependencies_from_fixture( - self, candidate: Candidate - ) -> tuple[list[str], str, str]: + def _get_dependencies_from_fixture(self, candidate: Candidate) -> tuple[list[str], str, str]: try: - pypi_data = self._pypi_data[cast(str, candidate.req.key)][ - cast(str, candidate.version) - ] + pypi_data = self._pypi_data[cast(str, candidate.req.key)][cast(str, candidate.version)] except KeyError: - raise CandidateInfoNotFound(candidate) + raise CandidateInfoNotFound(candidate) from None deps = pypi_data.get("dependencies", []) - deps = filter_requirements_with_extras( - cast(str, candidate.req.name), deps, candidate.req.extras or () - ) # type: ignore + deps = filter_requirements_with_extras(cast(str, candidate.req.name), deps, candidate.req.extras or ()) return deps, pypi_data.get("requires_python", ""), "" def dependency_generators(self) -> Iterable[Callable[[Candidate], CandidateInfo]]: @@ -323,9 +307,7 @@ def build_env_wheels() -> Iterable[Path]: @pytest.fixture(scope="session") -def build_env( - build_env_wheels: Iterable[Path], tmp_path_factory: pytest.TempPathFactory -) -> Path: +def build_env(build_env_wheels: Iterable[Path], tmp_path_factory: pytest.TempPathFactory) -> Path: """ A fixture build environment @@ -359,12 +341,8 @@ def pdm_session(pypi_indexes: IndexesDefinition) -> Callable[[Any], PDMSession]: def get_pypi_session(*args: Any, **kwargs: Any) -> PDMSession: session = PDMSession(*args, **kwargs) for root, specs in pypi_indexes.items(): - index, overrides, strip = ( - specs if isinstance(specs, tuple) else (specs, None, False) - ) - session.mount( - root, LocalFileAdapter(index, overrides=overrides, strip_suffix=strip) - ) + index, overrides, strip = specs if isinstance(specs, tuple) else (specs, None, False) + session.mount(root, LocalFileAdapter(index, overrides=overrides, strip_suffix=strip)) return session return get_pypi_session @@ -406,18 +384,12 @@ def project_no_init( test_home = tmp_path / ".pdm-home" test_home.mkdir(parents=True) test_home.joinpath("config.toml").write_text( - '[global_project]\npath = "{}"\n'.format( - test_home.joinpath("global-project").as_posix() - ) - ) - p = core.create_project( - tmp_path, global_config=test_home.joinpath("config.toml").as_posix() + '[global_project]\npath = "{}"\n'.format(test_home.joinpath("global-project").as_posix()) ) + p = core.create_project(tmp_path, global_config=test_home.joinpath("config.toml").as_posix()) p.global_config["venv.location"] = str(tmp_path / "venvs") mocker.patch("pdm.models.environment.PDMSession", pdm_session) - mocker.patch( - "pdm.builders.base.EnvBuilder.get_shared_env", return_value=str(build_env) - ) + mocker.patch("pdm.builders.base.EnvBuilder.get_shared_env", return_value=str(build_env)) tmp_path.joinpath("caches").mkdir(parents=True) p.global_config["cache_dir"] = tmp_path.joinpath("caches").as_posix() python_path = find_python_in_path(sys.base_prefix) @@ -468,7 +440,7 @@ def working_set(mocker: MockerFixture, repository: TestRepository) -> MockWorkin mocker.patch.object(Environment, "get_working_set", return_value=rv) def install(candidate: Candidate) -> None: - key = normalize_name(candidate.name) # type: ignore + key = normalize_name(candidate.name or "") dist = Distribution(key, cast(str, candidate.version), candidate.req.editable) dist.dependencies = repository.get_raw_dependencies(candidate) rv.add_distribution(dist) @@ -479,9 +451,7 @@ def uninstall(dist: Distribution) -> None: install_manager = mocker.MagicMock() install_manager.install.side_effect = install install_manager.uninstall.side_effect = uninstall - mocker.patch( - "pdm.installers.Synchronizer.get_manager", return_value=install_manager - ) + mocker.patch("pdm.installers.Synchronizer.get_manager", return_value=install_manager) return rv @@ -639,7 +609,7 @@ def caller( try: core.main(args, "pdm", obj=obj, **kwargs) except SystemExit as e: - exit_code = e.code # type: ignore + exit_code = cast(int, e.code) except Exception as e: exit_code = 1 exception = e @@ -647,9 +617,7 @@ def caller( result = RunResult(exit_code, stdout.getvalue(), stderr.getvalue(), exception) if strict and result.exit_code != 0: - raise RuntimeError( - f"Call command {args} failed({result.exit_code}): {result.stderr}" - ) + raise RuntimeError(f"Call command {args} failed({result.exit_code}): {result.stderr}") return result return caller diff --git a/src/pdm/resolver/__init__.py b/src/pdm/resolver/__init__.py index acf6233d88..5aa3b0f3e4 100644 --- a/src/pdm/resolver/__init__.py +++ b/src/pdm/resolver/__init__.py @@ -1 +1,3 @@ -from pdm.resolver.core import resolve # noqa +from pdm.resolver.core import resolve + +__all__ = ["resolve"] diff --git a/src/pdm/resolver/core.py b/src/pdm/resolver/core.py index 4c03468410..3972484819 100644 --- a/src/pdm/resolver/core.py +++ b/src/pdm/resolver/core.py @@ -37,11 +37,7 @@ def resolve( mapping = cast(Dict[str, Candidate], result.mapping) mapping.pop("python", None) - local_name = ( - normalize_name(repository.environment.project.name) - if repository.environment.project.name - else None - ) + local_name = normalize_name(repository.environment.project.name) if repository.environment.project.name else None for key, candidate in list(result.mapping.items()): if key is None: continue diff --git a/src/pdm/resolver/providers.py b/src/pdm/resolver/providers.py index 3bfae8ef84..5c9bda288f 100644 --- a/src/pdm/resolver/providers.py +++ b/src/pdm/resolver/providers.py @@ -50,9 +50,7 @@ def requirement_preference(self, requirement: Requirement) -> Comparable: editable = requirement.editable is_named = requirement.is_named is_prerelease = ( - requirement.prerelease - or requirement.specifier is not None - and bool(requirement.specifier.prereleases) + requirement.prerelease or requirement.specifier is not None and bool(requirement.specifier.prereleases) ) specifier_parts = len(requirement.specifier) if requirement.specifier else 0 return (not editable, is_named, not is_prerelease, -specifier_parts) @@ -70,9 +68,7 @@ def get_preference( ) -> tuple[Comparable, ...]: is_top = any(parent is None for _, parent in information[identifier]) is_backtrack_cause = any( - requirement.identify() == identifier - or parent - and parent.identify() == identifier + requirement.identify() == identifier or parent and parent.identify() == identifier for requirement, parent in backtrack_causes ) if is_top: @@ -85,14 +81,9 @@ def get_preference( dep_depth = min(parent_depths, default=0) + 1 # Use the REAL identifier as it may be updated after candidate preparation. self._known_depth[self.identify(next(candidates[identifier]))] = dep_depth - is_file_or_url = any( - not requirement.is_named for requirement, _ in information[identifier] - ) + is_file_or_url = any(not requirement.is_named for requirement, _ in information[identifier]) operators = [ - spec.operator - for req, _ in information[identifier] - if req.specifier is not None - for spec in req.specifier + spec.operator for req, _ in information[identifier] if req.specifier is not None for spec in req.specifier ] is_python = identifier == "python" is_pinned = any(op[:2] == "==" for op in operators) @@ -122,17 +113,13 @@ def get_override_candidates(self, identifier: str) -> Iterable[Candidate]: return self._find_candidates(parse_requirement(req)) def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]: - if not requirement.is_named and not isinstance( - self.repository, LockedRepository - ): + if not requirement.is_named and not isinstance(self.repository, LockedRepository): can = make_candidate(requirement) if not can.name: can.prepare(self.repository.environment).metadata return [can] else: - return self.repository.find_candidates( - requirement, requirement.prerelease or self.allow_prereleases - ) + return self.repository.find_candidates(requirement, requirement.prerelease or self.allow_prereleases) def find_matches( self, @@ -150,10 +137,7 @@ def matches_gen() -> Iterator[Candidate]: reqs = sorted(requirements[identifier], key=self.requirement_preference) candidates = self._find_candidates(reqs[0]) return ( - can - for can in candidates - if can not in incompat - and all(self.is_satisfied_by(r, can) for r in reqs) + can for can in candidates if can not in incompat and all(self.is_satisfied_by(r, can) for r in reqs) ) return matches_gen @@ -174,7 +158,7 @@ def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> boo if not requirement.is_named: if candidate.req.is_named: return False - return self._compare_file_reqs(requirement, candidate.req) # type: ignore + return self._compare_file_reqs(requirement, candidate.req) # type: ignore[arg-type] version = candidate.version this_name = self.repository.environment.project.name if version is None or candidate.name == this_name: @@ -182,12 +166,8 @@ def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> boo 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 = ( - self.allow_prereleases in (True, None) or not candidate.req.is_named - ) - return cast(SpecifierSet, requirement.specifier).contains( - version, allow_prereleases - ) + allow_prereleases = self.allow_prereleases in (True, None) or not candidate.req.is_named + return cast(SpecifierSet, requirement.specifier).contains(version, allow_prereleases) def get_dependencies(self, candidate: Candidate) -> list[Requirement]: if isinstance(candidate, PythonCandidate): @@ -214,13 +194,8 @@ def get_dependencies(self, candidate: Candidate) -> list[Requirement]: # For example, A v1 requires python>=3.6, it not eligible on a project with # requires-python=">=2.7". But it is eligible if A has environment marker # A1; python_version>='3.8' - new_requires_python = ( - candidate.req.requires_python & self.repository.environment.python_requires - ) - if ( - candidate.identify() not in self.overrides - and not requires_python.is_superset(new_requires_python) - ): + new_requires_python = candidate.req.requires_python & self.repository.environment.python_requires + if candidate.identify() not in self.overrides and not requires_python.is_superset(new_requires_python): valid_deps.append(PythonRequirement.from_pyspec_set(requires_python)) return valid_deps @@ -253,19 +228,14 @@ def find_matches( bare_name = strip_extras(identifier)[0] def matches_gen() -> Iterator[Candidate]: - if ( - bare_name not in self.tracked_names - and identifier in self.preferred_pins - ): + if bare_name not in self.tracked_names and identifier in self.preferred_pins: pin = self.preferred_pins[identifier] incompat = list(incompatibilities[identifier]) demanded_req = next(requirements[identifier], None) if demanded_req and demanded_req.is_named: pin.req = demanded_req - pin._preferred = True # type: ignore - if pin not in incompat and all( - self.is_satisfied_by(r, pin) for r in requirements[identifier] - ): + pin._preferred = True # type: ignore[attr-defined] + if pin not in incompat and all(self.is_satisfied_by(r, pin) for r in requirements[identifier]): yield pin yield from super_find() @@ -287,9 +257,7 @@ class EagerUpdateProvider(ReusePinProvider): def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool: # If this is a tracking package, tell the resolver out of using the # preferred pin, and into a "normal" candidate selection process. - if requirement.key in self.tracked_names and getattr( - candidate, "_preferred", False - ): + if requirement.key in self.tracked_names and getattr(candidate, "_preferred", False): return False return super().is_satisfied_by(requirement, candidate) @@ -312,7 +280,5 @@ def get_preference( backtrack_causes: Sequence[RequirementInformation], ) -> tuple[Comparable, ...]: # Resolve tracking packages so we have a chance to unpin them first. - (python, *others) = super().get_preference( - identifier, resolutions, candidates, information, backtrack_causes - ) + (python, *others) = super().get_preference(identifier, resolutions, candidates, information, backtrack_causes) return (python, identifier not in self.tracked_names, *others) diff --git a/src/pdm/resolver/python.py b/src/pdm/resolver/python.py index 26f71d04f7..acb0fddc85 100644 --- a/src/pdm/resolver/python.py +++ b/src/pdm/resolver/python.py @@ -12,7 +12,7 @@ class PythonCandidate(Candidate): def format(self) -> str: - return f"[req]{self.name}[/]" f"[warning]{str(self.req.specifier)}[/]" + return f"[req]{self.name}[/][warning]{str(self.req.specifier)}[/]" class PythonRequirement(NamedRequirement): diff --git a/src/pdm/resolver/reporters.py b/src/pdm/resolver/reporters.py index c97ba686a7..15366872db 100644 --- a/src/pdm/resolver/reporters.py +++ b/src/pdm/resolver/reporters.py @@ -22,9 +22,7 @@ def log_title(title: str) -> None: class SpinnerReporter(BaseReporter): - def __init__( - self, spinner: termui.Spinner, requirements: list[Requirement] - ) -> None: + def __init__(self, spinner: termui.Spinner, requirements: list[Requirement]) -> None: self.spinner = spinner self.requirements = requirements self._previous: dict[str, Candidate] | None = None @@ -79,11 +77,8 @@ def rejecting_candidate(self, criterion: Criterion, candidate: Candidate) -> Non "Candidate rejected: %s because it introduces a new requirement %s" " that conflicts with other requirements:\n %s", candidate, - last.requirement.as_line(), # type: ignore - " \n".join( - f" {req.as_line()} (from {parent if parent else 'project'})" - for req, parent in others - ), + last.requirement.as_line(), # type: ignore[attr-defined] + " \n".join(f" {req.as_line()} (from {parent if parent else 'project'})" for req, parent in others), ) def pinning(self, candidate: Candidate) -> None: @@ -92,8 +87,5 @@ def pinning(self, candidate: Candidate) -> None: logger.info("Pinning: %s %s", candidate.name, candidate.version) def resolving_conflicts(self, causes: list[RequirementInformation]) -> None: - conflicts = [ - f" {req.as_line()} (from {parent if parent else 'project'})" - for req, parent in causes - ] + conflicts = [f" {req.as_line()} (from {parent if parent else 'project'})" for req, parent in causes] logger.info("Conflicts detected: \n%s", "\n".join(conflicts)) diff --git a/src/pdm/termui.py b/src/pdm/termui.py index e3146411b2..973ed7d36e 100644 --- a/src/pdm/termui.py +++ b/src/pdm/termui.py @@ -66,9 +66,7 @@ def confirm(*args: str, **kwargs: Any) -> str: return Confirm.ask(*args, **kwargs) -def ask( - *args: str, prompt_type: type[str] | type[int] | None = None, **kwargs: Any -) -> str: +def ask(*args: str, prompt_type: type[str] | type[int] | None = None, **kwargs: Any) -> str: """prompt user and return response :prompt_type: which rich prompt to use, defaults to str. @@ -136,7 +134,7 @@ def update(self, text: str) -> None: self._show() def __enter__(self: SpinnerT) -> SpinnerT: - self._show() # type: ignore + self._show() # type: ignore[attr-defined] return self def __exit__(self, *args: Any) -> None: @@ -185,9 +183,7 @@ def echo( kwargs.setdefault("overflow", "ignore") console.print(message, **kwargs) - def display_columns( - self, rows: Sequence[Sequence[str]], header: list[str] | None = None - ) -> None: + def display_columns(self, rows: Sequence[Sequence[str]], header: list[str] | None = None) -> None: """Print rows in aligned columns. :param rows: a rows of data to be displayed. diff --git a/src/pdm/utils.py b/src/pdm/utils.py index e75d8489a9..4188ec5018 100644 --- a/src/pdm/utils.py +++ b/src/pdm/utils.py @@ -35,9 +35,7 @@ PACKAGING_22 = Version(_packaging_version) >= Version("22") -def create_tracked_tempdir( - suffix: str | None = None, prefix: str | None = None, dir: str | None = None -) -> str: +def create_tracked_tempdir(suffix: str | None = None, prefix: str | None = None, dir: str | None = None) -> str: name = tempfile.mkdtemp(suffix, prefix, dir) os.makedirs(name, mode=0o777, exist_ok=True) @@ -118,15 +116,11 @@ def get_user_email_from_git() -> tuple[str, str]: if not git: return "", "" try: - username = subprocess.check_output( - [git, "config", "user.name"], text=True, encoding="utf-8" - ).strip() + username = subprocess.check_output([git, "config", "user.name"], text=True, encoding="utf-8").strip() except subprocess.CalledProcessError: username = "" try: - email = subprocess.check_output( - [git, "config", "user.email"], text=True, encoding="utf-8" - ).strip() + email = subprocess.check_output([git, "config", "user.email"], text=True, encoding="utf-8").strip() except subprocess.CalledProcessError: email = "" return username, email @@ -146,9 +140,7 @@ def add_ssh_scheme_to_git_uri(uri: str) -> str: @contextlib.contextmanager -def atomic_open_for_write( - filename: str | Path, *, mode: str = "w", encoding: str = "utf-8" -) -> Iterator[IO]: +def atomic_open_for_write(filename: str | Path, *, mode: str = "w", encoding: str = "utf-8") -> Iterator[IO]: dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) @@ -189,9 +181,7 @@ def url_to_path(url: str) -> str: WINDOWS = sys.platform == "win32" - assert url.startswith( - "file:" - ), f"You can only turn file: urls into filenames (not {url!r})" + assert url.startswith("file:"), f"You can only turn file: urls into filenames (not {url!r})" _, netloc, path, _, _ = parse.urlsplit(url) @@ -202,9 +192,7 @@ def url_to_path(url: str) -> str: # If we have a UNC path, prepend UNC share notation. netloc = "\\\\" + netloc else: - raise ValueError( - f"non-local file URIs are not supported on this platform: {url!r}" - ) + raise ValueError(f"non-local file URIs are not supported on this platform: {url!r}") path = url2pathname(netloc + path) @@ -370,7 +358,7 @@ def pdm_scheme(base: str) -> dict[str, str]: """Return a PEP 582 style install scheme""" if "pep582" not in sysconfig.get_scheme_names(): bin_prefix = "Scripts" if os.name == "nt" else "bin" - sysconfig._INSTALL_SCHEMES["pep582"] = { # type: ignore + sysconfig._INSTALL_SCHEMES["pep582"] = { # type: ignore[attr-defined] "stdlib": "{pep582_base}/lib", "platstdlib": "{pep582_base}/lib", "purelib": "{pep582_base}/lib", @@ -406,9 +394,7 @@ def fs_supports_symlink() -> bool: return True -def deprecation_warning( - message: str, stacklevel: int = 1, raise_since: str | None = None -) -> None: +def deprecation_warning(message: str, stacklevel: int = 1, raise_since: str | None = None) -> None: """Show a deprecation warning with the given message and raise an error after a specified version. """ diff --git a/tasks/max_versions.py b/tasks/max_versions.py index 93881c1e93..f16baa7e9e 100644 --- a/tasks/max_versions.py +++ b/tasks/max_versions.py @@ -17,9 +17,7 @@ def __init__(self, *, convert_charrefs: bool = True) -> None: self.parsed_python_versions: list[str] = [] def handle_starttag(self, tag: str, attrs: list[tuple[str, str]]) -> None: - if tag == "span" and any( - "release-number" in value for key, value in attrs if key == "class" - ): + if tag == "span" and any("release-number" in value for key, value in attrs if key == "class"): self._parsing_release_number_span = True return diff --git a/tasks/release.py b/tasks/release.py index fcc4e5077a..787c2e5c8b 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -33,7 +33,7 @@ def bump_version(pre=None, major=False, minor=False, patch=True): patch = True if len([v for v in [major, minor, patch] if v]) > 1: echo( - "Only one option should be provided among " "(--major, --minor, --patch)", + "Only one option should be provided among (--major, --minor, --patch)", style="red", err=True, ) @@ -56,19 +56,13 @@ def release(dry_run=False, commit=True, pre=None, major=False, minor=False, patc new_version = bump_version(pre, major, minor, patch) echo(f"Bump version to: {new_version}", style="yellow") if dry_run: - subprocess.check_call( - ["towncrier", "build", "--version", new_version, "--draft"] - ) + subprocess.check_call(["towncrier", "build", "--version", new_version, "--draft"]) else: subprocess.check_call(["towncrier", "build", "--yes", "--version", new_version]) subprocess.check_call(["git", "add", "."]) if commit: - subprocess.check_call( - ["git", "commit", "-m", f"chore: Release {new_version}"] - ) - subprocess.check_call( - ["git", "tag", "-a", new_version, "-m", f"v{new_version}"] - ) + subprocess.check_call(["git", "commit", "-m", f"chore: Release {new_version}"]) + subprocess.check_call(["git", "tag", "-a", new_version, "-m", f"v{new_version}"]) subprocess.check_call(["git", "push"]) subprocess.check_call(["git", "push", "--tags"]) diff --git a/tests/cli/test_add.py b/tests/cli/test_add.py index 1fd3cf982d..49b3502c42 100644 --- a/tests/cli/test_add.py +++ b/tests/cli/test_add.py @@ -14,9 +14,7 @@ def test_add_package(project, working_set, is_dev): actions.do_add(project, is_dev, packages=["requests"]) group = ( - project.pyproject.settings["dev-dependencies"]["dev"] - if is_dev - else project.pyproject.metadata["dependencies"] + project.pyproject.settings["dev-dependencies"]["dev"] if is_dev else project.pyproject.metadata["dependencies"] ) assert group[0] == "requests~=2.19" @@ -68,10 +66,7 @@ def test_add_editable_package(project, working_set): group = project.pyproject.settings["dev-dependencies"]["dev"] assert group == ["-e git+https://github.com/test-root/demo.git#egg=demo"] locked_candidates = project.locked_repository.all_candidates - assert ( - locked_candidates["demo"].prepare(project.environment).revision - == "1234567890abcdef" - ) + assert locked_candidates["demo"].prepare(project.environment).revision == "1234567890abcdef" assert working_set["demo"].link_file assert locked_candidates["idna"].version == "2.7" assert "idna" in working_set @@ -84,9 +79,7 @@ def test_add_editable_package(project, working_set): def test_add_editable_package_to_metadata_forbidden(project): project.environment.python_requires = PySpecSet(">=3.6") with pytest.raises(PdmUsageError): - actions.do_add( - project, editables=["git+https://github.com/test-root/demo.git#egg=demo"] - ) + actions.do_add(project, editables=["git+https://github.com/test-root/demo.git#egg=demo"]) with pytest.raises(PdmUsageError): actions.do_add( project, @@ -122,14 +115,9 @@ def test_add_remote_package_url(project, is_dev): packages=["http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl"], ) group = ( - project.pyproject.settings["dev-dependencies"]["dev"] - if is_dev - else project.pyproject.metadata["dependencies"] - ) - assert ( - group[0] - == "demo @ http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl" + project.pyproject.settings["dev-dependencies"]["dev"] if is_dev else project.pyproject.metadata["dependencies"] ) + assert group[0] == "demo @ http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl" @pytest.mark.usefixtures("repository") @@ -178,9 +166,7 @@ def test_add_package_update_reuse(project, repository): "urllib3<1.24,>=1.21.1", ], ) - actions.do_add( - project, sync=False, save="wildcard", packages=["requests"], strategy="reuse" - ) + actions.do_add(project, sync=False, save="wildcard", packages=["requests"], strategy="reuse") locked_candidates = project.locked_repository.all_candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.4" @@ -208,9 +194,7 @@ def test_add_package_update_eager(project, repository): "urllib3<1.24,>=1.21.1", ], ) - actions.do_add( - project, sync=False, save="wildcard", packages=["requests"], strategy="eager" - ) + actions.do_add(project, sync=False, save="wildcard", packages=["requests"], strategy="eager") locked_candidates = project.locked_repository.all_candidates assert locked_candidates["requests"].version == "2.20.0" assert locked_candidates["chardet"].version == "3.0.5" @@ -252,9 +236,7 @@ def test_add_package_unconstrained_rewrite_specifier(project): assert locked_candidates["django"].version == "2.2.9" assert project.pyproject.metadata["dependencies"][0] == "django~=2.2" - actions.do_add( - project, packages=["django-toolbar"], no_self=True, unconstrained=True - ) + actions.do_add(project, packages=["django-toolbar"], no_self=True, unconstrained=True) locked_candidates = project.locked_repository.all_candidates assert locked_candidates["django"].version == "1.11.8" assert project.pyproject.metadata["dependencies"][0] == "django~=1.11" @@ -266,9 +248,7 @@ def test_add_cached_vcs_requirement(project, mocker): url = "git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo" built_path = FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl" wheel_cache = project.make_wheel_cache() - cache_path = wheel_cache.get_path_for_link( - Link(url), project.environment.target_python - ) + cache_path = wheel_cache.get_path_for_link(Link(url), project.environment.target_python) if not cache_path.exists(): cache_path.mkdir(parents=True) shutil.copy2(built_path, cache_path) @@ -307,10 +287,7 @@ def test_add_editable_package_with_extras(project, working_set): 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)[0] - ) + assert f"-e {path_to_url(dep_path)}#egg=demo[security]" in project.get_pyproject_dependencies("dev", True)[0] assert "demo" in working_set assert "requests" in working_set assert "urllib3" in working_set diff --git a/tests/cli/test_build.py b/tests/cli/test_build.py index 5c9b8eae32..6cc95db5dc 100644 --- a/tests/cli/test_build.py +++ b/tests/cli/test_build.py @@ -43,9 +43,7 @@ def test_build_single_module(fixture_project): ]: assert f"demo-module-0.1.0/{name}" in tar_names - zip_names = get_wheel_names( - project.root / "dist/demo_module-0.1.0-py3-none-any.whl" - ) + zip_names = get_wheel_names(project.root / "dist/demo_module-0.1.0-py3-none-any.whl") for name in ["foo_module.py", "bar_module.py"]: assert name in zip_names @@ -58,9 +56,7 @@ def test_build_single_module_with_readme(fixture_project): project.pyproject.metadata["readme"] = "README.md" project.pyproject.write() actions.do_build(project) - assert "demo-module-0.1.0/README.md" in get_tarball_names( - project.root / "dist/demo-module-0.1.0.tar.gz" - ) + assert "demo-module-0.1.0/README.md" in get_tarball_names(project.root / "dist/demo-module-0.1.0.tar.gz") def test_build_package(fixture_project): @@ -73,9 +69,7 @@ def test_build_package(fixture_project): assert "demo-package-0.1.0/single_module.py" not in tar_names assert "demo-package-0.1.0/data_out.json" not in tar_names - zip_names = get_wheel_names( - project.root / "dist/demo_package-0.1.0-py3-none-any.whl" - ) + zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names assert "single_module.py" not in zip_names @@ -90,9 +84,7 @@ def test_build_src_package(fixture_project): assert "demo-package-0.1.0/src/my_package/__init__.py" in tar_names assert "demo-package-0.1.0/src/my_package/data.json" in tar_names - zip_names = get_wheel_names( - project.root / "dist/demo_package-0.1.0-py3-none-any.whl" - ) + zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names @@ -114,9 +106,7 @@ def test_build_package_include(fixture_project): assert "demo-package-0.1.0/single_module.py" in tar_names assert "demo-package-0.1.0/data_out.json" in tar_names - zip_names = get_wheel_names( - project.root / "dist/demo_package-0.1.0-py3-none-any.whl" - ) + zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" not in zip_names assert "single_module.py" in zip_names @@ -133,9 +123,7 @@ def test_build_src_package_by_include(fixture_project): assert "demo-package-0.1.0/src/my_package/__init__.py" in tar_names assert "demo-package-0.1.0/src/my_package/data.json" in tar_names - zip_names = get_wheel_names( - project.root / "dist/demo_package-0.1.0-py3-none-any.whl" - ) + zip_names = get_wheel_names(project.root / "dist/demo_package-0.1.0-py3-none-any.whl") assert "my_package/__init__.py" in zip_names assert "my_package/data.json" in zip_names diff --git a/tests/cli/test_cache.py b/tests/cli/test_cache.py index 2a5e5db0ab..c3709b2385 100644 --- a/tests/cli/test_cache.py +++ b/tests/cli/test_cache.py @@ -150,8 +150,7 @@ def test_cache_info(project, invoke): "c30e269398d01144ee52aa33292682d", ), ( - "http://fixtures.test/artifacts/demo-0.0.1.tar.gz#md5=5218509812c9fcb4646a" - "dde8fd3307e1", + "http://fixtures.test/artifacts/demo-0.0.1.tar.gz#md5=5218509812c9fcb4646adde8fd3307e1", "sha256:d57bf5e3b8723e4fc68275159dcc4ca983d86d4c84220a4d715d491401f27db2", ), ], diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index 3e120f26d5..d496a10d7c 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -156,18 +156,13 @@ def test_hide_password_in_output_pypi(project, invoke): def test_config_get_repository(project, invoke): config = project.global_config["repository.pypi"] assert config == project.global_config.get_repository_config("pypi") - assert ( - project.global_config["repository.pypi.url"] - == "https://upload.pypi.org/legacy/" - ) + assert project.global_config["repository.pypi.url"] == "https://upload.pypi.org/legacy/" result = invoke(["config", "repository.pypi"], obj=project, strict=True) assert result.stdout.strip() == "url = https://upload.pypi.org/legacy/" assert ( - project.global_config.get_repository_config( - "https://example.pypi.org/legacy/" - ).url + project.global_config.get_repository_config("https://example.pypi.org/legacy/").url == "https://example.pypi.org/legacy/" ) @@ -178,10 +173,7 @@ def test_config_get_repository(project, invoke): def test_config_set_repository(project): project.global_config["repository.pypi.url"] = "https://example.pypi.org/legacy/" project.global_config["repository.pypi.username"] = "foo" - assert ( - project.global_config["repository.pypi.url"] - == "https://example.pypi.org/legacy/" - ) + assert project.global_config["repository.pypi.url"] == "https://example.pypi.org/legacy/" assert project.global_config["repository.pypi.username"] == "foo" del project.global_config["repository.pypi.username"] assert project.global_config["repository.pypi.username"] is None diff --git a/tests/cli/test_hooks.py b/tests/cli/test_hooks.py index d53558c954..5aefb14b4f 100644 --- a/tests/cli/test_hooks.py +++ b/tests/cli/test_hooks.py @@ -223,10 +223,7 @@ def test_skip_option_default_from_env(env, expected, monkeypatch): parametrize_with_commands = pytest.mark.parametrize( "specs", - [ - pytest.param(HookSpecs(command, hooks, fixtures), id=id) - for id, command, hooks, fixtures in KNOWN_COMMAND_HOOKS - ], + [pytest.param(HookSpecs(command, hooks, fixtures), id=id) for id, command, hooks, fixtures in KNOWN_COMMAND_HOOKS], ) parametrize_with_hooks = pytest.mark.parametrize( @@ -241,9 +238,7 @@ def test_skip_option_default_from_env(env, expected, monkeypatch): @pytest.fixture def hooked_project(project, capfd, specs, request): - project.pyproject.settings["scripts"] = { - hook: f"python -c \"print('{hook} CALLED')\"" for hook in KNOWN_HOOKS - } + project.pyproject.settings["scripts"] = {hook: f"python -c \"print('{hook} CALLED')\"" for hook in KNOWN_HOOKS} project.pyproject.write() for fixture in specs.fixtures: request.getfixturevalue(fixture) @@ -267,12 +262,8 @@ def test_hooks(hooked_project, invoke, capfd, specs: HookSpecs): @parametrize_with_hooks # Iterate over hooks as we need a clean slate for each run -def test_skip_option_from_signal( - hooked_project, invoke, capfd, specs: HookSpecs, hook: str -): - invoke( - [*shlex.split(specs.command), f"--skip={hook}"], strict=True, obj=hooked_project - ) +def test_skip_option_from_signal(hooked_project, invoke, capfd, specs: HookSpecs, hook: str): + invoke([*shlex.split(specs.command), f"--skip={hook}"], strict=True, obj=hooked_project) out, _ = capfd.readouterr() assert f"{hook} CALLED" not in out for known_hook in specs.hooks: @@ -282,9 +273,7 @@ def test_skip_option_from_signal( @parametrize_with_commands @pytest.mark.parametrize("option", [":all", ":pre,:post"]) -def test_skip_all_option_from_signal( - hooked_project, invoke, capfd, specs: HookSpecs, option: str -): +def test_skip_all_option_from_signal(hooked_project, invoke, capfd, specs: HookSpecs, option: str): invoke( [*shlex.split(specs.command), f"--skip={option}"], strict=True, @@ -297,9 +286,7 @@ def test_skip_all_option_from_signal( @parametrize_with_commands @pytest.mark.parametrize("prefix", ["pre", "post"]) -def test_skip_pre_post_option_from_signal( - hooked_project, invoke, capfd, specs: HookSpecs, prefix: str -): +def test_skip_pre_post_option_from_signal(hooked_project, invoke, capfd, specs: HookSpecs, prefix: str): invoke( [*shlex.split(specs.command), f"--skip=:{prefix}"], strict=True, diff --git a/tests/cli/test_init.py b/tests/cli/test_init.py index 7078556f05..b864ae4026 100644 --- a/tests/cli/test_init.py +++ b/tests/cli/test_init.py @@ -70,9 +70,7 @@ def test_init_non_interactive(project_no_init, invoke, mocker): return_value=("Testing", "me@example.org"), ) do_init = mocker.patch.object(actions, "do_init") - do_use = mocker.patch.object( - actions, "do_use", return_value=PythonInfo.from_path(sys.executable) - ) + 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}" @@ -104,10 +102,7 @@ def test_init_auto_create_venv(project_no_init, invoke, mocker): project_no_init.project_config["python.use_venv"] = True result = invoke(["init"], input="\n\n\n\n\n\n\n", obj=project_no_init) assert result.exit_code == 0 - assert ( - project_no_init.python.executable.parent.parent - == project_no_init.root / ".venv" - ) + assert project_no_init.python.executable.parent.parent == project_no_init.root / ".venv" def test_init_auto_create_venv_specify_python(project_no_init, invoke, mocker): @@ -119,10 +114,7 @@ def test_init_auto_create_venv_specify_python(project_no_init, invoke, mocker): obj=project_no_init, ) assert result.exit_code == 0 - assert ( - project_no_init.python.executable.parent.parent - == project_no_init.root / ".venv" - ) + assert project_no_init.python.executable.parent.parent == project_no_init.root / ".venv" def test_init_auto_create_venv_answer_no(project_no_init, invoke, mocker): @@ -132,7 +124,4 @@ def test_init_auto_create_venv_answer_no(project_no_init, invoke, mocker): result = invoke(["init"], input="\nn\n\n\n\n\n\n\n", obj=project_no_init) assert result.exit_code == 0 creator.assert_not_called() - assert ( - project_no_init.python.executable.parent.parent - != project_no_init.root / ".venv" - ) + assert project_no_init.python.executable.parent.parent != project_no_init.root / ".venv" diff --git a/tests/cli/test_install.py b/tests/cli/test_install.py index 4fd16d9c35..fdf810edc3 100644 --- a/tests/cli/test_install.py +++ b/tests/cli/test_install.py @@ -198,9 +198,7 @@ def test_sync_with_pure_option(project, working_set, invoke): project.add_dependencies({"requests": parse_requirement("requests>=2.0")}) project.add_dependencies({"django": parse_requirement("django")}, "web", True) invoke(["install"], obj=project, strict=True) - assert all( - p in working_set for p in ("requests", "urllib3", "django", "pytz") - ), list(working_set) + assert all(p in working_set for p in ("requests", "urllib3", "django", "pytz")), list(working_set) actions.do_sync(project, dev=False, only_keep=True) assert "requests" in working_set assert "urllib3" in working_set @@ -210,9 +208,7 @@ def test_sync_with_pure_option(project, working_set, invoke): def test_install_referencing_self_package(project, working_set, invoke): project.add_dependencies({"pytz": parse_requirement("pytz")}, to_group="tz") project.add_dependencies({"urllib3": parse_requirement("urllib3")}, to_group="web") - project.add_dependencies( - {"test-project": parse_requirement("test-project[tz,web]")}, to_group="all" - ) + project.add_dependencies({"test-project": parse_requirement("test-project[tz,web]")}, to_group="all") invoke(["install", "-Gall"], obj=project, strict=True) assert "pytz" in working_set assert "urllib3" in working_set diff --git a/tests/cli/test_list.py b/tests/cli/test_list.py index a0cc41cea6..4e04840c37 100644 --- a/tests/cli/test_list.py +++ b/tests/cli/test_list.py @@ -72,9 +72,7 @@ def test_list_dependency_graph_include_exclude(project, invoke): assert expects == result.outputs # Only include the dev dep - result = invoke( - ["list", "--graph", "--include", "dev", "--exclude", "*"], obj=project - ) + result = invoke(["list", "--graph", "--include", "dev", "--exclude", "*"], obj=project) expects = "demo[security] 0.0.1 [ required: Any ]\n" expects = "".join(expects) assert expects == result.outputs @@ -498,9 +496,7 @@ def read_text(self, *args, **kwargs): # missing package- License is set to UNKNOWN, text saved in LICENCE # using UK spelling - classifier = Distribution( - "classifier", "1.0", metadata={"Classifier": "License :: PDM TEST D"} - ) + classifier = Distribution("classifier", "1.0", metadata={"Classifier": "License :: PDM TEST D"}) classifier_l = _MockPackagePath("classifier-1.0.dist-info", "LICENCE") classifier_l.license_text = "license text for CLASSIFIER here" classifier_l.read_text = lambda *a, **kw: 1 / 0 # make it throw an error @@ -568,9 +564,7 @@ def test_list_bare_sorted_version_resolve(project, invoke, working_set): project.environment.python_requires = PySpecSet(">=3.6") actions.do_add(project, packages=["requests"], sync=False) - result = invoke( - ["list", "--sort", "version", "--resolve"], obj=project, strict=True - ) + result = invoke(["list", "--sort", "version", "--resolve"], obj=project, strict=True) assert "requests" not in working_set expected = ( "+----------------------------------+\n" @@ -620,9 +614,7 @@ def test_list_csv_fields_licences(project, invoke): @pytest.mark.usefixtures("fake_working_set") def test_list_json_fields_licences(project, invoke): - result = invoke( - ["list", "--json", "--fields", "name,version,licenses"], obj=project - ) + result = invoke(["list", "--json", "--fields", "name,version,licenses"], obj=project) expected = [ {"name": "foo", "version": "0.1.0", "licenses": "A License"}, {"name": "bar", "version": "3.0.1", "licenses": "B License"}, @@ -636,9 +628,7 @@ def test_list_json_fields_licences(project, invoke): @pytest.mark.usefixtures("fake_working_set") def test_list_markdown_fields_licences(project, invoke): - result = invoke( - ["list", "--markdown", "--fields", "name,version,licenses"], obj=project - ) + result = invoke(["list", "--markdown", "--fields", "name,version,licenses"], obj=project) expected = ( "# test_project licenses\n" "## foo\n\n" @@ -761,15 +751,7 @@ def test_list_csv_include_exclude(project, invoke): ], obj=project, ) - expected = ( - "name,groups\n" - "certifi,:sub\n" - "chardet,:sub\n" - "demo,dev\n" - "idna,:sub\n" - "requests,:sub\n" - "urllib3,:sub\n" - ) + expected = "name,groups\ncertifi,:sub\nchardet,:sub\ndemo,dev\nidna,:sub\nrequests,:sub\nurllib3,:sub\n" assert expected == result.output # Include all (default) except sub @@ -786,7 +768,7 @@ def test_list_csv_include_exclude(project, invoke): ], obj=project, ) - expected = "name,groups\n" "demo,dev\n" + expected = "name,groups\ndemo,dev\n" assert expected == result.output # Show just the dev group @@ -805,7 +787,7 @@ def test_list_csv_include_exclude(project, invoke): ], obj=project, ) - expected = "name,version,groups\n" "demo,0.0.1,dev\n" + expected = "name,version,groups\ndemo,0.0.1,dev\n" assert expected == result.output # Exclude the dev group. diff --git a/tests/cli/test_lock.py b/tests/cli/test_lock.py index 35cc7f8141..87a2133add 100644 --- a/tests/cli/test_lock.py +++ b/tests/cli/test_lock.py @@ -36,9 +36,7 @@ def test_lock_refresh(invoke, project, repository): "http://example1.com/requests-2.19.1-py3-none-any.whl": "sha256:abcdef123456", } repository.get_hashes = lambda c: ( - {Link(url): hash for url, hash in url_hashes.items()} - if c.identify() == "requests" - else {} + {Link(url): hash for url, hash in url_hashes.items()} if c.identify() == "requests" else {} ) assert not project.is_lockfile_hash_match() result = invoke(["lock", "--refresh", "-v"], obj=project) diff --git a/tests/cli/test_others.py b/tests/cli/test_others.py index 0b700fc9e7..c5153a1704 100644 --- a/tests/cli/test_others.py +++ b/tests/cli/test_others.py @@ -23,9 +23,7 @@ def test_project_no_init_error(project_no_init): actions.do_lock, actions.do_update, ): - with pytest.raises( - PdmException, match="The pyproject.toml has not been initialized yet" - ): + with pytest.raises(PdmException, match="The pyproject.toml has not been initialized yet"): handler(project_no_init) @@ -87,9 +85,7 @@ def test_import_other_format_file(project, invoke, filename): def test_import_requirement_no_overwrite(project, invoke, tmp_path): project.add_dependencies({"requests": parse_requirement("requests")}) tmp_path.joinpath("reqs.txt").write_text("flask\nflask-login\n") - result = invoke( - ["import", "-dGweb", str(tmp_path.joinpath("reqs.txt"))], obj=project - ) + result = invoke(["import", "-dGweb", str(tmp_path.joinpath("reqs.txt"))], obj=project) assert result.exit_code == 0, result.stderr assert list(project.get_dependencies()) == ["requests"] assert list(project.get_dependencies("web")) == ["flask", "flask-login"] @@ -151,13 +147,9 @@ def test_export_to_requirements_txt(invoke, fixture_project): assert result.exit_code == 0 assert result.output.strip() == requirements_pyproject.read_text().strip() - result = invoke( - ["export", "-o", str(project.root / "requirements_output.txt")], obj=project - ) + result = invoke(["export", "-o", str(project.root / "requirements_output.txt")], obj=project) assert result.exit_code == 0 - assert ( - project.root / "requirements_output.txt" - ).read_text() == requirements_txt.read_text() + assert (project.root / "requirements_output.txt").read_text() == requirements_txt.read_text() def test_export_doesnt_include_dep_with_extras(invoke, fixture_project): diff --git a/tests/cli/test_publish.py b/tests/cli/test_publish.py index 251b2053f1..000d84169b 100644 --- a/tests/cli/test_publish.py +++ b/tests/cli/test_publish.py @@ -22,9 +22,7 @@ def test_package_parse_metadata(filename): meta = package.metadata_dict assert meta["name"] == "demo" assert meta["version"] == "0.0.1" - assert all( - f"{hash_name}_digest" in meta for hash_name in ["md5", "sha256", "blake2_256"] - ) + assert all(f"{hash_name}_digest" in meta for hash_name in ["md5", "sha256", "blake2_256"]) if filename.endswith(".whl"): assert meta["pyversion"] == "py2.py3" @@ -38,24 +36,20 @@ 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["summary"] == "caj2pdf 重新组织,方便打包与安装" # noqa: RUF001 assert meta["author_email"] == "张三 " 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 - ) + package = PackageFile.from_filename(str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None) tmp_path.joinpath("signature.asc").write_bytes(b"test gpg signature") package.add_gpg_signature(str(tmp_path / "signature.asc"), "signature.asc") assert package.gpg_signature == ("signature.asc", b"test gpg signature") def test_package_call_gpg_sign(): - package = PackageFile.from_filename( - str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None - ) + package = PackageFile.from_filename(str(FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl"), None) try: package.sign(None) finally: @@ -75,16 +69,10 @@ def test_repository_get_release_urls(project): "demo-0.0.1.zip", ] ] - repository = Repository( - project, "https://upload.pypi.org/legacy/", "abc", "123", None - ) - assert repository.get_release_urls(package_files) == { - "https://pypi.org/project/demo/0.0.1/" - } + repository = Repository(project, "https://upload.pypi.org/legacy/", "abc", "123", None) + assert repository.get_release_urls(package_files) == {"https://pypi.org/project/demo/0.0.1/"} - repository = Repository( - project, "https://example.pypi.org/legacy/", "abc", "123", None - ) + repository = Repository(project, "https://example.pypi.org/legacy/", "abc", "123", None) assert not repository.get_release_urls(package_files) @@ -125,9 +113,7 @@ def test_publish_package_with_signature(project, uploaded, invoke): @pytest.mark.usefixtures("local_finder") def test_publish_and_build_in_one_run(fixture_project, invoke, mock_pypi): project = fixture_project("demo-module") - result = invoke( - ["publish", "--username=abc", "--password=123"], obj=project, strict=True - ).output + result = invoke(["publish", "--username=abc", "--password=123"], obj=project, strict=True).output mock_pypi.assert_called() assert "Uploading demo_module-0.1.0-py3-none-any.whl" in result @@ -138,9 +124,7 @@ def test_publish_and_build_in_one_run(fixture_project, invoke, mock_pypi): def test_publish_cli_args_and_env_var_precedence(project, monkeypatch): repo = PublishCommand.get_repository( project, - Namespace( - repository=None, username="foo", password="bar", ca_certs="custom.pem" - ), + Namespace(repository=None, username="foo", password="bar", ca_certs="custom.pem"), ) assert repo.url == "https://upload.pypi.org/legacy/" assert repo.session.auth == ("foo", "bar") @@ -162,9 +146,7 @@ def test_publish_cli_args_and_env_var_precedence(project, monkeypatch): repo = PublishCommand.get_repository( project, - Namespace( - repository="pypi", username="foo", password=None, ca_certs="custom.pem" - ), + Namespace(repository="pypi", username="foo", password=None, ca_certs="custom.pem"), ) assert repo.url == "https://upload.pypi.org/legacy/" assert repo.session.auth == ("foo", "secret") diff --git a/tests/cli/test_remove.py b/tests/cli/test_remove.py index c518e8e54b..5c627f513f 100644 --- a/tests/cli/test_remove.py +++ b/tests/cli/test_remove.py @@ -70,10 +70,7 @@ def test_remove_package_exist_in_multi_groups(project, working_set): actions.do_add(project, packages=["requests"]) actions.do_add(project, dev=True, packages=["urllib3"]) actions.do_remove(project, dev=True, packages=["urllib3"]) - assert all( - "urllib3" not in line - for line in project.pyproject.settings["dev-dependencies"]["dev"] - ) + assert all("urllib3" not in line for line in project.pyproject.settings["dev-dependencies"]["dev"]) assert "urllib3" in working_set assert "requests" in working_set diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py index 7cb5ba0a42..abd8c68a8d 100644 --- a/tests/cli/test_run.py +++ b/tests/cli/test_run.py @@ -29,9 +29,7 @@ def _args(project): def test_pep582_launcher_for_python_interpreter(project, local_finder, invoke): - project.root.joinpath("main.py").write_text( - "import first;print(first.first([0, False, 1, 2]))\n" - ) + project.root.joinpath("main.py").write_text("import first;print(first.first([0, False, 1, 2]))\n") result = invoke(["add", "first"], obj=project) assert result.exit_code == 0, result.stderr env = os.environ.copy() @@ -106,9 +104,7 @@ def test_run_cmd_script(project, invoke): def test_run_cmd_script_with_array(project, invoke): - project.pyproject.settings["scripts"] = { - "test_script": ["python", "-c", "import sys; sys.exit(22)"] - } + project.pyproject.settings["scripts"] = {"test_script": ["python", "-c", "import sys; sys.exit(22)"]} project.pyproject.write() result = invoke(["run", "test_script"], obj=project) assert result.exit_code == 22 @@ -172,9 +168,7 @@ def test_run_shell_script_with_args_placeholder(project, invoke, args, expected) pytest.param([], "default", id="with-default"), ), ) -def test_run_shell_script_with_args_placeholder_with_default( - project, invoke, args, expected -): +def test_run_shell_script_with_args_placeholder_with_default(project, invoke, args, expected): project.pyproject.settings["scripts"] = { "test_script": { "shell": "echo {args:default} > output.txt", @@ -247,9 +241,7 @@ def test_run_script_with_extra_args(project, invoke, capfd): pytest.param(["python", "test_script.py", "{args}", "-x"], id="as-list"), ), ) -def test_run_script_with_args_placeholder( - project, invoke, capfd, script, args, expected -): +def test_run_script_with_args_placeholder(project, invoke, capfd, script, args, expected): (project.root / "test_script.py").write_text( textwrap.dedent( """ @@ -277,14 +269,10 @@ def test_run_script_with_args_placeholder( "script", ( pytest.param("python test_script.py {args:--default --value} -x", id="as-str"), - pytest.param( - ["python", "test_script.py", "{args:--default --value}", "-x"], id="as-list" - ), + pytest.param(["python", "test_script.py", "{args:--default --value}", "-x"], id="as-list"), ), ) -def test_run_script_with_args_placeholder_with_default( - project, invoke, capfd, script, args, expected -): +def test_run_script_with_args_placeholder_with_default(project, invoke, capfd, script, args, expected): (project.root / "test_script.py").write_text( textwrap.dedent( """ @@ -332,9 +320,7 @@ def test_run_expand_env_vars(project, invoke, capfd, monkeypatch): def test_run_script_with_env_defined(project, invoke, capfd): (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'))") - project.pyproject.settings["scripts"] = { - "test_script": {"cmd": "python test_script.py", "env": {"FOO": "bar"}} - } + project.pyproject.settings["scripts"] = {"test_script": {"cmd": "python test_script.py", "env": {"FOO": "bar"}}} project.pyproject.write() capfd.readouterr() with cd(project.root): @@ -343,9 +329,7 @@ def test_run_script_with_env_defined(project, invoke, capfd): def test_run_script_with_dotenv_file(project, invoke, capfd, monkeypatch): - (project.root / "test_script.py").write_text( - "import os; print(os.getenv('FOO'), os.getenv('BAR'))" - ) + (project.root / "test_script.py").write_text("import os; print(os.getenv('FOO'), os.getenv('BAR'))") project.pyproject.settings["scripts"] = { "test_override": { "cmd": "python test_script.py", @@ -394,22 +378,11 @@ def test_run_show_list_of_scripts(project, invoke): project.pyproject.write() result = invoke(["run", "--list"], obj=project) result_lines = result.output.splitlines()[3:] - assert ( - result_lines[0][1:-1].strip() == "test_cmd │ cmd │ flask db upgrade" - ) + assert result_lines[0][1:-1].strip() == "test_cmd │ cmd │ flask db upgrade" sep = termui.Emoji.ARROW_SEPARATOR - assert ( - result_lines[1][1:-1].strip() - == f"test_composite │ composite │ test_cmd {sep} test_script {sep} test_shell" - ) - assert ( - result_lines[2][1:-1].strip() - == f"test_multi │ cmd │ I am a multilines{termui.Emoji.ELLIPSIS}" - ) - assert ( - result_lines[3][1:-1].strip() - == "test_script │ call │ call a python function" - ) + assert result_lines[1][1:-1].strip() == f"test_composite │ composite │ test_cmd {sep} test_script {sep} test_shell" + assert result_lines[2][1:-1].strip() == f"test_multi │ cmd │ I am a multilines{termui.Emoji.ELLIPSIS}" + assert result_lines[3][1:-1].strip() == "test_script │ call │ call a python function" assert result_lines[4][1:-1].strip() == "test_shell │ shell │ shell command" @@ -420,9 +393,7 @@ def test_run_with_another_project_root(project, invoke, capfd, explicit_python): project.pyproject.write() invoke(["add", "first"], obj=project) with TemporaryDirectory(prefix="pytest-run-") as tmp_dir: - Path(tmp_dir).joinpath("main.py").write_text( - "import first;print(first.first([0, False, 1, 2]))\n" - ) + Path(tmp_dir).joinpath("main.py").write_text("import first;print(first.first([0, False, 1, 2]))\n") capfd.readouterr() with cd(tmp_dir): args = ["run", "-p", str(project.root), "main.py"] @@ -441,9 +412,7 @@ def test_import_another_sitecustomize(project, invoke, capfd): project.root.joinpath("foo.py").write_text("import os;print(os.getenv('FOO'))") # ensure there have at least one sitecustomize can be imported # there may have more than one sitecustomize.py in sys.path - project.root.joinpath("sitecustomize.py").write_text( - "import os;os.environ['FOO'] = 'foo'" - ) + project.root.joinpath("sitecustomize.py").write_text("import os;os.environ['FOO'] = 'foo'") env = os.environ.copy() paths = env.get("PYTHONPATH") this_path = str(project.root) @@ -612,9 +581,7 @@ def test_composite_can_pass_parameters(project, invoke, capfd, _args): pytest.param([], "", id="without-args"), ), ) -def test_composite_only_pass_parameters_to_subtasks_with_args( - project, invoke, capfd, _args, args, expected -): +def test_composite_only_pass_parameters_to_subtasks_with_args(project, invoke, capfd, _args, args, expected): project.pyproject.settings["scripts"] = { "test": {"composite": ["first", "second {args} key=value"]}, "first": "python args.py First", diff --git a/tests/cli/test_self_command.py b/tests/cli/test_self_command.py index 948c406989..fc4174b5b7 100644 --- a/tests/cli/test_self_command.py +++ b/tests/cli/test_self_command.py @@ -12,12 +12,8 @@ def mock_distribution(metadata, entry_points=()): DISTRIBUTIONS = [ - mock_distribution( - {"Name": "foo", "Version": "1.0.0", "Summary": "Foo package"}, ["pdm.plugin"] - ), - mock_distribution( - {"Name": "bar", "Version": "2.0.0", "Summary": "Bar package"}, ["pdm"] - ), + mock_distribution({"Name": "foo", "Version": "1.0.0", "Summary": "Foo package"}, ["pdm.plugin"]), + mock_distribution({"Name": "bar", "Version": "2.0.0", "Summary": "Bar package"}, ["pdm"]), mock_distribution({"Name": "baz", "Version": "3.0.0", "Summary": "Baz package"}), ] @@ -31,9 +27,7 @@ def mock_pip(monkeypatch): @pytest.fixture() def mock_all_distributions(monkeypatch): - monkeypatch.setattr( - self_cmd, "_get_distributions", Mock(return_value=DISTRIBUTIONS) - ) + monkeypatch.setattr(self_cmd, "_get_distributions", Mock(return_value=DISTRIBUTIONS)) @pytest.fixture() @@ -66,9 +60,7 @@ def test_self_add(invoke, mock_pip): result = invoke(["self", "add", "--pip-args", "--force-reinstall --upgrade", "foo"]) assert result.exit_code == 0, result.stderr - mock_pip.assert_called_with( - ANY, ["install", "--force-reinstall", "--upgrade", "foo"] - ) + mock_pip.assert_called_with(ANY, ["install", "--force-reinstall", "--upgrade", "foo"]) def test_self_remove(invoke, mock_pip, monkeypatch): diff --git a/tests/cli/test_update.py b/tests/cli/test_update.py index 584201017f..5493f6e8bf 100644 --- a/tests/cli/test_update.py +++ b/tests/cli/test_update.py @@ -52,9 +52,7 @@ def test_update_all_packages(project, repository, capsys, strategy): actions.do_update(project, strategy=strategy) locked_candidates = project.locked_repository.all_candidates assert locked_candidates["requests"].version == "2.20.0" - assert locked_candidates["chardet"].version == ( - "3.0.5" if strategy == "all" else "3.0.4" - ) + assert locked_candidates["chardet"].version == ("3.0.5" if strategy == "all" else "3.0.4") assert locked_candidates["pytz"].version == "2019.6" out, _ = capsys.readouterr() update_num = 3 if strategy == "all" else 2 @@ -171,9 +169,7 @@ def test_update_with_package_and_groups_argument(project): @pytest.mark.usefixtures("repository", "working_set") def test_update_with_prerelease_without_package_argument(project): actions.do_add(project, packages=["requests"]) - with pytest.raises( - PdmUsageError, match="--prerelease must be used with packages given" - ): + with pytest.raises(PdmUsageError, match="--prerelease must be used with packages given"): actions.do_update(project, prerelease=True) @@ -187,9 +183,7 @@ def test_update_existing_package_with_prerelease(project, working_set): assert project.pyproject.metadata["dependencies"][0] == "urllib3~=1.22" assert working_set["urllib3"].version == "1.23b0" - actions.do_update( - project, packages=["urllib3"], prerelease=True, unconstrained=True - ) + actions.do_update(project, packages=["urllib3"], prerelease=True, unconstrained=True) assert project.pyproject.metadata["dependencies"][0] == "urllib3<2,>=1.23b0" assert working_set["urllib3"].version == "1.23b0" diff --git a/tests/cli/test_venv.py b/tests/cli/test_venv.py index 9440b2912e..1c68df855f 100644 --- a/tests/cli/test_venv.py +++ b/tests/cli/test_venv.py @@ -31,9 +31,7 @@ def test_venv_create(invoke, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) assert os.path.exists(venv_path) assert "python.path" not in project.project_config @@ -54,9 +52,7 @@ def test_venv_list(invoke, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = invoke(["venv", "list"], obj=project) assert result.exit_code == 0, result.stderr @@ -68,9 +64,7 @@ def test_venv_remove(invoke, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] result = invoke(["venv", "remove", "non-exist"], obj=project) @@ -100,9 +94,7 @@ def test_venv_activate(invoke, mocker, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) key = os.path.basename(venv_path)[len(get_venv_prefix(project)) :] mocker.patch("shellingham.detect_shell", return_value=("bash", None)) @@ -123,9 +115,7 @@ def test_venv_activate_custom_prompt(invoke, mocker, project): creator = mocker.patch("pdm.cli.commands.venv.backends.Backend.create") result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - creator.assert_called_once_with( - None, [], False, False, project.project_config["venv.prompt"], False - ) + creator.assert_called_once_with(None, [], False, False, project.project_config["venv.prompt"], False) def test_venv_activate_project_without_python(invoke, project): @@ -176,9 +166,7 @@ def test_venv_purge(invoke, project): result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = invoke(["venv", "purge"], input="y", obj=project) assert result.exit_code == 0, result.stderr assert not os.path.exists(venv_path) @@ -189,9 +177,7 @@ def test_venv_purge_force(invoke, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = invoke(["venv", "purge", "-f"], obj=project) assert result.exit_code == 0, result.stderr assert not os.path.exists(venv_path) @@ -206,9 +192,7 @@ def test_venv_purge_interactive(invoke, user_choices, is_path_exists, project): project.project_config["venv.in_project"] = False result = invoke(["venv", "create"], obj=project) assert result.exit_code == 0, result.stderr - venv_path = re.match( - r"Virtualenv (.+) is created successfully", result.output - ).group(1) + venv_path = re.match(r"Virtualenv (.+) is created successfully", result.output).group(1) result = invoke(["venv", "purge", "-i"], input=user_choices, obj=project) assert result.exit_code == 0, result.stderr assert os.path.exists(venv_path) == is_path_exists diff --git a/tests/models/test_backends.py b/tests/models/test_backends.py index b8fbb0f64d..d7ffadc3b3 100644 --- a/tests/models/test_backends.py +++ b/tests/models/test_backends.py @@ -16,13 +16,11 @@ def _setup_backend(project: Project, backend: str): project.root.joinpath("test_project").mkdir() project.root.joinpath("test_project/__init__.py").touch() if backend == "setuptools": - project.pyproject._data.setdefault("tool", {}).setdefault( - "setuptools", {} - ).update(packages=["test_project"]) + project.pyproject._data.setdefault("tool", {}).setdefault("setuptools", {}).update(packages=["test_project"]) elif backend == "hatchling": - project.pyproject._data.setdefault("tool", {}).setdefault( - "hatch", {} - ).setdefault("metadata", {}).update({"allow-direct-references": True}) + project.pyproject._data.setdefault("tool", {}).setdefault("hatch", {}).setdefault("metadata", {}).update( + {"allow-direct-references": True} + ) project.pyproject.write() project._environment = None assert isinstance(project.backend, backend_cls) @@ -50,9 +48,7 @@ def test_project_backend(project, working_set, backend, invoke): if backend not in ("hatchling", "pdm-backend"): candidate = project.make_self_candidate() # We skip hatchling here to avoid installing hatchling into the build env - metadata_dependency = candidate.prepare( - project.environment - ).metadata.requires[0] + metadata_dependency = candidate.prepare(project.environment).metadata.requires[0] assert metadata_dependency == f"demo @ {demo_url}" @@ -72,15 +68,9 @@ def test_pdm_pep517_expand_variables(monkeypatch): root_url = path_to_url(root.as_posix()) backend = get_backend("pdm-pep517")(root) monkeypatch.setenv("BAR", "bar") - assert ( - backend.expand_line("demo @ file:///${PROJECT_ROOT}/demo") - == f"demo @ {root_url}/demo" - ) + assert backend.expand_line("demo @ file:///${PROJECT_ROOT}/demo") == f"demo @ {root_url}/demo" assert backend.expand_line("demo==${BAR}") == "demo==bar" - assert ( - backend.relative_path_to_url("demo package") - == "file:///${PROJECT_ROOT}/demo%20package" - ) + assert backend.relative_path_to_url("demo package") == "file:///${PROJECT_ROOT}/demo%20package" assert backend.relative_path_to_url("../demo") == "file:///${PROJECT_ROOT}/../demo" diff --git a/tests/models/test_candidates.py b/tests/models/test_candidates.py index b071a1fdbd..5ee2373bc0 100644 --- a/tests/models/test_candidates.py +++ b/tests/models/test_candidates.py @@ -64,8 +64,7 @@ def test_parse_artifact_metadata(requirement_line, project): @pytest.mark.usefixtures("local_finder") def test_parse_metadata_with_extras(project): req = parse_requirement( - f"demo[tests,security] @ file://" - f"{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}" + f"demo[tests,security] @ file://{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}" ) candidate = Candidate(req) prepared = candidate.prepare(project.environment) @@ -78,9 +77,7 @@ def test_parse_metadata_with_extras(project): @pytest.mark.usefixtures("local_finder") def test_parse_remote_link_metadata(project): - req = parse_requirement( - "http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl" - ) + req = parse_requirement("http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl") candidate = Candidate(req) prepared = candidate.prepare(project.environment) assert prepared.link.is_wheel @@ -94,9 +91,7 @@ def test_parse_remote_link_metadata(project): @pytest.mark.usefixtures("local_finder") def test_extras_warning(project, recwarn): - req = parse_requirement( - "demo[foo] @ http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl" - ) + req = parse_requirement("demo[foo] @ http://fixtures.test/artifacts/demo-0.0.1-py2.py3-none-any.whl") candidate = Candidate(req) prepared = candidate.prepare(project.environment) assert prepared.link.is_wheel @@ -109,9 +104,7 @@ def test_extras_warning(project, recwarn): @pytest.mark.usefixtures("local_finder") def test_parse_abnormal_specifiers(project): - req = parse_requirement( - "http://fixtures.test/artifacts/celery-4.4.2-py2.py3-none-any.whl" - ) + req = parse_requirement("http://fixtures.test/artifacts/celery-4.4.2-py2.py3-none-any.whl") candidate = Candidate(req) assert candidate.prepare(project.environment).get_dependencies_from_metadata() @@ -120,8 +113,7 @@ def test_parse_abnormal_specifiers(project): @pytest.mark.parametrize( "req_str", [ - "demo @ file:///${PROJECT_ROOT}/tests/fixtures/artifacts" - "/demo-0.0.1-py2.py3-none-any.whl", + "demo @ file:///${PROJECT_ROOT}/tests/fixtures/artifacts/demo-0.0.1-py2.py3-none-any.whl", "demo @ file:///${PROJECT_ROOT}/tests/fixtures/artifacts/demo-0.0.1.tar.gz", "demo @ file:///${PROJECT_ROOT}/tests/fixtures/projects/demo", "-e ./tests/fixtures/projects/demo", @@ -152,9 +144,7 @@ def test_expand_project_root_in_url(req_str, core): def test_parse_project_file_on_build_error(project): req = parse_requirement(f"{(FIXTURES / 'projects/demo-failure').as_posix()}") candidate = Candidate(req) - assert sorted( - candidate.prepare(project.environment).get_dependencies_from_metadata() - ) == [ + assert sorted(candidate.prepare(project.environment).get_dependencies_from_metadata()) == [ 'chardet; os_name == "nt"', "idna", ] @@ -185,23 +175,17 @@ def test_parse_project_file_on_build_error_no_dep(project): @pytest.mark.usefixtures("local_finder") def test_parse_poetry_project_metadata(project, is_editable): - req = parse_requirement( - f"{(FIXTURES / 'projects/poetry-demo').as_posix()}", is_editable - ) + req = parse_requirement(f"{(FIXTURES / 'projects/poetry-demo').as_posix()}", is_editable) candidate = Candidate(req) requests_dep = "requests<3.0,>=2.6" - assert candidate.prepare(project.environment).get_dependencies_from_metadata() == [ - requests_dep - ] + assert candidate.prepare(project.environment).get_dependencies_from_metadata() == [requests_dep] assert candidate.name == "poetry-demo" assert candidate.version == "0.1.0" @pytest.mark.usefixtures("local_finder") def test_parse_flit_project_metadata(project, is_editable): - req = parse_requirement( - f"{(FIXTURES / 'projects/flit-demo').as_posix()}", is_editable - ) + req = parse_requirement(f"{(FIXTURES / 'projects/flit-demo').as_posix()}", is_editable) candidate = Candidate(req) deps = candidate.prepare(project.environment).get_dependencies_from_metadata() requests_dep = "requests>=2.6" @@ -213,28 +197,17 @@ def test_parse_flit_project_metadata(project, is_editable): @pytest.mark.usefixtures("vcs", "local_finder") def test_vcs_candidate_in_subdirectory(project, is_editable): - line = ( - "git+https://github.com/test-root/demo-parent-package.git" - "@master#egg=package-a&subdirectory=package-a" - ) + line = "git+https://github.com/test-root/demo-parent-package.git@master#egg=package-a&subdirectory=package-a" req = parse_requirement(line, is_editable) candidate = Candidate(req) - assert candidate.prepare(project.environment).get_dependencies_from_metadata() == [ - "flask" - ] + assert candidate.prepare(project.environment).get_dependencies_from_metadata() == ["flask"] assert candidate.version == "0.1.0" - line = ( - "git+https://github.com/test-root/demo-parent-package.git" - "@master#egg=package-b&subdirectory=package-b" - ) + line = "git+https://github.com/test-root/demo-parent-package.git@master#egg=package-b&subdirectory=package-b" req = parse_requirement(line, is_editable) candidate = Candidate(req) expected_deps = ["django"] - assert ( - candidate.prepare(project.environment).get_dependencies_from_metadata() - == expected_deps - ) + assert candidate.prepare(project.environment).get_dependencies_from_metadata() == expected_deps assert candidate.version == "0.1.0" @@ -243,9 +216,7 @@ def test_sdist_candidate_with_wheel_cache(project, mocker): file_link = Link(path_to_url((FIXTURES / "artifacts/demo-0.0.1.tar.gz").as_posix())) built_path = FIXTURES / "artifacts/demo-0.0.1-py2.py3-none-any.whl" wheel_cache = project.make_wheel_cache() - cache_path = wheel_cache.get_path_for_link( - file_link, project.environment.target_python - ) + cache_path = wheel_cache.get_path_for_link(file_link, project.environment.target_python) if not cache_path.exists(): cache_path.mkdir(parents=True) shutil.copy2(built_path, cache_path) @@ -272,9 +243,7 @@ def test_cache_vcs_immutable_revision(project): wheel.relative_to(project.cache_dir) assert candidate.get_revision() == "1234567890abcdef" - req = parse_requirement( - "git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo" - ) + req = parse_requirement("git+https://github.com/test-root/demo.git@1234567890abcdef#egg=demo") candidate = Candidate(req) wheel = candidate.prepare(project.environment).build() assert wheel.relative_to(project.cache_dir) @@ -304,18 +273,10 @@ def test_invalidate_incompatible_wheel_link(project): link=Link("http://fixtures.test/artifacts/demo-0.0.1-cp36-cp36m-win_amd64.whl"), ).prepare(project.environment) prepared.obtain(True) - assert ( - prepared.wheel.name - == prepared.link.filename - == "demo-0.0.1-cp36-cp36m-win_amd64.whl" - ) + assert prepared.wheel.name == prepared.link.filename == "demo-0.0.1-cp36-cp36m-win_amd64.whl" prepared.obtain(False) - assert ( - prepared.wheel.name - == prepared.link.filename - == "demo-0.0.1-py2.py3-none-any.whl" - ) + assert prepared.wheel.name == prepared.link.filename == "demo-0.0.1-py2.py3-none-any.whl" def test_legacy_pep345_tag_link(project): @@ -349,9 +310,7 @@ def test_find_candidates_from_find_links(project): def test_parse_metadata_from_pep621(project, mocker): builder = mocker.patch("pdm.builders.wheel.WheelBuilder.build") - req = parse_requirement( - f"test-hatch @ file://{FIXTURES.as_posix()}/projects/test-hatch-static" - ) + req = parse_requirement(f"test-hatch @ file://{FIXTURES.as_posix()}/projects/test-hatch-static") candidate = Candidate(req) distribution = candidate.prepare(project.environment).metadata assert sorted(distribution.requires) == ["click", "requests"] @@ -360,9 +319,7 @@ def test_parse_metadata_from_pep621(project, mocker): def test_parse_metadata_with_dynamic_fields(project, local_finder): - req = parse_requirement( - f"demo-package @ file://{FIXTURES.as_posix()}/projects/demo-src-package" - ) + req = parse_requirement(f"demo-package @ file://{FIXTURES.as_posix()}/projects/demo-src-package") candidate = Candidate(req) metadata = candidate.prepare(project.environment).metadata assert not metadata.requires diff --git a/tests/models/test_requirements.py b/tests/models/test_requirements.py index 374d9d8cbe..32b9a96a50 100644 --- a/tests/models/test_requirements.py +++ b/tests/models/test_requirements.py @@ -83,11 +83,7 @@ def test_illegal_requirement_line(line, expected): parse_requirement(line) -@pytest.mark.parametrize( - "line", ["requests >= 2.19.0", "https://github.com/pypa/pip/archive/1.3.1.zip"] -) +@pytest.mark.parametrize("line", ["requests >= 2.19.0", "https://github.com/pypa/pip/archive/1.3.1.zip"]) def test_not_supported_editable_requirement(line): - with pytest.raises( - RequirementError, match="Editable requirement is only supported" - ): + with pytest.raises(RequirementError, match="Editable requirement is only supported"): parse_requirement(line, True) diff --git a/tests/models/test_specifiers.py b/tests/models/test_specifiers.py index 362034e327..af8c9f2c3e 100644 --- a/tests/models/test_specifiers.py +++ b/tests/models/test_specifiers.py @@ -23,30 +23,22 @@ pytest.param( ">=3.4.*", ">=3.4", - marks=pytest.mark.xfail( - PACKAGING_22, reason="packaging 22.0 doesn't parse it" - ), + marks=pytest.mark.xfail(PACKAGING_22, reason="packaging 22.0 doesn't parse it"), ), pytest.param( ">3.4.*", ">=3.5", - marks=pytest.mark.xfail( - PACKAGING_22, reason="packaging 22.0 doesn't parse it" - ), + marks=pytest.mark.xfail(PACKAGING_22, reason="packaging 22.0 doesn't parse it"), ), pytest.param( "<=3.4.*", "<3.4", - marks=pytest.mark.xfail( - PACKAGING_22, reason="packaging 22.0 doesn't parse it" - ), + marks=pytest.mark.xfail(PACKAGING_22, reason="packaging 22.0 doesn't parse it"), ), pytest.param( "<3.4.*", "<3.4", - marks=pytest.mark.xfail( - PACKAGING_22, reason="packaging 22.0 doesn't parse it" - ), + marks=pytest.mark.xfail(PACKAGING_22, reason="packaging 22.0 doesn't parse it"), ), ("<3.10.0a6", "<3.10.0a6"), ("<3.10.2a3", "<3.10.2a3"), @@ -120,9 +112,7 @@ def test_impossible_pyspec(): # 11.* normalizes to 11.0 ">=3.11.*", ">=3.11.0rc", - marks=pytest.mark.xfail( - PACKAGING_22, reason="packaging 22.0 doesn't parse it" - ), + marks=pytest.mark.xfail(PACKAGING_22, reason="packaging 22.0 doesn't parse it"), ), ], ) diff --git a/tests/resolver/test_resolve.py b/tests/resolver/test_resolve.py index f556f62023..0b338c7a7b 100644 --- a/tests/resolver/test_resolve.py +++ b/tests/resolver/test_resolve.py @@ -33,9 +33,7 @@ def resolve_func( with ui.open_spinner("Resolving dependencies") as spin, ui.logging("lock"): reporter = SpinnerReporter(spin, requirements) resolver = Resolver(provider, reporter) - mapping, *_ = _resolve( - resolver, requirements, repository.environment.python_requires - ) + mapping, *_ = _resolve(resolver, requirements, repository.environment.python_requires) return mapping return resolve_func @@ -161,9 +159,7 @@ def test_resolve_conflicting_dependencies(resolve, repository): @pytest.mark.parametrize("overrides", ["2.1", ">=1.8", "==2.1"]) -def test_resolve_conflicting_dependencies_with_overrides( - project, resolve, repository, overrides -): +def test_resolve_conflicting_dependencies_with_overrides(project, resolve, repository, overrides): repository.add_candidate("foo", "0.1.0") repository.add_dependencies("foo", "0.1.0", ["hoho>=2.0"]) repository.add_candidate("bar", "0.1.0") diff --git a/tests/test_formats.py b/tests/test_formats.py index 04febbcb09..898314391f 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -45,13 +45,9 @@ def test_convert_requirements_file(project, is_dev): def test_convert_requirements_file_without_name(project, vcs): req_file = project.root.joinpath("reqs.txt") - project.root.joinpath("reqs.txt").write_text( - "git+https://github.com/test-root/demo.git\n" - ) + project.root.joinpath("reqs.txt").write_text("git+https://github.com/test-root/demo.git\n") assert requirements.check_fingerprint(project, str(req_file)) - result, _ = requirements.convert( - project, str(req_file), Namespace(dev=False, group=None) - ) + result, _ = requirements.convert(project, str(req_file), Namespace(dev=False, group=None)) assert result["dependencies"] == ["git+https://github.com/test-root/demo.git"] @@ -60,9 +56,7 @@ def test_convert_poetry(project): golden_file = FIXTURES / "pyproject.toml" assert poetry.check_fingerprint(project, golden_file) with cd(FIXTURES): - result, settings = poetry.convert( - project, golden_file, Namespace(dev=False, group=None) - ) + result, settings = poetry.convert(project, golden_file, Namespace(dev=False, group=None)) assert result["authors"][0] == { "name": "Sébastien Eustace", @@ -84,9 +78,7 @@ def test_convert_poetry(project): assert len(settings["dev-dependencies"]["dev"]) == 2 assert result["scripts"] == {"poetry": "poetry.console:run"} - assert result["entry-points"]["blogtool.parsers"] == { - ".rst": "some_module:SomeClass" - } + assert result["entry-points"]["blogtool.parsers"] == {".rst": "some_module:SomeClass"} build = settings["build"] assert build["includes"] == ["lib/my_package", "tests", "CHANGELOG.md"] assert build["excludes"] == ["my_package/excluded.py"] @@ -120,10 +112,7 @@ def test_convert_flit(project): ] assert result["scripts"]["flit"] == "flit:main" - assert ( - result["entry-points"]["pygments.lexers"]["dogelang"] - == "dogelang.lexer:DogeLexer" - ) + assert result["entry-points"]["pygments.lexers"]["dogelang"] == "dogelang.lexer:DogeLexer" build = settings["build"] assert build["includes"] == ["doc/"] assert build["excludes"] == ["doc/*.html"] @@ -132,9 +121,7 @@ def test_convert_flit(project): def test_import_requirements_with_group(project): golden_file = FIXTURES / "requirements.txt" assert requirements.check_fingerprint(project, golden_file) - result, settings = requirements.convert( - project, golden_file, Namespace(dev=False, group="test") - ) + result, settings = requirements.convert(project, golden_file, Namespace(dev=False, group="test")) group = result["optional-dependencies"]["test"] dev_group = settings["dev-dependencies"]["dev"] @@ -148,14 +135,9 @@ def test_import_requirements_with_group(project): def test_export_expand_env_vars_in_source(project, monkeypatch): monkeypatch.setenv("USER", "foo") monkeypatch.setenv("PASSWORD", "bar") - project.pyproject.settings["source"] = [ - {"url": "https://${USER}:${PASSWORD}@test.pypi.org/simple", "name": "pypi"} - ] + project.pyproject.settings["source"] = [{"url": "https://${USER}:${PASSWORD}@test.pypi.org/simple", "name": "pypi"}] result = requirements.export(project, [], Namespace()) - assert ( - result.strip().splitlines()[-1] - == "--index-url https://foo:bar@test.pypi.org/simple" - ) + assert result.strip().splitlines()[-1] == "--index-url https://foo:bar@test.pypi.org/simple" def test_export_replace_project_root(project): diff --git a/tests/test_installer.py b/tests/test_installer.py index 2ff74ca77c..75050287aa 100644 --- a/tests/test_installer.py +++ b/tests/test_installer.py @@ -46,9 +46,7 @@ def test_uninstall_commit_rollback(project): installer.install(candidate) lib_file = os.path.join(lib_path, "demo.py") assert os.path.exists(lib_file) - remove_paths = installer.get_paths_to_remove( - project.environment.get_working_set()["demo"] - ) + remove_paths = installer.get_paths_to_remove(project.environment.get_working_set()["demo"]) remove_paths.remove() assert not os.path.exists(lib_file) remove_paths.rollback() @@ -67,9 +65,7 @@ def test_rollback_after_commit(project, caplog): installer.install(candidate) lib_file = os.path.join(lib_path, "demo.py") assert os.path.exists(lib_file) - remove_paths = installer.get_paths_to_remove( - project.environment.get_working_set()["demo"] - ) + remove_paths = installer.get_paths_to_remove(project.environment.get_working_set()["demo"]) remove_paths.remove() remove_paths.commit() assert not os.path.exists(lib_file) @@ -77,10 +73,7 @@ def test_rollback_after_commit(project, caplog): remove_paths.rollback() assert not os.path.exists(lib_file) - assert any( - record.message == "Can't rollback, not uninstalled yet" - for record in caplog.records - ) + assert any(record.message == "Can't rollback, not uninstalled yet" for record in caplog.records) @pytest.mark.parametrize("use_install_cache", [False, True]) @@ -106,9 +99,7 @@ def test_install_wheel_with_cache(project, invoke): req = parse_requirement("future-fstrings") candidate = Candidate( req, - link=Link( - "http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl" - ), + link=Link("http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment, use_install_cache=True) installer.install(candidate) @@ -140,8 +131,7 @@ def test_install_wheel_with_cache(project, invoke): def test_url_requirement_is_not_cached(project): req = parse_requirement( - "future-fstrings @ http://fixtures.test/artifacts/" - "future_fstrings-1.2.0-py2.py3-none-any.whl" + "future-fstrings @ http://fixtures.test/artifacts/future_fstrings-1.2.0-py2.py3-none-any.whl" ) candidate = Candidate(req) installer = InstallManager(project.environment, use_install_cache=True) @@ -160,9 +150,7 @@ def test_install_wheel_with_data_scripts(project, use_install_cache): req = parse_requirement("jmespath") candidate = Candidate( req, - link=Link( - "http://fixtures.test/artifacts/jmespath-0.10.0-py2.py3-none-any.whl" - ), + link=Link("http://fixtures.test/artifacts/jmespath-0.10.0-py2.py3-none-any.whl"), ) installer = InstallManager(project.environment, use_install_cache=use_install_cache) installer.install(candidate) @@ -190,6 +178,4 @@ def test_compress_file_list_for_rename(): "test-removal/non_exist.py", } abs_paths = {os.path.join(project_root, path) for path in paths} - assert sorted(compress_for_rename(abs_paths)) == [ - os.path.join(project_root, "test-removal" + os.sep) - ] + assert sorted(compress_for_rename(abs_paths)) == [os.path.join(project_root, "test-removal" + os.sep)] diff --git a/tests/test_integration.py b/tests/test_integration.py index ec4bd95160..c71c595dc8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -37,9 +37,7 @@ def test_basic_integration(python_version, core, tmp_path, invoke): invoke(["build", "-v"], obj=project, strict=True) invoke(["remove", "-v", "django"], obj=project, strict=True) result = invoke(["list"], obj=project, strict=True) - assert not any( - line.strip().lower().startswith("django") for line in result.output.splitlines() - ) + assert not any(line.strip().lower().startswith("django") for line in result.output.splitlines()) def test_actual_list_freeze(project, local_finder, invoke): diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 427dafb058..044a1d4aea 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -92,9 +92,7 @@ def get_entry_points(group): return [make_entry_point(add_new_config)] return [] - mocker.patch.object( - importlib_metadata, "entry_points", side_effect=get_entry_points - ) + mocker.patch.object(importlib_metadata, "entry_points", side_effect=get_entry_points) core.init_parser() core.load_plugins() diff --git a/tests/test_project.py b/tests/test_project.py index 9f15a79d61..20bd0803e4 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -25,9 +25,7 @@ def test_project_python_with_pyenv_support(project, mocker, monkeypatch): "findpython.python.PythonVersion._get_version", return_value=parse("3.8.0"), ) - mocker.patch( - "findpython.python.PythonVersion._get_interpreter", return_value=sys.executable - ) + mocker.patch("findpython.python.PythonVersion._get_interpreter", return_value=sys.executable) assert Path(project.python.path) == pyenv_python assert project.python.executable == Path(sys.executable) @@ -56,25 +54,18 @@ def test_project_sources_overriding(project): project.project_config["pypi.url"] = "https://test.pypi.org/simple" assert project.sources[0]["url"] == "https://test.pypi.org/simple" - project.pyproject.settings["source"] = [ - {"url": "https://example.org/simple", "name": "pypi", "verify_ssl": True} - ] + project.pyproject.settings["source"] = [{"url": "https://example.org/simple", "name": "pypi", "verify_ssl": True}] assert project.sources[0]["url"] == "https://example.org/simple" def test_project_sources_env_var_expansion(project, monkeypatch): monkeypatch.setenv("PYPI_USER", "user") monkeypatch.setenv("PYPI_PASS", "password") - project.project_config[ - "pypi.url" - ] = "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" + project.project_config["pypi.url"] = "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" # expanded in sources assert project.sources[0]["url"] == "https://user:password@test.pypi.org/simple" # not expanded in project config - assert ( - project.project_config["pypi.url"] - == "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" - ) + assert project.project_config["pypi.url"] == "https://${PYPI_USER}:${PYPI_PASS}@test.pypi.org/simple" project.pyproject.settings["source"] = [ { @@ -86,10 +77,7 @@ def test_project_sources_env_var_expansion(project, monkeypatch): # expanded in sources assert project.sources[0]["url"] == "https://user:password@example.org/simple" # not expanded in tool settings - assert ( - project.pyproject.settings["source"][0]["url"] - == "https://${PYPI_USER}:${PYPI_PASS}@example.org/simple" - ) + assert project.pyproject.settings["source"][0]["url"] == "https://${PYPI_USER}:${PYPI_PASS}@example.org/simple" project.pyproject.settings["source"] = [ { @@ -101,10 +89,7 @@ def test_project_sources_env_var_expansion(project, monkeypatch): # expanded in sources assert project.sources[1]["url"] == "https://user:password@example2.org/simple" # not expanded in tool settings - assert ( - project.pyproject.settings["source"][0]["url"] - == "https://${PYPI_USER}:${PYPI_PASS}@example2.org/simple" - ) + assert project.pyproject.settings["source"][0]["url"] == "https://${PYPI_USER}:${PYPI_PASS}@example2.org/simple" def test_global_project(tmp_path, core): @@ -114,9 +99,7 @@ def test_global_project(tmp_path, core): def test_auto_global_project(tmp_path, core): tmp_path.joinpath(".pdm-home").mkdir() - (tmp_path / ".pdm-home/config.toml").write_text( - "[global_project]\nfallback = true\n" - ) + (tmp_path / ".pdm-home/config.toml").write_text("[global_project]\nfallback = true\n") with cd(tmp_path): project = core.create_project(global_config=tmp_path / ".pdm-home/config.toml") assert project.is_global @@ -131,10 +114,7 @@ def test_project_use_venv(project): project.project_config["python.use_venv"] = True env = project.environment - assert ( - env.interpreter.executable - == project.root / "venv" / scripts / f"python{suffix}" - ) + assert env.interpreter.executable == project.root / "venv" / scripts / f"python{suffix}" assert env.is_global @@ -155,9 +135,7 @@ def test_project_auto_detect_venv(project): project.project_config["python.use_venv"] = True project._python = None - project.project_config["python.path"] = ( - project.root / "test_venv" / scripts / f"python{suffix}" - ).as_posix() + project.project_config["python.path"] = (project.root / "test_venv" / scripts / f"python{suffix}").as_posix() assert project.environment.is_global @@ -171,9 +149,7 @@ def test_ignore_saved_python(project, monkeypatch): venv.create(project.root / "venv") monkeypatch.setenv("PDM_IGNORE_SAVED_PYTHON", "1") assert project.python.executable != project.project_config["python.path"] - assert ( - project.python.executable == project.root / "venv" / scripts / f"python{suffix}" - ) + assert project.python.executable == project.root / "venv" / scripts / f"python{suffix}" def test_select_dependencies(project): @@ -206,9 +182,7 @@ def test_global_python_path_config(project_no_init, tmp_path): tmp_path.joinpath(".pdm.toml").unlink() project_no_init.global_config["python.path"] = sys.executable # Recreate the project to clean cached properties - p = project_no_init.core.create_project( - project_no_init.root, global_config=tmp_path / ".pdm-home/config.toml" - ) + p = project_no_init.core.create_project(project_no_init.root, global_config=tmp_path / ".pdm-home/config.toml") assert p.python.executable == Path(sys.executable) assert "python.path" not in p.project_config diff --git a/tests/test_utils.py b/tests/test_utils.py index af081c82b0..74677c02a1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -55,10 +55,7 @@ def compare_python_paths(path1, path2): @pytest.mark.path def test_find_python_in_path(tmp_path): - assert ( - utils.find_python_in_path(sys.executable) - == pathlib.Path(sys.executable).absolute() - ) + assert utils.find_python_in_path(sys.executable) == pathlib.Path(sys.executable).absolute() posix_path_to_executable = pathlib.Path(sys.executable) assert compare_python_paths( @@ -94,9 +91,7 @@ def setup_dependencies(project): "optional-dependencies": {"web": ["flask"], "auth": ["passlib"]}, } ) - project.pyproject.settings.update( - {"dev-dependencies": {"test": ["pytest"], "doc": ["mkdocs"]}} - ) + project.pyproject.settings.update({"dev-dependencies": {"test": ["pytest"], "doc": ["mkdocs"]}}) project.pyproject.write()