Skip to content

Commit

Permalink
Merge branch 'release/1.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
Juergen Bocklage-Ryannel committed Jul 26, 2017
2 parents 715e324 + a9bf9bc commit f5cf4c3
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 30 deletions.
17 changes: 12 additions & 5 deletions MANIFEST.in
@@ -1,5 +1,12 @@
include LICENSE.GPLV3
include qface/builtin/qtcpp/templates/*
include qface/builtin/qtcpp/log.yaml
include qface/builtin/qtqml/templates/*
include qface/builtin/qtqml/log.yaml
include LICENSE
include README.md

graft qface/templates

global-exclude *.so
global-exclude *.pyc
global-exclude *~
global-exclude \#*
global-exclude .git*
global-exclude .DS_Store

2 changes: 1 addition & 1 deletion qface/__about__.py
Expand Up @@ -9,7 +9,7 @@
__title__ = "qface"
__summary__ = "A generator framework based on a common modern IDL"
__url__ = "https://pelagicore.github.io/qface/"
__version__ = "1.5"
__version__ = "1.6"
__author__ = "JRyannel"
__author_email__ = "qface-generator@googlegroups.com"
__copyright__ = "2017 Pelagicore"
28 changes: 16 additions & 12 deletions qface/filters.py
Expand Up @@ -2,25 +2,29 @@
import hashlib


def jsonify(obj):
def jsonify(symbol):
""" returns json format for symbol """
try:
# all symbols have a toJson method, try it
return json.dumps(obj.toJson(), indent=' ')
return json.dumps(symbol.toJson(), indent=' ')
except AttributeError:
pass
return json.dumps(obj, indent=' ')
return json.dumps(symbol, indent=' ')


def upper_first(s):
s = str(s)
return s[0].upper() + s[1:]
def upper_first(symbol):
""" uppercase first letter """
name = str(symbol)
return name[0].upper() + name[1:]


def hash(s, hash_type='sha1'):
h = hashlib.new(hash_type)
h.update(str(s).encode('utf-8'))
return h.hexdigest()
def hash(symbol, hash_type='sha1'):
""" create a hash code from symbol """
code = hashlib.new(hash_type)
code.update(str(symbol).encode('utf-8'))
return code.hexdigest()


def path(s):
return str(s).replace('.', '/')
def path(symbol):
""" replaces '.' with '/' """
return str(symbol).replace('.', '/')
55 changes: 51 additions & 4 deletions qface/generator.py
@@ -1,16 +1,18 @@

# Copyright (c) Pelagicore AB 2016

from jinja2 import Environment, Template
from jinja2 import FileSystemLoader, PackageLoader, ChoiceLoader
from jinja2 import TemplateSyntaxError, TemplateNotFound, TemplateError
from path import Path
from antlr4 import FileStream, CommonTokenStream, ParseTreeWalker
from antlr4.error import DiagnosticErrorListener
from antlr4.error import DiagnosticErrorListener, ErrorListener
import shelve
import logging
import hashlib
import yaml
import click
import sys

from .idl.parser.TLexer import TLexer
from .idl.parser.TParser import TParser
Expand Down Expand Up @@ -43,8 +45,30 @@ def lower_first_filter(s):
return s[0].lower() + s[1:]


class ReportingErrorListener(ErrorListener.ErrorListener):
def __init__(self, document):
self.document = document

def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
msg = '{0}:{1}:{2} {2}'.format(self.document, line, column, msg)
click.secho(msg, fg='red')
raise ValueError(msg)

def reportAmbiguity(self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs):
click.secho('ambiguity', fg='red')

def reportAttemptingFullContext(self, recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs):
click.secho('reportAttemptingFullContext', fg='red')

def reportContextSensitivity(self, recognizer, dfa, startIndex, stopIndex, prediction, configs):
click.secho('reportContextSensitivity', fg='red')


class Generator(object):
"""Manages the templates and applies your context data"""
strict = False
""" enables strict code generation """

def __init__(self, search_path: str):
loader = ChoiceLoader([
FileSystemLoader(search_path),
Expand Down Expand Up @@ -86,18 +110,24 @@ def write(self, file_path: Path, template: str, context: dict, preserve: bool =
"""Using a template file name it renders a template
into a file given a context
"""
error = False
try:
self._write(file_path, template, context, preserve)
except TemplateSyntaxError as exc:
# import pdb; pdb.set_trace()
message = '{0}:{1} error: {2}'.format(exc.filename, exc.lineno, exc.message)
click.secho(message, fg='red')
error = True
except TemplateNotFound as exc:
message = '{0} error: Template not found'.format(exc.name)
click.secho(message, fg='red')
error = True
except TemplateError as exc:
message = 'error: {0}'.format(exc.message)
click.secho(message, fg='red')
error = True
if error and Generator.strict:
sys.exit(-1)

def _write(self, file_path: Path, template: str, context: dict, preserve: bool = False):
path = self.destination / Path(self.apply(file_path, context))
Expand Down Expand Up @@ -125,29 +155,46 @@ def register_filter(self, name, callback):

class FileSystem(object):
"""QFace helper functions to work with the file system"""
strict = False
""" enables strict parsing """

@staticmethod
def parse_document(document: Path, system: System = None):
error = False
try:
return FileSystem._parse_document(document, system)
except FileNotFoundError as e:
click.secho('{0}: file not found'.format(document), fg='red')
error = True
except ValueError as e:
click.secho('Error parsing document {0}'.format(document))
error = True
if error and FileSystem.strict:
sys.exit(-1)


@staticmethod
def _parse_document(document: Path, system: System = None):
"""Parses a document and returns the resulting domain system
:param path: document path to parse
:param system: system to be used (optional)
"""
logger.debug('parse document: {0}'.format(document))
stream = FileStream(str(document), encoding='utf-8')
system = FileSystem._parse_stream(stream, system)
system = FileSystem._parse_stream(stream, system, document)
FileSystem.merge_annotations(system, document.stripext() + '.yaml')
return system

@staticmethod
def _parse_stream(stream, system: System = None):
def _parse_stream(stream, system: System = None, document=None):
logger.debug('parse stream')
system = system or System()

lexer = TLexer(stream)
stream = CommonTokenStream(lexer)
parser = TParser(stream)
parser.addErrorListener(DiagnosticErrorListener.DiagnosticErrorListener())
parser.addErrorListener(ReportingErrorListener(document))
tree = parser.documentSymbol()
walker = ParseTreeWalker()
walker.walk(DomainListener(system), tree)
Expand Down
4 changes: 3 additions & 1 deletion qface/helper/doc.py
Expand Up @@ -21,7 +21,7 @@ class DocObject:
The documentation object passed into the template engine
"""
def __init__(self):
self.brief = str()
self.brief = []
self.description = []
self.see = []
self.deprecated = False
Expand Down Expand Up @@ -78,4 +78,6 @@ def parse_doc(s):
doc.add_tag(tag, value)
elif tag: # append to previous matched tag
doc.add_tag(tag, line)
else: # append any loose lines to description
doc.add_tag('description', line)
return doc
2 changes: 2 additions & 0 deletions qface/helper/qtcpp.py
Expand Up @@ -39,6 +39,8 @@ def defaultValue(symbol):
module_name = upper_first(t.reference.module.module_name)
value = next(iter(t.reference.members))
return '{0}{1}Module::{2}'.format(prefix, module_name, value)
elif t.is_flag:
return '0'
elif symbol.type.is_list:
nested = Filters.returnType(symbol.type.nested)
return 'QVariantList()'.format(nested)
Expand Down
2 changes: 2 additions & 0 deletions qface/idl/domain.py
Expand Up @@ -125,6 +125,7 @@ def __init__(self, name: str, module: 'Module'):

self._contentMap = ChainMap()
self.type = TypeSymbol('', self)
self.kind = self.__class__.__name__.lower()
""" the associated type information """

@property
Expand Down Expand Up @@ -431,6 +432,7 @@ def parameters(self):
def toJson(self):
o = super().toJson()
o['parameters'] = [s.toJson() for s in self.parameters]
o['type'] = self.type.toJson()
return o


Expand Down
1 change: 1 addition & 0 deletions qface/idl/listener.py
Expand Up @@ -214,6 +214,7 @@ def enterEnumMemberSymbol(self, ctx: TParser.EnumMemberSymbolContext):
if ctx.intSymbol():
value = int(ctx.intSymbol().value.text, 0)
self.field.value = value
self.parse_annotations(ctx, self.field)
contextMap[ctx] = self.field
if self.enum.is_flag:
self.enumCounter <<= 1
Expand Down
Empty file removed qface/templates/__init__.py
Empty file.
Empty file removed qface/templates/qface/__init__.py
Empty file.
3 changes: 0 additions & 3 deletions setup.py
Expand Up @@ -43,9 +43,6 @@
keywords='qt code generator framework',
packages=find_packages(),
include_package_data=True,
package_data={
'': ['*[!*.pyc]']
},
install_requires=[
'jinja2',
'path.py',
Expand Down
2 changes: 1 addition & 1 deletion tests/in/com.pelagicore.ivi.tuner.qface
Expand Up @@ -2,7 +2,7 @@ module com.pelagicore.ivi.tuner 1.0;


interface BaseTuner {
property int baseValue;
int baseValue;
}


Expand Down
2 changes: 2 additions & 0 deletions tests/in/org.example.echo.qface
Expand Up @@ -10,6 +10,8 @@ module org.example.echo 1.0
* @see org.example
* @see http://qt.io
* @anything hello
*
* continued description
*/
interface Echo {
/**
Expand Down
5 changes: 5 additions & 0 deletions tests/in/org.example.failing.qface
@@ -0,0 +1,5 @@
module org.example 1.0

interfase xx Failed {

}
6 changes: 3 additions & 3 deletions tests/test_comments.py
Expand Up @@ -35,8 +35,8 @@ def test_comment():
interface = system.lookup('org.example.echo.Echo')
assert interface
o = doc.parse_doc(interface.comment)
assert o.brief == 'the brief'
assert o.description == ['the description', 'continues {@link http://qt.io}']
assert o.brief == ['the brief']
assert o.description == ['the description', 'continues {@link http://qt.io}', 'continued description']
assert o.deprecated is True
assert o.see == ['org.example.echo.Echo', 'org.example', 'http://qt.io']

Expand All @@ -50,4 +50,4 @@ def test_qdoc_translate():
assert interface
doc.translate = qdoc_translate
o = doc.parse_doc(interface.comment)
assert o.description == ['the description', 'continues \\link{http://qt.io}']
assert o.description == ['the description', 'continues \\link{http://qt.io}', 'continued description']
22 changes: 22 additions & 0 deletions tests/test_parser.py
@@ -1,5 +1,6 @@
import logging
import logging.config
import pytest
from path import Path

from qface.generator import FileSystem
Expand Down Expand Up @@ -190,3 +191,24 @@ def test_interface_property():
assert prop.type.is_interface
assert prop.type.reference is extension


def test_symbol_kind():
system = load_tuner()
tuner = system.lookup('com.pelagicore.ivi.tuner.Tuner')
assert tuner.kind == 'interface'
property = system.lookup('com.pelagicore.ivi.tuner.Tuner#primitiveModel')
assert property.kind == 'property'


def test_parser_exceptions():
path = inputPath / 'org.example.failing.qface'
system = FileSystem.parse_document(path)

try:
system = FileSystem.parse_document('not-exists')
except SystemExit as e:
pass
else:
pytest.fail('should not ome here')


14 changes: 14 additions & 0 deletions tests/test_qtcpp_helper.py
Expand Up @@ -23,6 +23,7 @@
void echo(string message);
Message message;
Status status;
ApplicationState state;
list<int> list001;
list<Message> list002;
model<int> model001;
Expand All @@ -38,6 +39,14 @@
ON,
OFF
}
flag ApplicationState {
Suspended,
Hidden,
Inactive,
Active,
}
"""


Expand Down Expand Up @@ -127,6 +136,11 @@ def test_default_value():
answer = qtcpp.Filters.defaultValue(prop)
assert answer == 'ExampleModule::ON'

# check for flag
prop = interface._propertyMap['state']
answer = qtcpp.Filters.defaultValue(prop)
assert answer == '0'

# check for list of primitive
prop = interface._propertyMap['list001']
answer = qtcpp.Filters.defaultValue(prop)
Expand Down

0 comments on commit f5cf4c3

Please sign in to comment.