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
notifymethod 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 tov0.1.0. - The workflow runs tests first; tags are only created when the test job succeeds.
- The workflow uses the repository's
GITHUB_TOKENto 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:
-
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).
-
Add the secret to the repository:
- On GitHub, open the repository → Settings → Secrets and variables → Actions → New repository secret.
- Name it
RELEASE_PATand 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f995d124f4f49cfc88c4bf1cd9ffb5e648e78345a70104ccea50dc50e90aff9f
|
|
| MD5 |
f9420cdfb7c4bdcb2478938398b43340
|
|
| BLAKE2b-256 |
5292ac33d490993adb6caf9fa8823b2ac7658c34644c2d5ff64d062baa843540
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95cd47dd0b5b589e2c420f8685451f31ffeee3e6ba8a80fffe4fb9dc258dce8d
|
|
| MD5 |
f4dd4781c0d2afd914d577d40db36ee5
|
|
| BLAKE2b-256 |
a0754a64ecdb6ee91e8c9f3f45b804f415bbba5a97ce6ed84f65064e51ab5dbe
|