diff --git a/src/amuse/__init__.py b/src/amuse/__init__.py index bdf377cf97..ffb7a81e82 100644 --- a/src/amuse/__init__.py +++ b/src/amuse/__init__.py @@ -25,39 +25,49 @@ or (directly from the terminal): > pydoc amuse.ic """ + import sys import os import numpy _AMUSE_ROOT = os.path.abspath(os.path.dirname(__file__)) + def numpy_fix(): try: - numpy.set_printoptions(legacy='1.13') + numpy.set_printoptions(legacy="1.13") except TypeError: pass - + + numpy_fix() -class NoConfig(object): + +class NoConfig: def __init__(self, message): - self._message=message + self._message = message + def __getattr__(self, attr): raise AttributeError(self._message) + try: from . import config -except Exception as ex: - message="Configuration not read in - or configuration invalid, exception:\n"+str(ex) - config=NoConfig(message) +except FileNotFoundError as ex: + message = ( + "Configuration not read in - or configuration invalid, exception:\n" + str(ex) + ) + config = NoConfig(message) # always report AMUSE reference information try: from amuse.support.literature import TrackLiteratureReferences + TrackLiteratureReferences.default() except: pass + def get_data(path): - return os.path.join(_AMUSE_ROOT, 'data', path) + return os.path.join(_AMUSE_ROOT, "data", path) diff --git a/src/amuse/support/core.py b/src/amuse/support/core.py index 6ffffaefa8..00c47bfc8e 100644 --- a/src/amuse/support/core.py +++ b/src/amuse/support/core.py @@ -1,33 +1,39 @@ """ """ -import types + +import types import collections.abc import re + def compare_version_strings(version1, version2): def normalize(v): - return [int(x if x.isdigit() else 0) for x in re.sub(r'(\.0+)*$','', v).split(".")] + return [ + int(x if x.isdigit() else 0) for x in re.sub(r"(\.0+)*$", "", v).split(".") + ] + version1 = normalize(version1) version2 = normalize(version2) - + return (version1 > version2) - (version1 < version2) -class late(object): + +class late: """ - An attribute that is set at first access. - + An attribute that is set at first access. + The value of the attribute will be determined from the *initializer* method. The name of the attribute is the same as the name of the *initializer* method. - - A late attribute is comparible with attributes set in the + + A late attribute is comparable with attributes set in the *__init__* method. Except the value of the late attribute is determined when first accessed and not when the class is instantiated. - + Typical use to define a managed attribute x: - + >>> class C(object): ... @late ... def x(self): @@ -39,61 +45,60 @@ class late(object): >>> c.x = "overridden" >>> print c.x overridden - + :argument initializer: function to determine the initial value of the property :returns: a descriptor to determine and set the value on first access - """ + """ + def __init__(self, initializer): self.initializer = initializer self.__doc__ = self.initializer.__doc__ - + def __get__(self, instance, owner): if instance is None: return self - + try: value = self.initializer(instance) except Exception as ex: raise AttributeError(ex) - - setattr(instance,self.initializer.__name__, value) - + + setattr(instance, self.initializer.__name__, value) + return value - - - -class print_out(object): + + +class print_out: """ - Efficient way to contruct large strings. - + Efficient way to construct large strings. + Strings are build up out of parts. Objects of this class store these parts while building the string. Only on request the parts are concatenated into a large string. - + Strings and numbers can be added to the print_out. For other objects str(object) is called before adding it to the print_out. - + >>> p = print_out() >>> p + "number of counts : " + 10 #doctest: +ELLIPSIS >>> print p.string number of counts : 10 - + All methods return the print_out instance, so that calls can be chained. """ - + def __init__(self): self.parts = [] self._indent = 0 self.number_of_characters_on_current_line = 0 - + def __add__(self, x): - """Add a new part to the print_out. - """ - + """Add a new part to the print_out.""" + if self.isstring(x): self.parts.append(x) self.number_of_characters_on_current_line += len(x) @@ -105,12 +110,12 @@ def __add__(self, x): else: part = str(x) self.parts.append(part) - self.number_of_characters_on_current_line += len(part) + self.number_of_characters_on_current_line += len(part) return self - + def n(self): """Start a new-line, if the current line is not-empty. - + >>> p = print_out() >>> for i in range(3): ... p.n() + i #doctest: +ELLIPSIS @@ -125,15 +130,15 @@ def n(self): """ if not self.parts: return self - if self.parts[-1] == '\n': + if self.parts[-1] == "\n": return self self.lf() return self - + def indent(self): """Increase the indent. The next and following lines will start indented. - + >>> p = print_out() >>> p + "01" #doctest: +ELLIPSIS @@ -148,10 +153,10 @@ def indent(self): """ self._indent += 1 return self - + def dedent(self): """Decrease the indent. The next line will start dedented. - + >>> p = print_out() >>> p + "01" #doctest: +ELLIPSIS @@ -166,17 +171,17 @@ def dedent(self): """ self._indent -= 1 return self - + def lf(self): """Start a new-line""" - self.parts.append('\n') + self.parts.append("\n") self.number_of_characters_on_current_line = 0 self.do_indent() return self - + def lf_noindent(self): """Start a new-line""" - self.parts.append('\n') + self.parts.append("\n") self.number_of_characters_on_current_line = 0 return self @@ -186,31 +191,30 @@ def do_indent(self): self.number_of_characters_on_current_line += len(self.indent_characters()) def indent_characters(self): - """ The indent characters, by default 2 spaces. - + """The indent characters, by default 2 spaces. + Override this method to change the indent characters. """ - return ' ' - + return " " + def __str__(self): - return ''.join(self.parts) - + return "".join(self.parts) + @property def string(self): - """String version of the print_out. - """ + """String version of the print_out.""" return str(self) - + def isstring(self, x): - return isinstance(x,bytes) - + return isinstance(x, bytes) + def isnumber(self, x): - return isinstance(x,int) or isinstance(x,float) - - -class OrderedDictionary(object): + return isinstance(x, int) or isinstance(x, float) + + +class OrderedDictionary: """A dictionary that keeps the keys in the dictionary in order. - + Ordered dictionaries are just like regular dictionaries but they remember the order that items were inserted. When iterating over an ordered dictionary, the values are returned in the order their keys were first added. @@ -222,80 +226,80 @@ class OrderedDictionary(object): >>> [x for x in d] [0, 1, 2] """ + def __init__(self): self.mapping = {} self.orderedKeys = [] - + def __setitem__(self, key, value): if key in self.mapping: self.mapping[key] = value return self.orderedKeys.append(key) self.mapping[key] = value - + def __getitem__(self, key): return self.mapping[key] - - def __contains__(self, key): + + def __contains__(self, key): return key in self.mapping - - def __iter__(self): + + def __iter__(self): return iter(self.values()) - - def __len__(self): + + def __len__(self): return len(self.orderedKeys) - + def __str__(self): - result = 'OrderedDictionary({' + result = "OrderedDictionary({" elements = [] for x in self.keys(): - elements.append(repr(x) + ':' + repr(self[x])) - result += ', '.join(elements) - result += '})' + elements.append(repr(x) + ":" + repr(self[x])) + result += ", ".join(elements) + result += "})" return result - + def __repr__(self): return str(self) - + def iterkeys(self): return iter(self.orderedKeys) - + def itervalues(self): for x in iter(self.orderedKeys): yield self.mapping[x] - + def iteritems(self): for x in self.orderedKeys: yield x, self.mapping[x] - + def keys(self): return list(self.orderedKeys) - + def pop(self, key): index = self.orderedKeys.index(key) del self.orderedKeys[index] return self.mapping.pop(key) - + def values(self): return [self.mapping[x] for x in self.orderedKeys] - + def items(self): - return [(x,self.mapping[x]) for x in self.orderedKeys] - + return [(x, self.mapping[x]) for x in self.orderedKeys] + def copy(self): result = OrderedDictionary() result.mapping = self.mapping.copy() result.orderedKeys = list(self.orderedKeys) return result - - -class OrderedMultiDictionary(object): + +class OrderedMultiDictionary: """A dictionary that keeps the keys in the dictionary in order and can store multiple items per key - + Ordered multi dictionaries remember the order that items were inserted - and can store multple values per key. When iterating over an ordered dictionary, + and can store multiple values per key. When iterating over an ordered dictionary, the values are returned in the order their keys were first added. >>> d = OrderedMultiDictionary() @@ -309,59 +313,65 @@ class OrderedMultiDictionary(object): >>> print d["second"] [1] """ - + def __init__(self): self.mapping = {} self.orderedKeys = [] - + def __setitem__(self, key, value): if not key in self.mapping: self.mapping[key] = [] self.mapping[key].append(value) - self.orderedKeys.append((key, len(self.mapping[key]) - 1,)) - + self.orderedKeys.append( + ( + key, + len(self.mapping[key]) - 1, + ) + ) + def __getitem__(self, key): return self.mapping[key] - - def __contains__(self, key): + + def __contains__(self, key): return key in self.mapping - - def __iter__(self): + + def __iter__(self): return list(self.values()) - - def __len__(self): + + def __len__(self): return len(self.orderedKeys) - + def __str__(self): - result = 'OrderedDictionary({' + result = "OrderedDictionary({" elements = [] for x, index in self.orderedKeys: - elements.append(repr(x) + ':' + repr(self[x][index])) - result += ', '.join(elements) - result += '})' + elements.append(repr(x) + ":" + repr(self[x][index])) + result += ", ".join(elements) + result += "})" return result - + def __repr__(self): return str(self) - + def __getattr__(self, key): return self.mapping[key] - + def keys(self): - return [x for x,index in self.orderedKeys ] - + return [x for x, index in self.orderedKeys] + def values(self): for x, index in self.orderedKeys: yield self.mapping[x][index] - -class CompositeDictionary(object): + + +class CompositeDictionary: """A dictionary that defers to other dictionaries when an item is not found. - + Composite dictionaries are just like regular dictionaries but they get items from their parent dictionarkies when they do not contain - the items. - + the items. + >>> p = {'a':1, 'b':2} >>> d = CompositeDictionary(p) >>> d['a'] @@ -374,15 +384,16 @@ class CompositeDictionary(object): False >>> 'b' in d True - + """ + def __init__(self, *parents): self.parents = parents self.mapping = {} - + def __setitem__(self, key, value): self.mapping[key] = value - + def __getitem__(self, key): if key in self.mapping: return self.mapping[key] @@ -390,72 +401,72 @@ def __getitem__(self, key): if key in parent: return parent[key] raise KeyError(key) - - def __contains__(self, key): + + def __contains__(self, key): if key in self.mapping: return True for parent in self.parents: if key in parent: return True return False - - def __iter__(self): + + def __iter__(self): return list(self.keys()) - - def __len__(self): + + def __len__(self): return len(list(self.keys())) - + def __str__(self): - result = 'CompositeDictionary({' + result = "CompositeDictionary({" elements = [] for x in list(self.keys()): - elements.append(str(x) + ':' + str(self[x]) ) - result += ','.join(elements) - result += '})' + elements.append(str(x) + ":" + str(self[x])) + result += ",".join(elements) + result += "})" return result - def keys(self): keys = set(self.mapping.keys()) - + for parent in self.parents: keys |= set(parent.keys()) - + return iter(keys) - + def values(self): for x in list(self.keys()): yield self[x] - + def copy(self): result = type(self)(*self.parents) result.mapping = self.mapping.copy() return result - + + class OrderedSet(collections.abc.MutableSet): class Node(object): - __slots__ = ['key', 'next', 'previous'] - - def __init__(self, key, next = None, previous = None): + __slots__ = ["key", "next", "previous"] + + def __init__(self, key, next=None, previous=None): self.key = key if next is None: next = self if previous is None: previous = self - + self.next = next self.previous = previous self.link() - + def link(self): self.next.previous = self self.previous.next = self - + def discard(self): self.previous.next = self.__next__ self.next.previous = self.previous - + def __init__(self, iterable=None): self.end = self.Node(None, None, None) self.end.previous = self.end @@ -486,8 +497,8 @@ def __reversed__(self): def __repr__(self): if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) + return "%s()" % (self.__class__.__name__,) + return "%s(%r)" % (self.__class__.__name__, list(self)) def __eq__(self, other): if isinstance(other, OrderedSet): @@ -496,47 +507,48 @@ def __eq__(self, other): def __del__(self): self.clear() + def add(self, key): if key not in self.map: self.map[key] = self.Node(key, self.end.previous, self.end.__next__) def discard(self, key): - if key in self.map: + if key in self.map: current = self.map.pop(key) current.discard() def pop(self, last=True): if not self: - raise KeyError('set is empty') + raise KeyError("set is empty") key = next(reversed(self)) if last else next(iter(self)) self.discard(key) return key + def memoize(f): def memof(*arg): try: return memof.d[arg] except: - result=f(*arg) - if len(memof.d)>5000: + result = f(*arg) + if len(memof.d) > 5000: return result - memof.d[arg]=result + memof.d[arg] = result return result - memof.d={} + + memof.d = {} return memof class MultitonMetaClass(type): def __new__(mcs, name, bases, dict): - dict['__INSTANCES__'] = {} + dict["__INSTANCES__"] = {} return type.__new__(mcs, name, bases, dict) - + def __call__(mcls, *arguments): if arguments in mcls.__INSTANCES__: - return mcls.__INSTANCES__[arguments] + return mcls.__INSTANCES__[arguments] else: instance = type.__call__(mcls, *arguments) mcls.__INSTANCES__[arguments] = instance return instance - - diff --git a/src/amuse/support/options.py b/src/amuse/support/options.py index 0bec523e0e..ee9762c89c 100644 --- a/src/amuse/support/options.py +++ b/src/amuse/support/options.py @@ -13,7 +13,7 @@ pkg_resources = None -class GlobalOptions(object): +class GlobalOptions: INSTANCE = None def __init__(self): @@ -22,14 +22,14 @@ def __init__(self): def load(self, preloadfp=None): if pkg_resources is not None: - if pkg_resources.resource_exists('amuse', 'amuserc'): - resourcerc = pkg_resources.resource_filename('amuse', 'amuserc') + if pkg_resources.resource_exists("amuse", "amuserc"): + resourcerc = pkg_resources.resource_filename("amuse", "amuserc") self.config.read(resourcerc) rootrc = os.path.join(self.amuse_rootdirectory, self.rcfilename) datarc = os.path.join(self.amuse_data_location, self.rcfilename) - homedirrc = os.path.join(self.homedirectory, '.' + self.rcfilename) + homedirrc = os.path.join(self.homedirectory, "." + self.rcfilename) self.config.read(rootrc) self.config.read(datarc) @@ -38,9 +38,13 @@ def load(self, preloadfp=None): self.config.read_file(preloadfp, "") self.config.read(homedirrc) - self.config.read(os.path.join(self.homedirectory, '.' + platform.node() + '_' + self.rcfilename)) - if 'AMUSERC' in os.environ: - self.config.read(os.environ['AMUSERC']) + self.config.read( + os.path.join( + self.homedirectory, "." + platform.node() + "_" + self.rcfilename + ) + ) + if "AMUSERC" in os.environ: + self.config.read(os.environ["AMUSERC"]) self.config.read(self.rcfilepath) @late @@ -49,21 +53,27 @@ def amuse_data_location(self): this = os.path.dirname(os.path.abspath(__file__)) # installed - result = os.path.abspath(os.path.join(this, "..", "..", "..", "..", "..", "share", "amuse")) - if os.path.exists(os.path.join(result, 'config.mk')): + result = os.path.abspath( + os.path.join(this, "..", "..", "..", "..", "..", "share", "amuse") + ) + if os.path.exists(os.path.join(result, "config.mk")): return result # for some virtualenv setups - result = os.path.abspath(os.path.join(this, "..", "..", "..", "..", "..", "..", "share", "amuse")) - if os.path.exists(os.path.join(result, 'config.mk')): + result = os.path.abspath( + os.path.join(this, "..", "..", "..", "..", "..", "..", "share", "amuse") + ) + if os.path.exists(os.path.join(result, "config.mk")): return result # in-place result = os.path.abspath(os.path.join(this, "..", "..", "..")) - if os.path.exists(os.path.join(result, 'config.mk')): + if os.path.exists(os.path.join(result, "config.mk")): return result - raise exceptions.AmuseException("Could not locate AMUSE root directory! set the AMUSE_DIR variable") + raise exceptions.AmuseException( + "Could not locate AMUSE root directory! set the AMUSE_DIR variable" + ) @late def amuse_rootdirectory(self): @@ -75,17 +85,17 @@ def rcfilepath(self): @late def rcfilename(self): - return 'amuserc' + return "amuserc" @late def homedirectory(self): - path = '' + path = "" try: path = os.path.expanduser("~") except: pass if not os.path.isdir(path): - for evar in ('HOME', 'USERPROFILE', 'TMP'): + for evar in ("HOME", "USERPROFILE", "TMP"): try: path = os.environ[evar] if os.path.isdir(path): @@ -95,7 +105,7 @@ def homedirectory(self): if path: return path else: - raise RuntimeError('please define environment variable $HOME') + raise RuntimeError("please define environment variable $HOME") @classmethod def instance(cls, preloadfp=None): @@ -126,7 +136,7 @@ def read_from_ini_string(self, string): self.config.read_file(file) -class option(object): +class option: """Decorator to define an option :argument type: Type of the value, used when reading from the configuration file. @@ -145,7 +155,15 @@ class option(object): """ - def __init__(self, function=None, type="string", name=None, sections=(), choices=(), global_options=None): + def __init__( + self, + function=None, + type="string", + name=None, + sections=(), + choices=(), + global_options=None, + ): self.specification_method = function if name is not None: @@ -159,7 +177,9 @@ def __init__(self, function=None, type="string", name=None, sections=(), choices if hasattr(self, type.upper()): self.valuetype = getattr(self, type.upper()) else: - raise exceptions.CoreException("'{0}' is not a valid type for option".format(type)) + raise exceptions.CoreException( + "'{0}' is not a valid type for option".format(type) + ) self.validator = self.default_validator self.choices = set(choices) @@ -212,11 +232,11 @@ def STRING(self, section, options): return options.get(section, self.name) def DICT(self, section, options): - opts=options.get(section, self.name).split(",") - result=dict() + opts = options.get(section, self.name).split(",") + result = dict() for o in opts: - key,value=o.split("=") - result[key.strip()]=value.strip() + key, value = o.split("=") + result[key.strip()] = value.strip() return result def default_validator(self, value): @@ -224,14 +244,18 @@ def default_validator(self, value): def choice_validator(self, value): if value not in self.choices: - raise exceptions.CoreException("{0} is not a valid choice for option '{1}', valid values are: {2}".format(value, self.name, sorted(self.choices))) + raise exceptions.CoreException( + "{0} is not a valid choice for option '{1}', valid values are: {2}".format( + value, self.name, sorted(self.choices) + ) + ) return value def get_sections(self, instance): result = [] result.extend(instance.option_sections) result.append(instance.__class__.__name__) - lastname = instance.__class__.__name__.split('.')[-1] + lastname = instance.__class__.__name__.split(".")[-1] if not lastname == instance.__class__.__name__: result.append(lastname) result.append(lastname.lower()) @@ -248,7 +272,7 @@ def __setx__(self, instance, value): pass -class OptionalAttributes(object): +class OptionalAttributes: """ Abstract superclass for all classes supporting optional attributes. @@ -278,9 +302,9 @@ def number_of_tries(self): "number of times to try to connect" return 5 - To code will first search for the value of the option in the - *mysection*, if no value is found the *try* section is searched. - For the following configuration file the **number_of_tries** + To code will first search for the value of the option in the + *mysection*, if no value is found the *try* section is searched. + For the following configuration file the **number_of_tries** attribute will be 10 as the *mysection* section is searched first. :: ini @@ -300,6 +324,7 @@ def number_of_tries(self): 15 """ + option_sections = () def __init__(self, **optional_keyword_arguments): @@ -318,7 +343,7 @@ def _local_options(self): def iter_options(self): cls = type(self) for x in dir(cls): - if x.startswith('_'): + if x.startswith("_"): continue value = getattr(cls, x) if isinstance(value, option):