Skip to content

Commit

Permalink
Move lazy reference resolving to its own helper function
Browse files Browse the repository at this point in the history
  • Loading branch information
flaeppe committed Sep 21, 2023
1 parent b96dd5f commit 62d84d3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 24 deletions.
28 changes: 28 additions & 0 deletions mypy_django_plugin/lib/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
AssignmentStmt,
Block,
ClassDef,
Context,
Expression,
MemberExpr,
MypyFile,
Expand Down Expand Up @@ -420,3 +421,30 @@ def is_abstract_model(model: TypeInfo) -> bool:

metadata["is_abstract_model"] = False
return False


def resolve_lazy_reference(
reference: str, *, api: Union[TypeChecker, SemanticAnalyzer], django_context: "DjangoContext", ctx: Context
) -> Optional[TypeInfo]:
"""
Attempts to resolve a lazy reference(e.g. "<app_label>.<object_name>") to a
'TypeInfo' instance.
"""
if "." not in reference:
# <object_name> -- needs prefix of <app_label>. We can't implicitly solve
# what app label this should be, yet.
return None

# Reference conforms to the structure of a lazy reference: '<app_label>.<object_name>'
fullname = django_context.model_class_fullnames_by_label.get(reference)
if fullname is not None:
model_info = lookup_fully_qualified_typeinfo(api, fullname)
if model_info is not None:
return model_info
elif isinstance(api, SemanticAnalyzer) and not api.final_iteration:
# Getting this far, where Django matched the reference but we still can't
# find it, we want to defer
api.defer()
else:
api.fail("Could not match lazy reference with any model", ctx)
return None
23 changes: 3 additions & 20 deletions mypy_django_plugin/transformers/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,26 +239,9 @@ def get_model_from_expression(
):
return Instance(expr.node, [])
elif isinstance(expr, StrExpr):
if "." not in expr.value:
# <object_name> -- needs prefix of <app_label>. We can't implicitly solve
# what app label this should be, yet.
api.fail("Could not resolve lazy reference without an app label", expr)
api.note(
("Try to use a reference explicitly prefixed with app label:" f' "<app_label>.{expr.value}" instead'),
expr,
)
return None

# Expression conforms to the structure of a lazy reference: '<app_label>.<object_name>'
fullname = django_context.model_class_fullnames_by_label.get(expr.value)
if fullname is not None:
model_info = helpers.lookup_fully_qualified_typeinfo(api, fullname)
if model_info is not None:
return Instance(model_info, [])
elif isinstance(api, SemanticAnalyzer) and not api.final_iteration:
# Getting this far, where Django matched the reference but we still can't
# find it, we want to defer
api.defer()
model_info = helpers.resolve_lazy_reference(expr.value, api=api, django_context=django_context, ctx=expr)
if model_info is not None:
return Instance(model_info, [])

return None

Expand Down
4 changes: 0 additions & 4 deletions tests/typecheck/fields/test_related.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1148,11 +1148,7 @@
main:2: note: Revealed type is "django.db.models.fields.related_descriptors.ManyToManyDescriptor[Any]"
main:3: note: Revealed type is "django.db.models.fields.related_descriptors.ManyRelatedManager[Any]"
myapp/models/child:5: error: Need type annotation for "parents"
myapp/models/child:5: error: Could not resolve lazy reference without an app label
myapp/models/child:5: note: Try to use a reference explicitly prefixed with app label: "<app_label>.Parent" instead
myapp/models/child:6: error: Need type annotation for "other_parents"
myapp/models/child:6: error: Could not resolve lazy reference without an app label
myapp/models/child:6: note: Try to use a reference explicitly prefixed with app label: "<app_label>.Parent" instead
installed_apps:
- myapp
files:
Expand Down

0 comments on commit 62d84d3

Please sign in to comment.