Skip to content

IndexError when searching for fields in files with no newline at the of the file #119

Open

Description

@dwiel

In order to reproduce:

x.py:

from .test import system

system('hello world')

test/__init__.py

from os import system

note that test/__init__.py must not have a newline at the end of the file. Try to find_occurrences on system in the call in x.py and you will get this IndexError:

  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/contrib/findit.py", line 35, in find_occurrences
    return _find_locations(finder, resources, job_set)
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/contrib/findit.py", line 111, in _find_locations
    for occurrence in finder.find_occurrences(resource):
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/refactor/occurrences.py", line 75, in find_occurrences
    result = filter(occurrence)
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/refactor/occurrences.py", line 205, in __call__
    if same_pyname(self.pyname, occurrence.get_pyname()):
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/utils.py", line 11, in _wrapper
    setattr(self, name, func(self, *args, **kwds))
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/refactor/occurrences.py", line 131, in get_pyname
    return self.tools.name_finder.get_pyname_at(self.offset)
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/evaluate.py", line 76, in get_pyname_at
    return self.get_primary_and_pyname_at(offset)[1]
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/evaluate.py", line 108, in get_primary_and_pyname_at
    if self.worder.is_from_aliased(offset):
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 66, in is_from_aliased
    return self.code_finder.is_from_aliased(offset)
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 370, in is_from_aliased
    as_start = self._find_word_start(as_end)
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 131, in _find_word_start
    while current_offset >= 0 and self._is_id_char(current_offset):
  File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 163, in _is_id_char
    return self.code[offset].isalnum() or self.code[offset] == '_'
IndexError: string index out of range

I tried to go through and find the right place to fix it, but wasn't sure where to make the change. We don't want to change the FIle.read to enforce newline at end of file. I'm having trouble getting a test case that has this error.

Making this change seems to help:

rope/base/worder.py:162
    def _is_id_char(self, offset):
+        if offset >= len(self.code):                                                                                                                                                                                                                                                      
+            return False                                                                                                                                                                                                                                                                  
        return self.code[offset].isalnum() or self.code[offset] == '_'

I'm not sure if this is the right way to go about fixing it though, it seems like it might need to be fixed somewhere else.

Activity

  1. mcepl commented on Jul 27, 2015

    @mcepl
    Contributor

    @aligrudi ... can I get your wisdom here, please?

  2. aligrudi commented on Jul 27, 2015

    @aligrudi
    Member

    Zach Dwiel notifications@github.com wrote:

     File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 131, in _find_word_start
        while current_offset >= 0 and self._is_id_char(current_offset):
      File "/home/ubuntu/anaconda/lib/python2.7/site-packages/rope/base/worder.py", line 163, in _is_id_char
        return self.code[offset].isalnum() or self.code[offset] == '_'
    IndexError: string index out of range
    
    

    I tried to go through and find the right place to fix it, but wasn't sure where to make the change. We don't want to change the FIle.read to enforce newline at end of file. I'm having trouble getting a test case that has this error.

    You may add a test case to WordRangeFinderTest in codeanalyzetest.py.

    Making this change seems to help:

    rope/base/worder.py:162
        def _is_id_char(self, offset):
    +        if offset >= len(self.code):
    +            return False
            return self.code[offset].isalnum() or self.code[offset] == '_'
    

    I'm not sure if this is the right way to go about fixing it though, it seems like it might need to be fixed somewhere else.

    The problem is that in is_from_aliased(), (end + 1) is passed to _find_word_end() to find the end of the next word (end is len(self.code) - 1 and end + 1 goes past self.code). I wonder if is_from_aliased() function should return False if an IndexError is raised (it already does so for ValueError). Does doing so fix the problem?

    Ali
    
  3. added
    bugUnexpected or incorrect user-visible behavior
    on Aug 9, 2015
  4. mcepl commented on Nov 2, 2026

    @mcepl
    Contributor

    This still reproduces on master (46ebf37) with Python 3.13, and it
    breaks rename as well as find_occurrences():

    import pathlib, tempfile
    from rope.base.project import Project
    from rope.contrib import findit
    from rope.refactor.rename import Rename
    
    root = pathlib.Path(tempfile.mkdtemp())
    (root / "x.py").write_text("from t import system\n\nsystem('hi')\n")
    (root / "t.py").write_text("from os import system")  # no trailing newline
    project = Project(str(root), ropefolder=None)
    resource = project.get_resource("x.py")
    offset = resource.read().rindex("system")
    findit.find_occurrences(project, resource, offset)  # IndexError
    Rename(project, resource, offset).get_changes("run")  # IndexError

    Both end in Worder._is_id_char() via is_from_aliased() and
    _find_word_start(), where self.code[offset] is read past the end of
    t.py, the module whose last token is the name. That matches
    @aligrudi's 2015 diagnosis above. is_from_aliased() now clamps
    as_end with min(..., len(self.code)), but that is still one past
    the last character, and it catches ValueError but not IndexError.

  5. added a commit that references this issue on Nov 5, 2026
    d9f8f35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUnexpected or incorrect user-visible behavior

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions