Skip to content

Commit

Permalink
Add support for git dependencies (#435)
Browse files Browse the repository at this point in the history
* feat: add support for git to lockfile

* wip

* pre-commit hooks

* satisfy tests and precommit hooks

* wip

* pre-commit hooks

* satisfy tests and precommit hooks

* tried some more tests

* adapted poetry vcsdependency

* fix tests

* review

* correct initial assingment

* do not execute slow test on windows with mamba

---------

Co-authored-by: Michael Lingelbach <m.j.lbach@gmail.com>
Co-authored-by: Mohammed Ajil <mohammed.ajil@quantco.com>
  • Loading branch information
3 people committed Jun 16, 2023
1 parent bf4307e commit e6111cc
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 88 deletions.
15 changes: 14 additions & 1 deletion conda_lock/models/lock_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,27 @@ class URLDependency(_BaseDependency):
hashes: List[str]


Dependency = Union[VersionedDependency, URLDependency]
class VCSDependency(_BaseDependency):
source: str
vcs: str
rev: Optional[str] = None


Dependency = Union[VersionedDependency, URLDependency, VCSDependency]


class Package(StrictModel):
url: str
hash: str


class PoetryMappedDependencySpec(StrictModel):
url: Optional[str]
manager: Literal["conda", "pip"]
extras: List
poetry_version_spec: Optional[str]


class LockSpecification(BaseModel):
dependencies: Dict[str, List[Dependency]]
# TODO: Should we store the auth info in here?
Expand Down
101 changes: 63 additions & 38 deletions conda_lock/pypi_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ProjectPackage as PoetryProjectPackage,
)
from conda_lock._vendor.poetry.core.packages import URLDependency as PoetryURLDependency
from conda_lock._vendor.poetry.core.packages import VCSDependency as PoetryVCSDependency
from conda_lock._vendor.poetry.factory import Factory
from conda_lock._vendor.poetry.installation.chooser import Chooser
from conda_lock._vendor.poetry.installation.operations.uninstall import Uninstall
Expand Down Expand Up @@ -165,6 +166,13 @@ def get_dependency(dep: lock_spec.Dependency) -> PoetryDependency:
url=f"{dep.url}#{dep.hashes[0].replace(':','=')}",
extras=extras,
)
elif isinstance(dep, lock_spec.VCSDependency):
return PoetryVCSDependency(
name=dep.name,
vcs=dep.vcs,
source=dep.source,
rev=dep.rev,
)
else:
raise ValueError(f"Unknown requirement {dep}")

Expand All @@ -181,6 +189,60 @@ def get_package(locked: LockedDependency) -> PoetryPackage:
return PoetryPackage(locked.name, version=locked.version)


def get_requirements(
result: List,
platform: str,
pool: Pool,
env: Env,
) -> List[LockedDependency]:
"""Extract distributions from Poetry package plan, ignoring uninstalls
(usually: conda package with no pypi equivalent) and skipped ops
(already installed)
"""
chooser = Chooser(pool, env=env)
requirements: List[LockedDependency] = []
for op in result:
if not isinstance(op, Uninstall) and not op.skipped:
# Take direct references verbatim
source: Optional[DependencySource] = None
if op.package.source_type == "url":
url, fragment = urldefrag(op.package.source_url)
hash_type, hash = fragment.split("=")
hash = HashModel(**{hash_type: hash})
source = DependencySource(type="url", url=op.package.source_url)
elif op.package.source_type == "git":
url = f"{op.package.source_type}+{op.package.source_url}@{op.package.source_resolved_reference}"
# TODO: FIXME git ls-remoet
hash = HashModel(**{"sha256": op.package.source_resolved_reference})
source = DependencySource(type="url", url=url)
# Choose the most specific distribution for the target
# TODO: need to handle git here
# https://github.com/conda/conda-lock/blob/ac31f5ddf2951ed4819295238ccf062fb2beb33c/conda_lock/_vendor/poetry/installation/executor.py#L557
else:
link = chooser.choose_for(op.package)
url = link.url_without_fragment
hashes: Dict[str, str] = {}
if link.hash_name is not None and link.hash is not None:
hashes[link.hash_name] = link.hash
hash = HashModel.parse_obj(hashes)

requirements.append(
LockedDependency(
name=op.package.name,
version=str(op.package.version),
manager="pip",
source=source,
platform=platform,
dependencies={
dep.name: str(dep.constraint) for dep in op.package.requires
},
url=url,
hash=hash,
)
)
return requirements


def solve_pypi(
pip_specs: Dict[str, lock_spec.Dependency],
use_latest: List[str],
Expand Down Expand Up @@ -274,44 +336,7 @@ def solve_pypi(
with s.use_environment(env):
result = s.solve(use_latest=to_update)

chooser = Chooser(pool, env=env)

# Extract distributions from Poetry package plan, ignoring uninstalls
# (usually: conda package with no pypi equivalent) and skipped ops
# (already installed)
requirements: List[LockedDependency] = []
for op in result:
if not isinstance(op, Uninstall) and not op.skipped:
# Take direct references verbatim
source: Optional[DependencySource] = None
if op.package.source_type == "url":
url, fragment = urldefrag(op.package.source_url)
hash_type, hash = fragment.split("=")
hash = HashModel(**{hash_type: hash})
source = DependencySource(type="url", url=op.package.source_url)
# Choose the most specific distribution for the target
else:
link = chooser.choose_for(op.package)
url = link.url_without_fragment
hashes: Dict[str, str] = {}
if link.hash_name is not None and link.hash is not None:
hashes[link.hash_name] = link.hash
hash = HashModel.parse_obj(hashes)

requirements.append(
LockedDependency(
name=op.package.name,
version=str(op.package.version),
manager="pip",
source=source,
platform=platform,
dependencies={
dep.name: str(dep.constraint) for dep in op.package.requires
},
url=url,
hash=hash,
)
)
requirements = get_requirements(result, platform, pool, env)

# use PyPI names of conda packages to walking the dependency tree and propagate
# categories from explicit to transitive dependencies
Expand Down
Loading

0 comments on commit e6111cc

Please sign in to comment.