Skip to content

Commit

Permalink
Merge pull request #13 from barseghyanartur/dev
Browse files Browse the repository at this point in the history
Feature/add traits (#12)
  • Loading branch information
barseghyanartur authored Dec 4, 2023
2 parents bc399b3 + f148a71 commit d9b3aa1
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 8 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.

0.3.1
-----
2023-12-04

- Improve Tortoise ORM factory.
- Add traits.
- Improve docmentation.

0.3
---
2023-12-03
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Update version ONLY here
VERSION := 0.3
VERSION := 0.3.1
SHELL := /bin/bash
# Makefile for project
VENV := ~/.virtualenvs/fake.py/bin/activate
Expand Down
7 changes: 7 additions & 0 deletions examples/dataclasses/article/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
SubFactory,
post_save,
pre_save,
trait,
)

from article.models import Article, User
Expand Down Expand Up @@ -38,6 +39,12 @@ class UserFactory(ModelFactory):
class Meta:
model = User

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down
7 changes: 7 additions & 0 deletions examples/django/article/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
SubFactory,
post_save,
pre_save,
trait,
)

from article.models import Article
Expand Down Expand Up @@ -44,6 +45,12 @@ class Meta:
model = User
get_or_create = ("username",)

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __set_password(instance):
Expand Down
7 changes: 7 additions & 0 deletions examples/pydantic/article/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
SubFactory,
post_save,
pre_save,
trait,
)

from article.models import Article, User
Expand Down Expand Up @@ -38,6 +39,12 @@ class UserFactory(ModelFactory):
class Meta:
model = User

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down
7 changes: 7 additions & 0 deletions examples/tortoise/article/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
TortoiseModelFactory,
post_save,
pre_save,
trait,
)

from article.models import Article, User
Expand Down Expand Up @@ -40,6 +41,12 @@ class Meta:
model = User
get_or_create = ("username",)

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down
94 changes: 89 additions & 5 deletions fake.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
)

__title__ = "fake.py"
__version__ = "0.3"
__version__ = "0.3.1"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2023 Artur Barseghyan"
__license__ = "MIT"
Expand All @@ -61,6 +61,7 @@
"post_save",
"pre_save",
"run_async_in_thread",
"trait",
)

LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -1625,6 +1626,11 @@ def post_save(func):
return func


def trait(func):
func.is_trait = True
return func


class ModelFactory:
"""ModelFactory."""

Expand Down Expand Up @@ -1654,20 +1660,42 @@ def _run_hooks(cls, hooks, instance):
for method in hooks:
getattr(cls, method)(instance)

@classmethod
def _apply_traits(cls, instance, **kwargs) -> None:
for name, method in cls.__dict__.items():
if getattr(method, "is_trait", False) and kwargs.get(name, False):
method(cls, instance)

@classmethod
def create(cls, **kwargs):
trait_keys = {
name
for name, method in cls.__dict__.items()
if getattr(method, "is_trait", False)
}

model_data = {
field: (
value()
if isinstance(value, (FactoryMethod, SubFactory))
else value
)
for field, value in cls.__dict__.items()
if not field.startswith("_") and not field == "Meta"
if (
not field.startswith("_")
and not field == "Meta"
and not getattr(value, "is_trait", False)
and not getattr(value, "is_pre_save", False)
and not getattr(value, "is_post_save", False)
)
}
model_data.update(kwargs)
# Update model_data with non-trait kwargs
model_data.update(
{k: v for k, v in kwargs.items() if k not in trait_keys}
)

instance = cls.Meta.model(**model_data)
cls._apply_traits(instance, **kwargs)

