From 7f7fa2f91467fdb2b11312be8562e7b51fdeb199 Mon Sep 17 00:00:00 2001
From: Sebastian Koslowski <sebastian.koslowski@gmail.com>
Date: Tue, 3 May 2016 17:13:08 +0200
Subject: grc: added yaml/mako support

Includes basic converter from XML/Cheetah to YAML/Mako based block format.
---
 grc/core/blocks/embedded_python.py | 242 +++++++++++++++++++++++++++++++++++++
 1 file changed, 242 insertions(+)
 create mode 100644 grc/core/blocks/embedded_python.py

(limited to 'grc/core/blocks/embedded_python.py')

diff --git a/grc/core/blocks/embedded_python.py b/grc/core/blocks/embedded_python.py
new file mode 100644
index 0000000000..0b5a7a21c5
--- /dev/null
+++ b/grc/core/blocks/embedded_python.py
@@ -0,0 +1,242 @@
+# Copyright 2015-16 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from __future__ import absolute_import
+
+from ast import literal_eval
+from textwrap import dedent
+
+from . import Block, register_build_in
+from ._templates import MakoTemplates
+
+from .. import utils
+from ..base import Element
+
+
+DEFAULT_CODE = '''\
+"""
+Embedded Python Blocks:
+
+Each time this file is saved, GRC will instantiate the first class it finds
+to get ports and parameters of your block. The arguments to __init__  will
+be the parameters. All of them are required to have default values!
+"""
+
+import numpy as np
+from gnuradio import gr
+
+
+class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
+    """Embedded Python Block example - a simple multiply const"""
+
+    def __init__(self, example_param=1.0):  # only default arguments here
+        """arguments to this function show up as parameters in GRC"""
+        gr.sync_block.__init__(
+            self,
+            name='Embedded Python Block',   # will show up in GRC
+            in_sig=[np.complex64],
+            out_sig=[np.complex64]
+        )
+        # if an attribute with the same name as a parameter is found,
+        # a callback is registered (properties work, too).
+        self.example_param = example_param
+
+    def work(self, input_items, output_items):
+        """example: multiply with constant"""
+        output_items[0][:] = input_items[0] * self.example_param
+        return len(output_items[0])
+'''
+
+DOC = """
+This block represents an arbitrary GNU Radio Python Block.
+
+Its source code can be accessed through the parameter 'Code' which opens your editor. \
+Each time you save changes in the editor, GRC will update the block. \
+This includes the number, names and defaults of the parameters, \
+the ports (stream and message) and the block name and documentation.
+
+Block Documentation:
+(will be replaced the docstring of your block class)
+"""
+
+
+@register_build_in
+class EPyBlock(Block):
+
+    key = 'epy_block'
+    label = 'Python Block'
+    documentation = {'': DOC}
+
+    parameters_data = [dict(
+        label='Code',
+        id='_source_code',
+        dtype='_multiline_python_external',
+        value=DEFAULT_CODE,
+        hide='part',
+    )]
+    inputs_data = []
+    outputs_data = []
+
+    def __init__(self, flow_graph, **kwargs):
+        super(EPyBlock, self).__init__(flow_graph, **kwargs)
+        self.states['_io_cache'] = ''
+
+        self._epy_source_hash = -1
+        self._epy_reload_error = None
+
+    def rewrite(self):
+        Element.rewrite(self)
+
+        param_src = self.params['_source_code']
+
+        src = param_src.get_value()
+        src_hash = hash((self.name, src))
+        if src_hash == self._epy_source_hash:
+            return
+
+        try:
+            blk_io = utils.epy_block_io.extract(src)
+
+        except Exception as e:
+            self._epy_reload_error = ValueError(str(e))
+            try:  # Load last working block io
+                blk_io_args = literal_eval(self.states['_io_cache'])
+                if len(blk_io_args) == 6:
+                    blk_io_args += ([],)  # add empty callbacks
+                blk_io = utils.epy_block_io.BlockIO(*blk_io_args)
+            except Exception:
+                return
+        else:
+            self._epy_reload_error = None  # Clear previous errors
+            self.states['_io_cache'] = repr(tuple(blk_io))
+
+        # print "Rewriting embedded python block {!r}".format(self.name)
+        self._epy_source_hash = src_hash
+
+        self.label = blk_io.name or blk_io.cls
+        self.documentation = {'': blk_io.doc}
+
+        self.templates['imports'] = 'import ' + self.name
+        self.templates['make'] = '{mod}.{cls}({args})'.format(
+            mod=self.name,
+            cls=blk_io.cls,
+            args=', '.join('{0}=${{ {0} }}'.format(key) for key, _ in blk_io.params))
+        self.templates['callbacks'] = [
+            '{0} = ${{ {0} }}'.format(attr) for attr in blk_io.callbacks
+            ]
+
+        self._update_params(blk_io.params)
+        self._update_ports('in', self.sinks, blk_io.sinks, 'sink')
+        self._update_ports('out', self.sources, blk_io.sources, 'source')
+
+        super(EPyBlock, self).rewrite()
+
+    def _update_params(self, params_in_src):
+        param_factory = self.parent_platform.make_param
+        params = {}
+        for param in list(self.params):
+            if hasattr(param, '__epy_param__'):
+                params[param.key] = param
+                del self.params[param.key]
+
+        for id_, value in params_in_src:
+            try:
+                param = params[id_]
+                if param.default == param.value:
+                    param.set_value(value)
+                param.default = str(value)
+            except KeyError:  # need to make a new param
+                param = param_factory(
+                    parent=self,  id=id_, dtype='raw', value=value,
+                    name=id_.replace('_', ' ').title(),
+                )
+                setattr(param, '__epy_param__', True)
+            self.params[id_] = param
+
+    def _update_ports(self, label, ports, port_specs, direction):
+        port_factory = self.parent_platform.make_port
+        ports_to_remove = list(ports)
+        iter_ports = iter(ports)
+        ports_new = []
+        port_current = next(iter_ports, None)
+        for key, port_type, vlen in port_specs:
+            reuse_port = (
+                port_current is not None and
+                port_current.dtype == port_type and
+                port_current.vlen == vlen and
+                (key.isdigit() or port_current.key == key)
+            )
+            if reuse_port:
+                ports_to_remove.remove(port_current)
+                port, port_current = port_current, next(iter_ports, None)
+            else:
+                n = dict(name=label + str(key), dtype=port_type, id=key)
+                if port_type == 'message':
+                    n['name'] = key
+                    n['optional'] = '1'
+                if vlen > 1:
+                    n['vlen'] = str(vlen)
+                port = port_factory(self, direction=direction, **n)
+            ports_new.append(port)
+        # replace old port list with new one
+        del ports[:]
+        ports.extend(ports_new)
+        # remove excess port connections
+        self.parent_flowgraph.disconnect(*ports_to_remove)
+
+    def validate(self):
+        super(EPyBlock, self).validate()
+        if self._epy_reload_error:
+            self.params['_source_code'].add_error_message(str(self._epy_reload_error))
+
+
+@register_build_in
+class EPyModule(Block):
+    key = 'epy_module'
+    label = 'Python Module'
+    documentation = {'': dedent("""
+        This block lets you embed a python module in your flowgraph.
+
+        Code you put in this module is accessible in other blocks using the ID of this
+        block. Example:
+
+        If you put
+
+            a = 2
+
+            def double(arg):
+                return 2 * arg
+
+        in a Python Module Block with the ID 'stuff' you can use code like
+
+            stuff.a  # evals to 2
+            stuff.double(3)  # evals to 6
+
+        to set parameters of other blocks in your flowgraph.
+    """)}
+
+    parameters_data = [dict(
+        label='Code',
+        id='source_code',
+        dtype='_multiline_python_external',
+        value='# this module will be imported in the into your flowgraph',
+        hide='part',
+    )]
+
+    templates = MakoTemplates(
+        imports='import ${ id }  # embedded python module',
+    )
-- 
cgit v1.2.3