Skip to content
This repository has been archived by the owner on Oct 22, 2019. It is now read-only.

Commit

Permalink
Consider flake8 rules (#388)
Browse files Browse the repository at this point in the history
* Consider flake8 rules

* Fix tests files

* Fix regressions

* Fix review comments
  • Loading branch information
amureki committed Feb 27, 2019
1 parent a92be3a commit 2f8948c
Show file tree
Hide file tree
Showing 24 changed files with 312 additions and 190 deletions.
21 changes: 21 additions & 0 deletions .editorconfig
@@ -0,0 +1,21 @@
# http://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.py]
indent_style = space
indent_size = 4

[*.{rst,ini}]
indent_style = space
indent_size = 4

[*.{yml,html,xml,xsl,json}]
indent_style = space
indent_size = 2
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Expand Up @@ -7,3 +7,7 @@ repos:
- id: end-of-file-fixer
- id: requirements-txt-fixer
- id: trailing-whitespace
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.6
hooks:
- id: flake8
3 changes: 1 addition & 2 deletions model_mommy/__init__.py
@@ -1,8 +1,7 @@
#coding:utf-8
__version__ = '1.6.0'
__title__ = 'model_mommy'
__author__ = 'Vanderson Mota'
__license__ = 'Apache 2.0'


from .utils import seq
from .utils import seq # NoQA
3 changes: 0 additions & 3 deletions model_mommy/exceptions.py
@@ -1,6 +1,3 @@
#coding:utf-8


class RecipeNotFound(Exception):
pass

Expand Down
81 changes: 59 additions & 22 deletions model_mommy/mommy.py
@@ -1,24 +1,29 @@
# -*- coding: utf-8 -*-
from os.path import dirname, join

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.apps import apps
get_model = apps.get_model
from django.contrib.contenttypes.fields import GenericRelation

from django.db.models.base import ModelBase
from django.db.models import ForeignKey, ManyToManyField, OneToOneField, Field, AutoField, BooleanField, FileField
from django.db.models.fields.related import ReverseManyToOneDescriptor as ForeignRelatedObjectsDescriptor
from django.db.models import (
ForeignKey, ManyToManyField, OneToOneField, Field, AutoField, BooleanField, FileField
)
from django.db.models.fields.related import \
ReverseManyToOneDescriptor as ForeignRelatedObjectsDescriptor
from django.db.models.fields.proxy import OrderWrt

from . import generators
from . import random_gen
from .exceptions import (ModelNotFound, AmbiguousModelName, InvalidQuantityException, RecipeIteratorEmpty,
CustomMommyNotFound, InvalidCustomMommy)
from .exceptions import (
ModelNotFound, AmbiguousModelName, InvalidQuantityException, RecipeIteratorEmpty,
CustomMommyNotFound, InvalidCustomMommy
)
from .utils import import_from_str, import_if_str
from six import string_types, advance_iterator

get_model = apps.get_model

recipes = None

# FIXME: use pkg_resource
Expand All @@ -32,7 +37,8 @@ def _valid_quantity(quantity):
return quantity is not None and (not isinstance(quantity, int) or quantity < 1)


def make(_model, _quantity=None, make_m2m=False, _save_kwargs=None, _refresh_after_create=False, _create_files=False, **attrs):
def make(_model, _quantity=None, make_m2m=False, _save_kwargs=None, _refresh_after_create=False,
_create_files=False, **attrs):
"""
Creates a persisted instance from a given model its associated models.
It fill the fields with random values or you can specify
Expand All @@ -45,10 +51,18 @@ def make(_model, _quantity=None, make_m2m=False, _save_kwargs=None, _refresh_aft

if _quantity:
return [
mommy.make(_save_kwargs=_save_kwargs, _refresh_after_create=_refresh_after_create, **attrs)
mommy.make(
_save_kwargs=_save_kwargs,
_refresh_after_create=_refresh_after_create,
**attrs
)
for _ in range(_quantity)
]
return mommy.make(_save_kwargs=_save_kwargs, _refresh_after_create=_refresh_after_create, **attrs)
return mommy.make(
_save_kwargs=_save_kwargs,
_refresh_after_create=_refresh_after_create,
**attrs
)


def prepare(_model, _quantity=None, _save_related=False, **attrs):
Expand Down Expand Up @@ -76,8 +90,14 @@ def _recipe(name):
def make_recipe(mommy_recipe_name, _quantity=None, **new_attrs):
return _recipe(mommy_recipe_name).make(_quantity=_quantity, **new_attrs)


def prepare_recipe(mommy_recipe_name, _quantity=None, _save_related=False, **new_attrs):
return _recipe(mommy_recipe_name).prepare(_quantity=_quantity, _save_related=_save_related, **new_attrs)
return _recipe(mommy_recipe_name).prepare(
_quantity=_quantity,
_save_related=_save_related,
**new_attrs
)


class ModelFinder(object):
'''
Expand Down Expand Up @@ -169,7 +189,9 @@ def _custom_mommy_class():

for required_function_name in ['make', 'prepare']:
if not hasattr(mommy_class, required_function_name):
raise InvalidCustomMommy('Custom Mommy classes must have a "%s" function' % required_function_name)
raise InvalidCustomMommy(
'Custom Mommy classes must have a "%s" function' % required_function_name
)

return mommy_class
except ImportError:
Expand Down Expand Up @@ -216,13 +238,13 @@ def init_type_mapping(self):
generator = import_if_str(v)
self.type_mapping[field_class] = generator

def make(self, _save_kwargs=None, _refresh_after_create=False,**attrs):
def make(self, _save_kwargs=None, _refresh_after_create=False, **attrs):
"""Creates and persists an instance of the model associated
with Mommy instance."""
params = {
'commit': True,
'commit_related': True,
'_save_kwargs':_save_kwargs,
'_save_kwargs': _save_kwargs,
'_refresh_after_create': _refresh_after_create,
}
params.update(attrs)
Expand All @@ -239,7 +261,8 @@ def get_fields(self):
def get_related(self):
return [r for r in self.model._meta.related_objects if not r.many_to_many]

def _make(self, commit=True, commit_related=True, _save_kwargs=None, _refresh_after_create=False, **attrs):
def _make(self, commit=True, commit_related=True, _save_kwargs=None,
_refresh_after_create=False, **attrs):
_save_kwargs = _save_kwargs or {}

self._clean_attrs(attrs)
Expand All @@ -253,13 +276,16 @@ def _make(self, commit=True, commit_related=True, _save_kwargs=None, _refresh_af
else:
self.m2m_dict[field.name] = self.model_attrs.pop(field.name)
elif field.name not in self.model_attrs:
if not isinstance(field, ForeignKey) or '{0}_id'.format(field.name) not in self.model_attrs:
if not isinstance(field, ForeignKey) or \
'{0}_id'.format(field.name) not in self.model_attrs:
self.model_attrs[field.name] = self.generate_value(field, commit_related)
elif callable(self.model_attrs[field.name]):
self.model_attrs[field.name] = self.model_attrs[field.name]()
elif field.name in self.iterator_attrs:
try:
self.model_attrs[field.name] = advance_iterator(self.iterator_attrs[field.name])
self.model_attrs[field.name] = advance_iterator(
self.iterator_attrs[field.name]
)
except StopIteration:
raise RecipeIteratorEmpty('{0} iterator is empty.'.format(field.name))

Expand Down Expand Up @@ -307,8 +333,9 @@ def create_by_related_name(self, instance, related):
make(**kwargs)

def _clean_attrs(self, attrs):
def is_rel_field(x):
return '__' in x
self.fill_in_optional = attrs.pop('_fill_optional', False)
is_rel_field = lambda x: '__' in x
self.iterator_attrs = dict((k, v) for k, v in attrs.items() if is_iterator(v))
self.model_attrs = dict((k, v) for k, v in attrs.items() if not is_rel_field(k))
self.rel_attrs = dict((k, v) for k, v in attrs.items() if is_rel_field(k))
Expand All @@ -331,9 +358,18 @@ def _skip_field(self, field):
if isinstance(field, (AutoField, GenericRelation, OrderWrt)):
return True

if all([field.name not in self.model_attrs, field.name not in self.rel_fields, field.name not in self.attr_mapping]):
# Django is quirky in that BooleanFields are always "blank", but have no default default.
if not field.fill_optional and (not issubclass(field.__class__, Field) or field.has_default() or (field.blank and not isinstance(field, BooleanField))):
if all([
field.name not in self.model_attrs,
field.name not in self.rel_fields,
field.name not in self.attr_mapping
]):
# Django is quirky in that BooleanFields are always "blank",
# but have no default default.
if not field.fill_optional and (
not issubclass(field.__class__, Field) or
field.has_default() or
(field.blank and not isinstance(field, BooleanField))
):
return True

if field.name not in self.model_attrs:
Expand Down Expand Up @@ -387,7 +423,8 @@ def generate_value(self, field, commit=True):
generator = self.attr_mapping[field.name]
elif getattr(field, 'choices'):
generator = random_gen.gen_from_choices(field.choices)
elif isinstance(field, ForeignKey) and issubclass(self._remote_field(field).model, ContentType):
elif isinstance(field, ForeignKey) and \
issubclass(self._remote_field(field).model, ContentType):
generator = self.type_mapping[ContentType]
elif generators.get(field.__class__):
generator = generators.get(field.__class__)
Expand Down Expand Up @@ -417,7 +454,7 @@ def get_required_values(generator, field):
If required value is a string, simply fetch the value from the field
and return.
'''
#FIXME: avoid abreviations
# FIXME: avoid abbreviations
rt = {}
if hasattr(generator, 'required'):
for item in generator.required:
Expand Down
5 changes: 3 additions & 2 deletions model_mommy/random_gen.py
@@ -1,4 +1,3 @@
# -*- coding:utf-8 -*-
"""
Generators are callables that return a value used to populate a field.
Expand Down Expand Up @@ -77,7 +76,9 @@ def gen_float():


def gen_decimal(max_digits, decimal_places):
num_as_str = lambda x: ''.join([str(randint(0, 9)) for i in range(x)])
def num_as_str(x):
return ''.join([str(randint(0, 9)) for i in range(x)])

if decimal_places:
return Decimal("%s.%s" % (num_as_str(max_digits - decimal_places - 1),
num_as_str(decimal_places)))
Expand Down
12 changes: 6 additions & 6 deletions model_mommy/recipe.py
Expand Up @@ -4,12 +4,10 @@
from .exceptions import RecipeNotFound

# Enable seq to be imported from recipes
from .utils import seq
from .utils import seq # NoQA

from six import string_types



finder = mommy.ModelFinder()


Expand All @@ -23,7 +21,7 @@ def __init__(self, _model, **attrs):
def _mapping(self, new_attrs):
_save_related = new_attrs.get('_save_related', True)
rel_fields_attrs = dict((k, v) for k, v in new_attrs.items() if '__' in k)
new_attrs = dict((k, v) for k, v in new_attrs.items() if not '__' in k)
new_attrs = dict((k, v) for k, v in new_attrs.items() if '__' not in k)
mapping = self.attr_mapping.copy()
for k, v in self.attr_mapping.items():
# do not generate values if field value is provided
Expand All @@ -35,10 +33,12 @@ def _mapping(self, new_attrs):
else:
m = self._model
if k not in self._iterator_backups or m.objects.count() == 0:
self._iterator_backups[k] = itertools.tee(self._iterator_backups.get(k, [v])[0])
self._iterator_backups[k] = itertools.tee(
self._iterator_backups.get(k, [v])[0]
)
mapping[k] = self._iterator_backups[k][1]
elif isinstance(v, RecipeForeignKey):
a={}
a = {}
for key, value in list(rel_fields_attrs.items()):
if key.startswith('%s__' % k):
a[key] = rel_fields_attrs.pop(key)
Expand Down
10 changes: 6 additions & 4 deletions model_mommy/timezone.py
@@ -1,22 +1,24 @@
# coding: utf-8
'''
"""
Add support for Django 1.4+ safe datetimes.
https://docs.djangoproject.com/en/1.4/topics/i18n/timezones/
'''
"""
# TODO: the whole file seems to be not needed anymore, since Django has this tooling built-in

from datetime import datetime

try:
from django.conf import settings
from django.utils.timezone import now, utc
except ImportError:
now = lambda: datetime.now()
def now():
return datetime.now()


def smart_datetime(*args):
value = datetime(*args)
return tz_aware(value)


def tz_aware(d):
value = d
if settings.USE_TZ:
Expand Down
5 changes: 3 additions & 2 deletions model_mommy/utils.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import importlib
import datetime
import itertools

from .timezone import tz_aware
from six import string_types

# TODO: remove this since Python 2 support was dropped
# Python 2.6.x compatibility code
itertools_count = itertools.count
try:
Expand Down Expand Up @@ -43,6 +43,7 @@ def import_from_str(import_string):
return getattr(module, field_name)


# TODO: remove this since Python 2 support was dropped
def _total_secs(td):
"""
python 2.6 compatible timedelta total seconds calculation
Expand All @@ -52,7 +53,7 @@ def _total_secs(td):
if hasattr(td, 'total_seconds'):
return td.total_seconds()
else:
#py26
# py26
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.0**6


Expand Down
9 changes: 4 additions & 5 deletions runtests.py
Expand Up @@ -7,6 +7,10 @@

import django

# We only need this if HstoreFields are a possibility
from django.db.backends.signals import connection_created
from django.test.runner import DiscoverRunner


def parse_args():
parser = OptionParser()
Expand Down Expand Up @@ -69,11 +73,6 @@ def configure_settings(options):
return settings


# We only need this if HstoreFields are a possibility
from django.db.backends.signals import connection_created
from django.test.runner import DiscoverRunner


def create_hstore(sender, **kwargs):
conn = kwargs.get('connection')
if conn is not None:
Expand Down
6 changes: 6 additions & 0 deletions setup.cfg
@@ -1,2 +1,8 @@
[bdist_wheel]
universal = 1

[flake8]
max-line-length = 99
statistics = true
show-source = true
exclude = docs/*

0 comments on commit 2f8948c

Please sign in to comment.