diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7be1e9dc..4e050699 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: git@github.com:pre-commit/pre-commit-hooks - rev: v2.1.0 + rev: v3.1.0 hooks: - id: check-added-large-files - id: debug-statements @@ -8,14 +8,18 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.6 + rev: 3.8.3 hooks: - id: flake8 -- repo: https://gitlab.com/pycqa/pydocstyle - rev: 4.0.1 +- repo: https://github.com/timothycrosley/isort + rev: 4.3.21 + hooks: + - id: isort +- repo: https://github.com/PyCQA/pydocstyle + rev: 5.0.2 hooks: - id: pydocstyle -- repo: https://github.com/ambv/black - rev: stable +- repo: https://github.com/psf/black + rev: 19.10b0 hooks: - id: black diff --git a/CHANGELOG.md b/CHANGELOG.md index ccebfeaf..fb5ac8eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased](https://github.com/model-bakers/model_bakery/tree/master) ### Added +- Add isort and fix imports [PR #77](https://github.com/model-bakers/model_bakery/pull/77) ### Changed - Enable `seq` to be imported from `baker` [PR #76](https://github.com/model-bakers/model_bakery/pull/76) diff --git a/Makefile b/Makefile index a6315703..5353e5d9 100644 --- a/Makefile +++ b/Makefile @@ -10,4 +10,3 @@ release: @twine upload dist/* .PHONY: test release - diff --git a/docs/source/conf.py b/docs/source/conf.py index ffd99c55..9a17a7d7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,8 +10,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the diff --git a/model_bakery/baker.py b/model_bakery/baker.py index 3625f766..0ff684e3 100644 --- a/model_bakery/baker.py +++ b/model_bakery/baker.py @@ -1,32 +1,31 @@ from os.path import dirname, join +from django.apps import apps from django.conf import settings from django.contrib import contenttypes -from django.apps import apps - -from django.db.models.base import ModelBase from django.db.models import ( - ForeignKey, - ManyToManyField, - OneToOneField, - Field, AutoField, BooleanField, + Field, FileField, + ForeignKey, + ManyToManyField, + OneToOneField, ) +from django.db.models.base import ModelBase +from django.db.models.fields.proxy import OrderWrt from django.db.models.fields.related import ( ReverseManyToOneDescriptor as ForeignRelatedObjectsDescriptor, ) -from django.db.models.fields.proxy import OrderWrt from . import generators, random_gen from .exceptions import ( - ModelNotFound, AmbiguousModelName, - InvalidQuantityException, - RecipeIteratorEmpty, CustomBakerNotFound, InvalidCustomBaker, + InvalidQuantityException, + ModelNotFound, + RecipeIteratorEmpty, ) from .utils import import_from_str diff --git a/model_bakery/gis.py b/model_bakery/gis.py index ef4a4c2c..e68c092e 100644 --- a/model_bakery/gis.py +++ b/model_bakery/gis.py @@ -1,4 +1,5 @@ from __future__ import absolute_import + from django.apps import apps BAKER_GIS = apps.is_installed("django.contrib.gis") diff --git a/model_bakery/random_gen.py b/model_bakery/random_gen.py index 433c38e2..39a5fa21 100644 --- a/model_bakery/random_gen.py +++ b/model_bakery/random_gen.py @@ -12,12 +12,11 @@ import string import warnings from decimal import Decimal -from os.path import abspath, join, dirname -from random import randint, choice, random, uniform +from os.path import abspath, dirname, join +from random import choice, randint, random, uniform from model_bakery.timezone import now - MAX_LENGTH = 300 # Using sys.maxint here breaks a bunch of tests when running against a # Postgres database. diff --git a/model_bakery/recipe.py b/model_bakery/recipe.py index 9dcab68a..c36ec97a 100644 --- a/model_bakery/recipe.py +++ b/model_bakery/recipe.py @@ -1,11 +1,9 @@ import inspect import itertools + from . import baker from .exceptions import RecipeNotFound - -# Enable seq to be imported from recipes -from .utils import seq # NoQA - +from .utils import seq # NoQA: Enable seq to be imported from recipes finder = baker.ModelFinder() diff --git a/model_bakery/utils.py b/model_bakery/utils.py index cdd98e33..ee89393b 100644 --- a/model_bakery/utils.py +++ b/model_bakery/utils.py @@ -1,5 +1,5 @@ -import importlib import datetime +import importlib import itertools import warnings diff --git a/setup.cfg b/setup.cfg index f1f4f8fa..82f4cda8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,3 +9,14 @@ exclude = docs/* [tool:pytest] addopts = --tb=short -rxs --nomigrations + +[isort] +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=88 + +[pydocstyle] +add_ignore = D1 +match-dir = (?!test|docs|\.).* diff --git a/setup.py b/setup.py index 5f821a1d..a4c0f64f 100755 --- a/setup.py +++ b/setup.py @@ -1,9 +1,9 @@ +from os.path import dirname, join + import setuptools -from os.path import join, dirname import model_bakery - setuptools.setup( name="model_bakery", version=model_bakery.__version__, diff --git a/tests/generic/baker_recipes.py b/tests/generic/baker_recipes.py index 91871ba6..31b35023 100644 --- a/tests/generic/baker_recipes.py +++ b/tests/generic/baker_recipes.py @@ -1,18 +1,17 @@ # ATTENTION: Recipes defined for testing purposes only +from datetime import timedelta from decimal import Decimal -from model_bakery.recipe import Recipe, foreign_key, seq -from model_bakery.recipe import related + +from model_bakery.recipe import Recipe, foreign_key, related, seq from model_bakery.timezone import now from tests.generic.models import ( TEST_TIME, - Person, Dog, DummyDefaultFieldsModel, DummyUniqueIntegerFieldModel, + Person, ) -from datetime import timedelta - person = Recipe( Person, name="John Doe", diff --git a/tests/generic/forms.py b/tests/generic/forms.py index 8db28b6d..e70f98da 100644 --- a/tests/generic/forms.py +++ b/tests/generic/forms.py @@ -1,6 +1,4 @@ from django.forms import ModelForm - - from tests.generic.models import DummyGenericIPAddressFieldModel diff --git a/tests/generic/models.py b/tests/generic/models.py index dda58364..a08f1577 100755 --- a/tests/generic/models.py +++ b/tests/generic/models.py @@ -6,21 +6,19 @@ from decimal import Decimal from tempfile import gettempdir -from model_bakery.gis import BAKER_GIS - -from django.core.files.storage import FileSystemStorage - +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey +from django.core.files.storage import FileSystemStorage +from model_bakery.gis import BAKER_GIS +from model_bakery.timezone import smart_datetime as datetime from .fields import ( + CustomFieldViaSettings, CustomFieldWithGenerator, CustomFieldWithoutGenerator, - CustomFieldViaSettings, - FakeListField, CustomForeignKey, + FakeListField, ) -from model_bakery.timezone import smart_datetime as datetime # check whether or not PIL is installed try: @@ -70,7 +68,7 @@ class PaymentBill(models.Model): class Person(models.Model): gender = models.CharField(max_length=1, choices=GENDER_CHOICES) - # jards macalé is an amazing brazilian musician! =] + # Jards Macalé is an amazing brazilian musician! =] enjoy_jards_macale = models.BooleanField(default=True) like_metal_music = models.BooleanField(default=False) name = models.CharField(max_length=30) @@ -328,10 +326,11 @@ class Movie(models.Model): class MovieManager(models.Manager): def get_queryset(self): - """Annotate queryset with an alias field 'name'. + """ + Annotate queryset with an alias field 'name'. We want to test whether this annotation has been run after - calling baker.make(). + calling `baker.make()`. """ return super(MovieManager, self).get_queryset().annotate(name=models.F("title")) diff --git a/tests/generic/tests/sub_package/baker_recipes.py b/tests/generic/tests/sub_package/baker_recipes.py index e8d71830..51731d69 100644 --- a/tests/generic/tests/sub_package/baker_recipes.py +++ b/tests/generic/tests/sub_package/baker_recipes.py @@ -1,8 +1,8 @@ +from datetime import date, datetime + from model_bakery.recipe import Recipe from tests.generic.models import Person -from datetime import date, datetime - person = Recipe( Person, name="John Deeper", diff --git a/tests/test_baker.py b/tests/test_baker.py index 97bffe4f..92ad5d25 100644 --- a/tests/test_baker.py +++ b/tests/test_baker.py @@ -1,21 +1,18 @@ -import pytest import datetime import itertools from decimal import Decimal from unittest.mock import patch +import pytest from django.db.models import Manager from django.db.models.signals import m2m_changed - -from model_bakery import baker -from model_bakery import random_gen +from model_bakery import baker, random_gen from model_bakery.exceptions import ( - ModelNotFound, AmbiguousModelName, InvalidQuantityException, + ModelNotFound, ) from model_bakery.timezone import smart_datetime - from tests.generic import models from tests.generic.forms import DummyGenericIPAddressFieldForm @@ -113,7 +110,7 @@ def test_multiple_inheritance_creation(self): @pytest.mark.django_db class TestsBakerRepeatedCreatesSimpleModel: def test_make_should_create_objects_respecting_quantity_parameter(self): - people = baker.make(models.Person, _quantity=5) + baker.make(models.Person, _quantity=5) assert models.Person.objects.count() == 5 people = baker.make(models.Person, _quantity=5, name="George Washington") @@ -144,7 +141,7 @@ def test_prepare_raises_correct_exception_if_invalid_quantity(self): baker.prepare(_model=models.Person, _quantity=0) def test_accepts_generators_with_quantity(self): - people = baker.make( + baker.make( models.Person, name=itertools.cycle(["a", "b", "c"]), id_document=itertools.cycle(["d1", "d2", "d3", "d4", "d5"]), @@ -164,7 +161,7 @@ def test_accepts_generators_with_quantity(self): assert "d5" == p5.id_document def test_accepts_generators_with_quantity_for_unique_fields(self): - people = baker.make( + baker.make( models.DummyUniqueIntegerFieldModel, value=itertools.cycle([1, 2, 3]), _quantity=3, @@ -179,9 +176,7 @@ def test_generators_work_with_user_model(self): from django.contrib.auth import get_user_model User = get_user_model() - people = baker.make( - User, username=itertools.cycle(["a", "b", "c"]), _quantity=3 - ) + baker.make(User, username=itertools.cycle(["a", "b", "c"]), _quantity=3) assert User.objects.count() == 3 u1, u2, u3 = User.objects.all() assert "a" == u1.username @@ -197,7 +192,7 @@ def test_default_behaviour_for_and_fk(self): assert dog.pk is None assert dog.owner.pk is None with pytest.raises(ValueError): - dog.friends_with + assert dog.friends_with def test_create_fk_instances(self): dog = baker.prepare(models.Dog, _save_related=True) @@ -205,7 +200,7 @@ def test_create_fk_instances(self): assert dog.pk is None assert dog.owner.pk with pytest.raises(ValueError): - dog.friends_with + assert dog.friends_with def test_create_fk_instances_with_quantity(self): dog1, dog2 = baker.prepare(models.Dog, _save_related=True, _quantity=2) @@ -213,12 +208,12 @@ def test_create_fk_instances_with_quantity(self): assert dog1.pk is None assert dog1.owner.pk with pytest.raises(ValueError): - dog1.friends_with + assert dog1.friends_with assert dog2.pk is None assert dog2.owner.pk with pytest.raises(ValueError): - dog2.friends_with + assert dog2.friends_with def test_create_one_to_one(self): lonely_person = baker.prepare(models.LonelyPerson, _save_related=True) @@ -651,7 +646,7 @@ def test_annotation_within_manager_get_queryset_are_run_on_make(self): """ movie = baker.make(models.MovieWithAnnotation) with pytest.raises(AttributeError): - movie.name + assert movie.name movie = baker.make( models.MovieWithAnnotation, title="Old Boy", _from_manager="objects", diff --git a/tests/test_extending_bakery.py b/tests/test_extending_bakery.py index 1db084db..ed5cd4a4 100644 --- a/tests/test_extending_bakery.py +++ b/tests/test_extending_bakery.py @@ -1,8 +1,7 @@ import pytest - from model_bakery import baker -from model_bakery.random_gen import gen_from_list from model_bakery.exceptions import CustomBakerNotFound, InvalidCustomBaker +from model_bakery.random_gen import gen_from_list from tests.generic.models import Person diff --git a/tests/test_filling_fields.py b/tests/test_filling_fields.py index 6cd4a04b..e9599c7b 100644 --- a/tests/test_filling_fields.py +++ b/tests/test_filling_fields.py @@ -7,16 +7,18 @@ import pytest from django.conf import settings from django.contrib.contenttypes.models import ContentType - +from django.core.validators import ( + validate_ipv4_address, + validate_ipv6_address, + validate_ipv46_address, +) from django.db import connection -from django.db.models import fields, ImageField, FileField - +from django.db.models import FileField, ImageField, fields from model_bakery import baker from model_bakery.gis import BAKER_GIS from model_bakery.random_gen import gen_related from tests.generic import generators, models - try: from django.contrib.postgres.fields import ( ArrayField, @@ -34,12 +36,6 @@ CIEmailField = None CITextField = None -from django.core.validators import ( - validate_ipv4_address, - validate_ipv6_address, - validate_ipv46_address, -) - @pytest.fixture def person(db): @@ -59,13 +55,13 @@ class TestFillingFromChoice: def test_if_gender_is_populated_from_choices(self, person): from tests.generic.models import GENDER_CHOICES - person.gender in map(lambda x: x[0], GENDER_CHOICES) + assert person.gender in map(lambda x: x[0], GENDER_CHOICES) def test_if_occupation_populated_from_choices(self, person): from tests.generic.models import OCCUPATION_CHOICES - occupations = [item[0] for list in OCCUPATION_CHOICES for item in list[1]] - person.occupation in occupations + occupations = [item[0] for lst in OCCUPATION_CHOICES for item in lst[1]] + assert person.occupation in occupations class TestStringFieldsFilling: @@ -263,18 +259,19 @@ def test_filling_file_field(self): def test_does_not_create_file_if_not_flagged(self): dummy = baker.make(models.DummyFileFieldModel) with pytest.raises(ValueError): - dummy.file_field.path # Django raises ValueError if file does not exist + # Django raises ValueError if file does not exist + assert dummy.file_field.path @pytest.mark.django_db class TestFillingCustomFields: def test_raises_unsupported_field_for_custom_field(self, custom_cfg): - """Should raise an exception if a generator is not provided for a custom field""" + """Should raise an exception if a generator is not provided for a custom field.""" with pytest.raises(TypeError): baker.make(models.CustomFieldWithoutGeneratorModel) def test_uses_generator_defined_on_settings_for_custom_field(self, custom_cfg): - """Should use the function defined in settings as a generator""" + """Should use the function defined in settings as a generator.""" generator_dict = { "tests.generic.fields.CustomFieldWithGenerator": generators.gen_value_string } @@ -285,7 +282,7 @@ def test_uses_generator_defined_on_settings_for_custom_field(self, custom_cfg): def test_uses_generator_defined_as_string_on_settings_for_custom_field( self, custom_cfg ): - """Should import and use the function present in the import path defined in settings""" + """Should import and use the function present in the import path defined in settings.""" # fmt: off generator_dict = { "tests.generic.fields.CustomFieldWithGenerator": @@ -297,7 +294,7 @@ def test_uses_generator_defined_as_string_on_settings_for_custom_field( assert "value" == obj.custom_value def test_uses_generator_defined_on_settings_for_custom_foreignkey(self, custom_cfg): - """Should use the function defined in the import path for a foreign key field""" + """Should use the function defined in the import path for a foreign key field.""" generator_dict = { "tests.generic.fields.CustomForeignKey": "model_bakery.random_gen.gen_related" } @@ -308,7 +305,7 @@ def test_uses_generator_defined_on_settings_for_custom_foreignkey(self, custom_c assert "a@b.com" == obj.custom_fk.email def test_uses_generator_defined_as_string_for_custom_field(self, custom_cfg): - """Should import and use the generator function used in the add method""" + """Should import and use the generator function used in the add method.""" baker.generators.add( "tests.generic.fields.CustomFieldWithGenerator", "tests.generic.generators.gen_value_string", @@ -317,7 +314,7 @@ def test_uses_generator_defined_as_string_for_custom_field(self, custom_cfg): assert "value" == obj.custom_value def test_uses_generator_function_for_custom_foreignkey(self, custom_cfg): - """Should use the generator function passed as a value for the add method""" + """Should use the generator function passed as a value for the add method.""" baker.generators.add("tests.generic.fields.CustomForeignKey", gen_related) obj = baker.make( models.CustomForeignKeyWithGeneratorModel, custom_fk__email="a@b.com" @@ -368,7 +365,8 @@ def test_filling_image_file_field(self): def test_does_not_create_file_if_not_flagged(self): dummy = baker.make(models.DummyImageFieldModel) with pytest.raises(ValueError): - dummy.image_field.path # Django raises ValueError if file does not exist + # Django raises ValueError if image does not exist + assert dummy.image_field.path @pytest.mark.skipif( diff --git a/tests/test_recipes.py b/tests/test_recipes.py index d4d23413..c30b2549 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -1,23 +1,22 @@ -import pytest import itertools - -from random import choice # noqa +from datetime import timedelta from decimal import Decimal +from random import choice # noqa from unittest.mock import patch -from datetime import timedelta +import pytest from model_bakery import baker -from model_bakery.recipe import Recipe, foreign_key, RecipeForeignKey -from model_bakery.timezone import now, tz_aware from model_bakery.exceptions import InvalidQuantityException, RecipeIteratorEmpty +from model_bakery.recipe import Recipe, RecipeForeignKey, foreign_key +from model_bakery.timezone import now, tz_aware +from tests.generic.baker_recipes import SmallDogRecipe, pug from tests.generic.models import ( TEST_TIME, - Person, - DummyNumbersModel, - DummyBlankFieldsModel, Dog, + DummyBlankFieldsModel, + DummyNumbersModel, + Person, ) -from tests.generic.baker_recipes import SmallDogRecipe, pug recipe_attrs = { "name": "John Doe", @@ -26,7 +25,7 @@ "bio": "Someone in the crowd", "birthday": now().date(), "appointment": now(), - "blog": "http://joe.blogspot.com", + "blog": "https://joe.example.com", "days_since_last_login": 4, "birth_time": now(), } @@ -34,7 +33,7 @@ def test_import_seq_from_recipe(): - """Test import seq method from recipe module.""" + """Import seq method directly from recipe module.""" try: from model_bakery.recipe import seq # NoQA except ImportError: @@ -44,10 +43,7 @@ def test_import_seq_from_recipe(): @pytest.mark.django_db class TestDefiningRecipes: def test_flat_model_make_recipe_with_the_correct_attributes(self): - """ - A 'flat model' means a model without associations, like - foreign keys, many to many and one to one - """ + """Test a 'flat model' - without associations, like FK, M2M and O2O.""" person = person_recipe.make() assert person.name == recipe_attrs["name"] assert person.nickname == recipe_attrs["nickname"] @@ -81,8 +77,8 @@ def test_always_calls_when_creating(self): choice_mock.return_value = "foo" lst = ["foo", "bar", "spam", "eggs"] r = Recipe(DummyBlankFieldsModel, blank_char_field=lambda: choice_mock(lst)) - r.make().blank_char_field - r.make().blank_char_field + assert r.make().blank_char_field + assert r.make().blank_char_field assert choice_mock.call_count == 2 def test_always_calls_with_quantity(self): @@ -94,9 +90,7 @@ def test_always_calls_with_quantity(self): assert choice_mock.call_count == 3 def test_make_recipes_with_args(self): - """ - Overriding some fields values at recipe execution - """ + """Overriding some fields values at recipe execution.""" person = person_recipe.make(name="Guido", age=56) assert person.name != recipe_attrs["name"] assert person.name == "Guido" @@ -113,9 +107,7 @@ def test_make_recipes_with_args(self): assert person.id is not None def test_prepare_recipes_with_args(self): - """ - Overriding some fields values at recipe execution - """ + """Overriding some fields values at recipe execution.""" person = person_recipe.prepare(name="Guido", age=56) assert person.name != recipe_attrs["name"] assert person.name == "Guido" @@ -169,9 +161,7 @@ def test_defining_recipes_str(self): @pytest.mark.django_db class TestExecutingRecipes: - """ - Tests for calling recipes defined in baker_recipes.py - """ + """Tests for calling recipes defined in baker_recipes.py.""" def test_model_with_foreign_key(self): dog = baker.make_recipe("tests.generic.dog") @@ -348,10 +338,7 @@ def test_not_accept_other_type(self): assert str(exception) == "Not a recipe" def test_do_not_create_related_model(self): - """ - It should not attempt to create other object when - passing the object as argument - """ + """It should not create another object when passing the object as argument.""" person = baker.make_recipe("tests.generic.person") assert Person.objects.count() == 1 baker.make_recipe("tests.generic.dog", owner=person) @@ -360,27 +347,18 @@ def test_do_not_create_related_model(self): assert Person.objects.count() == 1 def test_do_query_lookup_for_recipes_make_method(self): - """ - It should not attempt to create other object when - using query lookup syntax - """ + """It should not create another object when using query lookup syntax.""" dog = baker.make_recipe("tests.generic.dog", owner__name="James") assert Person.objects.count() == 1 assert dog.owner.name == "James" def test_do_query_lookup_for_recipes_prepare_method(self): - """ - It should not attempt to create other object when - using query lookup syntax - """ + """It should not create another object when using query lookup syntax.""" dog = baker.prepare_recipe("tests.generic.dog", owner__name="James") assert dog.owner.name == "James" def test_do_query_lookup_empty_recipes(self): - """ - It should not attempt to create other object when - using query lookup syntax - """ + """It should not create another object when using query lookup syntax.""" dog_recipe = Recipe(Dog) dog = dog_recipe.make(owner__name="James") assert Person.objects.count() == 1 @@ -446,9 +424,7 @@ def test_increment_for_numbers(self): assert dummy.default_float_field == 4.23 def test_increment_for_numbers_2(self): - """ - This test is a repeated one but it is necessary to ensure Sequences atomicity - """ + """Repeated test to ensure Sequences atomicity.""" dummy = baker.make_recipe("tests.generic.serial_numbers") assert dummy.default_int_field == 11 assert dummy.default_decimal_field == Decimal("21.1") diff --git a/tests/test_utils.py b/tests/test_utils.py index 35458702..b34295b8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,5 @@ import pytest - from model_bakery.utils import import_from_str - from tests.generic.models import User diff --git a/tox.ini b/tox.ini index b077241f..94a0fec0 100644 --- a/tox.ini +++ b/tox.ini @@ -3,12 +3,13 @@ envlist = py35-django{111,20,21,22}-{postgresql,sqlite} py{36,37,38}-django{111,20,21,22,30}-{postgresql,sqlite} flake8 + isort black [travis] python = 3.5: py35 - 3.6: py36,flake8,black + 3.6: py36,flake8,isort,black 3.7: py37 3.8: py38 @@ -36,6 +37,11 @@ deps=flake8 basepython=python3 commands=flake8 model_bakery {posargs} +[testenv:isort] +deps=isort +basepython=python3 +commands=isort model_bakery {posargs} + [testenv:black] deps=black basepython=python3