Пример #1
0
def setup_argparser(argparser):
    """
  Add argparse options to the parser.
  """
    argparser.register('action', 'extend', ExtendAction)
    argparser.add_argument('-v',
                           '--version',
                           action='version',
                           version=cmakelang.__version__)
    argparser.add_argument('-l',
                           '--log-level',
                           default="info",
                           choices=["error", "warning", "info", "debug"])

    mutex = argparser.add_mutually_exclusive_group()
    mutex.add_argument('--dump-config',
                       choices=['yaml', 'json', 'python'],
                       default=None,
                       const='python',
                       nargs='?',
                       help='If specified, print the default configuration to '
                       'stdout and exit')
    mutex.add_argument('--dump',
                       choices=['lex', 'parse', 'parsedb', 'layout', 'markup'],
                       default=None)

    argparser.add_argument(
        "--no-help",
        action="store_false",
        dest="with_help",
        help="When used with --dump-config, will omit helptext comments in the"
        " output")
    argparser.add_argument(
        "--no-default",
        action="store_false",
        dest="with_defaults",
        help="When used with --dump-config, will omit any unmodified "
        "configuration value.")

    mutex = argparser.add_mutually_exclusive_group()
    mutex.add_argument('-i', '--in-place', action='store_true')
    mutex.add_argument(
        '--check',
        action='store_true',
        help="Exit with status code 0 if formatting would not change file "
        "contents, or status code 1 if it would")
    mutex.add_argument('-o',
                       '--outfile-path',
                       default=None,
                       help='Where to write the formatted file. '
                       'Default is stdout.')

    argparser.add_argument('-c',
                           '--config-files',
                           nargs='+',
                           action='extend',
                           help='path to configuration file(s)')
    argparser.add_argument('infilepaths', nargs='*')

    configuration.Configuration().add_to_argparser(argparser)
Пример #2
0
def exec_sidecar(test, body, meta, input_str):
    """
  Assert a formatting and, optionally, a lex, parse, or layout tree.
  """
    if input_str is None:
        input_str = body
    expect_lex = meta.pop("expect_lex", None)
    if expect_lex is not None:
        with test.subTest(phase="lex"):
            assert_lex(test, input_str, expect_lex)
    expect_parse = meta.pop("expect_parse", None)
    if expect_parse is not None:
        with test.subTest(phase="parse"):
            assert_parse(test, input_str, expect_parse)
    expect_layout = meta.pop("expect_layout", None)
    if expect_layout is not None:
        with test.subTest(phase="layout"):
            assert_layout(test, input_str, expect_layout)

    test.config = configuration.Configuration(**meta)
    # TODO(josh): just move this into the configuration for the one test where
    # it's needed.
    test.config.parse.fn_spec.add('foo',
                                  flags=['BAR', 'BAZ'],
                                  kwargs={
                                      "HEADERS": '*',
                                      "SOURCES": '*',
                                      "DEPENDS": '*'
                                  })

    with test.subTest(phase="format"):
        assert_format(test, input_str, body)
Пример #3
0
def dump_config(args, config_dict, outfile):
    """
  Dump the default configuration to stdout
  """

    outfmt = args.dump_config
    config_dict.update(get_argdict(args))
    cfg = configuration.Configuration(**config_dict)
    # Don't dump default per-command configs
    for key in standard_funs.get_default_config():
        cfg.misc.per_command.pop(key, None)

    if outfmt == 'yaml':
        import yaml
        yaml_register_odict(yaml.SafeDumper)
        yaml_register_odict(yaml.Dumper)
        yaml.dump(cfg.as_odict(args.with_help, args.with_defaults),
                  outfile,
                  indent=2,
                  default_flow_style=False,
                  sort_keys=False)
        return
    if outfmt == 'json':
        json.dump(cfg.as_odict(args.with_help, args.with_defaults),
                  outfile,
                  indent=2)
        outfile.write('\n')
        return

    cfg.dump(outfile,
             with_help=args.with_help,
             with_defaults=args.with_defaults)
Пример #4
0
    def setUp(self):
        self.config = configuration.Configuration()
        parse_db = parse.funs.get_parse_db()
        self.parse_ctx = parse.ParseContext(parse_db)
        self.config.parse.fn_spec.add('foo',
                                      flags=['BAR', 'BAZ'],
                                      kwargs={
                                          "HEADERS": '*',
                                          "SOURCES": '*',
                                          "DEPENDS": '*'
                                      })

        self.parse_ctx.parse_db.update(
            parse.funs.get_funtree(self.config.parse.fn_spec))
