diff --git a/README.md b/README.md index 92a5611..47305de 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![GitHub contributors](https://img.shields.io/github/contributors/nasirhjafri/libyear.svg)](https://GitHub.com/nasirhjafri/libyear/graphs/contributors/) -# libyear +# libyear A **simple** measure of software dependency freshness. It is a **single number** telling you how up-to-date your dependencies are. @@ -23,9 +23,12 @@ A single requirement file A folder with requirement files `libyear -r requirements/` +A Pipfile +`libyear -p Pipfile` + ## Example output ``` -libyear -r requirements.txt +libyear -r requirements.txt +-------------------------+-----------------+----------------+-----------------+ | Library | Current Version | Latest Version | Libyears behind | +-------------------------+-----------------+----------------+-----------------+ diff --git a/libyear/libyear b/libyear/libyear index 0df17c2..bb048ee 100644 --- a/libyear/libyear +++ b/libyear/libyear @@ -4,22 +4,31 @@ import argparse from prettytable import PrettyTable from libyear.pypi import get_lib_days, get_no_of_releases -from libyear.utils import load_requirements, get_requirement_files, get_requirement_name_and_version +from libyear.utils import load_requirements, get_requirement_files, load_pipfile, get_requirement_name_and_version def main(): parser = argparse.ArgumentParser() parser.add_argument('-r', help="Requirements file/path", action='store') + parser.add_argument('-p', help="Pipfile path", action='store') parser.add_argument('--sort', help="Sort by years behind, in descending order", action='store_true') args = parser.parse_args() + requirements = set() - for req_file in get_requirement_files(args.r): - requirements.update(load_requirements(req_file)) + + if args.r: + for req_file in get_requirement_files(args.r): + requirements.update(load_requirements(req_file)) + elif args.p: + requirements.update(load_pipfile(args.p)) + else: + parser.print_help() + exit(1) pt = PrettyTable() pt.field_names = ['Library', 'Current Version', 'Latest Version', 'Libyears behind'] total_days = 0 - + for req in requirements: name, version, version_lt = get_requirement_name_and_version(req) if not name: @@ -27,7 +36,7 @@ def main(): if not version and not version_lt: continue - + v, lv, days = get_lib_days(name, version, version_lt) if v and days > 0: pt.add_row([name, v, lv, str(round(days / 365, 2))]) @@ -42,7 +51,7 @@ def main(): else: print(pt) print("Your system is %s libyears behind" % str(round(total_days / 365, 2))) - + if __name__ == "__main__": diff --git a/libyear/utils.py b/libyear/utils.py index 87cb1ff..68aef29 100644 --- a/libyear/utils.py +++ b/libyear/utils.py @@ -1,6 +1,9 @@ import os import re +from pipfile import Pipfile + + REQUIREMENT_NAME_RE = r'^([^=><]+)' REQUIREMENT_VERSION_LT_RE = r'<([^$,]*)' REQUIREMENT_VERSION_LTE_RE = r'[<=]=([^$,]*)' @@ -67,3 +70,29 @@ def load_requirements(*requirements_paths): if is_requirement(line) ) return list(requirements) + + +def load_pipfile(pipfile_path): + """ + Load all requirements from the specified Pipfile. + Returns a list of requirement strings. + """ + requirements = set() + parsed_data = Pipfile.load(pipfile_path) + + for package, version in parsed_data.data['default'].items(): + # Version not specified, assume latest + if version == '*': + requirements.add(package) + continue + + # If extra info is provided, use just version + # Ignore editable entries (e.g. git repos) + if isinstance(version, dict): + if 'version' in version.keys(): + requirements.add(f'{package}{version["version"]}') + continue + + requirements.add(f'{package}{version}') + + return list(requirements) diff --git a/setup.py b/setup.py index 1f80311..0fde84b 100644 --- a/setup.py +++ b/setup.py @@ -33,6 +33,7 @@ "requests>=2.0.0", "prettytable>=0.7.2", "python-dateutil>=2.7.0", + "pipfile", ], setup_requires=["pytest-runner"], ) diff --git a/tests/data/Pipfile b/tests/data/Pipfile new file mode 100644 index 0000000..2ba8f71 --- /dev/null +++ b/tests/data/Pipfile @@ -0,0 +1,18 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[dev-packages] +"flake8" = "==3.7.8" +"pytest-flake8" = "==1.0.4" +pytest = "==3.10.1" + +[packages] +requests = ">=2.0.0" +prettytable = ">=0.7.2" +idna = "==2.7" +docker = "<=4.4.4" +toml = "*" +random_lib1 = { git = "https://github.com/example/repo.git", ref = "1.0.0"} +random_lib2 = { version = "==2.0.0", extras = ["foo", "bar"] } diff --git a/tests/test_utils.py b/tests/test_utils.py index afa3856..12615d8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ from pathlib import Path -from libyear.utils import get_requirement_name_and_version, load_requirements +from libyear.utils import get_requirement_name_and_version, load_requirements, load_pipfile def test_loads_from_requirements_file_with_hashes(): @@ -15,3 +15,9 @@ def test_gets_name_and_version_from_requirements_file_with_hashes(): } assert ("appdirs", "1.4.3", None) in results + + +def test_loads_from_pipfile(): + path = Path(__file__).parent / "data" / "Pipfile" + assert set(load_pipfile(path)) == set( + ['idna==2.7', 'prettytable>=0.7.2', 'toml', 'requests>=2.0.0', 'docker<=4.4.4', 'random_lib2==2.0.0'])