diff options
Diffstat (limited to 'factory')
-rw-r--r-- | factory/__init__.py | 4 | ||||
-rw-r--r-- | factory/base.py | 88 | ||||
-rw-r--r-- | factory/containers.py | 4 | ||||
-rw-r--r-- | factory/declarations.py | 107 | ||||
-rw-r--r-- | factory/utils.py | 32 |
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) - |