def test_env_finds_the_correct_executables_for_generic_env( tmp_dir: str, manager: EnvManager ): venv_path = Path(tmp_dir) / "Virtual Env" child_venv_path = Path(tmp_dir) / "Child Virtual Env" manager.build_venv(str(venv_path), with_pip=True) parent_venv = VirtualEnv(venv_path) manager.build_venv( str(child_venv_path), executable=parent_venv.python, with_pip=True ) venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) expected_executable = ( f"python{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}" ) expected_pip_executable = ( f"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}" ) if WINDOWS: expected_executable = "python.exe" expected_pip_executable = "pip.exe" assert Path(venv.python).name == expected_executable assert Path(venv.pip).name == expected_pip_executable
def _pep517_metadata(cls, path): # type (Path) -> PackageInfo """ Helper method to use PEP-517 library to build and read package metadata. :param path: Path to package source to build and read metadata for. """ info = None try: info = cls.from_setup_files(path) if info.requires_dist is not None: return info except PackageInfoError: pass with temporary_directory() as tmp_dir: # TODO: cache PEP 517 build environment corresponding to each project venv venv_dir = Path(tmp_dir) / ".venv" EnvManager.build_venv(venv_dir.as_posix()) venv = VirtualEnv(venv_dir, venv_dir) dest_dir = Path(tmp_dir) / "dist" dest_dir.mkdir() try: venv.run("python", "-m", "pip", "install", "--disable-pip-version-check", "--ignore-installed", *PEP517_META_BUILD_DEPS) venv.run( "python", "-", input_=PEP517_META_BUILD.format(source=path.as_posix(), dest=dest_dir.as_posix()), ) return cls.from_metadata(dest_dir) except EnvCommandError as e: # something went wrong while attempting pep517 metadata build # fallback to egg_info if setup.py available cls._log("PEP517 build failed: {}".format(e), level="debug") setup_py = path / "setup.py" if not setup_py.exists(): raise PackageInfoError(path) cwd = Path.cwd() os.chdir(path.as_posix()) try: venv.run("python", "setup.py", "egg_info") return cls.from_metadata(path) except EnvCommandError: raise PackageInfoError(path) finally: os.chdir(cwd.as_posix()) if info: cls._log( "Falling back to parsed setup.py file for {}".format(path), "debug") return info # if we reach here, everything has failed and all hope is lost raise PackageInfoError(path)
def test_env_commands_with_spaces_in_their_arg_work_as_expected( tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) assert venv.run("python", venv.pip, "--version", shell=True).startswith(f"pip {venv.pip_version} from ")
def test_env_finds_the_correct_executables(tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path), with_pip=True) venv = VirtualEnv(venv_path) default_executable = expected_executable = f"python{'.exe' if WINDOWS else ''}" default_pip_executable = expected_pip_executable = f"pip{'.exe' if WINDOWS else ''}" major_executable = f"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}" major_pip_executable = f"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''}" if ( venv._bin_dir.joinpath(default_executable).exists() and venv._bin_dir.joinpath(major_executable).exists() ): venv._bin_dir.joinpath(default_executable).unlink() expected_executable = major_executable if ( venv._bin_dir.joinpath(default_pip_executable).exists() and venv._bin_dir.joinpath(major_pip_executable).exists() ): venv._bin_dir.joinpath(default_pip_executable).unlink() expected_pip_executable = major_pip_executable venv = VirtualEnv(venv_path) assert Path(venv.python).name == expected_executable assert Path(venv.pip).name.startswith(expected_pip_executable.split(".")[0])
def tmp_venv(tmp_dir: str) -> Iterator[VirtualEnv]: venv_path = Path(tmp_dir) / "venv" EnvManager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) yield venv shutil.rmtree(str(venv.path))
def test_virtualenvs_with_spaces_in_their_path_work_as_expected( tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) assert venv.run("python", "-V", shell=True).startswith("Python")
def test_env_get_supported_tags_matches_inside_virtualenv( tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) import packaging.tags assert venv.get_supported_tags() == list(packaging.tags.sys_tags())
def test_env_shell_commands_with_stdinput_in_their_arg_work_as_expected( tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) run_output_path = Path( venv.run("python", "-", input_=GET_BASE_PREFIX, shell=True).strip()) venv_base_prefix_path = Path(str(venv.get_base_prefix())) assert run_output_path.resolve() == venv_base_prefix_path.resolve()
def tmp_venv(tmp_dir: str, env_manager: EnvManager) -> VirtualEnv: venv_path = Path(tmp_dir) / "venv" env_manager.build_venv(str(venv_path)) venv = VirtualEnv(venv_path) yield venv shutil.rmtree(str(venv.path))
def _sane_env(poetry, build_dir, io, clean=False): "Yield a sane virtual environment in build_dir." manager = EnvManager(poetry) if os.path.isdir(build_dir): env = VirtualEnv(build_dir) if clean or (not env.is_sane()): io.write_line(f"removing env {build_dir}") manager.remove_venv(build_dir) if not os.path.isdir(build_dir): io.write_line(f"building env {build_dir}") manager.build_venv(build_dir, executable=None) return VirtualEnv(build_dir)
def test_env_finds_fallback_executables_for_generic_env( tmp_dir: str, manager: EnvManager): venv_path = Path(tmp_dir) / "Virtual Env" child_venv_path = Path(tmp_dir) / "Child Virtual Env" manager.build_venv(str(venv_path), with_pip=True) parent_venv = VirtualEnv(venv_path) manager.build_venv(str(child_venv_path), executable=parent_venv.python, with_pip=True) venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) default_executable = f"python{'.exe' if WINDOWS else ''}" major_executable = f"python{sys.version_info[0]}{'.exe' if WINDOWS else ''}" minor_executable = ( f"python{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}" ) expected_executable = minor_executable if (venv._bin_dir.joinpath(expected_executable).exists() and venv._bin_dir.joinpath(major_executable).exists()): venv._bin_dir.joinpath(expected_executable).unlink() expected_executable = major_executable if (venv._bin_dir.joinpath(expected_executable).exists() and venv._bin_dir.joinpath(default_executable).exists()): venv._bin_dir.joinpath(expected_executable).unlink() expected_executable = default_executable default_pip_executable = f"pip{'.exe' if WINDOWS else ''}" major_pip_executable = f"pip{sys.version_info[0]}{'.exe' if WINDOWS else ''}" minor_pip_executable = ( f"pip{sys.version_info[0]}.{sys.version_info[1]}{'.exe' if WINDOWS else ''}" ) expected_pip_executable = minor_pip_executable if (venv._bin_dir.joinpath(expected_pip_executable).exists() and venv._bin_dir.joinpath(major_pip_executable).exists()): venv._bin_dir.joinpath(expected_pip_executable).unlink() expected_pip_executable = major_pip_executable if (venv._bin_dir.joinpath(expected_pip_executable).exists() and venv._bin_dir.joinpath(default_pip_executable).exists()): venv._bin_dir.joinpath(expected_pip_executable).unlink() expected_pip_executable = default_pip_executable if not venv._bin_dir.joinpath(expected_executable).exists(): expected_executable = default_executable if not venv._bin_dir.joinpath(expected_pip_executable).exists(): expected_pip_executable = default_pip_executable venv = GenericEnv(parent_venv.path, child_env=VirtualEnv(child_venv_path)) assert Path(venv.python).name == expected_executable assert Path(venv.pip).name == expected_pip_executable
def test_venv_backup_exclusion(tmp_dir: str, manager: EnvManager): import xattr venv_path = Path(tmp_dir) / "Virtual Env" manager.build_venv(str(venv_path)) value = ( b"bplist00_\x10\x11com.apple.backupd" b"\x08\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00" b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c" ) assert (xattr.getxattr( str(venv_path), "com.apple.metadata:com_apple_backup_excludeItem") == value)
def get_package_from_directory( cls, directory, name=None ): # type: (Path, Optional[str]) -> Package supports_poetry = False pyproject = directory.joinpath("pyproject.toml") if pyproject.exists(): pyproject = TomlFile(pyproject) pyproject_content = pyproject.read() supports_poetry = ( "tool" in pyproject_content and "poetry" in pyproject_content["tool"] ) if supports_poetry: poetry = Factory().create_poetry(directory) pkg = poetry.package package = Package(pkg.name, pkg.version) for dep in pkg.requires: if not dep.is_optional(): package.requires.append(dep) for extra, deps in pkg.extras.items(): if extra not in package.extras: package.extras[extra] = [] for dep in deps: package.extras[extra].append(dep) package.python_versions = pkg.python_versions else: # Execute egg_info current_dir = os.getcwd() os.chdir(str(directory)) try: with temporary_directory() as tmp_dir: EnvManager.build_venv(tmp_dir) venv = VirtualEnv(Path(tmp_dir), Path(tmp_dir)) venv.run("python", "setup.py", "egg_info") except EnvCommandError: result = SetupReader.read_from_directory(directory) if not result["name"]: # The name could not be determined # We use the dependency name result["name"] = name if not result["version"]: # The version could not be determined # so we raise an error since it is mandatory raise RuntimeError( "Unable to retrieve the package version for {}".format( directory ) ) package_name = result["name"] package_version = result["version"] python_requires = result["python_requires"] if python_requires is None: python_requires = "*" package_summary = "" requires = "" for dep in result["install_requires"]: requires += dep + "\n" if result["extras_require"]: requires += "\n" for extra_name, deps in result["extras_require"].items(): requires += "[{}]\n".format(extra_name) for dep in deps: requires += dep + "\n" requires += "\n" reqs = parse_requires(requires) else: os.chdir(current_dir) # Sometimes pathlib will fail on recursive # symbolic links, so we need to workaround it # and use the glob module instead. # Note that this does not happen with pathlib2 # so it's safe to use it for Python < 3.4. if PY35: egg_info = next( Path(p) for p in glob.glob( os.path.join(str(directory), "**", "*.egg-info"), recursive=True, ) ) else: egg_info = next(directory.glob("**/*.egg-info")) meta = pkginfo.UnpackedSDist(str(egg_info)) package_name = meta.name package_version = meta.version package_summary = meta.summary python_requires = meta.requires_python if meta.requires_dist: reqs = list(meta.requires_dist) else: reqs = [] requires = egg_info / "requires.txt" if requires.exists(): with requires.open(encoding="utf-8") as f: reqs = parse_requires(f.read()) finally: os.chdir(current_dir) package = Package(package_name, package_version) package.description = package_summary for req in reqs: dep = dependency_from_pep_508(req) if dep.in_extras: for extra in dep.in_extras: if extra not in package.extras: package.extras[extra] = [] package.extras[extra].append(dep) if not dep.is_optional(): package.requires.append(dep) if python_requires: package.python_versions = python_requires if name and name != package.name: # For now, the dependency's name must match the actual package's name raise RuntimeError( "The dependency name for {} does not match the actual package's name: {}".format( name, package.name ) ) package.source_type = "directory" package.source_url = directory.as_posix() return package
def _execute_setup(self): with temporary_directory() as tmp_dir: EnvManager.build_venv(tmp_dir) venv = VirtualEnv(Path(tmp_dir), Path(tmp_dir)) venv.run("python", "setup.py", "egg_info")