def test_jail_install_command(temp_path: Path, caplog): caplog.set_level(logging.DEBUG) venv_path = temp_path / 'venv' bin_path = temp_path / 'bin' bin_path.mkdir() config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), 'bin': str(bin_path), }) command = JailInstallCommand(argv=['pycodestyle==2.5.0'], config=config) result = command() entrypoint_filename = 'pycodestyle' if IS_WINDOWS: entrypoint_filename += '.exe' assert result is True assert (bin_path / entrypoint_filename).exists() venv = VEnv(path=venv_path) assert venv.exists() assert (venv.bin_path / entrypoint_filename).exists() assert (venv.lib_path / 'pycodestyle-2.5.0.dist-info').exists()
def install(self) -> int: venv = VEnv(path=self.path) if not venv.exists(): print('creating venv...', file=self.stream) venv.create() bin_path = venv.bin_path assert bin_path if not (bin_path / 'wheel').exists(): print('installing wheel...', file=self.stream) self._pip_install('pip', 'wheel', 'setuptools') constr = self.root / 'requirements.txt' if constr.exists(): print('installing requirements.txt...', file=self.stream) self._pip_install('-r', str(constr)) try: print('installing project deps...', file=self.stream) flit.main([ 'install', '--python', str(venv.python_path), '--deps', 'production', '--extras', self.name, '--symlink', ]) except SystemExit as exc: return exc.code return 0
def test_deps_audit_command(temp_path: Path, capsys): reqs_path = temp_path / 'requirements.txt' reqs_path.write_text('six==1.12.0') venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False venv.create(python_path=sys.executable) config = Config() config.attach({ 'level': 'WARNING', 'silent': True, 'nocolors': True, }) command = DepsAuditCommand(argv=['jinja2==2.0'], config=config) result = command() captured = capsys.readouterr() print(captured.err) print(captured.out) assert result is False output = json.loads(captured.out) assert len(output) >= 2 for entry in output: assert entry['current'] == '2.0' assert entry['name'] == 'jinja2'
def run(self, exe, *cmd: str) -> int: venv = VEnv(path=self.path) bin_path = venv.bin_path if not bin_path: code = self.install() if code != 0: return code venv = VEnv(path=self.path) bin_path = venv.bin_path assert bin_path full_cmd = [str(bin_path / exe)] full_cmd.extend(cmd) result = subprocess.run(full_cmd) return result.returncode
def test_package_install_command(temp_path: Path): venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False venv.create(python_path=sys.executable) config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), }) command = PackageInstallCommand(argv=['six==1.12.0'], config=config) result = command() assert result is True assert (venv.lib_path / 'six-1.12.0.dist-info' / 'METADATA').exists()
def test_inspect_venv_command(temp_path: Path, capsys): venv = VEnv(path=temp_path) venv.create(python_path=sys.executable) config = Config() config.attach({ 'project': str(temp_path), 'venv': str(temp_path), }) command = InspectVenvCommand(argv=[], config=config) result = command() assert result is True captured = capsys.readouterr() output = json.loads(captured.out) assert output['exists'] is True assert output['bin'] == str(venv.bin_path)
def test_deps_install_command(temp_path: Path): reqs_path = temp_path / 'requirements.txt' reqs_path.write_text('six==1.12.0') venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False venv.create(python_path=sys.executable) config = Config() config.attach({ 'from': dict(format='pip', path=str(reqs_path)), 'project': str(temp_path), 'venv': str(venv_path), }) command = DepsInstallCommand(argv=[], config=config) result = command() assert result is True assert (venv.lib_path / 'six-1.12.0.dist-info' / 'METADATA').exists()
def test_create(temp_path): venv = VEnv(path=temp_path) assert venv.exists() is False assert venv.lib_path is None assert venv.bin_path is None assert venv.python_path is None venv.create() assert venv.exists() is True assert venv.lib_path.exists() assert venv.bin_path.exists() assert (venv.bin_path / 'pip').exists() or (venv.bin_path / 'pip.exe').exists() assert venv.python_path.exists() venv.destroy() assert venv.exists() is False
def test_jail_install_command(temp_path: Path): venv_path = temp_path / 'venv' bin_path = temp_path / 'bin' bin_path.mkdir() config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), 'bin': str(bin_path), }) command = JailInstallCommand(argv=['pycodestyle==2.5.0'], config=config) result = command() assert result is True assert (bin_path / 'pycodestyle').exists() venv = VEnv(path=venv_path) assert venv.exists() assert (venv.bin_path / 'pycodestyle').exists() assert (venv.lib_path / 'pycodestyle-2.5.0.dist-info').exists()
def test_deps_outdated_command_venv(temp_path: Path, capsys): venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False venv.create(python_path=sys.executable) config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), 'level': 'WARNING', 'silent': True, }) command = DepsOutdatedCommand(argv=[], config=config) result = command() assert type(result) is bool if result is False: captured = capsys.readouterr() output = json.loads(captured.out) names = {line['name'] for line in output} assert len(names - {'pip', 'setuptools'}) == 0
def test_venv_destroy_command(temp_path: Path): venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False venv.create(python_path=sys.executable) config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), }) command = VenvDestroyCommand(argv=[], config=config) result = command() assert result is True venv = VEnv(path=venv_path) assert venv.exists() is False
def test_venv_create_command(temp_path: Path): venv_path = temp_path / 'venv' venv = VEnv(path=venv_path) assert venv.exists() is False config = Config() config.attach({ 'project': str(temp_path), 'venv': str(venv_path), }) command = VenvCreateCommand(argv=[], config=config) result = command() assert result is True venv = VEnv(path=venv_path) assert venv.exists() is True
def __call__(self) -> bool: resolver = get_resolver(reqs=self.args.name) name = next(iter(resolver.graph.get_layer(0))).dependencies[0].name command = self.config.get('command') if not command: command = 'python' if isinstance(command, str): command = shlex.split(command) with TemporaryDirectory() as base_path: base_path = Path(base_path) # make venv venv = VEnv(path=base_path) if venv.exists(): self.logger.error('already installed', extra=dict(package=name)) return False python = get_python(self.config) self.logger.info('creating venv...', extra=dict( venv=str(venv.path), python=str(python.path), )) venv.create(python_path=python.path) # install ok = install_deps( resolver=resolver, python_path=venv.python_path, logger=self.logger, silent=self.config['silent'], ) if not ok: return False # install executable executable = venv.bin_path / command[0] if not executable.exists(): self.logger.warning('executable is not found in venv, trying to install...', extra=dict( executable=command[0], )) ok = install_dep( name=command[0], python_path=venv.python_path, logger=self.logger, silent=self.config['silent'], ) if not ok: return False if not executable.exists(): self.logger.error('package installed, but executable is not found') return False # make startup script to import installed packages startup_path = base_path / '_startup.py' packages = self._get_startup_packages(lib_path=venv.lib_path, packages=self.args.name) if not packages: self.logger.error('cannot find any packages') return False startup_path.write_text('import ' + ', '.join(sorted(packages))) # run self.logger.info('running...') with override_env_vars({'PYTHONSTARTUP': str(startup_path)}): result = subprocess.run([str(executable)] + command[1:]) if result.returncode != 0: self.logger.error('command failed', extra=dict(code=result.returncode)) return False return True
def __call__(self) -> bool: # load project loader = CONVERTERS[self.config['from']['format']] loader = loader.copy(project_path=Path(self.config['project'])) resolver = loader.load_resolver(path=self.config['from']['path']) if loader.lock: self.logger.warning('do not build project from lockfile!') # attach merged = attach_deps(resolver=resolver, config=self.config, merge=True) if not merged: conflict = analyze_conflict(resolver=resolver) self.logger.warning('conflict was found') print(conflict) return False # dump project_path = Path(self.config['project']) reqs = Requirement.from_graph(resolver.graph, lock=False) self.logger.info('creating wheel...') dumper = WheelConverter() project = resolver.graph.metainfo dumper.dump(path=project_path / 'dist', reqs=reqs, project=project) wheel_path = dumper._get_path(path=project_path / 'dist', project=project) # get command command = self.args.name if not command: command = self.config.get('command') if not command: self.logger.error('command required') return False if isinstance(command, str): command = shlex.split(command) # choose pythons self.logger.info('get interpreters') pythons = Pythons() choosen_pythons: Tuple[Python, ...] if 'python' in self.config: # get from config choosen_pythons = (pythons.get_best(self.config['python']), ) else: # get from project pythons_by_version = dict() # type: Dict[str, Python] python_constraint = resolver.graph.metainfo.python for python in pythons: version = str(python.get_short_version()) if version in pythons_by_version: continue if python.version not in python_constraint: continue pythons_by_version[version] = python choosen_pythons = tuple(pythons_by_version.values()) for python in choosen_pythons: with TemporaryDirectory( ) as root_path: # type: Path # type: ignore root_path = Path(root_path) # make venv self.logger.info('create venv', extra=dict(python=str(python.version))) venv = VEnv(path=root_path / 'venv') venv.create(python_path=python.path) # copy tests for path in self.config['tests']: # type: Path self.logger.info('copy files', extra=dict(path=path)) path = Path(path) if not path.exists(): raise FileNotFoundError(str(path)) # copy file if path.is_file(): shutil.copyfile(str(path), str(root_path / path.name)) continue # copy dir for subpath in path.glob('**/*'): if not subpath.is_file(): continue if '__pycache__' in subpath.parts: continue new_path = subpath.resolve().relative_to( self.config['project']) new_path = root_path.joinpath(new_path) self.logger.debug('copy', extra=dict(old=str(subpath), new=str(new_path))) new_path.parent.mkdir(exist_ok=True, parents=True) shutil.copyfile(str(subpath), str(new_path)) # install project self.logger.info('install project', extra=dict(path=str(wheel_path))) dep_spec = str(wheel_path) extras = set(self.config.get('envs', [])) - {'main'} if extras: dep_spec += '[{}]'.format(','.join(extras)) # we are using pip here to make it closer to the real installation result = subprocess.run( [str(venv.bin_path / 'pip'), 'install', dep_spec], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) if result.returncode != 0: self.logger.error('failed to install project') self.logger.error(result.stderr.decode()) return False # install executable executable = venv.bin_path / command[0] if not executable.exists(): self.logger.info('executable not found, installing', extra=dict(executable=command[0], )) result = subprocess.run( [str(venv.bin_path / 'pip'), 'install', command[0]], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) if result.returncode != 0: self.logger.error('failed to install tests executable') self.logger.error(result.stderr.decode()) return False # run tests self.logger.info('run tests', extra=dict(command=command)) result = subprocess.run( [str(executable)] + command[1:], cwd=str(root_path), ) if result.returncode != 0: self.logger.error('command failed, stopping') return False return True
def _pip_install(self, *args): venv = VEnv(path=self.path) cmd = [str(venv.python_path), '-m', 'pip', 'install', '-U'] cmd.extend(args) result = subprocess.run(cmd, stdout=subprocess.DEVNULL) result.check_returncode()