summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjryannel <juergen@ryannel.org>2017-04-19 10:33:51 +0200
committerGitHub <noreply@github.com>2017-04-19 10:33:51 +0200
commit80c7b9cedabb7db5356981cada2561f6d29b7b99 (patch)
tree8584b59be413e964f63595fb6939df119cc43817
parent7a9f38795010772e117de6ad4b3b32ed442234e7 (diff)
downloadqtivi-qface-80c7b9cedabb7db5356981cada2561f6d29b7b99.tar.gz
Added support for JSON serialization (#43)
Added jsonify filter
-rw-r--r--qface/builtin/schema/log.yaml18
-rwxr-xr-xqface/builtin/schema/schema.py55
-rw-r--r--qface/builtin/schema/templates/module.json1
-rw-r--r--qface/filters.py10
-rw-r--r--qface/idl/domain.py90
-rw-r--r--tests/test_json.py51
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'
+