diff --git a/casimport/__init__.py b/casimport/__init__.py index 34f0a17..c3eb7ee 100644 --- a/casimport/__init__.py +++ b/casimport/__init__.py @@ -37,6 +37,18 @@ from importlib.machinery import ModuleSpec @contextlib.contextmanager 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: oldvalue = obj[key] obj[key] = value @@ -45,16 +57,26 @@ def tempset(obj, key, value): obj[key] = oldvalue 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): self._path = pathlib.Path(path) self._hashes = {} def refresh_dir(self): + '''Internal method to refresh the internal cache of + hashes.''' + for i in glob.glob(os.path.join(self._path, '*.py')): _, hash = self.read_hash_file(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: data = fp.read() hash = hashlib.sha256(data).hexdigest() @@ -62,9 +84,15 @@ class FileDirCAS(object): return data, hash def is_package(self, hash): + '''Decode the provided hash, and decide if it's a package + or not.''' + return False 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() parts = hash.split('_', 2) @@ -78,6 +106,10 @@ class FileDirCAS(object): exec(data, module.__dict__) 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): self._loaders = [] @@ -93,12 +125,26 @@ class CASFinder(MetaPathFinder, Loader): self.disconnect() 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: sys.meta_path.remove(self) except ValueError: pass 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) # MetaPathFinder methods