File: index_error.py

package info (click to toggle)
python-friendly-traceback 0.7.62%2Bgit20240811.d7dbff6-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,264 kB
  • sloc: python: 21,500; makefile: 4
file content (162 lines) | stat: -rw-r--r-- 5,976 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""Getting specific information for IndexError"""

import ast
import re
from typing import Dict

import pure_eval

from .. import debug_helper, info_variables
from ..ft_gettext import current_lang
from ..message_parser import get_parser
from ..tb_data import TracebackData  # for type checking only
from ..typing_info import CauseInfo  # for type checking only

parser = get_parser(IndexError)
_ = current_lang.translate


@parser._add
def object_assignment_out_of_range(message: str, tb_data: TracebackData) -> CauseInfo:
    pattern = re.compile(r"(.*) assignment index out of range")
    match = re.search(pattern, message)
    if not match:
        return {}

    obj_type = match[1]
    frame = tb_data.exception_frame
    # first, try to identify object
    left_hand_side = tb_data.bad_line.split("=")[0].strip()
    all_objects = info_variables.get_all_objects(left_hand_side, frame)
    for name, sequence in all_objects["name, obj"]:
        truncated = left_hand_side.replace(name, "", 1).strip()
        if truncated.startswith("[") and truncated.endswith("]"):
            break
    else:  # pragma: no cover
        cause = _(
            "You have tried to assign a value to an item of an object\n"
            "of type `{obj_type}` which I cannot identify.\n"
            "The index you gave was not an allowed value.\n"
        ).format(obj_type=obj_type)
        return {"cause": cause}

    index = truncated[1:-1]
    length = len(sequence)

    cause = _(
        "You have tried to assign a value to index `{index}` of `{name}`,\n"
        "{obj_type} of length `{length}`.\n"
    ).format(
        index=index,
        name=name,
        length=length,
        obj_type=info_variables.convert_type(obj_type),
    )

    if length != 0:
        cause += _(
            "The valid index values of `{name}` are integers ranging from\n"
            "`{min}` to `{max}`.\n"
        ).format(name=name, min=-length, max=length - 1)
        if index == length:
            hint = _(
                "Remember: the first item of {obj_type} is not at index 1 but at index 0.\n"
            ).format(obj_type=info_variables.convert_type(obj_type))
            return {"cause": cause, "suggest": hint}
    else:
        hint = _("`{name}` contains no item.\n").format(name=name)
        cause = _(
            "You have tried to assign a value to index `{index}` of `{name}`,\n"
            "{obj_type} which contains no item.\n"
        ).format(index=index, name=name, obj_type=info_variables.convert_type(obj_type))
        return {"cause": cause, "suggest": hint}

    return {"cause": cause}


def cannot_identify_object(obj_type: str, bad_line: str) -> Dict:
    message = f"Cannot identify `{obj_type}` object. line: {bad_line}"
    debug_helper.log(message)
    cause = _(
        "You have tried to get an item of an object\n"
        "of type `{obj_type}` which I cannot identify.\n"
        "The index you gave was not an allowed value.\n"
    ).format(obj_type=obj_type)
    return {"cause": cause}


@parser._add
def index_out_of_range(message: str, tb_data: TracebackData) -> CauseInfo:
    pattern = re.compile(r"(.*) index out of range")
    match = re.search(pattern, message)
    if not match:
        return {}

    obj_type = match[1]
    frame = tb_data.exception_frame
    bad_line = tb_data.bad_line
    # first, try to identify object
    all_objects = info_variables.get_all_objects(bad_line, frame)
    for name, sequence in all_objects["name, obj"]:
        truncated = bad_line.replace(name, "", 1).strip()
        if truncated.startswith("[") and truncated.endswith("]"):
            break
    else:  # pragma: no cover
        return cannot_identify_object(obj_type, bad_line)

    try:
        node = tb_data.node
    except Exception:  # noqa # pragma: no cover
        return cannot_identify_object(obj_type, bad_line)

    if not (node and isinstance(node, ast.Subscript)):  # pragma: no cover
        return cannot_identify_object(obj_type, bad_line)

    length = len(sequence)
    evaluator = pure_eval.Evaluator.from_frame(frame)
    # The information that we want may differ for different Python versions
    try:
        index = evaluator[node.slice.value]  # noqa
    except Exception:  # noqa
        try:
            index = evaluator[node.slice]
        except Exception:  # noqa  # pragma: no cover
            debug_helper.log("Unknown index: new case to consider.")
            cause = _(
                "You have tried to get an item from `{name}`,\n"
                "{obj_type} of length `{length}`, by using a value for the index\n"
                "that I cannot determine but which is not allowed.\n"
            ).format(
                name=name, length=length, obj_type=info_variables.convert_type(obj_type)
            )
            return {"cause": cause}

    cause = _(
        "You have tried to get the item with index `{index}` of `{name}`,\n"
        "{obj_type} of length `{length}`.\n"
    ).format(
        index=index,
        name=name,
        length=length,
        obj_type=info_variables.convert_type(obj_type),
    )

    if length != 0:
        cause += _(
            "The valid index values of `{name}` are integers ranging from\n"
            "`{min}` to `{max}`.\n"
        ).format(name=name, min=-length, max=length - 1)
        if index == length:
            hint = _(
                "Remember: the first item of {obj_type} is not at index 1 but at index 0.\n"
            ).format(obj_type=info_variables.convert_type(obj_type))
            return {"cause": cause, "suggest": hint}
    else:
        hint = _("`{name}` contains no item.\n").format(name=name)
        cause = _(
            "You have tried to get the item with index `{index}` of `{name}`,\n"
            "{obj_type} which contains no item.\n"
        ).format(index=index, name=name, obj_type=info_variables.convert_type(obj_type))
        return {"cause": cause, "suggest": hint}

    return {"cause": cause}