diff options
Diffstat (limited to 'grc/core')
-rw-r--r-- | grc/core/Block.py | 8 | ||||
-rw-r--r-- | grc/core/Constants.py | 1 | ||||
-rw-r--r-- | grc/core/Element.pyi | 54 | ||||
-rw-r--r-- | grc/core/Platform.py | 76 | ||||
-rw-r--r-- | grc/core/generator/Generator.py | 35 | ||||
-rw-r--r-- | grc/core/generator/flow_graph.tmpl | 23 | ||||
-rw-r--r-- | grc/core/utils/expr_utils.py | 10 |
7 files changed, 122 insertions, 85 deletions
diff --git a/grc/core/Block.py b/grc/core/Block.py index ff7f041dc0..9fff5afcb7 100644 --- a/grc/core/Block.py +++ b/grc/core/Block.py @@ -67,7 +67,7 @@ class Block(Element): self._name = n['name'] self._key = n['key'] - self._category = n.get('category', '') + self.category = [cat.strip() for cat in n.get('category', '').split('/') if cat.strip()] self._flags = n.get('flags', '') self._doc = n.get('doc', '').strip('\n').replace('\\\n', '') @@ -491,12 +491,6 @@ class Block(Element): def get_key(self): return self._key - def get_category(self): - return self._category - - def set_category(self, cat): - self._category = cat - def get_ports(self): return self.get_sources() + self.get_sinks() diff --git a/grc/core/Constants.py b/grc/core/Constants.py index 8a99f8b256..992d5e7d83 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -41,6 +41,7 @@ FLOW_GRAPH_FILE_FORMAT_VERSION = 1 # Param tabs DEFAULT_PARAM_TAB = "General" ADVANCED_PARAM_TAB = "Advanced" +DEFAULT_BLOCK_MODULE_NAME = '(no module specified)' # Port domains GR_STREAM_DOMAIN = "gr_stream" diff --git a/grc/core/Element.pyi b/grc/core/Element.pyi new file mode 100644 index 0000000000..c81180a33e --- /dev/null +++ b/grc/core/Element.pyi @@ -0,0 +1,54 @@ +# Copyright 2008, 2009, 2015, 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 . import Platform, FlowGraph, Block + +def lazy_property(func): + return func + + +class Element(object): + + def __init__(self, parent=None): + ... + + @property + def parent(self): + ... + + def get_parent_by_type(self, cls): + parent = self.parent + if parent is None: + return None + elif isinstance(parent, cls): + return parent + else: + return parent.get_parent_by_type(cls) + + @lazy_property + def parent_platform(self): -> Platform.Platform + ... + + @lazy_property + def parent_flowgraph(self): -> FlowGraph.FlowGraph + ... + + @lazy_property + def parent_block(self): -> Block.Block + ... + + diff --git a/grc/core/Platform.py b/grc/core/Platform.py index 069870d389..be7b60ca59 100644 --- a/grc/core/Platform.py +++ b/grc/core/Platform.py @@ -69,7 +69,7 @@ class Platform(Element): self.blocks = {} self._blocks_n = {} - self._category_trees_n = [] + self._block_categories = {} self.domains = {} self.connection_templates = {} @@ -79,7 +79,8 @@ class Platform(Element): self._flow_graph = Element.__new__(FlowGraph) Element.__init__(self._flow_graph, self) self._flow_graph.connections = [] - self.load_blocks() + + self.build_block_library() def __str__(self): return 'Platform - {}({})'.format(self.config.key, self.config.name) @@ -137,14 +138,14 @@ class Platform(Element): self.load_block_xml(generator.file_path_xml) return True - def load_blocks(self): + def build_block_library(self): """load the blocks and block tree from the search paths""" self._docstring_extractor.start() # Reset self.blocks.clear() self._blocks_n.clear() - del self._category_trees_n[:] + self._block_categories.clear() self.domains.clear() self.connection_templates.clear() ParseXML.xml_failures.clear() @@ -162,9 +163,21 @@ class Platform(Element): # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) pass except Exception as e: - raise print('Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file), file=sys.stderr) + # Add blocks to block tree + for key, block in six.iteritems(self.blocks): + category = self._block_categories.get(key, block.category) + # Blocks with empty categories are hidden + if not category: + continue + root = category[0] + if root.startswith('[') and root.endswith(']'): + category[0] = root[1:-1] + else: + category.insert(0, Constants.DEFAULT_BLOCK_MODULE_NAME) + block.category = category + self._docstring_extractor.finish() # self._docstring_extractor.wait() @@ -202,8 +215,19 @@ class Platform(Element): def load_category_tree_xml(self, xml_file): """Validate and parse category tree file and add it to list""" ParseXML.validate_dtd(xml_file, Constants.BLOCK_TREE_DTD) - n = ParseXML.from_file(xml_file).get('cat', {}) - self._category_trees_n.append(n) + xml = ParseXML.from_file(xml_file) + path = [] + + def load_category(cat_n): + path.append(cat_n.get('name').strip()) + for block_key in cat_n.get('block', []): + if block_key not in self._block_categories: + self._block_categories[block_key] = list(path) + for sub_cat_n in cat_n.get('cat', []): + load_category(sub_cat_n) + path.pop() + + load_category(xml.get('cat', {})) def load_domain_xml(self, xml_file): """Load a domain properties and connection templates from XML""" @@ -248,44 +272,6 @@ class Platform(Element): else: self.connection_templates[key] = connection_n.get('make') or '' - def load_block_tree(self, block_tree): - """ - Load a block tree with categories and blocks. - Step 1: Load all blocks from the xml specification. - Step 2: Load blocks with builtin category specifications. - - Args: - block_tree: the block tree object - """ - # Recursive function to load categories and blocks - def load_category(cat_n, parent=None): - # Add this category - parent = (parent or []) + [cat_n.get('name')] - block_tree.add_block(parent) - # Recursive call to load sub categories - for cat in cat_n.get('cat', []): - load_category(cat, parent) - # Add blocks in this category - for block_key in cat_n.get('block', []): - if block_key not in self.blocks: - print('Warning: Block key "{}" not found when loading category tree.'.format(block_key), file=sys.stderr) - continue - block = self.blocks[block_key] - # If it exists, the block's category shall not be overridden by the xml tree - if not block.get_category(): - block.set_category(parent) - - # Recursively load the category trees and update the categories for each block - for category_tree_n in self._category_trees_n: - load_category(category_tree_n) - - # Add blocks to block tree - for block in six.itervalues(self.blocks): - # Blocks with empty categories are hidden - if not block.get_category(): - continue - block_tree.add_block(block.get_category(), block) - def _save_docstring_extraction_result(self, key, docstrings): docs = {} for match, docstring in six.iteritems(docstrings): diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index c3308d6c32..9c7d07e76b 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -144,16 +144,16 @@ class TopBlockGenerator(object): pass return code - blocks = expr_utils.sort_objects( + blocks_all = expr_utils.sort_objects( [b for b in fg.blocks if b.get_enabled() and not b.get_bypassed()], operator.methodcaller('get_id'), _get_block_sort_text ) - deprecated_block_keys = set(block.get_name() for block in blocks if block.is_deprecated) + deprecated_block_keys = set(b.get_name() for b in blocks_all if b.is_deprecated) for key in deprecated_block_keys: Messages.send_warning("The block {!r} is deprecated.".format(key)) # List of regular blocks (all blocks minus the special ones) - blocks = [b for b in blocks if b not in imports and b not in parameters] + blocks = [b for b in blocks_all if b not in imports and b not in parameters] for block in blocks: key = block.get_key() @@ -220,18 +220,20 @@ class TopBlockGenerator(object): # List of variable names var_ids = [var.get_id() for var in parameters + variables] - # Prepend self. - replace_dict = dict([(var_id, 'self.%s' % var_id) for var_id in var_ids]) - # List of callbacks - callbacks = [ - expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in fg.get_enabled_blocks()], []) - ] + replace_dict = dict((var_id, 'self.' + var_id) for var_id in var_ids) + callbacks_all = [] + for block in blocks_all: + callbacks_all.extend(expr_utils.expr_replace(cb, replace_dict) for cb in block.get_callbacks()) + # Map var id to callbacks - var_id2cbs = dict([ - (var_id, [c for c in callbacks if expr_utils.get_variable_dependencies(c, [var_id])]) - for var_id in var_ids - ]) + def uses_var_id(): + used = expr_utils.get_variable_dependencies(callback, [var_id]) + return used and 'self.' + var_id in callback # callback might contain var_id itself + + callbacks = {} + for var_id in var_ids: + callbacks[var_id] = [callback for callback in callbacks_all if uses_var_id()] + # Load the namespace namespace = { 'title': title, @@ -245,7 +247,7 @@ class TopBlockGenerator(object): 'connection_templates': connection_templates, 'msgs': msgs, 'generate_options': self._generate_options, - 'var_id2cbs': var_id2cbs, + 'callbacks': callbacks, } # Build the template t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) @@ -265,8 +267,9 @@ class HierBlockGenerator(TopBlockGenerator): file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR) """ TopBlockGenerator.__init__(self, flow_graph, file_path) + platform = flow_graph.parent - hier_block_lib_dir = flow_graph.parent_platform.config.hier_block_lib_dir + hier_block_lib_dir = platform.config.hier_block_lib_dir if not os.path.exists(hier_block_lib_dir): os.mkdir(hier_block_lib_dir) diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl index c86808455b..5eef3d2042 100644 --- a/grc/core/generator/flow_graph.tmpl +++ b/grc/core/generator/flow_graph.tmpl @@ -13,7 +13,7 @@ ##@param connections the connections ##@param msgs the msg type connections ##@param generate_options the type of flow graph -##@param var_id2cbs variable id map to callback strings +##@param callbacks variable id map to callback strings ######################################################## #def indent($code) #set $code = '\n '.join(str($code).splitlines()) @@ -301,12 +301,12 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) #if $flow_graph.get_option('thread_safe_setters') with self._lock: self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #else self.$id = $id - #for $callback in $var_id2cbs[$id] + #for $callback in $callbacks[$id] $indent($callback) #end for #end if @@ -336,19 +336,22 @@ $short_id#slurp def argument_parser(): - #set $desc_args = 'usage="%prog: [options]", option_class=eng_option' + #set $arg_parser_args = '' #if $flow_graph.get_option('description') - #set $desc_args += ', description=description' + #set $arg_parser_args = 'description=description' description = $repr($flow_graph.get_option('description')) #end if - parser = OptionParser($desc_args) + parser = ArgumentParser($arg_parser_args) #for $param in $parameters #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option( - "$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), - help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") + parser.add_argument( + #if $make_short_id($param) + "$make_short_id($param)", #slurp + #end if + "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type=$type, default=$make_default($type, $param), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%(default)r]") #end if #end for return parser @@ -358,7 +361,7 @@ def argument_parser(): def main(top_block_cls=$(class_name), options=None): #if $parameters if options is None: - options, _ = argument_parser().parse_args() + options = argument_parser().parse_args() #end if #if $flow_graph.get_option('realtime_scheduling') if gr.enable_realtime_scheduling() != gr.RT_OK: diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py index 0577f06a75..555bd709b1 100644 --- a/grc/core/utils/expr_utils.py +++ b/grc/core/utils/expr_utils.py @@ -61,7 +61,7 @@ class graph(object): return self._graph[node_key] -def expr_split(expr): +def expr_split(expr, var_chars=VAR_CHARS): """ Split up an expression by non alphanumeric characters, including underscore. Leave strings in-tact. @@ -77,7 +77,7 @@ def expr_split(expr): tok = '' quote = '' for char in expr: - if quote or char in VAR_CHARS: + if quote or char in var_chars: if char == quote: quote = '' tok += char @@ -104,7 +104,7 @@ def expr_replace(expr, replace_dict): Returns: a new expression with the prepend """ - expr_splits = expr_split(expr) + expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.') for i, es in enumerate(expr_splits): if es in list(replace_dict.keys()): expr_splits[i] = replace_dict[es] @@ -195,7 +195,3 @@ def sort_objects(objects, get_id, get_expr): # Return list of sorted objects return [id2obj[id] for id in sorted_ids] - -if __name__ == '__main__': - for i in sort_variables({'x': '1', 'y': 'x+1', 'a': 'x+y', 'b': 'y+1', 'c': 'a+b+x+y'}): - print(i) |