Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apply reserved keywords mapping #181

Merged
merged 3 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 83 additions & 52 deletions generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import re
import sys
from argparse import ArgumentParser
from collections import namedtuple
from collections import namedtuple, OrderedDict
from datetime import date
from inspect import getfile
from itertools import groupby
Expand All @@ -27,11 +27,10 @@
from parsers.rpc_base import ParseError
from model.interface import Interface
from transformers.generate_error import GenerateError
from transformers.common_producer import InterfaceProducerCommon
from transformers.enums_producer import EnumsProducer
from transformers.functions_producer import FunctionsProducer
from transformers.structs_producer import StructsProducer
except ModuleNotFoundError as message:
except ImportError as message:
print('%s.\nprobably you did not initialize submodule', message)
sys.exit(1)

Expand All @@ -52,6 +51,16 @@ def __init__(self):
'path_to_response_class path_to_notification_class enums_dir_name '
'structs_dir_name functions_dir_name rpc_creator')

_version = '1.0.0'

@property
def get_version(self) -> str:
"""
version of the entire generator
:return: current entire generator version
"""
return self._version

@property
def env(self):
"""
Expand All @@ -70,30 +79,28 @@ def env(self, value):
sys.exit(1)
else:
self._env = Environment(loader=FileSystemLoader(value))

@property
def get_version(self):
"""
:return: current version of Generator
"""
return InterfaceProducerCommon.version
self._env.globals['year'] = date.today().year

def config_logging(self, verbose):
"""
Configure logging
:param verbose: boolean
Configuring logging for all application
:param verbose: if True setting logging.DEBUG else logging.ERROR
:return: None
"""
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%m-%d %H:%M'))
handler.setFormatter(logging.Formatter(fmt='%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s',
datefmt='%H:%M:%S'))
root_logger = logging.getLogger()

if verbose:
handler.setLevel(logging.DEBUG)
self.logger.setLevel(logging.DEBUG)
root_logger.setLevel(logging.DEBUG)
else:
handler.setLevel(logging.ERROR)
self.logger.setLevel(logging.ERROR)
root_logger.setLevel(logging.ERROR)
logging.getLogger().handlers.clear()
root_logger = logging.getLogger()
root_logger.addHandler(handler)

def evaluate_output_directory(self, output_directory):
Expand Down Expand Up @@ -176,7 +183,7 @@ def get_parser(self):
self.logger.warning('%s set to %s', intermediate.name, intermediate.path)
setattr(args, intermediate.name, intermediate.path.as_posix())
break

confirm = input('Confirm default path {} for {} Y/Enter = yes, N = no'
.format(intermediate.path, intermediate.name))
if confirm.lower() == 'y' or not confirm:
Expand All @@ -193,7 +200,6 @@ def get_parser(self):

args.output_directory = self.evaluate_output_directory(args.output_directory)

self.logger.info('parsed arguments:\n%s', pformat((vars(args))))
return args

def versions_compatibility_validating(self):
Expand Down Expand Up @@ -221,37 +227,29 @@ def versions_compatibility_validating(self):
self.logger.info('Parser type: %s, version %s,\tGenerator version %s',
basename(getfile(Parser().__class__)), parser_origin, self.get_version)

def get_paths(self, file_name=ROOT.joinpath('paths.ini')):
def get_file_content(self, file_name: Path) -> list:
"""
:param file_name: path to file with Paths
:return: namedtuple with Paths to key elements

