Provides hierarchical factory
Project description
What is this package for
This package provides a simple way to build back structure of application. Can be used building composite root as acting a factory for resources, building a fixture factory for tests.
Features
Lazy initialisation
Simple composition and dependency handling
Example - composie root
from baluster import Baluster, placeholders
import psycopg2
class ApplicationRoot(Baluster):
@placeholders.factory
def db(self, root):
# Will be called at the first use
# Will be cached so won't be called again
return psycopg2.connect("dbname=test user=postgres")
@db.close
def _close_db(self, root, db):
db.close()
@placeholders.factory
def cr(self, root):
return self.db.cursor()
@cr.close
def _close_cr(self, root, cr):
cr.close()
def main():
approot = ApplicationRoot()
with approot:
approot.cr.execute('SELECT * FROM user')
# at this point the connection and the cursor has already been closed
Example - async composie root
from baluster import Baluster, placeholders
class AsyncApplicationRoot(Baluster):
@placeholders.factory
async def resource(self, root):
# Will be called at the first use
# Will be cached so won't be called again
return await some_aync_resource()
@db.close
async def _close_resource(self, root, resource):
await resource.close()
def main():
approot = AsyncApplicationRoot()
async with approot:
conn = await approot.resource
await conn.operation(...)
# at this point the resource has already been closed
Example - fixture factory for tests
from baluster import Baluster, placeholders
import psycopg2
class Fixtures(Baluster):
@placeholders.factory
def cr(self, root):
conn = psycopg2.connect("dbname=test user=postgres")
return conn.cursor()
class users(Baluster):
@placeholders.factory
def user(self, root):
root.cr.execute('SELECT * FROM user WHERE id=1')
return User(root.cr.fetchone())
@placeholders.factory
def customer(self, root):
root.cr.execute('SELECT * FROM customer WHERE id=1')
return Customer(root.cr.fetchone())
class orders(Baluster):
@placeholders.factory
def amount(self, root):
return 100
@placeholders.factory
def quantity(self, root):
return 1
@placeholders.factory
def order(self, root):
customer = root.users.customer
created_by = root.users.user
amount = self.amount
# Fictive order object...
return Order(
customer=customer, created_by=created_by,
amount=amount, quantity=quantity
)
@placeholders.factory
def shipped_order(self, root):
order = self.order
order.mark_shipped()
return order
def test_order():
# Demonstrate a few use fictive usecase
# Creating order with defaults
f = Fixtures()
assert f.order.calculated_total_value == 100
assert f.order.shipping_address == f.users.customer.address
# Create new fixtures, but keep some cached data
f2 = f.copy('cr', 'users')
# Set some value
f2.order.amount = 50
f2.order.quantity = 3
assert f2.order.calculated_total_value == 150
# Manage different stage of object life
f3 = f.copy('cr', 'users')
order = f3.shipped_order
with pytest.raises(OrderException):
order.cancel()
# as it is shipped
Installation
Python target: >=3.6
$ pip install baluster
Dependencies
The package is independent, using only the python standard library.
Development
pip install -r requirements-dev.txt
This installs the package in development mode (setup.py develop) and the testing packages. I would like to achive nearly 100% test coverage.
Contribution
I really welcome any comments! I would be happy if you fork my code and create pull requests. For an approved pull request flake8 have to pass just as all of tests.
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.