Skip to content

Commit

Permalink
Refactor parsers and support pyproject-only projects (#8)
Browse files Browse the repository at this point in the history
Refactor the parsers functions into classes, simplify the logic of parsers and
add support for projects that only have a pyproject.toml file.
  • Loading branch information
santisoler authored Oct 6, 2023
1 parent 6682aa9 commit 3973c4a
Show file tree
Hide file tree
Showing 6 changed files with 642 additions and 266 deletions.
77 changes: 62 additions & 15 deletions dependente/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@
"""
import sys
import traceback
from pathlib import Path

import click

from .converters import pin_to_oldest
from .parsers import (
parse_requirements,
parse_sources,
read_pyproject_toml,
read_setup_cfg,
)
from .parsers import get_parser, validate_sources


@click.command(context_settings={"help_option_names": ["-h", "--help"]})
Expand Down Expand Up @@ -54,28 +50,30 @@ def main(source, oldest, verbose):
Supported formats:
* pyproject.toml (only build-system > requires)
* pyproject.toml (build-system > requires, project > dependencies and
project.optional-dependencies)
* setup.cfg (install_requires and options.extras_require)
"""
reporter = Reporter(verbose)
readers = {"setup.cfg": read_setup_cfg, "pyproject.toml": read_pyproject_toml}
reporter.echo(f"Extracting dependencies: {source}")
sources = source.split(",")
validate_sources(sources)
try:
sources = parse_sources(source)
config_files = get_sources_and_config_files(sources)
# Parse dependencies
dependencies = []
for config_file in sources:
if not sources[config_file]:
continue
for config_file, sources in config_files.items():
reporter.echo(f"Parsing {config_file}")
config = readers[config_file]()
dependencies_found = parse_requirements(config, sources[config_file])
parser = get_parser(config_file)
dependencies_found = parser.parse_requirements(sources)
reporter.echo(f" - {count(dependencies_found)} dependencies found")
dependencies.extend(dependencies_found)
# Pin to oldest versions
if oldest:
reporter.echo("Pinning dependencies to their oldest versions")
dependencies = pin_to_oldest(dependencies)
# Print gathered dependencies to stdout
reporter.echo(
f"Printing {count(dependencies)} dependencies to standard output",
)
Expand All @@ -89,6 +87,55 @@ def main(source, oldest, verbose):
sys.exit(1)


def get_sources_and_config_files(sources):
"""
Get configuration files in working directory and corresponding sources
Find configuration files in current directory and sort out which sources
should be parsed from which config file.
Parameters
----------
sources : list of str
List containing a subset of valid sources ("build", "install",
"extras").
Returns
-------
config_files : dict
Dictionary with config files as keys. Their values are a list of
sources that should be parsed from each one of them.
Raises
------
FileNotFoundError
If both ``setup.cfg`` and ``pyproject.toml`` are missing in the current
directory.
If "build" is in ``sources``, but ``pyproject.toml`` is not present in
the current directory.
"""
# Get configuration files in working directory
fnames = ("pyproject.toml", "setup.cfg")
config_fnames = [fname for fname in fnames if Path(fname).is_file()]
if not config_fnames:
raise FileNotFoundError("Missing 'pyproject.toml' and 'setup.cfg' files.")
# Sort out sources
if "build" in sources and "pyproject.toml" not in config_fnames:
raise FileNotFoundError(
"Missing 'pyproject.toml' file while asking for 'build' sources. "
"The 'build' sources can only be parsed from a 'pyproject.toml' file."
)
if "setup.cfg" in config_fnames:
config_files = {
"pyproject.toml": ["build"] if "build" in sources else [],
"setup.cfg": [s for s in sources if s != "build"],
}
else:
config_files = {"pyproject.toml": sources}
config_files = {key: value for key, value in config_files.items() if value}
return config_files


def count(dependencies):
"""
Count the number of dependencies in a list.
Expand Down
Loading

0 comments on commit 3973c4a

Please sign in to comment.