def main(argv=None, stdin=None): parser = argparse.ArgumentParser( description='Convert (parts of) a GCL model file to JSON.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument( 'selectors', metavar='SELECTOR', type=str, nargs='*', help= 'Subnodes to convert. The first selector will be treated as the root of the printed output.' ) args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') sels = query.GPath(args.selectors) if not sels.everything(): model = sels.select(model).deep() plain = util.to_python(model) sys.stdout.write(json.dumps(plain)) except (gcl.ParseError, RuntimeError) as e: sys.stderr.write(str(e) + '\n') sys.exit(1)
def main(argv=None, stdin=None): parser = argparse.ArgumentParser(description='Convert (parts of) a GCL model file to JSON.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument('selectors', metavar='SELECTOR', type=str, nargs='*', help='Select nodes to include in the JSON.') parser.add_argument('--root', '-r', metavar='PATH', type=str, default='', help='Use the indicated root path as the root of the output JSON object (like a.b.c but without wildcards)') args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') sels = query.GPath(args.selectors) if not sels.everything(): model = sels.select(model).deep() plain = util.to_python(model) selectors = args.root.split('.') if args.root else [] selected = select(plain, selectors) sys.stdout.write(json.dumps(selected)) except (gcl.ParseError, RuntimeError) as e: sys.stderr.write(str(e) + '\n') sys.exit(1)
def testComposeAll(self): x = gcl.loads(''' empty = compose_all([]); combined = compose_all([{ a = 'a' }, { b = 'b' }]); ''') self.assertEquals([], list(x['empty'].keys())) self.assertEquals(['a', 'b'], sorted(x['combined'].keys()))
def testFlattenNotAccidentallyOnStrings(self): x = gcl.loads(''' list = flatten ["abc"] ''') with self.assertRaises(gcl.EvaluationError): print(x['list'])
def testSchemaTypoExceptionIsDescriptive(self): """Check that if you make a typo in a type name the error is helpful.""" with self.assertRaises(exceptions.EvaluationError): x = gcl.loads(""" a : strong; """)
def testMap(self): x = gcl.loads(""" lst = [1, 2]; y = map(lambda x: {hello = x}, lst) """) self.assertEquals(1, x['y'][0]['hello']) self.assertEquals(2, x['y'][1]['hello'])
def testMinusWithVariable(self): # This basically says: arithmetic binds stronger than juxtaposition obj = gcl.loads(''' x = 3; y = x - 2; ''') self.assertEquals(1, obj['y'])
def testSchemasCompose3Times(self): x = gcl.loads(""" y = { x : int } { y : string } { z : bool }; """) self.assertEquals(schema.ScalarSchema('string'), x['y'].tuple_schema.get_subschema('y')) self.assertEquals(schema.ScalarSchema('int'), x['y'].tuple_schema.get_subschema('x')) self.assertEquals(schema.ScalarSchema('bool'), x['y'].tuple_schema.get_subschema('z'))
def testSchemaSurvivesComposition(self): x = gcl.loads(""" A = { foo : int }; B = A { foo = 'hello' }; """) with self.assertRaises(exceptions.SchemaError): print(x['B']['foo'])
def testSchemasCompose(self): x = gcl.loads(""" Xint = { x : int; }; y = Xint { x = 'bla'; y : string }; """) self.assertEquals(schema.ScalarSchema('string'), x['y'].tuple_schema.get_subschema('y')) self.assertEquals(schema.ScalarSchema('int'), x['y'].tuple_schema.get_subschema('x'))
def main(argv=None, stdin=None): parser = argparse.ArgumentParser(description='Print a GCL model file.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument('selectors', metavar='SELECTOR', type=str, nargs='*', help='Subnodes to print') parser.add_argument('-e', '--errors-only', dest='errors_only', action='store_true', default=False, help='Only show errors') parser.add_argument('-q', '--qualified-paths', dest='qualified_paths', action='store_true', default=False, help='Show qualified paths') parser.add_argument('-l', '--lowercase', dest='lowercase', action='store_true', default=False, help='Don\'t recurse into variables starting with capital letters.') args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') except gcl.ParseError as e: print(e) sys.exit(1) else: printer = qualified_print if args.qualified_paths else pretty_print_model print_selectors(model, args.selectors, printer, lowercase=args.lowercase, errors_only=args.errors_only)
def testSchemaRefersToVariable(self): x = gcl.loads(""" Xint = { x : int; }; y : Xint = { x = 'bla' }; """) self.assertEquals(schema.ScalarSchema('int'), x['y'].tuple_schema.get_subschema('x'))
def testSubSchema(self): x = gcl.loads(""" a : int; """) self.assertEquals(schema.ScalarSchema('int'), x.tuple_schema.get_subschema('a'))
def testLazyStringInterpolation(self): x = gcl.loads( """ things = { foo = 'FOO'; boom; }; y = fmt('Hi {foo}', things) """ ) self.assertEquals("Hi FOO", x["y"])
def testTupleRequiredFields(self): x = gcl.loads(""" a : required int = 3; b : string; c : required = 1; """) self.assertEquals(['a', 'c'], sorted(x.tuple_schema.required_fields))
def testStringInterpolation(self): x = gcl.loads( """ things = { foo = 'FOO'; bar = 'BAR' }; y = fmt('Hey {foo}, are you calling me a {bar}?', things) """ ) self.assertEquals("Hey FOO, are you calling me a BAR?", x["y"])
def testFilter(self): x = gcl.loads( """ lst = [1, 2, 3]; y = filter(lambda x: x % 2 == 0, lst) """ ) self.assertEquals([2], x["y"])
def testImplicitScope(self): x = gcl.loads( """ things = { foo = 'FOO'; boom }; y = fmt 'Hi {things.foo}' """ ) self.assertEquals("Hi FOO", x["y"])
def testSplit(self): x = gcl.loads(''' list1 = split "one two three"; list2 = split("one/two/three", "/"); ''') self.assertEquals(['one', 'two', 'three'], x['list1']) self.assertEquals(['one', 'two', 'three'], x['list2'])
def testSubInterpolation(self): x = gcl.loads( """ things = { sub = { foo = 'FOO'; boom } }; y = fmt('Hi {sub.foo}', things) """ ) self.assertEquals("Hi FOO", x["y"])
def testMap(self): x = gcl.loads( """ lst = [1, 2]; y = map(lambda x: {hello = x}, lst) """ ) self.assertEquals(1, x["y"][0]["hello"]) self.assertEquals(2, x["y"][1]["hello"])
def testRequiredFieldsInComposition(self): x = gcl.loads(""" SuperClass = { x : required }; ok_instance = SuperClass { x = 1 }; failing_instance = SuperClass { y = 1 }; """) print(x['ok_instance']) with self.assertRaises(exceptions.SchemaError): print(x['failing_instance'])
def setUp(self): self.model = gcl.loads(""" x = { y = { z = 3 }; q = { z = 4 }; }; obj_list = [{x = 1}, {x = 2}, {x = 3}]; scalar_list = [1, 2, 3]; """)
def testHasKeyCompound(self): x = gcl.loads(''' X = { key }; Y = X { key = 3 }; x_key = has(X, 'key'); y_key = has(Y, 'key'); ''') self.assertEquals(False, x['x_key']) self.assertEquals(True, x['y_key'])
def __call__(self, current_file, rel_path, env=None): nice_path, full_path = self.fs.resolve(current_file, rel_path) if path.splitext(nice_path)[1] == '.json': return json.loads(self.fs.load(full_path)) if self.gclserver.contains(full_path): return framework.eval(self.gclserver.get(full_path).error_parse(), env=env) return gcl.loads(self.fs.load(full_path), filename=nice_path, loader=self, env=env)
def __call__(self, current_file, rel_path, env=None): nice_path, full_path = self.fs.resolve(current_file, rel_path) if path.splitext(nice_path)[1] == ".json": # Load as JSON do_load = lambda: self.filter_fn(nice_path, json.loads(self.fs.load(full_path))) else: # Load as GCL do_load = lambda: gcl.loads(self.fs.load(full_path), filename=nice_path, loader=self, env=env) return self.cache.get(full_path, do_load)
def testFunctionErrorsAreTraced(self): x = gcl.loads(''' list = flatten ["abc"] ''') try: print(x['list']) self.fail('Should have thrown') except Exception as e: print(str(e)) self.assertTrue('flatten ["abc"]' in str(e))
def __call__(self, current_file, rel_path, env=None): nice_path, full_path = self.fs.resolve(current_file, rel_path) if path.splitext(nice_path)[1] == '.json': # Load as JSON do_load = lambda: self.filter_fn(nice_path, json.loads(self.fs.load(full_path))) else: # Load as GCL import gcl do_load = lambda: gcl.loads(self.fs.load(full_path), filename=nice_path, loader=self, env=env) return self.cache.get(full_path, do_load)
def testSchemasFromScopes(self): """Found a case in the wild where using schema objects from a scope seems to make a difference.""" model = gcl.loads(""" scope = { X = { id: required }; Y = { xs: X }; }; y = scope.Y { xs = scope.X { id = 'hoi' }}; """) self.assertEquals('hoi', model['y']['xs']['id'])
def testHasKey(self): x = gcl.loads(''' X = { one; two = 2; }; has_one = has(X, 'one'); has_two = has(X, 'two'); has_three = has(X, 'three'); ''') self.assertEquals(False, x['has_one']) self.assertEquals(True, x['has_two']) self.assertEquals(False, x['has_three'])
def testGetUnrelatedAttributeOfNonValidatingObject(self): """Getting a value from a nonvalidating object should fail.""" model = gcl.loads(""" obj = { id: required; ego = 3; }; copy = obj.ego; """) with self.assertRaises(exceptions.EvaluationError): zzz = model['copy']
def main(argv=None, stdin=None): parser = argparse.ArgumentParser(description='Print a GCL model file.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument('selectors', metavar='SELECTOR', type=str, nargs='*', help='Subnodes to print') parser.add_argument('-e', '--errors-only', dest='errors_only', action='store_true', default=False, help='Only show errors') parser.add_argument('-q', '--qualified-paths', dest='qualified_paths', action='store_true', default=False, help='Show qualified paths') parser.add_argument( '-l', '--lowercase', dest='lowercase', action='store_true', default=False, help='Don\'t recurse into variables starting with capital letters.') args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') except gcl.ParseError as e: print(e) sys.exit(1) else: printer = qualified_print if args.qualified_paths else pretty_print_model print_selectors(model, args.selectors, printer, lowercase=args.lowercase, errors_only=args.errors_only)
def testValidateOuterObjectWhileApplyingInnerObject(self): """We disable validations while getting the LHS of an application, but the inner deref should be validated again.""" model = gcl.loads(""" obj = { id: required; Class = { X: required; } }; # This is not allowed because 'obj' should fail validation, no matter if # 'obj' appears in the LHS of an application. copy = obj.Class { X = 3 }; """) with self.assertRaises(exceptions.EvaluationError): self.assertEquals(3, model['copy']['X'])
def testMultipleQueriesWithStar(self): """Check that if we have multiple selectors with * in it, we keep the indexes consistent.""" sel = query.GPath([ 'list.*.item1', 'list.*.item2', ]) # Kinda know it breaks if the orders are different model = gcl.loads(""" list = [ { item2 = 1 }, { item1 = 5 }, ] """) result = sel.select(model).deep() self.assertEquals([{"item2": 1}, {"item1": 5}], result["list"])
def testRecursiveSchemaReference(self): obj = gcl.loads(''' LinkedList = { value : required int; next : LinkedList; }; one_two_three = LinkedList { value = 1; next = LinkedList { value = 2; next = LinkedList { value = 3; }; }; }; ''') self.assertEquals(3, obj['one_two_three']['next']['next']['value'])
def main(argv=None, stdin=None): parser = argparse.ArgumentParser( description='Convert (parts of) a GCL model file to JSON.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument('selectors', metavar='SELECTOR', type=str, nargs='*', help='Select nodes to include in the JSON.') parser.add_argument( '--root', '-r', metavar='PATH', type=str, default='', help= 'Use the indicated root path as the root of the output JSON object (like a.b.c but without wildcards)' ) args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') sels = query.GPath(args.selectors) if not sels.everything(): model = sels.select(model).deep() plain = util.to_python(model) selectors = args.root.split('.') if args.root else [] selected = select(plain, selectors) sys.stdout.write(json.dumps(selected, indent=2)) except (gcl.ParseError, RuntimeError) as e: sys.stderr.write(str(e) + '\n') sys.exit(1)
def setUp(self): self.obj = gcl.loads(''' Thing = { type = 'thing'; }; some-stuff = { thing1 = Thing; thing2 = Thing { something = 'different'; parent = thing1 }; }; heres_a_thing = Thing { type = 'boo'; after = some-stuff.thing2 }; in_list = [Thing]; deep_scope = { in_list = [{ type = 'nothing'; }, { kind = 'nice' }]; }; ''')
def testRecursiveDependencies(self): self.obj = gcl.loads(''' Thing = { type = 'thing'; }; thing1 = Thing { friend = thing2; }; thing2 = Thing { friend = thing1; } ''') finder = query.TupleFinder(query.HasKeyCondition('type')) finder.find(self.obj) finder.order() self.assertTrue(finder.has_recursive_dependency()) recursive_names = [n.key_name() for n in finder.find_recursive_dependency()] self.assertEquals(['thing1', 'thing2', 'thing1'], recursive_names)
def testClassesInTupleComposition(self): x = gcl.loads(""" Atom = { name : required; }; Molecule = { atoms : [Atom]; }; objects = { correct = Molecule { atoms = [{ name = 'O' }, { name = 'C' }]; }; broken = Molecule { atoms = [{ valence = 3 }]; }; } """) zzz = x['objects']['correct']['atoms'] with self.assertRaises(exceptions.SchemaError): zzz = x['objects']['broken']['atoms']
def testRecursiveDependencies(self): self.obj = gcl.loads(''' Thing = { type = 'thing'; }; thing1 = Thing { friend = thing2; }; thing2 = Thing { friend = thing1; } ''') finder = query.TupleFinder(query.HasKeyCondition('type')) finder.find(self.obj) finder.order() self.assertTrue(finder.has_recursive_dependency()) recursive_names = [ n.key_name() for n in finder.find_recursive_dependency() ] self.assertEquals(['thing1', 'thing2', 'thing1'], recursive_names)
def main(argv=None, stdin=None): parser = argparse.ArgumentParser(description='Convert (parts of) a GCL model file to JSON.') parser.add_argument('file', metavar='FILE', type=str, nargs='?', help='File to parse') parser.add_argument('selectors', metavar='SELECTOR', type=str, nargs='*', help='Subnodes to convert. The first selector will be treated as the root of the printed output.') args = parser.parse_args(argv or sys.argv[1:]) try: if args.file and args.file != '-': model = gcl.load(args.file) else: model = gcl.loads((stdin or sys.stdin).read(), filename='<stdin>') sels = query.GPath(args.selectors) if not sels.everything(): model = sels.select(model).deep() plain = util.to_python(model) sys.stdout.write(json.dumps(plain)) except (gcl.ParseError, RuntimeError) as e: sys.stderr.write(str(e) + '\n') sys.exit(1)
def loader(self, base, rel, env=None): target_path = gcl.find_relative(path.dirname(base), rel) return gcl.loads('loaded_from = %r; z = thing' % target_path, env=env)
def parse(self, fname, s): return gcl.loads(s, filename=fname, loader=self.loader, env=gcl.Environment({'thing': 'yes'}))
def parse(self, gcl_contents): return gcl.loads(gcl_contents, filename='/root.gcl', loader=self.loader)