Adds design for BatchController

This commit is contained in:
Christopher Neugebauer 2016-04-30 21:42:02 +10:00
parent 162a1f23dd
commit 78a41970ea
2 changed files with 88 additions and 3 deletions

View file

@ -0,0 +1,85 @@
import contextlib
import functools
class BatchController(object):
''' Batches are sets of operations where certain queries for users may be
repeated, but are also unlikely change within the boundaries of the batch.
Batches are keyed per-user. You can mark the edge of the batch with the
``batch`` context manager. If you nest calls to ``batch``, only the
outermost call will have the effect of ending the batch.
Batches store results for functions wrapped with ``memoise``. These results
for the user are flushed at the end of the batch.
If a return for a memoised function has a callable attribute called
``end_batch``, that attribute will be called at the end of the batch.
'''
_user_caches = {}
@classmethod
@contextlib.contextmanager
def batch(cls, user):
''' Marks the entry point for a batch for the given user. '''
pass
# TODO: store nesting count *inside* the cache object. You know it
# makes sense.
@classmethod
def memoise(cls, func):
''' Decorator that stores the result of the stored function in the
user's results cache until the batch completes.
Arguments:
func (callable(user, *a, **k)): The function whose results we want
to store. ``user`` must be the first argument; this is used as
the cache key.
Returns:
callable(user, *a, **k): The memosing version of ``func``.
'''
@functools.wraps(func)
def f(user, *a, **k):
cache = cls.get_cache(user)
if func not in cache:
cache[func] = func(user, *a, **k)
return cache[func]
return f
@classmethod
def get_cache(cls, user):
if user not in cls._user_caches:
return {} # Return blank cache here, we'll just discard :)
return cls._user_caches[user]
'''
TODO: memoise CartController.for_user
TODO: memoise user_remainders (Product, Category)
TODO: memoise _filtered_flags
TODO: memoise FlagCounter.count() (doesn't take user, but it'll do for now)
TODO: memoise _filtered_discounts
Tests:
- Correct nesting behaviour
- do we get different cache objects every time we get a cache in non-batched
contexts?
- do we get the same cache object for nested caches?
- do we get different cache objects when we back out of a batch and enter a
new one
- are cache clears independent for different users?
- ``end_batch`` behaviour for CartController (use for_user *A LOT*)
- discounts not calculated until outermost batch point exits.
- Revision number shouldn't change until outermost batch point exits.
- Make sure memoisation ONLY happens when we're in a batch.
'''

View file

@ -50,7 +50,7 @@ class DiscountController(object):
categories and products. The discounts also list the available quantity categories and products. The discounts also list the available quantity
for this user, not including products that are pending purchase. ''' for this user, not including products that are pending purchase. '''
filtered_clauses = cls._filtered_clauses(user, categories, products) filtered_clauses = cls._filtered_clauses(user)
# clauses that match provided categories # clauses that match provided categories
categories = set(categories) categories = set(categories)
@ -103,8 +103,8 @@ class DiscountController(object):
''' '''
Returns: Returns:
Sequence[discountbase]: All discounts that passed the filter Sequence[DiscountForProduct | DiscountForCategory]: All clauses
function. that passed the filter function.
''' '''