pre_save_hooks = [
method
Expand Down Expand Up @@ -1725,7 +1753,13 @@ def create(cls, **kwargs):
model_data = {
field: value
for field, value in cls.__dict__.items()
if not field.startswith("_") and not field == "Meta"
if (
not field.startswith("_")
and not field == "Meta"
and not getattr(value, "is_trait", False)
and not getattr(value, "is_pre_save", False)
and not getattr(value, "is_post_save", False)
)
}

# Separate nested attributes and direct attributes
Expand All @@ -1743,6 +1777,7 @@ def create(cls, **kwargs):

# Create a new instance if none found
instance = cls.Meta.model(**model_data)
cls._apply_traits(instance, **kwargs)

# Handle nested attributes
for attr, value in nested_attrs.items():
Expand Down Expand Up @@ -1826,7 +1861,13 @@ async def async_filter():
model_data = {
field: value
for field, value in cls.__dict__.items()
if not field.startswith("_") and not field == "Meta"
if (
not field.startswith("_")
and not field == "Meta"
and not getattr(value, "is_trait", False)
and not getattr(value, "is_pre_save", False)
and not getattr(value, "is_post_save", False)
)
}

# Separate nested attributes and direct attributes
Expand All @@ -1844,6 +1885,7 @@ async def async_filter():

# Create a new instance if none found
instance = cls.Meta.model(**model_data)
cls._apply_traits(instance, **kwargs)

# Handle nested attributes
for attr, value in nested_attrs.items():
Expand Down Expand Up @@ -2483,6 +2525,12 @@ class UserFactory(ModelFactory):
class Meta:
model = User

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down Expand Up @@ -2528,6 +2576,14 @@ class Meta:
hasattr(user, "post_save_called") and user.post_save_called
)

# Testing traits
admin_user = UserFactory(is_admin_user=True)
self.assertTrue(
admin_user.is_staff
and admin_user.is_superuser
and admin_user.is_active
)

# **********************************
# ******* DjangoModelFactory *******
# **********************************
Expand All @@ -2548,6 +2604,12 @@ class Meta:
model = User
get_or_create = ("username",)

@trait
def is_admin_user(self, instance: User) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down Expand Up @@ -2613,6 +2675,14 @@ def __post_save_method(instance):
self.assertEqual(len(django_articles), 5)
self.assertIsInstance(django_articles[0], Article)

# Testing traits
django_admin_user = DjangoUserFactory(is_admin_user=True)
self.assertTrue(
django_admin_user.is_staff
and django_admin_user.is_superuser
and django_admin_user.is_active
)

# **********************************
# ******* TortoiseModelFactory *******
# **********************************
Expand Down Expand Up @@ -2714,6 +2784,12 @@ class Meta:
model = TortoiseUser
get_or_create = ("username",)

@trait
def is_admin_user(self, instance: TortoiseUser) -> None:
instance.is_superuser = True
instance.is_staff = True
instance.is_active = True

@staticmethod
@pre_save
def __pre_save_method(instance):
Expand Down Expand Up @@ -2779,6 +2855,14 @@ def __post_save_method(instance):
self.assertEqual(len(tortoise_articles), 5)
self.assertIsInstance(tortoise_articles[0], TortoiseArticle)

# Testing traits
tortoise_admin_user = TortoiseUserFactory(is_admin_user=True)
self.assertTrue(
tortoise_admin_user.is_staff
and tortoise_admin_user.is_superuser
and tortoise_admin_user.is_active
)

def test_registry_integration(self) -> None:
"""Test `add`."""
# Create a TXT file.
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "fake.py"
description = "Minimalistic, standalone alternative fake data generator with no dependencies."
readme = "README.rst"
version = "0.3"
version = "0.3.1"
dependencies = []
authors = [
{name = "Artur Barseghyan", email = "artur.barseghyan@gmail.com"},
Expand Down Expand Up @@ -134,7 +134,7 @@ ignore-path = [
]

[tool.pytest.ini_options]
minversion = "0.3"
minversion = "0.3.1"
addopts = [
"-ra",
"-vvv",
Expand Down

0 comments on commit d9b3aa1

Please sign in to comment.