-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support built-in “venv” #173
Open
uranusjr
wants to merge
8
commits into
pew-org:master
Choose a base branch
from
uranusjr:venv
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
8ed7afb
Add __main__ so test runs are easier
uranusjr e72ece5
Beefing up tests so Tox works for 3.5+ on macOS
uranusjr 79ae559
Refactor some run-time constants to _utils
uranusjr e961bec
Implement venv backend
uranusjr e0b7cf0
Expand test matrix to be more comprehensible
uranusjr c1943cf
Try harder to find Python for venv in a virtualenv
uranusjr dba8b36
Refactor venv/virtualenv switch into pew._utils
uranusjr c25f19e
Ugly hack to work around activate script perms
uranusjr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
install: | ||
- SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH% | ||
- pip install tox | ||
- SET PATH=C:\\Python36;C:\\Python35;C:\\Python34;C:\\Python33;C:\\Python27;C:\\Python26;%PATH% | ||
- C:\\Python36\\Scripts\\pip.exe install tox | ||
|
||
build: false | ||
|
||
test_script: tox -e "py27,py34" | ||
test_script: C:\\Python36\\Scripts\\tox.exe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
if __name__ == '__main__': | ||
from . import pew | ||
pew.pew() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import collections | ||
import pathlib | ||
|
||
|
||
class Cfg(object): | ||
"""Minimal parser for pyvenv.cfg. | ||
""" | ||
def __init__(self, path): | ||
self._path = path | ||
self._data = collections.OrderedDict() | ||
with path.open(encoding='utf-8') as f: | ||
for line in f: | ||
key, _, value = line.partition('=') | ||
self._data[key.strip().lower()] = value.strip() | ||
|
||
@property | ||
def bindir(self): | ||
return pathlib.Path(self._data['home']) | ||
|
||
@property | ||
def include_system_sitepackages(self): | ||
return self._data['include-system-site-packages'] == 'true' | ||
|
||
@include_system_sitepackages.setter | ||
def include_system_sitepackages(self, value): | ||
value = {True: 'true', False: 'false'}[value] | ||
self._data['include-system-site-packages'] = value | ||
|
||
def save(self): | ||
with self._path.open('w', encoding='utf-8') as f: | ||
for k, v in self._data.items(): | ||
f.write('{} = {}\n'.format(k, v)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
__all__ = ['choose_backend', 'guess_backend'] | ||
|
||
import os | ||
import pathlib | ||
import subprocess | ||
import sys | ||
import warnings | ||
|
||
from ._cfg import Cfg | ||
from ._utils import env_bin_dir, invoke, uses_venv, windows | ||
|
||
|
||
class Backend(object): | ||
|
||
def __init__(self, root): | ||
super(Backend, self).__init__() | ||
self.root = root.absolute() | ||
|
||
def get_python(self): | ||
return self.root.joinpath(env_bin_dir, 'python.exe' if windows else 'python') | ||
|
||
def get_sitepackages_dir(self): | ||
result = invoke( | ||
str(self.get_python()), '-c', | ||
'import distutils.sysconfig; ' | ||
'print(distutils.sysconfig.get_python_lib())', | ||
) | ||
if result.returncode != 0: | ||
raise RuntimeError( | ||
'could not get site-packages location.\n%s' % result.err, | ||
) | ||
return pathlib.Path(result.out) | ||
|
||
|
||
class VirtualenvBackend(Backend): | ||
"""Functionality provided by virtualenv. | ||
""" | ||
def create_env(self, py, args): | ||
if py: | ||
args = ["--python=%s" % py] + args | ||
subprocess.check_call( | ||
[sys.executable, '-m', 'virtualenv', str(self.root)] + args, | ||
) | ||
|
||
def restore_env(self): | ||
subprocess.check_call([ | ||
sys.executable, "-m", 'virtualenv', str(self.root), | ||
"--python=%s" % self.get_python().name, | ||
]) | ||
|
||
def toggle_global_sitepackages(self): | ||
ngsp = self.get_sitepackages_dir().with_name( | ||
'no-global-site-packages.txt', | ||
) | ||
should_enable = not ngsp.exists() | ||
if should_enable: | ||
with ngsp.open('w'): | ||
pass | ||
else: | ||
ngsp.unlink() | ||
return should_enable | ||
|
||
|
||
class BrokenEnvironmentError(RuntimeError): | ||
pass | ||
|
||
|
||
def find_real_python(): | ||
# We need to run venv on the Python we want to use. sys.executable is not | ||
# enough because it might come from a venv itself. venv can be easily | ||
# confused if it is nested inside another venv. | ||
# https://bugs.python.org/issue30811 | ||
python = ('python.exe' if windows else 'python') | ||
|
||
# If we're in a venv, excellent! The config file has this information. | ||
pyvenv_cfg = pathlib.Path(sys.prefix, 'pyvenv.cfg') | ||
if pyvenv_cfg.exists(): | ||
return Cfg(pyvenv_cfg).bindir.joinpath(python) | ||
|
||
# Or we can try looking this up from the build configuration. This is | ||
# usually good enough, unless we're in a virtualenv (i.e. sys.real_prefix | ||
# is set), and sysconfig reports the wrong Python (i.e. in the virtualenv). | ||
import sysconfig | ||
bindir = sysconfig.get_config_var('BINDIR') | ||
try: | ||
real_prefix = sys.real_prefix | ||
except AttributeError: | ||
return pathlib.Path(bindir, python) | ||
if not os.path.realpath(bindir).startswith(real_prefix): | ||
return pathlib.Path(bindir, python) | ||
|
||
# OK, so we're in a virtualenv, and sysconfig lied. At this point there's | ||
# no definitive way to tell where the real Python is, so let's make an | ||
# educated guess. This works if the user isn't using a very exotic build. | ||
for rel in ['', os.path.relpath(str(bindir), real_prefix), env_bin_dir]: | ||
try: | ||
path = pathlib.Path(real_prefix, rel, python).resolve() | ||
except FileNotFoundError: | ||
continue | ||
if path.exists(): # On 3.6+ resolve() doesn't check for existence. | ||
return path | ||
|
||
# We've tried everything. Sorry. | ||
raise BrokenEnvironmentError | ||
|
||
|
||
class VenvBackend(Backend): | ||
"""Functionality provided by venv, built-in since Python 3.4. | ||
""" | ||
module_name = 'venv' | ||
|
||
def create_env(self, py, args): | ||
if not py: | ||
py = find_real_python() | ||
subprocess.check_call([str(py), '-m', 'venv', str(self.root)] + args) | ||
|
||
def restore_env(self): | ||
cfg = Cfg(self.root.joinpath('pyvenv.cfg')) | ||
py = cfg.bindir.joinpath('python.exe' if windows else 'python') | ||
subprocess.check_call([str(py), "-m", 'venv', str(self.root)]) | ||
|
||
def toggle_global_sitepackages(self): | ||
cfg = Cfg(self.root.joinpath('pyvenv.cfg')) | ||
should_enable = not cfg.include_system_sitepackages | ||
cfg.include_system_sitepackages = should_enable | ||
cfg.save() | ||
return should_enable | ||
|
||
|
||
def choose_backend(root): | ||
"""Choose a preferred virtual environment backend to use. | ||
""" | ||
if not uses_venv(): | ||
return VirtualenvBackend(root) | ||
|
||
# Without ensurepip the venv can can't bootstrap Setuptools and Pip. | ||
# The venv would not be useful for us without them. | ||
for module_name in ['ensurepip', 'venv']: | ||
try: | ||
__import__(module_name) | ||
except ImportError: | ||
warnings.warn( | ||
'Module "%s" unavailable, falling back to virtualenv...\n' | ||
'Set PEW_USE_VIRTUALENV environment variable to ' | ||
'suppress this.' % module_name, | ||
FutureWarning, | ||
) | ||
return VirtualenvBackend(root) | ||
return VenvBackend(root) | ||
|
||
|
||
def guess_backend(root): | ||
"""Guess what backend a virtual environment backend uses. | ||
|
||
The built-in venv writes a pyvenv.cfg file; check if it exists. | ||
""" | ||
pyvenv_cfg = root / 'pyvenv.cfg' | ||
if pyvenv_cfg.exists(): | ||
return VenvBackend(root) | ||
return VirtualenvBackend(root) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make a
Path
out ofbindir
straight away