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

Add --no-deps option #48

Merged
merged 3 commits into from
Jun 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 24 additions & 13 deletions liccheck/command_line.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import argparse
import collections

from liccheck.requirements import parse_requirements
from liccheck.requirements import parse_requirements, resolve, resolve_without_deps

try:
from configparser import ConfigParser, NoOptionError
Expand Down Expand Up @@ -108,7 +108,7 @@ class Reason(enum.Enum):
UNKNOWN = 'UNKNOWN'


def get_packages_info(requirement_file):
def get_packages_info(requirement_file, no_deps=False):
regex_license = re.compile(r'License: (?P<license>[^\r\n]+)\r?\n')
regex_classifier = re.compile(r'Classifier: License :: OSI Approved :: (?P<classifier>[^\r\n]+)\r?\n')

Expand Down Expand Up @@ -149,7 +149,8 @@ def strip_license(license):
return license[:-len(" license")]
return license

packages = [transform(dist) for dist in pkg_resources.working_set.resolve(requirements)]
resolve_func = resolve_without_deps if no_deps else resolve
packages = [transform(dist) for dist in resolve_func(requirements)]
# keep only unique values as there are maybe some duplicates
unique = []
[unique.append(item) for item in packages if item not in unique]
Expand Down Expand Up @@ -204,18 +205,23 @@ def find_parents(package, all, seen):
return dependency_trees


def write_package(package, all):
dependency_branches = find_parents(package['name'], all, set())
def write_package(package, all, no_deps=False):
licenses = package['licenses'] or 'UNKNOWN'
print(' {} ({}): {}'.format(package['name'], package['version'], licenses))
if not no_deps:
write_deps(package, all)


def write_deps(package, all):
dependency_branches = find_parents(package['name'], all, set())
print(' dependenc{}:'.format('y' if len(dependency_branches) <= 1 else 'ies'))
for dependency_branch in dependency_branches:
print(' {}'.format(dependency_branch))


def write_packages(packages, all):
def write_packages(packages, all, no_deps=False):
for package in packages:
write_package(package, all)
write_package(package, all, no_deps)


def group_by(items, key):
Expand All @@ -226,11 +232,12 @@ def group_by(items, key):
return res


def process(requirement_file, strategy, level=Level.STANDARD, reporting_file=None):
def process(requirement_file, strategy, level=Level.STANDARD, reporting_file=None, no_deps=False):
print('gathering licenses...')
pkg_info = get_packages_info(requirement_file)
pkg_info = get_packages_info(requirement_file, no_deps)
all = list(pkg_info)
print('{} package{} and dependencies.'.format(len(pkg_info), '' if len(pkg_info) <= 1 else 's'))
deps_mention = '' if no_deps else ' and dependencies'
print('{} package{}{}.'.format(len(pkg_info), '' if len(pkg_info) <= 1 else 's', deps_mention))
groups = group_by(
pkg_info, functools.partial(check_package, strategy, level=level))
ret = 0
Expand Down Expand Up @@ -261,13 +268,13 @@ def format(l):
if groups[Reason.UNAUTHORIZED]:
print('check unauthorized packages...')
print(format(groups[Reason.UNAUTHORIZED]))
write_packages(groups[Reason.UNAUTHORIZED], all)
write_packages(groups[Reason.UNAUTHORIZED], all, no_deps)
ret = -1

if groups[Reason.UNKNOWN]:
print('check unknown packages...')
print(format(groups[Reason.UNKNOWN]))
write_packages(groups[Reason.UNKNOWN], all)
write_packages(groups[Reason.UNKNOWN], all, no_deps)
ret = -1

return ret
Expand Down Expand Up @@ -308,12 +315,16 @@ def parse_args(args):
'-R', '--reporting', dest='reporting_txt_file',
help='path/to/reporting.txt file', nargs='?',
default=None)
parser.add_argument(
'--no-deps', dest='no_deps',
help="don't check dependencies", action='store_true')

return parser.parse_args(args)


def run(args):
strategy = read_strategy(args.strategy_ini_file)
return process(args.requirement_txt_file, strategy, args.level, args.reporting_txt_file)
return process(args.requirement_txt_file, strategy, args.level, args.reporting_txt_file, args.no_deps)