Пример #5
0
    def test_auto_line_endings(self):
        config_dict = self.config.as_dict()
        config_dict['line_ending'] = 'auto'
        self.config = configuration.Configuration(**config_dict)

        self.source_str = (
            "#[[*********************************************\r\n"
            "* Information line 1\r\n"
            "* Information line 2\r\n"
            "************************************************]]\r\n")

        self.expect_format = (
            "#[[*********************************************\r\n"
            "* Information line 1\r\n"
            "* Information line 2\r\n"
            "************************************************]]\r\n")
Пример #6
0
    def test_windows_line_endings_output(self):
        config_dict = self.config.as_dict()
        config_dict['line_ending'] = 'windows'
        self.config = configuration.Configuration(**config_dict)

        self.source_str = """\
#[[*********************************************
* Information line 1
* Information line 2
************************************************]]"""

        self.expect_format = (
            "#[[*********************************************\r\n"
            "* Information line 1\r\n"
            "* Information line 2\r\n"
            "************************************************]]\r\n")
Пример #7
0
    def __init__(self, parse_db=None, lint_ctx=None, config=None):
        if parse_db is None:
            from cmakelang.parse.funs import get_parse_db
            parse_db = get_parse_db()
        self.parse_db = parse_db

        if lint_ctx is None:
            lint_ctx = MockEverything()
        self.lint_ctx = lint_ctx

        if config is None:
            from cmakelang import configuration
            config = configuration.Configuration()
        self.config = config

        # List of currently open parse nodes. Only used by nodes below
        # the statement level.
        self.argstack = []
Пример #8
0
    def __init__(self, *args, **kwargs):
        super(TestBase, self).__init__(*args, **kwargs)
        self.config = configuration.Configuration()
        parse_db = parse.funs.get_parse_db()
        self.parse_ctx = parse.ParseContext(parse_db)
        self.source_str = None
        self.expect_lex = None
        self.expect_parse = None
        self.expect_layout = None
        self.expect_format = None

        # NOTE(josh): hacky introspective way of automatically calling
        # assertExpectations() at the end of every test_XXX() function
        for name in dir(self):
            if not name.startswith("test_"):
                continue
            value = getattr(self, name)
            if callable(value):
                setattr(self, name, WrapTestWithRunFun(self, value))
Пример #9
0
def main():
    rootdir = os.sep.join(os.path.realpath(__file__).split(os.sep)[:-3])

    argparser = argparse.ArgumentParser(description=__doc__)
    argparser.add_argument("--sourcefile",
                           default=os.path.join(
                               rootdir, "cmakelang/doc/configopts.rst"))
    argparser.add_argument("outfile", nargs="?", default="-")
    args = argparser.parse_args()

    with io.open(args.sourcefile, "r", encoding="utf-8") as infile:
        data = parse_sourcefile(infile)

    if args.outfile == "-":
        args.outfile = os.dup(sys.stdout.fileno())

    with io.open(args.outfile, "w", encoding="utf-8") as outfile:
        outfile.write(".. _configopts:\n\n")
        write_outfile(outfile, data["global"], configuration.Configuration(),
                      "global")
Пример #10
0
def setup_argparse(argparser):
  argparser.add_argument('-v', '--version', action='version',
                         version=cmakelang.__version__)
  argparser.add_argument(
      '-l', '--log-level', default="info",
      choices=["error", "warning", "info", "debug"])

  mutex = argparser.add_mutually_exclusive_group()
  mutex.add_argument('--dump-config', choices=['yaml', 'json', 'python'],
                     default=None, const='python', nargs='?',
                     help='If specified, print the default configuration to '
                          'stdout and exit')
  mutex.add_argument('-o', '--outfile-path', default=None,
                     help='Write errors to this file. '
                          'Default is stdout.')

  argparser.add_argument(
      "--no-help", action="store_false", dest="with_help",
      help="When used with --dump-config, will omit helptext comments in the"
           " output"
  )
  argparser.add_argument(
      "--no-default", action="store_false", dest="with_defaults",
      help="When used with --dump-config, will omit any unmodified "
           "configuration value."
  )

  argparser.add_argument(
      "--suppress-decorations", action="store_true",
      help="Suppress the file title decoration and summary statistics")

  argparser.add_argument(
      '-c', '--config-files', nargs='+',
      help='path to configuration file(s)')
  argparser.add_argument('infilepaths', nargs='*')

  configuration.Configuration().add_to_argparser(argparser)
