示例#1
0
文件: base.py 项目: jhauberg/comply
    def report_results(self,
                       results: List[str],
                       prefix_if_suppressed: str = '') -> int:
        """ Print each result (a formatted violation), suppressing similar results if needed. """

        emitted = 0

        for result in results:
            printout(result)

            emitted += 1

            # assuming each result is a violation "almost" identical to the rest
            if self.suppress_similar and emitted >= self.suppresses_after:
                remaining = len(results) - emitted

                # if results are being piped or redirected, we don't need to emit a diagnostic
                # note that the PyCharm bit is just for testing purposes
                should_notify = printing.results.isatty(
                ) or 'PYCHARM' in os.environ

                if remaining > 0 and should_notify:
                    # note that this does not require --verbose;
                    # when a suppression occurs it should always be mentioned
                    printdiag('{0}(...{1} more suppressed)'.format(
                        prefix_if_suppressed, remaining))

                break

        return emitted
示例#2
0
def check_for_update():
    """ Determine whether a newer version is available remotely. """

    from urllib.request import urlopen
    from urllib.error import URLError, HTTPError

    url = 'https://raw.githubusercontent.com/jhauberg/comply/master/comply/version.py'

    try:
        # specify a very short timeout, as this is a non-essential feature
        # and should not stall program exit indefinitely
        with urlopen(url, timeout=5) as response:
            # we're certain this file is UTF8, so we'll decode it right away
            response_body = response.read().decode('utf8')
            # search for the version string
            matches = re.search(VERSION_PATTERN, response_body, re.M)

            if matches:
                # if found, grab it and compare to the current installation
                remote_version_identifier = matches.group(1)

                if parse_version(__version__) < parse_version(
                        remote_version_identifier):
                    printdiag(Colors.GOOD +
                              'A newer version is available ({0})'.format(
                                  remote_version_identifier) + Colors.RESET)
    except HTTPError:
        # fail silently
        pass
    except URLError:
        # fail silently
        pass
示例#3
0
def exit_if_not_compatible():
    """ Warn and exit if system is running unsupported Python version. """

    if not is_compatible():
        printdiag('Python 3.5 or newer required', as_error=True)

        sys.exit(EXIT_CODE_FAILURE)
示例#4
0
    def not_checked(path: str, type: str, reason: str):
        """ Print a diagnostic stating when a file was not checked. """

        if reason is not None:
            printdiag('{type} \'{path}\' was not checked ({reason}).'.format(
                type=type, path=path, reason=reason))
        else:
            printdiag('{type} \'{path}\' was not checked.'.format(type=type,
                                                                  path=path))
示例#5
0
文件: base.py 项目: jhauberg/comply
    def report_progress(self, count: int, total: int):
        """ Print a progress indication. """

        if not self.is_verbose:
            return

        number_of_ticks = Reporter.determine_progress_ticks(count, total)

        printdiag('.' * number_of_ticks, end='')
示例#6
0
def print_rules_checked(rules: list, since_starting):
    """ Print the number of rules checked and time taken. """

    time_since_report = datetime.datetime.now() - since_starting
    report_in_seconds = time_since_report / datetime.timedelta(seconds=1)

    total_time_taken = report_in_seconds

    num_rules = len(rules)

    rules_grammar = 'rule' if num_rules == 1 else 'rules'

    printdiag('Checked {0} {1} in {2:.1f} seconds'.format(
        num_rules, rules_grammar, total_time_taken))
示例#7
0
def make_reporter(reporting_mode: str) -> Reporter:
    """ Return a reporter appropriate for the mode. """

    if reporting_mode == 'human':
        return HumanReporter()
    elif reporting_mode == 'oneline':
        return OneLineReporter()
    elif reporting_mode == 'xcode':
        return XcodeReporter()

    printdiag('Reporting mode \'{0}\' not available.'.format(reporting_mode),
              as_error=True)

    return Reporter()
示例#8
0
def print_invalid_names(names: list, rules: list):
    """ Go through and determine whether any of the provided names do not exist as named rules. """

    for name in names:
        if not is_name_valid(name, rules):
            # attempt fixing the name to provide a suggestion
            suggested_name = name.replace('_', '-').replace(' ', '-')

            if is_name_valid(suggested_name, rules):
                printdiag(
                    'Rule \'{rule}\' does not exist. Did you mean \'{suggestion}\'?'
                    .format(rule=name, suggestion=suggested_name))
            else:
                printdiag('Rule \'{rule}\' does not exist.'.format(rule=name))
示例#9
0
文件: setup.py 项目: jhauberg/comply
def determine_version_or_exit() -> str:
    """ Determine version identifier or exit the program. """

    with open('comply/version.py') as file:
        version_contents = file.read()
        version_match = re.search(VERSION_PATTERN, version_contents, re.M)

        if version_match:
            version = version_match.group(1)

            return version

    printdiag('Version could not be determined')

    sys.exit(EXIT_CODE_FAILURE)
示例#10
0
文件: base.py 项目: jhauberg/comply
    def report_before_results(self, violations: List[RuleViolation]):
        """ Print a diagnostic before reporting results.

            This diagnostic should indicate the total number of violations collected; not
            the number of results to print (some may be suppressed).
        """

        if not self.is_verbose:
            return

        count = len(violations)

        violation_or_violations = 'violation' if count == 1 else 'violations'

        diag = ' Found {0} {1}'.format(count, violation_or_violations)

        printdiag(diag)