def main():
Expand Down
17 changes: 17 additions & 0 deletions liccheck/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,20 @@ def parse_requirements(requirement_file):
continue
requirements.append(pkg_resources.Requirement.parse(str(install_req.req)))
return requirements


def resolve_without_deps(requirements):
working_set = pkg_resources.working_set
for req in requirements:
env = pkg_resources.Environment(working_set.entries)
dist = env.best_match(
req=req,
working_set=working_set,
installer=None,
replace_conflicting=False,
)
yield dist


def resolve(requirements):
yield from pkg_resources.working_set.resolve(requirements)
36 changes: 34 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
from liccheck.command_line import parse_args, read_strategy, run, Level

import textwrap

def test_parse_arguments():
args = parse_args(['--sfile', 'my_strategy.ini'])
assert args.strategy_ini_file == 'my_strategy.ini'
assert args.requirement_txt_file == './requirements.txt'
assert args.level is Level.STANDARD
assert args.no_deps is False
args = parse_args(['--sfile', 'my_strategy.ini', '--rfile', 'my_requirements.txt', '--level', 'cautious'])
assert args.strategy_ini_file == 'my_strategy.ini'
assert args.requirement_txt_file == 'my_requirements.txt'
assert args.level is Level.CAUTIOUS
assert args.no_deps is False
args = parse_args(['--sfile', 'my_strategy.ini', '--rfile', 'my_requirements.txt', '--level', 'cautious', '--no-deps'])
assert args.strategy_ini_file == 'my_strategy.ini'
assert args.requirement_txt_file == 'my_requirements.txt'
assert args.level is Level.CAUTIOUS
assert args.no_deps is True


def test_read_strategy():
Expand All @@ -20,6 +27,31 @@ def test_read_strategy():
assert len(strategy.UNAUTHORIZED_LICENSES) > 0


def test_run():
def test_run(capsys):
args = parse_args(['--sfile', 'license_strategy.ini', '--rfile', 'requirements.txt'])
run(args)
captured = capsys.readouterr().out
expected = textwrap.dedent(
'''\
gathering licenses...
3 packages and dependencies.
check authorized packages...
3 packages.
'''
)
assert captured == expected


def test_run_without_deps(capsys):
args = parse_args(['--sfile', 'license_strategy.ini', '--rfile', 'requirements.txt', '--no-deps'])
run(args)
captured = capsys.readouterr().out
expected = textwrap.dedent(
'''\
gathering licenses...
3 packages.
check authorized packages...
3 packages.
'''
)
assert captured == expected
15 changes: 15 additions & 0 deletions tests/test_get_packages_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,18 @@ def test_requirements_markers(tmpfile):
assert len(get_packages_info(tmppath)) == 2
else:
assert len(get_packages_info(tmppath)) == 1


@pytest.mark.parametrize(
('no_deps', 'expected_packages'), (
pytest.param(False, ('configparser', 'liccheck', 'semantic-version', 'toml'), id='with deps'),
pytest.param(True, ('liccheck',), id='without deps'),
)
)
def test_deps(tmpfile, no_deps, expected_packages):
tmpfh, tmppath = tmpfile
tmpfh.write('liccheck\n')
tmpfh.close()
packages_info = get_packages_info(tmppath, no_deps)
packages = tuple(package['name'] for package in packages_info)
assert packages == expected_packages
16 changes: 16 additions & 0 deletions tests/test_write_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,19 @@ def test_write_packages_with_cyclic_dependencies(capsys):
fixtures << testtools << fixtures
'''
assert captured == expected


def test_write_packages_without_deps(capsys):
packages = [
{'name': 'functools32', 'version': '3.2.3-2', 'location': 'path',
'dependencies': [], 'licenses': ['PSF license']},
{'name': 'jsonschema', 'version': '2.6.0', 'location': 'path',
'dependencies': ['functools32'], 'licenses': ['Apache2']},
{'name': 'os-faults', 'version': '0.2.0', 'location': 'path',
'dependencies': ['jsonschema'], 'licenses': ['Apache2']}]

write_packages([packages[0]], packages, no_deps=True)

captured = capsys.readouterr().out
expected = " functools32 (3.2.3-2): ['PSF license']\n"
assert captured == expected