python-testdata
Advanced tools
| from collections import defaultdict | ||
| from .errors import UnmetDependentFields | ||
| from .base import Factory, DependentField | ||
| class ChildrenTree(object): | ||
| def __init__(self): | ||
| self._tree = defaultdict(dict) | ||
| def load_bases(self, bases): | ||
| ChildrenTree._fuse_tree(self._tree, ChildrenTree._load_bases(bases)) | ||
| @staticmethod | ||
| def _load_bases(bases): | ||
| all_tree = ChildrenTree() | ||
| for base in bases: | ||
| if Factory == base: # means we reached the DictFactory cls | ||
| return ChildrenTree() | ||
| current_tree = base._child_factory_tree + ChildrenTree._load_bases(base.__bases__) | ||
| all_tree += current_tree | ||
| return all_tree | ||
| @staticmethod | ||
| def _fuse_tree(a, b): | ||
| for key in b.keys(): | ||
| a[key].update(b[key]) | ||
| def __add__(self, other): | ||
| new_tree = self._tree.copy() | ||
| ChildrenTree._fuse_tree(new_tree, other._tree) | ||
| new_children_tree = ChildrenTree() | ||
| new_children_tree._tree = new_tree | ||
| return new_children_tree | ||
| def __iadd__(self, o): | ||
| ChildrenTree._fuse_tree(self._tree, o._tree) | ||
| return self | ||
| def __repr__(self): | ||
| return '{}'.format(repr(self._tree)) | ||
| def keys(self): | ||
| return self._tree.keys() | ||
| def __getitem__(self, key): | ||
| return self._tree.__getitem__(key) | ||
| def update(self, factories_dct): | ||
| dependent_factories = {} | ||
| for key, value in factories_dct.iteritems(): | ||
| if issubclass(type(value), DependentField): | ||
| dependent_factories[key] = value | ||
| continue | ||
| if issubclass(type(value), Factory): | ||
| self._tree[0][key] = value | ||
| self._build_dependency_tree(dependent_factories) | ||
| def _build_dependency_tree(self, dependent_factories): | ||
| leftover_factories = set(dependent_factories.keys()) | ||
| unplaced_fields = set() | ||
| while leftover_factories: | ||
| unplaced_fields = self._build_tree(leftover_factories, dependent_factories) | ||
| if unplaced_fields == leftover_factories: # means that no placement has happened! | ||
| raise UnmetDependentFields("The fields: {} - depend on fields that aren't defined!".format(unplaced_fields)) | ||
| leftover_factories = unplaced_fields | ||
| def _build_tree(self, leftover_factory_names, all_dependent_factories): | ||
| unplaced_fields = set([]) | ||
| for factory_name in leftover_factory_names: | ||
| needed_factories = set(all_dependent_factories[factory_name].depending_field_names) # we need to know which fields are needed | ||
| for generation in sorted(self._tree.keys()): # will give the available generations | ||
| needed_factories -= set(self._tree[generation].keys()) # the factory names available in this generation | ||
| if not needed_factories: | ||
| break | ||
| if needed_factories: # if after traversing the generations, we didn't find all the dependencies, we save this for later | ||
| unplaced_fields.add(factory_name) | ||
| else: | ||
| self._tree[generation + 1][factory_name] = all_dependent_factories[factory_name] | ||
| return unplaced_fields |
+29
-4
| Metadata-Version: 1.1 | ||
| Name: python-testdata | ||
| Version: 1.0.3 | ||
| Version: 1.0.5 | ||
| Summary: A small package that helps generate content to fill databases for tests. | ||
@@ -98,5 +98,5 @@ Home-page: http://github.com/arieb/python-testdata | ||
| We also have factories that allow us to generate different data distributed by different percentage, for example, | ||
| lets say we want to create an 'Job', that will have an assigned user field, a state field and a description field. | ||
| we want the state to be 'pending' in 90% of dictionaries and 'error' the rest of the time. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', else it should be 'admin'. | ||
| lets say we want to create a 'Job', that will have an assigned user field, a state field and a description field. | ||
| We want the state to be 'pending' in 90% of dictionaries and 'error' in the rest of them. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', or else it should be 'admin'. | ||
@@ -123,2 +123,26 @@ ```python | ||
| In version 1.0.5 we extended the DictFactory to support passing additional factories, or overriding existing factories, for a specific instance. | ||
| Lets take for example our 'User' example from the begining. | ||
| ```python | ||
| import testdata | ||
| class Users(testdata.DictFactory): | ||
| id = testdata.CountingFactory(10) | ||
| firstname = testdata.FakeDataFactory('firstName') | ||
| lastname = testdata.FakeDataFactory('lastName') | ||
| address = testdata.FakeDataFactory('address') | ||
| age = testdata.RandomInteger(10, 30) | ||
| gender = testdata.RandomSelection(['female', 'male']) | ||
| ``` | ||
| But lets override it so the 'firstname' always returns John, and make the age be a random integer between 40 and 60 and add an 'email' field. | ||
| ```python | ||
| for user in Users(firstname=testdata.Constant('John'), age=testdata.RandomInteger(40, 60), email=testdata.FakeDataFactory('email')).generate(10): # let say we only want 10 users | ||
| print user | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 54, 'email': 'hazle.wehner@brekke.com', 'lastname': 'Willms', 'address': '245 Pfeffer Light Apt. 309\nEast Audieside, IN 11931', 'id': 10} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 47, 'email': 'mariam25@gmail.com', 'lastname': 'Ratke', 'address': '98710 Freddy Gateway\nDelilahborough, GU 50849', 'id': 11} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 55, 'email': 'tyler22@yahoo.com', 'lastname': 'Cormier', 'address': '432 Block Locks Apt. 547\nNew Estel, NJ 54026', 'id': 12} | ||
| # or more likely you'd want to insert them into your favorite database (MongoDB, ElasticSearch, ..) | ||
| ``` | ||
| ## Factories | ||
@@ -134,2 +158,3 @@ See the Factorie's Docstrings for more examples and doctests. | ||
| | Callable | Gets a callable object as an argument and returns the result of calling the object on every iteration | | ||
| | DependentCallable | Gets a callable object as an argument and returns the result of calling the object passing the defined fields as arguments on every iteration | | ||
| | ClonedField | A factory that copies the value of another factory. | | ||
@@ -136,0 +161,0 @@ #### Dates |
| Metadata-Version: 1.1 | ||
| Name: python-testdata | ||
| Version: 1.0.3 | ||
| Version: 1.0.5 | ||
| Summary: A small package that helps generate content to fill databases for tests. | ||
@@ -98,5 +98,5 @@ Home-page: http://github.com/arieb/python-testdata | ||
| We also have factories that allow us to generate different data distributed by different percentage, for example, | ||
| lets say we want to create an 'Job', that will have an assigned user field, a state field and a description field. | ||
| we want the state to be 'pending' in 90% of dictionaries and 'error' the rest of the time. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', else it should be 'admin'. | ||
| lets say we want to create a 'Job', that will have an assigned user field, a state field and a description field. | ||
| We want the state to be 'pending' in 90% of dictionaries and 'error' in the rest of them. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', or else it should be 'admin'. | ||
@@ -123,2 +123,26 @@ ```python | ||
| In version 1.0.5 we extended the DictFactory to support passing additional factories, or overriding existing factories, for a specific instance. | ||
| Lets take for example our 'User' example from the begining. | ||
| ```python | ||
| import testdata | ||
| class Users(testdata.DictFactory): | ||
| id = testdata.CountingFactory(10) | ||
| firstname = testdata.FakeDataFactory('firstName') | ||
| lastname = testdata.FakeDataFactory('lastName') | ||
| address = testdata.FakeDataFactory('address') | ||
| age = testdata.RandomInteger(10, 30) | ||
| gender = testdata.RandomSelection(['female', 'male']) | ||
| ``` | ||
| But lets override it so the 'firstname' always returns John, and make the age be a random integer between 40 and 60 and add an 'email' field. | ||
| ```python | ||
| for user in Users(firstname=testdata.Constant('John'), age=testdata.RandomInteger(40, 60), email=testdata.FakeDataFactory('email')).generate(10): # let say we only want 10 users | ||
| print user | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 54, 'email': 'hazle.wehner@brekke.com', 'lastname': 'Willms', 'address': '245 Pfeffer Light Apt. 309\nEast Audieside, IN 11931', 'id': 10} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 47, 'email': 'mariam25@gmail.com', 'lastname': 'Ratke', 'address': '98710 Freddy Gateway\nDelilahborough, GU 50849', 'id': 11} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 55, 'email': 'tyler22@yahoo.com', 'lastname': 'Cormier', 'address': '432 Block Locks Apt. 547\nNew Estel, NJ 54026', 'id': 12} | ||
| # or more likely you'd want to insert them into your favorite database (MongoDB, ElasticSearch, ..) | ||
| ``` | ||
| ## Factories | ||
@@ -134,2 +158,3 @@ See the Factorie's Docstrings for more examples and doctests. | ||
| | Callable | Gets a callable object as an argument and returns the result of calling the object on every iteration | | ||
| | DependentCallable | Gets a callable object as an argument and returns the result of calling the object passing the defined fields as arguments on every iteration | | ||
| | ClonedField | A factory that copies the value of another factory. | | ||
@@ -136,0 +161,0 @@ #### Dates |
@@ -12,2 +12,3 @@ MANIFEST.in | ||
| testdata/base.py | ||
| testdata/childrentree.py | ||
| testdata/dictionary.py | ||
@@ -14,0 +15,0 @@ testdata/errors.py |
+28
-3
@@ -90,5 +90,5 @@ python-testdata | ||
| We also have factories that allow us to generate different data distributed by different percentage, for example, | ||
| lets say we want to create an 'Job', that will have an assigned user field, a state field and a description field. | ||
| we want the state to be 'pending' in 90% of dictionaries and 'error' the rest of the time. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', else it should be 'admin'. | ||
| lets say we want to create a 'Job', that will have an assigned user field, a state field and a description field. | ||
| We want the state to be 'pending' in 90% of dictionaries and 'error' in the rest of them. In addition, we want that if the 'state' field is | ||
| 'error' the assigned user will be 'support', or else it should be 'admin'. | ||
@@ -115,2 +115,26 @@ ```python | ||
| In version 1.0.5 we extended the DictFactory to support passing additional factories, or overriding existing factories, for a specific instance. | ||
| Lets take for example our 'User' example from the begining. | ||
| ```python | ||
| import testdata | ||
| class Users(testdata.DictFactory): | ||
| id = testdata.CountingFactory(10) | ||
| firstname = testdata.FakeDataFactory('firstName') | ||
| lastname = testdata.FakeDataFactory('lastName') | ||
| address = testdata.FakeDataFactory('address') | ||
| age = testdata.RandomInteger(10, 30) | ||
| gender = testdata.RandomSelection(['female', 'male']) | ||
| ``` | ||
| But lets override it so the 'firstname' always returns John, and make the age be a random integer between 40 and 60 and add an 'email' field. | ||
| ```python | ||
| for user in Users(firstname=testdata.Constant('John'), age=testdata.RandomInteger(40, 60), email=testdata.FakeDataFactory('email')).generate(10): # let say we only want 10 users | ||
| print user | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 54, 'email': 'hazle.wehner@brekke.com', 'lastname': 'Willms', 'address': '245 Pfeffer Light Apt. 309\nEast Audieside, IN 11931', 'id': 10} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 47, 'email': 'mariam25@gmail.com', 'lastname': 'Ratke', 'address': '98710 Freddy Gateway\nDelilahborough, GU 50849', 'id': 11} | ||
| #{'firstname': 'John', 'gender': 'male', 'age': 55, 'email': 'tyler22@yahoo.com', 'lastname': 'Cormier', 'address': '432 Block Locks Apt. 547\nNew Estel, NJ 54026', 'id': 12} | ||
| # or more likely you'd want to insert them into your favorite database (MongoDB, ElasticSearch, ..) | ||
| ``` | ||
| ## Factories | ||
@@ -126,2 +150,3 @@ See the Factorie's Docstrings for more examples and doctests. | ||
| | Callable | Gets a callable object as an argument and returns the result of calling the object on every iteration | | ||
| | DependentCallable | Gets a callable object as an argument and returns the result of calling the object passing the defined fields as arguments on every iteration | | ||
| | ClonedField | A factory that copies the value of another factory. | | ||
@@ -128,0 +153,0 @@ #### Dates |
+32
-0
@@ -145,2 +145,34 @@ from copy import deepcopy | ||
| class DependentCallable(DependentField): | ||
| """ | ||
| Allows us to call a callable object (like a function), and pass it | ||
| other fields as parameters. | ||
| :param callable_obj: the object to __call__() on each iteration | ||
| :param fields: a list of fields that their values should be passed as parameters on each call. | ||
| Example, | ||
| >>> import testdata | ||
| >>> def sum_fields(x, y): | ||
| ... return x + y | ||
| >>> class A(testdata.DictFactory): | ||
| ... x = testdata.CountingFactory(100) | ||
| ... y = testdata.CountingFactory(1) | ||
| ... sum = DependentCallable(sum_fields, ['x', 'y']) | ||
| >>> for i in A().generate(4): | ||
| ... print i['x'], i['y'], i['sum'] | ||
| 100 1 101 | ||
| 101 2 103 | ||
| 102 3 105 | ||
| 103 4 107 | ||
| """ | ||
| def __init__(self, callable_obj, fields=[]): | ||
| super(DependentCallable, self).__init__(fields) | ||
| self._callable_obj = callable_obj | ||
| self._fields = fields | ||
| def __call__(self): | ||
| super(DependentCallable, self).__call__() | ||
| return self._callable_obj(**self.depending_fields) | ||
| class ClonedField(DependentField): | ||
@@ -147,0 +179,0 @@ """ |
@@ -1,2 +0,2 @@ | ||
| from copy import deepcopy | ||
| from copy import deepcopy, copy | ||
| from .base import Factory | ||
@@ -25,5 +25,6 @@ from .metaclasses import DictFactoryBuilder | ||
| def __init__(self): | ||
| def __init__(self, **factories): | ||
| super(DictFactory, self).__init__() | ||
| self._child_factories = deepcopy(self._child_factory_tree) | ||
| self._child_factories.update(factories) | ||
| self._oldest_generation = max(self._child_factories.keys()) | ||
@@ -36,3 +37,3 @@ | ||
| def _iter_child_factories(self): | ||
| child_factories = self._child_factories.copy() | ||
| child_factories = copy(self._child_factories) | ||
| for generation in child_factories.keys(): | ||
@@ -39,0 +40,0 @@ for key in child_factories[generation].keys(): |
@@ -8,4 +8,3 @@ class TestDataError(Exception): pass | ||
| class UnmetDependentFields(TestDataError): pass | ||
| class NoFactoriesDefined(TestDataError): pass | ||
| class NoFactoriesProvided(TestDataError): pass | ||
| class InvalidTotalPrecentage(TestDataError): pass |
@@ -1,6 +0,4 @@ | ||
| from collections import defaultdict | ||
| from .errors import UnmetDependentFields, NoFactoriesDefined | ||
| from .base import Factory | ||
| from .childrentree import ChildrenTree | ||
| from .base import Factory, DependentField | ||
| class DictFactoryBuilder(type): | ||
@@ -11,7 +9,5 @@ """ | ||
| def __new__(meta, name, bases, dct): | ||
| if name == "DictFactory": # we only modify the children of DictFactory, not the DictFactory itself | ||
| return super(DictFactoryBuilder, meta).__new__(meta, name, bases, dct) | ||
| _child_factory_tree = DictFactoryBuilder._collect_bases_children_trees(bases) | ||
| DictFactoryBuilder._build_children_tree(_child_factory_tree, dct) | ||
| _child_factory_tree = ChildrenTree() | ||
| _child_factory_tree.load_bases(bases) | ||
| _child_factory_tree.update(dct) | ||
| DictFactoryBuilder._clean_factories(dct) | ||
@@ -22,20 +18,2 @@ dct["_child_factory_tree"] = _child_factory_tree | ||
| @staticmethod | ||
| def _collect_bases_children_trees(bases): | ||
| all_tree = defaultdict(dict) | ||
| for base in bases: | ||
| if Factory in base.__bases__: # means we reached the DictFactory cls | ||
| return defaultdict(dict) | ||
| current_tree = DictFactoryBuilder._fuse_child_trees( | ||
| base._child_factory_tree, | ||
| DictFactoryBuilder._collect_bases_children_trees(base.__bases__)) | ||
| DictFactoryBuilder._fuse_child_trees(all_tree, current_tree) | ||
| return all_tree | ||
| @staticmethod | ||
| def _fuse_child_trees(p,q): | ||
| for key in q.keys(): | ||
| p[key].update(q[key]) | ||
| return p | ||
| @staticmethod | ||
| def _clean_factories(dct): | ||
@@ -47,41 +25,2 @@ """ | ||
| if issubclass(type(dct[key]), Factory): | ||
| del dct[key] | ||
| @staticmethod | ||
| def _build_children_tree(child_tree, dct): | ||
| dependent_factories = {} | ||
| for key, value in dct.iteritems(): | ||
| if issubclass(type(value), DependentField): | ||
| dependent_factories[key] = value | ||
| continue | ||
| if issubclass(type(value), Factory): | ||
| child_tree[0][key] = value | ||
| if not child_tree: | ||
| raise NoFactoriesDefined("DictFactory needs to contain at least one Factory") | ||
| DictFactoryBuilder._build_dependency_tree(child_tree, dependent_factories) | ||
| @staticmethod | ||
| def _build_dependency_tree(child_tree, dependent_factories): | ||
| leftover_factories = set(dependent_factories.keys()) | ||
| unplaced_fields = set() | ||
| while leftover_factories: | ||
| unplaced_fields = DictFactoryBuilder._build_tree(child_tree, leftover_factories, dependent_factories) | ||
| if unplaced_fields == leftover_factories: # means that no placement has happened! | ||
| raise UnmetDependentFields("The fields: {} - depend on fields that aren't defined!".format(unplaced_fields)) | ||
| leftover_factories = unplaced_fields | ||
| @staticmethod | ||
| def _build_tree(child_tree, leftover_factory_names, all_dependent_factories): | ||
| unplaced_fields = set([]) | ||
| for factory_name in leftover_factory_names: | ||
| needed_factories = set(all_dependent_factories[factory_name].depending_field_names) # we need to know which fields are needed | ||
| for generation in sorted(child_tree.keys()): # will give the available generations | ||
| needed_factories -= set(child_tree[generation].keys()) # the factory names available in this generation | ||
| if not needed_factories: | ||
| break | ||
| if needed_factories: # if after traversing the generations, we didn't find all the dependencies, we save this for later | ||
| unplaced_fields.add(factory_name) | ||
| else: | ||
| child_tree[generation + 1][factory_name] = all_dependent_factories[factory_name] | ||
| return unplaced_fields | ||
| dct.pop(key) |
+1
-1
@@ -1,1 +0,1 @@ | ||
| 1.0.3 | ||
| 1.0.5 |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
64183
11.95%27
3.85%784
5.52%