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)
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)
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)
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))
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")
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")
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 = []
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))
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")
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)
def __init__(self, *args): super(TestBase, self).__init__(*args) self.config = configuration.Configuration()
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)
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
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
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
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
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)