def find_from_path(path): names = set() max_possible = len(LIBRARY_ADAPTORS.keys()) for item in os.listdir(path): item_path = os.path.abspath(os.path.join(path, item)) if os.path.isdir(item_path): names |= find_from_path(item_path) elif not os.path.islink(item_path) and item_path.endswith('.py'): with open(item_path) as fip: names |= find_from_imports(fip.read()) if len(names) == max_possible: # don't continue on recursing, there's no point! break return names
def build_command_line_source(prog=None, description='Performs static analysis of Python code'): parser_options = {} if prog is not None: parser_options['prog'] = prog if description is not None: parser_options['description'] = description options = { 'zero_exit': { 'flags': ['-0', '--zero-exit'], 'help': 'Prospector will exit with a code of 1 (one) if any messages' ' are found. This makes automation easier; if there are any' ' problems at all, the exit code is non-zero. However this behaviour' ' is not always desirable, so if this flag is set, prospector will' ' exit with a code of 0 if it ran successfully, and non-zero if' ' it failed to run.' }, 'autodetect': { 'flags': ['-A', '--no-autodetect'], 'help': 'Turn off auto-detection of frameworks and libraries used.' ' By default, autodetection will be used. To specify' ' manually, see the --uses option.', }, 'uses': { 'flags': ['-u', '--uses'], 'help': 'A list of one or more libraries or frameworks that the' ' project uses. Possible values are: %s. This will be' ' autodetected by default, but if autodetection doesn\'t' ' work, manually specify them using this flag.' % ( ', '.join(sorted(LIBRARY_ADAPTORS.keys())), ) }, 'blending': { 'flags': ['-B', '--no-blending'], 'help': 'Turn off blending of messages. Prospector will merge' ' together messages from different tools if they represent' ' the same error. Use this option to see all unmerged' ' messages.', }, 'common_plugin': { 'flags': ['--no-common-plugin'], }, 'doc_warnings': { 'flags': ['-D', '--doc-warnings'], 'help': 'Include warnings about documentation.', }, 'test_warnings': { 'flags': ['-T', '--test-warnings'], 'help': 'Also check test modules and packages.', }, 'style_warnings': { 'flags': ['-8', '--no-style-warnings'], 'help': 'Don\'t create any warnings about style. This disables the' ' PEP8 tool and similar checks for formatting.', }, 'full_pep8': { 'flags': ['-F', '--full-pep8'], 'help': 'Enables every PEP8 warning, so that all PEP8 style' ' violations will be reported.', }, 'max_line_length': { 'flags': ['--max-line-length'], 'help': 'The maximum line length allowed. This will be set by the strictness if no' ' value is explicitly specified' }, 'messages_only': { 'flags': ['-M', '--messages-only'], 'help': 'Only output message information (don\'t output summary' ' information about the checks)', }, 'summary_only': { 'flags': ['-S', '--summary-only'], 'help': 'Only output summary information about the checks (don\'t' 'output message information)', }, 'output_format': { 'flags': ['-o', '--output-format'], 'help': 'The output format. Valid values are: %s' % ( ', '.join(sorted(FORMATTERS.keys())), ), }, 'absolute_paths': { 'help': 'Whether to output absolute paths when referencing files' 'in messages. By default, paths will be relative to the' 'project path', }, 'tools': { 'flags': ['-t', '--tool'], 'help': 'A list of tools to run. This lets you set exactly which ' 'tools to run. To add extra tools to the defaults, see ' '--extra-tool. Possible values are: %s. By ' 'default, the following tools will be run: %s' % ( ', '.join(sorted(TOOLS.keys())), ', '.join(sorted(DEFAULT_TOOLS)), ), }, 'with_tools': { 'flags': ['-w', '--with-tool'], 'help': 'A list of tools to run in addition to the default tools. ' 'To specify all tools explicitly, use the --tool argument. ' 'Possible values are %s.' % ( ', '.join(sorted(TOOLS.keys())) ), }, 'without_tools': { 'flags': ['-W', '--without-tool'], 'help': 'A list of tools that should not be run. Useful to turn off ' 'only a single tool from the defaults. ' 'To specify all tools explicitly, use the --tool argument. ' 'Possible values are %s.' % ( ', '.join(sorted(TOOLS.keys())) ), }, 'profiles': { 'flags': ['-P', '--profile'], 'help': 'The list of profiles to load. A profile is a certain' ' \'type\' of behaviour for prospector, and is represented' ' by a YAML configuration file. Either a full path to the YAML' ' file describing the profile must be provided, or it must be' ' on the profile path (see --profile-path)', }, 'profile_path': { 'flags': ['--profile-path'], 'help': 'Additional paths to search for profile files. By default this' ' is the path that prospector will check, and a directory ' ' called ".prospector" in the path that prospector will check.', }, 'strictness': { 'flags': ['-s', '--strictness'], 'help': 'How strict the checker should be. This affects how' ' harshly the checker will enforce coding guidelines. The' ' default value is "medium", possible values are' ' "veryhigh", "high", "medium", "low" and "verylow".', }, 'external_config': { 'flags': ['-e', '--external-config'], 'help': 'Determines how prospector should behave when' ' configuration already exists for a tool. By default,' ' prospector will use existing configuration. A value of' ' "merge" will cause prospector to merge existing config' ' and its own config, and "none" means that prospector' ' will use only its own config.', }, 'ignore_patterns': { 'flags': ['-I', '--ignore-patterns'], 'help': 'A list of paths to ignore, as a list of regular' ' expressions. Files and folders will be ignored if their' ' full path contains any of these patterns.', }, 'ignore_paths': { 'flags': ['-i', '--ignore-paths'], 'help': 'A list of file or directory names to ignore. If the' ' complete name matches any of the items in this list, the' ' file or directory (and all subdirectories) will be' ' ignored.', }, 'die_on_tool_error': { 'flags': ['-X', '--die-on-tool-error'], 'help': 'If a tool fails to run, prospector will try to carry on.' ' Use this flag to cause prospector to die and raise the' ' exception the tool generated. Mostly useful for' ' development on prospector.', }, 'path': { 'flags': ['-p', '--path'], 'help': 'The path to a Python project to inspect. Defaults to PWD' ' if not specified. Note: This command line argument is' ' deprecated and will be removed in a future update. Please' ' use the positional PATH argument instead.' } } positional = ( ('checkpath', { 'help': 'The path to a Python project to inspect. Defaults to PWD' ' if not specified.', 'metavar': 'PATH', 'nargs': '*', }), ) return soc.CommandLineSource( options=options, version=get_version(), parser_options=parser_options, positional=positional, )
def build_command_line_source(): parser_options = { 'description': 'Performs static analysis of Python code', } options = { 'autodetect': { 'flags': ['-A', '--no-autodetect'], 'help': 'Turn off auto-detection of frameworks and libraries used.' ' By default, autodetection will be used. To specify' ' manually, see the --uses option.', }, 'uses': { 'flags': ['-u', '--uses'], 'help': 'A list of one or more libraries or frameworks that the' ' project users. Possible values are: %s. This will be' ' autodetected by default, but if autodetection doesn\'t' ' work, manually specify them using this flag.' % (', '.join(sorted(LIBRARY_ADAPTORS.keys())), ) }, 'blending': { 'flags': ['-B', '--no-blending'], 'help': 'Turn off blending of messages. Prospector will merge' ' together messages from different tools if they represent' ' the same error. Use this option to see all unmerged' ' messages.', }, 'common_plugin': { 'flags': ['--no-common-plugin'], }, 'doc_warnings': { 'flags': ['-D', '--doc-warnings'], 'help': 'Include warnings about documentation.', }, 'test_warnings': { 'flags': ['-T', '--test-warnings'], 'help': 'Also check test modules and packages.', }, 'style_warnings': { 'flags': ['-8', '--no-style-warnings'], 'help': 'Don\'t create any warnings about style. This disables the' ' PEP8 tool and similar checks for formatting.', }, 'full_pep8': { 'flags': ['-F', '--full-pep8'], 'help': 'Enables every PEP8 warning, so that all PEP8 style' ' violations will be reported.', }, 'max_line_length': { 'flags': ['--max-line-length'], 'help': 'The maximum line length allowed. This will be set by the strictness if no' ' value is explicitly specified' }, 'messages_only': { 'flags': ['-M', '--messages-only'], 'help': 'Only output message information (don\'t output summary' ' information about the checks)', }, 'summary_only': { 'flags': ['-S', '--summary-only'], 'help': 'Only output summary information about the checks (don\'t' 'output message information)', }, 'output_format': { 'flags': ['-o', '--output-format'], 'help': 'The output format. Valid values are: %s' % (', '.join(sorted(FORMATTERS.keys())), ), }, 'absolute_paths': { 'help': 'Whether to output absolute paths when referencing files' 'in messages. By default, paths will be relative to the' 'project path', }, 'tools': { 'flags': ['-t', '--tool'], 'help': 'A list of tools to run. Possible values are: %s. By' ' default, the following tools will be run: %s' % ( ', '.join(sorted(TOOLS.keys())), ', '.join(sorted(DEFAULT_TOOLS)), ), }, 'profiles': { 'flags': ['-P', '--profile'], 'help': 'The list of profiles to load. A profile is a certain' ' \'type\' of behaviour for prospector, and is represented' ' by a YAML configuration file. A full path to the YAML' ' file describing the profile must be provided.', }, 'strictness': { 'flags': ['-s', '--strictness'], 'help': 'How strict the checker should be. This affects how' ' harshly the checker will enforce coding guidelines. The' ' default value is "medium", possible values are' ' "veryhigh", "high", "medium", "low" and "verylow".', }, 'external_config': { 'flags': ['-e', '--external-config'], 'help': 'Determines how prospector should behave when' ' configuration already exists for a tool. By default,' ' prospector will use existing configuration. A value of' ' "merge" will cause prospector to merge existing config' ' and its own config, and "none" means that prospector' ' will use only its own config.', }, 'ignore_patterns': { 'flags': ['-I', '--ignore-patterns'], 'help': 'A list of paths to ignore, as a list of regular' ' expressions. Files and folders will be ignored if their' ' full path contains any of these patterns.', }, 'ignore_paths': { 'flags': ['-i', '--ignore-paths'], 'help': 'A list of file or directory names to ignore. If the' ' complete name matches any of the items in this list, the' ' file or directory (and all subdirectories) will be' ' ignored.', }, 'die_on_tool_error': { 'flags': ['--die-on-tool-error'], 'help': 'If a tool fails to run, prospector will try to carry on.' ' Use this flag to cause prospector to die and raise the' ' exception the tool generated. Mostly useful for' ' development on prospector.', }, 'path': { 'flags': ['-p', '--path'], 'help': 'The path to a Python project to inspect. Defaults to PWD' ' if not specified. Note: This command line argument is' ' deprecated and will be removed in a future update. Please' ' use the positional PATH argument instead.' } } positional = (('checkpath', { 'help': 'The path to a Python project to inspect. Defaults to PWD' ' if not specified.', 'metavar': 'PATH', 'nargs': '*', }), ) return soc.CommandLineSource( options=options, version=get_version(), parser_options=parser_options, positional=positional, )
def run(): parser = make_arg_parser() args = parser.parse_args() if args.version: sys.stdout.write("Prospector version %s\n" % __pkginfo__.get_version()) sys.exit(0) summary = { 'started': datetime.now() } path = args.path or os.path.abspath(os.getcwd()) try: formatter = FORMATTERS[args.output_format] summary['formatter'] = args.output_format except KeyError: _die("Formatter %s is not valid - possible values are %s" % ( args.output_format, ', '.join(FORMATTERS.keys()), )) libraries_used = [] profiles = [] adaptors = [] if not args.no_common_plugin: adaptors.append(CommonAdaptor()) if not args.no_autodetect: for libname, adaptor in autodetect_libraries(path): libraries_used.append(libname) adaptors.append(adaptor) strictness = args.strictness strictness_options = ('veryhigh', 'high', 'medium', 'low', 'verylow') if strictness not in strictness_options: possible = ', '.join(strictness_options) _die( "%s is not a valid value for strictness - possible values are %s" % (strictness, possible) ) else: profiles.append('strictness_%s' % strictness) summary['strictness'] = strictness for library in args.uses: if library not in LIBRARY_ADAPTORS: possible = ', '.join(LIBRARY_ADAPTORS.keys()) _die( "Library/framework %s is not valid - possible values are %s" % (library, possible) ) libraries_used.append(library) adaptors.append(LIBRARY_ADAPTORS[library]()) summary['libraries'] = ', '.join(libraries_used) if not args.doc_warnings: profiles.append('no_doc_warnings') if not args.test_warnings: profiles.append('no_test_warnings') if args.no_style_warnings: profiles.append('no_pep8') profiles += args.profiles profile_adaptor = ProfileAdaptor(profiles) adaptors.append(profile_adaptor) summary['adaptors'] = [] for adaptor in adaptors: summary['adaptors'].append(adaptor.name) summary['adaptors'] = ', '.join(summary['adaptors']) tool_runners = [] tool_names = args.tools or tools.DEFAULT_TOOLS for tool in tool_names: if not tool in tools.TOOLS: _die("Tool %s is not valid - possible values are %s" % ( tool, ', '.join(tools.TOOLS.keys()) )) if not profile_adaptor.is_tool_enabled(tool): continue tool_runners.append(tools.TOOLS[tool]()) summary['tools'] = ', '.join(tool_names) ignore = [re.compile(ignore) for ignore in profile_adaptor.profile.ignore] for tool in tool_runners: tool.prepare(path, ignore, args, adaptors) messages = [] for tool in tool_runners: try: messages += tool.run() except Exception: if args.die_on_tool_error: raise loc = Location(path, None, None, None, None) message = "Tool %s failed to run (exception was raised)" % tool.__class__.__name__ msg = Message(tool.__class__.__name__, 'failure', loc, message=message) messages.append(msg) for message in messages: if args.absolute_paths: message.to_absolute_path(path) else: message.to_relative_path(path) if not args.no_blending: messages = blender.blend(messages) summary['message_count'] = len(messages) summary['completed'] = datetime.now() delta = (summary['completed'] - summary['started']) summary['time_taken'] = '%0.2f' % delta.total_seconds() summary['started'] = str(summary['started']) summary['completed'] = str(summary['completed']) if args.messages_only: summary = None if args.summary_only: messages = None formatter(summary, messages) sys.exit(0)