From 4fd7ed82106d0f7788d13cf59d21d6242e2ba473 Mon Sep 17 00:00:00 2001 From: Samuel Gaist <samuel.gaist@idiap.ch> Date: Mon, 6 May 2019 12:30:32 +0200 Subject: [PATCH] [algorithm] Pre-commit cleanup --- beat/backend/python/algorithm.py | 476 +++++++++++++++++-------------- 1 file changed, 254 insertions(+), 222 deletions(-) diff --git a/beat/backend/python/algorithm.py b/beat/backend/python/algorithm.py index 3905f82..3757208 100644 --- a/beat/backend/python/algorithm.py +++ b/beat/backend/python/algorithm.py @@ -76,14 +76,14 @@ class Storage(utils.CodeStorage): def __init__(self, prefix, name, language=None): - if name.count('/') != 2: + if name.count("/") != 2: raise RuntimeError("invalid algorithm name: `%s'" % name) - self.username, self.name, self.version = name.split('/') + self.username, self.name, self.version = name.split("/") self.fullname = name self.prefix = prefix - path = utils.hashed_or_simple(self.prefix, 'algorithms', name, suffix='.json') + path = utils.hashed_or_simple(self.prefix, "algorithms", name, suffix=".json") path = path[:-5] super(Storage, self).__init__(path, language) @@ -115,30 +115,28 @@ class Runner(object): try: class_ = getattr(module, obj_name) - except Exception as e: + except Exception: if exc is not None: type, value, traceback = sys.exc_info() six.reraise(exc, exc(value), traceback) else: raise # Just re-raise the user exception - self.obj = loader.run(class_, '__new__', exc) + self.obj = loader.run(class_, "__new__", exc) self.name = module.__name__ self.algorithm = algorithm self.exc = exc - self.ready = not hasattr(self.obj, 'setup') + self.ready = not hasattr(self.obj, "setup") has_api_v1 = self.algorithm.api_version == 1 - has_prepare = hasattr(self.obj, 'prepare') + has_prepare = hasattr(self.obj, "prepare") self.prepared = has_api_v1 or not has_prepare if has_api_v1 and has_prepare: logger.warning("Prepare is a reserved function name starting with API V2") - - def _check_parameters(self, parameters): """Checks input parameters from the user and fill defaults""" @@ -149,12 +147,11 @@ class Runner(object): # Checks the user is not trying to set an undeclared parameter if not user_keys.issubset(valid_keys): err_keys = user_keys - valid_keys - message = "parameters `%s' are not declared for algorithm `%s' - " \ - "valid parameters are `%s'" % ( - ','.join(err_keys), - self.name, - ','.join(valid_keys), - ) + message = ( + "parameters `%s' are not declared for algorithm `%s' - " + "valid parameters are `%s'" + % (",".join(err_keys), self.name, ",".join(valid_keys)) + ) exc = self.exc or KeyError raise exc(message) @@ -166,20 +163,21 @@ class Runner(object): try: value = self.algorithm.clean_parameter(key, parameters[key]) except Exception as e: - message = "parameter `%s' cannot be safely cast to the declared " \ - "type on algorithm `%s': %s" % (key, self.name, e) + message = ( + "parameter `%s' cannot be safely cast to the declared " + "type on algorithm `%s': %s" % (key, self.name, e) + ) exc = self.exc or ValueError raise exc(message) else: # User has not set a value, use the default - value = algo_parameters[key]['default'] + value = algo_parameters[key]["default"] # In the end, set the value on the dictionary to be returned retval[key] = value return retval - def setup(self, parameters): """Sets up the algorithm, only effective on the first call""" @@ -189,10 +187,9 @@ class Runner(object): completed_parameters = self._check_parameters(parameters) - self.ready = loader.run(self.obj, 'setup', self.exc, completed_parameters) + self.ready = loader.run(self.obj, "setup", self.exc, completed_parameters) return self.ready - def prepare(self, data_loaders): """Let the algorithm process the data on the non-principal channels""" @@ -211,25 +208,33 @@ class Runner(object): return True # The method is optional - if hasattr(self.obj, 'prepare'): + if hasattr(self.obj, "prepare"): if self.algorithm.type in [Algorithm.AUTONOMOUS, Algorithm.LOOP_USER]: - self.prepared = loader.run(self.obj, 'prepare', self.exc, data_loaders.secondaries()) + self.prepared = loader.run( + self.obj, "prepare", self.exc, data_loaders.secondaries() + ) else: - self.prepared = loader.run(self.obj, 'prepare', self.exc, data_loaders) + self.prepared = loader.run(self.obj, "prepare", self.exc, data_loaders) else: self.prepared = True return self.prepared - - def process(self, inputs=None, data_loaders=None, outputs=None, output=None, loop_channel=None): + def process( + self, + inputs=None, + data_loaders=None, + outputs=None, + output=None, + loop_channel=None, + ): """Runs through data""" exc = self.exc or RuntimeError def _check_argument(argument, name): if argument is None: - raise exc('Missing argument: %s' % name) + raise exc("Missing argument: %s" % name) # setup() must have run if not self.ready: @@ -241,42 +246,48 @@ class Runner(object): # Call the correct version of process() if self.algorithm.isAnalyzer: - _check_argument(output, 'output') + _check_argument(output, "output") outputs_to_use = output else: - _check_argument(outputs, 'outputs') + _check_argument(outputs, "outputs") outputs_to_use = outputs if self.algorithm.type == Algorithm.LEGACY: - _check_argument(inputs, 'inputs') - return loader.run(self.obj, 'process', self.exc, inputs, outputs_to_use) + _check_argument(inputs, "inputs") + return loader.run(self.obj, "process", self.exc, inputs, outputs_to_use) else: - _check_argument(data_loaders, 'data_loaders') + _check_argument(data_loaders, "data_loaders") if self.algorithm.type == Algorithm.SEQUENTIAL: - _check_argument(inputs, 'inputs') + _check_argument(inputs, "inputs") - return loader.run(self.obj, 'process', self.exc, inputs, data_loaders, - outputs_to_use) + return loader.run( + self.obj, "process", self.exc, inputs, data_loaders, outputs_to_use + ) elif self.algorithm.is_autonomous: - run_args = [self.obj, 'process', self.exc, data_loaders, outputs_to_use] + run_args = [self.obj, "process", self.exc, data_loaders, outputs_to_use] - has_loop_arg = utils.has_argument(self.obj.process, 'loop_channel') + has_loop_arg = utils.has_argument(self.obj.process, "loop_channel") if loop_channel is not None: if has_loop_arg: run_args.append(loop_channel) else: - raise exc("Algorithm '%s' is not a valid loop enabled algorithm" % self.name) + raise exc( + "Algorithm '%s' is not a valid loop enabled algorithm" + % self.name + ) elif has_loop_arg: - raise exc("Algorithm '%s' is a loop enabled algorithm but no loop_channel given" % self.name) + raise exc( + "Algorithm '%s' is a loop enabled algorithm but no loop_channel given" + % self.name + ) return loader.run(*run_args) else: - raise exc('Unknown algorithm type: %s' % self.algorithm.type) - + raise exc("Unknown algorithm type: %s" % self.algorithm.type) def validate(self, result): """Validates the given results""" @@ -284,7 +295,7 @@ class Runner(object): exc = self.exc or RuntimeError if self.algorithm.type != Algorithm.LOOP: - raise exc('Wrong algorithm type: %s' % self.algorithm.type) + raise exc("Wrong algorithm type: %s" % self.algorithm.type) # setup() must have run if not self.ready: @@ -294,14 +305,13 @@ class Runner(object): if not self.prepared: raise exc("Algorithm '%s' is not yet prepared" % self.name) - answer = loader.run(self.obj, 'validate', self.exc, result) + answer = loader.run(self.obj, "validate", self.exc, result) if not isinstance(answer, tuple): raise exc("Improper implementation: validate must return a tuple") return answer - def __getattr__(self, key): """Returns an attribute of the algorithm - only called at last resort """ @@ -385,12 +395,11 @@ class Algorithm(object): """ - LEGACY = 'legacy' - SEQUENTIAL = 'sequential' - AUTONOMOUS = 'autonomous' - LOOP = 'loop' - LOOP_USER = 'loop_user' - + LEGACY = "legacy" + SEQUENTIAL = "sequential" + AUTONOMOUS = "autonomous" + LOOP = "loop" + LOOP_USER = "loop_user" def __init__(self, prefix, name, dataformat_cache=None, library_cache=None): @@ -407,7 +416,6 @@ class Algorithm(object): self._load(name, dataformat_cache, library_cache) - def _load(self, data, dataformat_cache, library_cache): """Loads the algorithm""" @@ -416,67 +424,76 @@ class Algorithm(object): self.storage = Storage(self.prefix, data) json_path = self.storage.json.path if not self.storage.exists(): - self.errors.append('Algorithm declaration file not found: %s' % json_path) + self.errors.append("Algorithm declaration file not found: %s" % json_path) return - with open(json_path, 'rb') as f: - self.data = simplejson.loads(f.read().decode('utf-8')) + with open(json_path, "rb") as f: + self.data = simplejson.loads(f.read().decode("utf-8")) self.code_path = self.storage.code.path self.code = self.storage.code.load() - self.groups = self.data['groups'] + self.groups = self.data["groups"] # create maps for easy access to data - self.input_map = dict([(k,v['type']) for g in self.groups \ - for k,v in g['inputs'].items()]) - self.output_map = dict([(k,v['type']) for g in self.groups \ - for k,v in g.get('outputs', {}).items()]) - self.loop_map = dict([(k,v['type']) for g in self.groups \ - for k,v in g.get('loop', {}).items()]) + self.input_map = dict( + [(k, v["type"]) for g in self.groups for k, v in g["inputs"].items()] + ) + self.output_map = dict( + [ + (k, v["type"]) + for g in self.groups + for k, v in g.get("outputs", {}).items() + ] + ) + self.loop_map = dict( + [(k, v["type"]) for g in self.groups for k, v in g.get("loop", {}).items()] + ) self._load_dataformats(dataformat_cache) self._convert_parameter_types() self._load_libraries(library_cache) - def _load_dataformats(self, dataformat_cache): """Makes sure we can load all requested formats """ for group in self.groups: - for name, input in group['inputs'].items(): - if input['type'] in self.dataformats: continue + for name, input in group["inputs"].items(): + if input["type"] in self.dataformats: + continue - if dataformat_cache and input['type'] in dataformat_cache: #reuse - thisformat = dataformat_cache[input['type']] + if dataformat_cache and input["type"] in dataformat_cache: # reuse + thisformat = dataformat_cache[input["type"]] else: # load it - thisformat = dataformat.DataFormat(self.prefix, input['type']) + thisformat = dataformat.DataFormat(self.prefix, input["type"]) if dataformat_cache is not None: # update it - dataformat_cache[input['type']] = thisformat + dataformat_cache[input["type"]] = thisformat - self.dataformats[input['type']] = thisformat + self.dataformats[input["type"]] = thisformat - if 'outputs' not in group: continue + if "outputs" not in group: + continue - for name, output in group['outputs'].items(): - if output['type'] in self.dataformats: continue + for name, output in group["outputs"].items(): + if output["type"] in self.dataformats: + continue - if dataformat_cache and output['type'] in dataformat_cache: # reuse - thisformat = dataformat_cache[output['type']] + if dataformat_cache and output["type"] in dataformat_cache: # reuse + thisformat = dataformat_cache[output["type"]] else: # load it - thisformat = dataformat.DataFormat(self.prefix, output['type']) - if dataformat_cache is not None: #update it - dataformat_cache[output['type']] = thisformat + thisformat = dataformat.DataFormat(self.prefix, output["type"]) + if dataformat_cache is not None: # update it + dataformat_cache[output["type"]] = thisformat - self.dataformats[output['type']] = thisformat + self.dataformats[output["type"]] = thisformat - if 'loop' not in group: + if "loop" not in group: continue - for name, entry in group['loop'].items(): - entry_format = entry['type'] + for name, entry in group["loop"].items(): + entry_format = entry["type"] if entry_format in self.dataformats: continue @@ -489,24 +506,23 @@ class Algorithm(object): self.dataformats[entry_format] = thisformat - if self.results: for name, result in self.results.items(): - if result['type'].find('/') != -1: + if result["type"].find("/") != -1: - if result['type'] in self.dataformats: continue + if result["type"] in self.dataformats: + continue - if dataformat_cache and result['type'] in dataformat_cache: #reuse - thisformat = dataformat_cache[result['type']] + if dataformat_cache and result["type"] in dataformat_cache: # reuse + thisformat = dataformat_cache[result["type"]] else: - thisformat = dataformat.DataFormat(self.prefix, result['type']) - if dataformat_cache is not None: #update it - dataformat_cache[result['type']] = thisformat - - self.dataformats[result['type']] = thisformat + thisformat = dataformat.DataFormat(self.prefix, result["type"]) + if dataformat_cache is not None: # update it + dataformat_cache[result["type"]] = thisformat + self.dataformats[result["type"]] = thisformat def _convert_parameter_types(self): """Converts types to numpy equivalents, checks defaults, ranges and choices @@ -516,49 +532,75 @@ class Algorithm(object): try: return tp.type(value) except Exception as e: - self.errors.append("%s for parameter `%s' cannot be cast to type " \ - "`%s': %s" % (desc, name, tp.name, e)) + self.errors.append( + "%s for parameter `%s' cannot be cast to type " + "`%s': %s" % (desc, name, tp.name, e) + ) - if self.parameters is None: return + if self.parameters is None: + return for name, parameter in self.parameters.items(): - if parameter['type'] == 'string': - parameter['type'] = numpy.dtype('str') + if parameter["type"] == "string": + parameter["type"] = numpy.dtype("str") else: - parameter['type'] = numpy.dtype(parameter['type']) - - if 'range' in parameter: - parameter['range'][0] = _try_convert(name, parameter['type'], - parameter['range'][0], 'start of range') - parameter['range'][1] = _try_convert(name, parameter['type'], - parameter['range'][1], 'end of range') - if parameter['range'][0] >= parameter['range'][1]: - self.errors.append("range for parameter `%s' has a start greater " \ - "then the end value (%r >= %r)" % \ - (name, parameter['range'][0], parameter['range'][1])) - - if 'choice' in parameter: - for i, choice in enumerate(parameter['choice']): - parameter['choice'][i] = _try_convert(name, parameter['type'], - parameter['choice'][i], 'choice[%d]' % i) - - if 'default' in parameter: - parameter['default'] = _try_convert(name, parameter['type'], - parameter['default'], 'default') - - if 'range' in parameter: #check range - if parameter['default'] < parameter['range'][0] or \ - parameter['default'] > parameter['range'][1]: - self.errors.append("default for parameter `%s' (%r) is not " \ - "within parameter range [%r, %r]" % (name, parameter['default'], - parameter['range'][0], parameter['range'][1])) - - if 'choice' in parameter: #check choices - if parameter['default'] not in parameter['choice']: - self.errors.append("default for parameter `%s' (%r) is not " \ - "a valid choice `[%s]'" % (name, parameter['default'], - ', '.join(['%r' % k for k in parameter['choice']]))) + parameter["type"] = numpy.dtype(parameter["type"]) + + if "range" in parameter: + parameter["range"][0] = _try_convert( + name, parameter["type"], parameter["range"][0], "start of range" + ) + parameter["range"][1] = _try_convert( + name, parameter["type"], parameter["range"][1], "end of range" + ) + if parameter["range"][0] >= parameter["range"][1]: + self.errors.append( + "range for parameter `%s' has a start greater " + "then the end value (%r >= %r)" + % (name, parameter["range"][0], parameter["range"][1]) + ) + + if "choice" in parameter: + for i, choice in enumerate(parameter["choice"]): + parameter["choice"][i] = _try_convert( + name, + parameter["type"], + parameter["choice"][i], + "choice[%d]" % i, + ) + + if "default" in parameter: + parameter["default"] = _try_convert( + name, parameter["type"], parameter["default"], "default" + ) + + if "range" in parameter: # check range + if ( + parameter["default"] < parameter["range"][0] + or parameter["default"] > parameter["range"][1] + ): + self.errors.append( + "default for parameter `%s' (%r) is not " + "within parameter range [%r, %r]" + % ( + name, + parameter["default"], + parameter["range"][0], + parameter["range"][1], + ) + ) + if "choice" in parameter: # check choices + if parameter["default"] not in parameter["choice"]: + self.errors.append( + "default for parameter `%s' (%r) is not " + "a valid choice `[%s]'" + % ( + name, + parameter["default"], + ", ".join(["%r" % k for k in parameter["choice"]]), + ) + ) def _load_libraries(self, library_cache): @@ -568,16 +610,15 @@ class Algorithm(object): for name, value in self.uses.items(): - self.libraries[value] = library_cache.setdefault(value, - library.Library(self.prefix, value, library_cache)) - + self.libraries[value] = library_cache.setdefault( + value, library.Library(self.prefix, value, library_cache) + ) @property def name(self): """Returns the name of this object """ - return self._name or '__unnamed_algorithm__' - + return self._name or "__unnamed_algorithm__" @name.setter def name(self, value): @@ -587,30 +628,26 @@ class Algorithm(object): name (str): Name of this object """ - if self.data['language'] == 'unknown': + if self.data["language"] == "unknown": raise RuntimeError("algorithm has no programming language set") self._name = value - self.storage = Storage(self.prefix, value, self.data['language']) - + self.storage = Storage(self.prefix, value, self.data["language"]) @property def schema_version(self): """Returns the schema version""" - return self.data.get('schema_version', 1) - + return self.data.get("schema_version", 1) @property def language(self): """Returns the current language set for the executable code""" - return self.data['language'] - + return self.data["language"] @property def api_version(self): """Returns the API version""" - return self.data.get('api_version', 1) - + return self.data.get("api_version", 1) @property def type(self): @@ -618,24 +655,19 @@ class Algorithm(object): if self.api_version == 1: return Algorithm.LEGACY - return self.data.get('type', Algorithm.SEQUENTIAL) - + return self.data.get("type", Algorithm.SEQUENTIAL) @property def is_autonomous(self): """ Returns whether the algorithm is in the autonomous category""" - return self.type in [Algorithm.AUTONOMOUS, - Algorithm.LOOP_USER, - Algorithm.LOOP] - + return self.type in [Algorithm.AUTONOMOUS, Algorithm.LOOP_USER, Algorithm.LOOP] @language.setter def language(self, value): """Sets the current executable code programming language""" if self.storage: self.storage.language = value - self.data['language'] = value - + self.data["language"] = value def clean_parameter(self, parameter, value): """Checks if a given value against a declared parameter @@ -672,99 +704,100 @@ class Algorithm(object): if (self.parameters is None) or (parameter not in self.parameters): raise KeyError(parameter) - retval = self.parameters[parameter]['type'].type(value) - - if 'choice' in self.parameters[parameter] and \ - retval not in self.parameters[parameter]['choice']: - raise ValueError("value for `%s' (%r) must be one of `[%s]'" % \ - (parameter, value, ', '.join(['%r' % k for k in \ - self.parameters[parameter]['choice']]))) - - if 'range' in self.parameters[parameter] and \ - (retval < self.parameters[parameter]['range'][0] or \ - retval > self.parameters[parameter]['range'][1]): - raise ValueError("value for `%s' (%r) must be picked within " \ - "interval `[%r, %r]'" % (parameter, value, - self.parameters[parameter]['range'][0], - self.parameters[parameter]['range'][1])) + retval = self.parameters[parameter]["type"].type(value) + + if ( + "choice" in self.parameters[parameter] + and retval not in self.parameters[parameter]["choice"] + ): + raise ValueError( + "value for `%s' (%r) must be one of `[%s]'" + % ( + parameter, + value, + ", ".join(["%r" % k for k in self.parameters[parameter]["choice"]]), + ) + ) + + if "range" in self.parameters[parameter] and ( + retval < self.parameters[parameter]["range"][0] + or retval > self.parameters[parameter]["range"][1] + ): + raise ValueError( + "value for `%s' (%r) must be picked within " + "interval `[%r, %r]'" + % ( + parameter, + value, + self.parameters[parameter]["range"][0], + self.parameters[parameter]["range"][1], + ) + ) return retval - @property def valid(self): """A boolean that indicates if this algorithm is valid or not""" return not bool(self.errors) - @property def uses(self): - return self.data.get('uses') - + return self.data.get("uses") @uses.setter def uses(self, value): - self.data['uses'] = value + self.data["uses"] = value return value - @property def isAnalyzer(self): """Returns whether this algorithms is an analyzer""" - return (self.results is not None) - + return self.results is not None @property def results(self): """The results of this algorithm""" - return self.data.get('results') - + return self.data.get("results") @results.setter def results(self, value): - self.data['results'] = value + self.data["results"] = value return value - @property def parameters(self): """The parameters of this algorithm""" - return self.data.get('parameters') - + return self.data.get("parameters") @parameters.setter def parameters(self, value): - self.data['parameters'] = value + self.data["parameters"] = value return value - @property def splittable(self): """Whether this algorithm can be split between several processes""" - return self.data.get('splittable', False) - + return self.data.get("splittable", False) @splittable.setter def splittable(self, value): - self.data['splittable'] = value + self.data["splittable"] = value return value - @property def description(self): """The short description for this object""" - return self.data.get('description', None) - + return self.data.get("description", None) @description.setter def description(self, value): """Sets the short description for this object""" - self.data['description'] = value - + self.data["description"] = value @property def documentation(self): @@ -777,7 +810,6 @@ class Algorithm(object): return self.storage.doc.load() return None - @documentation.setter def documentation(self, value): """Sets the full-length description for this object""" @@ -785,12 +817,11 @@ class Algorithm(object): if not self._name: raise RuntimeError("algorithm has no name") - if hasattr(value, 'read'): + if hasattr(value, "read"): self.storage.doc.save(value.read()) else: self.storage.doc.save(value) - def hash(self): """Returns the hexadecimal hash for the current algorithm""" @@ -799,26 +830,26 @@ class Algorithm(object): return self.storage.hash() - def result_dataformat(self): """Generates, on-the-fly, the dataformat for the result readout""" if not self.results: - raise TypeError("algorithm `%s' is a block algorithm, not an analyzer" \ - % (self.name)) + raise TypeError( + "algorithm `%s' is a block algorithm, not an analyzer" % (self.name) + ) - format = dataformat.DataFormat(self.prefix, - dict([(k, v['type']) for k,v in self.results.items()])) + format = dataformat.DataFormat( + self.prefix, dict([(k, v["type"]) for k, v in self.results.items()]) + ) - format.name = 'analysis:' + self.name + format.name = "analysis:" + self.name return format - def uses_dict(self): """Returns the usage dictionary for all dependent modules""" - if self.data['language'] == 'unknown': + if self.data["language"] == "unknown": raise RuntimeError("algorithm has no programming language set") if not self._name: @@ -829,14 +860,13 @@ class Algorithm(object): if self.uses is not None: for name, value in self.uses.items(): retval[name] = dict( - path=self.libraries[value].storage.code.path, - uses=self.libraries[value].uses_dict(), - ) + path=self.libraries[value].storage.code.path, + uses=self.libraries[value].uses_dict(), + ) return retval - - def runner(self, klass='Algorithm', exc=None): + def runner(self, klass="Algorithm", exc=None): """Returns a runnable algorithm object. Parameters: @@ -857,7 +887,7 @@ class Algorithm(object): exc = exc or RuntimeError raise exc("algorithm has no name") - if self.data['language'] == 'unknown': + if self.data["language"] == "unknown": exc = exc or RuntimeError raise exc("algorithm has no programming language set") @@ -868,19 +898,24 @@ class Algorithm(object): # loads the module only once through the lifetime of the algorithm object try: - self.__module = getattr(self, 'module', - loader.load_module(self.name.replace(os.sep, '_'), - self.storage.code.path, self.uses_dict())) - except Exception as e: + self.__module = getattr( + self, + "module", + loader.load_module( + self.name.replace(os.sep, "_"), + self.storage.code.path, + self.uses_dict(), + ), + ) + except Exception: if exc is not None: type, value, traceback = sys.exc_info() six.reraise(exc, exc(value), traceback) else: - raise #just re-raise the user exception + raise # just re-raise the user exception return Runner(self.__module, klass, self, exc) - def json_dumps(self, indent=4): """Dumps the JSON declaration of this object in a string @@ -896,14 +931,11 @@ class Algorithm(object): """ - return simplejson.dumps(self.data, indent=indent, - cls=utils.NumpyJSONEncoder) - + return simplejson.dumps(self.data, indent=indent, cls=utils.NumpyJSONEncoder) def __str__(self): return self.json_dumps() - def write(self, storage=None): """Writes contents to prefix location @@ -915,17 +947,16 @@ class Algorithm(object): """ - if self.data['language'] == 'unknown': + if self.data["language"] == "unknown": raise RuntimeError("algorithm has no programming language set") if storage is None: if not self._name: raise RuntimeError("algorithm has no name") - storage = self.storage #overwrite + storage = self.storage # overwrite storage.save(str(self), self.code, self.description) - def export(self, prefix): """Recursively exports itself into another prefix @@ -955,8 +986,9 @@ class Algorithm(object): raise RuntimeError("algorithm is not valid:\n%s" % "\n".join(self.errors)) if prefix == self.prefix: - raise RuntimeError("Cannot export algorithm to the same prefix (" - "%s)" % prefix) + raise RuntimeError( + "Cannot export algorithm to the same prefix (" "%s)" % prefix + ) for k in self.libraries.values(): k.export(prefix) -- GitLab