Skip to main content

A library to easily use design patterns with Python

Project description

Pytterns

Pytterns is a Python library that provides an easy and intuitive way to use Design Patterns in your Python code.

Installation

To install Pytterns, use the following command:

pip install pytterns

Implemented Design Patterns

Strategy

The Strategy pattern allows the definition of a family of algorithms, encapsulating them and making them interchangeable at runtime.

Usage Example

from pytterns import strategy, load

@strategy("payment")
class CreditCardPayment:
    def check(self, method):
        return method == "credit_card"
    
    def execute(self):
        return "Processing payment via Credit Card"

@strategy("payment")
class PayPalPayment:
    def check(self, method):
        return method == "paypal"
    
    def execute(self):
        return "Processing payment via PayPal"

# Selecting strategy based on payment method
payment_strategy = load.strategy("payment").check("paypal").execute()
print(payment_strategy)  # Output: Processing payment via PayPal

Chain of Responsibility

Chain of Responsibility is a behavioral design pattern that allows a request to be processed by a sequence of handlers. The order of the handlers will be defined in the decorator/annotation itself.

Usage Example

from pytterns import chain, load

@chain("auth_chain", order=1)
class Authenticator:
    def handle(self, request):
        if not request.get("authenticated", False):
            print("Authentication failed!")
            return  # Stops the chain here
        print("User authenticated")

@chain("auth_chain", order=2)
class Authorizer:
    def handle(self, request):
        if request.get("role") != "admin":
            print("Authorization failed!")
            return
        print("User authorized")

@chain("auth_chain", order=3)
class Logger:
    def handle(self, request):
        print(f"Logging request: {request}")

# Loading the chain
auth_chain = load.chain("auth_chain")

# Simulating requests
print("=== Request 1 ===")
auth_chain.handle({"authenticated": False, "role": "admin"})

print("\n=== Request 2 ===")
auth_chain.handle({"authenticated": True, "role": "user"})

print("\n=== Request 3 ===")
auth_chain.handle({"authenticated": True, "role": "admin"})

Expected Output

=== Request 1 ===
Authentication failed!

=== Request 2 ===
User authenticated
Authorization failed!

=== Request 3 ===
User authenticated
User authorized
Logging request: {'authenticated': True, 'role': 'admin'}

Observer

The Observer pattern lets you register listeners for named events and notify them later.

Usage Example

from pytterns import observer, load

@observer('user.created')
def send_welcome_email(user_id):
    print(f"send welcome to {user_id}")
    return 'email_sent'

@observer('user.created')
class AuditListener:
    def update(self, user_id):
        print(f"audit: created {user_id}")
        return 'audited'

# Notify all listeners
results = load.observer('user.created').notify(42)
# results is a list of (success, value) tuples for each listener
print(results)  # [(True, 'email_sent'), (True, 'audited')]

Behavior Notes

  • Register listeners using @observer(event); you can register plain callables (functions) or classes (the decorator will instantiate the class).
  • load.observer(event).notify(*args, **kwargs) calls all listeners in registration order.
  • The notify method returns a list of (success, result) tuples. If a listener raised an exception, that entry will be (False, exception) and other listeners will still be called.

Factory

Register classes under a factory name and create instances dynamically.

Usage Example

from pytterns import factory, load

@factory('db')
class MyDB:
    def __init__(self, dsn):
        self.dsn = dsn

inst = load.factory('db').create('sqlite:///:memory:')
print(inst.dsn)

Command

Map names to executable handlers (functions or classes with execute), and run them via the loader.

Usage Example

from pytterns import command, load

@command('say')
def say(msg):
    print(msg)

@command('say')
class Loud:
    def execute(self, msg):
        print(msg.upper())

load.command('say').execute('hello')

Notes on Factory and Command

  • FactoryLoader.create(..., index=0) lets you pick which registered class to instantiate (default 0).

