...
 
Commits (11)
This diff is collapsed.
......@@ -75,6 +75,9 @@ class Storage(utils.CodeStorage):
"""
asset_type = "database"
asset_folder = "databases"
def __init__(self, prefix, name):
if name.count("/") != 1:
......@@ -84,7 +87,7 @@ class Storage(utils.CodeStorage):
self.fullname = name
self.prefix = prefix
path = os.path.join(self.prefix, "databases", name + ".json")
path = os.path.join(self.prefix, self.asset_folder, name + ".json")
path = path[:-5]
# views are coded in Python
super(Storage, self).__init__(path, "python")
......
This diff is collapsed.
......@@ -65,16 +65,21 @@ class Storage(utils.CodeStorage):
"""
asset_type = "library"
asset_folder = "libraries"
def __init__(self, prefix, name, language=None):
if name.count('/') != 2:
if name.count("/") != 2:
raise RuntimeError("invalid library 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, 'libraries', name, suffix='.json')
path = utils.hashed_or_simple(
self.prefix, self.asset_folder, name, suffix=".json"
)
path = path[:-5]
super(Storage, self).__init__(path, language)
......@@ -144,10 +149,9 @@ class Library(object):
try:
self._load(name, library_cache)
finally:
if self._name is not None: #registers it into the cache, even if failed
if self._name is not None: # registers it into the cache, even if failed
library_cache[self._name] = self
def _load(self, data, library_cache):
"""Loads the library"""
......@@ -156,16 +160,16 @@ class Library(object):
self.storage = Storage(self.prefix, data)
json_path = self.storage.json.path
if not self.storage.exists():
self.errors.append('Library declaration file not found: %s' % json_path)
self.errors.append("Library 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
# if no errors so far, make sense out of the library data
self.data.setdefault('uses', {})
self.data.setdefault("uses", {})
if self.uses is not None:
for name, value in self.uses.items():
......@@ -173,11 +177,10 @@ class Library(object):
self.libraries[self._name] = self
def uses_dict(self):
"""Returns the usage dictionary for all dependent modules"""
if self.data['language'] == 'unknown':
if self.data["language"] == "unknown":
raise RuntimeError("library has no programming language set")
if not self._name:
......@@ -189,95 +192,85 @@ class Library(object):
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 load(self):
"""Loads the Python module for this library resolving all references
Returns the loaded Python module.
"""
if self.data['language'] == 'unknown':
if self.data["language"] == "unknown":
raise RuntimeError("library has no programming language set")
if not self._name:
raise RuntimeError("library has no name")
return loader.load_module(self.name.replace(os.sep, '_'),
self.storage.code.path, self.uses_dict())
return loader.load_module(
self.name.replace(os.sep, "_"), self.storage.code.path, self.uses_dict()
)
@property
def name(self):
"""Returns the name of this object"""
return self._name or '__unnamed_library__'
return self._name or "__unnamed_library__"
@name.setter
def name(self, value):
if self.data['language'] == 'unknown':
if self.data["language"] == "unknown":
raise RuntimeError("library 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 library code"""
return self.data['language']
return self.data["language"]
@language.setter
def language(self, value):
"""Sets the current executable code programming language"""
if self.storage: self.storage.language = value
self.data['language'] = value
if self.storage:
self.storage.language = value
self.data["language"] = value
self._check_language_consistence()
@property
def valid(self):
"""A boolean that indicates if this library 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 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):
......@@ -290,7 +283,6 @@ class Library(object):
return self.storage.doc.load()
return None
@documentation.setter
def documentation(self, value):
"""Sets the full-length description for this object"""
......@@ -298,12 +290,11 @@ class Library(object):
if not self._name:
raise RuntimeError("library 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 library"""
......@@ -312,7 +303,6 @@ class Library(object):
return self.storage.hash()
def json_dumps(self, indent=4):
"""Dumps the JSON declaration of this object in a string
......@@ -328,14 +318,11 @@ class Library(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.
......@@ -347,17 +334,16 @@ class Library(object):
"""
if self.data['language'] == 'unknown':
if self.data["language"] == "unknown":
raise RuntimeError("library has no programming language set")
if storage is None:
if not self._name:
raise RuntimeError("library 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
......@@ -387,8 +373,9 @@ class Library(object):
raise RuntimeError("library is not valid")
if prefix == self.prefix:
raise RuntimeError("Cannot export library to the same prefix ("
"%s)" % (prefix))
raise RuntimeError(
"Cannot export library to the same prefix (" "%s)" % (prefix)
)
for k in self.libraries.values():
k.export(prefix)
......
......@@ -64,6 +64,9 @@ class Storage(utils.Storage):
"""
asset_type = "protocoltemplate"
asset_folder = "protocoltemplates"
def __init__(self, prefix, name):
if name.count("/") != 1:
......@@ -74,7 +77,7 @@ class Storage(utils.Storage):
self.prefix = prefix
path = utils.hashed_or_simple(
self.prefix, "protocoltemplates", name, suffix=".json"
self.prefix, self.asset_folder, name, suffix=".json"
)
path = path[:-5]
super(Storage, self).__init__(path)
......
......@@ -55,11 +55,11 @@ from . import hash
# ----------------------------------------------------------
def hashed_or_simple(prefix, what, path, suffix='.json'):
def hashed_or_simple(prefix, what, path, suffix=".json"):
"""Returns a hashed path or simple path depending on where the resource is
"""
username, right_bit = path.split('/', 1)
username, right_bit = path.split("/", 1)
hashed_prefix = hash.toUserPath(username)
candidate = os.path.join(prefix, what, hashed_prefix, right_bit) + suffix
......@@ -119,20 +119,13 @@ def extension_for_language(language):
"""
return dict(
unknown = '',
cxx = '.so',
matlab = '.m',
python = '.py',
r = '.r',
)[language]
return dict(unknown="", cxx=".so", matlab=".m", python=".py", r=".r")[language]
# ----------------------------------------------------------
class Prefix(object):
def __init__(self, paths=None):
if isinstance(paths, list):
self.paths = paths
......@@ -159,44 +152,38 @@ class Prefix(object):
class File(object):
"""User helper to read and write file objects"""
def __init__(self, path, binary=False):
self.path = path
self.binary = binary
def exists(self):
return os.path.exists(self.path)
def load(self):
mode = 'rb' if self.binary else 'rt'
mode = "rb" if self.binary else "rt"
with open(self.path, mode) as f:
return f.read()
def try_load(self):
if os.path.exists(self.path):
return self.load()
return None
def backup(self):
if not os.path.exists(self.path):
return #no point in backing-up
return # no point in backing-up
backup = self.path + '~'
backup = self.path + "~"
if os.path.exists(backup):
os.remove(backup)
shutil.copy(self.path, backup)
def save(self, contents):
d = os.path.dirname(self.path)
......@@ -206,25 +193,24 @@ class File(object):
if os.path.exists(self.path):
self.backup()
mode = 'wb' if self.binary else 'wt'
mode = "wb" if self.binary else "wt"
if self.binary:
mode = 'wb'
mode = "wb"
if isinstance(contents, six.string_types):
contents = contents.encode('utf-8')
contents = contents.encode("utf-8")
else:
mode = 'wt'
mode = "wt"
if not isinstance(contents, six.string_types):
contents = contents.decode('utf-8')
contents = contents.decode("utf-8")
with open(self.path, mode) as f:
f.write(contents)
def remove(self):
safe_rmfile(self.path)
safe_rmfile(self.path + '~') #backup
safe_rmdir(self.path) #remove containing directory
safe_rmfile(self.path + "~") # backup
safe_rmdir(self.path) # remove containing directory
# ----------------------------------------------------------
......@@ -233,13 +219,21 @@ class File(object):
class Storage(object):
"""Resolves paths for objects that provide only a description"""
asset_type = None
asset_folder = None
def __init__(self, path):
if not all(
[type(attr) == str for attr in [self.asset_type, self.asset_folder]]
):
raise TypeError("asset_type and asset_folder must be configure properly")
self.path = path
self.json = File(self.path + '.json')
self.doc = File(self.path + '.rst')
self.json = File(self.path + ".json")
self.doc = File(self.path + ".rst")
def hash(self, description='description'):
def hash(self, description="description"):
"""The 64-character hash of the database declaration JSON"""
return hash.hashJSONFile(self.json.path, description)
......@@ -249,12 +243,13 @@ class Storage(object):
def load(self):
"""Loads the JSON declaration as a file"""
tp = collections.namedtuple('Storage', ['declaration', 'description'])
tp = collections.namedtuple("Storage", ["declaration", "description"])
return tp(self.json.load(), self.doc.try_load())
def save(self, declaration, description=None):
"""Saves the JSON declaration as files"""
if description: self.doc.save(description.encode('utf8'))
if description:
self.doc.save(description.encode("utf8"))
if not isinstance(declaration, six.string_types):
declaration = simplejson.dumps(declaration, indent=4)
self.json.save(declaration)
......@@ -280,21 +275,22 @@ class CodeStorage(object):
def __init__(self, path, language=None):
self.path = path
self.json = File(self.path + '.json')
self.doc = File(self.path + '.rst')
self.json = File(self.path + ".json")
self.doc = File(self.path + ".rst")
self._language = language or self.__auto_discover_language()
self.code = File(self.path + \
extension_for_language(self._language), binary=True)
self.code = File(
self.path + extension_for_language(self._language), binary=True
)
def __auto_discover_language(self, json=None):
"""Discovers and sets the language from its own JSON descriptor"""
try:
text = json or self.json.load()
json = simplejson.loads(text)
return json['language']
return json["language"]
except IOError:
return 'unknown'
return "unknown"
@property
def language(self):
......@@ -303,21 +299,24 @@ class CodeStorage(object):
@language.setter
def language(self, value):
self._language = value
self.code = File(self.path + extension_for_language(self._language),
binary=True)
self.code = File(
self.path + extension_for_language(self._language), binary=True
)
def hash(self):
"""The 64-character hash of the database declaration JSON"""
if self.code.exists():
return hash.hash(dict(
json=hash.hashJSONFile(self.json.path, 'description'),
code=hash.hashFileContents(self.code.path),
))
return hash.hash(
dict(
json=hash.hashJSONFile(self.json.path, "description"),
code=hash.hashFileContents(self.code.path),
)
)
else:
return hash.hash(dict(
json=hash.hashJSONFile(self.json.path, 'description'),
))
return hash.hash(
dict(json=hash.hashJSONFile(self.json.path, "description"))
)
def exists(self):
"""If the database declaration file exists"""
......@@ -325,21 +324,22 @@ class CodeStorage(object):
def load(self):
"""Loads the JSON declaration as a file"""
tp = collections.namedtuple('CodeStorage',
['declaration', 'code', 'description'])
tp = collections.namedtuple(
"CodeStorage", ["declaration", "code", "description"]
)
return tp(self.json.load(), self.code.try_load(), self.doc.try_load())
def save(self, declaration, code=None, description=None):
"""Saves the JSON declaration and the code as files"""
if description:
self.doc.save(description.encode('utf8'))
self.doc.save(description.encode("utf8"))
if not isinstance(declaration, six.string_types):
declaration = simplejson.dumps(declaration, indent=4)
self.json.save(declaration)
if code:
if self._language == 'unknown':
if self._language == "unknown":
self.language = self.__auto_discover_language(declaration)
self.code.save(code)
......@@ -366,7 +366,8 @@ class NumpyJSONEncoder(simplejson.JSONEncoder):
if isinstance(obj, numpy.ndarray) or isinstance(obj, numpy.generic):
return obj.tolist()
elif isinstance(obj, numpy.dtype):
if obj.name == 'str': return 'string'
if obj.name == "str":
return "string"
return obj.name
return simplejson.JSONEncoder.default(self, obj)
......@@ -377,10 +378,12 @@ class NumpyJSONEncoder(simplejson.JSONEncoder):
def has_argument(method, argument):
try:
from inspect import signature
sig = signature(method)
params = sig.parameters
except ImportError:
from inspect import getargspec
params = getargspec(method).args
return argument in params