This is a pre-production deployment of Warehouse, however changes made here WILL affect the production instance of PyPI.
Latest Version Dependencies status unknown Test status unknown Test coverage unknown
Project Description is a small library that helps you write a modular and maintainable codebase.

The library works with class: the loaded plug-ins are “injected” in the given class. You can specify the way of injection, either by stacking functions, with the modulable decorator, by overloading the base method, with the overridable decorator, and finally with the alternative decorator, which runs every function until it finds one that doesn’t raise an exception.

Those decorators conserve the original method’s informations, such as name, module, docstring, and annotations.


Let’s say you want to build a modular shell, where the users can implement their own commands and prompt for example.

from modular import *

class Shell(Modular, plugin_directory='plugins'):

This declares a modular class, whose plug-ins are in the plugins directory relative to the current working directory.

The library will load every plug-in (must be a .py file) in that directory when the class is instantiated.

It is convenient to declare a init method, called within the real __init__ constructor, to allow users to initialize their plug-in specific attributes:

def __init__(self, *args, **kwds):
    self.init(*args, **kwds)

def init(self, *args, **kwds):
    self.running = False

Here, we decorate the init method with modulable. That means every plug-ins’ implementations of the init method will be executed with the given arguments.

Next, we want a function that is executed between every command, and, say, a method that returns the shell prompt:

def update(self):

def prompt(self):
    return '> '

This time, we use the overridable decorator. This decorator uses the last implementation of prompt loaded.

Lastly, we want a function that reacts on user input. What we want here is to run every implementation until one works (i.e. doesn’t raise an error). To do that, you can use the alternative decorator which takes an exception type and calls every implementation one-by-one until one doesn’t raise an error of this type:

def react(self, i):
    if i:
        print('Unrecognized command:', repr(i))

We provide a default case here, if there isn’t any implementation working.

Finally, we define some non-modulable methods to make the whole thing works:

def run(self):
    self.running = True

    while self.running:
        i = input(self.prompt())

For instance, our shell doesn’t implement any plug-in. Just for the example, we’ll implement a quit plug-in, which stops the shell when the user types in quit, a greet plug-in, and finally we’ll customize our prompt.

The implementation of the quit command is pretty straight-forward:

def react(self, i):
    if i == 'quit':
        self.running = False
        raise ValueError

By raising ValueError, we delegate the input processing to the next implementation of react.

The greet plug-in does the same, with a bit more complex parsing:

def react(self, i):
    lexemes = i.split()

        if lexemes[0] == 'greet':
            print('Hey', lexemes[1], '!')
            raise ValueError
    except IndexError:
        raise ValueError

Finally, lets define a prompt that displays the command count:

def init(self, *args, **kwds):
    self.command_count = 0

def update(self):
    self.command_count += 1

def prompt(self):
    return '[{}]: '.format(self.command_count)

The plug-ins must be contained in the specified plug-in directory in the class declaration, here, plugins. You should have a similar directory tree:

├── plugins
│   ├──
│   ├──
│   └──

To use this class, simply instantiate a Shell object and call its run method:

sh = Shell()

Here’s what it does:

[2]: greet Jonathan
Hey Jonathan !
[5]: unknown command
Unrecognized command: 'unknown command'
[7]: quit

You can see the complete code in the example directory.

Advanced use

You can temporarily load a plug-in with the plugin context manager:

with Shell.plugin('greet'):

You can also check the loaded plug-ins by typing Shell.loaded_plugins.

Finally, there is an optional virtual keyword argument at class definition. virtual is set to False by default, but if set to True, the class will not load the plug-ins automatically:

class AbstractShell(Modular, plugins='plugins', virtual=True):


$ pip install modulable

And, if you’re on Linux, and face a permission error, make sure to run sudo with the -H option:

$ sudo -H pip install modulable
$ git clone
$ cd modulable
$ sudo -H python3.4 install

Or, if you’re on Windows:

$ git clone
$ cd modulable
$ py -3.4 install

If you don’t have git, you can download the zip file here.


modulable is distributed under the MIT license.

Release History

Release History


This version

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

Download Files

Download Files

TODO: Brief introduction on what you do with files - including link to relevant help section.

File Name & Checksum SHA256 Checksum Help Version File Type Upload Date (11.0 kB) Copy SHA256 Checksum SHA256 Source Oct 16, 2016

Supported By

WebFaction WebFaction Technical Writing Elastic Elastic Search Pingdom Pingdom Monitoring Dyn Dyn DNS HPE HPE Development Sentry Sentry Error Logging CloudAMQP CloudAMQP RabbitMQ Heroku Heroku PaaS Kabu Creative Kabu Creative UX & Design Fastly Fastly CDN DigiCert DigiCert EV Certificate Rackspace Rackspace Cloud Servers DreamHost DreamHost Log Hosting