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()
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))