Skip to content

Commit

Permalink
update requirements_txt parsing to return valid Dependency objects
Browse files Browse the repository at this point in the history
  • Loading branch information
stefansjs committed Jul 28, 2023
1 parent 471aa35 commit 41cae9a
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 10 deletions.
16 changes: 9 additions & 7 deletions conda_lock/src_parser/environment_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
from typing import List, Tuple

import yaml
from packaging._parser import parse_requirement

from conda_lock.models.lock_spec import Dependency, LockSpecification
from conda_lock.src_parser.conda_common import conda_spec_to_versioned_dep
from conda_lock.src_parser.requirements_txt import parse_requirements_txt
from conda_lock.src_parser.requirements_txt import parse_requirements_txt, parse_one_requirement
from conda_lock.src_parser.selectors import filter_platform_selectors

_whitespace = re.compile(r"\s+")
Expand All @@ -27,6 +26,7 @@ def _parse_environment_file_for_platform(
content: str,
category: str,
platform: str,
source_path: pathlib.Path,
) -> List[Dependency]:
"""
Parse dependencies from a conda environment specification for an
Expand Down Expand Up @@ -54,15 +54,17 @@ def _parse_environment_file_for_platform(
for mapping_spec in mapping_specs:
if "pip" in mapping_spec:
# Generate the temporary requirements file
with tempfile.NamedTemporaryFile(suffix="requirements.txt", mode='w', encoding='utf-8') as requirements:
yaml_dir = source_path.parent
with tempfile.NamedTemporaryFile(dir=yaml_dir, suffix="requirements.txt", mode='w', encoding='utf-8') as requirements:
requirements.writelines(mapping_spec["pip"])
requirements.close()
requirements.write("\n") # Trailing newline
requirements.file.close()

dependencies.extend(filter(None, parse_requirements_txt(requirements.name)))
dependencies.extend(parse_requirements_txt(requirements.name, category))

# ensure pip is in target env
if 'pip' not in {d.name for d in dependencies}:
dependencies.append(parse_requirement("pip"))
dependencies.append(parse_one_requirement("pip", category))

return dependencies

Expand Down Expand Up @@ -111,7 +113,7 @@ def parse_environment_file(

# Parse with selectors for each target platform
dep_map = {
platform: _parse_environment_file_for_platform(content, category, platform)
platform: _parse_environment_file_for_platform(content, category, platform, environment_file)
for platform in platforms
}

Expand Down
43 changes: 40 additions & 3 deletions conda_lock/src_parser/requirements_txt.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
"""
Parses pip requirements from a requirements file
"""
from packaging._parser import parse_requirement
from pip._internal.network.session import PipSession
from pip._internal.req.req_file import parse_requirements
from pip._internal.req.req_file import ParsedRequirement

from conda_lock.models.lock_spec import VersionedDependency, VCSDependency, URLDependency, Dependency
from conda_lock.src_parser.pyproject_toml import unpack_git_url

def parse_requirements_txt(file_path):

def parse_requirements_txt(file_path, category=None) -> Dependency:
session = PipSession()
return parse_requirements(file_path, session)
for req in filter(None, parse_requirements(file_path, session)):
yield parse_one_requirement(req.requirement, category=category)


def parse_one_requirement(req_string: str, category=None) -> Dependency:
parsed_req = parse_requirement(req_string)

if parsed_req.url and parsed_req.url.startswith("git+"):
url, rev = unpack_git_url(parsed_req.url)
return VCSDependency(
name=parsed_req.name,
source=url,
manager='pip',
vcs="git",
rev=rev,
)
elif parsed_req.url: # type: ignore[attr-defined]
assert parsed_req.specifier in {"", "*", None}
url, frag = urldefrag(parsed_req.url) # type: ignore[attr-defined]
return URLDependency(
name=parsed_req.name,
manager='pip',
category=category,
extras=parsed_req.extras,
url=url,
hashes=[frag.replace("=", ":")],
)
else:
return VersionedDependency(
name=parsed_req.name,
version=parsed_req.specifier or "*",
manager='pip',
category=category,
extras=parsed_req.extras,
)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies = [
"pyyaml >= 5.1",
'tomli; python_version<"3.11"',
"typing-extensions",
"pip",
# conda dependencies
"ruamel.yaml",
"toolz >=0.12.0,<1.0.0",
Expand Down

0 comments on commit 41cae9a

Please sign in to comment.