aboutsummaryrefslogtreecommitdiff
path: root/factory
diff options
context:
space:
mode:
Diffstat (limited to 'factory')
-rw-r--r--factory/__init__.py4
-rw-r--r--factory/base.py88
-rw-r--r--factory/containers.py4
-rw-r--r--factory/declarations.py107
-rw-r--r--factory/utils.py32
5 files changed, 41 insertions, 194 deletions
diff --git a/factory/__init__.py b/factory/__init__.py
index d2267f0..3753461 100644
--- a/factory/__init__.py
+++ b/factory/__init__.py
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-__version__ = '1.2.0'
+__version__ = '1.1.5' # Remember to change in setup.py as well!
__author__ = 'Raphaƫl Barrois <raphael.barrois@polytechnique.org>'
from base import (
@@ -60,9 +60,7 @@ from declarations import (
SelfAttribute,
ContainerAttribute,
SubFactory,
- CircularSubFactory,
PostGeneration,
- PostGenerationMethodCall,
RelatedFactory,
lazy_attribute,
diff --git a/factory/base.py b/factory/base.py
index a3d91b0..c1dbd98 100644
--- a/factory/base.py
+++ b/factory/base.py
@@ -32,12 +32,7 @@ CREATE_STRATEGY = 'create'
STUB_STRATEGY = 'stub'
# Creation functions. Use Factory.set_creation_function() to set a creation function appropriate for your ORM.
-def DJANGO_CREATION(class_to_create, **kwargs):
- warnings.warn(
- "Factories defaulting to Django's Foo.objects.create() is deprecated, "
- "and will be removed in the future. Please inherit from "
- "factory.DjangoModelFactory instead.", PendingDeprecationWarning, 6)
- return class_to_create.objects.create(**kwargs)
+DJANGO_CREATION = lambda class_to_create, **kwargs: class_to_create.objects.create(**kwargs)
# Building functions. Use Factory.set_building_function() to set a building functions appropriate for your ORM.
NAIVE_BUILD = lambda class_to_build, **kwargs: class_to_build(**kwargs)
@@ -187,7 +182,7 @@ class FactoryMetaClass(BaseFactoryMetaClass):
FACTORY_CLASS_DECLARATION,
associated_name,
class_name,
- ), DeprecationWarning, 3)
+ ), PendingDeprecationWarning)
return getattr(factory_module, associated_name)
@@ -266,9 +261,6 @@ class BaseFactory(object):
# class.
_base_factory = None
- # List of arguments that should be passed as *args instead of **kwargs
- FACTORY_ARG_PARAMETERS = ()
-
@classmethod
def _setup_next_sequence(cls):
"""Set up an initial sequence value for Sequence attributes.
@@ -325,36 +317,6 @@ class BaseFactory(object):
return getattr(cls, CLASS_ATTRIBUTE_DECLARATIONS).copy(extra_defs)
@classmethod
- def _build(cls, target_class, *args, **kwargs):
- """Actually build an instance of the target_class.
-
- Customization point, will be called once the full set of args and kwargs
- has been computed.
-
- Args:
- target_class (type): the class for which an instance should be
- built
- args (tuple): arguments to use when building the class
- kwargs (dict): keyword arguments to use when building the class
- """
- return target_class(*args, **kwargs)
-
- @classmethod
- def _create(cls, target_class, *args, **kwargs):
- """Actually create an instance of the target_class.
-
- Customization point, will be called once the full set of args and kwargs
- has been computed.
-
- Args:
- target_class (type): the class for which an instance should be
- created
- args (tuple): arguments to use when creating the class
- kwargs (dict): keyword arguments to use when creating the class
- """
- return target_class(*args, **kwargs)
-
- @classmethod
def build(cls, **kwargs):
"""Build an instance of the associated class, with overriden attrs."""
raise cls.UnsupportedStrategy()
@@ -500,7 +462,7 @@ class Factory(BaseFactory):
# Customizing 'create' strategy, using a tuple to keep the creation function
# from turning it into an instance method.
- _creation_function = (None,)
+ _creation_function = (DJANGO_CREATION,)
@classmethod
def set_creation_function(cls, creation_function):
@@ -512,10 +474,6 @@ class Factory(BaseFactory):
which an instance will be created. The value of the various
fields are passed as keyword arguments.
"""
- warnings.warn(
- "Use of factory.set_creation_function is deprecated, and will be "
- "removed in the future. Please override Factory._create() instead.",
- PendingDeprecationWarning, 2)
cls._creation_function = (creation_function,)
@classmethod
@@ -527,22 +485,11 @@ class Factory(BaseFactory):
an instance will be created, and keyword arguments for the value
of the fields of the instance.
"""
- creation_function = cls._creation_function[0]
- if creation_function:
- return creation_function
- elif cls._create.__func__ == Factory._create.__func__:
- # Backwards compatibility.
- # Default creation_function and default _create() behavior.
- # The best "Vanilla" _create detection algorithm I found is relying
- # on actual method implementation (otherwise, make_factory isn't
- # detected as 'default').
- return DJANGO_CREATION
- else:
- return creation_function
+ return cls._creation_function[0]
# Customizing 'build' strategy, using a tuple to keep the creation function
# from turning it into an instance method.
- _building_function = (None,)
+ _building_function = (NAIVE_BUILD,)
@classmethod
def set_building_function(cls, building_function):
@@ -554,10 +501,6 @@ class Factory(BaseFactory):
which an instance will be built. The value of the various
fields are passed as keyword arguments.
"""
- warnings.warn(
- "Use of factory.set_building_function is deprecated, and will be "
- "removed in the future. Please override Factory._build() instead.",
- PendingDeprecationWarning, 2)
cls._building_function = (building_function,)
@classmethod
@@ -579,23 +522,10 @@ class Factory(BaseFactory):
create: bool, whether to create or to build the object
**kwargs: arguments to pass to the creation function
"""
- target_class = getattr(cls, CLASS_ATTRIBUTE_ASSOCIATED_CLASS)
-
- # Extract *args from **kwargs
- args = tuple(kwargs.pop(key) for key in cls.FACTORY_ARG_PARAMETERS)
-
if create:
- # Backwards compatibility
- creation_function = cls.get_creation_function()
- if creation_function:
- return creation_function(target_class, *args, **kwargs)
- return cls._create(target_class, *args, **kwargs)
+ return cls.get_creation_function()(getattr(cls, CLASS_ATTRIBUTE_ASSOCIATED_CLASS), **kwargs)
else:
- # Backwards compatibility
- building_function = cls.get_building_function()
- if building_function:
- return building_function(target_class, *args, **kwargs)
- return cls._build(target_class, *args, **kwargs)
+ return cls.get_building_function()(getattr(cls, CLASS_ATTRIBUTE_ASSOCIATED_CLASS), **kwargs)
@classmethod
def _generate(cls, create, attrs):
@@ -651,10 +581,6 @@ class DjangoModelFactory(Factory):
except IndexError:
return 1
- def _create(cls, target_class, *args, **kwargs):
- """Create an instance of the model, and save it to the database."""
- return target_class._default_manager.create(*args, **kwargs)
-
def make_factory(klass, **kwargs):
"""Create a new, simple factory for the given class."""
diff --git a/factory/containers.py b/factory/containers.py
index d50cb71..946fbd3 100644
--- a/factory/containers.py
+++ b/factory/containers.py
@@ -124,7 +124,7 @@ class DeclarationDict(dict):
return False
elif isinstance(value, declarations.OrderedDeclaration):
return True
- return (not name.startswith("_") and not name.startswith("FACTORY_"))
+ return (not name.startswith("_"))
def update_with_public(self, d):
"""Updates the DeclarationDict from a class definition dict.
@@ -167,7 +167,7 @@ class PostGenerationDeclarationDict(DeclarationDict):
class LazyValue(object):
"""Some kind of "lazy evaluating" object."""
- def evaluate(self, obj, containers=()): # pragma: no cover
+ def evaluate(self, obj, containers=()):
"""Compute the value, using the given object."""
raise NotImplementedError("This is an abstract method.")
diff --git a/factory/declarations.py b/factory/declarations.py
index 77000f2..83c32ab 100644
--- a/factory/declarations.py
+++ b/factory/declarations.py
@@ -208,115 +208,46 @@ class ContainerAttribute(OrderedDeclaration):
return self.function(obj, containers)
-class ParameteredAttribute(OrderedDeclaration):
- """Base class for attributes expecting parameters.
+class SubFactory(OrderedDeclaration):
+ """Base class for attributes based upon a sub-factory.
Attributes:
- defaults (dict): Default values for the paramters.
- May be overridden by call-time parameters.
-
- Class attributes:
- CONTAINERS_FIELD (str): name of the field, if any, where container
- information (e.g for SubFactory) should be stored. If empty,
- containers data isn't merged into generate() parameters.
+ defaults (dict): Overrides to the defaults defined in the wrapped
+ factory
+ factory (base.Factory): the wrapped factory
"""
- CONTAINERS_FIELD = '__containers'
-
- def __init__(self, **kwargs):
- super(ParameteredAttribute, self).__init__()
+ def __init__(self, factory, **kwargs):
+ super(SubFactory, self).__init__()
self.defaults = kwargs
+ self.factory = factory
def evaluate(self, create, extra, containers):
"""Evaluate the current definition and fill its attributes.
Uses attributes definition in the following order:
- - values defined when defining the ParameteredAttribute
- - additional values defined when instantiating the containing factory
+ - attributes defined in the wrapped factory class
+ - values defined when defining the SubFactory
+ - additional values defined in attributes
Args:
- create (bool): whether the parent factory is being 'built' or
- 'created'
+ create (bool): whether the subfactory should call 'build' or
+ 'create'
extra (containers.DeclarationDict): extra values that should
- override the defaults
+ override the wrapped factory's defaults
containers (list of LazyStub): List of LazyStub for the chain of
factories being evaluated, the calling stub being first.
"""
+
defaults = dict(self.defaults)
if extra:
defaults.update(extra)
- if self.CONTAINERS_FIELD:
- defaults[self.CONTAINERS_FIELD] = containers
+ defaults['__containers'] = containers
- return self.generate(create, defaults)
-
- def generate(self, create, params): # pragma: no cover
- """Actually generate the related attribute.
-
- Args:
- create (bool): whether the calling factory was in 'create' or
- 'build' mode
- params (dict): parameters inherited from init and evaluation-time
- overrides.
-
- Returns:
- Computed value for the current declaration.
- """
- raise NotImplementedError()
-
-
-class SubFactory(ParameteredAttribute):
- """Base class for attributes based upon a sub-factory.
-
- Attributes:
- defaults (dict): Overrides to the defaults defined in the wrapped
- factory
- factory (base.Factory): the wrapped factory
- """
-
- def __init__(self, factory, **kwargs):
- super(SubFactory, self).__init__(**kwargs)
- self.factory = factory
-
- def get_factory(self):
- """Retrieve the wrapped factory.Factory subclass."""
- return self.factory
-
- def generate(self, create, params):
- """Evaluate the current definition and fill its attributes.
-
- Args:
- create (bool): whether the subfactory should call 'build' or
- 'create'
- params (containers.DeclarationDict): extra values that should
- override the wrapped factory's defaults
- """
- subfactory = self.get_factory()
if create:
- return subfactory.create(**params)
+ return self.factory.create(**defaults)
else:
- return subfactory.build(**params)
-
-
-class CircularSubFactory(SubFactory):
- """Use to solve circular dependencies issues."""
- def __init__(self, module_name, factory_name, **kwargs):
- super(CircularSubFactory, self).__init__(None, **kwargs)
- self.module_name = module_name
- self.factory_name = factory_name
-
- def get_factory(self):
- """Retrieve the factory.Factory subclass.
-
- Its value is cached in the 'factory' attribute, and retrieved through
- the factory_getter callable.
- """
- if self.factory is None:
- factory_class = utils.import_object(
- self.module_name, self.factory_name)
-
- self.factory = factory_class
- return self.factory
+ return self.factory.build(**defaults)
class PostGenerationDeclaration(object):
@@ -352,7 +283,7 @@ class PostGenerationDeclaration(object):
kwargs = utils.extract_dict(extract_prefix, attrs)
return extracted, kwargs
- def call(self, obj, create, extracted=None, **kwargs): # pragma: no cover
+ def call(self, obj, create, extracted=None, **kwargs):
"""Call this hook; no return value is expected.
Args:
diff --git a/factory/utils.py b/factory/utils.py
index e7cdf5f..2fcd7ff 100644
--- a/factory/utils.py
+++ b/factory/utils.py
@@ -57,16 +57,20 @@ def extract_dict(prefix, kwargs, pop=True, exclude=()):
return extracted
+def declength_compare(a, b):
+ """Compare objects, choosing longest first."""
+ if len(a) > len(b):
+ return -1
+ elif len(a) < len(b):
+ return 1
+ else:
+ return cmp(a, b)
+
+
def multi_extract_dict(prefixes, kwargs, pop=True, exclude=()):
"""Extracts all values from a given list of prefixes.
- Extraction will start with longer prefixes.
-
- Args:
- prefixes (str list): the prefixes to use for lookups
- kwargs (dict): the dict from which values should be extracted
- pop (bool): whether to use pop (True) or get (False)
- exclude (iterable): list of prefixed keys that shouldn't be extracted
+ Arguments have the same meaning as for extract_dict.
Returns:
dict(str => dict): a dict mapping each prefix to the dict of extracted
@@ -74,22 +78,10 @@ def multi_extract_dict(prefixes, kwargs, pop=True, exclude=()):
"""
results = {}
exclude = list(exclude)
- for prefix in sorted(prefixes, key=lambda x: -len(x)):
+ for prefix in sorted(prefixes, cmp=declength_compare):
extracted = extract_dict(prefix, kwargs, pop=pop, exclude=exclude)
results[prefix] = extracted
exclude.extend(
['%s%s%s' % (prefix, ATTR_SPLITTER, key) for key in extracted])
return results
-
-
-def import_object(module_name, attribute_name):
- """Import an object from its absolute path.
-
- Example:
- >>> import_object('datetime', 'datetime')
- <type 'datetime.datetime'>
- """
- module = __import__(module_name, {}, {}, [attribute_name], 0)
- return getattr(module, attribute_name)
-