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 mock_clone(_, source, dest): # Checking source to determine which folder we need to copy parsed = ParsedUrl.parse(source) folder = (Path(__file__).parent / "fixtures" / "git" / parsed.resource / parsed.pathname.lstrip("/").rstrip(".git")) copy_or_symlink(folder, dest)
def test_parse_url(url: str, parsed: ParsedUrl) -> None: result = ParsedUrl.parse(url) assert parsed.name == result.name assert parsed.pathname == result.pathname assert parsed.port == result.port assert parsed.protocol == result.protocol assert parsed.resource == result.resource assert parsed.rev == result.rev assert parsed.url == result.url assert parsed.user == result.user
def test_parse_url(url, parsed): result = ParsedUrl.parse(url) assert parsed.name == result.name assert parsed.pathname == result.pathname assert parsed.port == result.port assert parsed.protocol == result.protocol assert parsed.resource == result.resource assert parsed.rev == result.rev assert parsed.url == result.url assert parsed.user == result.user assert parsed.password == result.password
def mock_clone( url: str, *_: Any, source_root: Path | None = None, **__: Any, ) -> MockDulwichRepo: # Checking source to determine which folder we need to copy parsed = ParsedUrl.parse(url) path = re.sub(r"(.git)?$", "", parsed.pathname.lstrip("/")) folder = Path(__file__).parent / "fixtures" / "git" / parsed.resource / path if not source_root: source_root = Path(Config.create().get("cache-dir")) / "src" dest = source_root / path dest.parent.mkdir(parents=True, exist_ok=True) copy_or_symlink(folder, dest) return MockDulwichRepo(dest)
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 dependency_from_pep_508(name): from poetry.core.vcs.git import ParsedUrl # Removing comments parts = name.split("#", 1) name = parts[0].strip() if len(parts) > 1: rest = parts[1] if ";" in rest: name += ";" + rest.split(";", 1)[1] req = Requirement(name) if req.marker: markers = convert_markers(req.marker) else: markers = {} name = req.name path = os.path.normpath(os.path.abspath(name)) link = None if is_url(name): link = Link(name) elif req.url: link = Link(req.url) else: p, extras = strip_extras(path) if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): if not is_installable_dir(p): raise ValueError( "Directory {!r} is not installable. File 'setup.py' " "not found.".format(name)) link = Link(path_to_url(p)) elif is_archive_file(p): link = Link(path_to_url(p)) # it's a local file, dir, or url if link: # Handle relative file URLs if link.scheme == "file" and re.search(r"\.\./", link.url): link = Link( path_to_url(os.path.normpath(os.path.abspath(link.path)))) # wheel file if link.is_wheel: m = wheel_file_re.match(link.filename) if not m: raise ValueError("Invalid wheel name: {}".format( link.filename)) name = m.group("name") version = m.group("ver") dep = Dependency(name, version) else: name = req.name or link.egg_fragment if link.scheme.startswith("git+"): url = ParsedUrl.parse(link.url) dep = VCSDependency(name, "git", url.url, rev=url.rev) elif link.scheme == "git": dep = VCSDependency(name, "git", link.url_without_fragment) elif link.scheme in ["http", "https"]: dep = URLDependency(name, link.url_without_fragment) else: dep = Dependency(name, "*") else: if req.pretty_constraint: constraint = req.constraint else: constraint = "*" dep = Dependency(name, constraint) if "extra" in markers: # If we have extras, the dependency is optional dep.deactivate() for or_ in markers["extra"]: for _, extra in or_: dep.in_extras.append(extra) if "python_version" in markers: ors = [] for or_ in markers["python_version"]: ands = [] for op, version in or_: # Expand python version if op == "==": version = "~" + version op = "" elif op == "!=": version += ".*" elif op in ("<=", ">"): parsed_version = Version.parse(version) if parsed_version.precision == 1: if op == "<=": op = "<" version = parsed_version.next_major.text elif op == ">": op = ">=" version = parsed_version.next_major.text elif parsed_version.precision == 2: if op == "<=": op = "<" version = parsed_version.next_minor.text elif op == ">": op = ">=" version = parsed_version.next_minor.text elif op in ("in", "not in"): versions = [] for v in re.split("[ ,]+", version): split = v.split(".") if len(split) in [1, 2]: split.append("*") op_ = "" if op == "in" else "!=" else: op_ = "==" if op == "in" else "!=" versions.append(op_ + ".".join(split)) glue = " || " if op == "in" else ", " if versions: ands.append(glue.join(versions)) continue ands.append("{}{}".format(op, version)) ors.append(" ".join(ands)) dep.python_versions = " || ".join(ors) if req.marker: dep.marker = req.marker # Extras for extra in req.extras: dep.extras.append(extra) return dep
def test_parse_url_should_fail() -> None: url = "https://" + "@" * 64 + "!" with pytest.raises(ValueError): ParsedUrl.parse(url)
def dependency_from_pep_508(name, relative_to=None ): # type: (str, Optional[Path]) -> Dependency """ Resolve a PEP-508 requirement string to a `Dependency` instance. If a `relative_to` path is specified, this is used as the base directory if the identified dependency is of file or directory type. """ from poetry.core.vcs.git import ParsedUrl # Removing comments parts = name.split("#", 1) name = parts[0].strip() if len(parts) > 1: rest = parts[1] if " ;" in rest: name += " ;" + rest.split(" ;", 1)[1] req = Requirement(name) if req.marker: markers = convert_markers(req.marker) else: markers = {} name = req.name path = os.path.normpath(os.path.abspath(name)) link = None if is_url(name): link = Link(name) elif req.url: link = Link(req.url) else: p, extras = strip_extras(path) if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): if not is_installable_dir(p): raise ValueError( "Directory {!r} is not installable. File 'setup.py' " "not found.".format(name)) link = Link(path_to_url(p)) elif is_archive_file(p): link = Link(path_to_url(p)) # it's a local file, dir, or url if link: is_file_uri = link.scheme == "file" is_relative_uri = is_file_uri and re.search(r"\.\./", link.url) # Handle relative file URLs if is_file_uri and is_relative_uri: path = Path(link.path) if relative_to: path = relative_to / path link = Link(path_to_url(path)) # wheel file version = None if link.is_wheel: m = wheel_file_re.match(link.filename) if not m: raise ValueError("Invalid wheel name: {}".format( link.filename)) name = m.group("name") version = m.group("ver") name = req.name or link.egg_fragment dep = None if link.scheme.startswith("git+"): url = ParsedUrl.parse(link.url) dep = VCSDependency(name, "git", url.url, rev=url.rev, extras=req.extras) elif link.scheme == "git": dep = VCSDependency(name, "git", link.url_without_fragment, extras=req.extras) elif link.scheme in ["http", "https"]: dep = URLDependency(name, link.url) elif is_file_uri: # handle RFC 8089 references path = url_to_path(req.url) dep = _make_file_or_dir_dep(name=name, path=path, base=relative_to, extras=req.extras) else: try: # this is a local path not using the file URI scheme dep = _make_file_or_dir_dep( name=name, path=Path(req.url), base=relative_to, extras=req.extras, ) except ValueError: pass if dep is None: dep = Dependency(name, version or "*", extras=req.extras) if version: dep._constraint = parse_constraint(version) else: if req.pretty_constraint: constraint = req.constraint else: constraint = "*" dep = Dependency(name, constraint, extras=req.extras) if "extra" in markers: # If we have extras, the dependency is optional dep.deactivate() for or_ in markers["extra"]: for _, extra in or_: dep.in_extras.append(extra) if "python_version" in markers: ors = [] for or_ in markers["python_version"]: ands = [] for op, version in or_: # Expand python version if op == "==" and "*" not in version: version = "~" + version op = "" elif op == "!=": version += ".*" elif op in ("<=", ">"): parsed_version = Version.parse(version) if parsed_version.precision == 1: if op == "<=": op = "<" version = parsed_version.next_major.text elif op == ">": op = ">=" version = parsed_version.next_major.text elif parsed_version.precision == 2: if op == "<=": op = "<" version = parsed_version.next_minor.text elif op == ">": op = ">=" version = parsed_version.next_minor.text elif op in ("in", "not in"): versions = [] for v in re.split("[ ,]+", version): split = v.split(".") if len(split) in [1, 2]: split.append("*") op_ = "" if op == "in" else "!=" else: op_ = "==" if op == "in" else "!=" versions.append(op_ + ".".join(split)) glue = " || " if op == "in" else ", " if versions: ands.append(glue.join(versions)) continue ands.append("{}{}".format(op, version)) ors.append(" ".join(ands)) dep.python_versions = " || ".join(ors) if req.marker: dep.marker = req.marker return dep
def create_from_pep_508(cls, name: str, relative_to: Path | None = None) -> Dependency: """ Resolve a PEP-508 requirement string to a `Dependency` instance. If a `relative_to` path is specified, this is used as the base directory if the identified dependency is of file or directory type. """ from poetry.core.packages.url_dependency import URLDependency from poetry.core.packages.utils.link import Link from poetry.core.packages.utils.utils import is_archive_file from poetry.core.packages.utils.utils import is_installable_dir from poetry.core.packages.utils.utils import is_url from poetry.core.packages.utils.utils import path_to_url from poetry.core.packages.utils.utils import strip_extras from poetry.core.packages.utils.utils import url_to_path from poetry.core.packages.vcs_dependency import VCSDependency from poetry.core.utils.patterns import wheel_file_re from poetry.core.vcs.git import ParsedUrl from poetry.core.version.requirements import Requirement # Removing comments parts = name.split(" #", 1) name = parts[0].strip() if len(parts) > 1: rest = parts[1] if " ;" in rest: name += " ;" + rest.split(" ;", 1)[1] req = Requirement(name) name = req.name link = None if is_url(name): link = Link(name) elif req.url: link = Link(req.url) else: path_str = os.path.normpath(os.path.abspath(name)) p, extras = strip_extras(path_str) if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): if not is_installable_dir(p): raise ValueError( f"Directory {name!r} is not installable. File 'setup.py' " "not found.") link = Link(path_to_url(p)) elif is_archive_file(p): link = Link(path_to_url(p)) # it's a local file, dir, or url if link: is_file_uri = link.scheme == "file" is_relative_uri = is_file_uri and re.search(r"\.\./", link.url) # Handle relative file URLs if is_file_uri and is_relative_uri: path = Path(link.path) if relative_to: path = relative_to / path link = Link(path_to_url(path)) # wheel file version = None if link.is_wheel: m = wheel_file_re.match(link.filename) if not m: raise ValueError(f"Invalid wheel name: {link.filename}") name = m.group("name") version = m.group("ver") dep: Dependency | None = None if link.scheme.startswith("git+"): url = ParsedUrl.parse(link.url) dep = VCSDependency( name, "git", url.url, rev=url.rev, directory=url.subdirectory, extras=req.extras, ) elif link.scheme == "git": dep = VCSDependency(name, "git", link.url_without_fragment, extras=req.extras) elif link.scheme in ["http", "https"]: dep = URLDependency(name, link.url, extras=req.extras) elif is_file_uri: # handle RFC 8089 references path = url_to_path(req.url) dep = _make_file_or_dir_dep(name=name, path=path, base=relative_to, extras=req.extras) else: with suppress(ValueError): # this is a local path not using the file URI scheme dep = _make_file_or_dir_dep( name=name, path=Path(req.url), base=relative_to, extras=req.extras, ) if dep is None: dep = Dependency(name, version or "*", extras=req.extras) if version: dep._constraint = parse_constraint(version) else: constraint: VersionConstraint | str if req.pretty_constraint: constraint = req.constraint else: constraint = "*" dep = Dependency(name, constraint, extras=req.extras) if req.marker: dep.marker = req.marker return dep