pyhk2

Hundred-Kilobyte Kernel library for dependency injection and service discovery


Keywords
injection, ioc, container, plugin, discovery, service, contract, hk2, kernel
License
MIT
Install
pip install pyhk2==0.0.1

Documentation

pyhk2

Python Hundred-Kilobyte Kernel library.

It is inspired by HK2 library for Java that allows easily create plug-in based applications.

This library provides:

  • Marking classes as services and interfaces as contracts thus declaring explicitly possible extension points and their implementations
  • Runtime discovery of services and contracts
  • Inversion of Control (IoC) and dependency injection (DI)
  • API for fine-level control over service instantiation
  • Lifetime control mechanisms (scopes)
  • Custom injection resolution

Interfaces for programming to contracts


@interface
class IFoo(object):
    def foo(self):
        '''Does foo'''

@interface
class IBar(object):

    def bar(self):
        '''Does bar'''

class FooBar(IFoo, IBar):
    def foo(self):
        return 'foo!'

>> fb = FooBar()
>> fb.foo()
foo!
>> fb.bar()
NotImplementedError: abstract method call

Dependency injection


class Foo(IFoo):
    
    @inject(IBar)
    def __init__(self, bar):
        self._bar = bar

    def foo(self):
        return 'foo ' + self._bar.bar() + ' !'

class Bar(IBar):
    def bar(self):
        return 'bar'

>> c = Container()
>> c.bind(IFoo, Foo)
>> c.bind(IBar, Bar)
>> f = c.get(IFoo)
>> f.foo()
foo bar !

Service registry

Primary way of using the pyhk2 library is by using decorator-based service discovery. This mechanism is based on using following decorators:

  • Contract decorator - marks interface as a potential extension point
  • Service decorator - marks class as implementation of one or many extension points

@interface
@contract
class IReporter(object):
    def report(self):
        """Reports some data"""

@service
class MyReporter(IReporter):
    
    @inject(ISomeDependency)
    def __init__(self, dep):
        ...

habitat = Habitat()
reporters = habitat.getAllByComponent(IReporter)
for r in reporters:
    r.report()

Example above demonstrates explicit instantiation of extensions via Habitat. Habitat is the client-side interface that represents the kernel. Habitat provides methods to look-up services manually, and explore the extension graph. You can inject Habitat into your services like any other service.

However, using Habitat explicitly is rarely needed, simpler and cleaner way to resolve the extensions by contract is to use dependency injection mechanism.

Explicit instantiation via Habitat is primarily useful for delayed instantiation of extensions, but in other cases prefer injection or you'll end up with Service Locator anti-pattern.