commit 03ac6f22772b506e5004bd459b2cac886d1c5094 Author: Timmy Welch Date: Thu Apr 27 11:29:06 2023 -0700 Initial Commit diff --git a/.activate.sh b/.activate.sh new file mode 120000 index 0000000..3082b32 --- /dev/null +++ b/.activate.sh @@ -0,0 +1 @@ +venv/bin/activate.xsh \ No newline at end of file diff --git a/.deactivate.sh b/.deactivate.sh new file mode 100644 index 0000000..d1898d7 --- /dev/null +++ b/.deactivate.sh @@ -0,0 +1 @@ +deactivate diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6bed2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,85 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion + +*.iml + +## Directory-based project format: +.idea/ + +### Other editors +.*.swp +nbproject/ +.vscode + +*.exe +*.zip + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# for testing +temp/ +tmp/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..36d8c49 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: debug-statements + - id: double-quote-string-fixer + - id: name-tests-test + - id: requirements-txt-fixer +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.2.0 + hooks: + - id: setup-cfg-fmt +- repo: https://github.com/asottile/reorder_python_imports + rev: v3.9.0 + hooks: + - id: reorder-python-imports + args: [--py38-plus, --add-import, 'from __future__ import annotations'] +- repo: https://github.com/asottile/add-trailing-comma + rev: v2.4.0 + hooks: + - id: add-trailing-comma + args: [--py36-plus] +- repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: [--py311-plus] +- repo: https://github.com/pre-commit/mirrors-autopep8 + rev: v2.0.2 + hooks: + - id: autopep8 +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + additional_dependencies: [flake8-encodings, flake8-warnings, flake8-builtins, flake8-length, flake8-print] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..22cc467 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +local_scheme = "no-local-version" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9186411 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,73 @@ +[metadata] +name = virtualenv_discovery_exclude_current +description = custom discovery that excludes current interpreter +author = Timmy Welch +author_email = timmy@narnian.us +license = MIT +classifiers = + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + +[options] +py_modules = virtualenv_discovery_exclude_current +install_requires = + virtualenv>=20.22.0 +python_requires = >=3.7 + +[options.entry_points] +virtualenv.discovery = + monkeypatch = virtualenv_discovery_exclude_current:Mock + +[tox:tox] +envlist = wheel + +[testenv:wheel] +description = Generate wheel and tar.gz +labels = + release + build +skip_install = true +deps = + build +commands_pre = + -python -c 'import shutil,pathlib; \ + shutil.rmtree("./build/", ignore_errors=True); \ + shutil.rmtree("./dist/", ignore_errors=True)' +commands = + python -m build + +[testenv:pypi-upload] +description = Upload wheel to PyPi +platform = Linux +labels = + release +skip_install = true +depends = wheel +deps = + twine +passenv = + TWINE_* +setenv = + TWINE_NON_INTERACTIVE=true +commands = + python -m twine upload dist/*.whl dist/*.tar.gz + +[pep8] +ignore = E265,E501 +max_line_length = 120 + +[flake8] +extend-ignore = E501, A003 +max_line_length = 120 + +[coverage:run] +plugins = covdefaults + +[coverage:report] +fail_under = 95 + +[mypy-testing.*] +disallow_untyped_defs = false + +[mypy-tests.*] +disallow_untyped_defs = false diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3d93aef --- /dev/null +++ b/setup.py @@ -0,0 +1,4 @@ +from __future__ import annotations + +from setuptools import setup +setup() diff --git a/virtualenv_discovery_exclude_current.py b/virtualenv_discovery_exclude_current.py new file mode 100644 index 0000000..c15d82d --- /dev/null +++ b/virtualenv_discovery_exclude_current.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import logging +import os + +from virtualenv.discovery.builtin import Builtin +from virtualenv.discovery.builtin import check_path +from virtualenv.discovery.builtin import get_paths +from virtualenv.discovery.builtin import LazyPathDump +from virtualenv.discovery.builtin import PathPythonInfo +from virtualenv.discovery.builtin import possible_specs +from virtualenv.discovery.py_info import PythonInfo +from virtualenv.discovery.py_spec import PythonSpec + + +# Same as Builtin, needed for scope +class Mock(Builtin): + def run(self): + for python_spec in self.python_spec: + result = get_interpreter(python_spec, self.try_first_with, self.app_data, self._env) + if result is not None: + return result + return None + +# Same as builtin, needed for scope + + +def get_interpreter(key, try_first_with, app_data=None, env=None): + spec = PythonSpec.from_string_spec(key) + logging.info('find interpreter for spec %r', spec) + proposed_paths = set() + env = os.environ if env is None else env + for interpreter, impl_must_match in propose_interpreters(spec, try_first_with, app_data, env): + key = interpreter.system_executable, impl_must_match + if key in proposed_paths: + continue + logging.info('proposed %s', interpreter) + if interpreter.satisfies(spec, impl_must_match): + logging.debug('accepted %s', interpreter) + return interpreter + proposed_paths.add(key) + +# Current interpreter is removed + + +def propose_interpreters(spec, try_first_with, app_data, env=None): + # 0. try with first + env = os.environ if env is None else env + for py_exe in try_first_with: + path = os.path.abspath(py_exe) + try: + # Windows Store Python does not work with os.path.exists, but does for os.lstat + os.lstat(path) + except OSError: + pass + else: + yield PythonInfo.from_exe(os.path.abspath(path), app_data, env=env), True + + # 1. if it's a path and exists + if spec.path is not None: + try: + # Windows Store Python does not work with os.path.exists, but does for os.lstat + os.lstat(spec.path) + except OSError: + if spec.is_abs: + raise + else: + yield PythonInfo.from_exe(os.path.abspath(spec.path), app_data, env=env), True + if spec.is_abs: + return + # finally just find on path, the path order matters (as the candidates are less easy to control by end user) + paths = get_paths(env) + tested_exes = set() + for pos, path in enumerate(paths): + path_str = str(path) + logging.debug(LazyPathDump(pos, path_str, env)) + for candidate, match in possible_specs(spec): + found = check_path(candidate, path_str) + if found is not None: + exe = os.path.abspath(found) + if exe not in tested_exes: + tested_exes.add(exe) + interpreter = PathPythonInfo.from_exe(exe, app_data, raise_on_error=False, env=env) + if interpreter is not None: + yield interpreter, match