Repository navigation
[1.20 regression] reachability in django-modern-rest #21132
Copy link
Copy link
Closed
Labels
bugmypy got something wrongmypy got something wrongtopic-reachabilityDetecting unreachable codeDetecting unreachable code
Description
Hi!
I am facing 150+ errors in my project https://github.com/wemake-services/django-modern-rest after updating mypy to 1.20 from 1.19.
Here are the most frequest ones:
lru_cache issues
dmr/response.py:236: error: Cannot determine type of "resolve_setting" [has-type]
dmr/response.py:236: note: Revealed type is "Any"
dmr/response.py:237: error: Cannot determine type of "resolve_setting" [has-type]
dmr/routing.py:108: error: Cannot determine type of "resolve_setting" [has-type]
dmr/routing.py:190: error: Cannot determine type of "resolve_setting" [has-type]
dmr/parsers.py:232: error: Cannot determine type of "resolve_setting" [has-type]
dmr/parsers.py:284: error: Cannot determine type of "resolve_setting" [has-type]
For some reason all code that is using resolve_setting fails with this error:
renderers_list = (
resolve_setting(Settings.renderers) if renderers is None else renderers
)Here's how it is defined:
@lru_cache(maxsize=MAX_CACHE_SIZE)
def resolve_setting(
setting_name: Settings,
*,
import_string: bool = False,
) -> Any:
"""
Resolves setting by *setting_name*.
The result is cached using ``@lru_cache`` for performance.
When testing with custom settings, you *must* call
:func:`clear_settings_cache` before and after modifying
Django settings to ensure the cache is invalidated properly.
"""
setting = _resolve_defaults().get(
setting_name,
_DEFAULTS[setting_name],
)
if import_string and isinstance(setting, str):
return module_loading.import_string(setting)
return setting # pyright: ignore[reportUnknownVariableType]Maybe @lru_cache(maxsize=MAX_CACHE_SIZE) is the root cause? If I remove this decorator - all errors are gone.
We also faced a similiar problem in our django-stubs PR: typeddjango/django-stubs#3252 (comment)
Unreachable
I have more than 120+ errors that some statements are unreachable.
Examples:
(.venv) ~/Desktop/django-modern-rest master ✗ 1 ⚠️
» mypy dmr
dmr/settings.py:122: error: Statement is unreachable [unreachable]
dmr/validation/settings.py:41: error: Statement is unreachable [unreachable]
- https://github.com/wemake-services/django-modern-rest/blob/9062072376962f8b9ba6e195244ca702fe3d15e8/dmr/settings.py#L122
- https://github.com/wemake-services/django-modern-rest/blob/9062072376962f8b9ba6e195244ca702fe3d15e8/dmr/validation/settings.py#L41
These are two different module-level definitions. Not sure what is the root cause here :(
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugmypy got something wrongmypy got something wrongtopic-reachabilityDetecting unreachable codeDetecting unreachable code
Activity
ilevkivskyi commentedon Apr 1, 2026
cc @hauntsaninja for the unreachable part.
ilevkivskyi commentedon Apr 1, 2026
The first error (Cannot determine type) may be caused by (either or both):
lru_cache()in typeshed that can't be handled bysemanal_infer.pyanymore.There is really not much we can do about the first possibility (and deferring module top-levels is hard) I will look at current signature of
lru_cache()to see what can be improved insemanal_infer.py.ilevkivskyi commentedon Apr 1, 2026
Hm, the signature of
lru_cache()is non-trivial, and didn't change recently. The only recent change is_lru_cache_wrapperclass was made covariant. Also at least some of the errors are from functions, not from top-levels, so this must be something subtle.I will try to take a look later today. In the meantime, @sobolevn is there any chance you can bisect these errors to a specific mypy commit?
ilevkivskyi commentedon Apr 1, 2026
Oh, I think I see it, there is
few lines above
resolve_setting()definition. So I guess both issues are caused by @hauntsaninja reachability work.woodruffw commentedon Apr 1, 2026
I think I have a similar case of this, in pypi/pypi-attestations#178 -- we have pytest assertions like this:
..which mypy now thinks are always asserted, causing subsequent lines to be flagged as unreachable. In reality these assertions pass and subsequent lines are reachable, but I suspect the mismatch here might happen because mypy sees
TypeAdapteras a discrete type and assumes that the__eq__cannot hold?[-]Regressions in `1.20` release[/-][+][1.20 regression] module level reachability in django-modern-rest[/+][-][1.20 regression] module level reachability in django-modern-rest[/-][+][1.20 regression] reachability in django-modern-rest[/+]hauntsaninja commentedon Apr 1, 2026
The
resolve_settingis a genuine issue (comparing frozenset[str] to set[StrEnum]). I will look furtherFor the unreachable issues in your tests, a lot of them trace back to a bug in django-stubs. I opened a PR for this: typeddjango/django-stubs#3256
(Note in the limit I expect
warn_unreachableto have more false positives in tests. It is currently an opt-in config knob. So different configuration for tests may be part of the story)In woodruffw's case, mypy and earlier versions of mypy reveal
TypeAdapter(impl.Publisher).validate_json(json.dumps(gh_raw))as Never. In 1.20, this now has an impact on reachability. Looking further on this as wellAvoid narrowing to unreachable at module level
hauntsaninja commentedon Apr 1, 2026
Okay.
The first issue is that
resolve_settingis a confusing symptom. I opened #21144 which is one way to fix thisThe second issue is the underlying reachability implicated by
resolve_settinginvolving set and frozenset. I have opened a new issue for that over here: #21143The third issue was a bug in django-stubs for which the fix has been merged
The fourth issue is woodruffw's. For that one, I'm currently leaning towards not doing anything. I think it's desirable that warn_unreachable indicates you are narrowing to Never here. It is an opt-in setting, so seems useful to know that
TypeAdapter(impl.Publisher).validate_json(json.dumps(gh_raw))is not doing what we hope it might.The next question is why isn't that doing what we hope it might, and the issue there is with the typing of
pydantic.TypeAdapter. One way to fix this might be whenTypeFormsupport is landed.I think that gets everything currently mentioned here, so will close this issue out once #21144 is merged. If you are stumbling upon this issue, consider opening a new one.
Avoid narrowing to unreachable at module level (#21144)
Avoid narrowing to unreachable at module level (#21144)