Skip to content
This repository was archived by the owner on Sep 6, 2025. It is now read-only.

Commit e8eecb9

Browse files
committed
usage: no_binary
I need it so venvstarter knows to install black as non binary and so I've added the ability to tell it about that. Also needed to make it so that if already installed as binary it must be removed first
1 parent d3c2869 commit e8eecb9

File tree

3 files changed

+101
-25
lines changed

3 files changed

+101
-25
lines changed

docs/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
---------
33

4+
.. _release-0.11.0:
5+
6+
0.11.0 - TBD
7+
* Ability to say some packages should not be installed as binary
8+
49
.. _release-0.10.0:
510

611
0.10.0 - 8 November 2021

tests/test_usage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,30 @@ def script():
146146
).split("\n")
147147
assert output[-1] == "yay"
148148

149+
it "can be used to make sure a dependency isn't binary":
150+
151+
def script():
152+
__import__("venvstarter").manager("python").add_pypi_deps("noy_black").run()
153+
154+
with pytest.helpers.make_script(script, prepare_venv=True) as filename:
155+
output = pytest.helpers.get_output(
156+
filename, "-c", "import black; print(black.__file__)"
157+
).split("\n")
158+
if not output[-1].endswith(".so"):
159+
pytest.skip("black doesn't install as binary on this system")
160+
161+
def script():
162+
__import__("venvstarter").manager("python").add_pypi_deps(
163+
"noy_black"
164+
).add_no_binary("black").run()
165+
166+
pytest.helpers.write_script(script, prepare_venv=True, filename=filename)
167+
168+
output = pytest.helpers.get_output(
169+
filename, "-c", "import black; print(black.__file__)"
170+
).split("\n")
171+
assert not output[-1].endswith(".so")
172+
149173
it "can be used to add environment variables":
150174

151175
def script():

venvstarter.py

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ class Starter(object):
409409
deps
410410
An optional list of pip dependencies to install into your virtualenv
411411
412+
no_binary
413+
List of deps that must not be installed as binary
414+
412415
env
413416
An optional dictionary of environment variables to add to the environment
414417
that the program is run in.
@@ -461,18 +464,23 @@ def __init__(
461464
venv_folder,
462465
venv_folder_name,
463466
deps=None,
467+
no_binary=None,
464468
env=None,
465469
min_python_version=None,
466470
max_python_version=None,
467471
):
468472
self.env = env
469473
self.deps = deps
470474
self.program = program
475+
self.no_binary = no_binary
471476
self.venv_folder = venv_folder
472477
self.venv_folder_name = venv_folder_name
473478
self.min_python_version = min_python_version
474479
self.max_python_version = max_python_version
475480

481+
if self.no_binary is None:
482+
self.no_binary = []
483+
476484
if self.deps is None:
477485
self.deps = []
478486

@@ -563,60 +571,93 @@ def make_virtualenv(self):
563571

564572
return True
565573

566-
def install_deps(self):
574+
def check_deps(self):
567575
deps = []
576+
568577
for dep in self.deps:
569578
if "#" in dep:
570-
deps.append(
571-
dict(arg.split("=", 1) for arg in dep.split("#", 1)[1].split("&"))["egg"]
572-
)
573-
else:
574-
deps.append(dep)
579+
dep = dict(arg.split("=", 1) for arg in dep.split("#", 1)[1].split("&"))["egg"]
580+
581+
deps.append(dep)
582+
575583
deps = json.dumps(deps)
576584

