Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding options to log addition additional metadata into the lockfile #204

Merged
merged 63 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
d9b34a7
Adding options to log metadata about lockfile in comments to the lock…
Jun 30, 2022
cfda7bd
Address failing lints
Jun 30, 2022
604b9fe
Structuring metadata as pydantic models on the LockMeta class
Jul 5, 2022
f448f05
Restructuring pydantic a bit to have less redundancy
Jul 5, 2022
90875c3
Fixing new lints
Jul 5, 2022
f904e79
trying to kickoff tests again after timeout last time
Jul 5, 2022
e7c613c
Adding regression tests for new metadata fields
NatPRoach Jul 13, 2022
dd97f51
Merge branch 'main' into add_lockfile_metadata
mariusvniekerk Jul 28, 2022
811662b
Merge branch 'main' into add_lockfile_metadata
mariusvniekerk Jul 29, 2022
8f4c6af
Update conda_lock/src_parser/__init__.py
NatPRoach Jul 29, 2022
fe54b6b
Update conda_lock/src_parser/__init__.py
NatPRoach Jul 29, 2022
3820802
re-adding tests, fixing comments from conda-lock team
NatPRoach Aug 10, 2022
1fafc94
Getting changes passing precommit checks
NatPRoach Aug 10, 2022
67cf13b
Attempting to fix git test
NatPRoach Aug 11, 2022
842702a
Fixing lint
NatPRoach Aug 11, 2022
11dfd80
Fixing changing dir into non-existent dir
NatPRoach Aug 11, 2022
6f4b57a
Applying suggestion from code review
NatPRoach Aug 11, 2022
e1cd6fa
Fixing precommits after applying suggestions
NatPRoach Aug 11, 2022
7bb79ae
Reverting to relative paths
NatPRoach Aug 11, 2022
376b5d4
Fixing lints
NatPRoach Aug 11, 2022
81a9e9c
Reverting suggestion from code review
NatPRoach Aug 12, 2022
d9ea458
Fixing problem of always calculating inputs metadata breaking tests
Aug 14, 2022
0e3e51e
Fixing precommit checks
Aug 14, 2022
d91c42a
Actually fixing precommit checks
Aug 14, 2022
3f1ffc1
Merge branch 'main' into add_lockfile_metadata
mariusvniekerk Oct 10, 2022
b346860
Adding options to log metadata about lockfile in comments to the lock…
Jun 30, 2022
086d836
Address failing lints
Jun 30, 2022
07f03f8
Structuring metadata as pydantic models on the LockMeta class
Jul 5, 2022
1afe3c8
Restructuring pydantic a bit to have less redundancy
Jul 5, 2022
f3f7170
Fixing new lints
Jul 5, 2022
cddab4e
trying to kickoff tests again after timeout last time
Jul 5, 2022
5277b03
Adding regression tests for new metadata fields
NatPRoach Jul 13, 2022
d356a05
Update conda_lock/src_parser/__init__.py
NatPRoach Jul 29, 2022
4769460
Update conda_lock/src_parser/__init__.py
NatPRoach Jul 29, 2022
a0bfe28
re-adding tests, fixing comments from conda-lock team
NatPRoach Aug 10, 2022
93a6f3e
Getting changes passing precommit checks
NatPRoach Aug 10, 2022
9b7928c
Attempting to fix git test
NatPRoach Aug 11, 2022
27da398
Fixing lint
NatPRoach Aug 11, 2022
8ce4b30
Fixing changing dir into non-existent dir
NatPRoach Aug 11, 2022
b1361d7
Applying suggestion from code review
NatPRoach Aug 11, 2022
2917b0b
Fixing precommits after applying suggestions
NatPRoach Aug 11, 2022
6897d20
Reverting to relative paths
NatPRoach Aug 11, 2022
8833927
Fixing lints
NatPRoach Aug 11, 2022
7fafd4a
Reverting suggestion from code review
NatPRoach Aug 12, 2022
8cb4f44
Changing git commit hash to commit hash of most recently modified inp…
NatPRoach Nov 10, 2022
2ef27bc
Addressing PR comments, fixing associated tests
NatPRoach Nov 12, 2022
d003e43
Merge branch 'add_lockfile_metadata3' into add_lockfile_metadata
NatPRoach Nov 12, 2022
3426fcf
Fixing tests after merge
NatPRoach Nov 12, 2022
c1e36ba
Removing redundant gitpython req
NatPRoach Nov 12, 2022
107508f
Removing unused and duplicate click fields that got readded in merge
NatPRoach Nov 12, 2022
d871556
fixing path resolution when files are on different drives
NatPRoach Nov 12, 2022
ef3cd98
reworking git metadata check to work in abscence of pre-existing conf…
NatPRoach Nov 12, 2022
f5761de
Fixing precommit lints
NatPRoach Nov 12, 2022
a833d13
attempting to fix git metadata issue
NatPRoach Nov 12, 2022
4bee557
Move to the git directory to address the relative path issue
NatPRoach Nov 12, 2022
da86fac
address user section not existing on github actions
NatPRoach Nov 12, 2022
ae256e3
Fixing mistype on addressing user section not existing in github actions
NatPRoach Nov 12, 2022
941f0d8
Addressing releasing lock of config writer
NatPRoach Nov 12, 2022
1e910d6
Cleanup type annotations a bit
mariusvniekerk Nov 15, 2022
645512d
Fix potential pydantic pyright type inference issues
mariusvniekerk Nov 15, 2022
19199f1
Fix windows skip
mariusvniekerk Nov 15, 2022
9a5292a
Remove some unused imports
mariusvniekerk Nov 15, 2022
16e32c8
Update conda_lock/conda_lock.py
mariusvniekerk Nov 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ exclude: ^conda_lock/_vendor/.*$

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v4.3.0
hooks:
- id: trailing-whitespace
exclude: "^.*\\.patch$"
- id: check-ast

- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.10.0
hooks:
- id: black
language_version: python3

- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8

Expand All @@ -26,8 +26,8 @@ repos:
args: ["--profile", "black", "--filter-files"]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.931
rev: v0.991
hooks:
- id: mypy
additional_dependencies: [types-filelock, types-requests, types-toml, types-PyYAML, types-setuptools, pydantic]
additional_dependencies: [types-filelock, types-requests, types-toml, types-PyYAML, types-freezegun, types-setuptools, pydantic]
exclude: ^tests/test-local-pip/setup.py$
122 changes: 117 additions & 5 deletions conda_lock/conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import pathlib
import posixpath
import re
import subprocess
import sys
import tempfile

Expand All @@ -34,7 +33,7 @@
import pkg_resources
import yaml

from ensureconda import ensureconda
from ensureconda.api import ensureconda
from typing_extensions import Literal

from conda_lock.click_helpers import OrderedGroup
Expand Down Expand Up @@ -65,10 +64,14 @@
from conda_lock.lookup import set_lookup_location
from conda_lock.src_parser import (
Dependency,
GitMeta,
InputMeta,
LockedDependency,
Lockfile,
LockMeta,
LockSpecification,
MetadataOption,
TimeMeta,
UpdateSpecification,
aggregate_lock_specs,
)
Expand Down Expand Up @@ -292,6 +295,8 @@ def make_lock_files(
filter_categories: bool = True,
extras: Optional[AbstractSet[str]] = None,
check_input_hash: bool = False,
metadata_choices: AbstractSet[MetadataOption] = frozenset(),
metadata_yamls: Sequence[pathlib.Path] = (),
) -> None:
"""
Generate a lock file from the src files provided
Expand Down Expand Up @@ -325,6 +330,10 @@ def make_lock_files(
Filter out unused categories prior to solving
check_input_hash :
Do not re-solve for each target platform for which specifications are unchanged
metadata_choices:
Set of selected metadata fields to generate for this lockfile.
metadata_yamls:
YAML or JSON file(s) containing structured metadata to add to metadata section of the lockfile.
"""

# initialize virtual package fake
Expand Down Expand Up @@ -401,10 +410,16 @@ def make_lock_files(
platforms=platforms_to_lock,
lockfile_path=lockfile_path,
update_spec=update_spec,
metadata_choices=metadata_choices,
metadata_yamls=metadata_yamls,
)

