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/utils/__init__.py | 8 +- grc/core/utils/_complexity.py | 50 ---------- grc/core/utils/backports/__init__.py | 25 +++++ grc/core/utils/backports/chainmap.py | 106 +++++++++++++++++++++ grc/core/utils/backports/shlex.py | 47 ++++++++++ grc/core/utils/descriptors/__init__.py | 26 ++++++ grc/core/utils/descriptors/_lazy.py | 39 ++++++++ grc/core/utils/descriptors/evaluated.py | 112 ++++++++++++++++++++++ grc/core/utils/expr_utils.py | 158 +++++++++++++++++++------------- grc/core/utils/extract_docs.py | 8 +- grc/core/utils/flow_graph_complexity.py | 50 ++++++++++ grc/core/utils/shlex.py | 47 ---------- 12 files changed, 507 insertions(+), 169 deletions(-) delete mode 100644 grc/core/utils/_complexity.py create mode 100644 grc/core/utils/backports/__init__.py create mode 100644 grc/core/utils/backports/chainmap.py create mode 100644 grc/core/utils/backports/shlex.py create mode 100644 grc/core/utils/descriptors/__init__.py create mode 100644 grc/core/utils/descriptors/_lazy.py create mode 100644 grc/core/utils/descriptors/evaluated.py create mode 100644 grc/core/utils/flow_graph_complexity.py delete mode 100644 grc/core/utils/shlex.py (limited to 'grc/core/utils') diff --git a/grc/core/utils/__init__.py b/grc/core/utils/__init__.py index d095179a10..2d12e280b5 100644 --- a/grc/core/utils/__init__.py +++ b/grc/core/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2008-2015 Free Software Foundation, Inc. +# Copyright 2016 Free Software Foundation, Inc. # This file is part of GNU Radio # # GNU Radio Companion is free software; you can redistribute it and/or @@ -17,8 +17,4 @@ from __future__ import absolute_import -from . import expr_utils -from . import epy_block_io -from . import extract_docs - -from ._complexity import calculate_flowgraph_complexity +from . import epy_block_io, expr_utils, extract_docs, flow_graph_complexity diff --git a/grc/core/utils/_complexity.py b/grc/core/utils/_complexity.py deleted file mode 100644 index c0f3ae9de4..0000000000 --- a/grc/core/utils/_complexity.py +++ /dev/null @@ -1,50 +0,0 @@ - -def calculate_flowgraph_complexity(flowgraph): - """ Determines the complexity of a flowgraph """ - dbal = 0 - for block in flowgraph.blocks: - # Skip options block - if block.key == 'options': - continue - - # Don't worry about optional sinks? - sink_list = [c for c in block.sinks if not c.get_optional()] - source_list = [c for c in block.sources if not c.get_optional()] - sinks = float(len(sink_list)) - sources = float(len(source_list)) - base = max(min(sinks, sources), 1) - - # Port ratio multiplier - if min(sinks, sources) > 0: - multi = sinks / sources - multi = (1 / multi) if multi > 1 else multi - else: - multi = 1 - - # Connection ratio multiplier - 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 = sum(not c.enabled for c in flowgraph.connections) - - variables = elements - blocks - connections - enabled = float(len(flowgraph.get_enabled_blocks())) - - # Disabled multiplier - if enabled > 0: - disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) - else: - disabled_multi = 1 - - # Connection multiplier (How many connections ) - if (connections - disabled_connections) > 0: - conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) - else: - conn_multi = 1 - - final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) - return final diff --git a/grc/core/utils/backports/__init__.py b/grc/core/utils/backports/__init__.py new file mode 100644 index 0000000000..a24ee3ae01 --- /dev/null +++ b/grc/core/utils/backports/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2016 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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 3, or (at your option) +# any later version. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +from __future__ import absolute_import + +try: + from collections import ChainMap +except ImportError: + from .chainmap import ChainMap diff --git a/grc/core/utils/backports/chainmap.py b/grc/core/utils/backports/chainmap.py new file mode 100644 index 0000000000..1f4f4a96fb --- /dev/null +++ b/grc/core/utils/backports/chainmap.py @@ -0,0 +1,106 @@ +# from https://hg.python.org/cpython/file/default/Lib/collections/__init__.py + +from collections import MutableMapping + + +class ChainMap(MutableMapping): + """ A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + be accessed or updated using the *maps* attribute. There is no other + state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + """ + + def __init__(self, *maps): + """Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + """ + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + """Create a ChainMap with a single dict created from the iterable.""" + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + """New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]""" + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self, m=None): # like Django's Context.push() + """New ChainMap with a new map followed by all previous maps. + If no map is provided, an empty dict is used. + """ + if m is None: + m = {} + return self.__class__(m, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + """New ChainMap from maps[1:].""" + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + """Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.""" + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + """Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].""" + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + """Clear maps[0], leaving maps[1:] intact.""" + self.maps[0].clear() diff --git a/grc/core/utils/backports/shlex.py b/grc/core/utils/backports/shlex.py new file mode 100644 index 0000000000..6b620fa396 --- /dev/null +++ b/grc/core/utils/backports/shlex.py @@ -0,0 +1,47 @@ +# Copyright 2016 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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 3, or (at your option) +# any later version. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +from __future__ import absolute_import + +import re +import shlex + +# back port from python3 + +_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search + + +def _shlex_quote(s): + """Return a shell-escaped version of the string *s*.""" + if not s: + return "''" + if _find_unsafe(s) is None: + return s + + # use single quotes, and put single quotes into double quotes + # the string $'b is then quoted as '$'"'"'b' + return "'" + s.replace("'", "'\"'\"'") + "'" + + +if not hasattr(shlex, 'quote'): + quote = _shlex_quote +else: + quote = shlex.quote + +split = shlex.split diff --git a/grc/core/utils/descriptors/__init__.py b/grc/core/utils/descriptors/__init__.py new file mode 100644 index 0000000000..80c5259230 --- /dev/null +++ b/grc/core/utils/descriptors/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2016 Free Software Foundation, Inc. +# This file is part of GNU Radio +# +# GNU Radio Companion is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# GNU Radio Companion is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from ._lazy import lazy_property, nop_write + +from .evaluated import ( + Evaluated, + EvaluatedEnum, + EvaluatedPInt, + EvaluatedFlag, + setup_names, +) diff --git a/grc/core/utils/descriptors/_lazy.py b/grc/core/utils/descriptors/_lazy.py new file mode 100644 index 0000000000..a0cb126932 --- /dev/null +++ b/grc/core/utils/descriptors/_lazy.py @@ -0,0 +1,39 @@ +# Copyright 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 functools + + +class lazy_property(object): + + def __init__(self, func): + self.func = func + functools.update_wrapper(self, func) + + def __get__(self, instance, owner): + if instance is None: + return self + value = self.func(instance) + setattr(instance, self.func.__name__, value) + return value + + +def nop_write(prop): + """Make this a property with a nop setter""" + def nop(self, value): + pass + return prop.setter(nop) diff --git a/grc/core/utils/descriptors/evaluated.py b/grc/core/utils/descriptors/evaluated.py new file mode 100644 index 0000000000..313cee5b96 --- /dev/null +++ b/grc/core/utils/descriptors/evaluated.py @@ -0,0 +1,112 @@ +# Copyright 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 + + +class Evaluated(object): + def __init__(self, expected_type, default, name=None): + self.expected_type = expected_type + self.default = default + + self.name = name or 'evaled_property_{}'.format(id(self)) + self.eval_function = self.default_eval_func + + @property + def name_raw(self): + return '_' + self.name + + def default_eval_func(self, instance): + raw = getattr(instance, self.name_raw) + try: + value = instance.parent_block.evaluate(raw) + except Exception as error: + if raw: + instance.add_error_message("Failed to eval '{}': {}".format(raw, error)) + return self.default + + if not isinstance(value, self.expected_type): + instance.add_error_message("Can not cast evaluated value '{}' to type {}" + "".format(value, self.expected_type)) + return self.default + # print(instance, self.name, raw, value) + return value + + def __call__(self, func): + self.name = func.__name__ + self.eval_function = func + return self + + def __get__(self, instance, owner): + if instance is None: + return self + attribs = instance.__dict__ + try: + value = attribs[self.name] + except KeyError: + value = attribs[self.name] = self.eval_function(instance) + return value + + def __set__(self, instance, value): + attribs = instance.__dict__ + value = value or self.default + if isinstance(value, str) and value.startswith('${') and value.endswith('}'): + attribs[self.name_raw] = value[2:-1].strip() + else: + attribs[self.name] = type(self.default)(value) + + def __delete__(self, instance): + attribs = instance.__dict__ + if self.name_raw in attribs: + attribs.pop(self.name, None) + + +class EvaluatedEnum(Evaluated): + def __init__(self, allowed_values, default=None, name=None): + self.allowed_values = allowed_values if isinstance(allowed_values, (list, tuple)) else \ + allowed_values.split() + default = default if default is not None else self.allowed_values[0] + super(EvaluatedEnum, self).__init__(str, default, name) + + def default_eval_func(self, instance): + value = super(EvaluatedEnum, self).default_eval_func(instance) + if value not in self.allowed_values: + instance.add_error_message("Value '{}' not in allowed values".format(value)) + return self.default + return value + + +class EvaluatedPInt(Evaluated): + def __init__(self, name=None): + super(EvaluatedPInt, self).__init__(int, 1, name) + + def default_eval_func(self, instance): + value = super(EvaluatedPInt, self).default_eval_func(instance) + if value < 1: + # todo: log + return self.default + return value + + +class EvaluatedFlag(Evaluated): + def __init__(self, name=None): + super(EvaluatedFlag, self).__init__((bool, int), False, name) + + +def setup_names(cls): + for name, attrib in cls.__dict__.items(): + if isinstance(attrib, Evaluated): + attrib.name = name + return cls diff --git a/grc/core/utils/expr_utils.py b/grc/core/utils/expr_utils.py index cc03e9cb1c..427585e93c 100644 --- a/grc/core/utils/expr_utils.py +++ b/grc/core/utils/expr_utils.py @@ -23,17 +23,105 @@ import string import six + +def expr_replace(expr, replace_dict): + """ + Search for vars in the expression and add the prepend. + + Args: + expr: an expression string + replace_dict: a dict of find:replace + + Returns: + a new expression with the prepend + """ + expr_splits = _expr_split(expr, var_chars=VAR_CHARS + '.') + for i, es in enumerate(expr_splits): + if es in list(replace_dict.keys()): + expr_splits[i] = replace_dict[es] + return ''.join(expr_splits) + + +def get_variable_dependencies(expr, vars): + """ + Return a set of variables used in this expression. + + Args: + expr: an expression string + vars: a list of variable names + + Returns: + a subset of vars used in the expression + """ + expr_toks = _expr_split(expr) + return set(v for v in vars if v in expr_toks) + + +def sort_objects(objects, get_id, get_expr): + """ + Sort a list of objects according to their expressions. + + Args: + objects: the list of objects to sort + get_id: the function to extract an id from the object + get_expr: the function to extract an expression from the object + + Returns: + a list of sorted objects + """ + id2obj = {get_id(obj): obj for obj in objects} + # Map obj id to expression code + id2expr = {get_id(obj): get_expr(obj) for obj in objects} + # Sort according to dependency + sorted_ids = _sort_variables(id2expr) + # Return list of sorted objects + return [id2obj[id] for id in sorted_ids] + + +import ast + + +def dependencies(expr, names=None): + node = ast.parse(expr, mode='eval') + used_ids = frozenset([n.id for n in ast.walk(node) if isinstance(n, ast.Name)]) + return used_ids & names if names else used_ids + + +def sort_objects2(objects, id_getter, expr_getter, check_circular=True): + known_ids = {id_getter(obj) for obj in objects} + + def dependent_ids(obj): + deps = dependencies(expr_getter(obj)) + return [id_ if id_ in deps else None for id_ in known_ids] + + objects = sorted(objects, key=dependent_ids) + + if check_circular: # walk var defines step by step + defined_ids = set() # variables defined so far + for obj in objects: + deps = dependencies(expr_getter(obj), known_ids) + if not defined_ids.issuperset(deps): # can't have an undefined dep + raise RuntimeError(obj, deps, defined_ids) + defined_ids.add(id_getter(obj)) # define this one + + return objects + + + + VAR_CHARS = string.ascii_letters + string.digits + '_' -class graph(object): +class _graph(object): """ Simple graph structure held in a dictionary. """ - def __init__(self): self._graph = dict() + def __init__(self): + self._graph = dict() - def __str__(self): return str(self._graph) + def __str__(self): + return str(self._graph) def add_node(self, node_key): if node_key in self._graph: @@ -61,7 +149,7 @@ class graph(object): return self._graph[node_key] -def expr_split(expr, var_chars=VAR_CHARS): +def _expr_split(expr, var_chars=VAR_CHARS): """ Split up an expression by non alphanumeric characters, including underscore. Leave strings in-tact. @@ -93,40 +181,7 @@ def expr_split(expr, var_chars=VAR_CHARS): return [t for t in toks if t] -def expr_replace(expr, replace_dict): - """ - Search for vars in the expression and add the prepend. - - Args: - expr: an expression string - replace_dict: a dict of find:replace - - Returns: - a new expression with the prepend - """ - expr_splits = expr_split(expr, var_chars=VAR_CHARS + '.') - for i, es in enumerate(expr_splits): - if es in list(replace_dict.keys()): - expr_splits[i] = replace_dict[es] - return ''.join(expr_splits) - - -def get_variable_dependencies(expr, vars): - """ - Return a set of variables used in this expression. - - Args: - expr: an expression string - vars: a list of variable names - - Returns: - a subset of vars used in the expression - """ - expr_toks = expr_split(expr) - return set(v for v in vars if v in expr_toks) - - -def get_graph(exprs): +def _get_graph(exprs): """ Get a graph representing the variable dependencies @@ -138,7 +193,7 @@ def get_graph(exprs): """ vars = list(exprs.keys()) # Get dependencies for each expression, load into graph - var_graph = graph() + var_graph = _graph() for var in vars: var_graph.add_node(var) for var, expr in six.iteritems(exprs): @@ -148,7 +203,7 @@ def get_graph(exprs): return var_graph -def sort_variables(exprs): +def _sort_variables(exprs): """ Get a list of variables in order of dependencies. @@ -159,7 +214,7 @@ def sort_variables(exprs): a list of variable names @throws Exception circular dependencies """ - var_graph = get_graph(exprs) + var_graph = _get_graph(exprs) sorted_vars = list() # Determine dependency order while var_graph.get_nodes(): @@ -173,24 +228,3 @@ def sort_variables(exprs): for var in indep_vars: var_graph.remove_node(var) return reversed(sorted_vars) - - -def sort_objects(objects, get_id, get_expr): - """ - Sort a list of objects according to their expressions. - - Args: - objects: the list of objects to sort - get_id: the function to extract an id from the object - get_expr: the function to extract an expression from the object - - Returns: - a list of sorted objects - """ - id2obj = dict([(get_id(obj), obj) for obj in objects]) - # Map obj id to expression code - id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) - # Sort according to dependency - 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 cff8a81099..7688f98de5 100644 --- a/grc/core/utils/extract_docs.py +++ b/grc/core/utils/extract_docs.py @@ -98,8 +98,7 @@ def docstring_from_make(key, imports, make): if '$' in blk_cls: raise ValueError('Not an identifier') ns = dict() - for _import in imports: - exec(_import.strip(), ns) + exec(imports.strip(), ns) blk = eval(blk_cls, ns) doc_strings = {key: blk.__doc__} @@ -166,7 +165,8 @@ class SubprocessLoader(object): else: break # normal termination, return finally: - self._worker.terminate() + if self._worker: + self._worker.terminate() else: print("Warning: docstring loader crashed too often", file=sys.stderr) self._thread = None @@ -277,7 +277,7 @@ elif __name__ == '__main__': print(key) for match, doc in six.iteritems(docs): print('-->', match) - print(doc.strip()) + print(str(doc).strip()) print() print() diff --git a/grc/core/utils/flow_graph_complexity.py b/grc/core/utils/flow_graph_complexity.py new file mode 100644 index 0000000000..d06f04ab5f --- /dev/null +++ b/grc/core/utils/flow_graph_complexity.py @@ -0,0 +1,50 @@ + +def calculate(flowgraph): + """ Determines the complexity of a flowgraph """ + dbal = 0 + for block in flowgraph.blocks: + # Skip options block + if block.key == 'options': + continue + + # Don't worry about optional sinks? + sink_list = [c for c in block.sinks if not c.optional] + source_list = [c for c in block.sources if not c.optional] + sinks = float(len(sink_list)) + sources = float(len(source_list)) + base = max(min(sinks, sources), 1) + + # Port ratio multiplier + if min(sinks, sources) > 0: + multi = sinks / sources + multi = (1 / multi) if multi > 1 else multi + else: + multi = 1 + + # Connection ratio multiplier + sink_multi = max(float(sum(len(c.connections()) for c in sink_list) / max(sinks, 1.0)), 1.0) + source_multi = max(float(sum(len(c.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 = sum(not c.enabled for c in flowgraph.connections) + + variables = elements - blocks - connections + enabled = float(len(flowgraph.get_enabled_blocks())) + + # Disabled multiplier + if enabled > 0: + disabled_multi = 1 / (max(1 - ((blocks - enabled) / max(blocks, 1)), 0.05)) + else: + disabled_multi = 1 + + # Connection multiplier (How many connections ) + if (connections - disabled_connections) > 0: + conn_multi = 1 / (max(1 - (disabled_connections / max(connections, 1)), 0.05)) + else: + conn_multi = 1 + + final = round(max((dbal - 1) * disabled_multi * conn_multi * connections, 0.0) / 1000000, 6) + return final diff --git a/grc/core/utils/shlex.py b/grc/core/utils/shlex.py deleted file mode 100644 index 6b620fa396..0000000000 --- a/grc/core/utils/shlex.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2016 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio 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 3, or (at your option) -# any later version. -# -# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. - -from __future__ import absolute_import - -import re -import shlex - -# back port from python3 - -_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - - -def _shlex_quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return "''" - if _find_unsafe(s) is None: - return s - - # use single quotes, and put single quotes into double quotes - # the string $'b is then quoted as '$'"'"'b' - return "'" + s.replace("'", "'\"'\"'") + "'" - - -if not hasattr(shlex, 'quote'): - quote = _shlex_quote -else: - quote = shlex.quote - -split = shlex.split -- cgit v1.2.3