@@ -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