diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 00000000..a13b70e0 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,15 @@ +# https://beta.ruff.rs +name: ruff +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: pip install --user ruff + - run: ruff --format=github . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9cc703c7..250252fc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,13 +18,13 @@ repos: exclude: .patch - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.259 + rev: v0.0.275 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black @@ -53,6 +53,6 @@ repos: stages: [manual] - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.12.2 + rev: v0.13 hooks: - id: validate-pyproject diff --git a/pipenv/cmdparse.py b/pipenv/cmdparse.py index 8ba46177..34a0d928 100644 --- a/pipenv/cmdparse.py +++ b/pipenv/cmdparse.py @@ -15,7 +15,7 @@ class ScriptParseError(ValueError): def _quote_if_contains(value, pattern): if next(iter(re.finditer(pattern, value)), None): - return '"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', value)) + return '"{}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', value)) return value @@ -40,7 +40,7 @@ def _parse_toml_inline_table(value: tomlkit.items.InlineTable) -> str: return f'python -c "import {module} as _m; _m.{func}"' -class Script(object): +class Script: """Parse a script line (in Pipfile's [scripts] section). This always works in POSIX mode, even on Windows. @@ -65,7 +65,7 @@ class Script(object): return cls(value[0], value[1:]) def __repr__(self): - return "Script({0!r})".format(self._parts) + return f"Script({self._parts!r})" @property def command(self): diff --git a/pipenv/environment.py b/pipenv/environment.py index 32eca94c..58f7ba40 100644 --- a/pipenv/environment.py +++ b/pipenv/environment.py @@ -38,7 +38,7 @@ except ImportError: if typing.TYPE_CHECKING: from types import ModuleType - from typing import ContextManager, Dict, Generator, List, Optional, Set, Union + from typing import ContextManager, Generator from pipenv.project import Project, TPipfile, TSource from pipenv.vendor import tomlkit @@ -49,13 +49,13 @@ BASE_WORKING_SET = pkg_resources.WorkingSet(sys.path) class Environment: def __init__( self, - prefix: Optional[str] = None, - python: Optional[str] = None, + prefix: str | None = None, + python: str | None = None, is_venv: bool = False, base_working_set: pkg_resources.WorkingSet = None, - pipfile: Optional[Union[tomlkit.toml_document.TOMLDocument, TPipfile]] = None, - sources: Optional[List[TSource]] = None, - project: Optional[Project] = None, + pipfile: tomlkit.toml_document.TOMLDocument | TPipfile | None = None, + sources: list[TSource] | None = None, + project: Project | None = None, ): super().__init__() self._modules = {"pkg_resources": pkg_resources, "pipenv": pipenv} @@ -101,7 +101,7 @@ class Environment: @classmethod def resolve_dist( cls, dist: pkg_resources.Distribution, working_set: pkg_resources.WorkingSet - ) -> Set[pkg_resources.Distribution]: + ) -> set[pkg_resources.Distribution]: """Given a local distribution and a working set, returns all dependencies from the set. :param dist: A single distribution to find the dependencies of @@ -146,12 +146,12 @@ class Environment: py_version = sysconfig.get_python_version() return py_version - def find_libdir(self) -> Optional[Path]: + def find_libdir(self) -> Path | None: libdir = self.prefix / "lib" return next(iter(list(libdir.iterdir())), None) @property - def python_info(self) -> Dict[str, str]: + def python_info(self) -> dict[str, str]: include_dir = self.prefix / "include" if not os.path.exists(include_dir): include_dirs = self.get_include_path() @@ -190,7 +190,7 @@ class Environment: return "posix_prefix" @cached_property - def base_paths(self) -> Dict[str, str]: + def base_paths(self) -> dict[str, str]: """ Returns the context appropriate paths for the environment. @@ -301,7 +301,7 @@ class Environment: return py @cached_property - def sys_path(self) -> List[str]: + def sys_path(self) -> list[str]: """ The system path inside the environment @@ -372,7 +372,7 @@ class Environment: py_command = py_command % lines_as_str return py_command - def get_paths(self) -> Optional[Dict[str, str]]: + def get_paths(self) -> dict[str, str] | None: """ Get the paths for the environment by running a subcommand @@ -404,7 +404,7 @@ class Environment: click.secho(f"Output: {c.stdout}", fg="yellow") return None - def get_lib_paths(self) -> Dict[str, str]: + def get_lib_paths(self) -> dict[str, str]: """Get the include path for the environment :return: The python include path for the environment @@ -455,7 +455,7 @@ class Environment: return paths return {} - def get_include_path(self) -> Optional[Dict[str, str]]: + def get_include_path(self) -> dict[str, str] | None: """Get the include path for the environment :return: The python include path for the environment @@ -490,7 +490,7 @@ class Environment: return sys_prefix @cached_property - def paths(self) -> Dict[str, str]: + def paths(self) -> dict[str, str]: paths = {} with temp_environ(), temp_path(): os.environ["PYTHONIOENCODING"] = "utf-8" @@ -588,7 +588,7 @@ class Environment: location = _normalized(make_posix(location)) return any(location.startswith(prefix) for prefix in prefixes) - def get_installed_packages(self) -> List[pkg_resources.Distribution]: + def get_installed_packages(self) -> list[pkg_resources.Distribution]: """Returns all of the installed packages in a given environment""" workingset = self.get_working_set() packages = [ @@ -652,7 +652,7 @@ class Environment: def get_outdated_packages( self, pre: bool = False - ) -> List[pkg_resources.Distribution]: + ) -> list[pkg_resources.Distribution]: return [ pkg for pkg in self.get_package_info(pre=pre) diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index 23ad6b9f..b5bd7af7 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -44,7 +44,7 @@ def handle_exception(exc_type, exception, traceback, hook=sys.excepthook): line = f" {line}" else: line = f" {line}" - line = "[{!s}]: {}".format(exception.__class__.__name__, line) + line = f"[{exception.__class__.__name__!s}]: {line}" formatted_lines.append(line) # use new exception prettification rules to format exceptions according to # UX rules @@ -56,7 +56,7 @@ sys.excepthook = handle_exception class PipenvException(ClickException): - message = "{0}: {{0}}".format(click.style("ERROR", fg="red", bold=True)) + message = "{}: {{}}".format(click.style("ERROR", fg="red", bold=True)) def __init__(self, message=None, **kwargs): if not message: @@ -164,7 +164,7 @@ class PipenvUsageError(UsageError): click.echo(extra, file=file) hint = "" if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None: - hint = 'Try "%s %s" for help.\n' % ( + hint = 'Try "{} {}" for help.\n'.format( self.ctx.command_path, self.ctx.help_option_names[0], ) @@ -174,9 +174,7 @@ class PipenvUsageError(UsageError): class PipenvFileError(FileError): - formatted_message = "{0} {{0}} {{1}}".format( - click.style("ERROR:", fg="red", bold=True) - ) + formatted_message = "{} {{}} {{}}".format(click.style("ERROR:", fg="red", bold=True)) def __init__(self, filename, message=None, **kwargs): extra = kwargs.pop("extra", []) diff --git a/pipenv/installers.py b/pipenv/installers.py index 7ec1ddba..dca7f2ee 100644 --- a/pipenv/installers.py +++ b/pipenv/installers.py @@ -180,7 +180,7 @@ class Pyenv(Installer): def _run(self, *args, **kwargs): if Pyenv.WIN: kwargs["shell"] = True - return super(Pyenv, self)._run(*args, **kwargs) + return super()._run(*args, **kwargs) def iter_installable_versions(self): """Iterate through CPython versions available for Pipenv to install.""" diff --git a/pipenv/project.py b/pipenv/project.py index c1e947bb..1303152d 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -3,7 +3,6 @@ from __future__ import annotations import base64 import fnmatch import hashlib -import io import json import operator import os @@ -56,9 +55,9 @@ except ImportError: from pipenv.patched.pip._vendor.distlib.util import cached_property if is_type_checking(): - from typing import Dict, List, Optional, Set, Text, Tuple, Union + from typing import Dict, List, Union - TSource = Dict[Text, Union[Text, bool]] + TSource = Dict[str, Union[str, bool]] TPackageEntry = Dict[str, Union[bool, str, List[str]]] TPackage = Dict[str, TPackageEntry] TScripts = Dict[str, str] @@ -86,17 +85,15 @@ class _LockFileEncoder(json.JSONEncoder): """ def __init__(self): - super(_LockFileEncoder, self).__init__( - indent=4, separators=(",", ": "), sort_keys=True - ) + super().__init__(indent=4, separators=(",", ": "), sort_keys=True) def default(self, obj): if isinstance(obj, Path): obj = obj.as_posix() - return super(_LockFileEncoder, self).default(obj) + return super().default(obj) def encode(self, obj): - content = super(_LockFileEncoder, self).encode(obj) + content = super().encode(obj) if not isinstance(content, str): content = content.decode("utf-8") return content @@ -281,7 +278,7 @@ class Project: return dot_venv # Now we assume .venv in project root is a file. Use its content. - with io.open(dot_venv) as f: + with open(dot_venv) as f: name = f.read().strip() # If .venv file is empty, set location based on config. @@ -305,11 +302,11 @@ class Project: return self.environment.get_installed_packages() @property - def installed_package_names(self) -> List[str]: + def installed_package_names(self) -> list[str]: return get_canonical_names([pkg.key for pkg in self.installed_packages]) @property - def lockfile_package_names(self) -> Dict[str, Set[str]]: + def lockfile_package_names(self) -> dict[str, set[str]]: results = { "combined": {}, } @@ -322,7 +319,7 @@ class Project: return results @property - def pipfile_package_names(self) -> Dict[str, Set[str]]: + def pipfile_package_names(self) -> dict[str, set[str]]: result = {} combined = set() for category in self.get_package_categories(): @@ -359,11 +356,11 @@ class Project: self._environment = self.get_environment(allow_global=allow_global) return self._environment - def get_outdated_packages(self) -> List[pkg_resources.Distribution]: + def get_outdated_packages(self) -> list[pkg_resources.Distribution]: return self.environment.get_outdated_packages(pre=self.pipfile.get("pre", False)) @classmethod - def _sanitize(cls, name: str) -> Tuple[str, str]: + def _sanitize(cls, name: str) -> tuple[str, str]: # Replace dangerous characters into '_'. The length of the sanitized # project name is limited as 42 because of the limit of linux kernel # @@ -391,7 +388,7 @@ class Project: return name, encoded_hash[:8] clean_name, encoded_hash = get_name(name, self.pipfile_location) - venv_name = "{0}-{1}".format(clean_name, encoded_hash) + venv_name = f"{clean_name}-{encoded_hash}" # This should work most of the time for # Case-sensitive filesystems, @@ -428,9 +425,9 @@ class Project: suffix = "" if self.s.PIPENV_PYTHON: if os.path.isabs(self.s.PIPENV_PYTHON): - suffix = "-{0}".format(os.path.basename(self.s.PIPENV_PYTHON)) + suffix = f"-{os.path.basename(self.s.PIPENV_PYTHON)}" else: - suffix = "-{0}".format(self.s.PIPENV_PYTHON) + suffix = f"-{self.s.PIPENV_PYTHON}" # If the pipfile was located at '/home/user/MY_PROJECT/Pipfile', # the name of its virtualenv will be 'my-project-wyUfYPqE' @@ -487,7 +484,7 @@ class Project: def register_proper_name(self, name: str) -> None: """Registers a proper name to the database.""" with self.proper_names_db_path.open("a") as f: - f.write("{0}\n".format(name)) + f.write(f"{name}\n") @property def pipfile_location(self) -> str: @@ -505,7 +502,7 @@ class Project: return self._pipfile_location @property - def requirements_location(self) -> Optional[str]: + def requirements_location(self) -> str | None: if self._requirements_location is None: try: loc = find_requirements(max_depth=self.s.PIPENV_MAX_DEPTH) @@ -515,7 +512,7 @@ class Project: return self._requirements_location @property - def parsed_pipfile(self) -> Union[tomlkit.toml_document.TOMLDocument, TPipfile]: + def parsed_pipfile(self) -> tomlkit.toml_document.TOMLDocument | TPipfile: """Parse Pipfile into a TOMLFile and cache it (call clear_pipfile_cache() afterwards if mutating)""" @@ -531,7 +528,7 @@ class Project: # Open the pipfile, read it into memory. if not self.pipfile_exists: return "" - with io.open(self.pipfile_location) as f: + with open(self.pipfile_location) as f: contents = f.read() self._pipfile_newlines = preferred_newlines(f) @@ -543,7 +540,7 @@ class Project: def _parse_pipfile( self, contents: str - ) -> Union[tomlkit.toml_document.TOMLDocument, TPipfile]: + ) -> tomlkit.toml_document.TOMLDocument | TPipfile: try: return tomlkit.parse(contents) except Exception: @@ -565,7 +562,7 @@ class Project: self._build_system = build_system @property - def build_requires(self) -> List[str]: + def build_requires(self) -> list[str]: return self._build_system.get("requires", ["setuptools>=40.8.0", "wheel"]) @property @@ -573,7 +570,7 @@ class Project: return self._build_system.get("build-backend", get_default_pyproject_backend()) @property - def settings(self) -> Union[tomlkit.items.Table, Dict[str, Union[str, bool]]]: + def settings(self) -> tomlkit.items.Table | dict[str, str | bool]: """A dictionary of the settings added to the Pipfile.""" return self.parsed_pipfile.get("pipenv", {}) @@ -583,7 +580,7 @@ class Project: except KeyError: return False - def build_script(self, name: str, extra_args: Optional[List[str]] = None) -> Script: + def build_script(self, name: str, extra_args: list[str] | None = None) -> Script: try: script = Script.parse(self.parsed_pipfile["scripts"][name]) except KeyError: @@ -592,7 +589,7 @@ class Project: script.extend(extra_args) return script - def update_settings(self, d: Dict[str, Union[str, bool]]) -> None: + def update_settings(self, d: dict[str, str | bool]) -> None: settings = self.settings changed = False for new in d: @@ -644,7 +641,7 @@ class Project: @property def lockfile_location(self): - return "{0}.lock".format(self.pipfile_location) + return f"{self.pipfile_location}.lock" @property def lockfile_exists(self): @@ -711,7 +708,7 @@ class Project: if not index: continue - source_name = "pip_index_{}".format(i) + source_name = f"pip_index_{i}" verify_ssl = index.startswith("https") sources.append({"url": index, "verify_ssl": verify_ssl, "name": source_name}) @@ -837,7 +834,7 @@ class Project: else: newlines = DEFAULT_NEWLINES formatted_data = cleanup_toml(formatted_data) - with io.open(path, "w", newline=newlines) as f: + with open(path, "w", newline=newlines) as f: f.write(formatted_data) # pipfile is mutated! self.clear_pipfile_cache() @@ -1007,7 +1004,7 @@ class Project: else: from random import randint - name = "{0}-{1}".format(src_name, randint(1, 1000)) + name = f"{src_name}-{randint(1, 1000)}" return name def add_index_to_pipfile(self, index, verify_ssl=True): @@ -1041,7 +1038,7 @@ class Project: def load_lockfile(self, expand_env_vars=True): lockfile_modified = False - with io.open(self.lockfile_location, encoding="utf-8") as lock: + with open(self.lockfile_location, encoding="utf-8") as lock: try: j = json.load(lock) self._lockfile_newlines = preferred_newlines(lock) @@ -1118,7 +1115,7 @@ class Project: try: # Get new casing for package name. new_casing = proper_case(dep) - except IOError: + except OSError: # Unable to normalize package name. continue diff --git a/pipenv/routines/lock.py b/pipenv/routines/lock.py index e4a91a91..9515064c 100644 --- a/pipenv/routines/lock.py +++ b/pipenv/routines/lock.py @@ -60,7 +60,7 @@ def do_lock( click.echo( "{} {} {}".format( click.style("Locking"), - click.style("[{}]".format(pipfile_category), fg="yellow"), + click.style(f"[{pipfile_category}]", fg="yellow"), click.style("dependencies..."), ), err=True, @@ -124,7 +124,7 @@ def do_lock( click.echo( "{}".format( click.style( - "Updated Pipfile.lock ({})!".format(project.get_lockfile_hash()), + f"Updated Pipfile.lock ({project.get_lockfile_hash()})!", bold=True, ) ), diff --git a/pipenv/utils/indexes.py b/pipenv/utils/indexes.py index 4f078a5c..99485caf 100644 --- a/pipenv/utils/indexes.py +++ b/pipenv/utils/indexes.py @@ -46,8 +46,8 @@ def prepare_pip_source_args(sources, pip_args=None): def get_project_index( project: Project, - index: Optional[Union[str, TSource]] = None, - trusted_hosts: Optional[List[str]] = None, + index: str | TSource | None = None, + trusted_hosts: list[str] | None = None, ) -> TSource: from pipenv.project import SourceNotFound @@ -67,11 +67,11 @@ def get_project_index( def get_source_list( project: Project, - index: Optional[Union[str, TSource]] = None, - extra_indexes: Optional[Union[str, List[str]]] = None, - trusted_hosts: Optional[List[str]] = None, - pypi_mirror: Optional[str] = None, -) -> List[TSource]: + index: str | TSource | None = None, + extra_indexes: str | list[str] | None = None, + trusted_hosts: list[str] | None = None, + pypi_mirror: str | None = None, +) -> list[TSource]: sources = project.sources[:] if index: sources.append(get_project_index(project, index)) diff --git a/pipenv/utils/internet.py b/pipenv/utils/internet.py index af04baa5..b02cbc0e 100644 --- a/pipenv/utils/internet.py +++ b/pipenv/utils/internet.py @@ -72,7 +72,7 @@ def get_host_and_port(url): :return: a string with the host:port pair if the URL includes port number explicitly; otherwise, returns host only """ url = urllib3_util.parse_url(url) - return "{}:{}".format(url.host, url.port) if url.port else url.host + return f"{url.host}:{url.port}" if url.port else url.host def get_url_name(url): diff --git a/pipenv/utils/pip.py b/pipenv/utils/pip.py index c77bfb57..b770c69b 100644 --- a/pipenv/utils/pip.py +++ b/pipenv/utils/pip.py @@ -293,7 +293,7 @@ def pip_install( if not search_all_sources and requirement.index in source_names: sources = list(filter(lambda d: d.get("name") == requirement.index, sources)) if r: - with open(r, "r") as fh: + with open(r) as fh: if "--hash" not in fh.read(): ignore_hashes = True if project.s.is_verbose(): diff --git a/pipenv/utils/pipfile.py b/pipenv/utils/pipfile.py index 90785e27..605d24b7 100644 --- a/pipenv/utils/pipfile.py +++ b/pipenv/utils/pipfile.py @@ -37,8 +37,7 @@ def walk_up(bottom): if new_path == bottom: return - for x in walk_up(new_path): - yield x + yield from walk_up(new_path) def find_pipfile(max_depth=3): @@ -76,7 +75,7 @@ def ensure_pipfile(project, validate=True, skip_requirements=False, system=False if project.requirements_exists and not skip_requirements: requirements_dir_path = os.path.dirname(project.requirements_location) click.echo( - "{0} found in {1} instead of {2}! Converting...".format( + "{} found in {} instead of {}! Converting...".format( click.style("requirements.txt", bold=True), click.style(requirements_dir_path, fg="yellow", bold=True), click.style("Pipfile", bold=True), diff --git a/pipenv/utils/processes.py b/pipenv/utils/processes.py index 5a38290a..f061be50 100644 --- a/pipenv/utils/processes.py +++ b/pipenv/utils/processes.py @@ -69,9 +69,7 @@ def subprocess_run( other_kwargs["stdout"] = subprocess.PIPE other_kwargs["stderr"] = subprocess.PIPE if block: - return subprocess.run( - args, universal_newlines=text, encoding=encoding, **other_kwargs - ) + return subprocess.run(args, text=text, encoding=encoding, **other_kwargs) else: return subprocess.Popen( args, universal_newlines=text, encoding=encoding, **other_kwargs diff --git a/pipenv/utils/requirements.py b/pipenv/utils/requirements.py index 97f35cb0..3d6f816b 100644 --- a/pipenv/utils/requirements.py +++ b/pipenv/utils/requirements.py @@ -71,14 +71,12 @@ def add_index_to_pipfile(project, index, trusted_hosts=None): host_and_port = get_host_and_port(index) require_valid_https = not any( - ( - v in trusted_hosts - for v in ( - host_and_port, - host_and_port.partition(":")[ - 0 - ], # also check if hostname without port is in trusted_hosts - ) + v in trusted_hosts + for v in ( + host_and_port, + host_and_port.partition(":")[ + 0 + ], # also check if hostname without port is in trusted_hosts ) ) index_name = project.add_index_to_pipfile(index, verify_ssl=require_valid_https) diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index 8fdbd91f..3d59caf1 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -121,7 +121,7 @@ class HashCacheMixin: with open_file(link.url, self.session) as fp: for chunk in iter(lambda: fp.read(8096), b""): h.update(chunk) - return ":".join([h.name, h.hexdigest()]) + return f"{h.name}:{h.hexdigest()}" class PackageIndexHTMLParser(HTMLParser): diff --git a/pipenv/utils/shell.py b/pipenv/utils/shell.py index 8ca46a4c..89c6ef9b 100644 --- a/pipenv/utils/shell.py +++ b/pipenv/utils/shell.py @@ -351,11 +351,13 @@ def handle_remove_readonly(func, path, exc): func(path) except OSError as e: if e.errno in [errno.EACCES, errno.EPERM]: - warnings.warn(default_warning_message.format(path), ResourceWarning) + warnings.warn( + default_warning_message.format(path), ResourceWarning, stacklevel=1 + ) return if exc_exception.errno in [errno.EACCES, errno.EPERM]: - warnings.warn(default_warning_message.format(path), ResourceWarning) + warnings.warn(default_warning_message.format(path), ResourceWarning, stacklevel=1) return raise exc diff --git a/pipenv/utils/virtualenv.py b/pipenv/utils/virtualenv.py index 30533dfd..ad9aa54f 100644 --- a/pipenv/utils/virtualenv.py +++ b/pipenv/utils/virtualenv.py @@ -49,11 +49,11 @@ def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=N python = sys.executable using_string = "Using default python from" click.echo( - "{0} {1} {3} {2}".format( + "{} {} {} {}".format( click.style(using_string, bold=True), click.style(python, fg="yellow", bold=True), - click.style("to create virtualenv...", bold=True), click.style(f"({python_version(python)})", fg="green"), + click.style("to create virtualenv...", bold=True), ), err=True, ) diff --git a/pyproject.toml b/pyproject.toml index 4fdcd732..69d6fcae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,24 +96,25 @@ exclude = [ "pipenv/vendor/*", ] select = [ + "ASYNC", "B", "C9", "E", "F", + "FLY", "G", "I", "ISC", "PIE", "PL", "TID", + "UP", "W", "YTT" ] ignore = [ - "B028", "B904", "PIE790", - "PLR2004", "PLR5501", "PLW2901", ] @@ -121,9 +122,10 @@ line-length = 137 target-version = "py37" [tool.ruff.mccabe] -max-complexity = 32 +max-complexity = 44 [tool.ruff.pylint] +allow-magic-value-types = ["int", "str"] max-args = 20 max-branches = 38 max-returns = 9 @@ -135,6 +137,7 @@ max-statements = 155 "pipenv/__init__.py" = ["E401"] "pipenv/cli/command.py" = ["TID252"] "pipenv/utils/internet.py" = ["PLW0603"] +"pipenv/utils/resolver.py" = ["B018"] "tests/*" = ["E501", "F401", "I", "PLC1901", "S101"] "tests/integration/conftest.py" = ["B003", "PIE800", "PLW0603"] "tests/integration/test_pipenv.py" = ["E741"] diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index 157f7f05..7b2d7c51 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -220,7 +220,7 @@ def rewrite_file_imports(item, vendored_libs): text = re.sub(r"(?m)^(\s*)from %s([\s\.]+)" % lib, r"\1from %s\2" % to_lib, text) text = re.sub( r"(?m)^(\s*)import %s(\s*[,\n#])" % lib, - r"\1import %s as %s\2" % (to_lib, lib), + rf"\1import {to_lib} as {lib}\2", text, ) for pattern, sub in GLOBAL_REPLACEMENT: @@ -517,7 +517,7 @@ def download_licenses( cmd = "pip download --no-binary :all: --only-binary requests_download --no-deps" ctx.run("pip install flit") # needed for the next step for req in requirements: - exe_cmd = "{} --no-build-isolation -d {} {}".format(cmd, tmp_dir.as_posix(), req) + exe_cmd = f"{cmd} --no-build-isolation -d {tmp_dir.as_posix()} {req}" try: ctx.run(exe_cmd) except invoke.exceptions.UnexpectedExit as e: diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 046c019a..c19f511c 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -45,11 +45,11 @@ def check_internet(): try_internet(url) except KeyboardInterrupt: warnings.warn( - f"Skipped connecting to internet: {url}", RuntimeWarning + f"Skipped connecting to internet: {url}", RuntimeWarning, stacklevel=1 ) except Exception: warnings.warn( - f"Failed connecting to internet: {url}", RuntimeWarning + f"Failed connecting to internet: {url}", RuntimeWarning, stacklevel=1 ) else: has_internet = True @@ -69,17 +69,17 @@ def check_github_ssh(): res = True if c.returncode == 1 else False except KeyboardInterrupt: warnings.warn( - "KeyboardInterrupt while checking GitHub ssh access", RuntimeWarning + "KeyboardInterrupt while checking GitHub ssh access", RuntimeWarning, stacklevel=1 ) except Exception: pass global HAS_WARNED_GITHUB if not res and not HAS_WARNED_GITHUB: warnings.warn( - 'Cannot connect to GitHub via SSH', RuntimeWarning + 'Cannot connect to GitHub via SSH', RuntimeWarning, stacklevel=1 ) warnings.warn( - 'Will skip tests requiring SSH access to GitHub', RuntimeWarning + 'Will skip tests requiring SSH access to GitHub', RuntimeWarning, stacklevel=1 ) HAS_WARNED_GITHUB = True return res @@ -353,7 +353,7 @@ class _PipenvInstance: self._path.cleanup() except OSError as e: _warn_msg = warn_msg.format(e) - warnings.warn(_warn_msg, ResourceWarning) + warnings.warn(_warn_msg, ResourceWarning, stacklevel=1) self.path = None self._path = None diff --git a/tests/integration/test_dot_venv.py b/tests/integration/test_dot_venv.py index b2958a03..b04cfbdb 100644 --- a/tests/integration/test_dot_venv.py +++ b/tests/integration/test_dot_venv.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function import os from pathlib import Path diff --git a/tests/integration/test_install_basic.py b/tests/integration/test_install_basic.py index afd0bd99..54bf7e8e 100644 --- a/tests/integration/test_install_basic.py +++ b/tests/integration/test_install_basic.py @@ -416,12 +416,12 @@ def test_rewrite_outline_table(pipenv_instance_private_pypi): with open(p.pipfile_path, 'w') as f: contents = """ [[source]] -url = "{0}" +url = "{}" verify_ssl = false name = "testindex" [packages] -six = {1} +six = {} [packages.requests] version = "*" diff --git a/tests/integration/test_install_markers.py b/tests/integration/test_install_markers.py index ccce2ab5..eb81e1f1 100644 --- a/tests/integration/test_install_markers.py +++ b/tests/integration/test_install_markers.py @@ -15,12 +15,12 @@ def test_package_environment_markers(pipenv_instance_private_pypi): with open(p.pipfile_path, 'w') as f: contents = """ [[source]] -url = "{0}" +url = "{}" verify_ssl = false name = "testindex" [packages] -fake_package = {1} +fake_package = {} [dev-packages] """.format(os.environ['PIPENV_TEST_INDEX'], "{version = \"*\", markers=\"os_name=='splashwear'\", index=\"testindex\"}").strip() diff --git a/tests/integration/test_install_twists.py b/tests/integration/test_install_twists.py index e13ef8d0..1efde320 100644 --- a/tests/integration/test_install_twists.py +++ b/tests/integration/test_install_twists.py @@ -281,12 +281,12 @@ def test_install_skip_lock(pipenv_instance_private_pypi): with open(p.pipfile_path, 'w') as f: contents = """ [[source]] -url = "{0}" +url = "{}" verify_ssl = true name = "pypi" [packages] -six = {1} +six = {} """.format(os.environ['PIPENV_TEST_INDEX'], '{version = "*", index = "pypi"}').strip() f.write(contents) c = p.pipenv('install --skip-lock') diff --git a/tests/integration/test_install_uri.py b/tests/integration/test_install_uri.py index ef448a15..53ba65c2 100644 --- a/tests/integration/test_install_uri.py +++ b/tests/integration/test_install_uri.py @@ -32,7 +32,7 @@ def test_urls_work(pipenv_instance_pypi): # the library this installs is "django-cms" url = "https://github.com/lidatong/dataclasses-json/archive/refs/tags/v0.5.7.zip" c = p.pipenv( - "install {0}".format(url) + f"install {url}" ) assert c.returncode == 0 @@ -56,7 +56,7 @@ def test_file_urls_work(pipenv_instance_pypi, pip_src_dir): except OSError: whl = whl.absolute() wheel_url = whl.as_uri() - c = p.pipenv('install "{0}"'.format(wheel_url)) + c = p.pipenv(f'install "{wheel_url}"') assert c.returncode == 0 assert "six" in p.pipfile["packages"] assert "file" in p.pipfile["packages"]["six"] @@ -190,7 +190,7 @@ def test_get_vcs_refs(pipenv_instance_private_pypi): == "5efb522b0647f7467248273ec1b893d06b984a59" ) pipfile = Path(p.pipfile_path) - new_content = pipfile.read_text().replace(u"1.9.0", u"1.11.0") + new_content = pipfile.read_text().replace("1.9.0", "1.11.0") pipfile.write_text(new_content) c = p.pipenv("lock") assert c.returncode == 0 @@ -223,7 +223,7 @@ name = "pypi" [packages] Flask = "*" -Jinja2 = {{ref = "2.11.0", git = "{0}"}} +Jinja2 = {{ref = "2.11.0", git = "{}"}} """.format(jinja2_uri).strip() ) c = p.pipenv("install") @@ -246,7 +246,7 @@ Jinja2 = {{ref = "2.11.0", git = "{0}"}} def test_vcs_can_use_markers(pipenv_instance_pypi): with pipenv_instance_pypi(chdir=True) as p: path = p._pipfile.get_fixture_path("git/six/.git") - p._pipfile.install("six", {"git": "{0}".format(path.as_uri()), "markers": "sys_platform == 'linux'"}) + p._pipfile.install("six", {"git": f"{path.as_uri()}", "markers": "sys_platform == 'linux'"}) assert "six" in p.pipfile["packages"] c = p.pipenv("install") assert c.returncode == 0 diff --git a/tests/integration/test_project.py b/tests/integration/test_project.py index ddd5436d..4e271b9f 100644 --- a/tests/integration/test_project.py +++ b/tests/integration/test_project.py @@ -43,7 +43,7 @@ def test_get_source(pipenv_instance_private_pypi, lock_first): with open(p.pipfile_path, 'w') as f: contents = """ [[source]] -url = "{0}" +url = "{}" verify_ssl = false name = "testindex" @@ -127,7 +127,7 @@ def test_many_indexes(pipenv_instance_pypi): with open(p.pipfile_path, 'w') as f: contents = """ [[source]] -url = "{0}" +url = "{}" verify_ssl = false name = "testindex" diff --git a/tests/integration/test_requirements.py b/tests/integration/test_requirements.py index 9ffad56b..00fe6343 100644 --- a/tests/integration/test_requirements.py +++ b/tests/integration/test_requirements.py @@ -122,7 +122,7 @@ def test_requirements_with_git_requirements(pipenv_instance_pypi): "default": { "dataclasses-json": { "editable": True, - "git": f"https://github.com/lidatong/dataclasses-json.git", + "git": "https://github.com/lidatong/dataclasses-json.git", "ref": req_hash } }, diff --git a/tests/integration/test_uninstall.py b/tests/integration/test_uninstall.py index b0aebcc2..6b73744f 100644 --- a/tests/integration/test_uninstall.py +++ b/tests/integration/test_uninstall.py @@ -114,7 +114,7 @@ def test_uninstall_all_dev(pipenv_instance_private_pypi): contents = """ [[source]] name = "pypi" - url = "{0}" + url = "{}" verify_ssl = true [packages] diff --git a/tests/unit/test_cmdparse.py b/tests/unit/test_cmdparse.py index 912031e9..9afc0b09 100644 --- a/tests/unit/test_cmdparse.py +++ b/tests/unit/test_cmdparse.py @@ -54,16 +54,8 @@ def test_cmdify_complex(): def test_cmdify_quote_if_paren_in_command(): """Ensure ONLY the command is quoted if it contains parentheses. """ - script = Script.parse(' '.join([ - '"C:\\Python36(x86)\\python.exe"', - '-c', - "print(123)", - ])) - assert script.cmdify() == ' '.join([ - '"C:\\Python36(x86)\\python.exe"', - '-c', - "print(123)", - ]), script + script = Script.parse('"C:\\Python36(x86)\\python.exe" -c print(123)') + assert script.cmdify() == '"C:\\Python36(x86)\\python.exe" -c print(123)', script @pytest.mark.run diff --git a/tests/unit/test_utils_windows_executable.py b/tests/unit/test_utils_windows_executable.py index 73f8b0a6..4236ff85 100644 --- a/tests/unit/test_utils_windows_executable.py +++ b/tests/unit/test_utils_windows_executable.py @@ -1,6 +1,6 @@ import os -import mock +from unittest import mock import pytest from pipenv.utils import shell @@ -26,7 +26,7 @@ def test_find_windows_executable_when_not_found(mocked_which, mocked_isfile): assert mocked_isfile.call_count > 1 calls = [mock.call('fake\\path\\python')] + [ - mock.call('fake\\path\\python{0}'.format(ext.lower())) + mock.call(f'fake\\path\\python{ext.lower()}') for ext in os.environ['PATHEXT'].split(';') ] assert mocked_isfile.mock_calls == calls @@ -46,7 +46,7 @@ def test_find_windows_executable_when_found(mocked_which, mocked_isfile): assert mocked_isfile.call_count > 1 calls = [mock.call('fake\\path\\pyenv')] + [ - mock.call('fake\\path\\pyenv{0}'.format(ext.lower())) + mock.call(f'fake\\path\\pyenv{ext.lower()}') for ext in os.environ['PATHEXT'].split(';') ] assert mocked_isfile.mock_calls == calls