summaryrefslogtreecommitdiff
path: root/grc/core
diff options
context:
space:
mode:
Diffstat (limited to 'grc/core')
-rw-r--r--grc/core/Block.py8
-rw-r--r--grc/core/Constants.py1
-rw-r--r--grc/core/Element.pyi54
-rw-r--r--grc/core/Platform.py76
-rw-r--r--grc/core/generator/Generator.py35
-rw-r--r--grc/core/generator/flow_graph.tmpl23
-rw-r--r--grc/core/utils/expr_utils.py10
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)