Пример #11
0
 def __init__(self, *args):
     super(TestBase, self).__init__(*args)
     self.config = configuration.Configuration()
Пример #12
0
 def __init__(self, *args, **kwargs):
     super(TestCanonicalParse, self).__init__(*args, **kwargs)
     self.config = configuration.Configuration()
     parse_db = parse.funs.get_parse_db()
     self.parse_ctx = parse.ParseContext(parse_db)
Пример #13
0
def inner_main():
  """Parse arguments, open files, start work."""
  logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

  argparser = argparse.ArgumentParser(
      description=__doc__,
      formatter_class=argparse.RawDescriptionHelpFormatter,
      usage=USAGE_STRING)

  setup_argparse(argparser)
  try:
    import argcomplete
    argcomplete.autocomplete(argparser)
  except ImportError:
    pass
  args = argparser.parse_args()
  logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))

  if args.dump_config:
    config_dict = __main__.get_config(os.getcwd(), args.config_files)
    __main__.dump_config(args, config_dict, sys.stdout)
    sys.exit(0)

  if args.outfile_path is None:
    args.outfile_path = '-'

  if '-' in args.infilepaths:
    assert len(args.infilepaths) == 1, \
        "You cannot mix stdin as an input with other input files"

  if args.outfile_path == '-':
    outfile = io.open(os.dup(sys.stdout.fileno()),
                      mode='w', encoding="utf-8", newline='')
  else:
    outfile = io.open(args.outfile_path, 'w', encoding="utf-8", newline='')

  global_ctx = lint_util.GlobalContext(outfile)
  returncode = 0
  argdict = __main__.get_argdict(args)

  for infile_path in args.infilepaths:
    # NOTE(josh): have to load config once for every file, because we may pick
    # up a new config file location for each path
    if infile_path == '-':
      config_dict = __main__.get_config(os.getcwd(), args.config_files)
    else:
      config_dict = __main__.get_config(infile_path, args.config_files)
    config_dict.update(argdict)

    cfg = configuration.Configuration(**config_dict)
    if infile_path == '-':
      infile_path = os.dup(sys.stdin.fileno())

    try:
      infile = io.open(
          infile_path, mode='r', encoding=cfg.encode.input_encoding, newline='')
    except (IOError, OSError):
      logger.error("Failed to open %s for read", infile_path)
      returncode = 1
      continue

    try:
      with infile:
        intext = infile.read()
    except UnicodeDecodeError:
      logger.error(
          "Unable to read %s as %s", infile_path, cfg.encode.input_encoding)
      returncode = 1
      continue

    local_ctx = global_ctx.get_file_ctx(infile_path, cfg)
    process_file(cfg, local_ctx, intext)
    if not args.suppress_decorations:
      outfile.write("{}\n{}\n".format(infile_path, "=" * len(infile_path)))
    local_ctx.writeout(outfile)
    if not args.suppress_decorations:
      outfile.write("\n")
    if local_ctx.has_lint():
      returncode = 1

  if not args.suppress_decorations:
    global_ctx.write_summary(outfile)
  outfile.close()
  return returncode
