| @@ -37,6 +37,18 @@ from importlib.machinery import ModuleSpec | |||||
| @contextlib.contextmanager | @contextlib.contextmanager | ||||
| def tempset(obj, key, value): | def tempset(obj, key, value): | ||||
| '''A context (with) manager for changing the value of an item in a | |||||
| dictionary, and restoring it after the with block. | |||||
| Example usage: | |||||
| ``` | |||||
| d = dict(a=5, b=10) | |||||
| with tempset(d, 'a', 15): | |||||
| print(repr(d['a']) | |||||
| print(repr(d['a']) | |||||
| ``` | |||||
| ''' | |||||
| try: | try: | ||||
| oldvalue = obj[key] | oldvalue = obj[key] | ||||
| obj[key] = value | obj[key] = value | ||||
| @@ -45,16 +57,26 @@ def tempset(obj, key, value): | |||||
| obj[key] = oldvalue | obj[key] = oldvalue | ||||
| class FileDirCAS(object): | class FileDirCAS(object): | ||||
| '''A file loader for CAS that operates on a directory. It looks | |||||
| at files, caches their hash, and loads them upon request.''' | |||||
| def __init__(self, path): | def __init__(self, path): | ||||
| self._path = pathlib.Path(path) | self._path = pathlib.Path(path) | ||||
| self._hashes = {} | self._hashes = {} | ||||
| def refresh_dir(self): | def refresh_dir(self): | ||||
| '''Internal method to refresh the internal cache of | |||||
| hashes.''' | |||||
| for i in glob.glob(os.path.join(self._path, '*.py')): | for i in glob.glob(os.path.join(self._path, '*.py')): | ||||
| _, hash = self.read_hash_file(i) | _, hash = self.read_hash_file(i) | ||||
| self._hashes[hash] = i | self._hashes[hash] = i | ||||
| def read_hash_file(self, fname): | |||||
| @staticmethod | |||||
| def read_hash_file(fname): | |||||
| '''Helper function that will read the file at fname, and | |||||
| return the tuple of it's contents and it's hash.''' | |||||
| with open(fname, 'rb') as fp: | with open(fname, 'rb') as fp: | ||||
| data = fp.read() | data = fp.read() | ||||
| hash = hashlib.sha256(data).hexdigest() | hash = hashlib.sha256(data).hexdigest() | ||||
| @@ -62,9 +84,15 @@ class FileDirCAS(object): | |||||
| return data, hash | return data, hash | ||||
| def is_package(self, hash): | def is_package(self, hash): | ||||
| '''Decode the provided hash, and decide if it's a package | |||||
| or not.''' | |||||
| return False | return False | ||||
| def exec_module(self, hash, module): | def exec_module(self, hash, module): | ||||
| '''Give the hash and module, load the code associated | |||||
| with the hash, and exec it in the module's context.''' | |||||
| self.refresh_dir() | self.refresh_dir() | ||||
| parts = hash.split('_', 2) | parts = hash.split('_', 2) | ||||
| @@ -78,6 +106,10 @@ class FileDirCAS(object): | |||||
| exec(data, module.__dict__) | exec(data, module.__dict__) | ||||
| class CASFinder(MetaPathFinder, Loader): | class CASFinder(MetaPathFinder, Loader): | ||||
| '''Overall class for using Content Addressable Storage to load | |||||
| Python modules into your code. It contains code to dispatch to | |||||
| the various loaders to attempt to load the hash.''' | |||||
| def __init__(self): | def __init__(self): | ||||
| self._loaders = [] | self._loaders = [] | ||||
| @@ -93,12 +125,26 @@ class CASFinder(MetaPathFinder, Loader): | |||||
| self.disconnect() | self.disconnect() | ||||
| def disconnect(self): | def disconnect(self): | ||||
| '''Disconnect this Finder from being used to load modules. | |||||
| As this claims an entire namespace, only the first loaded | |||||
| one will work, and any others will be hidden until the | |||||
| first one is disconnected. | |||||
| This can be used w/ a with block to automatically | |||||
| disconnect when no longer needed. This is mostly useful | |||||
| for testing.''' | |||||
| try: | try: | ||||
| sys.meta_path.remove(self) | sys.meta_path.remove(self) | ||||
| except ValueError: | except ValueError: | ||||
| pass | pass | ||||
| def register(self, loader): | def register(self, loader): | ||||
| '''Register a loader w/ this finder. This will attempt | |||||
| to load the hash passed to it. It is also (currently) | |||||
| responsible for executing the code in the module.''' | |||||
| self._loaders.append(loader) | self._loaders.append(loader) | ||||
| # MetaPathFinder methods | # MetaPathFinder methods | ||||