diff options
author | Bernát Gábor <gaborjbernat@gmail.com> | 2022-07-25 00:33:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-25 00:33:42 -0700 |
commit | b85542c31ca8afcff317e618da434f59fa06d122 (patch) | |
tree | 450066fe494d366c03da70b0de10899d5738d425 /src | |
parent | 3c57468470c292b235a67b27cec9468cc913f268 (diff) | |
download | virtualenv-b85542c31ca8afcff317e618da434f59fa06d122.tar.gz |
Drop support of running under Python 2.7 (#2382)
Diffstat (limited to 'src')
87 files changed, 787 insertions, 1363 deletions
diff --git a/src/virtualenv/__init__.py b/src/virtualenv/__init__.py index 5f74e3e..e40e8b7 100644 --- a/src/virtualenv/__init__.py +++ b/src/virtualenv/__init__.py @@ -1,10 +1,8 @@ -from __future__ import absolute_import, unicode_literals - from .run import cli_run, session_via_cli from .version import __version__ -__all__ = ( +__all__ = [ "__version__", "cli_run", "session_via_cli", -) +] diff --git a/src/virtualenv/__main__.py b/src/virtualenv/__main__.py index 3b06fd7..c3c5adf 100644 --- a/src/virtualenv/__main__.py +++ b/src/virtualenv/__main__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, print_function, unicode_literals - import logging import os import sys @@ -18,41 +16,32 @@ def run(args=None, options=None, env=None): session = cli_run(args, options, env) logging.warning(LogSession(session, start)) except ProcessCallFailed as exception: - print("subprocess call failed for {} with code {}".format(exception.cmd, exception.code)) + print(f"subprocess call failed for {exception.cmd} with code {exception.code}") print(exception.out, file=sys.stdout, end="") print(exception.err, file=sys.stderr, end="") raise SystemExit(exception.code) -class LogSession(object): +class LogSession: def __init__(self, session, start): self.session = session self.start = start def __str__(self): - from virtualenv.util.six import ensure_text - spec = self.session.creator.interpreter.spec elapsed = (datetime.now() - self.start).total_seconds() * 1000 lines = [ - "created virtual environment {} in {:.0f}ms".format(spec, elapsed), - " creator {}".format(ensure_text(str(self.session.creator))), + f"created virtual environment {spec} in {elapsed:.0f}ms", + f" creator {str(self.session.creator)}", ] if self.session.seeder.enabled: - lines += ( - " seeder {}".format(ensure_text(str(self.session.seeder))), - " added seed packages: {}".format( - ", ".join( - sorted( - "==".join(i.stem.split("-")) - for i in self.session.creator.purelib.iterdir() - if i.suffix == ".dist-info" - ), - ), - ), - ) + lines.append(f" seeder {str(self.session.seeder)}") + path = self.session.creator.purelib.iterdir() + packages = sorted("==".join(i.stem.split("-")) for i in path if i.suffix == ".dist-info") + lines.append(f" added seed packages: {', '.join(packages)}") + if self.session.activators: - lines.append(" activators {}".format(",".join(i.__class__.__name__ for i in self.session.activators))) + lines.append(f" activators {','.join(i.__class__.__name__ for i in self.session.activators)}") return "\n".join(lines) diff --git a/src/virtualenv/activation/__init__.py b/src/virtualenv/activation/__init__.py index e9296d8..99984bc 100644 --- a/src/virtualenv/activation/__init__.py +++ b/src/virtualenv/activation/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from .bash import BashActivator from .batch import BatchActivator from .cshell import CShellActivator diff --git a/src/virtualenv/activation/activator.py b/src/virtualenv/activation/activator.py index 80d7e47..ad9d9e0 100644 --- a/src/virtualenv/activation/activator.py +++ b/src/virtualenv/activation/activator.py @@ -1,14 +1,9 @@ -from __future__ import absolute_import, unicode_literals - import os from abc import ABCMeta, abstractmethod -from six import add_metaclass - -@add_metaclass(ABCMeta) -class Activator(object): - """Generates an activate script for the virtual environment""" +class Activator(metaclass=ABCMeta): + """Generates activate script for the virtual environment""" def __init__(self, options): """Create a new activator generator. @@ -37,9 +32,14 @@ class Activator(object): @abstractmethod def generate(self, creator): - """Generate the activate script for the given creator. + """Generate activate script for the given creator. :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \ virtual environment """ raise NotImplementedError + + +__all__ = [ + "Activator", +] diff --git a/src/virtualenv/activation/bash/__init__.py b/src/virtualenv/activation/bash/__init__.py index 22c90c3..e704126 100644 --- a/src/virtualenv/activation/bash/__init__.py +++ b/src/virtualenv/activation/bash/__init__.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.path import Path +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -11,3 +9,8 @@ class BashActivator(ViaTemplateActivator): def as_name(self, template): return template.stem + + +__all__ = [ + "BashActivator", +] diff --git a/src/virtualenv/activation/batch/__init__.py b/src/virtualenv/activation/batch/__init__.py index 4149712..6c35337 100644 --- a/src/virtualenv/activation/batch/__init__.py +++ b/src/virtualenv/activation/batch/__init__.py @@ -1,8 +1,5 @@ -from __future__ import absolute_import, unicode_literals - import os - -from virtualenv.util.path import Path +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -19,5 +16,10 @@ class BatchActivator(ViaTemplateActivator): def instantiate_template(self, replacements, template, creator): # ensure the text has all newlines as \r\n - required by batch - base = super(BatchActivator, self).instantiate_template(replacements, template, creator) + base = super().instantiate_template(replacements, template, creator) return base.replace(os.linesep, "\n").replace("\n", os.linesep) + + +__all__ = [ + "BatchActivator", +] diff --git a/src/virtualenv/activation/cshell/__init__.py b/src/virtualenv/activation/cshell/__init__.py index b25c602..b225ba3 100644 --- a/src/virtualenv/activation/cshell/__init__.py +++ b/src/virtualenv/activation/cshell/__init__.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.path import Path +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -12,3 +10,8 @@ class CShellActivator(ViaTemplateActivator): def templates(self): yield Path("activate.csh") + + +__all__ = [ + "CShellActivator", +] diff --git a/src/virtualenv/activation/fish/__init__.py b/src/virtualenv/activation/fish/__init__.py index 8d0e19c..4b24042 100644 --- a/src/virtualenv/activation/fish/__init__.py +++ b/src/virtualenv/activation/fish/__init__.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.path import Path +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -8,3 +6,8 @@ from ..via_template import ViaTemplateActivator class FishActivator(ViaTemplateActivator): def templates(self): yield Path("activate.fish") + + +__all__ = [ + "FishActivator", +] diff --git a/src/virtualenv/activation/nushell/__init__.py b/src/virtualenv/activation/nushell/__init__.py index 994c1fb..839c19c 100644 --- a/src/virtualenv/activation/nushell/__init__.py +++ b/src/virtualenv/activation/nushell/__init__.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import, unicode_literals - import os - -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -20,9 +16,14 @@ class NushellActivator(ViaTemplateActivator): return { "__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt, - "__VIRTUAL_ENV__": ensure_text(str(creator.dest)), + "__VIRTUAL_ENV__": str(creator.dest), "__VIRTUAL_NAME__": creator.env_name, - "__BIN_NAME__": ensure_text(str(creator.bin_dir.relative_to(creator.dest))), - "__PATH_SEP__": ensure_text(os.pathsep), - "__DEACTIVATE_PATH__": ensure_text(str(Path(dest_folder) / "deactivate.nu")), + "__BIN_NAME__": str(creator.bin_dir.relative_to(creator.dest)), + "__PATH_SEP__": os.pathsep, + "__DEACTIVATE_PATH__": str(Path(dest_folder) / "deactivate.nu"), } + + +__all__ = [ + "NushellActivator", +] diff --git a/src/virtualenv/activation/powershell/__init__.py b/src/virtualenv/activation/powershell/__init__.py index 4fadc63..6d561bf 100644 --- a/src/virtualenv/activation/powershell/__init__.py +++ b/src/virtualenv/activation/powershell/__init__.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.path import Path +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -8,3 +6,8 @@ from ..via_template import ViaTemplateActivator class PowerShellActivator(ViaTemplateActivator): def templates(self): yield Path("activate.ps1") + + +__all__ = [ + "PowerShellActivator", +] diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py index 9e57912..ad38201 100644 --- a/src/virtualenv/activation/python/__init__.py +++ b/src/virtualenv/activation/python/__init__.py @@ -1,11 +1,7 @@ -from __future__ import absolute_import, unicode_literals - import os import sys from collections import OrderedDict - -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text +from pathlib import Path from ..via_template import ViaTemplateActivator @@ -15,12 +11,12 @@ class PythonActivator(ViaTemplateActivator): yield Path("activate_this.py") def replacements(self, creator, dest_folder): - replacements = super(PythonActivator, self).replacements(creator, dest_folder) + replacements = super().replacements(creator, dest_folder) lib_folders = OrderedDict((os.path.relpath(str(i), str(dest_folder)), None) for i in creator.libs) win_py2 = creator.interpreter.platform == "win32" and creator.interpreter.version_info.major == 2 replacements.update( { - "__LIB_FOLDERS__": ensure_text(os.pathsep.join(lib_folders.keys())), + "__LIB_FOLDERS__": os.pathsep.join(lib_folders.keys()), "__DECODE_PATH__": ("yes" if win_py2 else ""), }, ) @@ -31,5 +27,10 @@ class PythonActivator(ViaTemplateActivator): py2 = creator.interpreter.version_info.major == 2 if py2: # on Python 2 we need to encode this into explicit utf-8, py3 supports unicode literals start = 2 if sys.version_info[0] == 3 else 1 - value = ensure_text(repr(value.encode("utf-8"))[start:-1]) + value = repr(value.encode("utf-8"))[start:-1] return value + + +__all__ = [ + "PythonActivator", +] diff --git a/src/virtualenv/activation/python/activate_this.py b/src/virtualenv/activation/python/activate_this.py index 29debe3..e8eeb84 100644 --- a/src/virtualenv/activation/python/activate_this.py +++ b/src/virtualenv/activation/python/activate_this.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Activate virtualenv for current interpreter: Use exec(open(this_file).read(), {'__file__': this_file}). diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py index 14f0979..75ecd83 100644 --- a/src/virtualenv/activation/via_template.py +++ b/src/virtualenv/activation/via_template.py @@ -1,13 +1,7 @@ -from __future__ import absolute_import, unicode_literals - import os import sys from abc import ABCMeta, abstractmethod -from six import add_metaclass - -from virtualenv.util.six import ensure_text - from .activator import Activator if sys.version_info >= (3, 7): @@ -16,8 +10,7 @@ else: from importlib_resources import read_binary -@add_metaclass(ABCMeta) -class ViaTemplateActivator(Activator): +class ViaTemplateActivator(Activator, metaclass=ABCMeta): @abstractmethod def templates(self): raise NotImplementedError @@ -33,10 +26,10 @@ class ViaTemplateActivator(Activator): def replacements(self, creator, dest_folder): return { "__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt, - "__VIRTUAL_ENV__": ensure_text(str(creator.dest)), + "__VIRTUAL_ENV__": str(creator.dest), "__VIRTUAL_NAME__": creator.env_name, - "__BIN_NAME__": ensure_text(str(creator.bin_dir.relative_to(creator.dest))), - "__PATH_SEP__": ensure_text(os.pathsep), + "__BIN_NAME__": str(creator.bin_dir.relative_to(creator.dest)), + "__PATH_SEP__": os.pathsep, } def _generate(self, replacements, templates, to_folder, creator): @@ -63,5 +56,9 @@ class ViaTemplateActivator(Activator): @staticmethod def _repr_unicode(creator, value): - # by default we just let it be unicode - return value + return value # by default we just let it be unicode + + +__all__ = [ + "ViaTemplateActivator", +] diff --git a/src/virtualenv/app_data/__init__.py b/src/virtualenv/app_data/__init__.py index e56e63d..262ac07 100644 --- a/src/virtualenv/app_data/__init__.py +++ b/src/virtualenv/app_data/__init__.py @@ -1,7 +1,6 @@ """ Application data stored by virtualenv. """ -from __future__ import absolute_import, unicode_literals import logging import os @@ -15,7 +14,7 @@ from .via_tempdir import TempAppData def _default_app_data_dir(env): - key = str("VIRTUALENV_OVERRIDE_APP_DATA") + key = "VIRTUALENV_OVERRIDE_APP_DATA" if key in env: return env[key] else: @@ -23,7 +22,7 @@ def _default_app_data_dir(env): def make_app_data(folder, **kwargs): - read_only = kwargs.pop("read_only") + is_read_only = kwargs.pop("read_only") env = kwargs.pop("env") if kwargs: # py3+ kwonly raise TypeError("unexpected keywords: {}") @@ -32,7 +31,7 @@ def make_app_data(folder, **kwargs): folder = _default_app_data_dir(env) folder = os.path.abspath(folder) - if read_only: + if is_read_only: return ReadOnlyAppData(folder) if not os.path.isdir(folder): diff --git a/src/virtualenv/app_data/base.py b/src/virtualenv/app_data/base.py index 4ea54d9..bc28b23 100644 --- a/src/virtualenv/app_data/base.py +++ b/src/virtualenv/app_data/base.py @@ -1,18 +1,14 @@ """ Application data stored by virtualenv. """ -from __future__ import absolute_import, unicode_literals from abc import ABCMeta, abstractmethod from contextlib import contextmanager -import six - from virtualenv.info import IS_ZIPAPP -@six.add_metaclass(ABCMeta) -class AppData(object): +class AppData(metaclass=ABCMeta): """Abstract storage interface for the virtualenv application""" @abstractmethod @@ -71,8 +67,7 @@ class AppData(object): raise NotImplementedError -@six.add_metaclass(ABCMeta) -class ContentStore(object): +class ContentStore(metaclass=ABCMeta): @abstractmethod def exists(self): raise NotImplementedError @@ -93,3 +88,9 @@ class ContentStore(object): @contextmanager def locked(self): pass + + +__all__ = [ + "ContentStore", + "AppData", +] diff --git a/src/virtualenv/app_data/na.py b/src/virtualenv/app_data/na.py index d589787..da45f58 100644 --- a/src/virtualenv/app_data/na.py +++ b/src/virtualenv/app_data/na.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from contextlib import contextmanager from .base import AppData, ContentStore @@ -44,7 +42,7 @@ class AppDataDisabled(AppData): raise self.error def py_info_clear(self): - """ """ + """nothing to clear""" class ContentStoreNA(ContentStore): @@ -52,15 +50,21 @@ class ContentStoreNA(ContentStore): return False def read(self): - """ """ + """nothing to read""" return None def write(self, content): - """ """ + """nothing to write""" def remove(self): - """ """ + """nothing to remove""" @contextmanager def locked(self): yield + + +__all__ = [ + "AppDataDisabled", + "ContentStoreNA", +] diff --git a/src/virtualenv/app_data/read_only.py b/src/virtualenv/app_data/read_only.py index 858978c..2663731 100644 --- a/src/virtualenv/app_data/read_only.py +++ b/src/virtualenv/app_data/read_only.py @@ -8,15 +8,16 @@ from .via_disk_folder import AppDataDiskFolder, PyInfoStoreDisk class ReadOnlyAppData(AppDataDiskFolder): can_update = False - def __init__(self, folder): # type: (str) -> None + def __init__(self, folder: str) -> None: if not os.path.isdir(folder): - raise RuntimeError("read-only app data directory {} does not exist".format(folder)) + raise RuntimeError(f"read-only app data directory {folder} does not exist") + super().__init__(folder) self.lock = NoOpFileLock(folder) - def reset(self): # type: () -> None + def reset(self) -> None: raise RuntimeError("read-only app data does not support reset") - def py_info_clear(self): # type: () -> None + def py_info_clear(self) -> None: raise NotImplementedError def py_info(self, path): @@ -31,4 +32,6 @@ class _PyInfoStoreDiskReadOnly(PyInfoStoreDisk): raise RuntimeError("read-only app data python info cannot be updated") -__all__ = ("ReadOnlyAppData",) +__all__ = [ + "ReadOnlyAppData", +] diff --git a/src/virtualenv/app_data/via_disk_folder.py b/src/virtualenv/app_data/via_disk_folder.py index 3f6afd5..94c4def 100644 --- a/src/virtualenv/app_data/via_disk_folder.py +++ b/src/virtualenv/app_data/via_disk_folder.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- """ A rough layout of the current storage goes as: virtualenv-app-data ├── py - <version> <cache information about python interpreters> -│ └── *.json/lock +│ └── *.json/lock ├── wheel <cache wheels used for seeding> -│ ├── house +│ ├── house │ │ └── *.whl <wheels downloaded go here> │ └── <python major.minor> -> 3.9 │ ├── img-<version> @@ -22,7 +21,6 @@ virtualenv-app-data ├── debug.py └── _virtualenv.py """ -from __future__ import absolute_import, unicode_literals import json import logging @@ -30,11 +28,8 @@ from abc import ABCMeta from contextlib import contextmanager from hashlib import sha256 -import six - from virtualenv.util.lock import ReentrantFileLock from virtualenv.util.path import safe_delete -from virtualenv.util.six import ensure_text from virtualenv.util.zipapp import extract from virtualenv.version import __version__ @@ -53,7 +48,7 @@ class AppDataDiskFolder(AppData): self.lock = ReentrantFileLock(folder) def __repr__(self): - return "{}({})".format(type(self).__name__, self.lock.path) + return f"{type(self).__name__}({self.lock.path})" def __str__(self): return str(self.lock.path) @@ -113,8 +108,7 @@ class AppDataDiskFolder(AppData): return self.lock.path / "wheel" / for_py_version / "image" / "1" / name -@six.add_metaclass(ABCMeta) -class JSONStoreDisk(ContentStore): +class JSONStoreDisk(ContentStore, metaclass=ABCMeta): def __init__(self, in_folder, key, msg, msg_args): self.in_folder = in_folder self.key = key @@ -123,7 +117,7 @@ class JSONStoreDisk(ContentStore): @property def file(self): - return self.in_folder.path / "{}.json".format(self.key) + return self.in_folder.path / f"{self.key}.json" def exists(self): return self.file.exists() @@ -132,7 +126,7 @@ class JSONStoreDisk(ContentStore): data, bad_format = None, False try: data = json.loads(self.file.read_text()) - logging.debug("got {} from %s".format(self.msg), *self.msg_args) + logging.debug(f"got {self.msg} from %s", *self.msg_args) return data except ValueError: bad_format = True @@ -147,7 +141,7 @@ class JSONStoreDisk(ContentStore): def remove(self): self.file.unlink() - logging.debug("removed {} at %s".format(self.msg), *self.msg_args) + logging.debug(f"removed {self.msg} at %s", *self.msg_args) @contextmanager def locked(self): @@ -157,21 +151,28 @@ class JSONStoreDisk(ContentStore): def write(self, content): folder = self.file.parent folder.mkdir(parents=True, exist_ok=True) - self.file.write_text(ensure_text(json.dumps(content, sort_keys=True, indent=2))) - logging.debug("wrote {} at %s".format(self.msg), *self.msg_args) + self.file.write_text(json.dumps(content, sort_keys=True, indent=2)) + logging.debug(f"wrote {self.msg} at %s", *self.msg_args) class PyInfoStoreDisk(JSONStoreDisk): def __init__(self, in_folder, path): - key = sha256(str(path).encode("utf-8") if six.PY3 else str(path)).hexdigest() - super(PyInfoStoreDisk, self).__init__(in_folder, key, "python info of %s", (path,)) + key = sha256(str(path).encode("utf-8")).hexdigest() + super().__init__(in_folder, key, "python info of %s", (path,)) class EmbedDistributionUpdateStoreDisk(JSONStoreDisk): def __init__(self, in_folder, distribution): - super(EmbedDistributionUpdateStoreDisk, self).__init__( + super().__init__( in_folder, distribution, "embed update of distribution %s", (distribution,), ) + + +__all__ = [ + "AppDataDiskFolder", + "JSONStoreDisk", + "PyInfoStoreDisk", +] diff --git a/src/virtualenv/app_data/via_tempdir.py b/src/virtualenv/app_data/via_tempdir.py index 112a3fe..c15741a 100644 --- a/src/virtualenv/app_data/via_tempdir.py +++ b/src/virtualenv/app_data/via_tempdir.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import logging from tempfile import mkdtemp @@ -13,7 +11,7 @@ class TempAppData(AppDataDiskFolder): can_update = False def __init__(self): - super(TempAppData, self).__init__(folder=mkdtemp()) + super().__init__(folder=mkdtemp()) logging.debug("created temporary app data folder %s", self.lock.path) def reset(self): @@ -25,3 +23,8 @@ class TempAppData(AppDataDiskFolder): def embed_update_log(self, distribution, for_py_version): raise NotImplementedError + + +__all__ = [ + "TempAppData", +] diff --git a/src/virtualenv/config/__init__.py b/src/virtualenv/config/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/config/__init__.py +++ b/src/virtualenv/config/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/config/cli/__init__.py b/src/virtualenv/config/cli/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/config/cli/__init__.py +++ b/src/virtualenv/config/cli/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/config/cli/parser.py b/src/virtualenv/config/cli/parser.py index c8e2f55..5ab4fbd 100644 --- a/src/virtualenv/config/cli/parser.py +++ b/src/virtualenv/config/cli/parser.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import os from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace from collections import OrderedDict @@ -12,7 +10,7 @@ from ..ini import IniConfig class VirtualEnvOptions(Namespace): def __init__(self, **kwargs): - super(VirtualEnvOptions, self).__init__(**kwargs) + super().__init__(**kwargs) self._src = None self._sources = {} @@ -25,7 +23,7 @@ class VirtualEnvOptions(Namespace): def __setattr__(self, key, value): if getattr(self, "_src", None) is not None: self._sources[key] = self._src - super(VirtualEnvOptions, self).__setattr__(key, value) + super().__setattr__(key, value) def get_source(self, key): return self._sources.get(key) @@ -37,10 +35,7 @@ class VirtualEnvOptions(Namespace): return max(self.verbose - self.quiet, 0) def __repr__(self): - return "{}({})".format( - type(self).__name__, - ", ".join("{}={}".format(k, v) for k, v in vars(self).items() if not k.startswith("_")), - ) + return f"{type(self).__name__}({', '.join(f'{k}={v}' for k, v in vars(self).items() if not k.startswith('_'))})" class VirtualEnvConfigParser(ArgumentParser): @@ -57,7 +52,7 @@ class VirtualEnvConfigParser(ArgumentParser): kwargs["add_help"] = False kwargs["formatter_class"] = HelpFormatter kwargs["prog"] = "virtualenv" - super(VirtualEnvConfigParser, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._fixed = set() if options is not None and not isinstance(options, VirtualEnvOptions): raise TypeError("options must be of type VirtualEnvOptions") @@ -105,20 +100,27 @@ class VirtualEnvConfigParser(ArgumentParser): self.options._src = "cli" try: namespace.env = self.env - return super(VirtualEnvConfigParser, self).parse_known_args(args, namespace=namespace) + return super().parse_known_args(args, namespace=namespace) finally: self.options._src = None class HelpFormatter(ArgumentDefaultsHelpFormatter): def __init__(self, prog): - super(HelpFormatter, self).__init__(prog, max_help_position=32, width=240) + super().__init__(prog, max_help_position=32, width=240) def _get_help_string(self, action): - # noinspection PyProtectedMember - text = super(HelpFormatter, self)._get_help_string(action) + + text = super()._get_help_string(action) if hasattr(action, "default_source"): default = " (default: %(default)s)" if text.endswith(default): - text = "{} (default: %(default)s -> from %(default_source)s)".format(text[: -len(default)]) + text = f"{text[: -len(default)]} (default: %(default)s -> from %(default_source)s)" return text + + +__all__ = [ + "HelpFormatter", + "VirtualEnvConfigParser", + "VirtualEnvOptions", +] diff --git a/src/virtualenv/config/convert.py b/src/virtualenv/config/convert.py index df40866..52f722e 100644 --- a/src/virtualenv/config/convert.py +++ b/src/virtualenv/config/convert.py @@ -1,16 +1,14 @@ -from __future__ import absolute_import, unicode_literals - import logging import os -class TypeData(object): +class TypeData: def __init__(self, default_type, as_type): self.default_type = default_type self.as_type = as_type def __repr__(self): - return "{}(base={}, as={})".format(self.__class__.__name__, self.default_type, self.as_type) + return f"{self.__class__.__name__}(base={self.default_type}, as={self.as_type})" def convert(self, value): return self.default_type(value) @@ -30,7 +28,7 @@ class BoolType(TypeData): def convert(self, value): if value.lower() not in self.BOOLEAN_STATES: - raise ValueError("Not a boolean: %s" % value) + raise ValueError(f"Not a boolean: {value}") return self.BOOLEAN_STATES[value.lower()] @@ -92,7 +90,7 @@ def get_type(action): return _CONVERT.get(default_type, TypeData)(default_type, as_type) -__all__ = ( +__all__ = [ "convert", "get_type", -) +] diff --git a/src/virtualenv/config/env_var.py b/src/virtualenv/config/env_var.py index 8f6211c..5dc0c1d 100644 --- a/src/virtualenv/config/env_var.py +++ b/src/virtualenv/config/env_var.py @@ -1,7 +1,3 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.six import ensure_str, ensure_text - from .convert import convert @@ -13,16 +9,18 @@ def get_env_var(key, as_type, env): :param env: environment variables to use :return: """ - environ_key = ensure_str("VIRTUALENV_{}".format(key.upper())) + environ_key = f"VIRTUALENV_{key.upper()}" if env.get(environ_key): value = env[environ_key] - # noinspection PyBroadException + try: - source = "env var {}".format(ensure_text(environ_key)) + source = f"env var {environ_key}" as_type = convert(value, as_type, source) return as_type, source except Exception: # note the converter already logs a warning when failures happen pass -__all__ = ("get_env_var",) +__all__ = [ + "get_env_var", +] diff --git a/src/virtualenv/config/ini.py b/src/virtualenv/config/ini.py index 0d945ee..50da884 100644 --- a/src/virtualenv/config/ini.py +++ b/src/virtualenv/config/ini.py @@ -1,20 +1,15 @@ -from __future__ import absolute_import, unicode_literals - import logging import os +from configparser import ConfigParser +from pathlib import Path from platformdirs import user_config_dir -from virtualenv.info import PY3 -from virtualenv.util import ConfigParser -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_str - from .convert import convert -class IniConfig(object): - VIRTUALENV_CONFIG_FILE_ENV_VAR = ensure_str("VIRTUALENV_CONFIG_FILE") +class IniConfig: + VIRTUALENV_CONFIG_FILE_ENV_VAR = "VIRTUALENV_CONFIG_FILE" STATE = {None: "failed to parse", True: "active", False: "missing"} section = "virtualenv" @@ -23,11 +18,10 @@ class IniConfig(object): env = os.environ if env is None else env config_file = env.get(self.VIRTUALENV_CONFIG_FILE_ENV_VAR, None) self.is_env_var = config_file is not None - config_file = ( - Path(config_file) - if config_file is not None - else Path(user_config_dir(appname="virtualenv", appauthor="pypa")) / "virtualenv.ini" - ) + if config_file is None: + config_file = Path(user_config_dir(appname="virtualenv", appauthor="pypa")) / "virtualenv.ini" + else: + config_file = Path(config_file) self.config_file = config_file self._cache = {} @@ -40,7 +34,7 @@ class IniConfig(object): else: if self.has_config_file: self.config_file = self.config_file.resolve() - self.config_parser = ConfigParser.ConfigParser() + self.config_parser = ConfigParser() try: self._load() self.has_virtualenv_section = self.config_parser.has_section(self.section) @@ -51,14 +45,12 @@ class IniConfig(object): def _load(self): with self.config_file.open("rt") as file_handler: - reader = getattr(self.config_parser, "read_file" if PY3 else "readfp") - reader(file_handler) + return self.config_parser.read_file(file_handler) def get(self, key, as_type): cache_key = key, as_type if cache_key in self._cache: return self._cache[cache_key] - # noinspection PyBroadException try: source = "file" raw_value = self.config_parser.get(self.section, key.lower()) @@ -74,11 +66,7 @@ class IniConfig(object): @property def epilog(self): - msg = "{}config file {} {} (change{} via env var {})" - return msg.format( - "\n", - self.config_file, - self.STATE[self.has_config_file], - "d" if self.is_env_var else "", - self.VIRTUALENV_CONFIG_FILE_ENV_VAR, + return ( + f"\nconfig file {self.config_file} {self.STATE[self.has_config_file]} " + f"(change{'d' if self.is_env_var else ''} via env var {self.VIRTUALENV_CONFIG_FILE_ENV_VAR})" ) diff --git a/src/virtualenv/create/__init__.py b/src/virtualenv/create/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/create/__init__.py +++ b/src/virtualenv/create/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/create/creator.py b/src/virtualenv/create/creator.py index f7b8554..ad41bfe 100644 --- a/src/virtualenv/create/creator.py +++ b/src/virtualenv/create/creator.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, print_function, unicode_literals - import json import logging import os @@ -8,14 +6,10 @@ from abc import ABCMeta, abstractmethod from argparse import ArgumentTypeError from ast import literal_eval from collections import OrderedDict -from textwrap import dedent - -from six import add_metaclass +from pathlib import Path from virtualenv.discovery.cached_py_info import LogCmd -from virtualenv.info import WIN_CPYTHON_2 -from virtualenv.util.path import Path, safe_delete -from virtualenv.util.six import ensure_str, ensure_text +from virtualenv.util.path import safe_delete from virtualenv.util.subprocess import run_cmd from virtualenv.version import __version__ @@ -25,13 +19,12 @@ HERE = Path(os.path.abspath(__file__)).parent DEBUG_SCRIPT = HERE / "debug.py" -class CreatorMeta(object): +class CreatorMeta: def __init__(self): self.error = None -@add_metaclass(ABCMeta) -class Creator(object): +class Creator(metaclass=ABCMeta): """A class that given a python Interpreter creates a virtual environment""" def __init__(self, options, interpreter): @@ -50,14 +43,11 @@ class Creator(object): self.env = options.env def __repr__(self): - return ensure_str(self.__unicode__()) - - def __unicode__(self): - return "{}({})".format(self.__class__.__name__, ", ".join("{}={}".format(k, v) for k, v in self._args())) + return f"{self.__class__.__name__}({', '.join(f'{k}={v}' for k, v in self._args())})" def _args(self): return [ - ("dest", ensure_text(str(self.dest))), + ("dest", str(self.dest)), ("clear", self.clear), ("no_vcs_ignore", self.no_vcs_ignore), ] @@ -112,16 +102,14 @@ class Creator(object): def non_write_able(dest, value): common = Path(*os.path.commonprefix([value.parts, dest.parts])) - raise ArgumentTypeError( - "the destination {} is not write-able at {}".format(dest.relative_to(common), common), - ) + raise ArgumentTypeError(f"the destination {dest.relative_to(common)} is not write-able at {common}") # the file system must be able to encode # note in newer CPython this is always utf-8 https://www.python.org/dev/peps/pep-0529/ encoding = sys.getfilesystemencoding() refused = OrderedDict() kwargs = {"errors": "ignore"} if encoding != "mbcs" else {} - for char in ensure_text(raw_value): + for char in str(raw_value): try: trip = char.encode(encoding, **kwargs).decode(encoding) if trip == char: @@ -130,23 +118,17 @@ class Creator(object): except ValueError: refused[char] = None if refused: - raise ArgumentTypeError( - "the file system codec ({}) cannot handle characters {!r} within {!r}".format( - encoding, - "".join(refused.keys()), - raw_value, - ), - ) + bad = "".join(refused.keys()) + msg = f"the file system codec ({encoding}) cannot handle characters {bad!r} within {raw_value!r}" + raise ArgumentTypeError(msg) if os.pathsep in raw_value: - raise ArgumentTypeError( - "destination {!r} must not contain the path separator ({}) as this would break " - "the activation scripts".format(raw_value, os.pathsep), - ) + msg = f"destination {raw_value!r} must not contain the path separator ({os.pathsep})" + raise ArgumentTypeError(f"{msg} as this would break the activation scripts") value = Path(raw_value) if value.exists() and value.is_file(): - raise ArgumentTypeError("the destination {} already exists and is a file".format(value)) - if (3, 3) <= sys.version_info <= (3, 6): + raise ArgumentTypeError(f"the destination {value} already exists and is a file") + if sys.version_info <= (3, 6): # pre 3.6 resolve is always strict, aka must exists, sidestep by using os.path operation dest = Path(os.path.realpath(raw_value)) else: @@ -154,7 +136,7 @@ class Creator(object): value = dest while dest: if dest.exists(): - if os.access(ensure_text(str(dest)), os.W_OK): + if os.access(str(dest), os.W_OK): break else: non_write_able(dest, value) @@ -185,14 +167,7 @@ class Creator(object): # mark this folder to be ignored by VCS, handle https://www.python.org/dev/peps/pep-0610/#registered-vcs git_ignore = self.dest / ".gitignore" if not git_ignore.exists(): - git_ignore.write_text( - dedent( - """ - # created by virtualenv automatically - * - """, - ).lstrip(), - ) + git_ignore.write_text("# created by virtualenv automatically\n*\n") # Mercurial - does not support the .hgignore file inside a subdirectory directly, but only if included via the # subinclude directive from root, at which point on might as well ignore the directory itself, see # https://www.selenic.com/mercurial/hgignore.5.html for more details @@ -208,23 +183,20 @@ class Creator(object): self._debug = get_env_debug_info(self.exe, self.debug_script(), self.app_data, self.env) return self._debug - # noinspection PyMethodMayBeStatic - def debug_script(self): + @staticmethod + def debug_script(): return DEBUG_SCRIPT def get_env_debug_info(env_exe, debug_script, app_data, env): env = env.copy() - env.pop(str("PYTHONPATH"), None) + env.pop("PYTHONPATH", None) with app_data.ensure_extracted(debug_script) as debug_script: cmd = [str(env_exe), str(debug_script)] - if WIN_CPYTHON_2: - cmd = [ensure_text(i) for i in cmd] - logging.debug(str("debug via %r"), LogCmd(cmd)) + logging.debug("debug via %r", LogCmd(cmd)) code, out, err = run_cmd(cmd) - # noinspection PyBroadException try: if code != 0: if out: @@ -243,3 +215,9 @@ def get_env_debug_info(env_exe, debug_script, app_data, env): if "sys" in result and "path" in result["sys"]: del result["sys"]["path"][0] return result + + +__all__ = [ + "Creator", + "CreatorMeta", +] diff --git a/src/virtualenv/create/describe.py b/src/virtualenv/create/describe.py index 6f05ff1..fade5c4 100644 --- a/src/virtualenv/create/describe.py +++ b/src/virtualenv/create/describe.py @@ -1,17 +1,11 @@ -from __future__ import absolute_import, print_function, unicode_literals - from abc import ABCMeta from collections import OrderedDict - -from six import add_metaclass +from pathlib import Path from virtualenv.info import IS_WIN -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text -@add_metaclass(ABCMeta) -class Describe(object): +class Describe(metaclass=ABCMeta): """Given a host interpreter tell us information about what the created interpreter might look like""" suffix = ".exe" if IS_WIN else "" @@ -59,13 +53,12 @@ class Describe(object): @property def _config_vars(self): if self._conf_vars is None: - self._conf_vars = self._calc_config_vars(ensure_text(str(self.dest))) + self._conf_vars = self._calc_config_vars(self.dest) return self._conf_vars def _calc_config_vars(self, to): - return { - k: (to if v.startswith(self.interpreter.prefix) else v) for k, v in self.interpreter.sysconfig_vars.items() - } + sys_vars = self.interpreter.sysconfig_vars + return {k: (to if v.startswith(self.interpreter.prefix) else v) for k, v in sys_vars.items()} @classmethod def can_describe(cls, interpreter): @@ -74,11 +67,11 @@ class Describe(object): @property def env_name(self): - return ensure_text(self.dest.parts[-1]) + return self.dest.parts[-1] @property def exe(self): - return self.bin_dir / "{}{}".format(self.exe_stem(), self.suffix) + return self.bin_dir / f"{self.exe_stem()}{self.suffix}" @classmethod def exe_stem(cls): @@ -86,32 +79,37 @@ class Describe(object): raise NotImplementedError def script(self, name): - return self.script_dir / "{}{}".format(name, self.suffix) + return self.script_dir / f"{name}{self.suffix}" -@add_metaclass(ABCMeta) -class Python2Supports(Describe): +class Python2Supports(Describe, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.version_info.major == 2 and super(Python2Supports, cls).can_describe(interpreter) + return interpreter.version_info.major == 2 and super().can_describe(interpreter) -@add_metaclass(ABCMeta) -class Python3Supports(Describe): +class Python3Supports(Describe, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.version_info.major == 3 and super(Python3Supports, cls).can_describe(interpreter) + return interpreter.version_info.major == 3 and super().can_describe(interpreter) -@add_metaclass(ABCMeta) -class PosixSupports(Describe): +class PosixSupports(Describe, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.os == "posix" and super(PosixSupports, cls).can_describe(interpreter) + return interpreter.os == "posix" and super().can_describe(interpreter) -@add_metaclass(ABCMeta) -class WindowsSupports(Describe): +class WindowsSupports(Describe, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.os == "nt" and super(WindowsSupports, cls).can_describe(interpreter) + return interpreter.os == "nt" and super().can_describe(interpreter) + + +__all__ = [ + "Describe", + "Python2Supports", + "Python3Supports", + "PosixSupports", + "WindowsSupports", +] diff --git a/src/virtualenv/create/pyenv_cfg.py b/src/virtualenv/create/pyenv_cfg.py index 1a8d824..9193a28 100644 --- a/src/virtualenv/create/pyenv_cfg.py +++ b/src/virtualenv/create/pyenv_cfg.py @@ -1,12 +1,8 @@ -from __future__ import absolute_import, unicode_literals - import logging from collections import OrderedDict -from virtualenv.util.six import ensure_text - -class PyEnvCfg(object): +class PyEnvCfg: def __init__(self, content, path): self.content = content self.path = path @@ -31,10 +27,10 @@ class PyEnvCfg(object): return content def write(self): - logging.debug("write %s", ensure_text(str(self.path))) + logging.debug("write %s", self.path) text = "" for key, value in self.content.items(): - line = "{} = {}".format(key, value) + line = f"{key} = {value}" logging.debug("\t%s", line) text += line text += "\n" @@ -58,4 +54,9 @@ class PyEnvCfg(object): return self def __repr__(self): - return "{}(path={})".format(self.__class__.__name__, self.path) + return f"{self.__class__.__name__}(path={self.path})" + + +__all__ = [ + "PyEnvCfg", +] diff --git a/src/virtualenv/create/via_global_ref/api.py b/src/virtualenv/create/via_global_ref/api.py index 6f296f4..7a4086f 100644 --- a/src/virtualenv/create/via_global_ref/api.py +++ b/src/virtualenv/create/via_global_ref/api.py @@ -1,21 +1,16 @@ -from __future__ import absolute_import, unicode_literals - import logging import os from abc import ABCMeta - -from six import add_metaclass +from pathlib import Path from virtualenv.info import fs_supports_symlink -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text from ..creator import Creator, CreatorMeta class ViaGlobalRefMeta(CreatorMeta): def __init__(self): - super(ViaGlobalRefMeta, self).__init__() + super().__init__() self.copy_error = None self.symlink_error = None if not fs_supports_symlink(): @@ -30,10 +25,9 @@ class ViaGlobalRefMeta(CreatorMeta): return not self.symlink_error -@add_metaclass(ABCMeta) -class ViaGlobalRefApi(Creator): +class ViaGlobalRefApi(Creator, metaclass=ABCMeta): def __init__(self, options, interpreter): - super(ViaGlobalRefApi, self).__init__(options, interpreter) + super().__init__(options, interpreter) self.symlinks = self._should_symlink(options) self.enable_system_site_package = options.system_site @@ -56,7 +50,7 @@ class ViaGlobalRefApi(Creator): @classmethod def add_parser_arguments(cls, parser, interpreter, meta, app_data): - super(ViaGlobalRefApi, cls).add_parser_arguments(parser, interpreter, meta, app_data) + super().add_parser_arguments(parser, interpreter, meta, app_data) parser.add_argument( "--system-site-packages", default=False, @@ -92,10 +86,10 @@ class ViaGlobalRefApi(Creator): text = self.env_patch_text() if text: pth = self.purelib / "_virtualenv.pth" - logging.debug("create virtualenv import hook file %s", ensure_text(str(pth))) + logging.debug("create virtualenv import hook file %s", pth) pth.write_text("import _virtualenv") dest_path = self.purelib / "_virtualenv.py" - logging.debug("create %s", ensure_text(str(dest_path))) + logging.debug("create %s", dest_path) dest_path.write_text(text) def env_patch_text(self): @@ -105,8 +99,14 @@ class ViaGlobalRefApi(Creator): return text.replace('"__SCRIPT_DIR__"', repr(os.path.relpath(str(self.script_dir), str(self.purelib)))) def _args(self): - return super(ViaGlobalRefApi, self)._args() + [("global", self.enable_system_site_package)] + return super()._args() + [("global", self.enable_system_site_package)] def set_pyenv_cfg(self): - super(ViaGlobalRefApi, self).set_pyenv_cfg() + super().set_pyenv_cfg() self.pyenv_cfg["include-system-site-packages"] = "true" if self.enable_system_site_package else "false" + + +__all__ = [ + "ViaGlobalRefMeta", + "ViaGlobalRefApi", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/builtin_way.py b/src/virtualenv/create/via_global_ref/builtin/builtin_way.py index 279ee80..e321593 100644 --- a/src/virtualenv/create/via_global_ref/builtin/builtin_way.py +++ b/src/virtualenv/create/via_global_ref/builtin/builtin_way.py @@ -1,17 +1,17 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta -from six import add_metaclass - from virtualenv.create.creator import Creator from virtualenv.create.describe import Describe -@add_metaclass(ABCMeta) -class VirtualenvBuiltin(Creator, Describe): +class VirtualenvBuiltin(Creator, Describe, metaclass=ABCMeta): """A creator that does operations itself without delegation, if we can create it we can also describe it""" def __init__(self, options, interpreter): Creator.__init__(self, options, interpreter) Describe.__init__(self, self.dest, interpreter) + + +__all__ = [ + "VirtualenvBuiltin", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/__init__.py b/src/virtualenv/create/via_global_ref/builtin/cpython/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/__init__.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py index c93f9f3..b2f7944 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py @@ -1,45 +1,36 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta from collections import OrderedDict - -from six import add_metaclass +from pathlib import Path from virtualenv.create.describe import PosixSupports, WindowsSupports from virtualenv.create.via_global_ref.builtin.ref import RefMust, RefWhen -from virtualenv.util.path import Path from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin -@add_metaclass(ABCMeta) -class CPython(ViaGlobalRefVirtualenvBuiltin): +class CPython(ViaGlobalRefVirtualenvBuiltin, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.implementation == "CPython" and super(CPython, cls).can_describe(interpreter) + return interpreter.implementation == "CPython" and super().can_describe(interpreter) @classmethod def exe_stem(cls): return "python" -@add_metaclass(ABCMeta) -class CPythonPosix(CPython, PosixSupports): +class CPythonPosix(CPython, PosixSupports, metaclass=ABCMeta): """Create a CPython virtual environment on POSIX platforms""" @classmethod def _executables(cls, interpreter): host_exe = Path(interpreter.system_executable) major, minor = interpreter.version_info.major, interpreter.version_info.minor - targets = OrderedDict( - (i, None) for i in ["python", "python{}".format(major), "python{}.{}".format(major, minor), host_exe.name] - ) + targets = OrderedDict((i, None) for i in ["python", f"python{major}", f"python{major}.{minor}", host_exe.name]) must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA yield host_exe, list(targets.keys()), must, RefWhen.ANY -@add_metaclass(ABCMeta) -class CPythonWindows(CPython, WindowsSupports): +class CPythonWindows(CPython, WindowsSupports, metaclass=ABCMeta): @classmethod def _executables(cls, interpreter): # symlink of the python executables does not work reliably, copy always instead @@ -63,3 +54,11 @@ def is_mac_os_framework(interpreter): value = "Python3" if interpreter.version_info.major == 3 else "Python" return framework_var == value return False + + +__all__ = [ + "CPython", + "CPythonPosix", + "CPythonWindows", + "is_mac_os_framework", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py index dc822bc..5a9eb85 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py @@ -1,25 +1,19 @@ -from __future__ import absolute_import, unicode_literals - import abc import logging - -from six import add_metaclass +from pathlib import Path from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest -from virtualenv.util.path import Path from ..python2.python2 import Python2 from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework -@add_metaclass(abc.ABCMeta) -class CPython2(CPython, Python2): +class CPython2(CPython, Python2, metaclass=abc.ABCMeta): """Create a CPython version 2 virtual environment""" @classmethod def sources(cls, interpreter): - for src in super(CPython2, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # include folder needed on Python 2 as we don't have pyenv.cfg host_include_marker = cls.host_include_marker(interpreter) if host_include_marker.exists(): @@ -40,12 +34,10 @@ class CPython2(CPython, Python2): @classmethod def modules(cls): - return [ - "os", # landmark to set sys.prefix - ] + return ["os"] # landmark to set sys.prefix def ensure_directories(self): - dirs = super(CPython2, self).ensure_directories() + dirs = super().ensure_directories() host_include_marker = self.host_include_marker(self.interpreter) if host_include_marker.exists(): dirs.add(self.include.parent) @@ -54,14 +46,12 @@ class CPython2(CPython, Python2): return dirs -@add_metaclass(abc.ABCMeta) -class CPython2PosixBase(CPython2, CPythonPosix): +class CPython2PosixBase(CPython2, CPythonPosix, metaclass=abc.ABCMeta): """common to macOs framework builds and other posix CPython2""" @classmethod def sources(cls, interpreter): - for src in super(CPython2PosixBase, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # check if the makefile exists and if so make it available under the virtual environment make_file = Path(interpreter.sysconfig["makefile_filename"]) @@ -75,12 +65,11 @@ class CPython2Posix(CPython2PosixBase): @classmethod def can_describe(cls, interpreter): - return is_mac_os_framework(interpreter) is False and super(CPython2Posix, cls).can_describe(interpreter) + return is_mac_os_framework(interpreter) is False and super().can_describe(interpreter) @classmethod def sources(cls, interpreter): - for src in super(CPython2Posix, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # landmark for exec_prefix exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload") yield PathRefToDest(exec_marker_file, dest=to_path) @@ -91,8 +80,7 @@ class CPython2Windows(CPython2, CPythonWindows): @classmethod def sources(cls, interpreter): - for src in super(CPython2Windows, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) py27_dll = Path(interpreter.system_executable).parent / "python27.dll" if py27_dll.exists(): # this might be global in the Windows folder in which case it's alright to be missing yield PathRefToDest(py27_dll, dest=cls.to_bin) @@ -100,3 +88,11 @@ class CPython2Windows(CPython2, CPythonWindows): libs = Path(interpreter.system_prefix) / "libs" if libs.exists(): yield PathRefToDest(libs, dest=lambda self, s: self.dest / s.name) + + +__all__ = [ + "CPython2", + "CPython2PosixBase", + "CPython2Posix", + "CPython2Windows", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py index 03d3097..433dc4a 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py @@ -1,33 +1,28 @@ -from __future__ import absolute_import, unicode_literals - import abc import fnmatch from itertools import chain from operator import methodcaller as method +from pathlib import Path from textwrap import dedent -from six import add_metaclass - from virtualenv.create.describe import Python3Supports from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest from virtualenv.create.via_global_ref.store import is_store_python -from virtualenv.util.path import Path from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework -@add_metaclass(abc.ABCMeta) -class CPython3(CPython, Python3Supports): - """ """ +class CPython3(CPython, Python3Supports, metaclass=abc.ABCMeta): + """CPython 3 or later""" class CPython3Posix(CPythonPosix, CPython3): @classmethod def can_describe(cls, interpreter): - return is_mac_os_framework(interpreter) is False and super(CPython3Posix, cls).can_describe(interpreter) + return is_mac_os_framework(interpreter) is False and super().can_describe(interpreter) def env_patch_text(self): - text = super(CPython3Posix, self).env_patch_text() + text = super().env_patch_text() if self.pyvenv_launch_patch_active(self.interpreter): text += dedent( """ @@ -52,7 +47,7 @@ class CPython3Windows(CPythonWindows, CPython3): def setup_meta(cls, interpreter): if is_store_python(interpreter): # store python is not supported here return None - return super(CPython3Windows, cls).setup_meta(interpreter) + return super().setup_meta(interpreter) @classmethod def sources(cls, interpreter): @@ -64,12 +59,11 @@ class CPython3Windows(CPythonWindows, CPython3): cls.dll_and_pyd(interpreter), cls.python_zip(interpreter), ) - for ref in refs: - yield ref + yield from refs @classmethod def executables(cls, interpreter): - return super(CPython3Windows, cls).sources(interpreter) + return super().sources(interpreter) @classmethod def has_shim(cls, interpreter): @@ -88,7 +82,7 @@ class CPython3Windows(CPythonWindows, CPython3): # starting with CPython 3.7 Windows ships with a venvlauncher.exe that avoids the need for dll/pyd copies # it also means the wrapper must be copied to avoid bugs such as https://bugs.python.org/issue42013 return cls.shim(interpreter) - return super(CPython3Windows, cls).host_python(interpreter) + return super().host_python(interpreter) @classmethod def dll_and_pyd(cls, interpreter): @@ -119,10 +113,17 @@ class CPython3Windows(CPythonWindows, CPython3): move/rename *zip* file and edit `sys.path` by editing *_pth* file. Here the `pattern` is used only for the default *zip* file name! """ - pattern = "*python{}.zip".format(interpreter.version_nodot) + pattern = f"*python{interpreter.version_nodot}.zip" matches = fnmatch.filter(interpreter.path, pattern) matched_paths = map(Path, matches) existing_paths = filter(method("exists"), matched_paths) path = next(existing_paths, None) if path is not None: yield PathRefToDest(path, cls.to_bin) + + +__all__ = [ + "CPython3", + "CPython3Posix", + "CPython3Windows", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py index d64f0d9..16da661 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py @@ -1,32 +1,27 @@ -# -*- coding: utf-8 -*- """The Apple Framework builds require their own customization""" import logging import os import struct import subprocess from abc import ABCMeta, abstractmethod +from pathlib import Path from textwrap import dedent -from six import add_metaclass, text_type - from virtualenv.create.via_global_ref.builtin.ref import ExePathRefToDest, PathRefToDest, RefMust from virtualenv.info import IS_MAC_ARM64 -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text from .common import CPython, CPythonPosix, is_mac_os_framework from .cpython2 import CPython2PosixBase from .cpython3 import CPython3 -@add_metaclass(ABCMeta) -class CPythonmacOsFramework(CPython): +class CPythonmacOsFramework(CPython, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): - return is_mac_os_framework(interpreter) and super(CPythonmacOsFramework, cls).can_describe(interpreter) + return is_mac_os_framework(interpreter) and super().can_describe(interpreter) def create(self): - super(CPythonmacOsFramework, self).create() + super().create() # change the install_name of the copied python executables target = self.desired_mach_o_image_path() @@ -42,7 +37,7 @@ class CPythonmacOsFramework(CPython): @classmethod def _executables(cls, interpreter): - for _, targets, must, when in super(CPythonmacOsFramework, cls)._executables(interpreter): + for _, targets, must, when in super()._executables(interpreter): # Make sure we use the embedded interpreter inside the framework, even if sys.executable points to the # stub executable in ${sys.prefix}/bin. # See http://groups.google.com/group/python-virtualenv/browse_thread/thread/17cab2f85da75951 @@ -61,8 +56,8 @@ class CPythonmacOsFramework(CPython): class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase): @classmethod def can_create(cls, interpreter): - if not IS_MAC_ARM64 and super(CPython2macOsFramework, cls).can_describe(interpreter): - return super(CPython2macOsFramework, cls).can_create(interpreter) + if not IS_MAC_ARM64 and super().can_describe(interpreter): + return super().can_create(interpreter) return False def current_mach_o_image_path(self): @@ -73,8 +68,7 @@ class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase): @classmethod def sources(cls, interpreter): - for src in super(CPython2macOsFramework, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # landmark for exec_prefix exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload") yield PathRefToDest(exec_marker_file, dest=to_path) @@ -89,21 +83,19 @@ class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase): @property def reload_code(self): - result = super(CPython2macOsFramework, self).reload_code + result = super().reload_code result = dedent( - """ + f""" # the bundled site.py always adds the global site package if we're on python framework build, escape this import sysconfig config = sysconfig.get_config_vars() before = config["PYTHONFRAMEWORK"] try: config["PYTHONFRAMEWORK"] = "" - {} + {result} finally: config["PYTHONFRAMEWORK"] = before - """.format( - result, - ), + """, ) return result @@ -132,11 +124,11 @@ class CPython2macOsArmFramework(CPython2macOsFramework, CPythonmacOsFramework, C # Reset the signing on Darwin since the exe has been modified. # Note codesign fails on the original exe, it needs to be copied and moved back. bak_dir.mkdir(parents=True, exist_ok=True) - subprocess.check_call(["cp", text_type(exe), text_type(bak_dir)]) - subprocess.check_call(["mv", text_type(bak_dir / exe.name), text_type(exe)]) + subprocess.check_call(["cp", str(exe), str(bak_dir)]) + subprocess.check_call(["mv", str(bak_dir / exe.name), str(exe)]) bak_dir.rmdir() metadata = "--preserve-metadata=identifier,entitlements,flags,runtime" - cmd = ["codesign", "-s", "-", metadata, "-f", text_type(exe)] + cmd = ["codesign", "-s", "-", metadata, "-f", str(exe)] logging.debug("Changing Signature: %s", cmd) subprocess.check_call(cmd) except Exception: @@ -153,8 +145,7 @@ class CPython3macOsFramework(CPythonmacOsFramework, CPython3, CPythonPosix): @classmethod def sources(cls, interpreter): - for src in super(CPython3macOsFramework, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # add a symlink to the host python image exe = Path(interpreter.prefix) / "Python3" @@ -162,20 +153,18 @@ class CPython3macOsFramework(CPythonmacOsFramework, CPython3, CPythonPosix): @property def reload_code(self): - result = super(CPython3macOsFramework, self).reload_code + result = super().reload_code result = dedent( - """ + f""" # the bundled site.py always adds the global site package if we're on python framework build, escape this import sys before = sys._framework try: sys._framework = None - {} + {result} finally: sys._framework = before - """.format( - result, - ), + """, ) return result @@ -205,7 +194,7 @@ def fix_mach_o(exe, current, new, max_size): unneeded bits of information, however Mac OS X 10.5 and earlier cannot read this new Link Edit table format. """ try: - logging.debug("change Mach-O for %s from %s to %s", ensure_text(exe), current, ensure_text(new)) + logging.debug("change Mach-O for %s from %s to %s", exe, current, new) _builtin_change_mach_o(max_size)(exe, current, new) except Exception as e: logging.warning("Could not call _builtin_change_mac_o: %s. " "Trying to call install_name_tool instead.", e) @@ -227,7 +216,7 @@ def _builtin_change_mach_o(maxint): LITTLE_ENDIAN = "<" LC_LOAD_DYLIB = 0xC - class FileView(object): + class FileView: """A proxy for file-like objects that exposes a given view of a file. Modified from macholib.""" def __init__(self, file_obj, start=0, size=maxint): @@ -240,15 +229,15 @@ def _builtin_change_mach_o(maxint): self._pos = 0 def __repr__(self): - return "<fileview [{:d}, {:d}] {!r}>".format(self._start, self._end, self._file_obj) + return f"<fileview [{self._start:d}, {self._end:d}] {self._file_obj!r}>" def tell(self): return self._pos def _checkwindow(self, seek_to, op): if not (self._start <= seek_to <= self._end): - msg = "{} to offset {:d} is outside window [{:d}, {:d}]".format(op, seek_to, self._start, self._end) - raise IOError(msg) + msg = f"{op} to offset {seek_to:d} is outside window [{self._start:d}, {self._end:d}]" + raise OSError(msg) def seek(self, offset, whence=0): seek_to = offset @@ -259,7 +248,7 @@ def _builtin_change_mach_o(maxint): elif whence == os.SEEK_END: seek_to += self._end else: - raise IOError("Invalid whence argument to seek: {!r}".format(whence)) + raise OSError(f"Invalid whence argument to seek: {whence!r}") self._checkwindow(seek_to, "seek") self._file_obj.seek(seek_to) self._pos = seek_to - self._start @@ -345,3 +334,10 @@ def _builtin_change_mach_o(maxint): do_file(f) return mach_o_change + + +__all__ = [ + "CPythonmacOsFramework", + "CPython2macOsFramework", + "CPython3macOsFramework", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py index 4504d81..97fa963 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py @@ -1,46 +1,39 @@ -from __future__ import absolute_import, unicode_literals - import abc - -from six import add_metaclass +from pathlib import Path from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest, RefMust, RefWhen -from virtualenv.util.path import Path from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin -@add_metaclass(abc.ABCMeta) -class PyPy(ViaGlobalRefVirtualenvBuiltin): +class PyPy(ViaGlobalRefVirtualenvBuiltin, metaclass=abc.ABCMeta): @classmethod def can_describe(cls, interpreter): - return interpreter.implementation == "PyPy" and super(PyPy, cls).can_describe(interpreter) + return interpreter.implementation == "PyPy" and super().can_describe(interpreter) @classmethod def _executables(cls, interpreter): host = Path(interpreter.system_executable) - targets = sorted("{}{}".format(name, PyPy.suffix) for name in cls.exe_names(interpreter)) + targets = sorted(f"{name}{PyPy.suffix}" for name in cls.exe_names(interpreter)) must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA yield host, targets, must, RefWhen.ANY @classmethod def executables(cls, interpreter): - for src in super(PyPy, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) @classmethod def exe_names(cls, interpreter): return { cls.exe_stem(), "python", - "python{}".format(interpreter.version_info.major), - "python{}.{}".format(*interpreter.version_info), + f"python{interpreter.version_info.major}", + f"python{interpreter.version_info.major}.{interpreter.version_info.minor}", } @classmethod def sources(cls, interpreter): - for exe in cls.executables(interpreter): - yield exe + yield from cls.executables(interpreter) for host in cls._add_shared_libs(interpreter): yield PathRefToDest(host, dest=lambda self, s: self.bin_dir / s.name) @@ -48,9 +41,13 @@ class PyPy(ViaGlobalRefVirtualenvBuiltin): def _add_shared_libs(cls, interpreter): # https://bitbucket.org/pypy/pypy/issue/1922/future-proofing-virtualenv python_dir = Path(interpreter.system_executable).resolve().parent - for src in cls._shared_libs(python_dir): - yield src + yield from cls._shared_libs(python_dir) @classmethod def _shared_libs(cls, python_dir): raise NotImplementedError + + +__all__ = [ + "PyPy", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py index 78349d4..9f2343f 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py @@ -1,22 +1,17 @@ -from __future__ import absolute_import, unicode_literals - import abc import logging import os - -from six import add_metaclass +from pathlib import Path from virtualenv.create.describe import PosixSupports, WindowsSupports from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest -from virtualenv.util.path import Path from ..python2.python2 import Python2 from .common import PyPy -@add_metaclass(abc.ABCMeta) -class PyPy2(PyPy, Python2): - """ """ +class PyPy2(PyPy, Python2, metaclass=abc.ABCMeta): + """PyPy 2""" @classmethod def exe_stem(cls): @@ -24,8 +19,7 @@ class PyPy2(PyPy, Python2): @classmethod def sources(cls, interpreter): - for src in super(PyPy2, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # include folder needed on Python 2 as we don't have pyenv.cfg host_include_marker = cls.host_include_marker(interpreter) if host_include_marker.exists(): @@ -46,7 +40,7 @@ class PyPy2(PyPy, Python2): @classmethod def modules(cls): # pypy2 uses some modules before the site.py loads, so we need to include these too - return super(PyPy2, cls).modules() + [ + return super().modules() + [ "os", "copy_reg", "genericpath", @@ -61,7 +55,7 @@ class PyPy2(PyPy, Python2): return self.dest / "lib_pypy" def ensure_directories(self): - dirs = super(PyPy2, self).ensure_directories() + dirs = super().ensure_directories() dirs.add(self.lib_pypy) host_include_marker = self.host_include_marker(self.interpreter) if host_include_marker.exists(): @@ -76,7 +70,7 @@ class PyPy2(PyPy, Python2): PyPy2 built-in imports are handled by this path entry, don't overwrite to not disable it see: https://github.com/pypa/virtualenv/issues/1652 """ - return 'or path.endswith("lib_pypy{}__extensions__") # PyPy2 built-in import marker'.format(os.sep) + return f'or path.endswith("lib_pypy{os.sep}__extensions__") # PyPy2 built-in import marker' class PyPy2Posix(PyPy2, PosixSupports): @@ -84,7 +78,7 @@ class PyPy2Posix(PyPy2, PosixSupports): @classmethod def modules(cls): - return super(PyPy2Posix, cls).modules() + ["posixpath"] + return super().modules() + ["posixpath"] @classmethod def _shared_libs(cls, python_dir): @@ -96,8 +90,7 @@ class PyPy2Posix(PyPy2, PosixSupports): @classmethod def sources(cls, interpreter): - for src in super(PyPy2Posix, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) host_lib = Path(interpreter.system_prefix) / "lib" if host_lib.exists(): yield PathRefToDest(host_lib, dest=lambda self, _: self.lib) @@ -108,7 +101,7 @@ class Pypy2Windows(PyPy2, WindowsSupports): @classmethod def modules(cls): - return super(Pypy2Windows, cls).modules() + ["ntpath"] + return super().modules() + ["ntpath"] @classmethod def _shared_libs(cls, python_dir): @@ -120,6 +113,12 @@ class Pypy2Windows(PyPy2, WindowsSupports): @classmethod def sources(cls, interpreter): - for src in super(Pypy2Windows, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) yield PathRefToDest(Path(interpreter.system_prefix) / "libs", dest=lambda self, s: self.dest / s.name) + + +__all__ = [ + "PyPy2", + "PyPy2Posix", + "Pypy2Windows", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py index 06e15a5..ca5778c 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py @@ -1,25 +1,20 @@ -from __future__ import absolute_import, unicode_literals - import abc - -from six import add_metaclass +from pathlib import Path from virtualenv.create.describe import PosixSupports, Python3Supports, WindowsSupports from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest -from virtualenv.util.path import Path from .common import PyPy -@add_metaclass(abc.ABCMeta) -class PyPy3(PyPy, Python3Supports): +class PyPy3(PyPy, Python3Supports, metaclass=abc.ABCMeta): @classmethod def exe_stem(cls): return "pypy3" @classmethod def exe_names(cls, interpreter): - return super(PyPy3, cls).exe_names(interpreter) | {"pypy"} + return super().exe_names(interpreter) | {"pypy"} class PyPy3Posix(PyPy3, PosixSupports): @@ -28,7 +23,7 @@ class PyPy3Posix(PyPy3, PosixSupports): @property def stdlib(self): """PyPy3 respects sysconfig only for the host python, virtual envs is instead lib/pythonx.y/site-packages""" - return self.dest / "lib" / "pypy{}".format(self.interpreter.version_release_str) / "site-packages" + return self.dest / "lib" / f"pypy{self.interpreter.version_release_str}" / "site-packages" @classmethod def _shared_libs(cls, python_dir): @@ -40,8 +35,7 @@ class PyPy3Posix(PyPy3, PosixSupports): @classmethod def sources(cls, interpreter): - for src in super(PyPy3Posix, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # PyPy >= 3.8 supports a standard prefix installation, where older # versions always used a portable/development style installation. # If this is a standard prefix installation, skip the below: @@ -87,5 +81,11 @@ class Pypy3Windows(PyPy3, WindowsSupports): # glob for libpypy*.dll and libffi*.dll for pattern in ["libpypy*.dll", "libffi*.dll"]: srcs = python_dir.glob(pattern) - for src in srcs: - yield src + yield from srcs + + +__all__ = [ + "PyPy3", + "PyPy3Posix", + "Pypy3Windows", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/python2/python2.py b/src/virtualenv/create/via_global_ref/builtin/python2/python2.py index cacd56e..9b963b3 100644 --- a/src/virtualenv/create/via_global_ref/builtin/python2/python2.py +++ b/src/virtualenv/create/via_global_ref/builtin/python2/python2.py @@ -1,16 +1,11 @@ -from __future__ import absolute_import, unicode_literals - import abc import json import os - -from six import add_metaclass +from pathlib import Path from virtualenv.create.describe import Python2Supports from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest from virtualenv.info import IS_ZIPAPP -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text from virtualenv.util.zipapp import read as read_from_zipapp from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin @@ -18,11 +13,10 @@ from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin HERE = Path(os.path.abspath(__file__)).parent -@add_metaclass(abc.ABCMeta) -class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports): +class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports, metaclass=abc.ABCMeta): def create(self): """Perform operations needed to make the created environment work on Python 2""" - super(Python2, self).create() + super().create() # install a patched site-package, the default Python 2 site.py is not smart enough to understand pyvenv.cfg, # so we inject a small shim that can do this, the location of this depends where it's on host sys_std_plat = Path(self.interpreter.system_stdlib_platform) @@ -38,14 +32,14 @@ class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports): custom_site_text = read_from_zipapp(custom_site) else: custom_site_text = custom_site.read_text() - expected = json.dumps([os.path.relpath(ensure_text(str(i)), ensure_text(str(site_py))) for i in self.libs]) + expected = json.dumps([os.path.relpath(str(i), str(site_py)) for i in self.libs]) custom_site_text = custom_site_text.replace("___EXPECTED_SITE_PACKAGES___", expected) - reload_code = os.linesep.join(" {}".format(i) for i in self.reload_code.splitlines()).lstrip() + reload_code = os.linesep.join(f" {i}" for i in self.reload_code.splitlines()).lstrip() custom_site_text = custom_site_text.replace("# ___RELOAD_CODE___", reload_code) - skip_rewrite = os.linesep.join(" {}".format(i) for i in self.skip_rewrite.splitlines()).lstrip() + skip_rewrite = os.linesep.join(f" {i}" for i in self.skip_rewrite.splitlines()).lstrip() custom_site_text = custom_site_text.replace("# ___SKIP_REWRITE____", skip_rewrite) site_py.write_text(custom_site_text) @@ -60,8 +54,7 @@ class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports): @classmethod def sources(cls, interpreter): - for src in super(Python2, cls).sources(interpreter): - yield src + yield from super().sources(interpreter) # install files needed to run site.py, either from stdlib or stdlib_platform, at least pyc, but both if exists # if neither exists return the module file to trigger failure mappings, needs_py_module = ( @@ -69,8 +62,8 @@ class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports): cls.needs_stdlib_py_module(), ) for req in cls.modules(): - module_file, to_module, module_exists = cls.from_stdlib(mappings, "{}.py".format(req)) - compiled_file, to_compiled, compiled_exists = cls.from_stdlib(mappings, "{}.pyc".format(req)) + module_file, to_module, module_exists = cls.from_stdlib(mappings, f"{req}.py") + compiled_file, to_compiled, compiled_exists = cls.from_stdlib(mappings, f"{req}.pyc") if needs_py_module or module_exists or not compiled_exists: yield PathRefToDest(module_file, dest=to_module) if compiled_exists: diff --git a/src/virtualenv/create/via_global_ref/builtin/ref.py b/src/virtualenv/create/via_global_ref/builtin/ref.py index 69f243b..fb1cf2e 100644 --- a/src/virtualenv/create/via_global_ref/builtin/ref.py +++ b/src/virtualenv/create/via_global_ref/builtin/ref.py @@ -3,34 +3,29 @@ Virtual environments in the traditional sense are built as reference to the host references to elements on the file system, allowing our system to automatically detect what modes it can support given the constraints: e.g. can the file system symlink, can the files be read, executed, etc. """ -from __future__ import absolute_import, unicode_literals import os from abc import ABCMeta, abstractmethod from collections import OrderedDict from stat import S_IXGRP, S_IXOTH, S_IXUSR -from six import add_metaclass - from virtualenv.info import fs_is_case_sensitive, fs_supports_symlink from virtualenv.util.path import copy, make_exe, symlink -from virtualenv.util.six import ensure_text -class RefMust(object): +class RefMust: NA = "NA" COPY = "copy" SYMLINK = "symlink" -class RefWhen(object): +class RefWhen: ANY = "ANY" COPY = "copy" SYMLINK = "symlink" -@add_metaclass(ABCMeta) -class PathRef(object): +class PathRef(metaclass=ABCMeta): """Base class that checks if a file reference can be symlink/copied""" FS_SUPPORTS_SYMLINK = fs_supports_symlink() @@ -49,7 +44,7 @@ class PathRef(object): self._can_symlink = None if self.exists else False def __repr__(self): - return "{}(src={})".format(self.__class__.__name__, self.src) + return f"{self.__class__.__name__}(src={self.src})" @property def can_read(self): @@ -61,7 +56,7 @@ class PathRef(object): except OSError: self._can_read = False else: - self._can_read = os.access(ensure_text(str(self.src)), os.R_OK) + self._can_read = os.access(str(self.src), os.R_OK) return self._can_read @property @@ -94,12 +89,11 @@ class PathRef(object): return symlink if symlinks else copy -@add_metaclass(ABCMeta) -class ExePathRef(PathRef): +class ExePathRef(PathRef, metaclass=ABCMeta): """Base class that checks if a executable can be references via symlink/copy""" def __init__(self, src, must=RefMust.NA, when=RefWhen.ANY): - super(ExePathRef, self).__init__(src, must, when) + super().__init__(src, must, when) self._can_run = None @property @@ -125,7 +119,7 @@ class PathRefToDest(PathRef): """Link a path on the file system""" def __init__(self, src, dest, must=RefMust.NA, when=RefWhen.ANY): - super(PathRefToDest, self).__init__(src, must, when) + super().__init__(src, must, when) self.dest = dest def run(self, creator, symlinks): @@ -169,4 +163,14 @@ class ExePathRefToDest(PathRefToDest, ExePathRef): make_exe(link_file) def __repr__(self): - return "{}(src={}, alias={})".format(self.__class__.__name__, self.src, self.aliases) + return f"{self.__class__.__name__}(src={self.src}, alias={self.aliases})" + + +__all__ = [ + "ExePathRef", + "ExePathRefToDest", + "PathRefToDest", + "PathRef", + "RefWhen", + "RefMust", +] diff --git a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py index 863ae16..2d491cc 100644 --- a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py +++ b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta -from six import add_metaclass - from virtualenv.create.via_global_ref.builtin.ref import ExePathRefToDest, RefMust, RefWhen from virtualenv.util.path import ensure_dir @@ -13,19 +9,18 @@ from .builtin_way import VirtualenvBuiltin class BuiltinViaGlobalRefMeta(ViaGlobalRefMeta): def __init__(self): - super(BuiltinViaGlobalRefMeta, self).__init__() + super().__init__() self.sources = [] -@add_metaclass(ABCMeta) -class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin): +class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin, metaclass=ABCMeta): def __init__(self, options, interpreter): - super(ViaGlobalRefVirtualenvBuiltin, self).__init__(options, interpreter) + super().__init__(options, interpreter) self._sources = getattr(options.meta, "sources", None) # if we're created as a describer this might be missing @classmethod def can_create(cls, interpreter): - """By default all built-in methods assume that if we can describe it we can create it""" + """By default, all built-in methods assume that if we can describe it we can create it""" # first we must be able to describe it if not cls.can_describe(interpreter): return None @@ -39,11 +34,11 @@ class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin): for src in cls.sources(interpreter): if src.exists: if meta.can_copy and not src.can_copy: - meta.copy_error = "cannot copy {}".format(src) + meta.copy_error = f"cannot copy {src}" if meta.can_symlink and not src.can_symlink: - meta.symlink_error = "cannot symlink {}".format(src) + meta.symlink_error = f"cannot symlink {src}" else: - msg = "missing required file {}".format(src) + msg = f"missing required file {src}" if src.when == RefMust.NA: meta.error = msg elif src.when == RefMust.COPY: @@ -51,10 +46,7 @@ class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin): elif src.when == RefMust.SYMLINK: meta.symlink_error = msg if not meta.can_copy and not meta.can_symlink: - meta.error = "neither copy or symlink supported, copy: {} symlink: {}".format( - meta.copy_error, - meta.symlink_error, - ) + meta.error = f"neither copy or symlink supported, copy: {meta.copy_error} symlink: {meta.symlink_error}" if meta.error: break meta.sources.append(src) @@ -98,7 +90,7 @@ class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin): finally: if true_system_site != self.enable_system_site_package: self.enable_system_site_package = true_system_site - super(ViaGlobalRefVirtualenvBuiltin, self).create() + super().create() def ensure_directories(self): return {self.dest, self.bin_dir, self.script_dir, self.stdlib} | set(self.libs) @@ -108,7 +100,13 @@ class ViaGlobalRefVirtualenvBuiltin(ViaGlobalRefApi, VirtualenvBuiltin): We directly inject the base prefix and base exec prefix to avoid site.py needing to discover these from home (which usually is done within the interpreter itself) """ - super(ViaGlobalRefVirtualenvBuiltin, self).set_pyenv_cfg() + super().set_pyenv_cfg() self.pyenv_cfg["base-prefix"] = self.interpreter.system_prefix self.pyenv_cfg["base-exec-prefix"] = self.interpreter.system_exec_prefix self.pyenv_cfg["base-executable"] = self.interpreter.system_executable + + +__all__ = [ + "BuiltinViaGlobalRefMeta", + "ViaGlobalRefVirtualenvBuiltin", +] diff --git a/src/virtualenv/create/via_global_ref/store.py b/src/virtualenv/create/via_global_ref/store.py index 134a535..a9c559c 100644 --- a/src/virtualenv/create/via_global_ref/store.py +++ b/src/virtualenv/create/via_global_ref/store.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from virtualenv.util.path import Path +from pathlib import Path def handle_store_python(meta, interpreter): @@ -20,7 +18,7 @@ def is_store_python(interpreter): ) -__all__ = ( +__all__ = [ "handle_store_python", "is_store_python", -) +] diff --git a/src/virtualenv/create/via_global_ref/venv.py b/src/virtualenv/create/via_global_ref/venv.py index d9fe9eb..071375a 100644 --- a/src/virtualenv/create/via_global_ref/venv.py +++ b/src/virtualenv/create/via_global_ref/venv.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import logging from copy import copy @@ -16,14 +14,13 @@ from .builtin.pypy.pypy3 import Pypy3Windows class Venv(ViaGlobalRefApi): def __init__(self, options, interpreter): self.describe = options.describe - super(Venv, self).__init__(options, interpreter) - self.can_be_inline = ( - interpreter is PythonInfo.current() and interpreter.executable == interpreter.system_executable - ) + super().__init__(options, interpreter) + current = PythonInfo.current() + self.can_be_inline = interpreter is current and interpreter.executable == interpreter.system_executable self._context = None def _args(self): - return super(Venv, self)._args() + ([("describe", self.describe.__class__.__name__)] if self.describe else []) + return super()._args() + ([("describe", self.describe.__class__.__name__)] if self.describe else []) @classmethod def can_create(cls, interpreter): @@ -41,7 +38,7 @@ class Venv(ViaGlobalRefApi): self.create_via_sub_process() for lib in self.libs: ensure_dir(lib) - super(Venv, self).create() + super().create() self.executables_for_win_pypy_less_v37() def executables_for_win_pypy_less_v37(self): @@ -84,7 +81,7 @@ class Venv(ViaGlobalRefApi): def set_pyenv_cfg(self): # prefer venv options over ours, but keep our extra venv_content = copy(self.pyenv_cfg.refresh()) - super(Venv, self).set_pyenv_cfg() + super().set_pyenv_cfg() self.pyenv_cfg.update(venv_content) def __getattribute__(self, item): @@ -94,3 +91,8 @@ class Venv(ViaGlobalRefApi): if not callable(element) or item in ("script",): return element return object.__getattribute__(self, item) + + +__all__ = [ + "Venv", +] diff --git a/src/virtualenv/discovery/__init__.py b/src/virtualenv/discovery/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/discovery/__init__.py +++ b/src/virtualenv/discovery/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py index 52f7398..40320d3 100644 --- a/src/virtualenv/discovery/builtin.py +++ b/src/virtualenv/discovery/builtin.py @@ -1,11 +1,8 @@ -from __future__ import absolute_import, unicode_literals - import logging import os import sys from virtualenv.info import IS_WIN -from virtualenv.util.six import ensure_str, ensure_text from .discover import Discover from .py_info import PythonInfo @@ -14,7 +11,7 @@ from .py_spec import PythonSpec class Builtin(Discover): def __init__(self, options): - super(Builtin, self).__init__(options) + super().__init__(options) self.python_spec = options.python if options.python else [sys.executable] self.app_data = options.app_data self.try_first_with = options.try_first_with @@ -50,11 +47,8 @@ class Builtin(Discover): return None def __repr__(self): - return ensure_str(self.__unicode__()) - - def __unicode__(self): spec = self.python_spec[0] if len(self.python_spec) == 1 else self.python_spec - return "{} discover of python_spec={!r}".format(self.__class__.__name__, spec) + return f"{self.__class__.__name__} discover of python_spec={spec!r}" def get_interpreter(key, try_first_with, app_data=None, env=None): @@ -110,10 +104,10 @@ def propose_interpreters(spec, try_first_with, app_data, env=None): paths = get_paths(env) tested_exes = set() for pos, path in enumerate(paths): - path = ensure_text(path) - logging.debug(LazyPathDump(pos, path, env)) + path_str = str(path) + logging.debug(LazyPathDump(pos, path_str, env)) for candidate, match in possible_specs(spec): - found = check_path(candidate, path) + found = check_path(candidate, path_str) if found is not None: exe = os.path.abspath(found) if exe not in tested_exes: @@ -124,7 +118,7 @@ def propose_interpreters(spec, try_first_with, app_data, env=None): def get_paths(env): - path = env.get(str("PATH"), None) + path = env.get("PATH", None) if path is None: try: path = os.confstr("CS_PATH") @@ -137,18 +131,15 @@ def get_paths(env): return paths -class LazyPathDump(object): +class LazyPathDump: def __init__(self, pos, path, env): self.pos = pos self.path = path self.env = env def __repr__(self): - return ensure_str(self.__unicode__()) - - def __unicode__(self): - content = "discover PATH[{}]={}".format(self.pos, self.path) - if self.env.get(str("_VIRTUALENV_DEBUG")): # this is the over the board debug + content = f"discover PATH[{self.pos}]={self.path}" + if self.env.get("_VIRTUALENV_DEBUG"): # this is the over the board debug content += " with =>" for file_name in os.listdir(self.path): try: @@ -178,9 +169,15 @@ def possible_specs(spec): # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts yield spec.str_spec, False # 5. or from the spec we can deduce a name on path that matches - for exe, match in spec.generate_names(): - yield exe, match + yield from spec.generate_names() class PathPythonInfo(PythonInfo): - """ """ + """python info from path""" + + +__all__ = [ + "get_interpreter", + "Builtin", + "PathPythonInfo", +] diff --git a/src/virtualenv/discovery/cached_py_info.py b/src/virtualenv/discovery/cached_py_info.py index 7eb4161..22ad249 100644 --- a/src/virtualenv/discovery/cached_py_info.py +++ b/src/virtualenv/discovery/cached_py_info.py @@ -4,26 +4,20 @@ We acquire the python information by running an interrogation script via subproc cheap, especially not on Windows. To not have to pay this hefty cost every time we apply multiple levels of caching. """ -from __future__ import absolute_import, unicode_literals import logging import os import random import sys from collections import OrderedDict +from pathlib import Path +from shlex import quote from string import ascii_lowercase, ascii_uppercase, digits +from subprocess import Popen from virtualenv.app_data import AppDataDisabled from virtualenv.discovery.py_info import PythonInfo -from virtualenv.info import PY2 -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_text -from virtualenv.util.subprocess import Popen, subprocess - -if PY2: - from pipes import quote -else: - from shlex import quote +from virtualenv.util.subprocess import subprocess _CACHE = OrderedDict() _CACHE[Path(sys.executable)] = PythonInfo() @@ -36,7 +30,7 @@ def from_exe(cls, app_data, exe, env=None, raise_on_error=True, ignore_cache=Fal if raise_on_error: raise result else: - logging.info("%s", str(result)) + logging.info("%s", result) result = None return result @@ -57,7 +51,7 @@ def _get_from_cache(cls, app_data, exe, env, ignore_cache=True): def _get_via_file_cache(cls, app_data, path, exe, env): - path_text = ensure_text(str(path)) + path_text = str(path) try: path_modified = path.stat().st_mtime except OSError: @@ -87,7 +81,7 @@ def _get_via_file_cache(cls, app_data, path, exe, env): return py_info -COOKIE_LENGTH = 32 # type: int +COOKIE_LENGTH: int = 32 def gen_cookie(): @@ -150,46 +144,30 @@ def _run_subprocess(cls, exe, app_data, env): result = cls._from_json(out) result.executable = exe # keep original executable as this may contain initialization code else: - msg = "failed to query {} with code {}{}{}".format( - exe, - code, - " out: {!r}".format(out) if out else "", - " err: {!r}".format(err) if err else "", - ) - failure = RuntimeError(msg) + msg = f"{exe} with code {code}{f' out: {out!r}' if out else ''}{f' err: {err!r}' if err else ''}" + failure = RuntimeError(f"failed to query {msg}") return failure, result -class LogCmd(object): +class LogCmd: def __init__(self, cmd, env=None): self.cmd = cmd self.env = env def __repr__(self): - def e(v): - return v.decode("utf-8") if isinstance(v, bytes) else v - - cmd_repr = e(" ").join(quote(e(c)) for c in self.cmd) + cmd_repr = " ".join(quote(str(c)) for c in self.cmd) if self.env is not None: - cmd_repr += e(" env of {!r}").format(self.env) - if PY2: - return cmd_repr.encode("utf-8") + cmd_repr = f"{cmd_repr} env of {self.env!r}" return cmd_repr - def __unicode__(self): - raw = repr(self) - if PY2: - return raw.decode("utf-8") - return raw - def clear(app_data): app_data.py_info_clear() _CACHE.clear() -___all___ = ( +___all___ = [ "from_exe", "clear", "LogCmd", -) +] diff --git a/src/virtualenv/discovery/discover.py b/src/virtualenv/discovery/discover.py index 72748c3..8950a55 100644 --- a/src/virtualenv/discovery/discover.py +++ b/src/virtualenv/discovery/discover.py @@ -1,12 +1,7 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta, abstractmethod -from six import add_metaclass - -@add_metaclass(ABCMeta) -class Discover(object): +class Discover(metaclass=ABCMeta): """Discover and provide the requested Python interpreter""" @classmethod @@ -17,7 +12,6 @@ class Discover(object): """ raise NotImplementedError - # noinspection PyUnusedLocal def __init__(self, options): """Create a new discovery mechanism. @@ -31,7 +25,6 @@ class Discover(object): def run(self): """Discovers an interpreter. - :return: the interpreter ready to use for virtual environment creation """ raise NotImplementedError @@ -45,3 +38,8 @@ class Discover(object): self._interpreter = self.run() self._has_run = True return self._interpreter + + +__all__ = [ + "Discover", +] diff --git a/src/virtualenv/discovery/py_spec.py b/src/virtualenv/discovery/py_spec.py index cb63e15..058d8be 100644 --- a/src/virtualenv/discovery/py_spec.py +++ b/src/virtualenv/discovery/py_spec.py @@ -1,19 +1,15 @@ -"""A Python specification is an abstract requirement definition of a interpreter""" -from __future__ import absolute_import, unicode_literals +"""A Python specification is an abstract requirement definition of an interpreter""" import os import re -import sys from collections import OrderedDict from virtualenv.info import fs_is_case_sensitive -from virtualenv.util.six import ensure_str PATTERN = re.compile(r"^(?P<impl>[a-zA-Z]+)?(?P<version>[0-9.]+)?(?:-(?P<arch>32|64))?$") -IS_WIN = sys.platform == "win32" -class PythonSpec(object): +class PythonSpec: """Contains specification about a Python Interpreter""" def __init__(self, str_spec, implementation, major, minor, micro, architecture, path): @@ -87,7 +83,7 @@ class PythonSpec(object): for impl, match in impls.items(): for at in range(len(version), -1, -1): cur_ver = version[0:at] - spec = "{}{}".format(impl, ".".join(str(i) for i in cur_ver)) + spec = f"{impl}{'.'.join(str(i) for i in cur_ver)}" yield spec, match @property @@ -108,15 +104,12 @@ class PythonSpec(object): return False return True - def __unicode__(self): - return "{}({})".format( - type(self).__name__, - ", ".join( - "{}={}".format(k, getattr(self, k)) - for k in ("implementation", "major", "minor", "micro", "architecture", "path") - if getattr(self, k) is not None - ), - ) - def __repr__(self): - return ensure_str(self.__unicode__()) + name = type(self).__name__ + params = "implementation", "major", "minor", "micro", "architecture", "path" + return f"{name}({', '.join(f'{k}={getattr(self, k)}' for k in params if getattr(self, k) is not None)})" + + +__all__ = [ + "PythonSpec", +] diff --git a/src/virtualenv/discovery/windows/__init__.py b/src/virtualenv/discovery/windows/__init__.py index 259be97..4663a3a 100644 --- a/src/virtualenv/discovery/windows/__init__.py +++ b/src/virtualenv/discovery/windows/__init__.py @@ -1,12 +1,10 @@ -from __future__ import absolute_import, unicode_literals - from ..py_info import PythonInfo from ..py_spec import PythonSpec from .pep514 import discover_pythons class Pep514PythonInfo(PythonInfo): - """ """ + """A Python information acquired from PEP-514""" def propose_interpreters(spec, cache_dir, env): @@ -29,3 +27,9 @@ def propose_interpreters(spec, cache_dir, env): if interpreter is not None: if interpreter.satisfies(spec, impl_must_match=True): yield interpreter + + +__all__ = [ + "Pep514PythonInfo", + "propose_interpreters", +] diff --git a/src/virtualenv/discovery/windows/pep514.py b/src/virtualenv/discovery/windows/pep514.py index 048436a..beb1d81 100644 --- a/src/virtualenv/discovery/windows/pep514.py +++ b/src/virtualenv/discovery/windows/pep514.py @@ -1,18 +1,10 @@ """Implement https://www.python.org/dev/peps/pep-0514/ to discover interpreters - Windows only""" -from __future__ import absolute_import, print_function, unicode_literals import os import re +import winreg from logging import basicConfig, getLogger -import six - -if six.PY3: - import winreg -else: - # noinspection PyUnresolvedReferences - import _winreg as winreg - LOGGER = getLogger(__name__) @@ -39,8 +31,7 @@ def discover_pythons(): (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64), (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32), ]: - for spec in process_set(hive, hive_name, key, flags, default_arch): - yield spec + yield from process_set(hive, hive_name, key, flags, default_arch) def process_set(hive, hive_name, key, flags, default_arch): @@ -49,8 +40,7 @@ def process_set(hive, hive_name, key, flags, default_arch): for company in enum_keys(root_key): if company == "PyLauncher": # reserved continue - for spec in process_company(hive_name, company, root_key, default_arch): - yield spec + yield from process_company(hive_name, company, root_key, default_arch) except OSError: pass @@ -77,9 +67,9 @@ def process_tag(hive_name, company, company_key, tag, default_arch): def load_exe(hive_name, company, company_key, tag): - key_path = "{}/{}/{}".format(hive_name, company, tag) + key_path = f"{hive_name}/{company}/{tag}" try: - with winreg.OpenKeyEx(company_key, r"{}\InstallPath".format(tag)) as ip_key: + with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key: with ip_key: exe = get_value(ip_key, "ExecutablePath") if exe is None: @@ -88,21 +78,21 @@ def load_exe(hive_name, company, company_key, tag): msg(key_path, "no ExecutablePath or default for it") else: - exe = os.path.join(ip, str("python.exe")) + exe = os.path.join(ip, "python.exe") if exe is not None and os.path.exists(exe): args = get_value(ip_key, "ExecutableArguments") return exe, args else: - msg(key_path, "could not load exe with value {}".format(exe)) + msg(key_path, f"could not load exe with value {exe}") except OSError: - msg("{}/{}".format(key_path, "InstallPath"), "missing") + msg(f"{key_path}/InstallPath", "missing") return None def load_arch_data(hive_name, company, tag, tag_key, default_arch): arch_str = get_value(tag_key, "SysArchitecture") if arch_str is not None: - key_path = "{}/{}/{}/SysArchitecture".format(hive_name, company, tag) + key_path = f"{hive_name}/{company}/{tag}/SysArchitecture" try: return parse_arch(arch_str) except ValueError as sys_arch: @@ -111,20 +101,20 @@ def load_arch_data(hive_name, company, tag, tag_key, default_arch): def parse_arch(arch_str): - if isinstance(arch_str, six.string_types): + if isinstance(arch_str, str): match = re.match(r"^(\d+)bit$", arch_str) if match: return int(next(iter(match.groups()))) - error = "invalid format {}".format(arch_str) + error = f"invalid format {arch_str}" else: - error = "arch is not string: {}".format(repr(arch_str)) + error = f"arch is not string: {repr(arch_str)}" raise ValueError(error) def load_version_data(hive_name, company, tag, tag_key): for candidate, key_path in [ - (get_value(tag_key, "SysVersion"), "{}/{}/{}/SysVersion".format(hive_name, company, tag)), - (tag, "{}/{}/{}".format(hive_name, company, tag)), + (get_value(tag_key, "SysVersion"), f"{hive_name}/{company}/{tag}/SysVersion"), + (tag, f"{hive_name}/{company}/{tag}"), ]: if candidate is not None: try: @@ -135,18 +125,18 @@ def load_version_data(hive_name, company, tag, tag_key): def parse_version(version_str): - if isinstance(version_str, six.string_types): + if isinstance(version_str, str): match = re.match(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$", version_str) if match: return tuple(int(i) if i is not None else None for i in match.groups()) - error = "invalid format {}".format(version_str) + error = f"invalid format {version_str}" else: - error = "version is not string: {}".format(repr(version_str)) + error = f"version is not string: {version_str!r}" raise ValueError(error) def msg(path, what): - LOGGER.warning("PEP-514 violation in Windows Registry at {} error: {}".format(path, what)) + LOGGER.warning(f"PEP-514 violation in Windows Registry at {path} error: {what}") def _run(): diff --git a/src/virtualenv/info.py b/src/virtualenv/info.py index 7d5e86d..a4fc4bf 100644 --- a/src/virtualenv/info.py +++ b/src/virtualenv/info.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import logging import os import platform @@ -9,13 +7,10 @@ import tempfile IMPLEMENTATION = platform.python_implementation() IS_PYPY = IMPLEMENTATION == "PyPy" IS_CPYTHON = IMPLEMENTATION == "CPython" -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 IS_WIN = sys.platform == "win32" IS_MAC_ARM64 = sys.platform == "darwin" and platform.machine() == "arm64" ROOT = os.path.realpath(os.path.join(os.path.abspath(__file__), os.path.pardir, os.path.pardir)) IS_ZIPAPP = os.path.isfile(ROOT) -WIN_CPYTHON_2 = IS_CPYTHON and IS_WIN and PY2 _CAN_SYMLINK = _FS_CASE_SENSITIVE = _CFG_DIR = _DATA_DIR = None @@ -39,7 +34,7 @@ def fs_supports_symlink(): if IS_WIN: with tempfile.NamedTemporaryFile(prefix="TmP") as tmp_file: temp_dir = os.path.dirname(tmp_file.name) - dest = os.path.join(temp_dir, "{}-{}".format(tmp_file.name, "b")) + dest = os.path.join(temp_dir, f"{tmp_file.name}-{'b'}") try: os.symlink(tmp_file.name, dest) can = True @@ -55,12 +50,10 @@ def fs_supports_symlink(): __all__ = ( "IS_PYPY", "IS_CPYTHON", - "PY3", - "PY2", "IS_WIN", "fs_is_case_sensitive", "fs_supports_symlink", "ROOT", "IS_ZIPAPP", - "WIN_CPYTHON_2", + "IS_MAC_ARM64", ) diff --git a/src/virtualenv/report.py b/src/virtualenv/report.py index 2a2954f..0236f21 100644 --- a/src/virtualenv/report.py +++ b/src/virtualenv/report.py @@ -1,10 +1,6 @@ -from __future__ import absolute_import, unicode_literals - import logging import sys -from virtualenv.util.six import ensure_str - LEVELS = { 0: logging.CRITICAL, 1: logging.ERROR, @@ -26,10 +22,10 @@ def setup_report(verbosity, show_pid=False): msg_format = "%(message)s" if level <= logging.DEBUG: locate = "module" - msg_format = "%(relativeCreated)d {} [%(levelname)s %({})s:%(lineno)d]".format(msg_format, locate) + msg_format = f"%(relativeCreated)d {msg_format} [%(levelname)s %({locate})s:%(lineno)d]" if show_pid: - msg_format = "[%(process)d] " + msg_format - formatter = logging.Formatter(ensure_str(msg_format)) + msg_format = f"[%(process)d] {msg_format}" + formatter = logging.Formatter(msg_format) stream_handler = logging.StreamHandler(stream=sys.stdout) stream_handler.setLevel(level) LOGGER.setLevel(logging.NOTSET) @@ -46,8 +42,8 @@ def _clean_handlers(log): log.removeHandler(log_handler) -__all__ = ( +__all__ = [ "LEVELS", "MAX_LEVEL", "setup_report", -) +] diff --git a/src/virtualenv/run/__init__.py b/src/virtualenv/run/__init__.py index e8e7ab1..8f18cd9 100644 --- a/src/virtualenv/run/__init__.py +++ b/src/virtualenv/run/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import logging import os from functools import partial @@ -69,7 +67,7 @@ def build_parser(args=None, options=None, setup_logging=True, env=None): discover = get_discover(parser, args) parser._interpreter = interpreter = discover.interpreter if interpreter is None: - raise RuntimeError("failed to find interpreter for {}".format(discover)) + raise RuntimeError(f"failed to find interpreter for {discover}") elements = [ CreatorSelector(interpreter, parser), SeederSelector(interpreter, parser), @@ -130,13 +128,13 @@ def add_version_flag(parser): parser.add_argument( "--version", action="version", - version="%(prog)s {} from {}".format(__version__, virtualenv.__file__), + version=f"%(prog)s {__version__} from {virtualenv.__file__}", help="display the version of the virtualenv package and its location, then exit", ) def _do_report_setup(parser, args, setup_logging): - level_map = ", ".join("{}={}".format(logging.getLevelName(l), c) for c, l in sorted(list(LEVELS.items()))) + level_map = ", ".join(f"{logging.getLevelName(l)}={c}" for c, l in sorted(list(LEVELS.items()))) msg = "verbosity = verbose - quiet, default {}, mapping => {}" verbosity_group = parser.add_argument_group( title="verbosity", @@ -150,7 +148,7 @@ def _do_report_setup(parser, args, setup_logging): setup_report(option.verbosity) -__all__ = ( +__all__ = [ "cli_run", "session_via_cli", -) +] diff --git a/src/virtualenv/run/plugin/activators.py b/src/virtualenv/run/plugin/activators.py index 8180981..320cae7 100644 --- a/src/virtualenv/run/plugin/activators.py +++ b/src/virtualenv/run/plugin/activators.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from argparse import ArgumentTypeError from collections import OrderedDict @@ -12,14 +10,14 @@ class ActivationSelector(ComponentBuilder): possible = OrderedDict( (k, v) for k, v in self.options("virtualenv.activate").items() if v.supports(interpreter) ) - super(ActivationSelector, self).__init__(interpreter, parser, "activators", possible) + super().__init__(interpreter, parser, "activators", possible) self.parser.description = "options for activation scripts" self.active = None def add_selector_arg_parse(self, name, choices): self.default = ",".join(choices) self.parser.add_argument( - "--{}".format(name), + f"--{name}", default=self.default, metavar="comma_sep_list", required=False, @@ -31,7 +29,7 @@ class ActivationSelector(ComponentBuilder): elements = [e.strip() for e in entered_str.split(",") if e.strip()] missing = [e for e in elements if e not in self.possible] if missing: - raise ArgumentTypeError("the following activators are not available {}".format(",".join(missing))) + raise ArgumentTypeError(f"the following activators are not available {','.join(missing)}") return elements def handle_selected_arg_parse(self, options): @@ -54,3 +52,8 @@ class ActivationSelector(ComponentBuilder): def create(self, options): return [activator_class(options) for activator_class in self.active.values()] + + +__all__ = [ + "ActivationSelector", +] diff --git a/src/virtualenv/run/plugin/base.py b/src/virtualenv/run/plugin/base.py index 048c76a..5fbf81b 100644 --- a/src/virtualenv/run/plugin/base.py +++ b/src/virtualenv/run/plugin/base.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import sys from collections import OrderedDict @@ -13,7 +11,7 @@ else: importlib_metadata_version = tuple(int(i) for i in version("importlib_metadata").split(".")[:2]) -class PluginLoader(object): +class PluginLoader: _OPTIONS = None _ENTRY_POINTS = None @@ -52,14 +50,20 @@ class ComponentBuilder(PluginLoader): def handle_selected_arg_parse(self, options): selected = getattr(options, self.name) if selected not in self.possible: - raise RuntimeError("No implementation for {}".format(self.interpreter)) + raise RuntimeError(f"No implementation for {self.interpreter}") self._impl_class = self.possible[selected] self.populate_selected_argparse(selected, options.app_data) return selected def populate_selected_argparse(self, selected, app_data): - self.parser.description = "options for {} {}".format(self.name, selected) + self.parser.description = f"options for {self.name} {selected}" self._impl_class.add_parser_arguments(self.parser, self.interpreter, app_data) def create(self, options): return self._impl_class(options, self.interpreter) + + +__all__ = [ + "PluginLoader", + "ComponentBuilder", +] diff --git a/src/virtualenv/run/plugin/creators.py b/src/virtualenv/run/plugin/creators.py index ef4177a..8953064 100644 --- a/src/virtualenv/run/plugin/creators.py +++ b/src/virtualenv/run/plugin/creators.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from collections import OrderedDict, defaultdict, namedtuple from virtualenv.create.describe import Describe @@ -13,7 +11,7 @@ CreatorInfo = namedtuple("CreatorInfo", ["key_to_class", "key_to_meta", "describ class CreatorSelector(ComponentBuilder): def __init__(self, interpreter, parser): creators, self.key_to_meta, self.describe, self.builtin_key = self.for_interpreter(interpreter) - super(CreatorSelector, self).__init__(interpreter, parser, "creator", creators) + super().__init__(interpreter, parser, "creator", creators) @classmethod def for_interpreter(cls, interpreter): @@ -37,10 +35,10 @@ class CreatorSelector(ComponentBuilder): describe = creator_class if not key_to_meta: if errors: - rows = ["{} for creators {}".format(k, ", ".join(i.__name__ for i in v)) for k, v in errors.items()] + rows = [f"{k} for creators {', '.join(i.__name__ for i in v)}" for k, v in errors.items()] raise RuntimeError("\n".join(rows)) else: - raise RuntimeError("No virtualenv implementation for {}".format(interpreter)) + raise RuntimeError(f"No virtualenv implementation for {interpreter}") return CreatorInfo( key_to_class=key_to_class, key_to_meta=key_to_meta, @@ -53,13 +51,11 @@ class CreatorSelector(ComponentBuilder): choices = sorted(choices, key=lambda a: 0 if a == "builtin" else 1) default_value = self._get_default(choices) self.parser.add_argument( - "--{}".format(name), + f"--{name}", choices=choices, default=default_value, required=False, - help="create environment via{}".format( - "" if self.builtin_key is None else " (builtin = {})".format(self.builtin_key), - ), + help=f"create environment via{'' if self.builtin_key is None else f' (builtin = {self.builtin_key})'}", ) @staticmethod @@ -67,11 +63,17 @@ class CreatorSelector(ComponentBuilder): return next(iter(choices)) def populate_selected_argparse(self, selected, app_data): - self.parser.description = "options for {} {}".format(self.name, selected) + self.parser.description = f"options for {self.name} {selected}" self._impl_class.add_parser_arguments(self.parser, self.interpreter, self.key_to_meta[selected], app_data) def create(self, options): options.meta = self.key_to_meta[getattr(options, self.name)] if not issubclass(self._impl_class, Describe): options.describe = self.describe(options, self.interpreter) - return super(CreatorSelector, self).create(options) + return super().create(options) + + +__all__ = [ + "CreatorSelector", + "CreatorInfo", +] diff --git a/src/virtualenv/run/plugin/discovery.py b/src/virtualenv/run/plugin/discovery.py index ac9b7f5..13f39ed 100644 --- a/src/virtualenv/run/plugin/discovery.py +++ b/src/virtualenv/run/plugin/discovery.py @@ -1,10 +1,8 @@ -from __future__ import absolute_import, unicode_literals - from .base import PluginLoader class Discovery(PluginLoader): - """ """ + """Discovery plugins""" def get_discover(parser, args): @@ -33,3 +31,9 @@ def get_discover(parser, args): def _get_default_discovery(discover_types): return list(discover_types.keys()) + + +__all__ = [ + "get_discover", + "Discovery", +] diff --git a/src/virtualenv/run/plugin/seeders.py b/src/virtualenv/run/plugin/seeders.py index d182c6f..1a51d2e 100644 --- a/src/virtualenv/run/plugin/seeders.py +++ b/src/virtualenv/run/plugin/seeders.py @@ -1,16 +1,14 @@ -from __future__ import absolute_import, unicode_literals - from .base import ComponentBuilder class SeederSelector(ComponentBuilder): def __init__(self, interpreter, parser): possible = self.options("virtualenv.seed") - super(SeederSelector, self).__init__(interpreter, parser, "seeder", possible) + super().__init__(interpreter, parser, "seeder", possible) def add_selector_arg_parse(self, name, choices): self.parser.add_argument( - "--{}".format(name), + f"--{name}", choices=choices, default=self._get_default(), required=False, @@ -29,7 +27,12 @@ class SeederSelector(ComponentBuilder): return "app-data" def handle_selected_arg_parse(self, options): - return super(SeederSelector, self).handle_selected_arg_parse(options) + return super().handle_selected_arg_parse(options) def create(self, options): return self._impl_class(options) + + +__all__ = [ + "SeederSelector", +] diff --git a/src/virtualenv/run/session.py b/src/virtualenv/run/session.py index 24836d2..84777fe 100644 --- a/src/virtualenv/run/session.py +++ b/src/virtualenv/run/session.py @@ -1,12 +1,8 @@ -from __future__ import absolute_import, unicode_literals - import json import logging -from virtualenv.util.six import ensure_text - -class Session(object): +class Session: """Represents a virtual environment creation session""" def __init__(self, verbosity, app_data, interpreter, creator, seeder, activators): @@ -49,7 +45,7 @@ class Session(object): self.creator.pyenv_cfg.write() def _create(self): - logging.info("create virtual environment via %s", ensure_text(str(self.creator))) + logging.info("create virtual environment via %s", self.creator) self.creator.run() logging.debug(_DEBUG_MARKER) logging.debug("%s", _Debug(self.creator)) @@ -61,10 +57,8 @@ class Session(object): def _activate(self): if self.activators: - logging.info( - "add activators for %s", - ", ".join(type(i).__name__.replace("Activator", "") for i in self.activators), - ) + active = ", ".join(type(i).__name__.replace("Activator", "") for i in self.activators) + logging.info("add activators for %s", active) for activator in self.activators: activator.generate(self.creator) @@ -78,14 +72,16 @@ class Session(object): _DEBUG_MARKER = "=" * 30 + " target debug " + "=" * 30 -class _Debug(object): +class _Debug: """lazily populate debug""" def __init__(self, creator): self.creator = creator - def __unicode__(self): - return ensure_text(repr(self)) - def __repr__(self): return json.dumps(self.creator.debug, indent=2) + + +__all__ = [ + "Session", +] diff --git a/src/virtualenv/seed/__init__.py b/src/virtualenv/seed/__init__.py index 01e6d4f..e69de29 100644 --- a/src/virtualenv/seed/__init__.py +++ b/src/virtualenv/seed/__init__.py @@ -1 +0,0 @@ -from __future__ import absolute_import, unicode_literals diff --git a/src/virtualenv/seed/embed/base_embed.py b/src/virtualenv/seed/embed/base_embed.py index c794e83..b0bcbc3 100644 --- a/src/virtualenv/seed/embed/base_embed.py +++ b/src/virtualenv/seed/embed/base_embed.py @@ -1,11 +1,5 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta - -from six import add_metaclass - -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_str, ensure_text +from pathlib import Path from ..seeder import Seeder from ..wheels import Version @@ -13,10 +7,9 @@ from ..wheels import Version PERIODIC_UPDATE_ON_BY_DEFAULT = True -@add_metaclass(ABCMeta) -class BaseEmbed(Seeder): +class BaseEmbed(Seeder, metaclass=ABCMeta): def __init__(self, options): - super(BaseEmbed, self).__init__(options, enabled=options.no_seed is False) + super().__init__(options, enabled=options.no_seed is False) self.download = options.download self.extra_search_dir = [i.resolve() for i in options.extra_search_dir if i.exists()] @@ -44,9 +37,9 @@ class BaseEmbed(Seeder): def distribution_to_versions(self): return { - distribution: getattr(self, "{}_version".format(distribution)) + distribution: getattr(self, f"{distribution}_version") for distribution in self.distributions() - if getattr(self, "no_{}".format(distribution)) is False + if getattr(self, f"no_{distribution}") is False } @classmethod @@ -57,14 +50,14 @@ class BaseEmbed(Seeder): "--never-download", dest="download", action="store_false", - help="pass to disable download of the latest {} from PyPI".format("/".join(cls.distributions())), + help=f"pass to disable download of the latest {'/'.join(cls.distributions())} from PyPI", default=True, ) group.add_argument( "--download", dest="download", action="store_true", - help="pass to enable download of the latest {} from PyPI".format("/".join(cls.distributions())), + help=f"pass to enable download of the latest {'/'.join(cls.distributions())} from PyPI", default=False, ) parser.add_argument( @@ -77,18 +70,18 @@ class BaseEmbed(Seeder): ) for distribution, default in cls.distributions().items(): parser.add_argument( - "--{}".format(distribution), + f"--{distribution}", dest=distribution, metavar="version", - help="version of {} to install as seed: embed, bundle or exact version".format(distribution), + help=f"version of {distribution} to install as seed: embed, bundle or exact version", default=default, ) for distribution in cls.distributions(): parser.add_argument( - "--no-{}".format(distribution), - dest="no_{}".format(distribution), + f"--no-{distribution}", + dest=f"no_{distribution}", action="store_true", - help="do not install {}".format(distribution), + help=f"do not install {distribution}", default=False, ) parser.add_argument( @@ -99,20 +92,20 @@ class BaseEmbed(Seeder): default=not PERIODIC_UPDATE_ON_BY_DEFAULT, ) - def __unicode__(self): + def __repr__(self): result = self.__class__.__name__ result += "(" if self.extra_search_dir: - result += "extra_search_dir={},".format(", ".join(ensure_text(str(i)) for i in self.extra_search_dir)) - result += "download={},".format(self.download) + result += f"extra_search_dir={', '.join(str(i) for i in self.extra_search_dir)}," + result += f"download={self.download}," for distribution in self.distributions(): - if getattr(self, "no_{}".format(distribution)): + if getattr(self, f"no_{distribution}"): continue - result += " {}{},".format( - distribution, - "={}".format(getattr(self, "{}_version".format(distribution), None) or "latest"), - ) + ver = f"={getattr(self, f'{distribution}_version', None) or 'latest'}" + result += f" {distribution}{ver}," return result[:-1] + ")" - def __repr__(self): - return ensure_str(self.__unicode__()) + +__all__ = [ + "BaseEmbed", +] diff --git a/src/virtualenv/seed/embed/pip_invoke.py b/src/virtualenv/seed/embed/pip_invoke.py index c935c02..2ca9438 100644 --- a/src/virtualenv/seed/embed/pip_invoke.py +++ b/src/virtualenv/seed/embed/pip_invoke.py @@ -1,18 +1,16 @@ -from __future__ import absolute_import, unicode_literals - import logging from contextlib import contextmanager +from subprocess import Popen from virtualenv.discovery.cached_py_info import LogCmd from virtualenv.seed.embed.base_embed import BaseEmbed -from virtualenv.util.subprocess import Popen from ..wheels import Version, get_wheel, pip_wheel_env_run class PipInvoke(BaseEmbed): def __init__(self, options): - super(PipInvoke, self).__init__(options) + super().__init__(options) def run(self, creator): if not self.enabled: @@ -28,7 +26,7 @@ class PipInvoke(BaseEmbed): process = Popen(cmd, env=env) process.communicate() if process.returncode != 0: - raise RuntimeError("failed seed with code {}".format(process.returncode)) + raise RuntimeError(f"failed seed with code {process.returncode}") return process @contextmanager @@ -49,9 +47,14 @@ class PipInvoke(BaseEmbed): env=self.env, ) if wheel is None: - raise RuntimeError("could not get wheel for distribution {}".format(dist)) + raise RuntimeError(f"could not get wheel for distribution {dist}") folders.add(str(wheel.path.parent)) cmd.append(Version.as_pip_req(dist, wheel.version)) for folder in sorted(folders): cmd.extend(["--find-links", str(folder)]) yield cmd + + +__all__ = [ + "PipInvoke", +] diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py index 35e0cca..3c44b77 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py @@ -1,23 +1,19 @@ -from __future__ import absolute_import, unicode_literals - import logging import os import re import zipfile from abc import ABCMeta, abstractmethod +from configparser import ConfigParser from itertools import chain +from pathlib import Path from tempfile import mkdtemp from distlib.scripts import ScriptMaker, enquote_executable -from six import PY3, add_metaclass -from virtualenv.util import ConfigParser -from virtualenv.util.path import Path, safe_delete -from virtualenv.util.six import ensure_text +from virtualenv.util.path import safe_delete -@add_metaclass(ABCMeta) -class PipInstall(object): +class PipInstall(metaclass=ABCMeta): def __init__(self, wheel, creator, image_folder): self._wheel = wheel self._creator = creator @@ -71,10 +67,7 @@ class PipInstall(object): self._image_dir = Path(to_folder) def _records_text(self, files): - record_data = "\n".join( - "{},,".format(os.path.relpath(ensure_text(str(rec)), ensure_text(str(self._image_dir)))) for rec in files - ) - return record_data + return "\n".join(f"{os.path.relpath(str(rec), str(self._image_dir))},," for rec in files) def _generate_new_files(self): new_files = set() @@ -82,17 +75,17 @@ class PipInstall(object): installer.write_text("pip\n") new_files.add(installer) # inject a no-op root element, as workaround for bug in https://github.com/pypa/pip/issues/7226 - marker = self._image_dir / "{}.virtualenv".format(self._dist_info.stem) + marker = self._image_dir / f"{self._dist_info.stem}.virtualenv" marker.write_text("") new_files.add(marker) folder = mkdtemp() try: to_folder = Path(folder) - rel = os.path.relpath(ensure_text(str(self._creator.script_dir)), ensure_text(str(self._creator.purelib))) + rel = os.path.relpath(str(self._creator.script_dir), str(self._creator.purelib)) version_info = self._creator.interpreter.version_info for name, module in self._console_scripts.items(): new_files.update( - Path(os.path.normpath(ensure_text(str(self._image_dir / rel / i.name)))) + Path(os.path.normpath(str(self._image_dir / rel / i.name))) for i in self._create_console_entry_point(name, module, to_folder, version_info) ) finally: @@ -111,8 +104,7 @@ class PipInstall(object): self.__dist_info = filename break else: - msg = "no .dist-info at {}, has {}".format(self._image_dir, ", ".join(files)) # pragma: no cover - raise RuntimeError(msg) # pragma: no cover + raise RuntimeError(f"no .dist-info at {self._image_dir}, has {', '.join(files)}") # pragma: no cover return self.__dist_info @abstractmethod @@ -127,10 +119,9 @@ class PipInstall(object): self._console_entry_points = {} entry_points = self._dist_info / "entry_points.txt" if entry_points.exists(): - parser = ConfigParser.ConfigParser() + parser = ConfigParser() with entry_points.open() as file_handler: - reader = getattr(parser, "read_file" if PY3 else "readfp") - reader(file_handler) + parser.read_file(file_handler) if "console_scripts" in parser.sections(): for name, value in parser.items("console_scripts"): match = re.match(r"(.*?)-?\d\.?\d*", name) @@ -142,7 +133,7 @@ class PipInstall(object): def _create_console_entry_point(self, name, value, to_folder, version_info): result = [] maker = ScriptMakerCustom(to_folder, version_info, self._creator.exe, name) - specification = "{} = {}".format(name, value) + specification = f"{name} = {value}" new_files = maker.make(specification) result.extend(Path(i) for i in new_files) return result @@ -187,7 +178,7 @@ class PipInstall(object): class ScriptMakerCustom(ScriptMaker): def __init__(self, target_dir, version_info, executable, name): - super(ScriptMakerCustom, self).__init__(None, str(target_dir)) + super().__init__(None, str(target_dir)) self.clobber = True # overwrite self.set_mode = True # ensure they are executable self.executable = enquote_executable(str(executable)) @@ -196,5 +187,10 @@ class ScriptMakerCustom(ScriptMaker): self._name = name def _write_script(self, names, shebang, script_bytes, filenames, ext): - names.add("{}{}.{}".format(self._name, *self.version_info)) - super(ScriptMakerCustom, self)._write_script(names, shebang, script_bytes, filenames, ext) + names.add(f"{self._name}{self.version_info[0]}.{self.version_info[1]}") + super()._write_script(names, shebang, script_bytes, filenames, ext) + + +__all__ = [ + "PipInstall", +] diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py b/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py index 29d0bc8..f5717e1 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py @@ -1,9 +1,7 @@ -from __future__ import absolute_import, unicode_literals - import os +from pathlib import Path -from virtualenv.util.path import Path, copy -from virtualenv.util.six import ensure_text +from virtualenv.util.path import copy from .base import PipInstall @@ -14,22 +12,27 @@ class CopyPipInstall(PipInstall): def _generate_new_files(self): # create the pyc files - new_files = super(CopyPipInstall, self)._generate_new_files() + new_files = super()._generate_new_files() new_files.update(self._cache_files()) return new_files def _cache_files(self): version = self._creator.interpreter.version_info - py_c_ext = ".{}-{}{}.pyc".format(self._creator.interpreter.implementation.lower(), version.major, version.minor) - for root, dirs, files in os.walk(ensure_text(str(self._image_dir)), topdown=True): + py_c_ext = f".{self._creator.interpreter.implementation.lower()}-{version.major}{version.minor}.pyc" + for root, dirs, files in os.walk(str(self._image_dir), topdown=True): root_path = Path(root) for name in files: if name.endswith(".py"): - yield root_path / "{}{}".format(name[:-3], py_c_ext) + yield root_path / f"{name[:-3]}{py_c_ext}" for name in dirs: yield root_path / name / "__pycache__" def _fix_records(self, new_files): extra_record_data_str = self._records_text(new_files) - with open(ensure_text(str(self._dist_info / "RECORD")), "ab") as file_handler: + with (self._dist_info / "RECORD").open("ab") as file_handler: file_handler.write(extra_record_data_str.encode("utf-8")) + + +__all__ = [ + "CopyPipInstall", +] diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py b/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py index f958b65..4695de5 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py @@ -1,29 +1,20 @@ -from __future__ import absolute_import, unicode_literals - import os -import subprocess from stat import S_IREAD, S_IRGRP, S_IROTH +from subprocess import PIPE, Popen from virtualenv.util.path import safe_delete, set_tree -from virtualenv.util.six import ensure_text -from virtualenv.util.subprocess import Popen from .base import PipInstall class SymlinkPipInstall(PipInstall): def _sync(self, src, dst): - src_str = ensure_text(str(src)) - dest_str = ensure_text(str(dst)) - os.symlink(src_str, dest_str) + os.symlink(str(src), str(dst)) def _generate_new_files(self): # create the pyc files, as the build image will be R/O - process = Popen( - [ensure_text(str(self._creator.exe)), "-m", "compileall", ensure_text(str(self._image_dir))], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) + cmd = [str(self._creator.exe), "-m", "compileall", str(self._image_dir)] + process = Popen(cmd, stdout=PIPE, stderr=PIPE) process.communicate() # the root pyc is shared, so we'll not symlink that - but still add the pyc files to the RECORD for close root_py_cache = self._image_dir / "__pycache__" @@ -32,7 +23,7 @@ class SymlinkPipInstall(PipInstall): new_files.update(root_py_cache.iterdir()) new_files.add(root_py_cache) safe_delete(root_py_cache) - core_new_files = super(SymlinkPipInstall, self)._generate_new_files() + core_new_files = super()._generate_new_files() # remove files that are within the image folder deeper than one level (as these will be not linked directly) for file in core_new_files: try: @@ -47,15 +38,20 @@ class SymlinkPipInstall(PipInstall): def _fix_records(self, new_files): new_files.update(i for i in self._image_dir.iterdir()) extra_record_data_str = self._records_text(sorted(new_files, key=str)) - with open(ensure_text(str(self._dist_info / "RECORD")), "wb") as file_handler: + with open(str(self._dist_info / "RECORD"), "wb") as file_handler: file_handler.write(extra_record_data_str.encode("utf-8")) def build_image(self): - super(SymlinkPipInstall, self).build_image() + super().build_image() # protect the image by making it read only set_tree(self._image_dir, S_IREAD | S_IRGRP | S_IROTH) def clear(self): if self._image_dir.exists(): safe_delete(self._image_dir) - super(SymlinkPipInstall, self).clear() + super().clear() + + +__all__ = [ + "SymlinkPipInstall", +] diff --git a/src/virtualenv/seed/embed/via_app_data/via_app_data.py b/src/virtualenv/seed/embed/via_app_data/via_app_data.py index 9a98a70..743ed15 100644 --- a/src/virtualenv/seed/embed/via_app_data/via_app_data.py +++ b/src/virtualenv/seed/embed/via_app_data/via_app_data.py @@ -1,17 +1,16 @@ """Bootstrap""" -from __future__ import absolute_import, unicode_literals import logging import sys import traceback from contextlib import contextmanager +from pathlib import Path from subprocess import CalledProcessError from threading import Lock, Thread from virtualenv.info import fs_supports_symlink from virtualenv.seed.embed.base_embed import BaseEmbed from virtualenv.seed.wheels import get_wheel -from virtualenv.util.path import Path from .pip_install.copy import CopyPipInstall from .pip_install.symlink import SymlinkPipInstall @@ -19,20 +18,19 @@ from .pip_install.symlink import SymlinkPipInstall class FromAppData(BaseEmbed): def __init__(self, options): - super(FromAppData, self).__init__(options) + super().__init__(options) self.symlinks = options.symlink_app_data @classmethod def add_parser_arguments(cls, parser, interpreter, app_data): - super(FromAppData, cls).add_parser_arguments(parser, interpreter, app_data) + super().add_parser_arguments(parser, interpreter, app_data) can_symlink = app_data.transient is False and fs_supports_symlink() + sym = "" if can_symlink else "not supported - " parser.add_argument( "--symlink-app-data", dest="symlink_app_data", action="store_true" if can_symlink else "store_false", - help="{} symlink the python packages from the app-data folder (requires seed pip>=19.3)".format( - "" if can_symlink else "not supported - ", - ), + help=f"{sym} symlink the python packages from the app-data folder (requires seed pip>=19.3)", default=False, ) @@ -64,7 +62,7 @@ class FromAppData(BaseEmbed): for thread in threads: thread.join() if exceptions: - messages = ["failed to build image {} because:".format(", ".join(exceptions.keys()))] + messages = [f"failed to build image {', '.join(exceptions.keys())} because:"] for value in exceptions.values(): exc_type, exc_value, exc_traceback = value messages.append("".join(traceback.format_exception(exc_type, exc_value, exc_traceback))) @@ -98,11 +96,11 @@ class FromAppData(BaseEmbed): failure = exception if failure: if isinstance(failure, CalledProcessError): - msg = "failed to download {}".format(distribution) + msg = f"failed to download {distribution}" if version is not None: - msg += " version {}".format(version) - msg += ", pip download exit code {}".format(failure.returncode) - output = failure.output if sys.version_info < (3, 5) else (failure.output + failure.stderr) + msg += f" version {version}" + msg += f", pip download exit code {failure.returncode}" + output = failure.output + failure.stderr if output: msg += "\n" msg += output @@ -124,7 +122,7 @@ class FromAppData(BaseEmbed): for thread in threads: thread.join() if fail: - raise RuntimeError("seed failed due to failing to download wheels {}".format(", ".join(fail.keys()))) + raise RuntimeError(f"seed failed due to failing to download wheels {', '.join(fail.keys())}") yield name_to_whl def installer_class(self, pip_version_tuple): @@ -134,7 +132,12 @@ class FromAppData(BaseEmbed): return SymlinkPipInstall return CopyPipInstall - def __unicode__(self): - base = super(FromAppData, self).__unicode__() - msg = ", via={}, app_data_dir={}".format("symlink" if self.symlinks else "copy", self.app_data) - return base[:-1] + msg + base[-1] + def __repr__(self): + msg = f", via={'symlink' if self.symlinks else 'copy'}, app_data_dir={self.app_data}" + base = super().__repr__() + return f"{base[:-1]}{msg}{base[-1]}" + + +__all__ = [ + "FromAppData", +] diff --git a/src/virtualenv/seed/seeder.py b/src/virtualenv/seed/seeder.py index 852e852..a14e483 100644 --- a/src/virtualenv/seed/seeder.py +++ b/src/virtualenv/seed/seeder.py @@ -1,15 +1,9 @@ -from __future__ import absolute_import, unicode_literals - from abc import ABCMeta, abstractmethod -from six import add_metaclass - -@add_metaclass(ABCMeta) -class Seeder(object): +class Seeder(metaclass=ABCMeta): """A seeder will install some seed packages into a virtual environment.""" - # noinspection PyUnusedLocal def __init__(self, options, enabled): """ @@ -38,3 +32,8 @@ class Seeder(object): virtual environment """ raise NotImplementedError + + +__all__ = [ + "Seeder", +] diff --git a/src/virtualenv/seed/wheels/__init__.py b/src/virtualenv/seed/wheels/__init__.py index dbffe2e..c563181 100644 --- a/src/virtualenv/seed/wheels/__init__.py +++ b/src/virtualenv/seed/wheels/__init__.py @@ -1,11 +1,9 @@ -from __future__ import absolute_import, unicode_literals - from .acquire import get_wheel, pip_wheel_env_run from .util import Version, Wheel -__all__ = ( +__all__ = [ "get_wheel", "pip_wheel_env_run", "Version", "Wheel", -) +] diff --git a/src/virtualenv/seed/wheels/acquire.py b/src/virtualenv/seed/wheels/acquire.py index 3744026..d8e8d1e 100644 --- a/src/virtualenv/seed/wheels/acquire.py +++ b/src/virtualenv/seed/wheels/acquire.py @@ -1,13 +1,10 @@ """Bootstrap""" -from __future__ import absolute_import, unicode_literals import logging import sys from operator import eq, lt - -from virtualenv.util.path import Path -from virtualenv.util.six import ensure_str -from virtualenv.util.subprocess import Popen, subprocess +from pathlib import Path +from subprocess import PIPE, CalledProcessError, Popen from .bundle import from_bundle from .periodic_update import add_wheel_to_update_log @@ -43,7 +40,7 @@ def get_wheel(distribution, version, for_py_version, search_dirs, download, app_ def download_wheel(distribution, version_spec, for_py_version, search_dirs, app_data, to_folder, env): - to_download = "{}{}".format(distribution, version_spec or "") + to_download = f"{distribution}{version_spec or ''}" logging.debug("download wheel %s %s to %s", to_download, for_py_version, to_folder) cmd = [ sys.executable, @@ -63,15 +60,11 @@ def download_wheel(distribution, version_spec, for_py_version, search_dirs, app_ ] # pip has no interface in python - must be a new sub-process env = pip_wheel_env_run(search_dirs, app_data, env) - process = Popen(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + process = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = process.communicate() if process.returncode != 0: - kwargs = {"output": out} - if sys.version_info < (3, 5): - kwargs["output"] += err - else: - kwargs["stderr"] = err - raise subprocess.CalledProcessError(process.returncode, cmd, **kwargs) + kwargs = {"output": out, "stderr": err} + raise CalledProcessError(process.returncode, cmd, **kwargs) result = _find_downloaded_wheel(distribution, version_spec, for_py_version, to_folder, out) logging.debug("downloaded wheel %s", result.name) return result @@ -83,7 +76,7 @@ def _find_downloaded_wheel(distribution, version_spec, for_py_version, to_folder for marker in ("Saved ", "File was already downloaded "): if line.startswith(marker): return Wheel(Path(line[len(marker) :]).absolute()) - # if for some reason the output does not match fallback to latest version with that spec + # if for some reason the output does not match fallback to the latest version with that spec return find_compatible_in_house(distribution, version_spec, for_py_version, to_folder) @@ -104,18 +97,12 @@ def find_compatible_in_house(distribution, version_spec, for_py_version, in_fold def pip_wheel_env_run(search_dirs, app_data, env): - for_py_version = "{}.{}".format(*sys.version_info[0:2]) env = env.copy() - env.update( - { - ensure_str(k): str(v) # python 2 requires these to be string only (non-unicode) - for k, v in {"PIP_USE_WHEEL": "1", "PIP_USER": "0", "PIP_NO_INPUT": "1"}.items() - }, - ) + env.update({"PIP_USE_WHEEL": "1", "PIP_USER": "0", "PIP_NO_INPUT": "1"}) wheel = get_wheel( distribution="pip", version=None, - for_py_version=for_py_version, + for_py_version=f"{sys.version_info.major}.{sys.version_info.minor}", search_dirs=search_dirs, download=False, app_data=app_data, @@ -124,5 +111,12 @@ def pip_wheel_env_run(search_dirs, app_data, env): ) if wheel is None: raise RuntimeError("could not find the embedded pip") - env[str("PYTHONPATH")] = str(wheel.path) + env["PYTHONPATH"] = str(wheel.path) return env + + +__all__ = [ + "get_wheel", + "download_wheel", + "pip_wheel_env_run", +] diff --git a/src/virtualenv/seed/wheels/bundle.py b/src/virtualenv/seed/wheels/bundle.py index 39cd3d3..66bbe56 100644 --- a/src/virtualenv/seed/wheels/bundle.py +++ b/src/virtualenv/seed/wheels/bundle.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from ..wheels.embed import get_embed_wheel from .periodic_update import periodic_update from .util import Version, Wheel, discover_wheels @@ -15,9 +13,8 @@ def from_bundle(distribution, version, for_py_version, search_dirs, app_data, do if version != Version.embed: # 2. check if we have upgraded embed if app_data.can_update: - wheel = periodic_update( - distribution, of_version, for_py_version, wheel, search_dirs, app_data, do_periodic_update, env - ) + per = do_periodic_update + wheel = periodic_update(distribution, of_version, for_py_version, wheel, search_dirs, app_data, per, env) # 3. acquire from extra search dir found_wheel = from_dir(distribution, of_version, for_py_version, search_dirs) @@ -49,3 +46,9 @@ def from_dir(distribution, version, for_py_version, directories): for wheel in discover_wheels(folder, distribution, version, for_py_version): return wheel return None + + +__all__ = [ + "load_embed_wheel", + "from_bundle", +] diff --git a/src/virtualenv/seed/wheels/embed/__init__.py b/src/virtualenv/seed/wheels/embed/__init__.py index 3ae80b5..3062176 100644 --- a/src/virtualenv/seed/wheels/embed/__init__.py +++ b/src/virtualenv/seed/wheels/embed/__init__.py @@ -1,7 +1,6 @@ -from __future__ import absolute_import, unicode_literals +from pathlib import Path from virtualenv.seed.wheels.util import Wheel -from virtualenv.util.path import Path BUNDLE_FOLDER = Path(__file__).absolute().parent BUNDLE_SUPPORT = { @@ -54,9 +53,9 @@ def get_embed_wheel(distribution, for_py_version): return Wheel.from_path(path) -__all__ = ( +__all__ = [ "get_embed_wheel", "BUNDLE_SUPPORT", "MAX", "BUNDLE_FOLDER", -) +] diff --git a/src/virtualenv/seed/wheels/periodic_update.py b/src/virtualenv/seed/wheels/periodic_update.py index 4f0336b..ec95bf5 100644 --- a/src/virtualenv/seed/wheels/periodic_update.py +++ b/src/virtualenv/seed/wheels/periodic_update.py @@ -2,40 +2,28 @@ Periodically update bundled versions. """ -from __future__ import absolute_import, unicode_literals import json import logging import os import ssl -import subprocess import sys from datetime import datetime, timedelta from itertools import groupby +from pathlib import Path from shutil import copy2 +from subprocess import PIPE, Popen from textwrap import dedent from threading import Thread - -from six.moves.urllib.error import URLError -from six.moves.urllib.request import urlopen +from urllib.error import URLError +from urllib.request import urlopen from virtualenv.app_data import AppDataDiskFolder -from virtualenv.info import PY2 -from virtualenv.util.path import Path -from virtualenv.util.subprocess import CREATE_NO_WINDOW, Popen +from virtualenv.util.subprocess import CREATE_NO_WINDOW from ..wheels.embed import BUNDLE_SUPPORT from ..wheels.util import Wheel -if PY2: - # on Python 2 datetime.strptime throws the error below if the import did not trigger on main thread - # Failed to import _strptime because the import lock is held by - try: - import _strptime # noqa - except ImportError: # pragma: no cov - pass # pragma: no cov - - GRACE_PERIOD_CI = timedelta(hours=1) # prevent version switch in the middle of a CI run GRACE_PERIOD_MINOR = timedelta(days=28) UPDATE_PERIOD = timedelta(days=14) @@ -106,7 +94,7 @@ def load_datetime(value): return None if value is None else datetime.strptime(value, DATETIME_FMT) -class NewVersion(object): +class NewVersion: def __init__(self, filename, found_date, release_date, source): self.filename = filename self.found_date = found_date @@ -142,12 +130,9 @@ class NewVersion(object): return False def __repr__(self): - return "{}(filename={}), found_date={}, release_date={}, source={})".format( - self.__class__.__name__, - self.filename, - self.found_date, - self.release_date, - self.source, + return ( + f"{self.__class__.__name__}(filename={self.filename}), found_date={self.found_date}, " + f"release_date={self.release_date}, source={self.source})" ) def __eq__(self, other): @@ -163,7 +148,7 @@ class NewVersion(object): return Wheel(Path(self.filename)) -class UpdateLog(object): +class UpdateLog: def __init__(self, started, completed, versions, periodic): self.started = started self.completed = completed @@ -224,8 +209,8 @@ def trigger_update(distribution, for_py_version, wheel, search_dirs, app_data, e .strip() .format(distribution, for_py_version, wheel_path, str(app_data), [str(p) for p in search_dirs], periodic), ] - debug = env.get(str("_VIRTUALENV_PERIODIC_UPDATE_INLINE")) == str("1") - pipe = None if debug else subprocess.PIPE + debug = env.get("_VIRTUALENV_PERIODIC_UPDATE_INLINE") == "1" + pipe = None if debug else PIPE kwargs = {"stdout": pipe, "stderr": pipe} if not debug and sys.platform == "win32": kwargs["creationflags"] = CREATE_NO_WINDOW @@ -233,7 +218,7 @@ def trigger_update(distribution, for_py_version, wheel, search_dirs, app_data, e logging.info( "triggered periodic upgrade of %s%s (for python %s) via background process having PID %d", distribution, - "" if wheel is None else "=={}".format(wheel.version), + "" if wheel is None else f"=={wheel.version}", for_py_version, process.pid, ) @@ -286,7 +271,7 @@ def _run_do_update(app_data, distribution, embed_filename, for_py_version, perio download_time = datetime.now() dest = acquire.download_wheel( distribution=distribution, - version_spec=None if last_version is None else "<{}".format(last_version), + version_spec=None if last_version is None else f"<{last_version}", for_py_version=for_py_version, search_dirs=search_dirs, app_data=app_data, @@ -346,7 +331,7 @@ def _pypi_get_distribution_info_cached(distribution): def _pypi_get_distribution_info(distribution): - content, url = None, "https://pypi.org/pypi/{}/json".format(distribution) + content, url = None, f"https://pypi.org/pypi/{distribution}/json" try: for context in _request_context(): try: @@ -401,20 +386,19 @@ def _run_manual_upgrade(app_data, distribution, for_py_version, env): search_dirs=[], periodic=False, ) - msg = "upgraded %s for python %s in %s {}".format( - "new entries found:\n%s" if versions else "no new versions found", - ) + args = [ distribution, for_py_version, datetime.now() - start, ] if versions: - args.append("\n".join("\t{}".format(v) for v in versions)) - logging.warning(msg, *args) + args.append("\n".join(f"\t{v}" for v in versions)) + ver_update = "new entries found:\n%s" if versions else "no new versions found" + logging.warning(f"upgraded %s for python %s in %s {ver_update}", *args) -__all__ = ( +__all__ = [ "add_wheel_to_update_log", "periodic_update", "do_update", @@ -425,4 +409,4 @@ __all__ = ( "dump_datetime", "trigger_update", "release_date_for_wheel_path", -) +] diff --git a/src/virtualenv/seed/wheels/util.py b/src/virtualenv/seed/wheels/util.py index 1240eb2..f09d873 100644 --- a/src/virtualenv/seed/wheels/util.py +++ b/src/virtualenv/seed/wheels/util.py @@ -1,12 +1,8 @@ -from __future__ import absolute_import, unicode_literals - from operator import attrgetter from zipfile import ZipFile -from virtualenv.util.six import ensure_text - -class Wheel(object): +class Wheel: def __init__(self, path): # https://www.python.org/dev/peps/pep-0427/#file-name-convention # The wheel filename is {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl @@ -48,8 +44,8 @@ class Wheel(object): return self.path.name def support_py(self, py_version): - name = "{}.dist-info/METADATA".format("-".join(self.path.stem.split("-")[0:2])) - with ZipFile(ensure_text(str(self.path)), "r") as zip_file: + name = f"{'-'.join(self.path.stem.split('-')[0:2])}.dist-info/METADATA" + with ZipFile(str(self.path), "r") as zip_file: metadata = zip_file.read(name).decode("utf-8") marker = "Requires-Python:" requires = next((i[len(marker) :] for i in metadata.splitlines() if i.startswith(marker)), None) @@ -75,7 +71,7 @@ class Wheel(object): return True def __repr__(self): - return "{}({})".format(self.__class__.__name__, self.path) + return f"{self.__class__.__name__}({self.path})" def __str__(self): return str(self.path) @@ -97,10 +93,7 @@ class Version: bundle = "bundle" embed = "embed" #: custom version handlers - non_version = ( - bundle, - embed, - ) + non_version = (bundle, embed) @staticmethod def of_version(value): @@ -108,9 +101,16 @@ class Version: @staticmethod def as_pip_req(distribution, version): - return "{}{}".format(distribution, Version.as_version_spec(version)) + return f"{distribution}{Version.as_version_spec(version)}" @staticmethod def as_version_spec(version): of_version = Version.of_version(version) - return "" if of_version is None else "=={}".format(of_version) + return "" if of_version is None else f"=={of_version}" + + +__all__ = [ + "discover_wheels", + "Version", + "Wheel", +] diff --git a/src/virtualenv/util/__init__.py b/src/virtualenv/util/__init__.py index 32d0292..e69de29 100644 --- a/src/virtualenv/util/__init__.py +++ b/src/virtualenv/util/__init__.py @@ -1,11 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import sys - -if sys.version_info[0] == 3: - import configparser as ConfigParser -else: - import ConfigParser - - -__all__ = ("ConfigParser",) diff --git a/src/virtualenv/util/error.py b/src/virtualenv/util/error.py index ac5aa50..945a25e 100644 --- a/src/virtualenv/util/error.py +++ b/src/virtualenv/util/error.py @@ -1,12 +1,11 @@ """Errors""" -from __future__ import absolute_import, unicode_literals class ProcessCallFailed(RuntimeError): """Failed a process call""" def __init__(self, code, out, err, cmd): - super(ProcessCallFailed, self).__init__(code, out, err, cmd) + super().__init__(code, out, err, cmd) self.code = code self.out = out self.err = err diff --git a/src/virtualenv/util/lock.py b/src/virtualenv/util/lock.py index 1cf968b..04c9f9c 100644 --- a/src/virtualenv/util/lock.py +++ b/src/virtualenv/util/lock.py @@ -1,16 +1,13 @@ """holds locking functionality that works across processes""" -from __future__ import absolute_import, unicode_literals import logging import os from abc import ABCMeta, abstractmethod from contextlib import contextmanager +from pathlib import Path from threading import Lock, RLock from filelock import FileLock, Timeout -from six import add_metaclass - -from virtualenv.util.path import Path class _CountedFileLock(FileLock): @@ -21,20 +18,20 @@ class _CountedFileLock(FileLock): os.makedirs(parent) except OSError: pass - super(_CountedFileLock, self).__init__(lock_file) + super().__init__(lock_file) self.count = 0 self.thread_safe = RLock() def acquire(self, timeout=None, poll_interval=0.05): with self.thread_safe: if self.count == 0: - super(_CountedFileLock, self).acquire(timeout, poll_interval) + super().acquire(timeout, poll_interval) self.count += 1 def release(self, force=False): with self.thread_safe: if self.count == 1: - super(_CountedFileLock, self).release(force=force) + super().release(force=force) self.count = max(self.count - 1, 0) @@ -42,14 +39,13 @@ _lock_store = {} _store_lock = Lock() -@add_metaclass(ABCMeta) -class PathLockBase(object): +class PathLockBase(metaclass=ABCMeta): def __init__(self, folder): path = Path(folder) self.path = path.resolve() if path.exists() else path def __repr__(self): - return "{}({})".format(self.__class__.__name__, self.path) + return f"{self.__class__.__name__}({self.path})" def __div__(self, other): return type(self)(self.path / other) @@ -78,11 +74,11 @@ class PathLockBase(object): class ReentrantFileLock(PathLockBase): def __init__(self, folder): - super(ReentrantFileLock, self).__init__(folder) + super().__init__(folder) self._lock = None def _create_lock(self, name=""): - lock_file = str(self.path / "{}.lock".format(name)) + lock_file = str(self.path / f"{name}.lock") with _store_lock: if lock_file not in _lock_store: _lock_store[lock_file] = _CountedFileLock(lock_file) @@ -144,7 +140,7 @@ class ReentrantFileLock(PathLockBase): @contextmanager def non_reentrant_lock_for_key(self, name): - with _CountedFileLock(str(self.path / "{}.lock".format(name))): + with _CountedFileLock(str(self.path / f"{name}.lock")): yield @@ -164,8 +160,8 @@ class NoOpFileLock(PathLockBase): yield -__all__ = ( +__all__ = [ "NoOpFileLock", "ReentrantFileLock", "Timeout", -) +] diff --git a/src/virtualenv/util/path/__init__.py b/src/virtualenv/util/path/__init__.py index dc628de..39a8db7 100644 --- a/src/virtualenv/util/path/__init__.py +++ b/src/virtualenv/util/path/__init__.py @@ -1,18 +1,14 @@ -from __future__ import absolute_import, unicode_literals - -from ._pathlib import Path from ._permission import make_exe, set_tree from ._sync import copy, copytree, ensure_dir, safe_delete, symlink from ._win import get_short_path_name -__all__ = ( +__all__ = [ "ensure_dir", "symlink", "copy", "copytree", - "Path", "make_exe", "set_tree", "safe_delete", "get_short_path_name", -) +] diff --git a/src/virtualenv/util/path/_pathlib/__init__.py b/src/virtualenv/util/path/_pathlib/__init__.py deleted file mode 100644 index 746c8ae..0000000 --- a/src/virtualenv/util/path/_pathlib/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import sys - -import six - -if six.PY3: - from pathlib import Path -else: - if sys.platform == "win32": - # workaround for https://github.com/mcmtroffaes/pathlib2/issues/56 - from .via_os_path import Path - else: - from pathlib2 import Path - - -__all__ = ("Path",) diff --git a/src/virtualenv/util/path/_pathlib/via_os_path.py b/src/virtualenv/util/path/_pathlib/via_os_path.py deleted file mode 100644 index c2b8a0d..0000000 --- a/src/virtualenv/util/path/_pathlib/via_os_path.py +++ /dev/null @@ -1,160 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import fnmatch -import os -import platform -from contextlib import contextmanager - -from virtualenv.util.six import ensure_str, ensure_text - -IS_PYPY = platform.python_implementation() == "PyPy" - - -class Path(object): - def __init__(self, path=""): - if isinstance(path, Path): - _path = path._path - else: - _path = ensure_text(path) - if IS_PYPY: - _path = _path.encode("utf-8") - self._path = _path - - def __repr__(self): - return ensure_str("Path({})".format(ensure_text(self._path))) - - def __unicode__(self): - return ensure_text(self._path) - - def __str__(self): - return ensure_str(self._path) - - def __div__(self, other): - if isinstance(other, Path): - right = other._path - else: - right = ensure_text(other) - if IS_PYPY: - right = right.encode("utf-8") - return Path(os.path.join(self._path, right)) - - def __truediv__(self, other): - return self.__div__(other) - - def __eq__(self, other): - return self._path == (other._path if isinstance(other, Path) else None) - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash(self._path) - - def as_posix(self): - return str(self).replace(os.sep, "/") - - def exists(self): - return os.path.exists(self._path) - - @property - def parent(self): - return Path(os.path.abspath(os.path.join(self._path, os.path.pardir))) - - def resolve(self): - return Path(os.path.realpath(self._path)) - - @property - def name(self): - return os.path.basename(self._path) - - @property - def parts(self): - return self._path.split(os.sep) - - def is_file(self): - return os.path.isfile(self._path) - - def is_dir(self): - return os.path.isdir(self._path) - - def mkdir(self, parents=True, exist_ok=True): - try: - os.makedirs(self._path) - except OSError: - if not exist_ok: - raise - - def read_text(self, encoding="utf-8"): - return self.read_bytes().decode(encoding) - - def read_bytes(self): - with open(self._path, "rb") as file_handler: - return file_handler.read() - - def write_bytes(self, content): - with open(self._path, "wb") as file_handler: - file_handler.write(content) - - def write_text(self, text, encoding="utf-8"): - self.write_bytes(text.encode(encoding)) - - def iterdir(self): - for p in os.listdir(self._path): - yield Path(os.path.join(self._path, p)) - - @property - def suffix(self): - _, ext = os.path.splitext(self.name) - return ext - - @property - def stem(self): - base, _ = os.path.splitext(self.name) - return base - - @contextmanager - def open(self, mode="r"): - with open(self._path, mode) as file_handler: - yield file_handler - - @property - def parents(self): - result = [] - parts = self.parts - for i in range(len(parts) - 1): - result.append(Path(os.sep.join(parts[0 : i + 1]))) - return result[::-1] - - def unlink(self): - os.remove(self._path) - - def with_name(self, name): - return self.parent / name - - def is_symlink(self): - return os.path.islink(self._path) - - def relative_to(self, other): - if not self._path.startswith(other._path): - raise ValueError("{} does not start with {}".format(self._path, other._path)) - return Path(os.sep.join(self.parts[len(other.parts) :])) - - def stat(self): - return os.stat(self._path) - - def chmod(self, mode): - os.chmod(self._path, mode) - - def absolute(self): - return Path(os.path.abspath(self._path)) - - def rglob(self, pattern): - """ - Rough emulation of the origin method. Just for searching fixture files. - """ - for root, _dirs, files in os.walk(self._path): - for filename in fnmatch.filter(files, pattern): - yield Path(os.path.join(root, filename)) - - -__all__ = ("Path",) diff --git a/src/virtualenv/util/path/_permission.py b/src/virtualenv/util/path/_permission.py index 73bb6e8..ca92314 100644 --- a/src/virtualenv/util/path/_permission.py +++ b/src/virtualenv/util/path/_permission.py @@ -1,10 +1,6 @@ -from __future__ import absolute_import, unicode_literals - import os from stat import S_IXGRP, S_IXOTH, S_IXUSR -from virtualenv.util.six import ensure_text - def make_exe(filename): original_mode = filename.stat().st_mode @@ -21,7 +17,7 @@ def make_exe(filename): def set_tree(folder, stat): - for root, _, files in os.walk(ensure_text(str(folder))): + for root, _, files in os.walk(str(folder)): for filename in files: os.chmod(os.path.join(root, filename), stat) diff --git a/src/virtualenv/util/path/_sync.py b/src/virtualenv/util/path/_sync.py index 05f19d0..10c793d 100644 --- a/src/virtualenv/util/path/_sync.py +++ b/src/virtualenv/util/path/_sync.py @@ -1,33 +1,18 @@ -from __future__ import absolute_import, unicode_literals - import logging import os import shutil from stat import S_IWUSR -from six import PY2 - -from virtualenv.info import IS_CPYTHON, IS_WIN -from virtualenv.util.six import ensure_text - -if PY2 and IS_CPYTHON and IS_WIN: # CPython2 on Windows supports unicode paths if passed as unicode - - def norm(src): - return ensure_text(str(src)) - -else: - norm = str - def ensure_dir(path): if not path.exists(): - logging.debug("create folder %s", ensure_text(str(path))) - os.makedirs(norm(path)) + logging.debug("create folder %s", str(path)) + os.makedirs(str(path)) def ensure_safe_to_do(src, dest): if src == dest: - raise ValueError("source and destination is the same {}".format(src)) + raise ValueError(f"source and destination is the same {src}") if not dest.exists(): return if dest.is_dir() and not dest.is_symlink(): @@ -49,7 +34,7 @@ def copy(src, dest): is_dir = src.is_dir() method = copytree if is_dir else shutil.copy logging.debug("copy %s", _Debug(src, dest)) - method(norm(src), norm(dest)) + method(str(src), str(dest)) def copytree(src, dest): @@ -71,27 +56,23 @@ def safe_delete(dest): else: raise - shutil.rmtree(ensure_text(str(dest)), ignore_errors=True, onerror=onerror) + shutil.rmtree(str(dest), ignore_errors=True, onerror=onerror) -class _Debug(object): +class _Debug: def __init__(self, src, dest): self.src = src self.dest = dest def __str__(self): - return "{}{} to {}".format( - "directory " if self.src.is_dir() else "", - ensure_text(str(self.src)), - ensure_text(str(self.dest)), - ) + return f"{'directory ' if self.src.is_dir() else ''}{str(self.src)} to {str(self.dest)}" -__all__ = ( +__all__ = [ "ensure_dir", "symlink", "copy", "symlink", "copytree", "safe_delete", -) +] diff --git a/src/virtualenv/util/path/_win.py b/src/virtualenv/util/path/_win.py index 02e16d0..8dd81c9 100644 --- a/src/virtualenv/util/path/_win.py +++ b/src/virtualenv/util/path/_win.py @@ -17,3 +17,8 @@ def get_short_path_name(long_name): return output_buf.value else: output_buf_size = needed + + +__all__ = [ + "get_short_path_name", +] diff --git a/src/virtualenv/util/six.py b/src/virtualenv/util/six.py deleted file mode 100644 index 199cbed..0000000 --- a/src/virtualenv/util/six.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Backward compatibility layer with older version of six. - -This is used to avoid virtualenv requiring a version of six newer than what -the system may have. -""" -from __future__ import absolute_import - -from six import PY2, PY3, binary_type, text_type - -try: - from six import ensure_text -except ImportError: - - def ensure_text(s, encoding="utf-8", errors="strict"): - """Coerce *s* to six.text_type. - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -try: - from six import ensure_str -except ImportError: - - def ensure_str(s, encoding="utf-8", errors="strict"): - """Coerce *s* to `str`. - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) - return s diff --git a/src/virtualenv/util/subprocess/__init__.py b/src/virtualenv/util/subprocess/__init__.py index 9f6ffd7..bc6ec4d 100644 --- a/src/virtualenv/util/subprocess/__init__.py +++ b/src/virtualenv/util/subprocess/__init__.py @@ -1,24 +1,11 @@ -from __future__ import absolute_import, unicode_literals - import subprocess -import sys - -import six - -if six.PY2 and sys.platform == "win32": - from . import _win_subprocess - - Popen = _win_subprocess.Popen -else: - Popen = subprocess.Popen - CREATE_NO_WINDOW = 0x80000000 def run_cmd(cmd): try: - process = Popen( + process = subprocess.Popen( cmd, universal_newlines=True, stdin=subprocess.PIPE, @@ -30,14 +17,11 @@ def run_cmd(cmd): except OSError as error: code, out, err = error.errno, "", error.strerror if code == 2 and "file" in err: - # FileNotFoundError in Python >= 3.3 - err = str(error) + err = str(error) # FileNotFoundError in Python >= 3.3 return code, out, err __all__ = ( - "subprocess", - "Popen", "run_cmd", "CREATE_NO_WINDOW", ) diff --git a/src/virtualenv/util/subprocess/_win_subprocess.py b/src/virtualenv/util/subprocess/_win_subprocess.py deleted file mode 100644 index ce53197..0000000 --- a/src/virtualenv/util/subprocess/_win_subprocess.py +++ /dev/null @@ -1,176 +0,0 @@ -# flake8: noqa -# fmt: off -## issue: https://bugs.python.org/issue19264 - -import ctypes -import os -import platform -import subprocess -from ctypes import Structure, WinError, byref, c_char_p, c_void_p, c_wchar, c_wchar_p, sizeof, windll -from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPVOID, LPWSTR, WORD - -import _subprocess - -## -## Types -## - -CREATE_UNICODE_ENVIRONMENT = 0x00000400 -LPCTSTR = c_char_p -LPTSTR = c_wchar_p -LPSECURITY_ATTRIBUTES = c_void_p -LPBYTE = ctypes.POINTER(BYTE) - -class STARTUPINFOW(Structure): - _fields_ = [ - ("cb", DWORD), ("lpReserved", LPWSTR), - ("lpDesktop", LPWSTR), ("lpTitle", LPWSTR), - ("dwX", DWORD), ("dwY", DWORD), - ("dwXSize", DWORD), ("dwYSize", DWORD), - ("dwXCountChars", DWORD), ("dwYCountChars", DWORD), - ("dwFillAtrribute", DWORD), ("dwFlags", DWORD), - ("wShowWindow", WORD), ("cbReserved2", WORD), - ("lpReserved2", LPBYTE), ("hStdInput", HANDLE), - ("hStdOutput", HANDLE), ("hStdError", HANDLE), - ] - -LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW) - - -class PROCESS_INFORMATION(Structure): - _fields_ = [ - ("hProcess", HANDLE), ("hThread", HANDLE), - ("dwProcessId", DWORD), ("dwThreadId", DWORD), - ] - -LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) - - -class DUMMY_HANDLE(ctypes.c_void_p): - - def __init__(self, *a, **kw): - super(DUMMY_HANDLE, self).__init__(*a, **kw) - self.closed = False - - def Close(self): - if not self.closed: - windll.kernel32.CloseHandle(self) - self.closed = True - - def __int__(self): - return self.value - - -CreateProcessW = windll.kernel32.CreateProcessW -CreateProcessW.argtypes = [ - LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, - LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, - LPSTARTUPINFOW, LPPROCESS_INFORMATION, -] -CreateProcessW.restype = BOOL - - -## -## Patched functions/classes -## - -def CreateProcess( - executable, args, _p_attr, _t_attr, - inherit_handles, creation_flags, env, cwd, - startup_info, -): - """Create a process supporting unicode executable and args for win32 - - Python implementation of CreateProcess using CreateProcessW for Win32 - - """ - - si = STARTUPINFOW( - dwFlags=startup_info.dwFlags, - wShowWindow=startup_info.wShowWindow, - cb=sizeof(STARTUPINFOW), - ## XXXvlab: not sure of the casting here to ints. - hStdInput=startup_info.hStdInput if startup_info.hStdInput is None else int(startup_info.hStdInput), - hStdOutput=startup_info.hStdOutput if startup_info.hStdOutput is None else int(startup_info.hStdOutput), - hStdError=startup_info.hStdError if startup_info.hStdError is None else int(startup_info.hStdError), - ) - - wenv = None - if env is not None: - ## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar - env = ( - unicode("").join([ - unicode("%s=%s\0") % (k, v) - for k, v in env.items() - ]) - ) + unicode("\0") - wenv = (c_wchar * len(env))() - wenv.value = env - - wcwd = None - if cwd is not None: - wcwd = unicode(cwd) - - pi = PROCESS_INFORMATION() - creation_flags |= CREATE_UNICODE_ENVIRONMENT - - if CreateProcessW( - executable, args, None, None, - inherit_handles, creation_flags, - wenv, wcwd, byref(si), byref(pi), - ): - return ( - DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread), - pi.dwProcessId, pi.dwThreadId, - ) - raise WinError() - - -class Popen(subprocess.Popen): - """This superseeds Popen and corrects a bug in cPython 2.7 implem""" - - def _execute_child( - self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, to_close, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite, - ): - """Code from part of _execute_child from Python 2.7 (9fbb65e) - - There are only 2 little changes concerning the construction of - the the final string in shell mode: we preempt the creation of - the command string when shell is True, because original function - will try to encode unicode args which we want to avoid to be able to - sending it as-is to ``CreateProcess``. - - """ - if startupinfo is None: - startupinfo = subprocess.STARTUPINFO() - if not isinstance(args, subprocess.types.StringTypes): - args = [i if isinstance(i, bytes) else i.encode('utf-8') for i in args] - args = subprocess.list2cmdline(args) - if platform.python_implementation() == "CPython": - args = args.decode('utf-8') - startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = _subprocess.SW_HIDE - env = os.environ if env is None else env - comspec = env.get("COMSPEC", unicode("cmd.exe")) - if ( - _subprocess.GetVersion() >= 0x80000000 or - os.path.basename(comspec).lower() == "command.com" - ): - w9xpopen = self._find_w9xpopen() - args = unicode('"%s" %s') % (w9xpopen, args) - creationflags |= _subprocess.CREATE_NEW_CONSOLE - - super(Popen, self)._execute_child( - args, executable, - preexec_fn, close_fds, cwd, env, universal_newlines, - startupinfo, creationflags, False, to_close, p2cread, - p2cwrite, c2pread, c2pwrite, errread, errwrite, - ) - -_subprocess.CreateProcess = CreateProcess -# fmt: on diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index 85d9294..e7578c4 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -1,11 +1,8 @@ -from __future__ import absolute_import, unicode_literals - import logging import os import zipfile from virtualenv.info import IS_WIN, ROOT -from virtualenv.util.six import ensure_text def read(full_path): @@ -21,7 +18,7 @@ def extract(full_path, dest): with zipfile.ZipFile(ROOT, "r") as zip_file: info = zip_file.getinfo(sub_file) info.filename = dest.name - zip_file.extract(info, ensure_text(str(dest.parent))) + zip_file.extract(info, str(dest.parent)) def _get_path_within_zip(full_path): @@ -31,3 +28,9 @@ def _get_path_within_zip(full_path): # paths are always UNIX separators, even on Windows, though __file__ still follows platform default sub_file = sub_file.replace(os.sep, "/") return sub_file + + +__all__ = [ + "read", + "extract", +] |