No project description provided
Project description
registry-py
Overview
Metaprogramming means writing programs that manipulate other programs, such as compilers, interpreters, macros, etc.
Python enables metaprogramming in a number of ways, one of which is metaclasses
, which are special classes that can
hook into child class creation methods, and can enforce invariants on those child classes. The
abc.ABCMeta class is one example, and allows for the
declaration of abstractmethods which must be
implemented by child classes. registry-py
uses metaprogramming to implement the
registry pattern, where a metaclass
stores 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.
Installation
Install from PyPi (preferred method)
pip install lc-registry
Install from GitHub with Pip
pip install git+https://github.com/libcommon/registry-py.git@vx.x.x#egg=lc_registry
where x.x.x
is the version you want to download.
Install by Manual Download
To download the source distribution and/or wheel files, navigate to
https://github.com/libcommon/registry-py/tree/releases/vx.x.x/dist
, where x.x.x
is the version you want to install,
and download either via the UI or with a tool like wget. Then to install run:
pip install <downloaded file>
Do not change the name of the file after downloading, as Pip requires a specific naming convention for installation files.
Dependencies
registry-py
does not have external dependencies. Only Python versions >= 3.6 are officially supported.
Getting Started
Suppose 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, say src/plugins/contrib
, and use
importlib.import_module to 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.py
from abc import ABCMeta
from typing import Any, ClassVar, Dict, Type
from lc_registry import RegistryMetaclassMixin
class PluginRegistry(RegistryMetaclassMixin, ABCMeta):
"""Metaclass with registry."""
__slots__ = ()
_REGISTRY: ClassVar[Dict[str, Type[Any]]] = dict()
@classmethod
def _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_class
if name == "PluginBase" or not name.endswith("Plugin"):
return None
cls._REGISTRY[name] = new_cls
return None
@classmethod
def run_plugins(cls):
# Use cls._REGISTRY (or cls.registry() method) to
# iterate over each plugin and run it.
class PluginBase(metaclass=PluginRegistry):
"""Base class for all plugins. All child classes
will be added to the plugin registry once they come
into scope (AKA when Python creates the type).
"""
__slots__ = ()
@abstractmethod
def run_plugin(self, context: Dict[str, Any]) -> None:
"""Plugin entrypoint."""
raise NotImplementedError
# line_count_plugin.py
from typing import Any, Dict
from plugin_registry import PluginBase
class LineCountPlugin(PluginBase):
"""Plugin that counts the number of lines in a file."""
__slots__ = ()
def run_plugin(self, context: Dict[str, Any]) -> None:
if "filepath" in context:
filepath = context.get("filepath")
line_count = 0
with open(filepath) as source_file:
for _ in source_file:
line_count += 1
print("{}\t{}".format(filepath, line_count))
Note the doc string for the PluginBase
class in the example above: "... once they come into scope (AKA when
Python creates the type)." This is important - child classes will only be added to the registry if Python
loads them into the program, meaning if they get imported somewhere.
Contributing/Suggestions
Contributions 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!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file lc_registry-0.1.2.tar.gz
.
File metadata
- Download URL: lc_registry-0.1.2.tar.gz
- Upload date:
- Size: 4.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/42.0.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 27e62956b8e02a7458a52f5bd627b7d143971ea8ea1d2aafa356d741f08a7cfe |
|
MD5 | ca04a80ee4ea3be2ee1dbe6ed904e82a |
|
BLAKE2b-256 | 4c8a0dec8ca80e448416aed867c9edd0ed83f25168efe3bbab7d0a50e5dd6760 |
File details
Details for the file lc_registry-0.1.2-py3-none-any.whl
.
File metadata
- Download URL: lc_registry-0.1.2-py3-none-any.whl
- Upload date:
- Size: 6.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/42.0.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f709fb32c11c53efbce23d2e2807deed040ce8c29f4f15bd5915d3ada139eca9 |
|
MD5 | 3a553abf34b355003dbe4e7a7e9ecf72 |
|
BLAKE2b-256 | be318f0b1a3862e22c79018c74bfa64d2cfeaf19e80af8c70f526730fd7016b0 |