diff options
author | jryannel <juergen@ryannel.org> | 2017-04-19 10:33:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-19 10:33:51 +0200 |
commit | 80c7b9cedabb7db5356981cada2561f6d29b7b99 (patch) | |
tree | 8584b59be413e964f63595fb6939df119cc43817 | |
parent | 7a9f38795010772e117de6ad4b3b32ed442234e7 (diff) | |
download | qtivi-qface-80c7b9cedabb7db5356981cada2561f6d29b7b99.tar.gz |
Added support for JSON serialization (#43)
Added jsonify filter
-rw-r--r-- | qface/builtin/schema/log.yaml | 18 | ||||
-rwxr-xr-x | qface/builtin/schema/schema.py | 55 | ||||
-rw-r--r-- | qface/builtin/schema/templates/module.json | 1 | ||||
-rw-r--r-- | qface/filters.py | 10 | ||||
-rw-r--r-- | qface/idl/domain.py | 90 | ||||
-rw-r--r-- | tests/test_json.py | 51 |
6 files changed, 224 insertions, 1 deletions
diff --git a/qface/builtin/schema/log.yaml b/qface/builtin/schema/log.yaml new file mode 100644 index 0000000..21b5bba --- /dev/null +++ b/qface/builtin/schema/log.yaml @@ -0,0 +1,18 @@ +version: 1 +formatters: + simple: + format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' +handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: simple + stream: ext://sys.stdout +loggers: + qface.generator: + level: WARN + handlers: [console] + propagate: no +root: + level: DEBUG + handlers: [console] diff --git a/qface/builtin/schema/schema.py b/qface/builtin/schema/schema.py new file mode 100755 index 0000000..5735844 --- /dev/null +++ b/qface/builtin/schema/schema.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Copyright (c) Pelagicore AB 2016 + +import click +import logging +import logging.config +import yaml +from path import Path + +from qface.generator import FileSystem, Generator +from qface.watch import monitor +from qface.filters import jsonify + + +here = Path(__file__).dirname() + +logging.config.dictConfig(yaml.load(open(here / 'log.yaml'))) + +log = logging.getLogger(__file__) + + +def run(src, dst): + log.debug('run {0} {1}'.format(src, dst)) + system = FileSystem.parse(src) + search_path = [ + Path('_templates').abspath(), + Path(here / 'templates').abspath() + ] + generator = Generator(search_path=search_path) + generator.register_filter('jsonify', jsonify) + ctx = {'dst': dst} + for module in system.modules: + ctx.update({ + 'module': module, + }) + generator.destination = generator.apply("{{dst}}", ctx) + generator.write('{{module}}.json', 'module.json', ctx) + + +@click.command() +@click.option('--reload/--no-reload', default=False) +@click.argument('src', nargs=-1, type=click.Path(exists=True)) +@click.argument('dst', nargs=1, type=click.Path(exists=True)) +def app(src, dst, reload): + """Takes several files or directories as src and generates the code + in the given dst directory.""" + if reload: + script = Path(__file__).abspath() + monitor(script, src, dst) + else: + run(src, dst) + + +if __name__ == '__main__': + app() diff --git a/qface/builtin/schema/templates/module.json b/qface/builtin/schema/templates/module.json new file mode 100644 index 0000000..288ea15 --- /dev/null +++ b/qface/builtin/schema/templates/module.json @@ -0,0 +1 @@ +{{module|jsonify}}
\ No newline at end of file diff --git a/qface/filters.py b/qface/filters.py new file mode 100644 index 0000000..09886c0 --- /dev/null +++ b/qface/filters.py @@ -0,0 +1,10 @@ +import json + + +def jsonify(obj): + try: + # all symbols have a toJson method, try it + return json.dumps(obj.toJson(), indent=' ') + except AttributeError: + pass + return json.dumps(obj, indent=' ') diff --git a/qface/idl/domain.py b/qface/idl/domain.py index 894c9b0..fd1efb3 100644 --- a/qface/idl/domain.py +++ b/qface/idl/domain.py @@ -78,6 +78,11 @@ class System(object): type_name = parts[1] return (module_name, type_name, fragment_name) + def toJson(self): + o = {} + o['modules'] = [o.toJson() for o in self.modules] + return o + class NamedElement(object): def __init__(self, name, module: 'Module'): @@ -103,6 +108,12 @@ class NamedElement(object): else: return '{0}.{1}'.format(self.module.name, self.name) + def toJson(self): + o = {} + if self.name: + o['name'] = self.name + return o + class Symbol(NamedElement): """A symbol represents a base class for names elements""" @@ -144,6 +155,11 @@ class Symbol(NamedElement): def contents(self): return self._contentMap.values() + def toJson(self): + o = super().toJson() + if self.type.is_valid: + o['type'] = self.type.toJson() + return o class TypeSymbol(NamedElement): @@ -165,7 +181,10 @@ class TypeSymbol(NamedElement): @property def is_valid(self): '''checks if type is a valid type''' - return self.is_primitive or self.is_complex + return (self.is_primitive and self.name) \ + or (self.is_complex and self.name) \ + or (self.is_list and self.nested) \ + or (self.is_model and self.nested) \ @property def is_bool(self): @@ -220,6 +239,22 @@ class TypeSymbol(NamedElement): def type(self): return self + def toJson(self): + o = super().toJson() + if self.is_void: + o['void'] = self.is_void + if self.is_primitive: + o['primitive'] = self.is_primitive + if self.is_complex: + o['complex'] = self.is_complex + if self.is_list: + o['list'] = self.is_list + if self.is_model: + o['model'] = self.is_model + if self.nested: + o['nested'] = self.nested.toJson() + return o + class Module(Symbol): """Module is a namespace for types, e.g. interfaces, enums, structs""" @@ -283,6 +318,14 @@ class Module(Symbol): return symbol return self.system.lookup(name) + def toJson(self): + o = super().toJson() + o['version'] = self.version + o['interfaces'] = [s.toJson() for s in self.interfaces] + o['structs'] = [s.toJson() for s in self.structs] + o['enums'] = [s.toJson() for s in self.enums] + return o + class Interface(Symbol): """A interface is an object with operations, properties and signals""" @@ -310,6 +353,13 @@ class Interface(Symbol): '''returns ordered list of signals''' return self._signalMap.values() + def toJson(self): + o = super().toJson() + o['properties'] = [s.toJson() for s in self.properties] + o['operations'] = [s.toJson() for s in self.operations] + o['signals'] = [s.toJson() for s in self.signals] + return o + class Operation(Symbol): """An operation inside a interface""" @@ -325,6 +375,11 @@ class Operation(Symbol): '''returns ordered list of parameters''' return self._parameterMap.values() + def toJson(self): + o = super().toJson() + o['parameters'] = [s.toJson() for s in self.parameters] + return o + class Signal(Symbol): """A signal inside an interface""" @@ -340,6 +395,11 @@ class Signal(Symbol): '''returns ordered list of parameters''' return self._parameterMap.values() + def toJson(self): + o = super().toJson() + o['parameters'] = [s.toJson() for s in self.parameters] + return o + class Parameter(Symbol): """An operation parameter""" @@ -360,6 +420,14 @@ class Property(Symbol): self.readonly = False self.const = False + def toJson(self): + o = super().toJson() + if self.readonly: + o['readonly'] = True + if self.const: + o['const'] = True + return o + class Struct(Symbol): """Represents a data container""" @@ -374,6 +442,11 @@ class Struct(Symbol): '''returns ordered list of members''' return self._fieldMap.values() + def toJson(self): + o = super().toJson() + o['fields'] = [s.toJson() for s in self.fields] + return o + class Field(Symbol): """A member in a struct""" @@ -399,6 +472,15 @@ class Enum(Symbol): '''returns ordered list of members''' return self._memberMap.values() + def toJson(self): + o = super().toJson() + if self.is_enum: + o['enum'] = self.is_enum + if self.is_flag: + o['flag'] = self.is_flag + o['members'] = [s.toJson() for s in self.members] + return o + class EnumMember(Symbol): """A enum value""" @@ -408,3 +490,9 @@ class EnumMember(Symbol): self.enum = enum self.enum._memberMap[name] = self self.value = 0 + + def toJson(self): + o = super().toJson() + o['value'] = self.value + return o + diff --git a/tests/test_json.py b/tests/test_json.py new file mode 100644 index 0000000..b4fea24 --- /dev/null +++ b/tests/test_json.py @@ -0,0 +1,51 @@ +from qface.generator import FileSystem +import logging +from path import Path +import json + +# logging.config.fileConfig('logging.ini') +logging.basicConfig() + +log = logging.getLogger(__name__) + +inputPath = Path('tests/in') + + +def loadEcho(): + path = inputPath / 'org.example.echo.qface' + return FileSystem.parse_document(path) + + +def load_tuner(): + path = inputPath / 'com.pelagicore.ivi.tuner.qface' + return FileSystem.parse_document(path) + + +def test_echo_json(): + system = loadEcho() + data = system.toJson() + text = json.dumps(data) + data = json.loads(text) + assert len(data['modules']) == 1 + module = data['modules'][0] + assert module['name'] == 'org.example.echo' + assert module['version'] == '1.0' + assert len(module['interfaces']) == 1 + interface = module['interfaces'][0] + assert interface['name'] == 'Echo' + assert len(interface['operations']) == 1 + # string echo(string msg); + operation = interface['operations'][0] + assert operation['parameters'][0]['name'] == 'msg' + assert operation['parameters'][0]['type']['name'] == 'string' + + +def test_tuner_json(): + system = load_tuner() + data = system.toJson() + text = json.dumps(data) + data = json.loads(text) + module = data['modules'][0] + interface = module['interfaces'][0] + assert interface['name'] == 'Tuner' + |