From 7f7fa2f91467fdb2b11312be8562e7b51fdeb199 Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski <sebastian.koslowski@gmail.com> Date: Tue, 3 May 2016 17:13:08 +0200 Subject: grc: added yaml/mako support Includes basic converter from XML/Cheetah to YAML/Mako based block format. --- grc/core/blocks/_build.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 grc/core/blocks/_build.py (limited to 'grc/core/blocks/_build.py') diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py new file mode 100644 index 0000000000..9a50086cea --- /dev/null +++ b/grc/core/blocks/_build.py @@ -0,0 +1,69 @@ +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import absolute_import + +import re + +from .block import Block +from ._flags import Flags +from ._templates import MakoTemplates + + +def build(id, label='', category='', flags='', documentation='', + checks=None, value=None, + parameters=None, inputs=None, outputs=None, templates=None, **kwargs): + block_id = id + + cls = type(block_id, (Block,), {}) + cls.key = block_id + + cls.label = label or block_id.title() + cls.category = [cat.strip() for cat in category.split('/') if cat.strip()] + + cls.flags = Flags(flags) + if re.match(r'options$|variable|virtual', block_id): + cls.flags += Flags.NOT_DSP + Flags.DISABLE_BYPASS + + cls.documentation = {'': documentation.strip('\n\t ').replace('\\\n', '')} + + cls.checks = [_single_mako_expr(check, block_id) for check in (checks or [])] + + cls.parameters_data = parameters or [] + cls.inputs_data = inputs or [] + cls.outputs_data = outputs or [] + cls.extra_data = kwargs + + templates = templates or {} + cls.templates = MakoTemplates( + imports=templates.get('imports', ''), + make=templates.get('make', ''), + callbacks=templates.get('callbacks', []), + var_make=templates.get('var_make', ''), + ) + # todo: MakoTemplates.compile() to check for errors + + cls.value = _single_mako_expr(value, block_id) + + return cls + + +def _single_mako_expr(value, block_id): + match = re.match(r'\s*\$\{\s*(.*?)\s*\}\s*', str(value)) + if value and not match: + raise ValueError('{} is not a mako substitution in {}'.format(value, block_id)) + return match.group(1) if match else None -- cgit v1.2.3 From ff8caa034ac65c0c28b2a542b7e72f23390ccd46 Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski <sebastian.koslowski@gmail.com> Date: Sun, 1 Oct 2017 09:51:27 +0200 Subject: [grc] fix conversion of <check> and rename them to asserts --- grc/converter/block.py | 6 +++--- grc/core/blocks/_build.py | 4 ++-- grc/core/blocks/block.py | 14 +++++++------- grc/core/schema_checker/block.py | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'grc/core/blocks/_build.py') diff --git a/grc/converter/block.py b/grc/converter/block.py index 04e5c905a0..0e362d97c0 100644 --- a/grc/converter/block.py +++ b/grc/converter/block.py @@ -95,14 +95,14 @@ def convert_block_xml(node): data['outputs'] = [convert_port_xml(port_node, converter.to_python_dec) for port_node in node.iterfind('source')] or no_value - - data['checks'] = [converter.to_python_dec(check_node.text) - for check_node in node.iterfind('checks')] or no_value data['value'] = ( converter.to_python_dec(node.findtext('var_value')) or ('${ value }' if block_id.startswith('variable') else no_value) ) + data['asserts'] = [converter.to_python_dec(check_node.text) + for check_node in node.iterfind('check')] or no_value + data['templates'] = convert_templates(node, converter.to_mako, block_id) or no_value docs = node.findtext('doc') diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py index 9a50086cea..9221433387 100644 --- a/grc/core/blocks/_build.py +++ b/grc/core/blocks/_build.py @@ -25,7 +25,7 @@ from ._templates import MakoTemplates def build(id, label='', category='', flags='', documentation='', - checks=None, value=None, + value=None, asserts=None, parameters=None, inputs=None, outputs=None, templates=None, **kwargs): block_id = id @@ -41,7 +41,7 @@ def build(id, label='', category='', flags='', documentation='', cls.documentation = {'': documentation.strip('\n\t ').replace('\\\n', '')} - cls.checks = [_single_mako_expr(check, block_id) for check in (checks or [])] + cls.asserts = [_single_mako_expr(a, block_id) for a in (asserts or [])] cls.parameters_data = parameters or [] cls.inputs_data = inputs or [] diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py index 0d8a779486..adc046936d 100644 --- a/grc/core/blocks/block.py +++ b/grc/core/blocks/block.py @@ -55,7 +55,7 @@ class Block(Element): documentation = {'': ''} value = None - checks = [] + asserts = [] templates = MakoTemplates() parameters_data = [] @@ -190,18 +190,18 @@ class Block(Element): Evaluate the checks: each check must evaluate to True. """ Element.validate(self) - self._run_checks() + self._run_asserts() self._validate_generate_mode_compat() self._validate_var_value() - def _run_checks(self): + def _run_asserts(self): """Evaluate the checks""" - for check in self.checks: + for expr in self.asserts: try: - if not self.evaluate(check): - self.add_error_message('Check "{}" failed.'.format(check)) + if not self.evaluate(expr): + self.add_error_message('Assertion "{}" failed.'.format(expr)) except: - self.add_error_message('Check "{}" did not evaluate.'.format(check)) + self.add_error_message('Assertion "{}" did not evaluate.'.format(expr)) def _validate_generate_mode_compat(self): """check if this is a GUI block and matches the selected generate option""" diff --git a/grc/core/schema_checker/block.py b/grc/core/schema_checker/block.py index db8830fddf..ea079b4276 100644 --- a/grc/core/schema_checker/block.py +++ b/grc/core/schema_checker/block.py @@ -44,7 +44,7 @@ BLOCK_SCHEME = expand( inputs=Spec(types=list, required=False, item_scheme=PORT_SCHEME), outputs=Spec(types=list, required=False, item_scheme=PORT_SCHEME), - checks=(list, str_), + asserts=(list, str_), value=str_, templates=Spec(types=dict, required=False, item_scheme=TEMPLATES_SCHEME), -- cgit v1.2.3 From 1fa89b3704d7f476e4395eb9358d5a6d7642251b Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski <sebastian.koslowski@gmail.com> Date: Thu, 26 Oct 2017 20:15:22 +0200 Subject: grc: disable auto-conversion and implement json cache --- grc/core/Config.py | 6 -- grc/core/Constants.py | 2 + grc/core/blocks/__init__.py | 1 + grc/core/blocks/_build.py | 10 +-- grc/core/cache.py | 99 +++++++++++++++++++++++++++++ grc/core/platform.py | 109 +++++++++++++++----------------- grc/core/schema_checker/block.py | 2 +- grc/core/utils/__init__.py | 12 ++++ grc/core/utils/descriptors/evaluated.py | 6 +- grc/gui/Platform.py | 4 +- 10 files changed, 178 insertions(+), 73 deletions(-) create mode 100644 grc/core/cache.py (limited to 'grc/core/blocks/_build.py') diff --git a/grc/core/Config.py b/grc/core/Config.py index eb53e1751d..4accb74c63 100644 --- a/grc/core/Config.py +++ b/grc/core/Config.py @@ -31,8 +31,6 @@ class Config(object): hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR) - yml_block_cache = os.path.expanduser('~/.cache/grc_gnuradio') # FIXME: remove this as soon as converter is stable - def __init__(self, version, version_parts=None, name=None, prefs=None): self._gr_prefs = prefs if prefs else DummyPrefs() self.version = version @@ -40,9 +38,6 @@ class Config(object): if name: self.name = name - if not os.path.exists(self.yml_block_cache): - os.mkdir(self.yml_block_cache) - @property def block_paths(self): path_list_sep = {'/': ':', '\\': ';'}[os.path.sep] @@ -50,7 +45,6 @@ class Config(object): paths_sources = ( self.hier_block_lib_dir, os.environ.get('GRC_BLOCKS_PATH', ''), - self.yml_block_cache, self._gr_prefs.get_string('grc', 'local_blocks_path', ''), self._gr_prefs.get_string('grc', 'global_blocks_path', ''), ) diff --git a/grc/core/Constants.py b/grc/core/Constants.py index fc5383378c..59c5dff35e 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -31,6 +31,8 @@ BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') DEFAULT_HIER_BLOCK_LIB_DIR = os.path.expanduser('~/.grc_gnuradio') +CACHE_FILE = os.path.expanduser('~/.cache/grc_gnuradio/cache.json') + BLOCK_DESCRIPTION_FILE_FORMAT_VERSION = 1 # File format versions: # 0: undefined / legacy diff --git a/grc/core/blocks/__init__.py b/grc/core/blocks/__init__.py index e4a085d477..4ca0d5d2bc 100644 --- a/grc/core/blocks/__init__.py +++ b/grc/core/blocks/__init__.py @@ -29,6 +29,7 @@ build_ins = {} def register_build_in(cls): + cls.loaded_from = '(build-in)' build_ins[cls.key] = cls return cls diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py index 9221433387..ce3496d9c4 100644 --- a/grc/core/blocks/_build.py +++ b/grc/core/blocks/_build.py @@ -29,7 +29,7 @@ def build(id, label='', category='', flags='', documentation='', parameters=None, inputs=None, outputs=None, templates=None, **kwargs): block_id = id - cls = type(block_id, (Block,), {}) + cls = type(str(block_id), (Block,), {}) cls.key = block_id cls.label = label or block_id.title() @@ -63,7 +63,9 @@ def build(id, label='', category='', flags='', documentation='', def _single_mako_expr(value, block_id): - match = re.match(r'\s*\$\{\s*(.*?)\s*\}\s*', str(value)) - if value and not match: + if not value: + return None + value = value.strip() + if not (value.startswith('${') and value.endswith('}')): raise ValueError('{} is not a mako substitution in {}'.format(value, block_id)) - return match.group(1) if match else None + return value[2:-1].strip() diff --git a/grc/core/cache.py b/grc/core/cache.py new file mode 100644 index 0000000000..b72255ce1f --- /dev/null +++ b/grc/core/cache.py @@ -0,0 +1,99 @@ +# Copyright 2017 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import absolute_import, print_function + +from io import open +import json +import logging +import os + +import six + +from .io import yaml + +logger = logging.getLogger(__name__) + + +class Cache(object): + + def __init__(self, filename): + self.cache_file = filename + self.cache = {} + self.need_cache_write = True + self._accessed_items = set() + try: + os.makedirs(os.path.dirname(filename)) + except OSError: + pass + try: + self._converter_mtime = os.path.getmtime(filename) + except OSError: + self._converter_mtime = -1 + + def load(self): + try: + logger.debug("Loading block cache from: {}".format(self.cache_file)) + with open(self.cache_file, encoding='utf-8') as cache_file: + self.cache = json.load(cache_file) + self.need_cache_write = False + except (IOError, ValueError): + self.need_cache_write = True + + def get_or_load(self, filename): + self._accessed_items.add(filename) + if os.path.getmtime(filename) <= self._converter_mtime: + try: + return self.cache[filename] + except KeyError: + pass + + with open(filename, encoding='utf-8') as fp: + data = yaml.safe_load(fp) + self.cache[filename] = data + self.need_cache_write = True + return data + + def save(self): + if not self.need_cache_write: + return + + logger.info('Saving %d entries to json cache', len(self.cache)) + with open(self.cache_file, 'wb') as cache_file: + json.dump(self.cache, cache_file, encoding='utf-8') + + def prune(self): + for filename in (set(self.cache) - self._accessed_items): + del self.cache[filename] + + def __enter__(self): + self.load() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.save() + + +def byteify(data): + if isinstance(data, dict): + return {byteify(key): byteify(value) for key, value in six.iteritems(data)} + elif isinstance(data, list): + return [byteify(element) for element in data] + elif isinstance(data, six.text_type) and six.PY2: + return data.encode('utf-8') + else: + return data diff --git a/grc/core/platform.py b/grc/core/platform.py index 538bacade2..54deef3455 100644 --- a/grc/core/platform.py +++ b/grc/core/platform.py @@ -19,7 +19,6 @@ from __future__ import absolute_import, print_function from codecs import open from collections import namedtuple -import glob import os import logging from itertools import chain @@ -33,6 +32,7 @@ from . import ( ) from .Config import Config +from .cache import Cache from .base import Element from .io import yaml from .generator import Generator @@ -141,44 +141,41 @@ class Platform(Element): self.connection_templates.clear() self._block_categories.clear() - # FIXME: remove this as soon as converter is stable - from ..converter import Converter - converter = Converter(self.config.block_paths, self.config.yml_block_cache) - converter.run() - logging.info('XML converter done.') - - for file_path in self._iter_files_in_block_path(path): - try: - data = converter.cache[file_path] - except KeyError: - with open(file_path, encoding='utf-8') as fp: - data = yaml.safe_load(fp) - - if file_path.endswith('.block.yml'): - loader = self.load_block_description - scheme = schema_checker.BLOCK_SCHEME - elif file_path.endswith('.domain.yml'): - loader = self.load_domain_description - scheme = schema_checker.DOMAIN_SCHEME - elif file_path.endswith('.tree.yml'): - loader = self.load_category_tree_description - scheme = None - else: - continue - - try: - checker = schema_checker.Validator(scheme) - passed = checker.run(data) - for msg in checker.messages: - logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg)) - if not passed: - logger.info('YAML schema check failed for: ' + file_path) - - loader(data, file_path) - except Exception as error: - logger.exception('Error while loading %s', file_path) - logger.exception(error) - raise + # # FIXME: remove this as soon as converter is stable + # from ..converter import Converter + # converter = Converter(self.config.block_paths, self.config.yml_block_cache) + # converter.run() + # logging.info('XML converter done.') + + with Cache(Constants.CACHE_FILE) as cache: + for file_path in self._iter_files_in_block_path(path): + data = cache.get_or_load(file_path) + + if file_path.endswith('.block.yml'): + loader = self.load_block_description + scheme = schema_checker.BLOCK_SCHEME + elif file_path.endswith('.domain.yml'): + loader = self.load_domain_description + scheme = schema_checker.DOMAIN_SCHEME + elif file_path.endswith('.tree.yml'): + loader = self.load_category_tree_description + scheme = None + else: + continue + + try: + checker = schema_checker.Validator(scheme) + passed = checker.run(data) + for msg in checker.messages: + logger.warning('{:<40s} {}'.format(os.path.basename(file_path), msg)) + if not passed: + logger.info('YAML schema check failed for: ' + file_path) + + loader(data, file_path) + except Exception as error: + logger.exception('Error while loading %s', file_path) + logger.exception(error) + raise for key, block in six.iteritems(self.blocks): category = self._block_categories.get(key, block.category) @@ -201,10 +198,9 @@ class Platform(Element): if os.path.isfile(entry): yield entry elif os.path.isdir(entry): - pattern = os.path.join(entry, '**.' + ext) - yield_from = glob.iglob(pattern) - for file_path in yield_from: - yield file_path + for dirpath, dirnames, filenames in os.walk(entry): + for filename in sorted(filter(lambda f: f.endswith('.' + ext), filenames)): + yield os.path.join(dirpath, filename) else: logger.debug('Ignoring invalid path entry %r', entry) @@ -232,16 +228,18 @@ class Platform(Element): log.error('Unknown format version %d in %s', file_format, file_path) return - block_id = data.pop('id').rstrip('_') + block_id = data['id'] = data['id'].rstrip('_') if block_id in self.block_classes_build_in: log.warning('Not overwriting build-in block %s with %s', block_id, file_path) return if block_id in self.blocks: - log.warning('Block with id "%s" overwritten by %s', block_id, file_path) + log.warning('Block with id "%s" loaded from\n %s\noverwritten by\n %s', + block_id, self.blocks[block_id].loaded_from, file_path) try: - block_cls = self.blocks[block_id] = self.new_block_class(block_id, **data) + block_cls = self.blocks[block_id] = self.new_block_class(**data) + block_cls.loaded_from = file_path except errors.BlockLoadError as error: log.error('Unable to load block %s', block_id) log.exception(error) @@ -288,19 +286,12 @@ class Platform(Element): path = [] def load_category(name, elements): - if not isinstance(name, str): - log.debug('invalid name %r', name) - return - if isinstance(elements, list): - pass - elif isinstance(elements, str): - elements = [elements] - else: - log.debug('Ignoring elements of %s', name) + if not isinstance(name, six.string_types): + log.debug('Invalid name %r', name) return path.append(name) - for element in elements: - if isinstance(element, str): + for element in utils.to_list(elements): + if isinstance(element, six.string_types): block_id = element self._block_categories[block_id] = list(path) elif isinstance(element, dict): @@ -415,8 +406,8 @@ class Platform(Element): fg.import_data(data) return fg - def new_block_class(self, block_id, **data): - return blocks.build(block_id, **data) + def new_block_class(self, **data): + return blocks.build(**data) def make_block(self, parent, block_id, **kwargs): cls = self.block_classes[block_id] diff --git a/grc/core/schema_checker/block.py b/grc/core/schema_checker/block.py index ea079b4276..d511e36887 100644 --- a/grc/core/schema_checker/block.py +++ b/grc/core/schema_checker/block.py @@ -37,7 +37,7 @@ TEMPLATES_SCHEME = expand( BLOCK_SCHEME = expand( id=Spec(types=str_, required=True, item_scheme=None), label=str_, - category=(list, str_), + category=str_, flags=(list, str_), parameters=Spec(types=list, required=False, item_scheme=PARAM_SCHEME), diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py index 660eb594a5..f2ac986fb4 100644 --- a/grc/core/utils/__init__.py +++ b/grc/core/utils/__init__.py @@ -17,5 +17,17 @@ from __future__ import absolute_import +import six + from . import epy_block_io, expr_utils, extract_docs, flow_graph_complexity from .hide_bokeh_gui_options_if_not_installed import hide_bokeh_gui_options_if_not_installed + + +def to_list(value): + if not value: + return [] + elif isinstance(value, six.string_types): + return [value] + else: + return list(value) + diff --git a/grc/core/utils/descriptors/evaluated.py b/grc/core/utils/descriptors/evaluated.py index 313cee5b96..e8bce6e6ed 100644 --- a/grc/core/utils/descriptors/evaluated.py +++ b/grc/core/utils/descriptors/evaluated.py @@ -15,6 +15,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from __future__ import absolute_import + +import six + class Evaluated(object): def __init__(self, expected_type, default, name=None): @@ -62,7 +66,7 @@ class Evaluated(object): def __set__(self, instance, value): attribs = instance.__dict__ value = value or self.default - if isinstance(value, str) and value.startswith('${') and value.endswith('}'): + if isinstance(value, six.text_type) and value.startswith('${') and value.endswith('}'): attribs[self.name_raw] = value[2:-1].strip() else: attribs[self.name] = type(self.default)(value) diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index 2a38bc619e..8eb79f3459 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -63,8 +63,8 @@ class Platform(CorePlatform): FlowGraph = canvas.FlowGraph Connection = canvas.Connection - def new_block_class(self, block_id, **data): - cls = CorePlatform.new_block_class(self, block_id, **data) + def new_block_class(self, **data): + cls = CorePlatform.new_block_class(self, **data) return canvas.Block.make_cls_with_base(cls) block_classes_build_in = {key: canvas.Block.make_cls_with_base(cls) -- cgit v1.2.3 From fbc1627340ea3ed4bedc3424d5a2ec3736e66b4b Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski <sebastian.koslowski@gmail.com> Date: Wed, 8 Nov 2017 19:05:19 +0100 Subject: grc: move port and param init to block builder --- grc/core/blocks/_build.py | 76 ++++++++++++++++++++++++++++++++++++++++---- grc/core/blocks/_flags.py | 16 +++++----- grc/core/blocks/block.py | 81 ++++++++--------------------------------------- 3 files changed, 91 insertions(+), 82 deletions(-) (limited to 'grc/core/blocks/_build.py') diff --git a/grc/core/blocks/_build.py b/grc/core/blocks/_build.py index ce3496d9c4..6db06040cf 100644 --- a/grc/core/blocks/_build.py +++ b/grc/core/blocks/_build.py @@ -17,8 +17,13 @@ from __future__ import absolute_import +import collections +import itertools import re +from ..Constants import ADVANCED_PARAM_TAB +from ..utils import to_list + from .block import Block from ._flags import Flags from ._templates import MakoTemplates @@ -35,17 +40,18 @@ def build(id, label='', category='', flags='', documentation='', cls.label = label or block_id.title() cls.category = [cat.strip() for cat in category.split('/') if cat.strip()] - cls.flags = Flags(flags) + cls.flags = Flags(to_list(flags)) if re.match(r'options$|variable|virtual', block_id): - cls.flags += Flags.NOT_DSP + Flags.DISABLE_BYPASS + cls.flags.set(Flags.NOT_DSP, Flags.DISABLE_BYPASS) cls.documentation = {'': documentation.strip('\n\t ').replace('\\\n', '')} - cls.asserts = [_single_mako_expr(a, block_id) for a in (asserts or [])] + cls.asserts = [_single_mako_expr(a, block_id) for a in to_list(asserts)] - cls.parameters_data = parameters or [] - cls.inputs_data = inputs or [] - cls.outputs_data = outputs or [] + cls.inputs_data = _build_ports(inputs, 'sink') if inputs else [] + cls.outputs_data = _build_ports(outputs, 'source') if outputs else [] + cls.parameters_data = _build_params(parameters or [], + bool(cls.inputs_data), bool(cls.outputs_data), cls.flags) cls.extra_data = kwargs templates = templates or {} @@ -62,6 +68,64 @@ def build(id, label='', category='', flags='', documentation='', return cls +def _build_ports(ports_raw, direction): + ports = [] + port_ids = set() + stream_port_ids = itertools.count() + + for i, port_params in enumerate(ports_raw): + port = port_params.copy() + port['direction'] = direction + + port_id = port.setdefault('id', str(next(stream_port_ids))) + if port_id in port_ids: + raise Exception('Port id "{}" already exists in {}s'.format(port_id, direction)) + port_ids.add(port_id) + + ports.append(port) + return ports + + +def _build_params(params_raw, have_inputs, have_outputs, flags): + params = [] + + def add_param(**data): + params.append(data) + + add_param(id='id', name='ID', dtype='id', hide='part') + + if not flags.not_dsp: + add_param(id='alias', name='Block Alias', dtype='string', + hide='part', category=ADVANCED_PARAM_TAB) + + if have_outputs or have_inputs: + add_param(id='affinity', name='Core Affinity', dtype='int_vector', + hide='part', category=ADVANCED_PARAM_TAB) + + if have_outputs: + add_param(id='minoutbuf', name='Min Output Buffer', dtype='int', + hide='part', value='0', category=ADVANCED_PARAM_TAB) + add_param(id='maxoutbuf', name='Max Output Buffer', dtype='int', + hide='part', value='0', category=ADVANCED_PARAM_TAB) + + base_params_n = {} + for param_data in params_raw: + param_id = param_data['id'] + if param_id in params: + raise Exception('Param id "{}" is not unique'.format(param_id)) + + base_key = param_data.get('base_key', None) + param_data_ext = base_params_n.get(base_key, {}).copy() + param_data_ext.update(param_data) + + add_param(**param_data_ext) + base_params_n[param_id] = param_data_ext + + add_param(id='comment', name='Comment', dtype='_multiline', hide='part', + value='', category=ADVANCED_PARAM_TAB) + return params + + def _single_mako_expr(value, block_id): if not value: return None diff --git a/grc/core/blocks/_flags.py b/grc/core/blocks/_flags.py index ffea2ad569..bbedd6a2d7 100644 --- a/grc/core/blocks/_flags.py +++ b/grc/core/blocks/_flags.py @@ -17,10 +17,8 @@ from __future__ import absolute_import -import six - -class Flags(six.text_type): +class Flags(object): THROTTLE = 'throttle' DISABLE_BYPASS = 'disable_bypass' @@ -28,12 +26,14 @@ class Flags(six.text_type): DEPRECATED = 'deprecated' NOT_DSP = 'not_dsp' + def __init__(self, flags): + self.data = set(flags) + def __getattr__(self, item): return item in self - def __add__(self, other): - if not isinstance(other, six.string_types): - return NotImplemented - return self.__class__(str(self) + other) + def __contains__(self, item): + return item in self.data - __iadd__ = __add__ + def set(self, *flags): + self.data.update(flags) diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py index adc046936d..3a3de43fce 100644 --- a/grc/core/blocks/block.py +++ b/grc/core/blocks/block.py @@ -29,7 +29,6 @@ from six.moves import range from ._templates import MakoTemplates from ._flags import Flags -from ..Constants import ADVANCED_PARAM_TAB from ..base import Element from ..utils.descriptors import lazy_property @@ -63,82 +62,28 @@ class Block(Element): outputs_data = [] extra_data = {} + loaded_from = '(unknown)' - # region Init def __init__(self, parent): """Make a new block from nested data.""" super(Block, self).__init__(parent) - self.params = self._init_params() - self.sinks = self._init_ports(self.inputs_data, direction='sink') - self.sources = self._init_ports(self.outputs_data, direction='source') - - self.active_sources = [] # on rewrite - self.active_sinks = [] # on rewrite - - self.states = {'state': True} - - def _init_params(self): - is_dsp_block = not self.flags.not_dsp - has_inputs = bool(self.inputs_data) - has_outputs = bool(self.outputs_data) - - params = collections.OrderedDict() param_factory = self.parent_platform.make_param - - def add_param(id, **kwargs): - params[id] = param_factory(self, id=id, **kwargs) - - add_param(id='id', name='ID', dtype='id', - hide='none' if (self.key == 'options' or self.is_variable) else 'part') - - if is_dsp_block: - add_param(id='alias', name='Block Alias', dtype='string', - hide='part', category=ADVANCED_PARAM_TAB) - - if has_outputs or has_inputs: - add_param(id='affinity', name='Core Affinity', dtype='int_vector', - hide='part', category=ADVANCED_PARAM_TAB) - - if has_outputs: - add_param(id='minoutbuf', name='Min Output Buffer', dtype='int', - hide='part', value='0', category=ADVANCED_PARAM_TAB) - add_param(id='maxoutbuf', name='Max Output Buffer', dtype='int', - hide='part', value='0', category=ADVANCED_PARAM_TAB) - - base_params_n = {} - for param_data in self.parameters_data: - param_id = param_data['id'] - if param_id in params: - raise Exception('Param id "{}" is not unique'.format(param_id)) - - base_key = param_data.get('base_key', None) - param_data_ext = base_params_n.get(base_key, {}).copy() - param_data_ext.update(param_data) - - add_param(**param_data_ext) - base_params_n[param_id] = param_data_ext - - add_param(id='comment', name='Comment', dtype='_multiline', hide='part', - value='', category=ADVANCED_PARAM_TAB) - return params - - def _init_ports(self, ports_n, direction): - ports = [] port_factory = self.parent_platform.make_port - port_ids = set() - stream_port_ids = itertools.count() + self.params = collections.OrderedDict( + (data['id'], param_factory(parent=self, **data)) + for data in self.parameters_data + ) + if self.key == 'options' or self.is_variable: + self.params['id'].hide = 'part' - for i, port_data in enumerate(ports_n): - port_id = port_data.setdefault('id', str(next(stream_port_ids))) - if port_id in port_ids: - raise Exception('Port id "{}" already exists in {}s'.format(port_id, direction)) - port_ids.add(port_id) + self.sinks = [port_factory(parent=self, **params) for params in self.inputs_data] + self.sources = [port_factory(parent=self, **params) for params in self.outputs_data] - port = port_factory(parent=self, direction=direction, **port_data) - ports.append(port) - return ports - # endregion + self.active_sources = [] # on rewrite + self.active_sinks = [] # on rewrite + + self.states = {'state': True} # region Rewrite_and_Validation def rewrite(self): -- cgit v1.2.3