Skip to content
This repository has been archived by the owner on Jun 18, 2021. It is now read-only.
/ progeny Public archive

Simple (but powerful) management of complex class hierarchies

License

Notifications You must be signed in to change notification settings

GoodRx/progeny

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Progeny

Simple (but powerful) management for complex class hierarchies.

https://travis-ci.org/GoodRx/progeny.svg?branch=master

Motivation

While XYZClass.__subclasses__() returns the children of a class, there is no built-in way to return all descendants. This is the core of Progeny's purpose.

In addition, Progeny provides tools to help manage complex, deeply nested class hierarchies - hiding individual classes, keeping a registry of descendants, etc.

Examples

Basic Usage

import progeny


class NotificationHandler(progeny.Base):
    def send_message(self, *args, **kwargs):
        raise RuntimeError


class CustomerOneNotificationHandler(NotificationHandler):
    def send_message(self, *args, **kwargs):
        # .. business logic ...


class CustomerTwoNotificationHandler(NotificationHandler):
    def send_message(self, *args, **kwargs):
        # .. business logic ...

Now we can iterate over all of the subclasses of NotificationHandler:

def send_newsletter():
    for handler in NotificationHandler.progeny.values():
        handler.send_message('Your attention, please!')

Omitting descendant classes

In some cases, it may be useful to prevent descendant classes from being visible to Progeny.

from progeny import progeny.Base


class NotificationHandler(progeny.Base):
    def send_message(self, *args, **kwargs):
        raise RuntimeError


class EmailNotificationHandler(NotificationHandler):
    __progeny_tracked__ = False

    def send_message(self, *args, **kwargs):
        # .. business logic ..


class SmsNotificationHandler(NotificationHandler):
    __progeny_tracked__ = False

    def send_message(self, *args, **kwargs):
        # .. business logic ..


class CustomerOneNotificationHandler(EmailNotificationHandler):
    pass


class CustomerTwoNotificationHandler(SmsNotificationHandler):
    pass

Any classes with __progeny_tracked__ set to a falsy value during class construction will be ignored by Progeny. It's descendant classes are unaffected:

NotificationHandler.progeny.values()
# {CustomerOneNotificationHandler, CustomerTwoNotificationHandler}

This can be especially handy to conditionally track subclasses based on config context:

class CustomerFooNotificationHandler(EmailNotificationHandler):
    __progeny_tracked__ = config.get('CUSTOMER_FOO_ACTIVE')

Using the descendants registry

Progeny makes it easy to choose between descendant classes at runtime:

from progeny import progeny.Base
from my_app.users import UserLevel


class UploadParser(progeny.Base):
    pass


class FreeUserUploadParser(UploadParser):
    __progeny_key__ = UserLevel.FREE

    def parse_upload(self, *args, **kwargs):
        # .. logic to parse the upload slowly, using shared resources


class PremiumUserUploadParser(UploadParser):
    __progeny_key__ = UserLevel.PAID

    def parse_upload(self, *args, **kwargs):
        # .. logic to parse the upload immediately with dedicated resources
def parse_upload(data):
    UploadParser.progeny.get(session.user.level).parse_upload(data)

Publishing to PyPI

python setup.py sdist bdist_wheel
twine upload "dist/*"

About

Simple (but powerful) management of complex class hierarchies

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages