summaryrefslogtreecommitdiff
path: root/grc/core/Block.py
diff options
context:
space:
mode:
authorSebastian Koslowski <koslowski@kit.edu>2016-07-15 23:25:54 +0200
committerSebastian Koslowski <koslowski@kit.edu>2016-07-29 15:45:07 +0200
commit93ce3961a572da6ec3dbef1f24a22f4153acaa61 (patch)
tree274a78b5ce37f5305818c6ebab7ba495a2da4e12 /grc/core/Block.py
parent36f186bc46f528d95d9186955e91736d1fdb299e (diff)
grc: refactor: Port, Param, Options init clean-up
Diffstat (limited to 'grc/core/Block.py')
-rw-r--r--grc/core/Block.py300
1 files changed, 145 insertions, 155 deletions
diff --git a/grc/core/Block.py b/grc/core/Block.py
index 8ffb99b5bd..d37909b4b8 100644
--- a/grc/core/Block.py
+++ b/grc/core/Block.py
@@ -36,7 +36,7 @@ from . Constants import (
BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS,
BLOCK_FLAG_DEPRECATED,
)
-from . Element import Element
+from . Element import Element, lazy_property
def _get_elem(iterable, key):
@@ -53,21 +53,12 @@ class Block(Element):
STATE_LABELS = ['disabled', 'enabled', 'bypassed']
- def __init__(self, flow_graph, n):
- """
- Make a new block from nested data.
-
- Args:
- flow_graph: the parent element
- n: the nested odict
+ def __init__(self, parent, key, name, **n):
+ """Make a new block from nested data."""
+ super(Block, self).__init__(parent)
- Returns:
- block a new block
- """
- Element.__init__(self, parent=flow_graph)
-
- self.name = n['name']
- self.key = n['key']
+ self.key = key
+ self.name = name
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', '')
@@ -98,7 +89,6 @@ class Block(Element):
has_sources=len(sources_n)
)
- self.port_counters = [itertools.count(), itertools.count()]
self.sources = self._init_ports(sources_n, direction='source')
self.sinks = self._init_ports(sinks_n, direction='sink')
self.active_sources = [] # on rewrite
@@ -106,18 +96,15 @@ class Block(Element):
self.states = {'_enabled': True}
- self._epy_source_hash = -1 # for epy blocks
- self._epy_reload_error = None
-
self._init_bus_ports(n)
- def _add_param(self, key, name, value='', type='raw', **kwargs):
- n = {'key': key, 'name': name, 'value': value, 'type': type}
- n.update(kwargs)
- self.params[key] = self.parent_platform.Param(block=self, n=n)
-
def _init_params(self, params_n, has_sources, has_sinks):
- self._add_param(key='id', name='ID', type='id')
+ param_factory = self.parent_platform.get_new_param
+
+ def add_param(key, **kwargs):
+ self.params[key] = param_factory(self, key=key, **kwargs)
+
+ add_param(key='id', name='ID', type='id')
# Virtual source/sink and pad source/sink blocks are
# indistinguishable from normal GR blocks. Make explicit
@@ -133,35 +120,40 @@ class Block(Element):
self.flags += BLOCK_FLAG_DISABLE_BYPASS
if not (is_virtual_or_pad or is_variable or self.key == 'options'):
- self._add_param(key='alias', name='Block Alias', type='string',
- hide='part', tab=ADVANCED_PARAM_TAB)
+ add_param(key='alias', name='Block Alias', type='string',
+ hide='part', tab=ADVANCED_PARAM_TAB)
if not is_virtual_or_pad and (has_sources or has_sinks):
- self._add_param(key='affinity', name='Core Affinity', type='int_vector',
- hide='part', tab=ADVANCED_PARAM_TAB)
+ add_param(key='affinity', name='Core Affinity', type='int_vector',
+ hide='part', tab=ADVANCED_PARAM_TAB)
if not is_virtual_or_pad and has_sources:
- self._add_param(key='minoutbuf', name='Min Output Buffer', type='int',
- hide='part', value='0', tab=ADVANCED_PARAM_TAB)
- self._add_param(key='maxoutbuf', name='Max Output Buffer', type='int',
- hide='part', value='0', tab=ADVANCED_PARAM_TAB)
+ add_param(key='minoutbuf', name='Min Output Buffer', type='int',
+ hide='part', value='0', tab=ADVANCED_PARAM_TAB)
+ add_param(key='maxoutbuf', name='Max Output Buffer', type='int',
+ hide='part', value='0', tab=ADVANCED_PARAM_TAB)
+ base_params_n = {n['key']: n for n in params_n}
for param_n in params_n:
- param = self.parent_platform.Param(block=self, n=param_n)
- key = param.key
+ key = param_n['key']
if key in self.params:
raise Exception('Key "{}" already exists in params'.format(key))
- self.params[key] = param
- self._add_param(key='comment', name='Comment', type='_multiline', hide='part',
- value='', tab=ADVANCED_PARAM_TAB)
+ extended_param_n = base_params_n.get(param_n.pop('base_key', None), {})
+ extended_param_n.update(param_n)
+ self.params[key] = param_factory(self, **extended_param_n)
+
+ add_param(key='comment', name='Comment', type='_multiline', hide='part',
+ value='', tab=ADVANCED_PARAM_TAB)
def _init_ports(self, ports_n, direction):
- port_cls = self.parent_platform.Port
+ port_factory = self.parent_platform.get_new_port
ports = []
port_keys = set()
- for port_n in ports_n:
- port = port_cls(block=self, n=port_n, dir=direction)
+ stream_port_keys = itertools.count()
+ for i, port_n in enumerate(ports_n):
+ port_n.setdefault('key', str(next(stream_port_keys)))
+ port = port_factory(parent=self, direction=direction, **port_n)
key = port.key
if key in port_keys:
raise Exception('Key "{}" already exists in {}'.format(key, direction))
@@ -169,61 +161,14 @@ class Block(Element):
ports.append(port)
return ports
- def _run_checks(self):
- """Evaluate the checks"""
- for check in self._checks:
- check_res = self.resolve_dependencies(check)
- try:
- if not self.parent.evaluate(check_res):
- self.add_error_message('Check "{}" failed.'.format(check))
- except:
- self.add_error_message('Check "{}" did not evaluate.'.format(check))
-
- def _validate_generate_mode_compat(self):
- """check if this is a GUI block and matches the selected generate option"""
- current_generate_option = self.parent.get_option('generate_options')
-
- def check_generate_mode(label, flag, valid_options):
- block_requires_mode = (
- flag in self.flags or self.name.upper().startswith(label)
- )
- if block_requires_mode and current_generate_option not in valid_options:
- self.add_error_message("Can't generate this block in mode: {} ".format(
- repr(current_generate_option)))
-
- check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
- check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
-
- def _validate_var_value(self):
- """or variables check the value (only if var_value is used)"""
- if self.is_variable and self._var_value != '$value':
- value = self._var_value
- try:
- value = self.get_var_value()
- self.parent.evaluate(value)
- except Exception as err:
- self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
-
- def validate(self):
- """
- Validate this block.
- Call the base class validate.
- Evaluate the checks: each check must evaluate to True.
- """
- Element.validate(self)
- self._run_checks()
- self._validate_generate_mode_compat()
- self._validate_var_value()
- if self._epy_reload_error:
- self.params['_source_code'].add_error_message(str(self._epy_reload_error))
-
+ ##############################################
+ # validation and rewrite
+ ##############################################
def rewrite(self):
"""
Add and remove ports to adjust for the nports.
"""
Element.rewrite(self)
- # Check and run any custom rewrite function for this block
- getattr(self, 'rewrite_' + self.key, lambda: None)()
# Adjust nports, disconnect hidden ports
for ports in (self.sources, self.sinks):
@@ -258,6 +203,55 @@ class Block(Element):
self.active_sources = [p for p in self.get_sources_gui() if not p.get_hide()]
self.active_sinks = [p for p in self.get_sinks_gui() if not p.get_hide()]
+ def validate(self):
+ """
+ Validate this block.
+ Call the base class validate.
+ Evaluate the checks: each check must evaluate to True.
+ """
+ Element.validate(self)
+ self._run_checks()
+ self._validate_generate_mode_compat()
+ self._validate_var_value()
+
+ def _run_checks(self):
+ """Evaluate the checks"""
+ for check in self._checks:
+ check_res = self.resolve_dependencies(check)
+ try:
+ if not self.parent.evaluate(check_res):
+ self.add_error_message('Check "{}" failed.'.format(check))
+ except:
+ self.add_error_message('Check "{}" did not evaluate.'.format(check))
+
+ def _validate_generate_mode_compat(self):
+ """check if this is a GUI block and matches the selected generate option"""
+ current_generate_option = self.parent.get_option('generate_options')
+
+ def check_generate_mode(label, flag, valid_options):
+ block_requires_mode = (
+ flag in self.flags or self.name.upper().startswith(label)
+ )
+ if block_requires_mode and current_generate_option not in valid_options:
+ self.add_error_message("Can't generate this block in mode: {} ".format(
+ repr(current_generate_option)))
+
+ check_generate_mode('WX GUI', BLOCK_FLAG_NEED_WX_GUI, ('wx_gui',))
+ check_generate_mode('QT GUI', BLOCK_FLAG_NEED_QT_GUI, ('qt_gui', 'hb_qt_gui'))
+
+ def _validate_var_value(self):
+ """or variables check the value (only if var_value is used)"""
+ if self.is_variable and self._var_value != '$value':
+ value = self._var_value
+ try:
+ value = self.get_var_value()
+ self.parent.evaluate(value)
+ except Exception as err:
+ self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err))
+
+ ##############################################
+ # Getters
+ ##############################################
def get_imports(self, raw=False):
"""
Resolve all import statements.
@@ -316,14 +310,7 @@ class Block(Element):
# Also kept get_enabled and set_enabled to keep compatibility
@property
def state(self):
- """
- Gets the block's current state.
-
- Returns:
- ENABLED - 0
- BYPASSED - 1
- DISABLED - 2
- """
+ """Gets the block's current state."""
try:
return self.STATE_LABELS[int(self.states['_enabled'])]
except ValueError:
@@ -331,14 +318,7 @@ class Block(Element):
@state.setter
def state(self, value):
- """
- Sets the state for the block.
-
- Args:
- ENABLED - 0
- BYPASSED - 1
- DISABLED - 2
- """
+ """Sets the state for the block."""
try:
encoded = self.STATE_LABELS.index(value)
except ValueError:
@@ -425,11 +405,11 @@ class Block(Element):
def get_comment(self):
return self.params['comment'].get_value()
- @property
+ @lazy_property
def is_throtteling(self):
return BLOCK_FLAG_THROTTLE in self.flags
- @property
+ @lazy_property
def is_deprecated(self):
return BLOCK_FLAG_DEPRECATED in self.flags
@@ -459,12 +439,11 @@ class Block(Element):
return self.filter_bus_port(self.sources)
def get_connections(self):
- return sum([port.get_connections() for port in self.get_ports()], [])
+ return sum((port.get_connections() for port in self.get_ports()), [])
##############################################
# Resolve
##############################################
-
def resolve_dependencies(self, tmpl):
"""
Resolve a paramater dependency with cheetah templates.
@@ -515,7 +494,7 @@ class Block(Element):
# Try to increment the enum by direction
try:
- keys = type_param.get_option_keys()
+ keys = list(type_param.options.keys())
old_index = keys.index(type_param.get_value())
new_index = (old_index + direction + len(keys)) % len(keys)
type_param.set_value(keys[new_index])
@@ -693,7 +672,7 @@ class Block(Element):
for i in range(len(struct)):
n['key'] = str(len(ports))
n = dict(n)
- port = self.parent.parent.Port(block=self, n=n, dir=direc)
+ port = self.parent_platform.get_new_port(self, direction=direc, **n)
ports.append(port)
elif any('bus' == p.get_type() for p in ports):
for elt in get_p_gui():
@@ -716,15 +695,14 @@ class Block(Element):
class EPyBlock(Block):
- def __init__(self, flow_graph, n):
- super(EPyBlock, self).__init__(flow_graph, n)
+ def __init__(self, flow_graph, **n):
+ super(EPyBlock, self).__init__(flow_graph, **n)
self._epy_source_hash = -1 # for epy blocks
self._epy_reload_error = None
+ def rewrite(self):
+ Element.rewrite(self)
- def rewrite_epy_block(self):
- flowgraph = self.parent_flowgraph
- platform = self.parent_platform
param_blk = self.params['_io_cache']
param_src = self.params['_source_code']
@@ -758,53 +736,65 @@ class EPyBlock(Block):
self._make = '{0}.{1}({2})'.format(self.get_id(), blk_io.cls, ', '.join(
'{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params))
self._callbacks = ['{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks]
+ self._update_params(blk_io.params)
+ self._update_ports('in', self.sinks, blk_io.sinks, 'sink')
+ self._update_ports('out', self.sources, blk_io.sources, 'source')
+
+ super(EPyBlock, self).rewrite()
+ def _update_params(self, params_in_src):
+ param_factory = self.parent_platform.get_new_param
params = {}
for param in list(self.params):
if hasattr(param, '__epy_param__'):
params[param.key] = param
del self.params[param.key]
- for key, value in blk_io.params:
+ for key, value in params_in_src:
try:
param = params[key]
- param.set_default(value)
+ if param.default == param.value:
+ param.set_value(value)
+ param.default = str(value)
except KeyError: # need to make a new param
- name = key.replace('_', ' ').title()
- n = dict(name=name, key=key, type='raw', value=value)
- param = platform.Param(block=self, n=n)
+ param = param_factory(
+ parent=self, key=key, type='raw', value=value,
+ name=key.replace('_', ' ').title(),
+ )
setattr(param, '__epy_param__', True)
self.params[key] = param
- def update_ports(label, ports, port_specs, direction):
- ports_to_remove = list(ports)
- iter_ports = iter(ports)
- ports_new = []
- port_current = next(iter_ports, None)
- for key, port_type in port_specs:
- reuse_port = (
- port_current is not None and
- port_current.get_type() == port_type and
- (key.isdigit() or port_current.key == key)
- )
- if reuse_port:
- ports_to_remove.remove(port_current)
- port, port_current = port_current, next(iter_ports, None)
- else:
- n = dict(name=label + str(key), type=port_type, key=key)
- if port_type == 'message':
- n['name'] = key
- n['optional'] = '1'
- port = platform.Port(block=self, n=n, dir=direction)
- ports_new.append(port)
- # replace old port list with new one
- del ports[:]
- ports.extend(ports_new)
- # remove excess port connections
- for port in ports_to_remove:
- for connection in port.get_connections():
- flowgraph.remove_element(connection)
-
- update_ports('in', self.sinks, blk_io.sinks, 'sink')
- update_ports('out', self.sources, blk_io.sources, 'source')
- self.rewrite()
+ def _update_ports(self, label, ports, port_specs, direction):
+ port_factory = self.parent_platform.get_new_port
+ ports_to_remove = list(ports)
+ iter_ports = iter(ports)
+ ports_new = []
+ port_current = next(iter_ports, None)
+ for key, port_type in port_specs:
+ reuse_port = (
+ port_current is not None and
+ port_current.get_type() == port_type and
+ (key.isdigit() or port_current.key == key)
+ )
+ if reuse_port:
+ ports_to_remove.remove(port_current)
+ port, port_current = port_current, next(iter_ports, None)
+ else:
+ n = dict(name=label + str(key), type=port_type, key=key)
+ if port_type == 'message':
+ n['name'] = key
+ n['optional'] = '1'
+ port = port_factory(self, direction=direction, **n)
+ ports_new.append(port)
+ # replace old port list with new one
+ del ports[:]
+ ports.extend(ports_new)
+ # remove excess port connections
+ for port in ports_to_remove:
+ for connection in port.get_connections():
+ self.parent_flowgraph.remove_element(connection)
+
+ def validate(self):
+ super(EPyBlock, self).validate()
+ if self._epy_reload_error:
+ self.params['_source_code'].add_error_message(str(self._epy_reload_error))