Observer & Command removal

  • load.observer('evt').unsubscribe(listener) removes a listener (pass the exact function or the class to remove its instances).
  • load.command('name').unregister(handler) removes a handler (exact callable or class).

Quick API reference

  • Registration decorators: strategy, chain, observer, factory, command.
  • Loaders: use load.strategy(name), load.chain(name), load.observer(event), load.factory(name), load.command(name) to retrieve registered items.

Common methods:

  • create(*args, index=0, **kwargs) (factory loader) — instantiate a registered class.
  • notify(*args, **kwargs) (observer loader) — call all listeners and return list of (success, result) tuples.
  • handle(request) (chain loader) — run the chain handlers in order.
  • execute(*args, **kwargs) (command loader) — execute registered command handler(s).

Running tests

Install test dependencies and run pytest:

pip install -r requirements.txt
pytest -q

Continuous Integration & Automatic Releases

When code is merged into the main branch, a GitHub Actions workflow will run the test suite and, if it succeeds, automatically create and push the next patch tag (for example v0.1.1). That tag then triggers the existing publish workflow which builds and uploads the package to PyPI.

Behavior details:

  • The auto-release workflow looks for the latest tag matching v* and increments the patch number. If no tag is found it falls back to v0.1.0.
  • The workflow runs tests first; tags are only created when the test job succeeds.
  • The workflow uses the repository's GITHUB_TOKEN to create and push the tag. If you need a different behavior (for example signing tags or pushing with a separate deploy key), update the workflow or provide a different secret.

Options if you prefer manual control:

  • Disable or edit .github/workflows/auto-release.yml.
  • Create and push a tag locally: git tag -a v0.1.1 -m "v0.1.1" && git push origin v0.1.1.

Using a Personal Access Token (RELEASE_PAT) to allow automated tag pushes

If you want the workflow to push tags in a way that GitHub treats as a normal user push (so it's allowed by branch protection and triggers any workflows that listen to tag pushes), create a Personal Access Token (PAT) with the repo scope and add it as a repository secret named RELEASE_PAT.

Steps:

  1. Create a PAT:

    • Go to https://github.com/settings/tokens
    • Click "Generate new token" (classic) or use a fine-grained token with repository write permissions.
    • Give it a descriptive name (e.g., "pytterns-release-bot"), set the expiration you want, and grant repo (or the minimum needed) scopes.
    • Copy the token (you won't see it again).
  2. Add the secret to the repository:

    • On GitHub, open the repository → Settings → Secrets and variables → Actions → New repository secret.
    • Name it RELEASE_PAT and paste the token value.

Once RELEASE_PAT is present, the publish.yml workflow will use it automatically to push tags and publication will proceed without manual tagging.

Contributing

Contributions are welcome. Please open an issue for discussion and submit a PR with tests for new behavior. Keep formatting consistent with the project style.

License

See the LICENCE file in the repository for license terms.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytterns-0.1.4.tar.gz (18.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytterns-0.1.4-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file pytterns-0.1.4.tar.gz.

File metadata

  • Download URL: pytterns-0.1.4.tar.gz
  • Upload date:
  • Size: 18.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.23

File hashes

Hashes for pytterns-0.1.4.tar.gz
Algorithm Hash digest
SHA256 f995d124f4f49cfc88c4bf1cd9ffb5e648e78345a70104ccea50dc50e90aff9f
MD5 f9420cdfb7c4bdcb2478938398b43340
BLAKE2b-256 5292ac33d490993adb6caf9fa8823b2ac7658c34644c2d5ff64d062baa843540

See more details on using hashes here.

File details

Details for the file pytterns-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: pytterns-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.23

File hashes

Hashes for pytterns-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 95cd47dd0b5b589e2c420f8685451f31ffeee3e6ba8a80fffe4fb9dc258dce8d
MD5 f4dd4781c0d2afd914d577d40db36ee5
BLAKE2b-256 a0754a64ecdb6ee91e8c9f3f45b804f415bbba5a97ce6ed84f65064e51ab5dbe

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page