Package: lc-registry
registry-pyOverviewMetaprogramming means writing programs that manipulate other programs, such as compilers, interpreters, macros, etc.
Python enables metaprogramming in a number of ways, one of which ismetaclasses, which are special classes that can
hook into child class creation methods, and can enforce invariants on those child classes. Theabc.ABCMetaclass is one example, and allows for the
declaration ofabstractmethods which must be
implemented by child classes.registry-pyuses metaprogramming to implement theregistry pattern, where ametaclassstores a map of all child classes. This pattern is particularly useful for auto-discovery or auto-registration, where a
program doesn't doesn't need to know where code is stored to know what's been implemented.InstallationInstall from PyPi (preferred method)pipinstalllc-registryInstall from GitHub with Pippipinstallgit+https://github.com/libcommon/registry-py.git@vx.x.x#egg=lc_registrywherex.x.xis the version you want to download.Install by Manual DownloadTo download the source distribution and/or wheel files, navigate tohttps://github.com/libcommon/registry-py/tree/releases/vx.x.x/dist, wherex.x.xis the version you want to install,
and download either via the UI or with a tool like wget. Then to install run:pipinstall<downloadedfile>Donotchange the name of the file after downloading, as Pip requires a specific naming convention for installation files.Dependenciesregistry-pydoes not have external dependencies. Only Python versions >= 3.6 are officially supported.Getting StartedSuppose you want to implement a plugin system for a command line tool. One approach could be to require that plugin
files are written to a specific folder, saysrc/plugins/contrib, and useimportlib.import_moduleto import modules
from there on program startup. However, plugin files not in the proper directory will not get loaded, and finding
plugins requires walking a directory tree. Using a registry would alleviate both these issues.# plugin_registry.pyfromabcimportABCMetafromtypingimportAny,ClassVar,Dict,Typefromlc_registryimportRegistryMetaclassMixinclassPluginRegistry(RegistryMetaclassMixin,ABCMeta):"""Metaclass with registry."""__slots__=()_REGISTRY:ClassVar[Dict[str,Type[Any]]]=dict()@classmethoddef_add_class(cls,name:str,new_cls:Type[Any])->None:# All plugins must be named "*Plugin"# NOTE: This is just an example what you can do with _add_classifname=="PluginBase"ornotname.endswith("Plugin"):returnNonecls._REGISTRY[name]=new_clsreturnNone@classmethoddefrun_plugins(cls):# Use cls._REGISTRY (or cls.registry() method) to# iterate over each plugin and run it.classPluginBase(metaclass=PluginRegistry):"""Base class for all plugins. All child classeswill be added to the plugin registry once they comeinto scope (AKA when Python creates the type)."""__slots__=()@abstractmethoddefrun_plugin(self,context:Dict[str,Any])->None:"""Plugin entrypoint."""raiseNotImplementedError# line_count_plugin.pyfromtypingimportAny,Dictfromplugin_registryimportPluginBaseclassLineCountPlugin(PluginBase):"""Plugin that counts the number of lines in a file."""__slots__=()defrun_plugin(self,context:Dict[str,Any])->None:if"filepath"incontext:filepath=context.get("filepath")line_count=0withopen(filepath)assource_file:for_insource_file:line_count+=1print("{}\t{}".format(filepath,line_count))Note the doc string for thePluginBaseclass in the example above: "... once they come into scope (AKA when
Python creates the type)." This isimportant- child classes will only be added to the registry if Python
loads them into the program, meaning if they get imported somewhere.Contributing/SuggestionsContributions and suggestions are welcome! To make a feature request, report a bug, or otherwise comment on existing
functionality, please file an issue. For contributions please submit a PR, but make sure to lint, type-check, and test
your code before doing so. Thanks in advance!