def test_begin_twice(): transaction = Transaction(Collection([])) transaction.begin() raises(TransactionAlreadyActiveError, transaction.begin) # no exception raised if transaction is already finished transaction.rollback() transaction.begin()
class Collection(object): """Basic data-collection. Example: >>> Collection(((1,2,3), (2,3,4))) <Collection 2 rows, 3 columns> >>> Collection([]) <Collection Empty> """ def __init__(self, data, **kwargs): self.data = [list(x) for x in data] self.width = 0 if not self.data else len(self.data[0]) self.transaction = Transaction(self) # State vars self._child_collections = {} # Add filters def _common_kwarg_handling(): if 'filter' in kwargs: for filter_fn in kwargs['filter']: self.filter(filter_fn) if 'group' in kwargs: self.group(kwargs['group']) self._handle_kwargs(_common_kwarg_handling, **kwargs) def __len__(self): return len(self.data) def __iter__(self): for idx, record in enumerate(self.data): if idx in self._child_collections: children = self._child_collections[idx] else: children = None yield Record(record, children) def __getitem__(self, key): if key in self._child_collections: return Record(self.data[key], self._child_collections[key]) else: return Record(self.data[key]) def __enter__(self): self.transaction.begin() def __exit__(self, exc_type, exc_value, traceback): if not exc_type: self.transaction.commit() else: self.transaction.rollback() def __repr__(self): if self.data: return ("<Collection %s rows, %s columns>" % (len(self.data), len(self.data[0]))) else: return "<Collection Empty>" def add_formatted_column(self, fmt): """Add new column defined by given format string. The python format specification is used to create the resulting columns. (http://docs.python.org/library/string.html#formatstrings) >>> col = Collection((('a', 'b'), ('c', 'd'))) >>> col.add_formatted_column('{0}, {1}') >>> col[0][2] 'a, b' >>> col[1][2] 'c, d' """ def _do_format(row, collection): return fmt.format(*row) self.transaction.add('new_cols', _do_format) def add_calculated_column(self, calculation): """Add new column whose value is the result of the given calculation.""" # replace place-holders with dictionary refs calculation = calculation.replace('{', 'r[').replace('}', ']') # create new function and return exec '_do_calc = lambda r, c: ' + calculation self.transaction.add('new_cols', _do_calc) def factory(self, data): """Returns method to generate similar collection instance.""" return type(self)(data) def filter(self, fn): """Filter rows that match given function.""" self.transaction.add('filter', fn) def group(self, groupby_list): """Group rows with the same values for the given column positions.""" for groupby in groupby_list: self.transaction.add('group', groupby) def _handle_kwargs(self, common_kwarg_handling, **kwargs): """Handle kwargs passed in on __init__.""" with self: common_kwarg_handling() if 'coerce' in kwargs: for i in xrange(len(self.data)): for idx, type_ in kwargs['coerce'].iteritems(): self.data[i][idx] = type_(self.data[i][idx]) if 'formatted_columns' in kwargs: for col in kwargs['formatted_columns']: self.add_formatted_column(col) if 'calculated_columns' in kwargs: for col in kwargs['calculated_columns']: self.add_calculated_column(col)