def get_package_vcs_properties_from_path(cls, src: Path) -> tuple[str, str, str]: from poetry.core.vcs.git import Git git = Git() revision = git.rev_parse("HEAD", src).strip() url = git.remote_url(src) return "git", url, revision
def set_package_vcs_properties_from_path(cls, src: Path, package: Package) -> None: from poetry.core.vcs.git import Git git = Git() revision = git.rev_parse("HEAD", src).strip() url = git.remote_url(src) package._source_type = "git" package._source_url = url package._source_reference = revision
def _parse_dependency_specification_git_url(requirement: str, env: Env | None = None ) -> DependencySpec | None: from poetry.core.vcs.git import Git from poetry.core.vcs.git import ParsedUrl parsed = ParsedUrl.parse(requirement) url = Git.normalize_url(requirement) pair = {"name": parsed.name, "git": url.url} if parsed.rev: pair["rev"] = url.revision if parsed.subdirectory: pair["subdirectory"] = parsed.subdirectory source_root = env.path.joinpath("src") if env else None package = Provider.get_package_from_vcs( "git", url=url.url, rev=pair.get("rev"), subdirectory=parsed.subdirectory, source_root=source_root, ) pair["name"] = package.name return pair
def get_package_from_vcs( cls, vcs, url, branch=None, tag=None, rev=None, name=None ): # type: (str, str, Optional[str], Optional[str], Optional[str], Optional[str]) -> Package if vcs != "git": raise ValueError("Unsupported VCS dependency {}".format(vcs)) tmp_dir = Path( mkdtemp(prefix="pypoetry-git-{}".format(url.split("/")[-1].rstrip(".git"))) ) try: git = Git() git.clone(url, tmp_dir) reference = branch or tag or rev if reference is not None: git.checkout(reference, tmp_dir) else: reference = "HEAD" revision = git.rev_parse(reference, tmp_dir).strip() package = cls.get_package_from_directory(tmp_dir, name=name) package._source_type = "git" package._source_url = url package._source_reference = reference package._source_resolved_reference = revision except Exception: raise finally: safe_rmtree(str(tmp_dir)) return package
def test_ensure_existing_git_executable_is_found(mocker): mock = mocker.patch.object(subprocess, "check_output", return_value=b"") Git().run("config") cmd = Path(mock.call_args_list[-1][0][0][0]) assert cmd.is_absolute() assert cmd.name == "git.exe"
def install_git(self, package: Package) -> None: from poetry.core.packages.package import Package from poetry.core.vcs.git import Git src_dir = self._env.path / "src" / package.name if src_dir.exists(): safe_rmtree(str(src_dir)) src_dir.parent.mkdir(exist_ok=True) git = Git() git.clone(package.source_url, src_dir) reference = package.source_resolved_reference if not reference: reference = package.source_reference git.checkout(reference, src_dir) # Now we just need to install from the source directory pkg = Package(package.name, package.version) pkg._source_type = "directory" pkg._source_url = str(src_dir) pkg.develop = package.develop self.install_directory(pkg)
def test_ensure_absolute_path_to_git(mocker): _reset_executable() def checkout_output(cmd, *args, **kwargs): if Path(cmd[0]).name == "where.exe": return "\n".join( [str(Path.cwd().joinpath("git.exe")), "C:\\Git\\cmd\\git.exe"]) return b"" mock = mocker.patch.object(subprocess, "check_output", side_effect=checkout_output) Git().run("config") assert mock.call_args_list[-1][0][0] == [ "C:\\Git\\cmd\\git.exe", "config", ]
def get_vcs(directory: Path) -> Git | None: working_dir = Path.cwd() os.chdir(str(directory.resolve())) vcs: Git | None try: from poetry.core.vcs.git import executable git_dir = (subprocess.check_output( [executable(), "rev-parse", "--show-toplevel"], stderr=subprocess.STDOUT).decode().strip()) vcs = Git(Path(git_dir)) except (subprocess.CalledProcessError, OSError, RuntimeError): vcs = None finally: os.chdir(str(working_dir)) return vcs
def get_package_from_vcs( cls, vcs: str, url: str, branch: Optional[str] = None, tag: Optional[str] = None, rev: Optional[str] = None, name: Optional[str] = None, ) -> "Package": if vcs != "git": raise ValueError(f"Unsupported VCS dependency {vcs}") suffix = url.split("/")[-1].rstrip(".git") tmp_dir = Path(mkdtemp(prefix=f"pypoetry-git-{suffix}")) try: git = Git() git.clone(url, tmp_dir) reference = branch or tag or rev if reference is not None: git.checkout(reference, tmp_dir) else: reference = "HEAD" revision = git.rev_parse(reference, tmp_dir).strip() package = cls.get_package_from_directory(tmp_dir, name=name) package._source_type = "git" package._source_url = url package._source_reference = reference package._source_resolved_reference = revision except Exception: raise finally: safe_rmtree(str(tmp_dir)) return package
def solve(self, use_latest=None): # type: (...) -> List[Operation] with self._provider.progress(): start = time.time() packages, depths = self._solve(use_latest=use_latest) end = time.time() if len(self._overrides) > 1: self._provider.debug( "Complete version solving took {:.3f} seconds with {} overrides" .format(end - start, len(self._overrides))) self._provider.debug("Resolved with overrides: {}".format( ", ".join("({})".format(b) for b in self._overrides))) operations = [] for i, package in enumerate(packages): installed = False for pkg in self._installed.packages: if package.name == pkg.name: installed = True if pkg.source_type == "git" and package.source_type == "git": from poetry.core.vcs.git import Git # Trying to find the currently installed version pkg_source_url = Git.normalize_url(pkg.source_url) package_source_url = Git.normalize_url( package.source_url) for locked in self._locked.packages: if locked.name != pkg.name or locked.source_type != "git": continue locked_source_url = Git.normalize_url( locked.source_url) if (locked.name == pkg.name and locked.source_type == pkg.source_type and locked_source_url == pkg_source_url and locked.source_reference == pkg.source_reference): pkg = Package(pkg.name, locked.version) pkg.source_type = "git" pkg.source_url = locked.source_url pkg.source_reference = locked.source_reference break if pkg_source_url != package_source_url or ( pkg.source_reference != package.source_reference and not pkg.source_reference.startswith( package.source_reference)): operations.append( Update(pkg, package, priority=depths[i])) else: operations.append( Install(package).skip("Already installed")) elif package.version != pkg.version: # Checking version operations.append( Update(pkg, package, priority=depths[i])) elif pkg.source_type and package.source_type != pkg.source_type: operations.append( Update(pkg, package, priority=depths[i])) else: operations.append( Install( package, priority=depths[i]).skip("Already installed")) break if not installed: operations.append(Install(package, priority=depths[i])) # Checking for removals for pkg in self._locked.packages: remove = True for package in packages: if pkg.name == package.name: remove = False break if remove: skip = True for installed in self._installed.packages: if installed.name == pkg.name: skip = False break op = Uninstall(pkg) if skip: op.skip("Not currently installed") operations.append(op) if self._remove_untracked: locked_names = {locked.name for locked in self._locked.packages} for installed in self._installed.packages: if installed.name == self._package.name: continue if installed.name in Provider.UNSAFE_PACKAGES: # Never remove pip, setuptools etc. continue if installed.name not in locked_names: operations.append(Uninstall(installed)) return sorted( operations, key=lambda o: ( -o.priority, o.package.name, o.package.version, ), )
def _parse_requirements(self, requirements: List[str]) -> List[Dict[str, str]]: from poetry.puzzle.provider import Provider result = [] try: cwd = self.poetry.file.parent except (PyProjectException, RuntimeError): cwd = Path.cwd() for requirement in requirements: requirement = requirement.strip() extras = [] extras_m = re.search(r"\[([\w\d,-_ ]+)\]$", requirement) if extras_m: extras = [e.strip() for e in extras_m.group(1).split(",")] requirement, _ = requirement.split("[") url_parsed = urllib.parse.urlparse(requirement) if url_parsed.scheme and url_parsed.netloc: # Url if url_parsed.scheme in ["git+https", "git+ssh"]: from poetry.core.vcs.git import Git from poetry.core.vcs.git import ParsedUrl parsed = ParsedUrl.parse(requirement) url = Git.normalize_url(requirement) pair = dict([("name", parsed.name), ("git", url.url)]) if parsed.rev: pair["rev"] = url.revision if extras: pair["extras"] = extras package = Provider.get_package_from_vcs( "git", url.url, rev=pair.get("rev") ) pair["name"] = package.name result.append(pair) continue elif url_parsed.scheme in ["http", "https"]: package = Provider.get_package_from_url(requirement) pair = dict([("name", package.name), ("url", package.source_url)]) if extras: pair["extras"] = extras result.append(pair) continue elif (os.path.sep in requirement or "/" in requirement) and cwd.joinpath( requirement ).exists(): path = cwd.joinpath(requirement) if path.is_file(): package = Provider.get_package_from_file(path.resolve()) else: package = Provider.get_package_from_directory(path) result.append( dict( [ ("name", package.name), ("path", path.relative_to(cwd).as_posix()), ] + ([("extras", extras)] if extras else []) ) ) continue pair = re.sub( "^([^@=: ]+)(?:@|==|(?<![<>~!])=|:| )(.*)$", "\\1 \\2", requirement ) pair = pair.strip() require = dict() if " " in pair: name, version = pair.split(" ", 2) extras_m = re.search(r"\[([\w\d,-_]+)\]$", name) if extras_m: extras = [e.strip() for e in extras_m.group(1).split(",")] name, _ = name.split("[") require["name"] = name if version != "latest": require["version"] = version else: m = re.match( r"^([^><=!: ]+)((?:>=|<=|>|<|!=|~=|~|\^).*)$", requirement.strip() ) if m: name, constraint = m.group(1), m.group(2) extras_m = re.search(r"\[([\w\d,-_]+)\]$", name) if extras_m: extras = [e.strip() for e in extras_m.group(1).split(",")] name, _ = name.split("[") require["name"] = name require["version"] = constraint else: extras_m = re.search(r"\[([\w\d,-_]+)\]$", pair) if extras_m: extras = [e.strip() for e in extras_m.group(1).split(",")] pair, _ = pair.split("[") require["name"] = pair if extras: require["extras"] = extras result.append(require) return result
def load(cls, env): # type: (Env) -> InstalledRepository """ Load installed packages. """ repo = cls() seen = set() for entry in reversed(env.sys_path): for distribution in sorted( metadata.distributions(path=[entry]), key=lambda d: str(d._path), ): name = distribution.metadata["name"] path = Path(str(distribution._path)) version = distribution.metadata["version"] package = Package(name, version, version) package.description = distribution.metadata.get("summary", "") if package.name in seen: continue try: path.relative_to(_VENDORS) except ValueError: pass else: continue seen.add(package.name) repo.add_package(package) is_standard_package = True try: path.relative_to(env.site_packages) except ValueError: is_standard_package = False if is_standard_package: if path.name.endswith(".dist-info"): paths = cls.get_package_paths( sitedir=env.site_packages, name=package.pretty_name) if paths: # TODO: handle multiple source directories? package.source_type = "directory" package.source_url = paths.pop().as_posix() continue src_path = env.path / "src" # A VCS dependency should have been installed # in the src directory. If not, it's a path dependency try: path.relative_to(src_path) from poetry.core.vcs.git import Git git = Git() revision = git.rev_parse("HEAD", src_path / package.name).strip() url = git.remote_url(src_path / package.name) package.source_type = "git" package.source_url = url package.source_reference = revision except ValueError: package.source_type = "directory" package.source_url = str(path.parent) return repo
def test_git_rev_parse_raises_error_on_invalid_repository() -> None: with pytest.raises(GitError): Git().rev_parse("-u./payload")
def test_git_checkout_raises_error_on_invalid_repository() -> None: with pytest.raises(GitError): Git().checkout("-u./payload")
def test_git_clone_raises_error_on_invalid_repository() -> None: with pytest.raises(GitError): Git().clone("-u./payload", Path("foo"))
def test_normalize_url(url: str, normalized: GitUrl) -> None: assert normalized == Git.normalize_url(url)
def test_normalize_url(url, normalized): assert normalized == Git.normalize_url(url)