File: runtime_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 (66 lines) | stat: -rw-r--r-- 2,550 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
import re

from .. import info_variables, token_utils
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(RuntimeError)
_ = current_lang.translate


@parser._add
def container_changed_size_during_iteration(
    message: str, tb_data: TracebackData
) -> CauseInfo:
    pattern = re.compile(r"(.*) changed size during iteration")
    match = re.search(pattern, message)
    if not match:
        return {}
    container_name = match[1].lower()
    frame = tb_data.exception_frame
    if container_name.startswith("dict"):
        container_name = "dict"
        obj_type = dict
    elif container_name == "set":
        obj_type = set
    else:
        return {}
    container_type = info_variables.convert_type(container_name)

    objects = info_variables.get_all_objects(tb_data.bad_line, frame)
    names = []
    for name, obj in objects["name, obj"]:
        if isinstance(obj, obj_type):
            names.append(name)

    tokens = token_utils.tokenize(tb_data.bad_line)
    loop_keywords = [tok.string for tok in tokens if tok.string in {"for", "while"}]

    loop_keywords = set(loop_keywords)
    if len(loop_keywords) == 1:
        for_while = "for" if "for" in loop_keywords else "while"
    else:
        for_while = "for/while"

    if len(names) == 1:
        cause = _(
            "While you were iterating over the items of `{name}` ({container_type})\n"
            "in a `{for_while}` loop, you either tried to add or remove items from it.\n"
            "Suggestion: start by making a copy of `{name}` and iterate over the items\n"
            "of that copy if you want to change `{name}` inside a loop.\n"
            "You might want to do this as follows:\n\n"
            "    my_{name} = {name}.copy()\n"
            "    for item in my_{name}:\n"
            "        # Change {name}\n"
        ).format(name=names[0], container_type=container_type, for_while=for_while)
    else:
        cause = _(
            "While you were iterating over the items of a container ({container_type})\n"
            "in a `{for_while}` loop, you either tried to add or remove items from it.\n"
            "Suggestion: start by making a copy of the container and iterate over the items\n"
            "of that copy if you want to change that container inside a loop.\n"
        ).format(container_type=container_type, for_while=for_while)

    return {"cause": cause}