Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
bruno-fs committed Oct 7, 2023
1 parent d7e7093 commit 807f670
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 53 deletions.
11 changes: 6 additions & 5 deletions src/pybuild_deps/__main__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
"""Command-line interface."""
import logging

import click

from . import find_build_dependencies
from .logger import log
from .scripts import compile


@click.group()
@click.version_option()
@click.option("--log-level", default="ERROR")
def cli(log_level) -> None:
def cli() -> None:
"""Entrypoint for PyBuild Deps."""
logging.basicConfig(level=log_level)


@cli.command()
@click.argument("package-name")
@click.argument("version")
def find_build_deps(package_name, version):
@click.option("-v", "--verbose", count=True, help="Show more output")
def find_build_deps(package_name, version, verbose):
"""Find build dependencies for given package."""
log.verbosity = verbose

deps = find_build_dependencies(package_name=package_name, version=version)
for dep in deps:
click.echo(dep)
Expand Down
64 changes: 36 additions & 28 deletions src/pybuild_deps/compile_build_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,40 @@ def get_version(ireq: InstallRequirement):
return next(iter(ireq.specifier)).version


def compile_build_dependencies(
install_requirements: Iterable[InstallRequirement],
repository: PyPIRepository,
constraints: Iterable[InstallRequirement] | None = None,
) -> tuple[set[InstallRequirement], BacktrackingResolver]:
"""Resolve all build dependencies for a given set of dependencies."""
constraints: list[InstallRequirement] = list(constraints) if constraints else []
constraint_qty = len(constraints)
for req in install_requirements:
req_version = get_version(req)
raw_build_dependencies = find_build_dependencies(req.name, req_version)
for raw_build_req in raw_build_dependencies:
build_req = install_req_from_req_string(raw_build_req, comes_from=req.name)
constraints.append(build_req)
resolver = BacktrackingResolver(
constraints=constraints,
existing_constraints={},
repository=repository,
allow_unsafe=True,
)
build_dependencies = resolver.resolve()
# dependencies of build dependencies might have their own build dependencies XD
# so let's recursively search for those.
while len(build_dependencies) != constraint_qty:
constraint_qty = len(build_dependencies)
build_dependencies, _ = compile_build_dependencies(
build_dependencies, repository, constraints=build_dependencies
class BuildDependencyCompiler:
def __init__(self, repository: PyPIRepository) -> None:
self.repository = repository
self.resolver = None

def resolve(
self,
install_requirements: Iterable[InstallRequirement],
constraints: Iterable[InstallRequirement] | None = None,
) -> tuple[set[InstallRequirement], BacktrackingResolver]:
"""Resolve all build dependencies for a given set of dependencies."""
constraints: list[InstallRequirement] = list(constraints) if constraints else []
constraint_qty = len(constraints)
for req in install_requirements:
req_version = get_version(req)
raw_build_dependencies = find_build_dependencies(req.name, req_version)
for raw_build_req in raw_build_dependencies:
build_req = install_req_from_req_string(
raw_build_req, comes_from=req.name
)
constraints.append(build_req)
# override resolver - we only want the latest and greatest
self.resolver = BacktrackingResolver(
constraints=constraints,
existing_constraints={},
repository=self.repository,
allow_unsafe=True,
)
return build_dependencies, resolver
build_dependencies = self.resolver.resolve()
# dependencies of build dependencies might have their own build dependencies XD
# so let's recursively search for those.
while len(build_dependencies) != constraint_qty:
constraint_qty = len(build_dependencies)
build_dependencies = self.resolve(
build_dependencies, constraints=build_dependencies
)
return build_dependencies
22 changes: 8 additions & 14 deletions src/pybuild_deps/find_build_dependencies.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Find build dependencies of a python package."""

import logging
import tarfile

from pybuild_deps.get_package_source import get_package_source
from pybuild_deps.parsers import parse_pyproject_toml, parse_setup_cfg, parse_setup_py
from .get_package_source import get_package_source
from .logger import log
from .parsers import parse_pyproject_toml, parse_setup_cfg, parse_setup_py


def find_build_dependencies(package_name, version):
Expand All @@ -14,7 +14,7 @@ def find_build_dependencies(package_name, version):
"setup.cfg": parse_setup_cfg,
"setup.py": parse_setup_py,
}
logging.info("retrieving source for package %s==%s", package_name, version)
log.debug(f"retrieving source for package {package_name}=={version}")
source_path = get_package_source(package_name, version)
build_dependencies = []
with tarfile.open(fileobj=source_path.open("rb")) as tarball:
Expand All @@ -23,18 +23,12 @@ def find_build_dependencies(package_name, version):
try:
file = tarball.extractfile(f"{root_dir}/{file_name}")
except KeyError:
logging.info(
"%s file not found for package %s==%s",
file_name,
package_name,
version,
log.debug(
f"{file_name} file not found for package {package_name}=={version}",
)
continue
logging.info(
"parsing file %s for package %s==%s",
file_name,
package_name,
version,
log.debug(
f"parsing file {file_name} for package {package_name}=={version}",
)
build_dependencies += parser(file.read().decode())
return build_dependencies
19 changes: 13 additions & 6 deletions src/pybuild_deps/scripts/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from piptools.writer import OutputWriter

from pybuild_deps.compile_build_dependencies import (
compile_build_dependencies,
BuildDependencyCompiler,
)
from pybuild_deps.exceptions import PyBuildDepsError
from pybuild_deps.logger import log
Expand All @@ -30,12 +30,15 @@
def get_compile_command(click_ctx):
"""Get pip-compile equivalent command and adjust for pybuild-deps."""
command = _get_compile_command(click_ctx)
# this is just overriding the command for reproducibility. pip-compile will still
# get credits since it will be displayed in the top of the header.
return command.replace("pip-compile", "pybuild-deps compile")


@click.command(context_settings={"help_option_names": ("-h", "--help")})
@click.version_option(package_name="pybuild-deps")
@click.pass_context
@click.option("-v", "--verbose", count=True, help="Show more output")
@click.option(
"-n",
"--dry-run",
Expand Down Expand Up @@ -100,6 +103,7 @@ def get_compile_command(click_ctx):
)
def cli(
ctx: click.Context,
verbose: int,
dry_run: bool,
header: bool,
annotate: bool,
Expand All @@ -111,6 +115,7 @@ def cli(
constraint: tuple[str, ...],
) -> None:
"""Compiles build_requirements.txt from requirements.txt."""
log.verbosity = verbose
if len(src_files) == 0:
if Path(REQUIREMENTS_TXT).exists():
src_files = REQUIREMENTS_TXT
Expand All @@ -119,7 +124,8 @@ def cli(
f"Couldn't find a '{REQUIREMENTS_TXT}'. "
"You must specify at least one input file."
)
if not output_file:
if not output_file and not dry_run:
log.warning("No output file (-o) specified. Defaulting to 'dry run' mode.")
dry_run = True

pip_args = []
Expand All @@ -139,9 +145,10 @@ def cli(
except PyBuildDepsError as err:
log.error(str(err))
sys.exit(2)
compiler = BuildDependencyCompiler(repository)
try:
results, resolver = compile_build_dependencies(constraints, repository)
hashes = resolver.resolve_hashes(results) if generate_hashes else None
results = compiler.resolve(constraints)
hashes = compiler.resolver.resolve_hashes(results) if generate_hashes else None
except PipToolsError as e:
log.error(str(e))
sys.exit(2)
Expand Down Expand Up @@ -173,8 +180,8 @@ def cli(
)
writer.write(
results=results,
unsafe_packages=resolver.unsafe_packages,
unsafe_requirements=resolver.unsafe_constraints,
unsafe_packages=compiler.resolver.unsafe_packages,
unsafe_requirements=compiler.resolver.unsafe_constraints,
markers={
key_from_ireq(ireq): ireq.markers for ireq in constraints if ireq.markers
},
Expand Down

0 comments on commit 807f670

Please sign in to comment.