`RecursionError` with recursive type alias and concatenate
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
Crash Report
While trying to make an infinitely expanding function, I kept getting 500 errors on the playground. Trying it on the CLI, mypy crashed with error: INTERNAL ERROR, with the traceback ending in RecursionError: maximum recursion depth exceeded.
Traceback
Collapsed for readability
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "Scripts\mypy.exe\__main__.py", line 10, in <module>
sys.exit(console_entry())
File "Lib\site-packages\mypy\__main__.py", line 15, in console_entry
main()
File "Lib\site-packages\mypy\main.py", line 141, in main
res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
File "Lib\site-packages\mypy\main.py", line 225, in run_build
res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
File "Lib\site-packages\mypy\build.py", line 391, in build
result = build_inner(
File "Lib\site-packages\mypy\build.py", line 488, in build_inner
graph = dispatch(sources, manager, stdout)
File "Lib\site-packages\mypy\build.py", line 3754, in dispatch
process_graph(graph, manager)
File "Lib\site-packages\mypy\build.py", line 4206, in process_graph
done, still_working, results = manager.wait_for_done(graph)
File "Lib\site-packages\mypy\build.py", line 1208, in wait_for_done
process_stale_scc(graph, next_scc, self)
File "Lib\site-packages\mypy\build.py", line 4371, in process_stale_scc
mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors)
File "Lib\site-packages\mypy\semanal_main.py", line 92, in semantic_analysis_for_scc
process_functions(graph, scc, patches)
File "Lib\site-packages\mypy\semanal_main.py", line 275, in process_functions
process_top_level_function(
File "Lib\site-packages\mypy\semanal_main.py", line 314, in process_top_level_function
deferred, incomplete, progress = semantic_analyze_target(
File "Lib\site-packages\mypy\semanal_main.py", line 380, in semantic_analyze_target
analyzer.refresh_partial(
File "Lib\site-packages\mypy\semanal.py", line 704, in refresh_partial
self.accept(node)
File "Lib\site-packages\mypy\semanal.py", line 7710, in accept
node.accept(self)
~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\nodes.py", line 1089, in accept
return visitor.visit_func_def(self)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\semanal.py", line 988, in visit_func_def
self.analyze_func_def(defn)
~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\semanal.py", line 1032, in analyze_func_def
if self.found_incomplete_ref(tag) or has_placeholder(result):
~~~~~~~~~~~~~~~^^^^^^^^
File "Lib\site-packages\mypy\semanal_shared.py", line 377, in has_placeholder
return typ.accept(HasPlaceholders())
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\mypy\types.py", line 2324, in accept
return visitor.visit_callable_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\type_visitor.py", line 554, in visit_callable_type
args = self.query_types(t.arg_types)
File "Lib\site-packages\mypy\type_visitor.py", line 605, in query_types
return any(t.accept(self) for t in types)
^^^
File "Lib\site-packages\mypy\type_visitor.py", line 605, in <genexpr>
return any(t.accept(self) for t in types)
~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\types.py", line 418, in accept
return visitor.visit_type_alias_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\type_visitor.py", line 598, in visit_type_alias_type
return get_proper_type(t).accept(self)
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\types.py", line 2324, in accept
return visitor.visit_callable_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\type_visitor.py", line 555, in visit_callable_type
ret = t.ret_type.accept(self)
^ Above 4 traceback items repeated 4085 more times (excluded for brevity)
File "Lib\site-packages\mypy\types.py", line 418, in accept
return visitor.visit_type_alias_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\type_visitor.py", line 598, in visit_type_alias_type
return get_proper_type(t).accept(self)
~~~~~~~~~~~~~~~^^^
File "Lib\site-packages\mypy\types.py", line 3649, in get_proper_type
typ = typ._expand_once()
File "Lib\site-packages\mypy\types.py", line 382, in _expand_once
return self.alias.target.accept(InstantiateAliasVisitor(mapping))
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\mypy\types.py", line 2324, in accept
return visitor.visit_callable_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\expandtype.py", line 495, in visit_callable_type
arg_types = self.expand_types(t.arg_types)
File "Lib\site-packages\mypy\expandtype.py", line 616, in expand_types
a.append(t.accept(self))
~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\types.py", line 2324, in accept
return visitor.visit_callable_type(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "Lib\site-packages\mypy\expandtype.py", line 443, in visit_callable_type
param_spec = t.param_spec()
File "Lib\site-packages\mypy\types.py", line 2440, in param_spec
return arg_type.copy_modified(flavor=ParamSpecFlavor.BARE, prefix=prefix)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\mypy\types.py", line 827, in copy_modified
return ParamSpecType(
self.name,
...<7 lines>...
prefix=prefix if prefix is not _dummy else self.prefix,
)
File "Lib\site-packages\mypy\types.py", line 803, in __init__
super().__init__(name, fullname, id, upper_bound, default, line=line, column=column)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\mypy\types.py", line 609, in __init__
super().__init__(line, column)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "Lib\site-packages\mypy\types.py", line 267, in __init__
super().__init__(line, column)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded
To Reproduce
https://mypy-play.net/?gist=2c6fdf9918139e545067e2d5dd443b8f
from typing import Callable, reveal_type, Concatenate
type A[**P] = Callable[[Callable[P, None]], A[Concatenate[int, P]]]
def foo[**P](x: A[P]):
reveal_type(x)
Powershell command used
PS ~\Downloads> uvx mypy@https://github.com/python/mypy.git --show-traceback -c @'
from typing import Callable, reveal_type, Concatenate
type A[**P] = Callable[[Callable[P, None]], A[Concatenate[int, P]]]
def foo[**P](x: A[P]):
reveal_type(x)
'@ > output.txt
Your Environment
- Mypy version used: Current master
- Mypy command-line flags:
--show-traceback -c "code" - Mypy configuration options from
mypy.ini(and other config files): None - Python version used: 3.14? Whatever
uvxdoes. - Operating system and version: Windows 11
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run the minimal reproducer from the issue with --show-traceback, then inspect mypy/semanal_shared.py, mypy/type_visitor.py, mypy/types.py, and mypy/expandtype.py around has_placeholder, type-alias expansion, and callable traversal. Done means the recursive alias and Concatenate example no longer crashes with RecursionError or reports an INTERNAL ERROR.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100