Source code for brian2.synapses.parse_synaptic_generator_syntax

from brian2 import *
from brian2.parsing.rendering import NodeRenderer
import ast

__all__ = ['parse_synapse_generator']


def _cname(obj):
    return obj.__class__.__name__


[docs]def handle_range(*args, **kwds): ''' Checks the arguments/keywords for the range iterator Should have 1-3 positional arguments. Returns a dict with keys low, high, step. Default values are low=0, step=1. ''' if len(args) == 0 or len(args) > 3: raise SyntaxError("Range iterator takes 1-3 positional arguments.") if len(kwds): raise SyntaxError("Range iterator doesn't accept any keyword " "arguments.") if len(args) == 1: high = args[0] low = '0' step = '1' elif len(args) == 2: low, high = args step = '1' else: low, high, step = args return {'low': low, 'high': high, 'step': step}
[docs]def handle_sample(*args, **kwds): ''' Checks the arguments/keywords for the sample iterator Should have 1-3 positional arguments and 1 keyword argument (either p or size). Returns a dict with keys ``low, high, step, sample_size, p, size``. Default values are ``low=0``, ``step=1`. Sample size will be either ``'random'`` or ``'fixed'``. In the first case, ``p`` will have a value and size will be ``None`` (and vice versa for the second case). ''' if len(args) == 0 or len(args) > 3: raise SyntaxError("Sample iterator takes 1-3 positional arguments.") if len(kwds) != 1 or ('p' not in kwds and 'size' not in kwds): raise SyntaxError("Sample iterator accepts one keyword argument, " "either 'p' or 'size'.") if len(args) == 1: high = args[0] low = '0' step = '1' elif len(args) == 2: low, high = args step = '1' else: low, high, step = args if 'p' in kwds: sample_size = 'random' p = kwds['p'] size = None else: sample_size = 'fixed' size = kwds['size'] p = None return {'low': low, 'high': high, 'step': step, 'p': p, 'size': size, 'sample_size': sample_size}
iterator_function_handlers = { 'range': handle_range, 'sample': handle_sample, }
[docs]def parse_synapse_generator(expr): ''' Returns a parsed form of a synapse generator expression. The general form is: ``element for iteration_variable in iterator_func(...)`` or ``element for iteration_variable in iterator_func(...) if if_expression`` Returns a dictionary with keys: ``original_expression`` The original expression as a string. ``element`` As above, a string expression. ``iteration_variable`` A variable name, as above. ``iterator_func`` String. Either ``range`` or ``sample``. ``if_expression`` String expression or ``None``. ``iterator_kwds`` Dictionary of key/value pairs representing the keywords. See `handle_range` and `handle_sample`. ''' nr = NodeRenderer(use_vectorisation_idx=False) parse_error = ("Error parsing expression '%s'. Expression must have " "generator syntax, for example 'k for k in range(i-10, " "i+10)'." % expr) try: node = ast.parse('[%s]' % expr, mode='eval').body except Exception as e: raise SyntaxError(parse_error + " Error encountered was %s" % e) if _cname(node) != 'ListComp': raise SyntaxError(parse_error + " Expression is not a generator " "expression.") element = node.elt if len(node.generators) != 1: raise SyntaxError(parse_error + " Generator expression must involve " "only one iterator.") generator = node.generators[0] target = generator.target if _cname(target) != 'Name': raise SyntaxError(parse_error + " Generator must iterate over a single " "variable (not tuple, etc.).") iteration_variable = target.id iterator = generator.iter if _cname(iterator) != 'Call' or _cname(iterator.func) != 'Name': raise SyntaxError(parse_error + " Iterator expression must be one of " "the supported functions: " + str(iterator_function_handlers.keys())) iterator_funcname = iterator.func.id if iterator_funcname not in iterator_function_handlers: raise SyntaxError(parse_error + " Iterator expression must be one of " "the supported functions: " + str(iterator_function_handlers.keys())) if (getattr(iterator, 'starargs', None) is not None or getattr(iterator, 'kwargs', None) is not None): raise SyntaxError(parse_error + " Star arguments not supported.") args = [] for argnode in iterator.args: args.append(nr.render_node(argnode)) keywords = {} for kwdnode in iterator.keywords: keywords[kwdnode.arg] = nr.render_node(kwdnode.value) try: iterator_handler = iterator_function_handlers[iterator_funcname] iterator_kwds = iterator_handler(*args,**keywords) except SyntaxError as exc: raise SyntaxError(parse_error + " " + exc.msg) if len(generator.ifs) == 0: condition = ast.parse('True', mode='eval').body elif len(generator.ifs) > 1: raise SyntaxError(parse_error + " Generator must have at most one if " "statement.") else: condition = generator.ifs[0] parsed = { 'original_expression': expr, 'element': nr.render_node(element), 'iteration_variable': iteration_variable, 'iterator_func': iterator_funcname, 'iterator_kwds': iterator_kwds, 'if_expression': nr.render_node(condition), } return parsed
if __name__=='__main__': for parsed in [ parse_synapse_generator('k for k in sample(0, N, p=p) if abs(i-k)<10'), parse_synapse_generator('k for k in sample(0, N, size=5) if abs(i-k)<10'), parse_synapse_generator('k+1 for k in range(i-100, i+100, 2)'), ]: print 'PARSED:' for k, v in parsed.items(): print ' '+k+': '+str(v)