rope
rope copied to clipboard
MismatchedTokenError when parsing TypeVar with constraints in Python 3.12
Describe the bug When using Python 3.12’s new syntax for TypeVar with constraints, rope fails to correctly parse the function definition and raises a MismatchedTokenError.
To Reproduce Run the code below:
from rope.base.project import Project
from rope.refactor.inline import InlineVariable
project = Project('.', ropefolder=None)
file = project.root.create_file('file.py')
file.write('''\
s = None
print(s)
def _[T: (A, B)](x):
pass
''')
try:
changes = InlineVariable(project, file, 0).get_changes('s')
project.do(changes)
print(file.read())
finally:
file.remove()
project.close()
And get the following error:
Traceback (most recent call last):
File "Lib\site-packages\rope\refactor\patchedast.py", line 837, in consume
new_offset = self.source.index(token, self.offset)
ValueError: substring not found
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "rope_bug.py", line 14, in <module>
changes = InlineVariable(project, file, 0).get_changes('s')
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\inline.py", line 241, in __init__
super().__init__(*args, **kwds)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\inline.py", line 82, in __init__
self.pyname = _get_pyname(project, resource, offset)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\inline.py", line 684, in _get_pyname
pyname = evaluate.eval_location(pymodule, offset)
File "Lib\site-packages\rope\base\evaluate.py", line 22, in eval_location
return eval_location2(pymodule, offset)[1]
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\base\evaluate.py", line 28, in eval_location2
return pyname_finder.get_primary_and_pyname_at(offset)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "Lib\site-packages\rope\base\evaluate.py", line 95, in get_primary_and_pyname_at
holding_scope = self.module_scope.get_inner_scope_for_offset(offset)
File "Lib\site-packages\rope\base\pyscopes.py", line 156, in get_inner_scope_for_offset
return self._scope_finder.get_holding_scope_for_offset(self, offset)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "Lib\site-packages\rope\base\pyscopes.py", line 310, in get_holding_scope_for_offset
if inner_scope.in_region(offset):
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "Lib\site-packages\rope\base\pyscopes.py", line 114, in in_region
region = self.get_region()
File "Lib\site-packages\rope\base\pyscopes.py", line 103, in get_region
self._calculate_scope_regions_for_module()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "Lib\site-packages\rope\base\pyscopes.py", line 109, in _calculate_scope_regions_for_module
self._get_global_scope()._calculate_scope_regions()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "Lib\site-packages\rope\base\utils\__init__.py", line 12, in _wrapper
setattr(self, name, func(self, *args, **kwds))
~~~~^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\base\pyscopes.py", line 140, in _calculate_scope_regions
patchedast.patch_ast(self.pyobject.get_ast(), source)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 37, in patch_ast
walker(node)
~~~~~~^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 80, in __call__
return method(node)
File "Lib\site-packages\rope\refactor\patchedast.py", line 625, in _Module
self._handle(node, list(node.body), eat_spaces=True)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 113, in _handle
self(child)
~~~~^^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 80, in __call__
return method(node)
File "Lib\site-packages\rope\refactor\patchedast.py", line 500, in _FunctionDef
self._handle_function_def_node(node, is_async=False)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 497, in _handle_function_def_node
self._handle(node, children)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 142, in _handle
start = self._handle_parens(children, start, formats)
File "Lib\site-packages\rope\refactor\patchedast.py", line 164, in _handle_parens
new_end = self.source.consume(")")[1]
~~~~~~~~~~~~~~~~~~~^^^^^
File "Lib\site-packages\rope\refactor\patchedast.py", line 843, in consume
raise MismatchedTokenError(
f"Token <{token}> at {self._get_location()} cannot be matched"
)
rope.refactor.patchedast.MismatchedTokenError: Token <)> at (5, 8) cannot be matched
It appears that the new parentheses in the TypeVar constraint are not handled correctly.
Editor information:
- Project Python version: 3.13.3
- Rope Python version: 3.13.3
- Rope version: 1.13.0
- Text editor/IDE and version: None (rope used as a library)