Пример #14
0
def inner_main():
    """Parse arguments, open files, start work."""
    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

    argparser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=USAGE_STRING)

    setup_argparse(argparser)
    args = argparser.parse_args()
    logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))

    if args.outfile_path is None:
        args.outfile_path = '-'

    if '-' in args.infilepaths:
        assert len(args.infilepaths) == 1, \
            "You cannot mix stdin as an input with other input files"

    if args.outfile_path == '-':
        outfile = io.open(os.dup(sys.stdout.fileno()),
                          mode='w',
                          encoding="utf-8",
                          newline='')
    else:
        outfile = io.open(args.outfile_path, 'w', encoding="utf-8", newline='')

    returncode = 0

    cfg = configuration.Configuration()
    collector = NameCollector()
    for infile_path in args.infilepaths:
        # NOTE(josh): have to load config once for every file, because we may pick
        # up a new config file location for each path
        if infile_path == '-':
            infile_path = os.dup(sys.stdin.fileno())

        try:
            infile = io.open(infile_path,
                             mode='r',
                             encoding=cfg.encode.input_encoding,
                             newline='')
        except (IOError, OSError):
            logger.error("Failed to open %s for read", infile_path)
            returncode = 1
            continue

        try:
            with infile:
                infile_content = infile.read()
        except UnicodeDecodeError:
            logger.error("Unable to read %s as %s", infile_path,
                         cfg.encode.input_encoding)
            returncode = 1
            continue

        tokens = lex.tokenize(infile_content)
        parse_db = parse.funs.get_parse_db()
        ctx = parse.ParseContext(parse_db, config=cfg)
        parse_tree = parse.parse(tokens, ctx)
        parse_tree.build_ancestry()
        collector.collect_names(parse_tree)

    regexes = [
        re.compile(pattern) for pattern in [
            r"[A-Z][A-Z0-9_]+",  # upper snake-case
            r"[a-z][a-z0-9_]+",  # lower snake-case
            r"_[A-Z0-9_]+",  # upper snake-case with underscore prefix
            r"_[a-z0-9_]+",  # lower snake-case with underscore prefix
        ]
    ]

    outmap = {}
    patmap = {}
    for scope, varname in sorted(collector.varnames):
        if scope not in outmap:
            outmap[scope] = {}

        if scope not in patmap:
            patmap[scope] = {}
            for regex in regexes:
                patmap[scope][str(regex)] = 0
            patmap[scope]["other"] = 0

        for regex in regexes:
            if regex.match(varname):
                patmap[scope][str(regex)] += 1
                break
        else:
            patmap[scope]["other"] += 1

        if varname not in outmap[scope]:
            outmap[scope][varname] = 0
        outmap[scope][varname] += 1

    for scope, countmap in sorted(outmap.items()):
        outfile.write("\n{}\n{}\n".format(scope.name, "=" * len(scope.name)))
        for varname, count in sorted(countmap.items()):
            outfile.write("{}: {}\n".format(varname, count))

    for scope, countmap in sorted(patmap.items()):
        outfile.write("\n{}\n{}\n".format(scope.name, "=" * len(scope.name)))
        for varname, count in sorted(countmap.items()):
            outfile.write("{}: {}\n".format(varname, count))

    outfile.close()
    return returncode
Пример #15
0
def main():
    """Parse arguments, open files, start work."""
    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

    argparser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=USAGE_STRING)

    setup_argparse(argparser)
    args = argparser.parse_args()
    logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))

    if '-' in args.infilepaths:
        assert len(args.infilepaths) == 1, \
            "You cannot mix stdin as an input with other input files"

    if args.outfile_path is None:
        args.outfile_path = '-'

    outfile_arg = args.outfile_path
    if outfile_arg == '-':
        outfile_arg = os.dup(sys.stdout.fileno())

    returncode = 0
    outdict = collections.OrderedDict()
    for infile_path in args.infilepaths:
        print(infile_path)
        cfg = configuration.Configuration()
        if infile_path == '-':
            infile_path = os.dup(sys.stdin.fileno())

        try:
            infile = io.open(infile_path,
                             mode='r',
                             encoding=cfg.encode.input_encoding,
                             newline='')
        except (IOError, OSError):
            logger.error("Failed to open %s for read", infile_path)
            returncode = 1

        try:
            with infile:
                intext = infile.read()
        except UnicodeDecodeError:
            logger.error("Unable to read %s as %s", infile_path,
                         cfg.encode.input_encoding)

        try:
            parse_tree = process_file(cfg, intext)
            cmd_spec = process_tree(parse_tree)
            outdict.update(cmd_spec)
        except:
            logger.warning('While processing %s', infile_path)
            raise

    outfile = io.open(outfile_arg, mode='w', encoding="utf-8", newline='')
    if args.output_format == "json":
        json.dump(outdict, outfile, indent=2)
    elif args.output_format == "yaml":
        import yaml
        __main__.yaml_register_odict(yaml.SafeDumper)
        __main__.yaml_register_odict(yaml.Dumper)
        yaml.dump(outdict,
                  outfile,
                  indent=2,
                  default_flow_style=False,
                  sort_keys=False)
    elif args.output_format == "python":
        ppr = pprint.PrettyPrinter(indent=2, width=80)
        outfile.write(ppr.pformat(dict(outdict)))
    else:
        logger.error("Unrecognized output format {}".format(
            args.output_format))
    outfile.write("\n")
    outfile.close()
    return returncode
