pythoninterfacedependency-injectionplugin-architecture

How do I implement run time objects (different) implementation with using Python


My requirement is that I have a standard framework of generating HTML To PDF in 3 steps :

1)Get stuff from our DB and create a meaning out of it , read XML and over-ride or decided settings etc - pre_processing

2)Creating an HTML Template using the preprocesed data - render_template

3)Run a a HTML to PDF Converted and get the PDF back . - invoke_service

Now , its possible that sometimes different PDF engines might be needed and implementations can change ... Its even possible some of them need a very strict data or proprietary settings

So I might need to extend 1 , over-ride 2 , over-ride 3 and I would like to make this implementation configurable and get switchable easily .

I am thinking of creating a Base Class and then creating Foo.py implementation anf Bar.py implementation that are actually going to be the runtime objects . How can I get this with simple Python ?

A terrible way I can think of is:

#Module Name ServiceHandler

def implementation(imp_name):
    try:
       from imp_name import imp_class
       return imp_class(AbtractClass) 
    else:
       Sorry No Implemenation Found!

class AbstractClass(...)
      def __init__



Gets invoked in another object method as 


self.ServiceHandler.implementation(read_from_xml)

Can anyone give me good pointers to solve this problem ? Basically I want Foo(Base) or Bar(Base) and this should be like read a configuration and map it to that .

My current module layout is :

module.py


Solution

  • I'd do something like that :

    # file : service_handler.py
    from importlib import import_module
    
    # get parameters from your config file beforehand
    def get_backend(backend_name, **kwargs): 
    
        # example : foo.Spam gets backend Spam from module foo
        backend_module, backend_class = backend_name.rsplit('.', 1)
    
        mdl = import_module(backend_module)
        cls = getattr(mdl, backend_class) 
    
        return cls(**kwargs) # create the object
    

    Define your backends as you want to (using abstract classes or duck typing).

    This is by no means a complete solution, rather a skeleton pattern that I saw and have done on several projects. You might want to add registry mechanisms, default backends, autodiscover... ymmv.

    Projects like django do a lot of multiple-backend stuff, give their codebase a look.