示例#11
0
def print_report(report: CheckResult):
    """ Print the number of violations found in a report. """

    # note the whitespace; important for the full format later on
    severe_format = '({0} severe) ' if report.num_severe_violations > 0 else ''
    severe_format = severe_format.format(report.num_severe_violations)

    total_violations = report.num_violations + report.num_severe_violations

    violations_grammar = 'violation' if total_violations == 1 else 'violations'

    files_format = '{1}/{0}' if report.num_files_with_violations > 0 else '{0}'
    files_format = files_format.format(report.num_files,
                                       report.num_files_with_violations)

    printdiag('Found {num_violations} {violations} {severe}'
              'in {files} files'.format(num_violations=total_violations,
                                        violations=violations_grammar,
                                        severe=severe_format,
                                        files=files_format))
示例#12
0
def print_profiling_results(rules: list):
    """ Print benchmarking results/time taken for each rule. """

    num_rules_profiled = 0

    for rule in rules:
        time_taken = rule.total_time_spent_collecting

        if time_taken >= 0.1:
            printdiag(' [{0}] took {1:.1f} seconds'.format(
                rule.name, rule.total_time_spent_collecting))

            num_rules_profiled += 1

    num_rules_not_profiled = len(rules) - num_rules_profiled

    if num_rules_not_profiled > 0:
        printdiag(
            ' (...{0} rules took nearly no time and were not shown)'.format(
                num_rules_not_profiled))
示例#13
0
文件: base.py 项目: jhauberg/comply
    def report_before_checking(self,
                               path: str,
                               encoding: str = None,
                               show_progress: bool = True):
        """ Print a diagnostic before initiating a check on a given file. """

        if self.is_verbose:
            normalized_path = os.path.normpath(path)

            encoding = (' ({0})'.format(encoding.upper())
                        if encoding is not None else '')

            progress = (' [{n:0{width}d}/{total}]'.format(
                n=self.files_encountered,
                width=len(str(self.files_total)),
                total=self.files_total)
                        if self.files_total > 1 and show_progress else '')

            diag = 'Checking \'{path}\'{enc}{progress} '.format(
                path=truncated(normalized_path),
                enc=encoding,
                progress=progress)

            printdiag(diag, end='')
示例#14
0
def main():
    """ Entry point for invoking the comply module. """

    exit_if_not_compatible()

    if comply.PROFILING_IS_ENABLED:
        printdiag((
            'Profiling is enabled by default; '
            'profiling should only be enabled through --profile or for debugging purposes'
        ),
                  as_error=True)

    if not supports_unicode():
        if not is_windows_environment():
            # do not warn about this on Windows, as it probably won't work anyway
            printdiag('Unsupported shell encoding \'{0}\'. '
                      'Set environment variable `PYTHONIOENCODING` as UTF-8:\n'
                      '\texport PYTHONIOENCODING=UTF-8'.format(
                          diagnostics.encoding),
                      as_error=True)

    arguments = docopt(__doc__, version='comply ' + __version__)

    enable_profiling = arguments['--profile']

    comply.PROFILING_IS_ENABLED = enable_profiling

    is_verbose = arguments['--verbose']

    if enable_profiling and not is_verbose:
        printdiag('Profiling is enabled; --verbose was set automatically')

        is_verbose = True

    is_strict = arguments['--strict']
    only_severe = arguments['--only-severe']
    checks = expand_params(arguments['--check'])
    exceptions = expand_params(arguments['--except'])

    severities = ([RuleViolation.DENY] if only_severe else [
        RuleViolation.DENY, RuleViolation.WARN, RuleViolation.ALLOW
    ])

    # remove potential duplicates
    checks = list(set(checks))
    exceptions = list(set(exceptions))

    rules = filtered_rules(checks, exceptions, severities)

    reporting_mode = arguments['--reporter']

    reporter = make_reporter(reporting_mode)
    reporter.suppress_similar = not is_strict
    reporter.is_strict = is_strict
    reporter.is_verbose = is_verbose

    if arguments['--limit'] is not None:
        reporter.limit = int(arguments['--limit'])

    if not comply.printing.results.isatty() and reporter.suppress_similar:
        # when piping output elsewhere, let it be known that some results might be suppressed
        printdiag('Suppressing similar violations; results may be omitted '
                  '(set `--strict` to show everything)')

    inputs = arguments['<input>']

    time_started_report = datetime.datetime.now()

    report = make_report(inputs, rules, reporter)

    should_emit_verbose_diagnostics = reporter.is_verbose and report.num_files > 0

    if should_emit_verbose_diagnostics:
        print_rules_checked(rules, since_starting=time_started_report)

    if comply.PROFILING_IS_ENABLED:
        print_profiling_results(rules)

    if should_emit_verbose_diagnostics:
        print_report(report)

    if report.num_severe_violations > 0:
        # everything went fine; severe violations were encountered
        sys.exit(EXIT_CODE_SUCCESS_WITH_SEVERE_VIOLATIONS)
    else:
        # everything went fine; violations might have been encountered
        sys.exit(EXIT_CODE_SUCCESS)
示例#15
0
文件: setup.py 项目: jhauberg/comply
https://github.com/jhauberg/comply

Copyright 2018 Jacob Hauberg Hansen.
License: MIT (see LICENSE)
"""

import sys
import re

from setuptools import setup, find_packages

from comply import VERSION_PATTERN, EXIT_CODE_FAILURE, is_compatible
from comply.printing import printdiag

if not is_compatible():
    printdiag('Python 3.5 or newer required')

    sys.exit(EXIT_CODE_FAILURE)


def determine_version_or_exit() -> str:
    """ Determine version identifier or exit the program. """

    with open('comply/version.py') as file:
        version_contents = file.read()
        version_match = re.search(VERSION_PATTERN, version_contents, re.M)

        if version_match:
            version = version_match.group(1)

            return version