def _bad_string_concatenation(self, other_type_name, string_first):
        self.intro_text = "Your program is trying to put together " + (
            "a string and %s." if string_first else "%s and a string."
        ) % _get_phrase_for_object(other_type_name)

        self.suggestions.append(
            Suggestion(
                "convert-other-operand-to-string",
                "Did you mean to treat both sides as text and produce a string?",
                "In this case you should apply function `str` to the %s "
                % _get_phrase_for_object(other_type_name, False)
                + "in order to convert it to string first, eg:\n\n"
                + ("`'abc' + str(%s)`" if string_first else "`str(%s) + 'abc'`")
                % _get_sample_for_type(other_type_name),
                8,
            )
        )

        if other_type_name in ("float", "int"):
            self.suggestions.append(
                Suggestion(
                    "convert-other-operand-to-number",
                    "Did you mean to treat both sides as numbers and produce a sum?",
                    "In this case you should first convert the string to a number "
                    + "using either function `float` or `int`, eg:\n\n"
                    + ("`float('3.14') + 22`" if string_first else "`22 + float('3.14')`"),
                    7,
                )
            )
    def __init__(self, error_info):
        super().__init__(error_info)

        self.intro_text = (
            "Python was asked to do an operation with an object which " + "doesn't support it."
        )

        self.suggestions = [
            Suggestion(
                "step-to-find-type-error",
                "Did you expect another type?",
                "Step through your program to see why this type appears here.",
                3,
            ),
            Suggestion(
                "look-documentation-type-error",
                "Maybe you forgot some details about this operation?",
                "Look up the documentation or perform a web search with the error message.",
                2,
            ),
        ]

        # overwrite / add for special cases
        # something + str or str + something
        for r, string_first in [
            (r"unsupported operand type\(s\) for \+: '(.+?)' and 'str'", False),
            (r"^Can't convert '(.+?)' object to str implicitly$", True),  # Python 3.5
            (r"^must be str, not (.+)$", True),  # Python 3.6
            (r'^can only concatenate str (not "(.+?)") to str$', True),  # Python 3.7
        ]:
            m = re.match(r, error_info["message"], re.I)  # @UndefinedVariable
            if m is not None:
                self._bad_string_concatenation(m.group(1), string_first)
                return
    def _sug_bad_spelling(self):

        # Yes, it would be more proper to consult builtins from the backend,
        # but it's easier this way...
        all_names = {name for name in dir(builtins) if not name.startswith("_")}
        all_names |= {"pass", "break", "continue", "return", "yield"}

        if self.last_frame.globals is not None:
            all_names |= set(self.last_frame.globals.keys())
        if self.last_frame.locals is not None:
            all_names |= set(self.last_frame.locals.keys())

        similar_names = {self.name}
        if all_names:
            relevance = 0
            for name in all_names:
                sim = name_similarity(name, self.name)
                if sim > 4:
                    similar_names.add(name)
                relevance = max(sim, relevance)
        else:
            relevance = 3

        if len(similar_names) > 1:
            body = "I found similar names. Are all of them spelled correctly?\n\n"
            for name in sorted(similar_names, key=lambda x: x.lower()):
                # TODO: add location info
                body += "* `%s`\n\n" % name
        else:
            body = (
                "Compare the name with corresponding definition / assignment / documentation."
                + " Don't forget that case of the letters matters!"
            )

        return Suggestion("bad-spelling-name", "Did you misspell it (somewhere)?", body, relevance)
 def _sug_bad_spelling(self):
     # TODO: compare with attributes of known types
     return Suggestion(
         "bad-spelling-attribute",
         "Did you misspell the name?",
         "Don't forget that case of the letters matters too!",
         3,
     )
 def _sug_not_defined_yet(self):
     return Suggestion(
         "not-defined-yet",
         "Has Python executed the definition?",
         (
             "Don't forget that name becomes defined when corresponding definition ('=', 'def' or 'import') gets executed."
             + " If the definition comes later in code or is inside an if-statement, Python may not have executed it (yet)."
             + "\n\n"
             + "Make sure Python arrives to the definition before it arrives to this line. When in doubt, "
             + "`use the debugger <debuggers.rst>`_."
         ),
         2,
     )
    def _sug_missing_quotes(self):
        if self._is_attribute_value() or self._is_call_function() or self._is_subscript_value():
            relevance = 0
        else:
            relevance = 5

        return Suggestion(
            "missing-quotes",
            "Did you actually mean string (text)?",
            'If you didn\'t mean a variable but literal text "%s", then surround it with quotes.'
            % self.name,
            relevance,
        )
    def __init__(self, error_info):
        super().__init__(error_info)

        if "Address already in use" in self.error_info["message"]:
            self.intro_text = "Your programs tries to listen on a port which is already taken."
            self.suggestions = [
                Suggestion(
                    "kill-by-port-type-error",
                    "Want to close the other process?",
                    self.get_kill_process_instructions(),
                    5,
                ),
                Suggestion(
                    "use-another-type-error",
                    "Can you use another port?",
                    "If you don't want to mess with the other process, then check whether"
                    + " you can configure your program to use another port.",
                    3,
                ),
            ]

        else:
            self.intro_text = "No specific information is available for this error."
    def _sug_bad_type(self):
        if self._is_call_function():
            action = "call this function on"
        else:
            action = "ask this attribute from"

        return Suggestion(
            "wrong-type-attribute",
            "Did you expect another type?",
            "If you didn't mean %s %s, " % (action, _get_phrase_for_object(self.type_name))
            + "then step through your program to see "
            + "why this type appears here.",
            3,
        )
    def _sug_local_from_global(self):
        relevance = 0
        body = None

        if self.last_frame.code_name == "<module>" and self.last_frame_module_ast is not None:
            function_names = set()
            for node in ast.walk(self.last_frame_module_ast):
                if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                    if self.name in map(lambda x: x.arg, node.args.args):
                        function_names.add(node.name)
                    # TODO: varargs, kw, ...
                    declared_global = False
                    for localnode in ast.walk(node):
                        # print(node.name, localnode)
                        if (
                            isinstance(localnode, ast.Name)
                            and localnode.id == self.name
                            and isinstance(localnode.ctx, ast.Store)
                        ):
                            function_names.add(node.name)
                        elif isinstance(localnode, ast.Global) and self.name in localnode.names:
                            declared_global = True

                    if node.name in function_names and declared_global:
                        function_names.remove(node.name)

            if function_names:
                relevance = 9
                body = (
                    (
                        "Name `%s` defined in `%s` is not accessible in the global/module level."
                        % (self.name, " and ".join(function_names))
                    )
                    + "\n\nIf you need that data at the global level, then consider changing the function so that it `return`-s the value."
                )

        return Suggestion(
            "local-from-global",
            "Are you trying to acces a local variable outside of the function?",
            body,
            relevance,
        )
    def _sug_wrong_attribute_instead_of_len(self):

        if self.type_name == "str":
            goal = "length"
        elif self.type_name == "bytes":
            goal = "number of bytes"
        elif self.type_name == "list":
            goal = "number of elements"
        elif self.type_name == "tuple":
            goal = "number of elements"
        elif self.type_name == "set":
            goal = "number of elements"
        elif self.type_name == "dict":
            goal = "number of entries"
        else:
            return

        return Suggestion(
            "wrong-attribute-instead-of-len",
            "Did you mean to ask the %s?" % goal,
            "This can be done with function `len`, eg:\n\n`len(%s)`"
            % _get_sample_for_type(self.type_name),
            (9 if self.att_name.lower() in ("len", "length", "size") else 0),
        )
    def _sug_missing_or_misplaced_colon(self):
        i = 0
        title = "Did you forget the colon?"
        relevance = 0
        body = ""
        while i < len(self.tokens) and self.tokens[i].type != token.ENDMARKER:
            t = self.tokens[i]
            if t.string in [
                "if",
                "elif",
                "else",
                "while",
                "for",
                "with",
                "try",
                "except",
                "finally",
                "class",
                "def",
            ]:
                keyword_pos = i
                while (
                    self.tokens[i].type
                    not in [
                        token.NEWLINE,
                        token.ENDMARKER,
                        token.COLON,  # colon may be OP
                        token.RBRACE,
                    ]
                    and self.tokens[i].string != ":"
                ):

                    old_i = i
                    if self.tokens[i].string in "([{":
                        i = self._skip_braced_part(i)
                        assert i > old_i
                        if i == len(self.tokens):
                            return None
                    else:
                        i += 1

                if self.tokens[i].string != ":":
                    relevance = 9
                    body = "`%s` header must end with a colon." % t.string
                    break

                # Colon was present, but maybe it should have been right
                # after the keyword.
                if (
                    t.string in ["else", "try", "finally"]
                    and self.tokens[keyword_pos + 1].string != ":"
                ):
                    title = "Incorrect use of `%s`" % t.string
                    body = "Nothing is allowed between `%s` and colon." % t.string
                    relevance = 9
                    if (
                        self.tokens[keyword_pos + 1].type not in (token.NEWLINE, tokenize.COMMENT)
                        and t.string == "else"
                    ):
                        body = "If you want to specify a conditon, then use `elif` or nested `if`."
                    break

            i += 1

        return Suggestion("missing-or-misplaced-colon", title, body, relevance)
    def _sug_missing_import(self):
        likely_importable_functions = {
            "math": {"ceil", "floor", "sqrt", "sin", "cos", "degrees"},
            "random": {"randint"},
            "turtle": {
                "left",
                "right",
                "forward",
                "fd",
                "goto",
                "setpos",
                "Turtle",
                "penup",
                "up",
                "pendown",
                "down",
                "color",
                "pencolor",
                "fillcolor",
                "begin_fill",
                "end_fill",
                "pensize",
                "width",
            },
            "re": {"search", "match", "findall"},
            "datetime": {"date", "time", "datetime", "today"},
            "statistics": {
                "mean",
                "median",
                "median_low",
                "median_high",
                "mode",
                "pstdev",
                "pvariance",
                "stdev",
                "variance",
            },
            "os": {"listdir"},
            "time": {"time", "sleep"},
        }

        body = None

        if self._is_call_function():
            relevance = 5
            for mod in likely_importable_functions:
                if self.name in likely_importable_functions[mod]:
                    relevance += 3
                    body = (
                        "If you meant `%s` from module `%s`, then add\n\n`from %s import %s`\n\nto the beginning of your script."
                        % (self.name, mod, mod, self.name)
                    )
                    break

        elif self._is_attribute_value():
            relevance = 5
            body = (
                "If you meant module `%s`, then add `import %s` to the beginning of your script"
                % (self.name, self.name)
            )

            if self.name in likely_importable_functions:
                relevance += 3

        elif self._is_subscript_value() and self.name != "argv":
            relevance = 0
        elif self.name == "pi":
            body = "If you meant the constant π, then add `from math import pi` to the beginning of your script."
            relevance = 8
        elif self.name == "argv":
            body = "If you meant the list with program arguments, then add `from sys import argv` to the beginning of your script."
            relevance = 8
        else:
            relevance = 3

        if body is None:
            body = "Some functions/variables need to be imported before they can be used."

        return Suggestion("missing-import", "Did you forget to import it?", body, relevance)
    def _sug_unbalanced_parens(self):
        problem = self._find_first_braces_problem()
        if not problem:
            return None

        return Suggestion("missing-or-misplaced-colon", "Unbalanced brackets", problem[1], 8)