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()
def test_commit_group(): ref_collection = Collection([(1,2),(1,3),(2,4)]) # index-based grouping col = ref_collection.factory(ref_collection) transaction = Transaction(col) transaction._commit_group([0]) assert len(col) == 2 assert set(col[0].children) < set(ref_collection) # aka col2 = ref_collection.factory(ref_collection) col2.transaction.add('group', 0) assert set(col2) == set(col) # fn based grouping col = ref_collection.factory(ref_collection) transaction = Transaction(col) transaction._commit_group([lambda x: x[1]]) assert len(col) == 3 assert set(col[0].children) < set(ref_collection) # aka col2 = ref_collection.factory(ref_collection) col2.transaction.add('group', lambda x: x[1]) assert set(col2) == set(col)
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 test_commit_error(): transaction = Transaction(Collection([[1]])) # test long way transaction.begin() transaction.add('group', 1) # <- Column "1" doesn't exist. raises(DependencyResolutionError, transaction.commit) # shorter test (but this does the same thing) raises(DependencyResolutionError, transaction.add, 'group', 1) # check exception error message try: transaction.add('group', 1) except DependencyResolutionError, e: assert str(e) == "[('group', 'list index out of range')]"
def test_commit_filter(): ref_collection = Collection([(1,2),(2,3),(3,4)]) # test impossible filter col1 = ref_collection.factory(ref_collection) t1 = Transaction(col1) t1._commit_filter([lambda x: False]) assert col1.data == [] # something more reasonable col2 = ref_collection.factory(ref_collection) t2 = Transaction(col2) t2._commit_filter([lambda x: x[0] > 2]) assert len(col2) == 1 assert set(col2) < set(ref_collection)
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)