diff options
Diffstat (limited to 'grc/core')
-rw-r--r-- | grc/core/Block.py | 530 | ||||
-rw-r--r-- | grc/core/Config.py | 1 | ||||
-rw-r--r-- | grc/core/Connection.py | 119 | ||||
-rw-r--r-- | grc/core/Constants.py | 29 | ||||
-rw-r--r-- | grc/core/Element.py | 96 | ||||
-rw-r--r-- | grc/core/FlowGraph.py | 157 | ||||
-rw-r--r-- | grc/core/Messages.py | 3 | ||||
-rw-r--r-- | grc/core/Param.py | 153 | ||||
-rw-r--r-- | grc/core/ParseXML.py | 67 | ||||
-rw-r--r-- | grc/core/Platform.py | 84 | ||||
-rw-r--r-- | grc/core/Port.py | 107 | ||||
-rw-r--r-- | grc/core/generator/FlowGraphProxy.py | 18 | ||||
-rw-r--r-- | grc/core/generator/Generator.py | 91 | ||||
-rw-r--r-- | grc/core/generator/__init__.py | 3 | ||||
-rw-r--r-- | grc/core/generator/flow_graph.tmpl | 10 | ||||
-rw-r--r-- | grc/core/utils/__init__.py | 9 | ||||
-rw-r--r-- | grc/core/utils/complexity.py | 13 | ||||
-rw-r--r-- | grc/core/utils/epy_block_io.py | 11 | ||||
-rw-r--r-- | grc/core/utils/expr_utils.py | 20 | ||||
-rw-r--r-- | grc/core/utils/extract_docs.py | 28 | ||||
-rw-r--r-- | grc/core/utils/odict.py | 115 |
21 files changed, 795 insertions, 869 deletions
diff --git a/grc/core/Block.py b/grc/core/Block.py index 8a683a2b6b..9fff5afcb7 100644 --- a/grc/core/Block.py +++ b/grc/core/Block.py @@ -17,12 +17,16 @@ 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 collections import itertools +from six.moves import map, range + from Cheetah.Template import Template -from .utils import epy_block_io, odict +from .utils import epy_block_io from . Constants import ( BLOCK_FLAG_NEED_QT_GUI, BLOCK_FLAG_NEED_WX_GUI, ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB, @@ -59,184 +63,111 @@ class Block(Element): Returns: block a new block """ - # Grab the data - self._doc = (n.find('doc') or '').strip('\n').replace('\\\n', '') - self._imports = map(lambda i: i.strip(), n.findall('import')) - self._make = n.find('make') - self._var_make = n.find('var_make') - self._checks = n.findall('check') - self._callbacks = n.findall('callback') - self._bus_structure_source = n.find('bus_structure_source') or '' - self._bus_structure_sink = n.find('bus_structure_sink') or '' - self.port_counters = [itertools.count(), itertools.count()] + Element.__init__(self, parent=flow_graph) + + self._name = n['name'] + self._key = n['key'] + 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', '') - # Build the block - Element.__init__(self, flow_graph) - - # Grab the data - params = n.findall('param') - sources = n.findall('source') - sinks = n.findall('sink') - self._name = n.find('name') - self._key = n.find('key') - category = (n.find('category') or '').split('/') - self.category = [cat.strip() for cat in category if cat.strip()] - self._flags = n.find('flags') or '' # Backwards compatibility - if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: + if n.get('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: self._flags += BLOCK_FLAG_THROTTLE - self._grc_source = n.find('grc_source') or '' - self._block_wrapper_path = n.find('block_wrapper_path') - self._bussify_sink = n.find('bus_sink') - self._bussify_source = n.find('bus_source') - self._var_value = n.find('var_value') or '$value' + + self._imports = [i.strip() for i in n.get('import', [])] + self._make = n.get('make') + self._var_make = n.get('var_make') + self._var_value = n.get('var_value', '$value') + self._checks = n.get('check', []) + self._callbacks = n.get('callback', []) + + self._grc_source = n.get('grc_source', '') + self._block_wrapper_path = n.get('block_wrapper_path') + + params_n = n.get('param', []) + sources_n = n.get('source', []) + sinks_n = n.get('sink', []) # Get list of param tabs - n_tabs = n.find('param_tab_order') or None - self._param_tab_labels = n_tabs.findall('tab') if n_tabs is not None else [DEFAULT_PARAM_TAB] - - # Create the param objects - self._params = list() - - # Add the id param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'ID', - 'key': 'id', - 'type': 'id', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'Enabled', - 'key': '_enabled', - 'type': 'raw', - 'value': 'True', - 'hide': 'all', - }) - )) - for param in itertools.imap(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): - key = param.get_key() - # Test against repeated keys - if key in self.get_param_keys(): - raise Exception('Key "{}" already exists in params'.format(key)) - # Store the param - self.get_params().append(param) - # Create the source objects - self._sources = list() - for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): - key = source.get_key() - # Test against repeated keys - if key in self.get_source_keys(): - raise Exception('Key "{}" already exists in sources'.format(key)) - # Store the port - self.get_sources().append(source) - self.back_ofthe_bus(self.get_sources()) - # Create the sink objects - self._sinks = list() - for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): - key = sink.get_key() - # Test against repeated keys - if key in self.get_sink_keys(): - raise Exception('Key "{}" already exists in sinks'.format(key)) - # Store the port - self.get_sinks().append(sink) - self.back_ofthe_bus(self.get_sinks()) - self.current_bus_structure = {'source': '', 'sink': ''} + self._param_tab_labels = n.get('param_tab_order', {}).get('tab') or [DEFAULT_PARAM_TAB] + self._params = [] + self._init_params( + params_n=params_n, + has_sinks=len(sinks_n), + 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._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) + param = self.parent_platform.Param(block=self, n=n) + self._params.append(param) + + def _init_params(self, params_n, has_sources, has_sinks): + self._add_param(key='id', name='ID', type='id') + self._add_param(key='_enabled', name='Enabled', value='True', type='raw', hide='all') # Virtual source/sink and pad source/sink blocks are # indistinguishable from normal GR blocks. Make explicit # checks for them here since they have no work function or # buffers to manage. - self.is_virtual_or_pad = self._key in ( + self.is_virtual_or_pad = is_virtual_or_pad = self._key in ( "virtual_source", "virtual_sink", "pad_source", "pad_sink") - self.is_variable = self._key.startswith('variable') + self.is_variable = is_variable = self._key.startswith('variable') self.is_import = (self._key == 'import') # Disable blocks that are virtual/pads or variables if self.is_virtual_or_pad or self.is_variable: self._flags += BLOCK_FLAG_DISABLE_BYPASS - if not (self.is_virtual_or_pad or self.is_variable or self._key == 'options'): - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Block Alias', - 'key': 'alias', - 'type': 'string', - 'hide': 'part', - 'tab': ADVANCED_PARAM_TAB - }) - )) - - if (len(sources) or len(sinks)) and not self.is_virtual_or_pad: - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Core Affinity', - 'key': 'affinity', - 'type': 'int_vector', - 'hide': 'part', - 'tab': ADVANCED_PARAM_TAB - }) - )) - if len(sources) and not self.is_virtual_or_pad: - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Min Output Buffer', - 'key': 'minoutbuf', - 'type': 'int', - 'hide': 'part', - 'value': '0', - 'tab': ADVANCED_PARAM_TAB - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Max Output Buffer', - 'key': 'maxoutbuf', - 'type': 'int', - 'hide': 'part', - 'value': '0', - 'tab': ADVANCED_PARAM_TAB - }) - )) - - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({'name': 'Comment', - 'key': 'comment', - 'type': '_multiline', - 'hide': 'part', - 'value': '', - 'tab': ADVANCED_PARAM_TAB - }) - )) + 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) - self._epy_source_hash = -1 # for epy blocks - self._epy_reload_error = None + 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) - if self._bussify_sink: - self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') - if self._bussify_source: - self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + 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) - def get_bus_structure(self, direction): - if direction == 'source': - bus_structure = self._bus_structure_source - else: - bus_structure = self._bus_structure_sink - - bus_structure = self.resolve_dependencies(bus_structure) - - if not bus_structure: - return '' # TODO: Don't like empty strings. should change this to None eventually + param_keys = set(param.get_key() for param in self._params) + for param_n in params_n: + param = self.parent_platform.Param(block=self, n=param_n) + key = param.get_key() + if key in param_keys: + raise Exception('Key "{}" already exists in params'.format(key)) + param_keys.add(key) + self.get_params().append(param) - try: - clean_bus_structure = self.get_parent().evaluate(bus_structure) - return clean_bus_structure - except: - return '' + self._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 + ports = [] + port_keys = set() + for port_n in ports_n: + port = port_cls(block=self, n=port_n, dir=direction) + key = port.get_key() + if key in port_keys: + raise Exception('Key "{}" already exists in {}'.format(key, direction)) + port_keys.add(key) + ports.append(port) + return ports def validate(self): """ @@ -249,7 +180,7 @@ class Block(Element): for check in self._checks: check_res = self.resolve_dependencies(check) try: - if not self.get_parent().evaluate(check_res): + 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)) @@ -259,12 +190,12 @@ class Block(Element): value = self._var_value try: value = self.get_var_value() - self.get_parent().evaluate(value) + self.parent.evaluate(value) except Exception as err: self.add_error_message('Value "{}" cannot be evaluated:\n{}'.format(value, err)) # check if this is a GUI block and matches the selected generate option - current_generate_option = self.get_parent().get_option('generate_options') + current_generate_option = self.parent.get_option('generate_options') def check_generate_mode(label, flag, valid_options): block_requires_mode = ( @@ -295,14 +226,14 @@ class Block(Element): num_ports = 1 + len(master_port.get_clones()) if master_port.get_hide(): for connection in master_port.get_connections(): - self.get_parent().remove_element(connection) + self.parent.remove_element(connection) if not nports and num_ports == 1: # Not a master port and no left-over clones continue # Remove excess cloned ports for port in master_port.get_clones()[nports-1:]: # Remove excess connections for connection in port.get_connections(): - self.get_parent().remove_element(connection) + self.parent.remove_element(connection) master_port.remove_clone(port) ports.remove(port) # Add more cloned ports @@ -313,42 +244,13 @@ class Block(Element): self.back_ofthe_bus(ports) # Renumber non-message/message ports domain_specific_port_index = collections.defaultdict(int) - for port in filter(lambda p: p.get_key().isdigit(), ports): + for port in [p for p in ports if p.get_key().isdigit()]: domain = port.get_domain() port._key = str(domain_specific_port_index[domain]) domain_specific_port_index[domain] += 1 - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - # Concat the nports string from the private nports settings of all ports - nports_str = ' '.join([port._nports for port in self.get_ports()]) - # Modify all params whose keys appear in the nports string - for param in self.get_params(): - if param.is_enum() or param.get_key() not in nports_str: - continue - # Try to increment the port controller by direction - try: - value = param.get_evaluated() - value = value + direction - if 0 < value: - param.set_value(value) - changed = True - except: - pass - return changed - def get_doc(self): - platform = self.get_parent().get_parent() - documentation = platform.block_docstrings.get(self._key, {}) + documentation = self.parent_platform.block_docstrings.get(self._key, {}) from_xml = self._doc.strip() if from_xml: documentation[''] = from_xml @@ -366,7 +268,8 @@ class Block(Element): """ if raw: return self._imports - return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) + return [i for i in sum((self.resolve_dependencies(i).split('\n') + for i in self._imports), []) if i] def get_make(self, raw=False): if raw: @@ -391,7 +294,7 @@ class Block(Element): if 'self.' in callback: return callback return 'self.{}.{}'.format(self.get_id(), callback) - return map(make_callback, self._callbacks) + return [make_callback(c) for c in self._callbacks] def is_virtual_sink(self): return self.get_key() == 'virtual_sink' @@ -404,8 +307,8 @@ class Block(Element): ########################################################################### def rewrite_epy_block(self): - flowgraph = self.get_parent() - platform = flowgraph.get_parent() + flowgraph = self.parent_flowgraph + platform = self.parent_block param_blk = self.get_param('_io_cache') param_src = self.get_param('_source_code') @@ -452,7 +355,7 @@ class Block(Element): param.set_default(value) except KeyError: # need to make a new param name = key.replace('_', ' ').title() - n = odict(dict(name=name, key=key, type='raw', value=value)) + n = dict(name=name, key=key, type='raw', value=value) param = platform.Param(block=self, n=n) setattr(param, '__epy_param__', True) self._params.append(param) @@ -472,7 +375,7 @@ class Block(Element): ports_to_remove.remove(port_current) port, port_current = port_current, next(iter_ports, None) else: - n = odict(dict(name=label + str(key), type=port_type, key=key)) + n = dict(name=label + str(key), type=port_type, key=key) if port_type == 'message': n['name'] = key n['optional'] = '1' @@ -490,13 +393,6 @@ class Block(Element): update_ports('out', self.get_sources(), blk_io.sources, 'source') self.rewrite() - def back_ofthe_bus(self, portlist): - portlist.sort(key=lambda p: p._type == 'bus') - - def filter_bus_port(self, ports): - buslist = [p for p in ports if p._type == 'bus'] - return buslist or ports - # Main functions to get and set the block state # Also kept get_enabled and set_enabled to keep compatibility def get_state(self): @@ -651,9 +547,6 @@ class Block(Element): ############################################## # Access Sinks ############################################## - def get_sink_keys(self): - return _get_keys(self._sinks) - def get_sink(self, key): return _get_elem(self._sinks, key) @@ -666,9 +559,6 @@ class Block(Element): ############################################## # Access Sources ############################################## - def get_source_keys(self): - return _get_keys(self._sources) - def get_source(self, key): return _get_elem(self._sources, key) @@ -681,6 +571,10 @@ class Block(Element): def get_connections(self): return sum([port.get_connections() for port in self.get_ports()], []) + ############################################## + # Resolve + ############################################## + def resolve_dependencies(self, tmpl): """ Resolve a paramater dependency with cheetah templates. @@ -716,10 +610,10 @@ class Block(Element): """ changed = False type_param = None - for param in filter(lambda p: p.is_enum(), self.get_params()): + for param in [p for p in self.get_params() if p.is_enum()]: children = self.get_ports() + self.get_params() # Priority to the type controller - if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + if param.get_key() in ' '.join([p._type for p in children]): type_param = param # Use param if type param is unset if not type_param: type_param = param @@ -735,6 +629,128 @@ class Block(Element): pass return changed + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + # Concat the nports string from the private nports settings of all ports + nports_str = ' '.join([port._nports for port in self.get_ports()]) + # Modify all params whose keys appear in the nports string + for param in self.get_params(): + if param.is_enum() or param.get_key() not in nports_str: + continue + # Try to increment the port controller by direction + try: + value = param.get_evaluated() + value = value + direction + if 0 < value: + param.set_value(value) + changed = True + except: + pass + return changed + + ############################################## + # Import/Export Methods + ############################################## + def export_data(self): + """ + Export this block's params to nested data. + + Returns: + a nested data odict + """ + n = collections.OrderedDict() + n['key'] = self.get_key() + n['param'] = [p.export_data() for p in sorted(self.get_params(), key=str)] + if 'bus' in [a.get_type() for a in self.get_sinks()]: + n['bus_sink'] = str(1) + if 'bus' in [a.get_type() for a in self.get_sources()]: + n['bus_source'] = str(1) + return n + + def import_data(self, n): + """ + Import this block's params from nested data. + Any param keys that do not exist will be ignored. + Since params can be dynamically created based another param, + call rewrite, and repeat the load until the params stick. + This call to rewrite will also create any dynamic ports + that are needed for the connections creation phase. + + Args: + n: the nested data odict + """ + params_n = n.get('param', []) + params = dict((param.get_key(), param) for param in self._params) + + def get_hash(): + return hash(tuple(map(hash, self._params))) + + my_hash = 0 + while get_hash() != my_hash: + for param_n in params_n: + key = param_n['key'] + value = param_n['value'] + try: + params[key].set_value(value) + except KeyError: + continue + # Store hash and call rewrite + my_hash = get_hash() + self.rewrite() + + self._import_bus_stuff(n) + + ############################################## + # Bus ports stuff + ############################################## + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self._bus_structure_source + else: + bus_structure = self._bus_structure_sink + + bus_structure = self.resolve_dependencies(bus_structure) + + if not bus_structure: + return '' # TODO: Don't like empty strings. should change this to None eventually + + try: + clean_bus_structure = self.parent.evaluate(bus_structure) + return clean_bus_structure + except: + return '' + + def back_ofthe_bus(self, portlist): + portlist.sort(key=lambda p: p._type == 'bus') + + def filter_bus_port(self, ports): + buslist = [p for p in ports if p._type == 'bus'] + return buslist or ports + + def _import_bus_stuff(self, n): + bussinks = n.get('bus_sink', []) + if len(bussinks) > 0 and not self._bussify_sink: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + elif len(bussinks) > 0: + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + bussrcs = n.get('bus_source', []) + if len(bussrcs) > 0 and not self._bussify_source: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + elif len(bussrcs) > 0: + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + def form_bus_structure(self, direc): if direc == 'source': get_p = self.get_sources @@ -745,12 +761,12 @@ class Block(Element): get_p_gui = self.get_sinks_gui bus_structure = self.get_bus_structure('sink') - struct = [range(len(get_p()))] - if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + struct = [list(range(len(get_p())))] + if True in [isinstance(a.get_nports(), int) for a in get_p()]: structlet = [] last = 0 for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: - structlet.extend(map(lambda a: a+last, range(j))) + structlet.extend([a+last for a in range(j)]) last = structlet[-1] + 1 struct = [structlet] if bus_structure: @@ -764,17 +780,15 @@ class Block(Element): if direc == 'source': get_p = self.get_sources get_p_gui = self.get_sources_gui - bus_structure = self.get_bus_structure('source') else: get_p = self.get_sinks get_p_gui = self.get_sinks_gui - bus_structure = self.get_bus_structure('sink') for elt in get_p(): for connect in elt.get_connections(): - self.get_parent().remove_element(connect) + self.parent.remove_element(connect) - if ('bus' not in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: + if ('bus' not in [a.get_type() for a in get_p()]) and len(get_p()) > 0: struct = self.form_bus_structure(direc) self.current_bus_structure[direc] = struct if get_p()[0].get_nports(): @@ -782,69 +796,23 @@ class Block(Element): for i in range(len(struct)): n['key'] = str(len(get_p())) - n = odict(n) - port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc) + n = dict(n) + port = self.parent.parent.Port(block=self, n=n, dir=direc) get_p().append(port) - elif 'bus' in map(lambda a: a.get_type(), get_p()): + elif 'bus' in [a.get_type() for a in get_p()]: for elt in get_p_gui(): get_p().remove(elt) self.current_bus_structure[direc] = '' - ############################################## - # Import/Export Methods - ############################################## - def export_data(self): - """ - Export this block's params to nested data. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['param'] = map(lambda p: p.export_data(), sorted(self.get_params(), key=str)) - if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): - n['bus_sink'] = str(1) - if 'bus' in map(lambda a: a.get_type(), self.get_sources()): - n['bus_source'] = str(1) - return n - - def get_hash(self): - return hash(tuple(map(hash, self.get_params()))) - - def import_data(self, n): - """ - Import this block's params from nested data. - Any param keys that do not exist will be ignored. - Since params can be dynamically created based another param, - call rewrite, and repeat the load until the params stick. - This call to rewrite will also create any dynamic ports - that are needed for the connections creation phase. - - Args: - n: the nested data odict - """ - my_hash = 0 - while self.get_hash() != my_hash: - params_n = n.findall('param') - for param_n in params_n: - key = param_n.find('key') - value = param_n.find('value') - # The key must exist in this block's params - if key in self.get_param_keys(): - self.get_param(key).set_value(value) - # Store hash and call rewrite - my_hash = self.get_hash() - self.rewrite() - bussinks = n.findall('bus_sink') - if len(bussinks) > 0 and not self._bussify_sink: - self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') - elif len(bussinks) > 0: - self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') + def _init_bus_ports(self, n): + self.back_ofthe_bus(self._sources) + self.back_ofthe_bus(self._sinks) + self.current_bus_structure = {'source': '', 'sink': ''} + self._bus_structure_source = n.get('bus_structure_source', '') + self._bus_structure_sink = n.get('bus_structure_sink', '') + self._bussify_sink = n.get('bus_sink') + self._bussify_source = n.get('bus_source') + if self._bussify_sink: self.bussify({'name': 'bus', 'type': 'bus'}, 'sink') - bussrcs = n.findall('bus_source') - if len(bussrcs) > 0 and not self._bussify_source: - self.bussify({'name': 'bus', 'type': 'bus'}, 'source') - elif len(bussrcs) > 0: - self.bussify({'name': 'bus', 'type': 'bus'}, 'source') + if self._bussify_source: self.bussify({'name': 'bus', 'type': 'bus'}, 'source') diff --git a/grc/core/Config.py b/grc/core/Config.py index ac38d9978c..400d5d365f 100644 --- a/grc/core/Config.py +++ b/grc/core/Config.py @@ -17,6 +17,7 @@ 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 os from os.path import expanduser, normpath, expandvars, exists diff --git a/grc/core/Connection.py b/grc/core/Connection.py index 3aa32ef183..52cba4257c 100644 --- a/grc/core/Connection.py +++ b/grc/core/Connection.py @@ -17,9 +17,14 @@ 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 collections + +from six.moves import range + from . import Constants -from .Element import Element -from .utils import odict +from .Element import Element, lazy_property class Connection(Element): @@ -40,6 +45,21 @@ class Connection(Element): a new connection """ Element.__init__(self, flow_graph) + + source, sink = self._get_sink_source(porta, portb) + + self.source_port = source + self.sink_port = sink + + # Ensure that this connection (source -> sink) is unique + for connection in flow_graph.connections: + if connection.source_port is source and connection.sink_port is sink: + raise LookupError('This connection between source and sink is not unique.') + + self._make_bus_connect() + + @staticmethod + def _get_sink_source(porta, portb): source = sink = None # Separate the source and sink for port in (porta, portb): @@ -51,42 +71,18 @@ class Connection(Element): raise ValueError('Connection could not isolate source') if not sink: raise ValueError('Connection could not isolate sink') - busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink])) % 2 - if not busses == 0: - raise ValueError('busses must get with busses') - - if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): - raise ValueError('port connections must have same cardinality') - # Ensure that this connection (source -> sink) is unique - for connection in flow_graph.connections: - if connection.get_source() is source and connection.get_sink() is sink: - raise LookupError('This connection between source and sink is not unique.') - self._source = source - self._sink = sink - if source.get_type() == 'bus': - - sources = source.get_associated_ports() - sinks = sink.get_associated_ports() - - for i in range(len(sources)): - try: - flow_graph.connect(sources[i], sinks[i]) - except: - pass + return source, sink def __str__(self): return 'Connection (\n\t{}\n\t\t{}\n\t{}\n\t\t{}\n)'.format( - self.get_source().get_parent(), - self.get_source(), - self.get_sink().get_parent(), - self.get_sink(), + self.source_block, self.source_port, self.sink_block, self.sink_port, ) def is_msg(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'msg' + return self.source_port.get_type() == self.sink_port.get_type() == 'msg' def is_bus(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'bus' + return self.source_port.get_type() == self.sink_port.get_type() == 'bus' def validate(self): """ @@ -98,19 +94,21 @@ class Connection(Element): The ports must match in type. """ Element.validate(self) - platform = self.get_parent().get_parent() - source_domain = self.get_source().get_domain() - sink_domain = self.get_sink().get_domain() + platform = self.parent_platform + + source_domain = self.source_port.get_domain() + sink_domain = self.sink_port.get_domain() + if (source_domain, sink_domain) not in platform.connection_templates: self.add_error_message('No connection known for domains "{}", "{}"'.format( - source_domain, sink_domain)) + source_domain, sink_domain)) too_many_other_sinks = ( not platform.domains.get(source_domain, []).get('multiple_sinks', False) and - len(self.get_source().get_enabled_connections()) > 1 + len(self.source_port.get_enabled_connections()) > 1 ) too_many_other_sources = ( not platform.domains.get(sink_domain, []).get('multiple_sources', False) and - len(self.get_sink().get_enabled_connections()) > 1 + len(self.sink_port.get_enabled_connections()) > 1 ) if too_many_other_sinks: self.add_error_message( @@ -119,8 +117,8 @@ class Connection(Element): self.add_error_message( 'Domain "{}" can have only one upstream block'.format(sink_domain)) - source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() - sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() + source_size = Constants.TYPE_TO_SIZEOF[self.source_port.get_type()] * self.source_port.get_vlen() + sink_size = Constants.TYPE_TO_SIZEOF[self.sink_port.get_type()] * self.sink_port.get_vlen() if source_size != sink_size: self.add_error_message('Source IO size "{}" does not match sink IO size "{}".'.format(source_size, sink_size)) @@ -131,17 +129,15 @@ class Connection(Element): Returns: true if source and sink blocks are enabled """ - return self.get_source().get_parent().get_enabled() and \ - self.get_sink().get_parent().get_enabled() + return self.source_block.get_enabled() and self.sink_block.get_enabled() - ############################# - # Access Ports - ############################# - def get_sink(self): - return self._sink + @lazy_property + def source_block(self): + return self.source_port.parent_block - def get_source(self): - return self._source + @lazy_property + def sink_block(self): + return self.sink_port.parent_block ############################################## # Import/Export Methods @@ -153,9 +149,28 @@ class Connection(Element): Returns: a nested data odict """ - n = odict() - n['source_block_id'] = self.get_source().get_parent().get_id() - n['sink_block_id'] = self.get_sink().get_parent().get_id() - n['source_key'] = self.get_source().get_key() - n['sink_key'] = self.get_sink().get_key() + n = collections.OrderedDict() + n['source_block_id'] = self.source_block.get_id() + n['sink_block_id'] = self.sink_block.get_id() + n['source_key'] = self.source_port.get_key() + n['sink_key'] = self.sink_port.get_key() return n + + def _make_bus_connect(self): + source, sink = self.source_port, self.sink_port + + if (source.get_type() == 'bus') != (sink.get_type() == 'bus'): + raise ValueError('busses must get with busses') + + if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): + raise ValueError('port connections must have same cardinality') + + if source.get_type() == 'bus': + sources = source.get_associated_ports() + sinks = sink.get_associated_ports() + + for i in range(len(sources)): + try: + self.parent_flowgraph.connect(sources[i], sinks[i]) + except: + pass diff --git a/grc/core/Constants.py b/grc/core/Constants.py index eeb1d7f848..992d5e7d83 100644 --- a/grc/core/Constants.py +++ b/grc/core/Constants.py @@ -17,10 +17,14 @@ 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 os -import numpy import stat +import numpy +import six + # Data files DATA_DIR = os.path.dirname(__file__) FLOW_GRAPH_DTD = os.path.join(DATA_DIR, 'flow_graph.dtd') @@ -64,15 +68,15 @@ HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP VECTOR_TYPES = (tuple, list, set, numpy.ndarray) COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128] REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64] -INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64, +INT_TYPES = [int, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64, numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] # Cast to tuple for isinstance, concat subtypes COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES) REAL_TYPES = tuple(REAL_TYPES + INT_TYPES) INT_TYPES = tuple(INT_TYPES) -# Updating colors. Using the standard color pallette from: -# http://www.google.com/design/spec/style/color.html#color-color-palette +# Updating colors. Using the standard color palette from: +# http://www.google.com/design/spec/style/color.html#color-color-palette # Most are based on the main, primary color standard. Some are within # that color's spectrum when it was deemed necessary. GRC_COLOR_BROWN = '#795548' @@ -130,21 +134,6 @@ for name, key, sizeof, color in CORE_TYPES: TYPE_TO_COLOR[key] = color TYPE_TO_SIZEOF[key] = sizeof -for key, (sizeof, color) in ALIAS_TYPES.iteritems(): +for key, (sizeof, color) in six.iteritems(ALIAS_TYPES): TYPE_TO_COLOR[key] = color TYPE_TO_SIZEOF[key] = sizeof - -# Coloring -COMPLEX_COLOR_SPEC = '#3399FF' -FLOAT_COLOR_SPEC = '#FF8C69' -INT_COLOR_SPEC = '#00FF99' -SHORT_COLOR_SPEC = '#FFFF66' -BYTE_COLOR_SPEC = '#FF66FF' -COMPLEX_VECTOR_COLOR_SPEC = '#3399AA' -FLOAT_VECTOR_COLOR_SPEC = '#CC8C69' -INT_VECTOR_COLOR_SPEC = '#00CC99' -SHORT_VECTOR_COLOR_SPEC = '#CCCC33' -BYTE_VECTOR_COLOR_SPEC = '#CC66CC' -ID_COLOR_SPEC = '#DDDDDD' -WILDCARD_COLOR_SPEC = '#FFFFFF' -MSG_COLOR_SPEC = '#777777' diff --git a/grc/core/Element.py b/grc/core/Element.py index 67c36e12b4..f07bb113e1 100644 --- a/grc/core/Element.py +++ b/grc/core/Element.py @@ -1,28 +1,44 @@ -""" -Copyright 2008, 2009, 2015 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 -""" +# 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 + +import weakref +import functools + + +class lazy_property(object): + + def __init__(self, func): + self.func = func + functools.update_wrapper(self, func) + + def __get__(self, instance, cls): + if instance is None: + return self + value = self.func(instance) + weak_value = weakref.proxy(value) if not weakref.ProxyType else value + setattr(instance, self.func.__name__, weak_value) + return weak_value class Element(object): def __init__(self, parent=None): - self._parent = parent - self._error_messages = list() + self._parent = weakref.ref(parent) if parent else lambda: None + self._error_messages = [] ################################################## # Element Validation API @@ -33,6 +49,7 @@ class Element(object): Call this base method before adding error messages in the subclass. """ del self._error_messages[:] + for child in self.get_children(): child.validate() @@ -64,7 +81,9 @@ class Element(object): a list of error message strings """ error_messages = list(self._error_messages) # Make a copy - for child in filter(lambda c: c.get_enabled() and not c.get_bypassed(), self.get_children()): + for child in self.get_children(): + if not child.get_enabled() or child.get_bypassed(): + continue for msg in child.get_error_messages(): error_messages.append("{}:\n\t{}".format(child, msg.replace("\n", "\n\t"))) return error_messages @@ -86,8 +105,39 @@ class Element(object): ############################################## # Tree-like API ############################################## - def get_parent(self): - return self._parent + @property + def parent(self): + return self._parent() + + 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): + from .Platform import Platform + return self.get_parent_by_type(Platform) + + @lazy_property + def parent_flowgraph(self): + from .FlowGraph import FlowGraph + return self.get_parent_by_type(FlowGraph) + + @lazy_property + def parent_block(self): + from .Block import Block + return self.get_parent_by_type(Block) + + def reset_parents_by_type(self): + """Reset all lazy properties""" + for name, obj in vars(Element): + if isinstance(obj, lazy_property): + delattr(self, name) def get_children(self): return list() diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py index 949eecaa71..67e86f3e6e 100644 --- a/grc/core/FlowGraph.py +++ b/grc/core/FlowGraph.py @@ -15,17 +15,21 @@ # 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 + import imp import time -from itertools import ifilter, chain +import re +from itertools import chain from operator import methodcaller, attrgetter +import collections -import re +from six.moves import filter from . import Messages from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION from .Element import Element -from .utils import odict, expr_utils +from .utils import expr_utils _parameter_matcher = re.compile('^(parameter)$') _monitors_searcher = re.compile('(ctrlport_monitor)') @@ -49,26 +53,21 @@ class FlowGraph(Element): Returns: the flow graph object """ - Element.__init__(self, platform) - self._elements = [] + Element.__init__(self, parent=platform) self._timestamp = time.ctime() + self._options_block = self.parent_platform.get_new_block(self, 'options') - self.platform = platform # todo: make this a lazy prop - self.blocks = [] + self.blocks = [self._options_block] self.connections = [] self._eval_cache = {} self.namespace = {} self.grc_file_path = '' - self._options_block = self.new_block('options') def __str__(self): return 'FlowGraph - {}({})'.format(self.get_option('title'), self.get_option('id')) - ############################################## - # TODO: Move these to new generator package - ############################################## def get_imports(self): """ Get a set of all import statements in this flow graph namespace. @@ -87,7 +86,7 @@ class FlowGraph(Element): Returns: a sorted list of variable blocks in order of dependency (indep -> dep) """ - variables = filter(attrgetter('is_variable'), self.iter_enabled_blocks()) + variables = list(filter(attrgetter('is_variable'), self.iter_enabled_blocks())) return expr_utils.sort_objects(variables, methodcaller('get_id'), methodcaller('get_var_make')) def get_parameters(self): @@ -97,15 +96,14 @@ class FlowGraph(Element): Returns: a list of parameterized variables """ - parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.iter_enabled_blocks()) + parameters = [b for b in self.iter_enabled_blocks() if _parameter_matcher.match(b.get_key())] return parameters def get_monitors(self): """ Get a list of all ControlPort monitors """ - monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), - self.iter_enabled_blocks()) + monitors = [b for b in self.iter_enabled_blocks() if _monitors_searcher.search(b.get_key())] return monitors def get_python_modules(self): @@ -115,7 +113,7 @@ class FlowGraph(Element): yield block.get_id(), block.get_param('source_code').get_value() def get_bussink(self): - bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) + bussink = [b for b in self.get_enabled_blocks() if _bussink_searcher.search(b.get_key())] for i in bussink: for j in i.get_params(): @@ -124,7 +122,7 @@ class FlowGraph(Element): return False def get_bussrc(self): - bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) + bussrc = [b for b in self.get_enabled_blocks() if _bussrc_searcher.search(b.get_key())] for i in bussrc: for j in i.get_params(): @@ -133,18 +131,18 @@ class FlowGraph(Element): return False def get_bus_structure_sink(self): - bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) + bussink = [b for b in self.get_enabled_blocks() if _bus_struct_sink_searcher.search(b.get_key())] return bussink def get_bus_structure_src(self): - bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) + bussrc = [b for b in self.get_enabled_blocks() if _bus_struct_src_searcher.search(b.get_key())] return bussrc def iter_enabled_blocks(self): """ Get an iterator of all blocks that are enabled and not bypassed. """ - return ifilter(methodcaller('get_enabled'), self.blocks) + return filter(methodcaller('get_enabled'), self.blocks) def get_enabled_blocks(self): """ @@ -162,7 +160,7 @@ class FlowGraph(Element): Returns: a list of blocks """ - return filter(methodcaller('get_bypassed'), self.blocks) + return list(filter(methodcaller('get_bypassed'), self.blocks)) def get_enabled_connections(self): """ @@ -171,7 +169,7 @@ class FlowGraph(Element): Returns: a list of connections """ - return filter(methodcaller('get_enabled'), self.connections) + return list(filter(methodcaller('get_enabled'), self.connections)) def get_option(self, key): """ @@ -196,19 +194,6 @@ class FlowGraph(Element): raise KeyError('No block with ID {!r}'.format(id)) def get_elements(self): - """ - Get a list of all the elements. - Always ensure that the options block is in the list (only once). - - Returns: - the element list - """ - options_block_count = self.blocks.count(self._options_block) - if not options_block_count: - self.blocks.append(self._options_block) - for i in range(options_block_count-1): - self.blocks.remove(self._options_block) - return self.blocks + self.connections get_children = get_elements @@ -217,7 +202,6 @@ class FlowGraph(Element): """ Flag the namespace to be renewed. """ - self.renew_namespace() for child in chain(self.blocks, self.connections): child.rewrite() @@ -229,14 +213,14 @@ class FlowGraph(Element): # Load imports for expr in self.get_imports(): try: - exec expr in namespace + exec(expr, namespace) except: pass for id, expr in self.get_python_modules(): try: module = imp.new_module(id) - exec expr in module.__dict__ + exec(expr, module.__dict__) namespace[id] = module except: pass @@ -294,8 +278,10 @@ class FlowGraph(Element): Returns: the new block or None if not found """ + if key == 'options': + return self._options_block try: - block = self.platform.get_new_block(self, key) + block = self.parent_platform.get_new_block(self, key) self.blocks.append(block) except KeyError: block = None @@ -314,7 +300,7 @@ class FlowGraph(Element): the new connection """ - connection = self.platform.Connection( + connection = self.parent_platform.Connection( flow_graph=self, porta=porta, portb=portb) self.connections.append(connection) return connection @@ -326,22 +312,25 @@ class FlowGraph(Element): If the element is a block, remove its connections. If the element is a connection, just remove the connection. """ + if element is self._options_block: + return + if element.is_port: # Found a port, set to parent signal block - element = element.get_parent() + element = element.parent if element in self.blocks: # Remove block, remove all involved connections for port in element.get_ports(): - map(self.remove_element, port.get_connections()) + for connection in port.get_connections(): + self.remove_element(connection) self.blocks.remove(element) elif element in self.connections: if element.is_bus(): - cons_list = [] - for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): - cons_list.extend(i) - map(self.remove_element, cons_list) + for port in element.source_port.get_associated_ports(): + for connection in port.get_connections(): + self.remove_element(connection) self.connections.remove(element) ############################################## @@ -362,20 +351,19 @@ class FlowGraph(Element): str(b) )) connections = sorted(self.connections, key=str) - n = odict() + n = collections.OrderedDict() n['timestamp'] = self._timestamp n['block'] = [b.export_data() for b in blocks] n['connection'] = [c.export_data() for c in connections] - instructions = odict({ - 'created': '.'.join(self.get_parent().config.version_parts), - 'format': FLOW_GRAPH_FILE_FORMAT_VERSION, - }) - return odict({'flow_graph': n, '_instructions': instructions}) + instructions = collections.OrderedDict() + instructions['created'] = '.'.join(self.parent.config.version_parts) + instructions['format'] = FLOW_GRAPH_FILE_FORMAT_VERSION + return {'flow_graph': n, '_instructions': instructions} def import_data(self, n): """ Import blocks and connections into this flow graph. - Clear this flowgraph of all previous blocks and connections. + Clear this flow graph of all previous blocks and connections. Any blocks or connections in error will be ignored. Args: @@ -386,31 +374,31 @@ class FlowGraph(Element): del self.connections[:] # set file format try: - instructions = n.find('_instructions') or {} + instructions = n.get('_instructions', {}) file_format = int(instructions.get('format', '0')) or _guess_file_format_1(n) except: file_format = 0 - fg_n = n and n.find('flow_graph') or odict() # use blank data if none provided - self._timestamp = fg_n.find('timestamp') or time.ctime() + fg_n = n and n.get('flow_graph', {}) # use blank data if none provided + self._timestamp = fg_n.get('timestamp', time.ctime()) # build the blocks - self._options_block = self.new_block('options') - for block_n in fg_n.findall('block'): - key = block_n.find('key') - block = self._options_block if key == 'options' else self.new_block(key) + self.blocks.append(self._options_block) + for block_n in fg_n.get('block', []): + key = block_n['key'] + block = self.new_block(key) if not block: # we're before the initial fg update(), so no evaluated values! # --> use raw value instead path_param = self._options_block.get_param('hier_block_src_path') - file_path = self.platform.find_file_in_paths( + file_path = self.parent_platform.find_file_in_paths( filename=key + '.grc', paths=path_param.get_value(), cwd=self.grc_file_path ) if file_path: # grc file found. load and get block - self.platform.load_and_generate_flow_graph(file_path) + self.parent_platform.load_and_generate_flow_graph(file_path) block = self.new_block(key) # can be None if not block: # looks like this block key cannot be found @@ -440,12 +428,12 @@ class FlowGraph(Element): return port errors = False - for connection_n in fg_n.findall('connection'): + for connection_n in fg_n.get('connection', []): # get the block ids and port keys - source_block_id = connection_n.find('source_block_id') - sink_block_id = connection_n.find('sink_block_id') - source_key = connection_n.find('source_key') - sink_key = connection_n.find('sink_key') + source_block_id = connection_n.get('source_block_id') + sink_block_id = connection_n.get('sink_block_id') + source_key = connection_n.get('source_key') + sink_key = connection_n.get('sink_key') try: source_block = self.get_block(source_block_id) sink_block = self.get_block(sink_block_id) @@ -484,31 +472,28 @@ class FlowGraph(Element): get_p_gui = block.get_sinks_gui bus_structure = block.form_bus_structure('sink') - if 'bus' in map(lambda a: a.get_type(), get_p_gui()): + if 'bus' in [a.get_type() for a in get_p_gui()]: if len(get_p_gui()) > len(bus_structure): times = range(len(bus_structure), len(get_p_gui())) for i in times: for connect in get_p_gui()[-1].get_connections(): - block.get_parent().remove_element(connect) + block.parent.remove_element(connect) get_p().remove(get_p_gui()[-1]) elif len(get_p_gui()) < len(bus_structure): n = {'name': 'bus', 'type': 'bus'} - if True in map( - lambda a: isinstance(a.get_nports(), int), - get_p()): + if any(isinstance(a.get_nports(), int) for a in get_p()): n['nports'] = str(1) times = range(len(get_p_gui()), len(bus_structure)) for i in times: n['key'] = str(len(get_p())) - n = odict(n) - port = block.get_parent().get_parent().Port( + n = dict(n) + port = block.parent.parent.Port( block=block, n=n, dir=direc) get_p().append(port) - if 'bus' in map(lambda a: a.get_type(), - block.get_sources_gui()): + if 'bus' in [a.get_type() for a in block.get_sources_gui()]: for i in range(len(block.get_sources_gui())): if len(block.get_sources_gui()[ i].get_connections()) > 0: @@ -517,7 +502,7 @@ class FlowGraph(Element): for j in range(len(source.get_connections())): sink.append( - source.get_connections()[j].get_sink()) + source.get_connections()[j].sink_port) for elt in source.get_connections(): self.remove_element(elt) for j in sink: @@ -554,9 +539,9 @@ def _guess_file_format_1(n): """ try: has_non_numeric_message_keys = any(not ( - connection_n.find('source_key').isdigit() and - connection_n.find('sink_key').isdigit() - ) for connection_n in n.find('flow_graph').findall('connection')) + connection_n.get('source_key', '').isdigit() and + connection_n.get('sink_key', '').isdigit() + ) for connection_n in n.get('flow_graph', []).get('connection', [])) if has_non_numeric_message_keys: return 1 except: @@ -570,21 +555,21 @@ def _initialize_dummy_block(block, block_n): Modify block object to get the behaviour for a missing block """ - block._key = block_n.find('key') + block._key = block_n.get('key') block.is_dummy_block = lambda: True block.is_valid = lambda: False block.get_enabled = lambda: False - for param_n in block_n.findall('param'): + for param_n in block_n.get('param', []): if param_n['key'] not in block.get_param_keys(): - new_param_n = odict({'key': param_n['key'], 'name': param_n['key'], 'type': 'string'}) - params = block.get_parent().get_parent().Param(block=block, n=new_param_n) + new_param_n = {'key': param_n['key'], 'name': param_n['key'], 'type': 'string'} + params = block.parent_platform.Param(block=block, n=new_param_n) block.get_params().append(params) def _dummy_block_add_port(block, key, dir): """ This is so ugly... Add a port to a dummy-field block """ - port_n = odict({'name': '?', 'key': key, 'type': ''}) - port = block.get_parent().get_parent().Port(block=block, n=port_n, dir=dir) + port_n = {'name': '?', 'key': key, 'type': ''} + port = block.parent_platform.Port(block=block, n=port_n, dir=dir) if port.is_source: block.get_sources().append(port) else: diff --git a/grc/core/Messages.py b/grc/core/Messages.py index 8daa12c33f..596b6197d8 100644 --- a/grc/core/Messages.py +++ b/grc/core/Messages.py @@ -16,9 +16,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from __future__ import absolute_import + import traceback import sys -import os # A list of functions that can receive a message. MESSENGERS_LIST = list() diff --git a/grc/core/Param.py b/grc/core/Param.py index d155800c43..35bb176744 100644 --- a/grc/core/Param.py +++ b/grc/core/Param.py @@ -17,20 +17,20 @@ 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 ast import weakref import re +import collections + +from six.moves import builtins, filter, map, range, zip from . import Constants -from .Constants import VECTOR_TYPES, COMPLEX_TYPES, REAL_TYPES, INT_TYPES from .Element import Element -from .utils import odict # Blacklist certain ids, its not complete, but should help -import __builtin__ - - -ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__) +ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(builtins) try: from gnuradio import gr ID_BLACKLIST.extend(attr for attr in dir(gr.top_block()) if not attr.startswith('_')) @@ -64,7 +64,7 @@ def num_to_str(num): return template.format(value / factor, symbol.strip()) return template.format(value, '') - if isinstance(num, COMPLEX_TYPES): + if isinstance(num, Constants.COMPLEX_TYPES): num = complex(num) # Cast to python complex if num == 0: return '0' @@ -79,12 +79,12 @@ class Option(Element): def __init__(self, param, n): Element.__init__(self, param) - self._name = n.find('name') - self._key = n.find('key') + self._name = n.get('name') + self._key = n.get('key') self._opts = dict() - opts = n.findall('opt') + opts = n.get('opt', []) # Test against opts when non enum - if not self.get_parent().is_enum() and opts: + if not self.parent.is_enum() and opts: raise Exception('Options for non-enum types cannot have sub-options') # Extract opts for opt in opts: @@ -112,13 +112,13 @@ class Option(Element): # Access Opts ############################################## def get_opt_keys(self): - return self._opts.keys() + return list(self._opts.keys()) def get_opt(self, key): return self._opts[key] def get_opts(self): - return self._opts.values() + return list(self._opts.values()) class TemplateArg(object): @@ -155,7 +155,7 @@ class Param(Element): n: the nested odict """ # If the base key is a valid param key, copy its data and overlay this params data - base_key = n.find('base_key') + base_key = n.get('base_key') if base_key and base_key in block.get_param_keys(): n_expanded = block.get_param(base_key)._n.copy() n_expanded.update(n) @@ -163,20 +163,21 @@ class Param(Element): # Save odict in case this param will be base for another self._n = n # Parse the data - self._name = n.find('name') - self._key = n.find('key') - value = n.find('value') or '' - self._type = n.find('type') or 'raw' - self._hide = n.find('hide') or '' - self._tab_label = n.find('tab') or block.get_param_tab_labels()[0] + self._name = n['name'] + self._key = n['key'] + value = n.get('value', '') + self._type = n.get('type', 'raw') + self._hide = n.get('hide', '') + self._tab_label = n.get('tab', block.get_param_tab_labels()[0]) if self._tab_label not in block.get_param_tab_labels(): block.get_param_tab_labels().append(self._tab_label) # Build the param - Element.__init__(self, block) + Element.__init__(self, parent=block) # Create the Option objects from the n data self._options = list() self._evaluated = None - for option in map(lambda o: Option(param=self, n=o), n.findall('option')): + for o_n in n.get('option', []): + option = Option(param=self, n=o_n) key = option.get_key() # Test against repeated keys if key in self.get_option_keys(): @@ -257,9 +258,9 @@ class Param(Element): t = self.get_type() if isinstance(e, bool): return str(e) - elif isinstance(e, COMPLEX_TYPES): + elif isinstance(e, Constants.COMPLEX_TYPES): dt_str = num_to_str(e) - elif isinstance(e, VECTOR_TYPES): + elif isinstance(e, Constants.VECTOR_TYPES): # Vector types if len(e) > 8: # Large vectors use code @@ -292,38 +293,6 @@ class Param(Element): def __str__(self): return 'Param - {}({})'.format(self.get_name(), self.get_key()) - def get_color(self): - """ - Get the color that represents this param's type. - - Returns: - a hex color code. - """ - try: - return { - # Number types - 'complex': Constants.COMPLEX_COLOR_SPEC, - 'real': Constants.FLOAT_COLOR_SPEC, - 'float': Constants.FLOAT_COLOR_SPEC, - 'int': Constants.INT_COLOR_SPEC, - # Vector types - 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, - 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, - # Special - 'bool': Constants.INT_COLOR_SPEC, - 'hex': Constants.INT_COLOR_SPEC, - 'string': Constants.BYTE_VECTOR_COLOR_SPEC, - 'id': Constants.ID_COLOR_SPEC, - 'stream_id': Constants.ID_COLOR_SPEC, - 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, - 'notebook': Constants.INT_VECTOR_COLOR_SPEC, - 'raw': Constants.WILDCARD_COLOR_SPEC, - }[self.get_type()] - except: - return '#FFFFFF' - def get_hide(self): """ Get the hide value from the base class. @@ -335,20 +304,17 @@ class Param(Element): Returns: hide the hide property string """ - hide = self.get_parent().resolve_dependencies(self._hide).strip() + hide = self.parent.resolve_dependencies(self._hide).strip() if hide: return hide # Hide ID in non variable blocks - if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): + if self.get_key() == 'id' and not _show_id_matcher.match(self.parent.get_key()): return 'part' # Hide port controllers for type and nports - if self.get_key() in ' '.join(map(lambda p: ' '.join([p._type, p._nports]), - self.get_parent().get_ports())): + if self.get_key() in ' '.join([' '.join([p._type, p._nports]) for p in self.parent.get_ports()]): return 'part' # Hide port controllers for vlen, when == 1 - if self.get_key() in ' '.join(map( - lambda p: p._vlen, self.get_parent().get_ports()) - ): + if self.get_key() in ' '.join(p._vlen for p in self.parent.get_ports()): try: if int(self.get_evaluated()) == 1: return 'part' @@ -371,7 +337,7 @@ class Param(Element): self._evaluated = None try: self._evaluated = self.evaluate() - except Exception, e: + except Exception as e: self.add_error_message(str(e)) def get_evaluated(self): @@ -403,22 +369,22 @@ class Param(Element): elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): # Raise exception if python cannot evaluate this value try: - e = self.get_parent().get_parent().evaluate(v) - except Exception, e: + e = self.parent_flowgraph.evaluate(v) + except Exception as e: raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) # Raise an exception if the data is invalid if t == 'raw': return e elif t == 'complex': - if not isinstance(e, COMPLEX_TYPES): + if not isinstance(e, Constants.COMPLEX_TYPES): raise Exception('Expression "{}" is invalid for type complex.'.format(str(e))) return e elif t == 'real' or t == 'float': - if not isinstance(e, REAL_TYPES): + if not isinstance(e, Constants.REAL_TYPES): raise Exception('Expression "{}" is invalid for type float.'.format(str(e))) return e elif t == 'int': - if not isinstance(e, INT_TYPES): + if not isinstance(e, Constants.INT_TYPES): raise Exception('Expression "{}" is invalid for type integer.'.format(str(e))) return e elif t == 'hex': @@ -438,29 +404,29 @@ class Param(Element): v = '()' # Raise exception if python cannot evaluate this value try: - e = self.get_parent().get_parent().evaluate(v) - except Exception, e: + e = self.parent.parent.evaluate(v) + except Exception as e: raise Exception('Value "{}" cannot be evaluated:\n{}'.format(v, e)) # Raise an exception if the data is invalid if t == 'complex_vector': - if not isinstance(e, VECTOR_TYPES): + if not isinstance(e, Constants.VECTOR_TYPES): self._lisitify_flag = True e = [e] - if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): + if not all([isinstance(ei, Constants.COMPLEX_TYPES) for ei in e]): raise Exception('Expression "{}" is invalid for type complex vector.'.format(str(e))) return e elif t == 'real_vector' or t == 'float_vector': - if not isinstance(e, VECTOR_TYPES): + if not isinstance(e, Constants.VECTOR_TYPES): self._lisitify_flag = True e = [e] - if not all([isinstance(ei, REAL_TYPES) for ei in e]): + if not all([isinstance(ei, Constants.REAL_TYPES) for ei in e]): raise Exception('Expression "{}" is invalid for type float vector.'.format(str(e))) return e elif t == 'int_vector': - if not isinstance(e, VECTOR_TYPES): + if not isinstance(e, Constants.VECTOR_TYPES): self._lisitify_flag = True e = [e] - if not all([isinstance(ei, INT_TYPES) for ei in e]): + if not all([isinstance(ei, Constants.INT_TYPES) for ei in e]): raise Exception('Expression "{}" is invalid for type integer vector.'.format(str(e))) return e ######################### @@ -469,7 +435,7 @@ class Param(Element): elif t in ('string', 'file_open', 'file_save', '_multiline', '_multiline_python_external'): # Do not check if file/directory exists, that is a runtime issue try: - e = self.get_parent().get_parent().evaluate(v) + e = self.parent.parent.evaluate(v) if not isinstance(e, str): raise Exception() except: @@ -500,16 +466,16 @@ class Param(Element): elif t == 'stream_id': # Get a list of all stream ids used in the virtual sinks ids = [param.get_value() for param in filter( - lambda p: p.get_parent().is_virtual_sink(), + lambda p: p.parent.is_virtual_sink(), self.get_all_params(t), )] # Check that the virtual sink's stream id is unique - if self.get_parent().is_virtual_sink(): + if self.parent.is_virtual_sink(): # Id should only appear once, or zero times if block is disabled if ids.count(v) > 1: raise Exception('Stream ID "{}" is not unique.'.format(v)) # Check that the virtual source's steam id is found - if self.get_parent().is_virtual_source(): + if self.parent.is_virtual_source(): if v not in ids: raise Exception('Stream ID "{}" is not found.'.format(v)) return v @@ -557,7 +523,7 @@ class Param(Element): if not v: # Allow for empty grid pos return '' - e = self.get_parent().get_parent().evaluate(v) + e = self.parent_flowgraph.evaluate(v) if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): raise Exception('A grid position must be a list of 4 integers.') row, col, row_span, col_span = e @@ -569,7 +535,7 @@ class Param(Element): raise Exception('Row and column span must be greater than zero.') # Get hostage cell parent try: - my_parent = self.get_parent().get_param('notebook').evaluate() + my_parent = self.parent.get_param('notebook').evaluate() except: my_parent = '' # Calculate hostage cells @@ -577,7 +543,7 @@ class Param(Element): for c in range(col_span): self._hostage_cells.append((my_parent, (row+r, col+c))) # Avoid collisions - params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) + params = [p for p in self.get_all_params('grid_pos') if p is not self] for param in params: for parent, cell in param._hostage_cells: if (parent, cell) in self._hostage_cells: @@ -592,7 +558,7 @@ class Param(Element): return '' # Get a list of all notebooks - notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) + notebook_blocks = [b for b in self.parent_flowgraph.get_enabled_blocks() if b.get_key() == 'notebook'] # Check for notebook param syntax try: notebook_id, page_index = map(str.strip, v.split(',')) @@ -600,7 +566,7 @@ class Param(Element): raise Exception('Bad notebook page format.') # Check that the notebook id is valid try: - notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] + notebook_block = [b for b in notebook_blocks if b.get_id() == notebook_id][0] except: raise Exception('Notebook id "{}" is not an existing notebook id.'.format(notebook_id)) @@ -616,12 +582,12 @@ class Param(Element): # New namespace n = dict() try: - exec v in n + exec(v, n) except ImportError: raise Exception('Import "{}" failed.'.format(v)) except Exception: raise Exception('Bad import syntax: "{}".'.format(v)) - return filter(lambda k: str(k) != '__builtins__', n.keys()) + return [k for k in list(n.keys()) if str(k) != '__builtins__'] ######################### else: @@ -667,7 +633,10 @@ class Param(Element): Returns: a list of params """ - return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) + params = [] + for block in self.parent_flowgraph.get_enabled_blocks(): + params.extend(p for p in block.get_params() if p.get_type() == type) + return params def is_enum(self): return self._type == 'enum' @@ -689,13 +658,13 @@ class Param(Element): self._default = str(value) def get_type(self): - return self.get_parent().resolve_dependencies(self._type) + return self.parent.resolve_dependencies(self._type) def get_tab_label(self): return self._tab_label def get_name(self): - return self.get_parent().resolve_dependencies(self._name).strip() + return self.parent.resolve_dependencies(self._name).strip() def get_key(self): return self._key @@ -734,7 +703,7 @@ class Param(Element): Returns: a nested data odict """ - n = odict() + n = collections.OrderedDict() n['key'] = self.get_key() n['value'] = self.get_value() return n diff --git a/grc/core/ParseXML.py b/grc/core/ParseXML.py index c9f6541ee7..163289ba06 100644 --- a/grc/core/ParseXML.py +++ b/grc/core/ParseXML.py @@ -17,9 +17,13 @@ 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 + from lxml import etree -from .utils import odict +import six +from six.moves import map + xml_failures = {} etree.set_default_parser(etree.XMLParser(remove_comments=True)) @@ -75,17 +79,35 @@ def from_file(xml_file): the nested data with grc version information """ xml = etree.parse(xml_file) - nested_data = _from_file(xml.getroot()) + + tag, nested_data = _from_file(xml.getroot()) + nested_data = {tag: nested_data, '_instructions': {}} # Get the embedded instructions and build a dictionary item - nested_data['_instructions'] = {} xml_instructions = xml.xpath('/processing-instruction()') - for inst in filter(lambda i: i.target == 'grc', xml_instructions): - nested_data['_instructions'] = odict(inst.attrib) + for inst in xml_instructions: + if inst.target != 'grc': + continue + nested_data['_instructions'] = dict(inst.attrib) return nested_data -def _from_file(xml): +WANT_A_LIST = { + '/block': 'import callback param check sink source'.split(), + '/block/param_tab_order': 'tab'.split(), + '/block/param': 'option'.split(), + '/block/param/option': 'opt'.split(), + '/flow_graph': 'block connection'.split(), + '/flow_graph/block': 'param'.split(), + '/cat': 'cat block'.split(), + '/cat/cat': 'cat block'.split(), + '/cat/cat/cat': 'cat block'.split(), + '/cat/cat/cat/cat': 'cat block'.split(), + '/domain': 'connection'.split(), +} + + +def _from_file(xml, parent_tag=''): """ Recursively parse the xml tree into nested data format. @@ -96,21 +118,24 @@ def _from_file(xml): the nested data """ tag = xml.tag + tag_path = parent_tag + '/' + tag + if not len(xml): - return odict({tag: xml.text or ''}) # store empty tags (text is None) as empty string - nested_data = odict() + return tag, xml.text or '' # store empty tags (text is None) as empty string + + nested_data = {} for elem in xml: - key, value = _from_file(elem).items()[0] - if key in nested_data: - nested_data[key].append(value) + key, value = _from_file(elem, tag_path) + + if key in WANT_A_LIST.get(tag_path, []): + try: + nested_data[key].append(value) + except KeyError: + nested_data[key] = [value] else: - nested_data[key] = [value] - # Delistify if the length of values is 1 - for key, values in nested_data.iteritems(): - if len(values) == 1: - nested_data[key] = values[0] + nested_data[key] = value - return odict({tag: nested_data}) + return tag, nested_data def to_file(nested_data, xml_file): @@ -127,7 +152,7 @@ def to_file(nested_data, xml_file): if instructions: xml_data += etree.tostring(etree.ProcessingInstruction( 'grc', ' '.join( - "{0}='{1}'".format(*item) for item in instructions.iteritems()) + "{0}='{1}'".format(*item) for item in six.iteritems(instructions)) ), xml_declaration=True, pretty_print=True, encoding='utf-8') xml_data += etree.tostring(_to_file(nested_data)[0], pretty_print=True, encoding='utf-8') @@ -146,14 +171,14 @@ def _to_file(nested_data): the xml tree filled with child nodes """ nodes = list() - for key, values in nested_data.iteritems(): + for key, values in six.iteritems(nested_data): # Listify the values if not a list if not isinstance(values, (list, set, tuple)): values = [values] for value in values: node = etree.Element(key) - if isinstance(value, (str, unicode)): - node.text = unicode(value) + if isinstance(value, (str, six.text_type)): + node.text = six.text_type(value) else: node.extend(_to_file(value)) nodes.append(node) diff --git a/grc/core/Platform.py b/grc/core/Platform.py index 9b25e67d65..be7b60ca59 100644 --- a/grc/core/Platform.py +++ b/grc/core/Platform.py @@ -17,9 +17,14 @@ 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 + import os import sys +import six +from six.moves import range + from . import ParseXML, Messages, Constants from .Config import Config @@ -31,7 +36,7 @@ from .Block import Block from .Port import Port from .Param import Param -from .utils import odict, extract_docs +from .utils import extract_docs class Platform(Element): @@ -48,10 +53,9 @@ class Platform(Element): def __init__(self, *args, **kwargs): """ Make a platform for GNU Radio """ - Element.__init__(self) + Element.__init__(self, parent=None) self.config = self.Config(*args, **kwargs) - self.block_docstrings = {} self.block_docstrings_loaded_callback = lambda: None # dummy to be replaced by BlockTreeWindow @@ -63,18 +67,19 @@ class Platform(Element): self._block_dtd = Constants.BLOCK_DTD self._default_flow_graph = Constants.DEFAULT_FLOW_GRAPH - # Create a dummy flow graph for the blocks - self._flow_graph = Element(self) - self._flow_graph.connections = [] - - self.blocks = odict() - self._blocks_n = odict() + self.blocks = {} + self._blocks_n = {} self._block_categories = {} self.domains = {} self.connection_templates = {} self._auto_hier_block_generate_chain = set() + # Create a dummy flow graph for the blocks + self._flow_graph = Element.__new__(FlowGraph) + Element.__init__(self._flow_graph, self) + self._flow_graph.connections = [] + self.build_block_library() def __str__(self): @@ -130,12 +135,13 @@ class Platform(Element): Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e))) return False - self.load_block_xml(generator.get_file_path_xml()) + self.load_block_xml(generator.file_path_xml) return True 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() @@ -157,10 +163,10 @@ class Platform(Element): # print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s' % (e, xml_file) pass except Exception as e: - print >> sys.stderr, 'Warning: XML parsing failed:\n\t%r\n\tIgnoring: %s' % (e, xml_file) + 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 self.blocks.iteritems(): + 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: @@ -182,20 +188,20 @@ class Platform(Element): yield block_path elif os.path.isdir(block_path): for dirpath, dirnames, filenames in os.walk(block_path): - for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): + for filename in sorted(f for f in filenames if f.endswith('.xml')): yield os.path.join(dirpath, filename) def load_block_xml(self, xml_file): """Load block description from xml file""" # Validate and import ParseXML.validate_dtd(xml_file, self._block_dtd) - n = ParseXML.from_file(xml_file).find('block') + n = ParseXML.from_file(xml_file).get('block', {}) n['block_wrapper_path'] = xml_file # inject block wrapper path # Get block instance and add it to the list of blocks block = self.Block(self._flow_graph, n) key = block.get_key() if key in self.blocks: - print >> sys.stderr, 'Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) + print('Warning: Block with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr) else: # Store the block self.blocks[key] = block self._blocks_n[key] = n @@ -213,62 +219,62 @@ class Platform(Element): path = [] def load_category(cat_n): - path.append(cat_n.find('name').strip()) - for block_key in cat_n.findall('block'): + 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.findall('cat'): + for sub_cat_n in cat_n.get('cat', []): load_category(sub_cat_n) path.pop() - load_category(xml.find('cat')) + load_category(xml.get('cat', {})) def load_domain_xml(self, xml_file): """Load a domain properties and connection templates from XML""" ParseXML.validate_dtd(xml_file, Constants.DOMAIN_DTD) - n = ParseXML.from_file(xml_file).find('domain') + n = ParseXML.from_file(xml_file).get('domain') - key = n.find('key') + key = n.get('key') if not key: - print >> sys.stderr, 'Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file) + print('Warning: Domain with emtpy key.\n\tIgnoring: {}'.format(xml_file), file=sys.stderr) return if key in self.domains: # test against repeated keys - print >> sys.stderr, 'Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file) + print('Warning: Domain with key "{}" already exists.\n\tIgnoring: {}'.format(key, xml_file), file=sys.stderr) return - #to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '') + # to_bool = lambda s, d: d if s is None else s.lower() not in ('false', 'off', '0', '') def to_bool(s, d): if s is not None: return s.lower() not in ('false', 'off', '0', '') return d - color = n.find('color') or '' + color = n.get('color') or '' try: - import gtk # ugly but handy - gtk.gdk.color_parse(color) - except (ValueError, ImportError): + chars_per_color = 2 if len(color) > 4 else 1 + tuple(int(color[o:o + 2], 16) / 255.0 for o in range(1, 3 * chars_per_color, chars_per_color)) + except ValueError: if color: # no color is okay, default set in GUI - print >> sys.stderr, 'Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key) + print('Warning: Can\'t parse color code "{}" for domain "{}" '.format(color, key), file=sys.stderr) color = None self.domains[key] = dict( - name=n.find('name') or key, - multiple_sinks=to_bool(n.find('multiple_sinks'), True), - multiple_sources=to_bool(n.find('multiple_sources'), False), + name=n.get('name') or key, + multiple_sinks=to_bool(n.get('multiple_sinks'), True), + multiple_sources=to_bool(n.get('multiple_sources'), False), color=color ) - for connection_n in n.findall('connection'): - key = (connection_n.find('source_domain'), connection_n.find('sink_domain')) + for connection_n in n.get('connection', []): + key = (connection_n.get('source_domain'), connection_n.get('sink_domain')) if not all(key): - print >> sys.stderr, 'Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file) + print('Warning: Empty domain key(s) in connection template.\n\t{}'.format(xml_file), file=sys.stderr) elif key in self.connection_templates: - print >> sys.stderr, 'Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file) + print('Warning: Connection template "{}" already exists.\n\t{}'.format(key, xml_file), file=sys.stderr) else: - self.connection_templates[key] = connection_n.find('make') or '' + self.connection_templates[key] = connection_n.get('make') or '' def _save_docstring_extraction_result(self, key, docstrings): docs = {} - for match, docstring in docstrings.iteritems(): + for match, docstring in six.iteritems(docstrings): if not docstring or match.endswith('_sptr'): continue docstring = docstring.replace('\n\n', '\n').strip() @@ -300,7 +306,7 @@ class Platform(Element): return self.FlowGraph(platform=self) def get_blocks(self): - return self.blocks.values() + return list(self.blocks.values()) def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) diff --git a/grc/core/Port.py b/grc/core/Port.py index 6a8f484082..9a33c5c506 100644 --- a/grc/core/Port.py +++ b/grc/core/Port.py @@ -17,7 +17,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 .Constants import DEFAULT_DOMAIN, GR_STREAM_DOMAIN, GR_MESSAGE_DOMAIN +from __future__ import absolute_import + +from six.moves import filter + from .Element import Element from . import Constants @@ -30,7 +33,7 @@ def _get_source_from_virtual_sink_port(vsp): """ try: return _get_source_from_virtual_source_port( - vsp.get_enabled_connections()[0].get_source()) + vsp.get_enabled_connections()[0].source_port) except: raise Exception('Could not resolve source for virtual sink port {}'.format(vsp)) @@ -40,20 +43,20 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]): Recursively resolve source ports over the virtual connections. Keep track of traversed sources to avoid recursive loops. """ - if not vsp.get_parent().is_virtual_source(): + if not vsp.parent.is_virtual_source(): return vsp if vsp in traversed: raise Exception('Loop found when resolving virtual source {}'.format(vsp)) try: return _get_source_from_virtual_source_port( _get_source_from_virtual_sink_port( - filter( # Get all virtual sinks with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter( # Get all enabled blocks that are also virtual sinks + list(filter( # Get all virtual sinks with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.parent.get_param('stream_id').get_value(), + list(filter( # Get all enabled blocks that are also virtual sinks lambda b: b.is_virtual_sink(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sinks()[0] + vsp.parent.parent.get_enabled_blocks(), + )), + ))[0].get_sinks()[0] ), traversed + [vsp], ) except: @@ -68,7 +71,7 @@ def _get_sink_from_virtual_source_port(vsp): try: # Could have many connections, but use first return _get_sink_from_virtual_sink_port( - vsp.get_enabled_connections()[0].get_sink()) + vsp.get_enabled_connections()[0].sink_port) except: raise Exception('Could not resolve source for virtual source port {}'.format(vsp)) @@ -78,7 +81,7 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]): Recursively resolve sink ports over the virtual connections. Keep track of traversed sinks to avoid recursive loops. """ - if not vsp.get_parent().is_virtual_sink(): + if not vsp.parent.is_virtual_sink(): return vsp if vsp in traversed: raise Exception('Loop found when resolving virtual sink {}'.format(vsp)) @@ -86,11 +89,11 @@ def _get_sink_from_virtual_sink_port(vsp, traversed=[]): return _get_sink_from_virtual_sink_port( _get_sink_from_virtual_source_port( filter( # Get all virtual source with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter( # Get all enabled blocks that are also virtual sinks + lambda vs: vs.get_param('stream_id').get_value() == vsp.parent.get_param('stream_id').get_value(), + list(filter( # Get all enabled blocks that are also virtual sinks lambda b: b.is_virtual_source(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), + vsp.parent.parent.get_enabled_blocks(), + )), )[0].get_sources()[0] ), traversed + [vsp], ) @@ -113,31 +116,32 @@ class Port(Element): """ self._n = n if n['type'] == 'message': - n['domain'] = GR_MESSAGE_DOMAIN + n['domain'] = Constants.GR_MESSAGE_DOMAIN if 'domain' not in n: - n['domain'] = DEFAULT_DOMAIN - elif n['domain'] == GR_MESSAGE_DOMAIN: + n['domain'] = Constants.DEFAULT_DOMAIN + elif n['domain'] == Constants.GR_MESSAGE_DOMAIN: n['key'] = n['name'] n['type'] = 'message' # For port color if n['type'] == 'msg': n['key'] = 'msg' - if not n.find('key'): - n['key'] = str(next(block.port_counters[dir == 'source'])) + + n.setdefault('key', str(next(block.port_counters[dir == 'source']))) # Build the port - Element.__init__(self, block) + Element.__init__(self, parent=block) # Grab the data self._name = n['name'] self._key = n['key'] - self._type = n['type'] or '' - self._domain = n['domain'] - self._hide = n.find('hide') or '' + self._type = n.get('type', '') + self._domain = n.get('domain') + self._hide = n.get('hide', '') self._dir = dir self._hide_evaluated = False # Updated on rewrite() - self._nports = n.find('nports') or '' - self._vlen = n.find('vlen') or '' - self._optional = bool(n.find('optional')) + self._nports = n.get('nports', '') + self._vlen = n.get('vlen', '') + self._optional = bool(n.get('optional')) + self.di_optional = bool(n.get('optional')) self._clones = [] # References to cloned ports (for nports > 1) def __str__(self): @@ -147,7 +151,7 @@ class Port(Element): return 'Sink - {}({})'.format(self.get_name(), self.get_key()) def get_types(self): - return Constants.TYPE_TO_SIZEOF.keys() + return list(Constants.TYPE_TO_SIZEOF.keys()) def is_type_empty(self): return not self._n['type'] @@ -156,7 +160,7 @@ class Port(Element): Element.validate(self) if self.get_type() not in self.get_types(): self.add_error_message('Type "{}" is not a possible type.'.format(self.get_type())) - platform = self.get_parent().get_parent().get_parent() + platform = self.parent.parent.parent if self.get_domain() not in platform.domains: self.add_error_message('Domain key "{}" is not registered.'.format(self.get_domain())) if not self.get_enabled_connections() and not self.get_optional(): @@ -184,22 +188,22 @@ class Port(Element): self._vlen = '' Element.rewrite(self) - hide = self.get_parent().resolve_dependencies(self._hide).strip().lower() + hide = self.parent.resolve_dependencies(self._hide).strip().lower() self._hide_evaluated = False if hide in ('false', 'off', '0') else bool(hide) # Update domain if was deduced from (dynamic) port type type_ = self.get_type() - if self._domain == GR_STREAM_DOMAIN and type_ == "message": - self._domain = GR_MESSAGE_DOMAIN + if self._domain == Constants.GR_STREAM_DOMAIN and type_ == "message": + self._domain = Constants.GR_MESSAGE_DOMAIN self._key = self._name - if self._domain == GR_MESSAGE_DOMAIN and type_ != "message": - self._domain = GR_STREAM_DOMAIN + if self._domain == Constants.GR_MESSAGE_DOMAIN and type_ != "message": + self._domain = Constants.GR_STREAM_DOMAIN self._key = '0' # Is rectified in rewrite() def resolve_virtual_source(self): - if self.get_parent().is_virtual_sink(): + if self.parent.is_virtual_sink(): return _get_source_from_virtual_sink_port(self) - if self.get_parent().is_virtual_source(): + if self.parent.is_virtual_source(): return _get_source_from_virtual_source_port(self) def resolve_empty_type(self): @@ -232,9 +236,9 @@ class Port(Element): Returns: the vector length or 1 """ - vlen = self.get_parent().resolve_dependencies(self._vlen) + vlen = self.parent.resolve_dependencies(self._vlen) try: - return int(self.get_parent().get_parent().evaluate(vlen)) + return int(self.parent.parent.evaluate(vlen)) except: return 1 @@ -250,9 +254,9 @@ class Port(Element): if self._nports == '': return '' - nports = self.get_parent().resolve_dependencies(self._nports) + nports = self.parent.resolve_dependencies(self._nports) try: - return max(1, int(self.get_parent().get_parent().evaluate(nports))) + return max(1, int(self.parent.parent.evaluate(nports))) except: return 1 @@ -321,7 +325,7 @@ class Port(Element): n['key'] = '99999' if self._key.isdigit() else n['name'] # Clone - port = self.__class__(self.get_parent(), n, self._dir) + port = self.__class__(self.parent, n, self._dir) self._clones.append(port) return port @@ -341,7 +345,7 @@ class Port(Element): def get_name(self): number = '' if self.get_type() == 'bus': - busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()) + busses = [a for a in self.parent.get_ports_gui() if a._dir == self._dir] number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())) return self._name + number @@ -357,7 +361,7 @@ class Port(Element): return self._dir == 'source' def get_type(self): - return self.get_parent().resolve_dependencies(self._type) + return self.parent_block.resolve_dependencies(self._type) def get_domain(self): return self._domain @@ -372,8 +376,8 @@ class Port(Element): Returns: a list of connection objects """ - connections = self.get_parent().get_parent().connections - connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) + connections = self.parent_flowgraph.connections + connections = [c for c in connections if c.source_port is self or c.sink_port is self] return connections def get_enabled_connections(self): @@ -383,22 +387,23 @@ class Port(Element): Returns: a list of connection objects """ - return filter(lambda c: c.get_enabled(), self.get_connections()) + return [c for c in self.get_connections() if c.get_enabled()] def get_associated_ports(self): if not self.get_type() == 'bus': return [self] else: + flowgraph = self.parent_flowgraph if self.is_source: - get_ports = self.get_parent().get_sources - bus_structure = self.get_parent().current_bus_structure['source'] + get_ports = flowgraph.get_sources + bus_structure = flowgraph.current_bus_structure['source'] else: - get_ports = self.get_parent().get_sinks - bus_structure = self.get_parent().current_bus_structure['sink'] + get_ports = flowgraph.get_sinks + bus_structure = flowgraph.current_bus_structure['sink'] ports = [i for i in get_ports() if not i.get_type() == 'bus'] if bus_structure: busses = [i for i in get_ports() if i.get_type() == 'bus'] bus_index = busses.index(self) - ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports) + ports = [a for a in ports if ports.index(a) in bus_structure[bus_index]] return ports diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py index 3723005576..a23c6d84ab 100644 --- a/grc/core/generator/FlowGraphProxy.py +++ b/grc/core/generator/FlowGraphProxy.py @@ -16,6 +16,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from __future__ import absolute_import +from six.moves import range + + class FlowGraphProxy(object): def __init__(self, fg): @@ -34,7 +38,7 @@ class FlowGraphProxy(object): Returns: a list of dicts with: type, label, vlen, size, optional """ - return filter(lambda p: p['type'] != "message", self.get_hier_block_io(direction)) + return [p for p in self.get_hier_block_io(direction) if p['type'] != "message"] def get_hier_block_message_io(self, direction): """ @@ -46,7 +50,7 @@ class FlowGraphProxy(object): Returns: a list of dicts with: type, label, vlen, size, optional """ - return filter(lambda p: p['type'] == "message", self.get_hier_block_io(direction)) + return [p for p in self.get_hier_block_io(direction) if p['type'] == "message"] def get_hier_block_io(self, direction): """ @@ -71,7 +75,7 @@ class FlowGraphProxy(object): } num_ports = pad.get_param('num_streams').get_evaluated() if num_ports > 1: - for i in xrange(num_ports): + for i in range(num_ports): clone = master.copy() clone['label'] += str(i) ports.append(clone) @@ -86,7 +90,7 @@ class FlowGraphProxy(object): Returns: a list of pad source blocks in this flow graph """ - pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) + pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_source'] return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) def get_pad_sinks(self): @@ -96,7 +100,7 @@ class FlowGraphProxy(object): Returns: a list of pad sink blocks in this flow graph """ - pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) + pads = [b for b in self.get_enabled_blocks() if b.get_key() == 'pad_sink'] return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) def get_pad_port_global_key(self, port): @@ -113,7 +117,7 @@ class FlowGraphProxy(object): # using the block param 'type' instead of the port domain here # to emphasize that hier block generation is domain agnostic is_message_pad = pad.get_param('type').get_evaluated() == "message" - if port.get_parent() == pad: + if port.parent == pad: if is_message_pad: key = pad.get_param('label').get_value() else: @@ -123,4 +127,4 @@ class FlowGraphProxy(object): # assuming we have either only sources or sinks if not is_message_pad: key_offset += len(pad.get_ports()) - return -1
\ No newline at end of file + return -1 diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index fb7a3afb99..9c7d07e76b 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -16,11 +16,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from __future__ import absolute_import + import codecs import os import tempfile +import operator +import collections from Cheetah.Template import Template +import six from .FlowGraphProxy import FlowGraphProxy from .. import ParseXML, Messages @@ -28,7 +33,7 @@ from ..Constants import ( TOP_BLOCK_FILE_MODE, BLOCK_FLAG_NEED_QT_GUI, HIER_BLOCK_FILE_MODE, BLOCK_DTD ) -from ..utils import expr_utils, odict +from ..utils import expr_utils DATA_DIR = os.path.dirname(__file__) FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') @@ -82,20 +87,18 @@ class TopBlockGenerator(object): filename = self._flow_graph.get_option('id') + '.py' self.file_path = os.path.join(dirname, filename) - def get_file_path(self): - return self.file_path - def write(self): """generate output and write it to files""" # Do throttle warning - throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks()) + throttling_blocks = [b for b in self._flow_graph.get_enabled_blocks() + if b.throtteling()] if not throttling_blocks and not self._generate_options.startswith('hb'): Messages.send_warning("This flow graph may not have flow control: " "no audio or RF hardware blocks found. " "Add a Misc->Throttle block to your flow " "graph to avoid CPU congestion.") if len(throttling_blocks) > 1: - keys = set(map(lambda b: b.get_key(), throttling_blocks)) + keys = set([b.get_key() for b in throttling_blocks]) if len(keys) > 1 and 'blocks_throttle' in keys: Messages.send_warning("This flow graph contains a throttle " "block and another rate limiting block, " @@ -142,15 +145,15 @@ class TopBlockGenerator(object): return code blocks_all = expr_utils.sort_objects( - filter(lambda b: b.get_enabled() and not b.get_bypassed(), fg.blocks), - lambda b: b.get_id(), _get_block_sort_text + [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_all 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 = filter(lambda b: b not in (imports + parameters), blocks_all) + 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() @@ -164,15 +167,15 @@ class TopBlockGenerator(object): # Filter out virtual sink connections def cf(c): - return not (c.is_bus() or c.is_msg() or c.get_sink().get_parent().is_virtual_sink()) - connections = filter(cf, fg.get_enabled_connections()) + return not (c.is_bus() or c.is_msg() or c.sink_block.is_virtual_sink()) + connections = [con for con in fg.get_enabled_connections() if cf(con)] # Get the virtual blocks and resolve their connections - virtual = filter(lambda c: c.get_source().get_parent().is_virtual_source(), connections) + virtual = [c for c in connections if c.source_block.is_virtual_source()] for connection in virtual: - source = connection.get_source().resolve_virtual_source() - sink = connection.get_sink() - resolved = fg.get_parent().Connection(flow_graph=fg, porta=source, portb=sink) + source = connection.source.resolve_virtual_source() + sink = connection.sink_port + resolved = fg.parent.Connection(flow_graph=fg, porta=source, portb=sink) connections.append(resolved) # Remove the virtual connection connections.remove(connection) @@ -186,20 +189,20 @@ class TopBlockGenerator(object): for block in bypassed_blocks: # Get the upstream connection (off of the sink ports) # Use *connections* not get_connections() - source_connection = filter(lambda c: c.get_sink() == block.get_sinks()[0], connections) + source_connection = [c for c in connections if c.sink_port == block.get_sinks()[0]] # The source connection should never have more than one element. assert (len(source_connection) == 1) # Get the source of the connection. - source_port = source_connection[0].get_source() + source_port = source_connection[0].source_port # Loop through all the downstream connections - for sink in filter(lambda c: c.get_source() == block.get_sources()[0], connections): + for sink in (c for c in connections if c.source_port == block.get_sources()[0]): if not sink.get_enabled(): # Ignore disabled connections continue - sink_port = sink.get_sink() - connection = fg.get_parent().Connection(flow_graph=fg, porta=source_port, portb=sink_port) + sink_port = sink.sink_port + connection = fg.parent.Connection(flow_graph=fg, porta=source_port, portb=sink_port) connections.append(connection) # Remove this sink connection connections.remove(sink) @@ -208,12 +211,12 @@ class TopBlockGenerator(object): # List of connections where each endpoint is enabled (sorted by domains, block names) connections.sort(key=lambda c: ( - c.get_source().get_domain(), c.get_sink().get_domain(), - c.get_source().get_parent().get_id(), c.get_sink().get_parent().get_id() + c.source_port.get_domain(), c.sink_port.get_domain(), + c.source_block.get_id(), c.sink_block.get_id() )) - connection_templates = fg.get_parent().connection_templates - msgs = filter(lambda c: c.is_msg(), fg.get_enabled_connections()) + connection_templates = fg.parent.connection_templates + msgs = [c for c in fg.get_enabled_connections() if c.is_msg()] # List of variable names var_ids = [var.get_id() for var in parameters + variables] @@ -264,7 +267,7 @@ 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.get_parent() + platform = flow_graph.parent hier_block_lib_dir = platform.config.hier_block_lib_dir if not os.path.exists(hier_block_lib_dir): @@ -272,18 +275,15 @@ class HierBlockGenerator(TopBlockGenerator): self._mode = HIER_BLOCK_FILE_MODE self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id') + '.py') - self._file_path_xml = self.file_path + '.xml' - - def get_file_path_xml(self): - return self._file_path_xml + self.file_path_xml = self.file_path + '.xml' def write(self): """generate output and write it to files""" TopBlockGenerator.write(self) - ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.get_file_path_xml()) - ParseXML.validate_dtd(self.get_file_path_xml(), BLOCK_DTD) + ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.file_path_xml) + ParseXML.validate_dtd(self.file_path_xml, BLOCK_DTD) try: - os.chmod(self.get_file_path_xml(), self._mode) + os.chmod(self.file_path_xml, self._mode) except: pass @@ -299,12 +299,12 @@ class HierBlockGenerator(TopBlockGenerator): parameters = self._flow_graph.get_parameters() def var_or_value(name): - if name in map(lambda p: p.get_id(), parameters): - return "$"+name + if name in (p.get_id() for p in parameters): + return "$" + name return name # Build the nested data - block_n = odict() + block_n = collections.OrderedDict() block_n['name'] = self._flow_graph.get_option('title') or \ self._flow_graph.get_option('id').replace('_', ' ').title() block_n['key'] = block_key @@ -329,7 +329,7 @@ class HierBlockGenerator(TopBlockGenerator): # Parameters block_n['param'] = list() for param in parameters: - param_n = odict() + param_n = collections.OrderedDict() param_n['name'] = param.get_param('label').get_value() or param.get_id() param_n['key'] = param.get_id() param_n['value'] = param.get_param('value').get_value() @@ -346,7 +346,7 @@ class HierBlockGenerator(TopBlockGenerator): for direction in ('sink', 'source'): block_n[direction] = list() for port in self._flow_graph.get_hier_block_io(direction): - port_n = odict() + port_n = collections.OrderedDict() port_n['name'] = port['label'] port_n['type'] = port['type'] if port['type'] != "message": @@ -379,14 +379,18 @@ class QtHierBlockGenerator(HierBlockGenerator): def _build_block_n_from_flow_graph_io(self): n = HierBlockGenerator._build_block_n_from_flow_graph_io(self) - block_n = n['block'] + block_n = collections.OrderedDict() + + # insert flags after category + for key, value in six.iteritems(n['block']): + block_n[key] = value + if key == 'category': + block_n['flags'] = BLOCK_FLAG_NEED_QT_GUI if not block_n['name'].upper().startswith('QT GUI'): block_n['name'] = 'QT GUI ' + block_n['name'] - block_n.insert_after('category', 'flags', BLOCK_FLAG_NEED_QT_GUI) - - gui_hint_param = odict() + gui_hint_param = collections.OrderedDict() gui_hint_param['name'] = 'GUI Hint' gui_hint_param['key'] = 'gui_hint' gui_hint_param['value'] = '' @@ -398,4 +402,5 @@ class QtHierBlockGenerator(HierBlockGenerator): "\n#set $win = 'self.%s' % $id" "\n${gui_hint()($win)}" ) - return n + + return {'block': block_n} diff --git a/grc/core/generator/__init__.py b/grc/core/generator/__init__.py index f44b94a85d..98f410c8d4 100644 --- a/grc/core/generator/__init__.py +++ b/grc/core/generator/__init__.py @@ -15,4 +15,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -from Generator import Generator +from __future__ import absolute_import +from .Generator import Generator diff --git a/grc/core/generator/flow_graph.tmpl b/grc/core/generator/flow_graph.tmpl index 436e3bbf0d..5eef3d2042 100644 --- a/grc/core/generator/flow_graph.tmpl +++ b/grc/core/generator/flow_graph.tmpl @@ -205,7 +205,7 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) $DIVIDER #end if #for $msg in $msgs - $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) + $(msg.source_block.get_id())_msgq_out = $(msg.sink_block.get_id())_msgq_in = gr.msg_queue(2) #end for ######################################################## ##Create Blocks @@ -241,11 +241,11 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) ## However, port names for IO pads should be self. ######################################################## #def make_port_sig($port) - #if $port.get_parent().get_key() in ('pad_source', 'pad_sink') + #if $port.parent.get_key() in ('pad_source', 'pad_sink') #set block = 'self' #set key = $flow_graph.get_pad_port_global_key($port) #else - #set block = 'self.' + $port.get_parent().get_id() + #set block = 'self.' + $port.parent.get_id() #set key = $port.get_key() #end if #if not $key.isdigit() @@ -260,8 +260,8 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) $DIVIDER #end if #for $con in $connections - #set global $source = $con.get_source() - #set global $sink = $con.get_sink() + #set global $source = $con.source_port + #set global $sink = $con.sink_port #include source=$connection_templates[($source.get_domain(), $sink.get_domain())] #end for diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py index 6b23da2723..66393fdc1b 100644 --- a/grc/core/utils/__init__.py +++ b/grc/core/utils/__init__.py @@ -15,8 +15,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import expr_utils -import epy_block_io -import extract_docs +from __future__ import absolute_import + +from . import expr_utils +from . import epy_block_io +from . import extract_docs -from odict import odict diff --git a/grc/core/utils/complexity.py b/grc/core/utils/complexity.py index baa8040db4..d72db9b3dd 100644 --- a/grc/core/utils/complexity.py +++ b/grc/core/utils/complexity.py @@ -8,8 +8,8 @@ def calculate_flowgraph_complexity(flowgraph): continue # Don't worry about optional sinks? - sink_list = filter(lambda c: not c.get_optional(), block.get_sinks()) - source_list = filter(lambda c: not c.get_optional(), block.get_sources()) + sink_list = [c for c in block.get_sinks() if not c.get_optional()] + source_list = [c for c in block.get_sources() if not c.get_optional()] sinks = float(len(sink_list)) sources = float(len(source_list)) base = max(min(sinks, sources), 1) @@ -22,14 +22,15 @@ def calculate_flowgraph_complexity(flowgraph): multi = 1 # Connection ratio multiplier - sink_multi = max(float(sum(map(lambda c: len(c.get_connections()), sink_list)) / max(sinks, 1.0)), 1.0) - source_multi = max(float(sum(map(lambda c: len(c.get_connections()), source_list)) / max(sources, 1.0)), 1.0) - dbal = dbal + (base * multi * sink_multi * source_multi) + sink_multi = max(float(sum(len(c.get_connections()) for c in sink_list) / max(sinks, 1.0)), 1.0) + source_multi = max(float(sum(len(c.get_connections()) for c in source_list) / max(sources, 1.0)), 1.0) + dbal += base * multi * sink_multi * source_multi blocks = float(len(flowgraph.blocks)) connections = float(len(flowgraph.connections)) elements = blocks + connections - disabled_connections = len(filter(lambda c: not c.get_enabled(), flowgraph.connections)) + disabled_connections = sum(not c.get_enabled() for c in flowgraph.connections) + variables = elements - blocks - connections enabled = float(len(flowgraph.get_enabled_blocks())) diff --git a/grc/core/utils/epy_block_io.py b/grc/core/utils/epy_block_io.py index 76b50051db..7a2006a833 100644 --- a/grc/core/utils/epy_block_io.py +++ b/grc/core/utils/epy_block_io.py @@ -1,7 +1,12 @@ +from __future__ import absolute_import + import inspect import collections +import six +from six.moves import zip + TYPE_MAP = { 'complex64': 'complex', 'complex': 'complex', @@ -31,10 +36,10 @@ def _ports(sigs, msgs): def _find_block_class(source_code, cls): ns = {} try: - exec source_code in ns + exec(source_code, ns) except Exception as e: raise ValueError("Can't interpret source code: " + str(e)) - for var in ns.itervalues(): + for var in six.itervalues(ns): if inspect.isclass(var) and issubclass(var, cls): return var raise ValueError('No python block class found in code') @@ -52,7 +57,7 @@ def extract(cls): spec = inspect.getargspec(cls.__init__) init_args = spec.args[1:] - defaults = map(repr, spec.defaults or ()) + defaults = [repr(arg) for arg in (spec.defaults or ())] doc = cls.__doc__ or cls.__init__.__doc__ or '' cls_name = cls.__name__ diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py index 2059ceff9f..555bd709b1 100644 --- a/grc/core/utils/expr_utils.py +++ b/grc/core/utils/expr_utils.py @@ -17,7 +17,12 @@ 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 + import string + +import six + VAR_CHARS = string.letters + string.digits + '_' @@ -50,7 +55,7 @@ class graph(object): self._graph[src_node_key].remove(dest_node_key) def get_nodes(self): - return self._graph.keys() + return list(self._graph.keys()) def get_edges(self, node_key): return self._graph[node_key] @@ -85,7 +90,7 @@ def expr_split(expr, var_chars=VAR_CHARS): toks.append(char) tok = '' toks.append(tok) - return filter(lambda t: t, toks) + return [t for t in toks if t] def expr_replace(expr, replace_dict): @@ -101,7 +106,7 @@ def expr_replace(expr, replace_dict): """ expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.') for i, es in enumerate(expr_splits): - if es in replace_dict.keys(): + if es in list(replace_dict.keys()): expr_splits[i] = replace_dict[es] return ''.join(expr_splits) @@ -118,7 +123,7 @@ def get_variable_dependencies(expr, vars): a subset of vars used in the expression """ expr_toks = expr_split(expr) - return set(var for var in vars if var in expr_toks) + return set(v for v in vars if v in expr_toks) def get_graph(exprs): @@ -131,12 +136,12 @@ def get_graph(exprs): Returns: a graph of variable deps """ - vars = exprs.keys() + vars = list(exprs.keys()) # Get dependencies for each expression, load into graph var_graph = graph() for var in vars: var_graph.add_node(var) - for var, expr in exprs.iteritems(): + for var, expr in six.iteritems(exprs): for dep in get_variable_dependencies(expr, vars): if dep != var: var_graph.add_edge(dep, var) @@ -159,7 +164,7 @@ def sort_variables(exprs): # Determine dependency order while var_graph.get_nodes(): # Get a list of nodes with no edges - indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) + indep_vars = [var for var in var_graph.get_nodes() if not var_graph.get_edges(var)] if not indep_vars: raise Exception('circular dependency caught in sort_variables') # Add the indep vars to the end of the list @@ -189,3 +194,4 @@ def sort_objects(objects, get_id, get_expr): sorted_ids = sort_variables(id2expr) # Return list of sorted objects return [id2obj[id] for id in sorted_ids] + diff --git a/grc/core/utils/extract_docs.py b/grc/core/utils/extract_docs.py index a6e0bc971e..cff8a81099 100644 --- a/grc/core/utils/extract_docs.py +++ b/grc/core/utils/extract_docs.py @@ -17,15 +17,19 @@ 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 + import sys import re import subprocess import threading import json -import Queue import random import itertools +import six +from six.moves import queue, filter, range + ############################################################################### # The docstring extraction @@ -124,7 +128,7 @@ class SubprocessLoader(object): self.callback_query_result = callback_query_result self.callback_finished = callback_finished or (lambda: None) - self._queue = Queue.Queue() + self._queue = queue.Queue() self._thread = None self._worker = None self._shutdown = threading.Event() @@ -157,14 +161,14 @@ class SubprocessLoader(object): cmd, args = self._last_cmd if cmd == 'query': msg += " (crashed while loading {0!r})".format(args[0]) - print >> sys.stderr, msg + print(msg, file=sys.stderr) continue # restart else: break # normal termination, return finally: self._worker.terminate() else: - print >> sys.stderr, "Warning: docstring loader crashed too often" + print("Warning: docstring loader crashed too often", file=sys.stderr) self._thread = None self._worker = None self.callback_finished() @@ -203,9 +207,9 @@ class SubprocessLoader(object): key, docs = args self.callback_query_result(key, docs) elif cmd == 'error': - print args + print(args) else: - print >> sys.stderr, "Unknown response:", cmd, args + print("Unknown response:", cmd, args, file=sys.stderr) def query(self, key, imports=None, make=None): """ Request docstring extraction for a certain key """ @@ -270,12 +274,12 @@ if __name__ == '__worker__': elif __name__ == '__main__': def callback(key, docs): - print key - for match, doc in docs.iteritems(): - print '-->', match - print doc.strip() - print - print + print(key) + for match, doc in six.iteritems(docs): + print('-->', match) + print(doc.strip()) + print() + print() r = SubprocessLoader(callback) diff --git a/grc/core/utils/odict.py b/grc/core/utils/odict.py deleted file mode 100644 index 85927e869f..0000000000 --- a/grc/core/utils/odict.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -Copyright 2008-2015 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 UserDict import DictMixin - - -class odict(DictMixin): - - def __init__(self, d={}): - self._keys = list(d.keys()) - self._data = dict(d.copy()) - - def __setitem__(self, key, value): - if key not in self._data: - self._keys.append(key) - self._data[key] = value - - def __getitem__(self, key): - return self._data[key] - - def __delitem__(self, key): - del self._data[key] - self._keys.remove(key) - - def keys(self): - return list(self._keys) - - def copy(self): - copy_dict = odict() - copy_dict._data = self._data.copy() - copy_dict._keys = list(self._keys) - return copy_dict - - def insert_after(self, pos_key, key, val): - """ - Insert the new key, value entry after the entry given by the position key. - If the positional key is None, insert at the end. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) - if key in self._keys: - raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) - self._keys.insert(index+1, key) - self._data[key] = val - - def insert_before(self, pos_key, key, val): - """ - Insert the new key, value entry before the entry given by the position key. - If the positional key is None, insert at the begining. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is not None) and self._keys.index(pos_key) or 0 - if key in self._keys: - raise KeyError('Cannot insert, key "{}" already exists'.format(str(key))) - self._keys.insert(index, key) - self._data[key] = val - - def find(self, key): - """ - Get the value for this key if exists. - - Args: - key: the key to search for - - Returns: - the value or None - """ - if key in self: - return self[key] - return None - - def findall(self, key): - """ - Get a list of values for this key. - - Args: - key: the key to search for - - Returns: - a list of values or empty list - """ - obj = self.find(key) - if obj is None: - obj = list() - if isinstance(obj, list): - return obj - return [obj] - - def clear(self): - self._data.clear() - del self._keys[:]
\ No newline at end of file |