def test_file_imports_binary_file(mocker, binary_file, encoding):
    logger = Mock()
    mocker.patch('important.parse.LOGGER', logger)

    # Attempt to parse and assert that it logged a warning
    list(parse_file_imports(binary_file))
    logger.warning.assert_called_with(
        'Skipping %s due to decode error: %s', binary_file,
        "'{encoding}' codec can't decode byte 0xff in position 0:\
 invalid start byte".format(encoding=encoding)
    )
def test_file_imports_with_syntax_error(mocker, python_source_file):
    logger = Mock()
    mocker.patch('important.parse.LOGGER', logger)

    # Alter file so that it won't compile
    with open(python_source_file, 'a') as fh:
        fh.write('not a valid Python statement')

    # Attempt to parse and assert that it logged a warning
    list(parse_file_imports(python_source_file))
    logger.warning.assert_called_with(
        'Skipping %s due to syntax error: %s', python_source_file,
        'invalid syntax (test.py, line 23)'
    )
def test_file_imports_with_utf8_encoding(mocker, tmpdir):
    python_source_file = str(tmpdir.join('utf8.py'))
    with codecs.open(python_source_file, mode='w', encoding='utf8') as fh:
        fh.write(
            '''# -*- coding: utf-8 -*-
import os

print('uʍop ǝpısdn')'''
        )

    logger = Mock()
    mocker.patch('important.parse.LOGGER', logger)

    # Attempt to parse and assert that it logged a warning
    assert list(parse_file_imports(python_source_file)) == [
        Import(module='os', filename='utf8.py', lineno=2, col_offset=0)
    ]

    logger.warning.assert_not_called()
Example #4
0
def check(requirements, constraints, ignore, ignorefile, exclude, sourcecode,
          verbose):
    # Validate options
    if not requirements and not constraints:
        raise click.BadParameter('no checks performed; supply either '
                                 '--requirements or --contraints')

    # Parse requirements and contraints
    parsed_requirements = []
    for requirements_path in requirements:
        parsed_requirements.extend(parse_requirements(requirements_path))
    parsed_contraints = []
    for contraints_path in constraints:
        parsed_contraints.extend(parse_requirements(contraints_path))

    # Remove requirements that are ignored
    ignore = list(ignore)
    for ignorefile_path in ignorefile:
        ignore.extend((r.name for r in parse_requirements(ignorefile_path)))
    if ignore:
        parsed_requirements = [
            r for r in parsed_requirements if r.name not in ignore
        ]

    if verbose >= 2:
        click.echo('Read requirements:')
        for parsed_requirement in sorted(parsed_requirements,
                                         key=lambda r: r.name):
            click.echo(parsed_requirement.name)
        click.echo('Read constraints:')
        for parsed_contraint in sorted(parsed_contraints,
                                       key=lambda r: r.name):
            click.echo('{constraint}{specifier}'.format(
                constraint=parsed_contraint.name,
                specifier=str(parsed_contraint.specifier)))

    # Parse source code
    imports = None
    if os.path.isfile(sourcecode):
        imports = set(parse_file_imports(sourcecode, exclude))
    elif os.path.isdir(sourcecode):
        imports = set(parse_dir_imports(sourcecode, exclude))
    else:
        raise click.BadParameter("could not parse SOURCECODE '%s'; path is "
                                 "either not a file or not a directory" %
                                 sourcecode)
    filenames = set(i.filename for i in imports)

    output = []

    # Test requirements
    unused_requirements = None
    if parsed_requirements:
        unused_requirements = check_unused_requirements(
            imports, parsed_requirements)
        if verbose > 0:
            for unused_requirement in sorted(unused_requirements):
                output.append('%s (unused requirement)' % unused_requirement)

    # Test contraints
    contraint_violations = None
    if constraints:
        contraint_violations = check_import_frequencies(
            imports, parsed_contraints)
        if verbose > 0:
            for module, violation in sorted(
                    contraint_violations.items(),
                    key=lambda module_violation: module_violation[0]):
                constraint, frequency = violation
                output.append('%s%s (constraint violated by %s==%d)' %
                              (module, constraint, module, frequency))

    # Statistics
    if verbose >= 1:
        click.echo('Parsed {imports} imports in {files} files'.format(
            imports=len(imports),
            files=len(filenames),
        ))
    if verbose >= 3:
        for filename in sorted(filenames):
            click.echo(filename)
        for i in sorted(imports):
            click.echo('{module}={filename}:{lineno}'.format(
                module=i.module, filename=i.filename, lineno=i.lineno))

    # Exit
    if unused_requirements or contraint_violations:
        if verbose >= 1:
            message = 'Unused requirements or violated constraints found'
            message += '\n' if output else ''
            message += '\n'.join(output) if output else ''
            raise click.ClickException(message)
        else:
            sys.exit(1)
def test_file_imports(python_source_file, python_imports):
    assert list(parse_file_imports(python_source_file)) == \
        list(map(lambda i: Import(i[0], 'test.py', i[1], i[2]),
                 python_imports))