Package: persisting-theory
IntroductionPersisting-theory is a small python utility designed to automate data discovering and access inside a list of packages. Use case: you are building an application that will have pluggable components. You want to allow these components to register data so it can be accessed by any other component of your app.
If you ever used Django framework, you may remember this:fromdjango.contribimportadminadmin.autodiscover()Basically, persisting-theory will do the same, except that it let you declare what you want to autodiscover.Okay, I’m bad at explaining things, and english is not my mother tongue. Let’s build a simple example.QuickstartInstallInstall the package fromPyPi. via pip (or any other tool):pip install persisting-theoryPersisting-theory does not require any dependency but a python installation (it has been tested on python 2.7 and python 3.4).SetupA basic setup:# registries.pyfrompersiting_theoryimportRegistryclassCallbacksRegistry(Registry):"""
        Allow your apps to register callbacks
    """# the package where the registry will try to find callbacks in each applook_into="callbacks_registry"callbacks_registry=CallbacksRegistry()# app1/callbacks_registry.pyfromregistriesimportcallbacks_registry@callbacks_registry.registerdefdog():print("Wouf")# app2/callbacks_registry.pyfromregistriesimportcallbacks_registry@callbacks_registry.registerdefcat():print("Meow")# dosomething.pyfromregistriesimportcallbacks_registryAPPS=('app1','app2',)# Trigger autodiscovering processcallbacks_registry.autodiscover(APPS)forcallbackincallbacks_registry.values():callback()# Wouf# MeowAPIRegistryinherits from python built-incollections.OrderedDict, which means you can use regular dict methods to access registered data:callbacks_registry.get("dog")()#  will print Woufassertcallbacks_registry.get("chicken",None)isNoneRegistry.register()You can use this function as a decorator for registering functions and classes:frompersisting_theoryimportRegistryclassAwesomeRegistry(Registry):passr=AwesomeRegistry()# register a class@r.registerclassAwesomeClass:pass# register a function@r.registerdefawesome_function():pass# By default, the key in the registry for a given value is obtained from the function or class name, if possibleassertr.get("AwesomeClass")==AwesomeClassassertr.get("awesome_function")==awesome_function# You can override this behaviour:@r.register(name="Chuck")classAwesomeClass:pass@r.register(name="Norris")defawesome_function():passassertr.get("Chuck")==AwesomeClassassertr.get("Norris")==awesome_function# You can also use the register method as isawesome_var="Chuck Norris"r.register(awesome_var,name="Who am I ?")assertr.get("Who am I ?")==awesome_var# I f you are not registering a function or a class, you MUST provide a name argumentRegistry.validate()By default, a registry will accept any registered value. Sometimes, it’s not what you want, so you can restrict what kind of data your registry accepts:frompersisting_theoryimportRegistryclassStartsWithAwesomeRegistry(Registry):defvalidate(self,data):ifisinstance(data,str):returndata.startswith("awesome")returnFalser=StartsWithAwesomeRegistry()# will pass registrationr.register("awesome day",name="awesome_day")# will fail and raise ValueErrorr.register("not so awesome day",name="not_so_awesome_day")Registry.prepare_data()If you want to manipulate your data before registering it, override this method. In this example, we prefix every registered string with ‘hello’:frompersisting_theoryimportRegistryclassHelloRegistry(Registry):defprepare_data(self,data):return'hello '+datar=HelloRegistry()classGreeting:def__init__(self,first_name):self.first_name=first_namer.register(Greeting('World'),name="world")r.register(Greeting('agate'),name="agate")assertr.register.get('world')=="hello World"assertr.register.get('agate')=="hello agate"Registry.prepare_name()In a similar way, you can manipulate the name of registered data. This can help if you want to avoid repetitions. Let’s improve our previous example:frompersisting_theoryimportRegistryclassHelloRegistry(Registry):defprepare_data(self,data):return'hello '+datadefprepare_name(self,data,name=None):returnself.data.first_name.lower()r=HelloRegistry()classGreeting:def__init__(self,first_name):self.first_name=first_namer.register(Greeting('World'))r.register(Greeting('agate'))assertr.register.get('world')=="hello World"assertr.register.get('agate')=="hello agate"Going metaIf you have multiple registries, or want to allow your apps to declare their own registries, this is for you:# registries.pyfrompersisting_theoryimportmeta_registry,RegistryclassRegistryA(Registry):look_into="a"classRegistryB(Registry):look_into="b"registry_a=RegistryA()meta_registry.register(registry_a,name="registry_a")registry_b=RegistryB()meta_registry.register(registry_b,name="registry_b")# dosomethingelse.pyfrompersisting_theoryimportmeta_registry# will import registries declared in `registries` packages, and trigger autodiscover() on each of themmeta_registry.autodiscover(apps=("app1","app2"))What the hell is that name ?It’s an anagram for “python registries”.ContributeContributions, bug reports, and “thank you” are welcomed.LicenseThe project is licensed under BSD licence.