:param file_name:
:return:
"""
data = {}
try:
with file_name.open('r') as file:
for line in file:
if line.startswith('#'):
self.logger.warning('commented property %s, which will be skipped', line.strip())
continue
if re.match(r'^(\w+)\s?=\s?(.+)', line):
if len(line.split('=')) > 2:
self.logger.critical('can not evaluate value, too many separators %s', str(line))
sys.exit(1)
name, var = line.partition('=')[::2]
if name.strip() in data:
self.logger.critical('duplicate key %s', name)
sys.exit(1)
data[name.strip().lower()] = var.strip()
content = file.readlines()
return content
except FileNotFoundError as message1:
self.logger.critical(message1)
sys.exit(1)

missed = list(set(self.paths_named._fields) - set(data.keys()))
if missed:
self.logger.critical('in %s missed fields: %s ', file, str(missed))
sys.exit(1)
self.logger.error(message1)
return []

return self.paths_named(**data)
def get_key_words(self, file_name=ROOT.parents[0].joinpath('lib/rpc_spec/RpcParser/RESERVED_KEYWORDS')):
"""
:param file_name:
:return:
"""
content = self.get_file_content(file_name)
content = tuple(map(lambda e: re.sub(r'\n', r'', e).strip().casefold(), content))
content = tuple(filter(lambda e: not re.search(r'^#+\s+.+|^$', e), content))
return content

def get_mappings(self, file_name=ROOT.joinpath('mapping.json')):
"""
Expand All @@ -260,14 +258,44 @@ def get_mappings(self, file_name=ROOT.joinpath('mapping.json')):
:return: dictionary with custom manual mappings
"""

content = self.get_file_content(file_name)
try:
with file_name.open('r') as file:
intermediate = file.readlines()
return loads(''.join(intermediate))
except (FileNotFoundError, JSONDecodeError) as message1:
return loads(''.join(content))
except JSONDecodeError as message1:
self.logger.error(message1)
return {}

def get_paths(self, file_name=ROOT.joinpath('paths.ini')):
"""
:param file_name: path to file with Paths
:return: namedtuple with Paths to key elements
"""
content = self.get_file_content(file_name)
if not content:
self.logger.critical('%s not found', file_name)
sys.exit(1)
data = OrderedDict()
for line in content:
if line.startswith('#'):
self.logger.warning('commented property %s, which will be skipped', line.strip())
continue
if re.match(r'^(\w+)\s?=\s?(.+)', line):
if len(line.split('=')) > 2:
self.logger.critical('can not evaluate value, too many separators %s', str(line))
sys.exit(1)
name, var = line.partition('=')[::2]
if name.strip() in data:
self.logger.critical('duplicate key %s', name)
sys.exit(1)
data[name.strip().lower()] = var.strip()

missed = list(set(self.paths_named._fields) - set(data.keys()))
if missed:
self.logger.critical('in %s missed fields: %s ', content, str(missed))
sys.exit(1)

return self.paths_named(**data)

def write_file(self, file_name, template, data):
"""
Calling producer/transformer instance to transform initial Model to dict used in jinja2 templates.
Expand Down Expand Up @@ -317,7 +345,7 @@ def process_function_name(self, file, dir_name, skip, overwrite, functions, tran
dir_name = dir_name[1:]

creator = namedtuple('creator', 'function_name class_name type')
data = {'name': file.stem, 'imports': [], 'cases': [], 'year': date.today().year, }
data = {'name': file.stem, 'imports': [], 'cases': []}

grouped = [{'name': k, 'type': [x for x in v]} for k, v in groupby(functions.values(), key=lambda x: x.name)]

Expand Down Expand Up @@ -353,6 +381,7 @@ def process_common(self, skip, overwrite, file, template, data):
return
if overwrite:
self.logger.info('Overriding %s', file.name)
file.unlink()
self.write_file(file, template, data)
else:
while True:
Expand Down Expand Up @@ -382,7 +411,7 @@ def filter_pattern(self, interface, pattern):
names = tuple(interface.enums.keys()) + tuple(interface.structs.keys())

if pattern:
match = {i: {} for i in vars(interface).keys()}
match = {i: OrderedDict() for i in vars(interface).keys()}
match['params'] = interface.params
empty = True
for key, value in vars(interface).items():
Expand Down Expand Up @@ -423,6 +452,7 @@ def main(self):
"""
args = self.get_parser()
self.config_logging(args.verbose)
self.logger.debug('parsed arguments:\n%s', pformat((vars(args))))
self.env = args.templates_directory

self.versions_compatibility_validating()
Expand All @@ -437,23 +467,24 @@ def main(self):

filtered, names = self.filter_pattern(interface, args.regex_pattern)

key_words = self.get_key_words()
mappings = self.get_mappings()

functions_transformer = FunctionsProducer(paths, names, mappings)
functions_transformer = FunctionsProducer(paths, names, mappings, key_words)
if args.enums and filtered.enums:
directory = args.output_directory.joinpath(self.evaluate_instance_directory(paths.enums_dir_name))
self.process(directory, args.skip, args.overwrite, filtered.enums,
EnumsProducer(paths, mappings))
EnumsProducer(paths, mappings, key_words))
if args.structs and filtered.structs:
directory = args.output_directory.joinpath(self.evaluate_instance_directory(paths.structs_dir_name))
self.process(directory, args.skip, args.overwrite, filtered.structs,
StructsProducer(paths, names, mappings))
StructsProducer(paths, names, mappings, key_words))
if args.functions and filtered.functions:
directory = args.output_directory.joinpath(self.evaluate_instance_directory(paths.functions_dir_name))
self.process(directory, args.skip, args.overwrite, filtered.functions, functions_transformer)
self.process_function_name(args.output_directory.joinpath(paths.rpc_creator), paths.functions_dir_name,
args.skip, args.overwrite, interface.functions, functions_transformer,
mappings.get('functions', {}))
mappings.get('functions', OrderedDict()))


if __name__ == '__main__':
Expand Down
5 changes: 2 additions & 3 deletions generator/test/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from test_functions import TestFunctionsProducer
from test_structs import TestStructsProducer
from test_code_format_and_quality import CodeFormatAndQuality
except ModuleNotFoundError as message:
except ImportError as message:
print('{}.\nProbably you did not initialize submodule'.format(message))
sys.exit(1)

Expand All @@ -33,8 +33,7 @@ def main():
suite.addTests(TestLoader().loadTestsFromTestCase(CodeFormatAndQuality))

runner = TextTestRunner(verbosity=2)
test_result = runner.run(suite)
print(test_result)
runner.run(suite)


if __name__ == '__main__':
Expand Down
75 changes: 35 additions & 40 deletions generator/test/test_enums.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections import namedtuple
from datetime import date
from collections import namedtuple, OrderedDict
from unittest import TestCase

from model.enum import Enum
Expand All @@ -17,45 +16,41 @@ def setUp(self):
self.producer = EnumsProducer(paths)

def test_FunctionID(self):
item = Enum(name='FunctionID', elements={
'RESERVED': EnumElement(name='RESERVED', value=0),
'RegisterAppInterfaceID': EnumElement(name='RegisterAppInterfaceID', hex_value=1),
'PerformAudioPassThruID': EnumElement(name='PerformAudioPassThruID', hex_value=10)
})
expected = {
'name': 'FunctionID',
'imports': {self.producer.imports(what='Enum', wherefrom='../../util/Enum.js')},
'methods': [self.producer.methods(method_title='RESERVED',
description=[], type='Number'),
self.producer.methods(method_title='RegisterAppInterface',
description=[], type='Number'),
self.producer.methods(method_title='PerformAudioPassThru',
description=[], type='Number')],
'params': [self.producer.params(key='RESERVED', value=0),
self.producer.params(key='RegisterAppInterface', value='0x01'),
self.producer.params(key='PerformAudioPassThru', value='0x10')],
'extend': 'Enum'
}
elements = OrderedDict()
elements['RESERVED'] = EnumElement(name='RESERVED', value=0)
elements['RegisterAppInterfaceID'] = EnumElement(name='RegisterAppInterfaceID', hex_value=1)
elements['PerformAudioPassThruID'] = EnumElement(name='PerformAudioPassThruID', hex_value=10)

item = Enum(name='FunctionID', elements=elements)
expected = OrderedDict()
expected['file_name'] = 'FunctionID'
expected['name'] = 'FunctionID'
expected['imports'] = {self.producer.imports(what='Enum', wherefrom='../../util/Enum.js')}
expected['methods'] = (self.producer.methods(method_title='RESERVED',
description=[], type='Number'),
self.producer.methods(method_title='RegisterAppInterface',
description=[], type='Number'),
self.producer.methods(method_title='PerformAudioPassThru',
description=[], type='Number'))
expected['params'] = (self.producer.params(key='RESERVED', value="'RESERVED'"),
self.producer.params(key='RegisterAppInterface', value='0x01'),
self.producer.params(key='PerformAudioPassThru', value='0x10'))
expected['extend'] = 'Enum'
result = self.producer.transform(item)
self.assertEqual(expected['name'], result['name'])
self.assertListEqual(sorted(expected['imports']), sorted(result['imports']))
self.assertListEqual(sorted(expected['methods']), sorted(result['methods']))
self.assertListEqual(sorted(expected['params']), sorted(result['params']))
self.assertEqual(expected['extend'], result['extend'])
self.assertDictEqual(expected, result)

def test_Result(self):
item = Enum(name='Result', elements={
'SUCCESS': EnumElement(name='SUCCESS')
})
expected = {
'year': date.today().year,
'name': 'Result',
'file_name': 'Result',
'imports': {self.producer.imports(what='Enum', wherefrom='../../util/Enum.js')},
'methods': tuple([self.producer.methods(method_title='SUCCESS',
description=[], type='String')]),
'params': tuple([self.producer.params(key='SUCCESS', value="'SUCCESS'")]),
'extend': 'Enum'
}
elements = OrderedDict()
elements['SUCCESS'] = EnumElement(name='SUCCESS')
item = Enum(name='Result', elements=elements)
expected = OrderedDict()
expected['file_name'] = 'Result'
expected['name'] = 'Result'
expected['file_name'] = 'Result'
expected['imports'] = {self.producer.imports(what='Enum', wherefrom='../../util/Enum.js')}
expected['methods'] = (self.producer.methods(method_title='SUCCESS',
description=[], type='String'),)
expected['params'] = (self.producer.params(key='SUCCESS', value="'SUCCESS'"),)
expected['extend'] = 'Enum'
result = self.producer.transform(item)
self.assertEqual(expected, result)
self.assertDictEqual(expected, result)
Loading