Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
lint-command:
- ruff check --output-format=github .
- black --check --diff .
- mypy model_bakery
- ty check model_bakery
steps:
- uses: actions/checkout@v6
- name: Install uv
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

### Changed
- [dev] Replace mypy with ty as the primary type checker with stricter type checks

### Removed

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ release:
lint:
@black .
@ruff check .
@mypy model_bakery
@ty check model_bakery

.PHONY: help test release lint
2 changes: 1 addition & 1 deletion model_bakery/baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ def _handle_m2m(self, instance: Model):
m2m_relation.source_field_name: instance,
m2m_relation.target_field_name: value,
}
make(
make( # ty: ignore[no-matching-overload]
cast(type[Model], through_model),
_using=self._using,
**base_kwargs,
Expand Down
20 changes: 10 additions & 10 deletions model_bakery/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@
# PostgreSQL-specific field (only available when psycopg is installed)
from django.contrib.postgres.fields import ArrayField
except ImportError:
ArrayField = None
ArrayField = None # type: ignore[misc,assignment]

try:
# PostgreSQL-specific field (only available when psycopg is installed)
from django.contrib.postgres.fields import HStoreField
except ImportError:
HStoreField = None
HStoreField = None # type: ignore[misc,assignment]

try:
# PostgreSQL-specific fields (only available when psycopg is installed)
Expand All @@ -59,9 +59,9 @@
CITextField,
)
except ImportError:
CICharField = None
CIEmailField = None
CITextField = None
CICharField = None # type: ignore[misc,assignment]
CIEmailField = None # type: ignore[misc,assignment]
CITextField = None # type: ignore[misc,assignment]


try:
Expand All @@ -74,11 +74,11 @@
IntegerRangeField,
)
except ImportError:
BigIntegerRangeField = None
DateRangeField = None
DateTimeRangeField = None
DecimalRangeField = None
IntegerRangeField = None
BigIntegerRangeField = None # type: ignore[misc,assignment]
DateRangeField = None # type: ignore[misc,assignment]
DateTimeRangeField = None # type: ignore[misc,assignment]
DecimalRangeField = None # type: ignore[misc,assignment]
IntegerRangeField = None # type: ignore[misc,assignment]


default_mapping = {
Expand Down
18 changes: 12 additions & 6 deletions model_bakery/random_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,11 @@ def gen_pg_numbers_range(number_cast: Callable[[int], Any]) -> Callable:

def gen_range():
try:
from psycopg.types.range import Range
from psycopg.types.range import Range # ty: ignore[unresolved-import]
except ImportError:
from psycopg2._range import NumericRange as Range
from psycopg2._range import ( # ty: ignore[unresolved-import]
NumericRange as Range,
)

base_num = baker_random.randint(1, 100000)
return Range(number_cast(-1 * base_num), number_cast(base_num))
Expand All @@ -533,9 +535,9 @@ def gen_date_range():
to avoid empty ranges in tests.
"""
try:
from psycopg.types.range import DateRange
from psycopg.types.range import DateRange # ty: ignore[unresolved-import]
except ImportError:
from psycopg2.extras import DateRange
from psycopg2.extras import DateRange # ty: ignore[unresolved-import]

base_date = gen_date()
interval = gen_interval(min_interval=24 * 60 * 60 * 1000)
Expand All @@ -551,9 +553,13 @@ def gen_datetime_range():
to avoid empty ranges in tests.
"""
try:
from psycopg.types.range import TimestamptzRange
from psycopg.types.range import ( # ty: ignore[unresolved-import]
TimestamptzRange,
)
except ImportError:
from psycopg2.extras import DateTimeTZRange as TimestamptzRange
from psycopg2.extras import ( # ty: ignore[unresolved-import]
DateTimeTZRange as TimestamptzRange,
)

base_datetime = gen_datetime()
interval = gen_interval(min_interval=24 * 60 * 60 * 1000)
Expand Down
20 changes: 10 additions & 10 deletions model_bakery/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
Any,
Generic,
TypeVar,
cast,
overload,
)

from django.db.models import Model

from . import baker
from ._types import M
from .exceptions import RecipeNotFound
Expand Down Expand Up @@ -178,19 +175,19 @@ def extend(self: _T, **attrs: Any) -> _T:
return type(self)(self._model, **attr_mapping)


def _load_recipe_from_calling_module(recipe: str) -> Recipe[Model]:
def _load_recipe_from_calling_module(recipe_name: str) -> Recipe[Any]:
"""Load `Recipe` from the string attribute given from the calling module.

Args:
recipe (str): the name of the recipe attribute within the module from
recipe_name (str): the name of the recipe attribute within the module from
which it should be loaded

Returns:
(Recipe): recipe resolved from calling module
"""
recipe = getattr(get_calling_module(2), recipe)
recipe = getattr(get_calling_module(2), recipe_name)
if recipe:
return cast(Recipe[Model], recipe)
return recipe
else:
raise RecipeNotFound

Expand All @@ -217,16 +214,19 @@ def foreign_key(
This resolves recipes supplied as strings from other module paths or from
the calling code's module.
"""
resolved_recipe: Recipe[M]
if isinstance(recipe, str):
# Load `Recipe` from string before handing off to `RecipeForeignKey`
try:
# Try to load from another module
recipe = baker._recipe(recipe)
resolved_recipe = baker._recipe(recipe)
except (AttributeError, ImportError, ValueError):
# Probably not in another module, so load it from calling module
recipe = _load_recipe_from_calling_module(cast(str, recipe))
resolved_recipe = _load_recipe_from_calling_module(recipe)
else:
resolved_recipe = recipe

return RecipeForeignKey(cast(Recipe[M], recipe), one_to_one)
return RecipeForeignKey(resolved_recipe, one_to_one)


class related(Generic[M]): # FIXME
Expand Down
13 changes: 9 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ dev = [
"pytest-django",
"black",
"ruff",
"mypy",
"ty",
]

[project.urls]
Expand All @@ -84,9 +84,14 @@ source = [
[tool.coverage.report]
show_missing = true

[tool.mypy]
ignore_missing_imports = true
disallow_untyped_calls = true
[tool.ty.rules]
# Django dynamic attributes (.objects, ._meta, etc.)
# TODO: remove these two ignores when ty adds better Django support
# https://github.com/astral-sh/ty/issues/1018
unresolved-attribute = "ignore"
possibly-missing-attribute = "ignore"
# Allow `# type: ignore` comments (mypy syntax) without warnings
unused-ignore-comment = "ignore"

[tool.pytest.ini_options]
addopts = "--tb=short -rxs --nomigrations"
Expand Down