Пример #16
0
def main():
    """Parse arguments, open files, start work."""

    # set up main logger, which logs everything. We'll leave this one logging
    # to the console
    logging.basicConfig(level=logging.INFO)
    arg_parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=USAGE_STRING)
    setup_argparser(arg_parser)
    args = arg_parser.parse_args()

    assert (len(args.infilepaths) == 1
            or args.outfile_path is None), \
        ("if more than one input file is specified, then annotates must be "
         "written to stdout")

    if args.outfile_path is None:
        args.outfile_path = '-'

    if '-' in args.infilepaths:
        assert len(args.infilepaths) == 1, \
            "You cannot mix stdin as an input with other input files"
        assert args.outfile_path == '-', \
            "If stdin is the input file, then stdout must be the output file"

    argdict = __main__.get_argdict(args)
    output_format = argdict.pop("format")

    for infile_path in args.infilepaths:
        # NOTE(josh): have to load config once for every file, because we may pick
        # up a new config file location for each path
        if infile_path == '-':
            config_dict = __main__.get_config(os.getcwd(), args.config_file)
        else:
            config_dict = __main__.get_config(infile_path, args.config_file)
        config_dict.update(argdict)

        cfg = configuration.Configuration(**config_dict)
        if args.outfile_path == '-':
            # NOTE(josh): The behavior or sys.stdout is different in python2 and
            # python3. sys.stdout is opened in 'w' mode which means that write()
            # takes strings in python2 and python3 and, in particular, in python3
            # it does not take byte arrays. io.StreamWriter will write to
            # it with byte arrays (assuming it was opened with 'wb'). So we use
            # io.open instead of open in this case
            outfile = io.open(os.dup(sys.stdout.fileno()),
                              mode='w',
                              encoding=cfg.encode.output_encoding,
                              newline='')
        else:
            outfile = io.open(args.outfile_path,
                              'w',
                              encoding=cfg.encode.output_encoding,
                              newline='')

        if infile_path == '-':
            infile = io.open(os.dup(sys.stdin.fileno()),
                             mode='r',
                             encoding=cfg.encode.input_encoding,
                             newline='')
        else:
            infile = io.open(infile_path,
                             'r',
                             encoding=cfg.encode.input_encoding)

        try:
            with infile:
                annotate_file(cfg, infile, outfile, output_format)
        except:
            sys.stderr.write('While processing {}\n'.format(infile_path))
            raise
        finally:
            outfile.close()

    return 0
Пример #17
0
def onefile_main(infile_path, args, argparse_dict):
    """
  Find config, open file, process, write result
  """
    # NOTE(josh): have to load config once for every file, because we may pick
    # up a new config file location for each path
    if infile_path == '-':
        config_dict = get_config(os.getcwd(), args.config_files)
    else:
        config_dict = get_config(infile_path, args.config_files)

    cfg = configuration.Configuration(**config_dict)
    cfg.legacy_consume(argparse_dict)

    if cfg.format.disable:
        return

    if infile_path == '-':
        infile = io.open(os.dup(sys.stdin.fileno()),
                         mode='r',
                         encoding=cfg.encode.input_encoding,
                         newline='')
    else:
        infile = io.open(infile_path,
                         'r',
                         encoding=cfg.encode.input_encoding,
                         newline='')
    with infile:
        intext = infile.read()

    try:
        outtext, reflow_valid = process_file(cfg, intext, args.dump)
        if cfg.format.require_valid_layout and not reflow_valid:
            raise common.FormatError("Failed to format {}".format(infile_path))
    except:
        logger.warning('While processing %s', infile_path)
        raise

    if args.check:
        if intext != outtext:
            raise common.FormatError("Check failed: {}".format(infile_path))
        return

    if args.in_place:
        if intext == outtext:
            logger.debug("No delta for %s", infile_path)
            return
        tempfile_path = infile_path + ".cmf-temp"
        outfile = io.open(tempfile_path,
                          'w',
                          encoding=cfg.encode.output_encoding,
                          newline='')
    else:
        if args.outfile_path == '-':
            # NOTE(josh): The behavior of sys.stdout is different in python2 and
            # python3. sys.stdout is opened in 'w' mode which means that write()
            # takes strings in python2 and python3 and, in particular, in python3
            # it does not take byte arrays. io.StreamWriter will write to
            # it with byte arrays (assuming it was opened with 'wb'). So we use
            # io.open instead of open in this case
            outfile = io.open(os.dup(sys.stdout.fileno()),
                              mode='w',
                              encoding=cfg.encode.output_encoding,
                              newline='')
        else:
            outfile = io.open(args.outfile_path,
                              'w',
                              encoding=cfg.encode.output_encoding,
                              newline='')

    with outfile:
        outfile.write(outtext)

    if args.in_place:
        shutil.copymode(infile_path, tempfile_path)
        shutil.move(tempfile_path, infile_path)