if "lock" in kinds:
write_conda_lock_file(lock_content, lockfile_path)
write_conda_lock_file(
lock_content,
lockfile_path,
metadata_choices=metadata_choices,
)
print(
" - Install lock using:",
KIND_USE_TEXT["lock"].format(lockfile=str(lockfile_path)),
Expand Down Expand Up @@ -725,13 +740,43 @@ def _solve_for_arch(
return list(conda_deps.values()) + list(pip_deps.values())


def convert_structured_metadata_yaml(in_path: pathlib.Path) -> Dict[str, Any]:
with in_path.open("r") as infile:
metadata = yaml.safe_load(infile)
return metadata


def update_metadata(to_change: Dict[str, Any], change_source: Dict[str, Any]) -> None:
for key in change_source:
if key in to_change:
logger.warning(
f"Custom metadata field {key} provided twice, overwriting value "
+ f"{to_change[key]} with {change_source[key]}"
)
to_change.update(change_source)


def get_custom_metadata(
metadata_yamls: Sequence[pathlib.Path],
) -> Optional[Dict[str, str]]:
custom_metadata_dict: Dict[str, str] = {}
for yaml_path in metadata_yamls:
new_metadata = convert_structured_metadata_yaml(yaml_path)
update_metadata(custom_metadata_dict, new_metadata)
if custom_metadata_dict:
return custom_metadata_dict
return None


def create_lockfile_from_spec(
*,
conda: PathLike,
spec: LockSpecification,
platforms: List[str] = [],
lockfile_path: pathlib.Path,
update_spec: Optional[UpdateSpecification] = None,
metadata_choices: AbstractSet[MetadataOption] = frozenset(),
metadata_yamls: Sequence[pathlib.Path] = (),
) -> Lockfile:
"""
Solve or update specification
Expand All @@ -754,13 +799,49 @@ def create_lockfile_from_spec(
for dep in deps:
locked[(dep.manager, dep.name, dep.platform)] = dep

spec_sources: Dict[str, pathlib.Path] = {}
for source in spec.sources:
try:
path = relative_path(lockfile_path.parent, source)
except ValueError as e:
if "Paths don't have the same drive" not in str(e):
raise e
path = str(source.resolve())
spec_sources[path] = source

if MetadataOption.TimeStamp in metadata_choices:
time_metadata = TimeMeta.create()
else:
time_metadata = None

git_metadata = GitMeta.create(
metadata_choices=metadata_choices,
src_files=spec.sources,
)

if metadata_choices & {MetadataOption.InputSha, MetadataOption.InputMd5}:
inputs_metadata: Optional[Dict[str, InputMeta]] = {
relative_path: InputMeta.create(
metadata_choices=metadata_choices, src_file=src_file
)
for relative_path, src_file in spec_sources.items()
}
else:
inputs_metadata = None

custom_metadata = get_custom_metadata(metadata_yamls=metadata_yamls)

return Lockfile(
package=[locked[k] for k in locked],
metadata=LockMeta(
content_hash=spec.content_hash(),
channels=[c for c in spec.channels],
platforms=spec.platforms,
sources=[str(source.resolve()) for source in spec.sources],
git_metadata=git_metadata,
time_metadata=time_metadata,
inputs_metadata=inputs_metadata,
custom_metadata=custom_metadata,
),
)

Expand Down Expand Up @@ -939,6 +1020,8 @@ def run_lock(
virtual_package_spec: Optional[pathlib.Path] = None,
update: Optional[List[str]] = None,
filter_categories: bool = False,
metadata_choices: AbstractSet[MetadataOption] = frozenset(),
metadata_yamls: Sequence[pathlib.Path] = (),
) -> None:
if environment_files == DEFAULT_FILES:
if lockfile_path.exists():
Expand Down Expand Up @@ -983,6 +1066,8 @@ def run_lock(
extras=extras,
check_input_hash=check_input_hash,
filter_categories=filter_categories,
metadata_choices=metadata_choices,
metadata_yamls=metadata_yamls,
)


Expand Down Expand Up @@ -1114,10 +1199,29 @@ def main() -> None:
type=str,
help="Location of the lookup file containing Pypi package names to conda names.",
)
@click.option(
"--md",
"--metadata",
"metadata_choices",
default=[],
multiple=True,
type=click.Choice([md.value for md in MetadataOption]),
help="Metadata fields to include in lock-file",
)
@click.option(
"--mdy",
"--metadata-yaml",
"--metadata-json",
"metadata_yamls",
default=[],
multiple=True,
type=click.Path(),
help="YAML or JSON file(s) containing structured metadata to add to metadata section of the lockfile.",
)
@click.pass_context
def lock(
ctx: click.Context,
conda: Optional[PathLike],
conda: Optional[str],
mamba: bool,
micromamba: bool,
platform: List[str],
Expand All @@ -1133,9 +1237,11 @@ def lock(
check_input_hash: bool,
log_level: TLogLevel,
pdb: bool,
virtual_package_spec: Optional[PathLike],
virtual_package_spec: Optional[pathlib.Path],
pypi_to_conda_lookup_file: Optional[str],
update: Optional[List[str]] = None,
metadata_choices: Sequence[str] = (),
metadata_yamls: Sequence[pathlib.Path] = (),
) -> None:
"""Generate fully reproducible lock files for conda environments.

Expand All @@ -1155,6 +1261,10 @@ def lock(
if pypi_to_conda_lookup_file:
set_lookup_location(pypi_to_conda_lookup_file)

metadata_enum_choices = set(MetadataOption(md) for md in metadata_choices)

metadata_yamls = [pathlib.Path(path) for path in metadata_yamls]

# bail out if we do not encounter the default file if no files were passed
if ctx.get_parameter_source("files") == click.core.ParameterSource.DEFAULT:
candidates = list(files)
Expand Down Expand Up @@ -1199,6 +1309,8 @@ def lock(
virtual_package_spec=virtual_package_spec,
update=update,
filter_categories=filter_categories,
metadata_choices=metadata_enum_choices,
metadata_yamls=metadata_yamls,
)
if strip_auth:
with tempfile.TemporaryDirectory() as tempdir:
Expand Down
3 changes: 1 addition & 2 deletions conda_lock/conda_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
import os
import pathlib
import re
import shlex
import subprocess
import sys
Expand Down Expand Up @@ -420,7 +419,7 @@ def update_specs_for_arch(
)
)
}
spec_for_name = {MatchSpec(v).name: v for v in specs}
spec_for_name = {MatchSpec(v).name: v for v in specs} # type: ignore
to_update = [
spec_for_name[name] for name in set(installed).intersection(update)
]
Expand Down
14 changes: 7 additions & 7 deletions conda_lock/invoke_conda.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from distutils.version import LooseVersion
from typing import IO, Dict, Iterator, List, Optional, Sequence, Union

import ensureconda
from ensureconda.api import determine_micromamba_version, ensureconda

from conda_lock.models.channel import Channel

Expand All @@ -26,15 +26,17 @@ def _ensureconda(
micromamba: bool = False,
conda: bool = False,
conda_exe: bool = False,
) -> Optional[PathLike]:
_conda_exe = ensureconda.ensureconda(
) -> Optional[pathlib.Path]:
_conda_exe = ensureconda(
mamba=mamba,
micromamba=micromamba,
conda=conda,
conda_exe=conda_exe,
)

return _conda_exe
if _conda_exe is None:
return None
return pathlib.Path(_conda_exe)


def _determine_conda_executable(
Expand All @@ -54,9 +56,7 @@ def determine_conda_executable(
for candidate in _determine_conda_executable(conda_executable, mamba, micromamba):
if candidate is not None:
if is_micromamba(candidate):
if ensureconda.api.determine_micromamba_version(
str(candidate)
) < LooseVersion("0.17"):
if determine_micromamba_version(str(candidate)) < LooseVersion("0.17"):
mamba_root_prefix()
return candidate
raise RuntimeError("Could not find conda (or compatible) executable")
Expand Down
Loading