585+
handler = PythonHandler()
586+
question = """
587+
import pkg_resources
588+
import importlib
589+
import sys
590+
591+
try:
592+
pkg_resources.working_set.require({0})
593+
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict) as error:
594+
sys.stderr.write(str(error) + "\\n\\n")
595+
sys.stderr.flush()
596+
raise SystemExit(1)
597+
598+
for name in {1}:
599+
if importlib.import_module(name).__file__.endswith(".so"):
600+
sys.stderr.write(f"{{name}} needs to not be a binary installation\\n\\n")
601+
sys.stderr.flush()
602+
raise SystemExit(1)
603+
""".format(
604+
deps, self.no_binary
605+
)
606+
return handler.run_command(self.venv_python, question, check=False).returncode
607+
608+
def find_deps_to_be_made_not_binary(self):
609+
handler = PythonHandler()
610+
question = """
611+
import importlib
612+
613+
for name in {0}:
614+
try:
615+
if importlib.import_module(name).__file__.endswith(".so"):
616+
print(name)
617+
except ImportError:
618+
pass
619+
""".format(
620+
json.dumps(self.no_binary)
621+
)
622+
found = handler.run_command(self.venv_python, question, get_output=True).split("\n")
623+
return [shlex.quote(name.strip()) for name in found if name.strip()]
624+
625+
def install_deps(self):
577626
# Fix a bug whereby the virtualenv has the wrong sys.executable
578627
env = dict(os.environ)
579628
if "__PYVENV_LAUNCHER__" in env:
580629
del env["__PYVENV_LAUNCHER__"]
581630

582-
def check_deps():
583-
handler = PythonHandler()
584-
question = """
585-
import pkg_resources
586-
import sys
587-
try:
588-
pkg_resources.working_set.require({0})
589-
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict) as error:
590-
sys.stderr.write(str(error) + "\\n\\n")
591-
sys.stderr.flush()
592-
raise SystemExit(1)
593-
""".format(
594-
deps
595-
)
596-
return handler.run_command(self.venv_python, question, check=False).returncode
597-
598-
ret = check_deps()
631+
ret = self.check_deps()
599632
if ret != 0:
600633
ret = 1
601634
reqs = None
602635
try:
636+
to_remove = self.find_deps_to_be_made_not_binary()
637+
if to_remove:
638+
cmd = [str(self.venv_python), "-m", "pip", "uninstall", "-y", *to_remove]
639+
subprocess.call(cmd, env=env)
640+
603641
reqs = tempfile.NamedTemporaryFile(
604642
delete=False, suffix="venvstarter_requirements", dir="."
605643
)
606644
reqs.write("\n".join(str(dep) for dep in self.deps).encode("utf-8"))
645+
for name in self.no_binary:
646+
reqs.write(f"\n--no-binary {name}".encode("utf-8"))
607647
reqs.close()
608648

609649
cmd = [str(self.venv_python), "-m", "pip", "install", "-r", reqs.name]
610650
ret = subprocess.call(cmd, env=env)
611651
finally:
612-
reqs_loc = Path(reqs.name)
652+
if reqs is not None:
653+
reqs_loc = Path(reqs.name)
613654
if reqs is not None and reqs_loc.exists():
614655
reqs_loc.unlink()
615656

616657
if ret != 0:
617658
raise SystemExit(1)
618659

619-
ret = check_deps()
660+
ret = self.check_deps()
620661
if ret != 0:
621662
raise Exception("Couldn't install the requirements")
622663

@@ -730,6 +771,7 @@ def __init__(self, program, here=None):
730771

731772
self._env = []
732773
self._deps = []
774+
self._no_binary = []
733775
self._max_python = None
734776
self._min_python = None
735777
self._venv_folder = NotSpecified
@@ -755,6 +797,10 @@ def add_pypi_deps(self, *deps):
755797
self._deps.extend(deps)
756798
return self
757799

800+
def add_no_binary(self, *no_binary):
801+
self._no_binary.extend(no_binary)
802+
return self
803+
758804
def add_requirements_file(self, *parts):
759805
home = Path.home()
760806

@@ -844,6 +890,7 @@ def run(self):
844890
self.venv_folder_name,
845891
env=self._env,
846892
deps=self._deps,
893+
no_binary=self._no_binary,
847894
min_python_version=self._min_python,
848895
max_python_version=self._max_python,
849896
).run()

0 commit comments

Comments
 (0)