import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def compute_bracket_indent(self):
        # pylint: disable=redefined-builtin
        self._study2()
        assert self.continuation == C_BRACKET
        j = self.lastopenbracketpos
        str = self.str  # @ReservedAssignment
        n = len(str)
        origi = i = str.rfind("\n", 0, j) + 1
        j = j + 1  # one beyond open bracket
        # find first list item; set i to start of its line
        while j < n:
            m = _itemre(str, j)
            if m:
                j = m.end() - 1  # index of first interesting char
                extra = 0
                break
            else:
                # this line is junk; advance to next line
                i = j = str.find("\n", j) + 1
        else:
            # nothing interesting follows the bracket;
            # reproduce the bracket line's indentation + a level
            j = i = origi
            while str[j] in " \t":
                j = j + 1
            extra = self.indent_width
        return len(str[i:j].expandtabs(self.tabwidth)) + extra


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def get_num_lines_in_stmt(self):
        self._study1()
        goodlines = self.goodlines
        return goodlines[-1] - goodlines[-2]


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def compute_backslash_indent(self):
        # pylint: disable=redefined-builtin
        self._study2()
        assert self.continuation == C_BACKSLASH
        str = self.str  # @ReservedAssignment
        i = self.stmt_start
        while str[i] in " \t":
            i = i + 1
        startpos = i

        # See whether the initial line starts an assignment stmt; i.e.,
        # look for an = operator
        endpos = str.find("\n", startpos) + 1
        found = level = 0
        while i < endpos:
            ch = str[i]
            if ch in "([{":
                level = level + 1
                i = i + 1
            elif ch in ")]}":
                if level:
                    level = level - 1
                i = i + 1
            elif ch == '"' or ch == "'":
                i = _match_stringre(str, i, endpos).end()
            elif ch == "#":
                break
            elif (
                level == 0
                and ch == "="
                and (i == 0 or str[i - 1] not in "=<>!")
                and str[i + 1] != "="
            ):
                found = 1
                break
            else:
                i = i + 1

        if found:
            # found a legit =, but it may be the last interesting
            # thing on the line
            i = i + 1  # move beyond the =
            found = re.match(r"\s*\\", str[i:endpos]) is None

        if not found:
            # oh well ... settle for moving beyond the first chunk
            # of non-whitespace chars
            i = startpos
            while str[i] not in " \t\n":
                i = i + 1

        return len(str[self.stmt_start : i].expandtabs(self.tabwidth)) + 1


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def get_base_indent_string(self):
        self._study2()
        i, n = self.stmt_start, self.stmt_end
        j = i
        str_ = self.str
        while j < n and str_[j] in " \t":
            j = j + 1
        return str_[i:j]


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def is_block_opener(self):
        self._study2()
        return self.lastch == ":"


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def is_block_closer(self):
        self._study2()
        return _closere(self.str, self.stmt_start) is not None


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def get_last_open_bracket_pos(self):
        self._study2()
        return self.lastopenbracketpos


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

    def get_last_stmt_bracketing(self):
        self._study2()
        return self.stmt_bracketing


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class RoughParser:

    _tran1 = {}

    _tran = StringTranslatePseudoMapping(_tran1, default_value=ord("x"))

    lastopenbracketpos = None

    stmt_bracketing = None

    def __init__(self, indent_width, tabwidth):
        self.indent_width = indent_width
        self.tabwidth = tabwidth

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)

    def set_index(self, index):
        """Set the index to which the functions relate.

        The index must be in the same statement.
        """
        indexinrawtext = len(self.rawtext) - len(self.text.get(index, self.stopatindex))
        if indexinrawtext < 0:
            raise ValueError("Index %s precedes the analyzed statement" % index)
        self.indexinrawtext = indexinrawtext
        # find the rightmost bracket to which index belongs
        self.indexbracket = 0
        while (
            self.indexbracket < len(self.bracketing) - 1
            and self.bracketing[self.indexbracket + 1][0] < self.indexinrawtext
        ):
            self.indexbracket += 1
        if (
            self.indexbracket < len(self.bracketing) - 1
            and self.bracketing[self.indexbracket + 1][0] == self.indexinrawtext
            and not self.isopener[self.indexbracket + 1]
        ):
            self.indexbracket += 1


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)

    def is_in_string(self):
        """Is the index given to the HyperParser in a string?"""
        # The bracket to which we belong should be an opener.
        # If it's an opener, it has to have a character.
        return self.isopener[self.indexbracket] and self.rawtext[
            self.bracketing[self.indexbracket][0]
        ] in ('"', "'")


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)

    def is_in_code(self):
        """Is the index given to the HyperParser in normal code?"""
        return not self.isopener[self.indexbracket] or self.rawtext[
            self.bracketing[self.indexbracket][0]
        ] not in ("#", '"', "'")


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)

    def get_surrounding_brackets(self, openers="([{", mustclose=False):
        """Return bracket indexes or None.

        If the index given to the HyperParser is surrounded by a
        bracket defined in openers (or at least has one before it),
        return the indices of the opening bracket and the closing
        bracket (or the end of line, whichever comes first).

        If it is not surrounded by brackets, or the end of line comes
        before the closing bracket and mustclose is True, returns None.
        """

        bracketinglevel = self.bracketing[self.indexbracket][1]
        before = self.indexbracket
        while (
            not self.isopener[before]
            or self.rawtext[self.bracketing[before][0]] not in openers
            or self.bracketing[before][1] > bracketinglevel
        ):
            before -= 1
            if before < 0:
                return None
            bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
        after = self.indexbracket + 1
        while after < len(self.bracketing) and self.bracketing[after][1] >= bracketinglevel:
            after += 1

        beforeindex = self.text.index(
            "%s-%dc" % (self.stopatindex, len(self.rawtext) - self.bracketing[before][0])
        )
        if after >= len(self.bracketing) or self.bracketing[after][0] > len(self.rawtext):
            if mustclose:
                return None
            afterindex = self.stopatindex
        else:
            # We are after a real char, so it is a ')' and we give the
            # index before it.
            afterindex = self.text.index(
                "%s-%dc" % (self.stopatindex, len(self.rawtext) - (self.bracketing[after][0] - 1))
            )

        return beforeindex, afterindex


import re
import string
from collections.abc import Mapping
from keyword import iskeyword
from typing import Dict

NUM_CONTEXT_LINES = (50, 500, 5000000)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, C_STRING_NEXT_LINES, C_BRACKET) = range(5)
_synchre = re.compile(
    r"""
    ^
    [ \t]*
    (?: while
    |   else
    |   def
    |   return
    |   assert
    |   break
    |   class
    |   continue
    |   elif
    |   try
    |   except
    |   raise
    |   import
    |   yield
    )
    \b
""",
    re.VERBOSE | re.MULTILINE,
).search
_junkre = re.compile(
    r"""
    [ \t]*
    (?: \# \S .* )?
    \n
""",
    re.VERBOSE,
).match
_match_stringre = re.compile(
    r"""
    \""" [^"\\]* (?:
                     (?: \\. | "(?!"") )
                     [^"\\]*
                 )*
    (?: \""" )?

|   " [^"\\\n]* (?: \\. [^"\\\n]* )* "?

|   ''' [^'\\]* (?:
                   (?: \\. | '(?!'') )
                   [^'\\]*
                )*
    (?: ''' )?

|   ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
    re.VERBOSE | re.DOTALL,
).match
_itemre = re.compile(
    r"""
    [ \t]*
    [^\s#\\]    # if we match, m.end()-1 is the interesting char
""",
    re.VERBOSE,
).match
_closere = re.compile(
    r"""
    \s*
    (?: return
    |   break
    |   continue
    |   raise
    |   pass
    )
    \b
""",
    re.VERBOSE,
).match
_chew_ordinaryre = re.compile(
    r"""
    [^[\](){}#'"\\]+
""",
    re.VERBOSE,
).match
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
_IS_ASCII_ID_FIRST_CHAR = [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]

class HyperParser:

    _ID_KEYWORDS = frozenset({"True", "False", "None"})

    _whitespace_chars = " \t\n\\"

    def __init__(self, text, index):
        "To initialize, analyze the surroundings of the given index."

        self.text = text

        parser = RoughParser(text.indent_width, text.tabwidth)

        def index2line(index):
            return int(float(index))

        lno = index2line(text.index(index))

        for context in NUM_CONTEXT_LINES:
            startat = max(lno - context, 1)
            startatindex = repr(startat) + ".0"
            stopatindex = "%d.end" % lno
            # We add the newline because PyParse requires a newline
            # at end. We add a space so that index won't be at end
            # of line, so that its status will be the same as the
            # char before it, if should.
            parser.set_str(text.get(startatindex, stopatindex) + " \n")
            bod = parser.find_good_parse_start(_build_char_in_string_func(startatindex))
            if bod is not None or startat == 1:
                break
        parser.set_lo(bod or 0)

        # We want what the parser has, minus the last newline and space.
        self.rawtext = parser.str[:-2]
        # Parser.str apparently preserves the statement we are in, so
        # that stopatindex can be used to synchronize the string with
        # the text box indices.
        self.stopatindex = stopatindex
        self.bracketing = parser.get_last_stmt_bracketing()
        # find which pairs of bracketing are openers. These always
        # correspond to a character of rawtext.
        self.isopener = [
            i > 0 and self.bracketing[i][1] > self.bracketing[i - 1][1]
            for i in range(len(self.bracketing))
        ]

        self.set_index(index)

    def get_expression(self):
        """Return a string with the Python expression which ends at the
        given index, which is empty if there is no real one.
        """
        if not self.is_in_code():
            raise ValueError("get_expression should only be called" "if index is inside a code.")

        rawtext = self.rawtext
        bracketing = self.bracketing

        brck_index = self.indexbracket
        brck_limit = bracketing[brck_index][0]
        pos = self.indexinrawtext

        last_identifier_pos = pos
        postdot_phase = True

        while 1:
            # Eat whitespaces, comments, and if postdot_phase is False - a dot
            while 1:
                if pos > brck_limit and rawtext[pos - 1] in self._whitespace_chars:
                    # Eat a whitespace
                    pos -= 1
                elif not postdot_phase and pos > brck_limit and rawtext[pos - 1] == ".":
                    # Eat a dot
                    pos -= 1
                    postdot_phase = True
                # The next line will fail if we are *inside* a comment,
                # but we shouldn't be.
                elif (
                    pos == brck_limit
                    and brck_index > 0
                    and rawtext[bracketing[brck_index - 1][0]] == "#"
                ):
                    # Eat a comment
                    brck_index -= 2
                    brck_limit = bracketing[brck_index][0]
                    pos = bracketing[brck_index + 1][0]
                else:
                    # If we didn't eat anything, quit.
                    break

            if not postdot_phase:
                # We didn't find a dot, so the expression end at the
                # last identifier pos.
                break

            ret = self._eat_identifier(rawtext, brck_limit, pos)
            if ret:
                # There is an identifier to eat
                pos = pos - ret
                last_identifier_pos = pos
                # Now, to continue the search, we must find a dot.
                postdot_phase = False
                # (the loop continues now)

            elif pos == brck_limit:
                # We are at a bracketing limit. If it is a closing
                # bracket, eat the bracket, otherwise, stop the search.
                level = bracketing[brck_index][1]
                while brck_index > 0 and bracketing[brck_index - 1][1] > level:
                    brck_index -= 1
                if bracketing[brck_index][0] == brck_limit:
                    # We were not at the end of a closing bracket
                    break
                pos = bracketing[brck_index][0]
                brck_index -= 1
                brck_limit = bracketing[brck_index][0]
                last_identifier_pos = pos
                if rawtext[pos] in "([":
                    # [] and () may be used after an identifier, so we
                    # continue. postdot_phase is True, so we don't allow a dot.
                    pass
                else:
                    # We can't continue after other types of brackets
                    if rawtext[pos] in "'\"":
                        # Scan a string prefix
                        while pos > 0 and rawtext[pos - 1] in "rRbBuU":
                            pos -= 1
                        last_identifier_pos = pos
                    break

            else:
                # We've found an operator or something.
                break

        return rawtext[last_identifier_pos : self.indexinrawtext]


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OpenIdMixin(object):

    def authenticate_redirect(
        self,
        callback_uri: Optional[str] = None,
        ax_attrs: List[str] = ["name", "email", "language", "username"],
    ) -> None:
        """Redirects to the authentication URL for this service.

        After authentication, the service will redirect back to the given
        callback URI with additional parameters including ``openid.mode``.

        We request the given attributes for the authenticated user by
        default (name, email, language, and username). If you don't need
        all those attributes for your app, you can request fewer with
        the ax_attrs keyword argument.

        .. versionchanged:: 6.0

            The ``callback`` argument was removed and this method no
            longer returns an awaitable object. It is now an ordinary
            synchronous function.
        """
        handler = cast(RequestHandler, self)
        callback_uri = callback_uri or handler.request.uri
        assert callback_uri is not None
        args = self._openid_args(callback_uri, ax_attrs=ax_attrs)
        endpoint = self._OPENID_ENDPOINT  # type: ignore
        handler.redirect(endpoint + "?" + urllib.parse.urlencode(args))


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OpenIdMixin(object):

    async def get_authenticated_user(
        self, http_client: Optional[httpclient.AsyncHTTPClient] = None
    ) -> Dict[str, Any]:
        """Fetches the authenticated user data upon redirect.

        This method should be called by the handler that receives the
        redirect from the `authenticate_redirect()` method (which is
        often the same as the one that calls it; in that case you would
        call `get_authenticated_user` if the ``openid.mode`` parameter
        is present and `authenticate_redirect` if it is not).

        The result of this method will generally be used to set a cookie.

        .. versionchanged:: 6.0

            The ``callback`` argument was removed. Use the returned
            awaitable object instead.
        """
        handler = cast(RequestHandler, self)
        # Verify the OpenID response via direct request to the OP
        args = dict(
            (k, v[-1]) for k, v in handler.request.arguments.items()
        )  # type: Dict[str, Union[str, bytes]]
        args["openid.mode"] = u"check_authentication"
        url = self._OPENID_ENDPOINT  # type: ignore
        if http_client is None:
            http_client = self.get_auth_http_client()
        resp = await http_client.fetch(
            url, method="POST", body=urllib.parse.urlencode(args)
        )
        return self._on_authentication_verified(resp)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OAuthMixin(object):

    async def authorize_redirect(
        self,
        callback_uri: Optional[str] = None,
        extra_params: Optional[Dict[str, Any]] = None,
        http_client: Optional[httpclient.AsyncHTTPClient] = None,
    ) -> None:
        """Redirects the user to obtain OAuth authorization for this service.

        The ``callback_uri`` may be omitted if you have previously
        registered a callback URI with the third-party service. For
        some services, you must use a previously-registered callback
        URI and cannot specify a callback via this method.

        This method sets a cookie called ``_oauth_request_token`` which is
        subsequently used (and cleared) in `get_authenticated_user` for
        security purposes.

        This method is asynchronous and must be called with ``await``
        or ``yield`` (This is different from other ``auth*_redirect``
        methods defined in this module). It calls
        `.RequestHandler.finish` for you so you should not write any
        other response after it returns.

        .. versionchanged:: 3.1
           Now returns a `.Future` and takes an optional callback, for
           compatibility with `.gen.coroutine`.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned
           awaitable object instead.

        """
        if callback_uri and getattr(self, "_OAUTH_NO_CALLBACKS", False):
            raise Exception("This service does not support oauth_callback")
        if http_client is None:
            http_client = self.get_auth_http_client()
        assert http_client is not None
        if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a":
            response = await http_client.fetch(
                self._oauth_request_token_url(
                    callback_uri=callback_uri, extra_params=extra_params
                )
            )
        else:
            response = await http_client.fetch(self._oauth_request_token_url())
        url = self._OAUTH_AUTHORIZE_URL  # type: ignore
        self._on_request_token(url, callback_uri, response)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OAuthMixin(object):

    async def get_authenticated_user(
        self, http_client: Optional[httpclient.AsyncHTTPClient] = None
    ) -> Dict[str, Any]:
        """Gets the OAuth authorized user and access token.

        This method should be called from the handler for your
        OAuth callback URL to complete the registration process. We run the
        callback with the authenticated user dictionary.  This dictionary
        will contain an ``access_key`` which can be used to make authorized
        requests to this service on behalf of the user.  The dictionary will
        also contain other fields such as ``name``, depending on the service
        used.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned
           awaitable object instead.
        """
        handler = cast(RequestHandler, self)
        request_key = escape.utf8(handler.get_argument("oauth_token"))
        oauth_verifier = handler.get_argument("oauth_verifier", None)
        request_cookie = handler.get_cookie("_oauth_request_token")
        if not request_cookie:
            raise AuthError("Missing OAuth request token cookie")
        handler.clear_cookie("_oauth_request_token")
        cookie_key, cookie_secret = [
            base64.b64decode(escape.utf8(i)) for i in request_cookie.split("|")
        ]
        if cookie_key != request_key:
            raise AuthError("Request token does not match cookie")
        token = dict(
            key=cookie_key, secret=cookie_secret
        )  # type: Dict[str, Union[str, bytes]]
        if oauth_verifier:
            token["verifier"] = oauth_verifier
        if http_client is None:
            http_client = self.get_auth_http_client()
        assert http_client is not None
        response = await http_client.fetch(self._oauth_access_token_url(token))
        access_token = _oauth_parse_response(response.body)
        user = await self._oauth_get_user_future(access_token)
        if not user:
            raise AuthError("Error getting user")
        user["access_token"] = access_token
        return user


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OAuth2Mixin(object):

    def authorize_redirect(
        self,
        redirect_uri: Optional[str] = None,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None,
        extra_params: Optional[Dict[str, Any]] = None,
        scope: Optional[List[str]] = None,
        response_type: str = "code",
    ) -> None:
        """Redirects the user to obtain OAuth authorization for this service.

        Some providers require that you register a redirect URL with
        your application instead of passing one via this method. You
        should call this method to log the user in, and then call
        ``get_authenticated_user`` in the handler for your
        redirect URL to complete the authorization process.

        .. versionchanged:: 6.0

           The ``callback`` argument and returned awaitable were removed;
           this is now an ordinary synchronous function.
        """
        handler = cast(RequestHandler, self)
        args = {"response_type": response_type}
        if redirect_uri is not None:
            args["redirect_uri"] = redirect_uri
        if client_id is not None:
            args["client_id"] = client_id
        if extra_params:
            args.update(extra_params)
        if scope:
            args["scope"] = " ".join(scope)
        url = self._OAUTH_AUTHORIZE_URL  # type: ignore
        handler.redirect(url_concat(url, args))


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class OAuth2Mixin(object):

    async def oauth2_request(
        self,
        url: str,
        access_token: Optional[str] = None,
        post_args: Optional[Dict[str, Any]] = None,
        **args: Any
    ) -> Any:
        """Fetches the given URL auth an OAuth2 access token.

        If the request is a POST, ``post_args`` should be provided. Query
        string arguments should be given as keyword arguments.

        Example usage:

        ..testcode::

            class MainHandler(tornado.web.RequestHandler,
                              tornado.auth.FacebookGraphMixin):
                @tornado.web.authenticated
                async def get(self):
                    new_entry = await self.oauth2_request(
                        "https://graph.facebook.com/me/feed",
                        post_args={"message": "I am posting from my Tornado application!"},
                        access_token=self.current_user["access_token"])

                    if not new_entry:
                        # Call failed; perhaps missing permission?
                        self.authorize_redirect()
                        return
                    self.finish("Posted a message!")

        .. testoutput::
           :hide:

        .. versionadded:: 4.3

        .. versionchanged::: 6.0

           The ``callback`` argument was removed. Use the returned awaitable object instead.
        """
        all_args = {}
        if access_token:
            all_args["access_token"] = access_token
            all_args.update(args)

        if all_args:
            url += "?" + urllib.parse.urlencode(all_args)
        http = self.get_auth_http_client()
        if post_args is not None:
            response = await http.fetch(
                url, method="POST", body=urllib.parse.urlencode(post_args)
            )
        else:
            response = await http.fetch(url)
        return escape.json_decode(response.body)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class TwitterMixin(OAuthMixin):

    _OAUTH_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token"

    _OAUTH_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token"

    _OAUTH_AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize"

    _OAUTH_AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate"

    _OAUTH_NO_CALLBACKS = False

    _TWITTER_BASE_URL = "https://api.twitter.com/1.1"

    async def authenticate_redirect(self, callback_uri: Optional[str] = None) -> None:
        """Just like `~OAuthMixin.authorize_redirect`, but
        auto-redirects if authorized.

        This is generally the right interface to use if you are using
        Twitter for single-sign on.

        .. versionchanged:: 3.1
           Now returns a `.Future` and takes an optional callback, for
           compatibility with `.gen.coroutine`.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned
           awaitable object instead.
        """
        http = self.get_auth_http_client()
        response = await http.fetch(
            self._oauth_request_token_url(callback_uri=callback_uri)
        )
        self._on_request_token(self._OAUTH_AUTHENTICATE_URL, None, response)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class TwitterMixin(OAuthMixin):

    _OAUTH_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token"

    _OAUTH_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token"

    _OAUTH_AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize"

    _OAUTH_AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate"

    _OAUTH_NO_CALLBACKS = False

    _TWITTER_BASE_URL = "https://api.twitter.com/1.1"

    async def twitter_request(
        self,
        path: str,
        access_token: Dict[str, Any],
        post_args: Optional[Dict[str, Any]] = None,
        **args: Any
    ) -> Any:
        """Fetches the given API path, e.g., ``statuses/user_timeline/btaylor``

        The path should not include the format or API version number.
        (we automatically use JSON format and API version 1).

        If the request is a POST, ``post_args`` should be provided. Query
        string arguments should be given as keyword arguments.

        All the Twitter methods are documented at http://dev.twitter.com/

        Many methods require an OAuth access token which you can
        obtain through `~OAuthMixin.authorize_redirect` and
        `~OAuthMixin.get_authenticated_user`. The user returned through that
        process includes an 'access_token' attribute that can be used
        to make authenticated requests via this method. Example
        usage:

        .. testcode::

            class MainHandler(tornado.web.RequestHandler,
                              tornado.auth.TwitterMixin):
                @tornado.web.authenticated
                async def get(self):
                    new_entry = await self.twitter_request(
                        "/statuses/update",
                        post_args={"status": "Testing Tornado Web Server"},
                        access_token=self.current_user["access_token"])
                    if not new_entry:
                        # Call failed; perhaps missing permission?
                        await self.authorize_redirect()
                        return
                    self.finish("Posted a message!")

        .. testoutput::
           :hide:

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned
           awaitable object instead.
        """
        if path.startswith("http:") or path.startswith("https:"):
            # Raw urls are useful for e.g. search which doesn't follow the
            # usual pattern: http://search.twitter.com/search.json
            url = path
        else:
            url = self._TWITTER_BASE_URL + path + ".json"
        # Add the OAuth resource request signature if we have credentials
        if access_token:
            all_args = {}
            all_args.update(args)
            all_args.update(post_args or {})
            method = "POST" if post_args is not None else "GET"
            oauth = self._oauth_request_parameters(
                url, access_token, all_args, method=method
            )
            args.update(oauth)
        if args:
            url += "?" + urllib.parse.urlencode(args)
        http = self.get_auth_http_client()
        if post_args is not None:
            response = await http.fetch(
                url, method="POST", body=urllib.parse.urlencode(post_args)
            )
        else:
            response = await http.fetch(url)
        return escape.json_decode(response.body)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class GoogleOAuth2Mixin(OAuth2Mixin):

    _OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth"

    _OAUTH_ACCESS_TOKEN_URL = "https://www.googleapis.com/oauth2/v4/token"

    _OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"

    _OAUTH_NO_CALLBACKS = False

    _OAUTH_SETTINGS_KEY = "google_oauth"

    async def get_authenticated_user(
        self, redirect_uri: str, code: str
    ) -> Dict[str, Any]:
        """Handles the login for the Google user, returning an access token.

        The result is a dictionary containing an ``access_token`` field
        ([among others](https://developers.google.com/identity/protocols/OAuth2WebServer#handlingtheresponse)).
        Unlike other ``get_authenticated_user`` methods in this package,
        this method does not return any additional information about the user.
        The returned access token can be used with `OAuth2Mixin.oauth2_request`
        to request additional information (perhaps from
        ``https://www.googleapis.com/oauth2/v2/userinfo``)

        Example usage:

        .. testcode::

            class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
                                           tornado.auth.GoogleOAuth2Mixin):
                async def get(self):
                    if self.get_argument('code', False):
                        access = await self.get_authenticated_user(
                            redirect_uri='http://your.site.com/auth/google',
                            code=self.get_argument('code'))
                        user = await self.oauth2_request(
                            "https://www.googleapis.com/oauth2/v1/userinfo",
                            access_token=access["access_token"])
                        # Save the user and access token with
                        # e.g. set_secure_cookie.
                    else:
                        self.authorize_redirect(
                            redirect_uri='http://your.site.com/auth/google',
                            client_id=self.settings['google_oauth']['key'],
                            scope=['profile', 'email'],
                            response_type='code',
                            extra_params={'approval_prompt': 'auto'})

        .. testoutput::
           :hide:

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned awaitable object instead.
        """  # noqa: E501
        handler = cast(RequestHandler, self)
        http = self.get_auth_http_client()
        body = urllib.parse.urlencode(
            {
                "redirect_uri": redirect_uri,
                "code": code,
                "client_id": handler.settings[self._OAUTH_SETTINGS_KEY]["key"],
                "client_secret": handler.settings[self._OAUTH_SETTINGS_KEY]["secret"],
                "grant_type": "authorization_code",
            }
        )

        response = await http.fetch(
            self._OAUTH_ACCESS_TOKEN_URL,
            method="POST",
            headers={"Content-Type": "application/x-www-form-urlencoded"},
            body=body,
        )
        return escape.json_decode(response.body)


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class FacebookGraphMixin(OAuth2Mixin):

    _OAUTH_ACCESS_TOKEN_URL = "https://graph.facebook.com/oauth/access_token?"

    _OAUTH_AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?"

    _OAUTH_NO_CALLBACKS = False

    _FACEBOOK_BASE_URL = "https://graph.facebook.com"

    async def get_authenticated_user(
        self,
        redirect_uri: str,
        client_id: str,
        client_secret: str,
        code: str,
        extra_fields: Optional[Dict[str, Any]] = None,
    ) -> Optional[Dict[str, Any]]:
        """Handles the login for the Facebook user, returning a user object.

        Example usage:

        .. testcode::

            class FacebookGraphLoginHandler(tornado.web.RequestHandler,
                                            tornado.auth.FacebookGraphMixin):
              async def get(self):
                  if self.get_argument("code", False):
                      user = await self.get_authenticated_user(
                          redirect_uri='/auth/facebookgraph/',
                          client_id=self.settings["facebook_api_key"],
                          client_secret=self.settings["facebook_secret"],
                          code=self.get_argument("code"))
                      # Save the user with e.g. set_secure_cookie
                  else:
                      self.authorize_redirect(
                          redirect_uri='/auth/facebookgraph/',
                          client_id=self.settings["facebook_api_key"],
                          extra_params={"scope": "read_stream,offline_access"})

        .. testoutput::
           :hide:

        This method returns a dictionary which may contain the following fields:

        * ``access_token``, a string which may be passed to `facebook_request`
        * ``session_expires``, an integer encoded as a string representing
          the time until the access token expires in seconds. This field should
          be used like ``int(user['session_expires'])``; in a future version of
          Tornado it will change from a string to an integer.
        * ``id``, ``name``, ``first_name``, ``last_name``, ``locale``, ``picture``,
          ``link``, plus any fields named in the ``extra_fields`` argument. These
          fields are copied from the Facebook graph API
          `user object <https://developers.facebook.com/docs/graph-api/reference/user>`_

        .. versionchanged:: 4.5
           The ``session_expires`` field was updated to support changes made to the
           Facebook API in March 2017.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned awaitable object instead.
        """
        http = self.get_auth_http_client()
        args = {
            "redirect_uri": redirect_uri,
            "code": code,
            "client_id": client_id,
            "client_secret": client_secret,
        }

        fields = set(
            ["id", "name", "first_name", "last_name", "locale", "picture", "link"]
        )
        if extra_fields:
            fields.update(extra_fields)

        response = await http.fetch(
            self._oauth_request_token_url(**args)  # type: ignore
        )
        args = escape.json_decode(response.body)
        session = {
            "access_token": args.get("access_token"),
            "expires_in": args.get("expires_in"),
        }
        assert session["access_token"] is not None

        user = await self.facebook_request(
            path="/me",
            access_token=session["access_token"],
            appsecret_proof=hmac.new(
                key=client_secret.encode("utf8"),
                msg=session["access_token"].encode("utf8"),
                digestmod=hashlib.sha256,
            ).hexdigest(),
            fields=",".join(fields),
        )

        if user is None:
            return None

        fieldmap = {}
        for field in fields:
            fieldmap[field] = user.get(field)

        # session_expires is converted to str for compatibility with
        # older versions in which the server used url-encoding and
        # this code simply returned the string verbatim.
        # This should change in Tornado 5.0.
        fieldmap.update(
            {
                "access_token": session["access_token"],
                "session_expires": str(session.get("expires_in")),
            }
        )
        return fieldmap


import base64
import binascii
import hashlib
import hmac
import time
import urllib.parse
import uuid
from tornado import httpclient
from tornado import escape
from tornado.httputil import url_concat
from tornado.util import unicode_type
from tornado.web import RequestHandler
from typing import List, Any, Dict, cast, Iterable, Union, Optional

class FacebookGraphMixin(OAuth2Mixin):

    _OAUTH_ACCESS_TOKEN_URL = "https://graph.facebook.com/oauth/access_token?"

    _OAUTH_AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?"

    _OAUTH_NO_CALLBACKS = False

    _FACEBOOK_BASE_URL = "https://graph.facebook.com"

    async def facebook_request(
        self,
        path: str,
        access_token: Optional[str] = None,
        post_args: Optional[Dict[str, Any]] = None,
        **args: Any
    ) -> Any:
        """Fetches the given relative API path, e.g., "/btaylor/picture"

        If the request is a POST, ``post_args`` should be provided. Query
        string arguments should be given as keyword arguments.

        An introduction to the Facebook Graph API can be found at
        http://developers.facebook.com/docs/api

        Many methods require an OAuth access token which you can
        obtain through `~OAuth2Mixin.authorize_redirect` and
        `get_authenticated_user`. The user returned through that
        process includes an ``access_token`` attribute that can be
        used to make authenticated requests via this method.

        Example usage:

        .. testcode::

            class MainHandler(tornado.web.RequestHandler,
                              tornado.auth.FacebookGraphMixin):
                @tornado.web.authenticated
                async def get(self):
                    new_entry = await self.facebook_request(
                        "/me/feed",
                        post_args={"message": "I am posting from my Tornado application!"},
                        access_token=self.current_user["access_token"])

                    if not new_entry:
                        # Call failed; perhaps missing permission?
                        self.authorize_redirect()
                        return
                    self.finish("Posted a message!")

        .. testoutput::
           :hide:

        The given path is relative to ``self._FACEBOOK_BASE_URL``,
        by default "https://graph.facebook.com".

        This method is a wrapper around `OAuth2Mixin.oauth2_request`;
        the only difference is that this method takes a relative path,
        while ``oauth2_request`` takes a complete url.

        .. versionchanged:: 3.1
           Added the ability to override ``self._FACEBOOK_BASE_URL``.

        .. versionchanged:: 6.0

           The ``callback`` argument was removed. Use the returned awaitable object instead.
        """
        url = self._FACEBOOK_BASE_URL + path
        return await self.oauth2_request(
            url, access_token=access_token, post_args=post_args, **args
        )


import asyncio
from concurrent import futures
import functools
import sys
import types
from tornado.log import app_log
import typing
from typing import Any, Callable, Optional, Tuple, Union

_T = typing.TypeVar("_T")
Future = asyncio.Future
FUTURES = (futures.Future, Future)
dummy_executor = DummyExecutor()
_NO_RESULT = object()

def run_on_executor(*args: Any, **kwargs: Any) -> Callable:
    """Decorator to run a synchronous method asynchronously on an executor.

    Returns a future.

    The executor to be used is determined by the ``executor``
    attributes of ``self``. To use a different attribute name, pass a
    keyword argument to the decorator::

        @run_on_executor(executor='_thread_pool')
        def foo(self):
            pass

    This decorator should not be confused with the similarly-named
    `.IOLoop.run_in_executor`. In general, using ``run_in_executor``
    when *calling* a blocking method is recommended instead of using
    this decorator when *defining* a method. If compatibility with older
    versions of Tornado is required, consider defining an executor
    and using ``executor.submit()`` at the call site.

    .. versionchanged:: 4.2
       Added keyword arguments to use alternative attributes.

    .. versionchanged:: 5.0
       Always uses the current IOLoop instead of ``self.io_loop``.

    .. versionchanged:: 5.1
       Returns a `.Future` compatible with ``await`` instead of a
       `concurrent.futures.Future`.

    .. deprecated:: 5.1

       The ``callback`` argument is deprecated and will be removed in
       6.0. The decorator itself is discouraged in new code but will
       not be removed in 6.0.

    .. versionchanged:: 6.0

       The ``callback`` argument was removed.
    """
    # Fully type-checking decorators is tricky, and this one is
    # discouraged anyway so it doesn't have all the generic magic.
    def run_on_executor_decorator(fn: Callable) -> Callable[..., Future]:
        executor = kwargs.get("executor", "executor")

        @functools.wraps(fn)
        def wrapper(self: Any, *args: Any, **kwargs: Any) -> Future:
            async_future = Future()  # type: Future
            conc_future = getattr(self, executor).submit(fn, self, *args, **kwargs)
            chain_future(conc_future, async_future)
            return async_future

        return wrapper

    if args and kwargs:
        raise ValueError("cannot combine positional and keyword args")
    if len(args) == 1:
        return run_on_executor_decorator(args[0])
    elif len(args) != 0:
        raise ValueError("expected 1 argument, got %d", len(args))
    return run_on_executor_decorator


import asyncio
from concurrent import futures
import functools
import sys
import types
from tornado.log import app_log
import typing
from typing import Any, Callable, Optional, Tuple, Union

_T = typing.TypeVar("_T")
Future = asyncio.Future
FUTURES = (futures.Future, Future)
dummy_executor = DummyExecutor()
_NO_RESULT = object()

def chain_future(a: "Future[_T]", b: "Future[_T]") -> None:
    """Chain two futures together so that when one completes, so does the other.

    The result (success or failure) of ``a`` will be copied to ``b``, unless
    ``b`` has already been completed or cancelled by the time ``a`` finishes.

    .. versionchanged:: 5.0

       Now accepts both Tornado/asyncio `Future` objects and
       `concurrent.futures.Future`.

    """

    def copy(future: "Future[_T]") -> None:
        assert future is a
        if b.done():
            return
        if hasattr(a, "exc_info") and a.exc_info() is not None:  # type: ignore
            future_set_exc_info(b, a.exc_info())  # type: ignore
        elif a.exception() is not None:
            b.set_exception(a.exception())
        else:
            b.set_result(a.result())

    if isinstance(a, Future):
        future_add_done_callback(a, copy)
    else:
        # concurrent.futures.Future
        from tornado.ioloop import IOLoop

        IOLoop.current().add_future(a, copy)


import asyncio
from concurrent import futures
import functools
import sys
import types
from tornado.log import app_log
import typing
from typing import Any, Callable, Optional, Tuple, Union

_T = typing.TypeVar("_T")
Future = asyncio.Future
FUTURES = (futures.Future, Future)
dummy_executor = DummyExecutor()
_NO_RESULT = object()

def future_set_result_unless_cancelled(
    future: "Union[futures.Future[_T], Future[_T]]", value: _T
) -> None:
    """Set the given ``value`` as the `Future`'s result, if not cancelled.

    Avoids ``asyncio.InvalidStateError`` when calling ``set_result()`` on
    a cancelled `asyncio.Future`.

    .. versionadded:: 5.0
    """
    if not future.cancelled():
        future.set_result(value)


import asyncio
from concurrent import futures
import functools
import sys
import types
from tornado.log import app_log
import typing
from typing import Any, Callable, Optional, Tuple, Union

_T = typing.TypeVar("_T")
Future = asyncio.Future
FUTURES = (futures.Future, Future)
dummy_executor = DummyExecutor()
_NO_RESULT = object()

def future_set_exception_unless_cancelled(
    future: "Union[futures.Future[_T], Future[_T]]", exc: BaseException
) -> None:
    """Set the given ``exc`` as the `Future`'s exception.

    If the Future is already canceled, logs the exception instead. If
    this logging is not desired, the caller should explicitly check
    the state of the Future and call ``Future.set_exception`` instead of
    this wrapper.

    Avoids ``asyncio.InvalidStateError`` when calling ``set_exception()`` on
    a cancelled `asyncio.Future`.

    .. versionadded:: 6.0

    """
    if not future.cancelled():
        future.set_exception(exc)
    else:
        app_log.error("Exception after Future was cancelled", exc_info=exc)


import asyncio
from concurrent import futures
import functools
import sys
import types
from tornado.log import app_log
import typing
from typing import Any, Callable, Optional, Tuple, Union

_T = typing.TypeVar("_T")
Future = asyncio.Future
FUTURES = (futures.Future, Future)
dummy_executor = DummyExecutor()
_NO_RESULT = object()

class DummyExecutor(futures.Executor):

    def submit(
        self, fn: Callable[..., _T], *args: Any, **kwargs: Any
    ) -> "futures.Future[_T]":
        future = futures.Future()  # type: futures.Future[_T]
        try:
            future_set_result_unless_cancelled(future, fn(*args, **kwargs))
        except Exception:
            future_set_exc_info(future, sys.exc_info())
        return future


import html.entities
import json
import re
import urllib.parse
from tornado.util import unicode_type
import typing
from typing import Union, Any, Optional, Dict, List, Callable

_XHTML_ESCAPE_RE = re.compile("[&<>\"']")
_XHTML_ESCAPE_DICT = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': """,
    "'": "'",
}
_UTF8_TYPES = (bytes, type(None))
_TO_UNICODE_TYPES = (unicode_type, type(None))
_unicode = to_unicode
native_str = to_unicode
to_basestring = to_unicode
_URL_RE = re.compile(
    to_unicode(
        r"""\b((?:([\w-]+):(/{1,3})|www[.])(?:(?:(?:[^\s&()]|&|")*(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))|(?:\((?:[^\s&()]|&|")*\)))+)"""  # noqa: E501
    )
)
_HTML_UNICODE_MAP = _build_unicode_map()

def url_unescape(  # noqa: F811
    value: Union[str, bytes], encoding: Optional[str] = "utf-8", plus: bool = True
) -> Union[str, bytes]:
    """Decodes the given value from a URL.

    The argument may be either a byte or unicode string.

    If encoding is None, the result will be a byte string.  Otherwise,
    the result is a unicode string in the specified encoding.

    If ``plus`` is true (the default), plus signs will be interpreted
    as spaces (literal plus signs must be represented as "%2B").  This
    is appropriate for query strings and form-encoded values but not
    for the path component of a URL.  Note that this default is the
    reverse of Python's urllib module.

    .. versionadded:: 3.1
       The ``plus`` argument
    """
    if encoding is None:
        if plus:
            # unquote_to_bytes doesn't have a _plus variant
            value = to_basestring(value).replace("+", " ")
        return urllib.parse.unquote_to_bytes(value)
    else:
        unquote = urllib.parse.unquote_plus if plus else urllib.parse.unquote
        return unquote(to_basestring(value), encoding=encoding)


import html.entities
import json
import re
import urllib.parse
from tornado.util import unicode_type
import typing
from typing import Union, Any, Optional, Dict, List, Callable

_XHTML_ESCAPE_RE = re.compile("[&<>\"']")
_XHTML_ESCAPE_DICT = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': """,
    "'": "'",
}
_UTF8_TYPES = (bytes, type(None))
_TO_UNICODE_TYPES = (unicode_type, type(None))
_unicode = to_unicode
native_str = to_unicode
to_basestring = to_unicode
_URL_RE = re.compile(
    to_unicode(
        r"""\b((?:([\w-]+):(/{1,3})|www[.])(?:(?:(?:[^\s&()]|&|")*(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))|(?:\((?:[^\s&()]|&|")*\)))+)"""  # noqa: E501
    )
)
_HTML_UNICODE_MAP = _build_unicode_map()

def utf8(value: Union[None, str, bytes]) -> Optional[bytes]:  # noqa: F811
    """Converts a string argument to a byte string.

    If the argument is already a byte string or None, it is returned unchanged.
    Otherwise it must be a unicode string and is encoded as utf8.
    """
    if isinstance(value, _UTF8_TYPES):
        return value
    if not isinstance(value, unicode_type):
        raise TypeError("Expected bytes, unicode, or None; got %r" % type(value))
    return value.encode("utf-8")


import html.entities
import json
import re
import urllib.parse
from tornado.util import unicode_type
import typing
from typing import Union, Any, Optional, Dict, List, Callable

_XHTML_ESCAPE_RE = re.compile("[&<>\"']")
_XHTML_ESCAPE_DICT = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': """,
    "'": "'",
}
_UTF8_TYPES = (bytes, type(None))
_TO_UNICODE_TYPES = (unicode_type, type(None))
_unicode = to_unicode
native_str = to_unicode
to_basestring = to_unicode
_URL_RE = re.compile(
    to_unicode(
        r"""\b((?:([\w-]+):(/{1,3})|www[.])(?:(?:(?:[^\s&()]|&|")*(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))|(?:\((?:[^\s&()]|&|")*\)))+)"""  # noqa: E501
    )
)
_HTML_UNICODE_MAP = _build_unicode_map()

def recursive_unicode(obj: Any) -> Any:
    """Walks a simple data structure, converting byte strings to unicode.

    Supports lists, tuples, and dictionaries.
    """
    if isinstance(obj, dict):
        return dict(
            (recursive_unicode(k), recursive_unicode(v)) for (k, v) in obj.items()
        )
    elif isinstance(obj, list):
        return list(recursive_unicode(i) for i in obj)
    elif isinstance(obj, tuple):
        return tuple(recursive_unicode(i) for i in obj)
    elif isinstance(obj, bytes):
        return to_unicode(obj)
    else:
        return obj


import html.entities
import json
import re
import urllib.parse
from tornado.util import unicode_type
import typing
from typing import Union, Any, Optional, Dict, List, Callable

_XHTML_ESCAPE_RE = re.compile("[&<>\"']")
_XHTML_ESCAPE_DICT = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': """,
    "'": "'",
}
_UTF8_TYPES = (bytes, type(None))
_TO_UNICODE_TYPES = (unicode_type, type(None))
_unicode = to_unicode
native_str = to_unicode
to_basestring = to_unicode
_URL_RE = re.compile(
    to_unicode(
        r"""\b((?:([\w-]+):(/{1,3})|www[.])(?:(?:(?:[^\s&()]|&|")*(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))|(?:\((?:[^\s&()]|&|")*\)))+)"""  # noqa: E501
    )
)
_HTML_UNICODE_MAP = _build_unicode_map()

def linkify(
    text: Union[str, bytes],
    shorten: bool = False,
    extra_params: Union[str, Callable[[str], str]] = "",
    require_protocol: bool = False,
    permitted_protocols: List[str] = ["http", "https"],
) -> str:
    """Converts plain text into HTML with links.

    For example: ``linkify("Hello http://tornadoweb.org!")`` would return
    ``Hello http://tornadoweb.org!``

    Parameters:

    * ``shorten``: Long urls will be shortened for display.

    * ``extra_params``: Extra text to include in the link tag, or a callable
      taking the link as an argument and returning the extra text
      e.g. ``linkify(text, extra_params='rel="nofollow" class="external"')``,
      or::

          def extra_params_cb(url):
              if url.startswith("http://example.com"):
                  return 'class="internal"'
              else:
                  return 'class="external" rel="nofollow"'
          linkify(text, extra_params=extra_params_cb)

    * ``require_protocol``: Only linkify urls which include a protocol. If
      this is False, urls such as www.facebook.com will also be linkified.

    * ``permitted_protocols``: List (or set) of protocols which should be
      linkified, e.g. ``linkify(text, permitted_protocols=["http", "ftp",
      "mailto"])``. It is very unsafe to include protocols such as
      ``javascript``.
    """
    if extra_params and not callable(extra_params):
        extra_params = " " + extra_params.strip()

    def make_link(m: typing.Match) -> str:
        url = m.group(1)
        proto = m.group(2)
        if require_protocol and not proto:
            return url  # not protocol, no linkify

        if proto and proto not in permitted_protocols:
            return url  # bad protocol, no linkify

        href = m.group(1)
        if not proto:
            href = "http://" + href  # no proto specified, use http

        if callable(extra_params):
            params = " " + extra_params(href).strip()
        else:
            params = extra_params

        # clip long urls. max_len is just an approximation
        max_len = 30
        if shorten and len(url) > max_len:
            before_clip = url
            if proto:
                proto_len = len(proto) + 1 + len(m.group(3) or "")  # +1 for :
            else:
                proto_len = 0

            parts = url[proto_len:].split("/")
            if len(parts) > 1:
                # Grab the whole host part plus the first bit of the path
                # The path is usually not that interesting once shortened
                # (no more slug, etc), so it really just provides a little
                # extra indication of shortening.
                url = (
                    url[:proto_len]
                    + parts[0]
                    + "/"
                    + parts[1][:8].split("?")[0].split(".")[0]
                )

            if len(url) > max_len * 1.5:  # still too long
                url = url[:max_len]

            if url != before_clip:
                amp = url.rfind("&")
                # avoid splitting html char entities
                if amp > max_len - 5:
                    url = url[:amp]
                url += "..."

                if len(url) >= len(before_clip):
                    url = before_clip
                else:
                    # full url is visible on mouse-over (for those who don't
                    # have a status bar, such as Safari by default)
                    params += ' title="%s"' % href

        return u'%s' % (href, params, url)

    # First HTML-escape so that our strings are all safe.
    # The regex is modified to avoid character entites other than & so
    # that we won't pick up ", etc.
    text = _unicode(xhtml_escape(text))
    return _URL_RE.sub(make_link, text)


import datetime
import functools
from io import BytesIO
import ssl
import time
import weakref
from tornado.concurrent import (
    Future,
    future_set_result_unless_cancelled,
    future_set_exception_unless_cancelled,
)
from tornado.escape import utf8, native_str
from tornado import gen, httputil
from tornado.ioloop import IOLoop
from tornado.util import Configurable
from typing import Type, Any, Union, Dict, Callable, Optional, cast

HTTPError = HTTPClientError

class HTTPClient(object):

    def __init__(
        self,
        async_client_class: "Optional[Type[AsyncHTTPClient]]" = None,
        **kwargs: Any
    ) -> None:
        # Initialize self._closed at the beginning of the constructor
        # so that an exception raised here doesn't lead to confusing
        # failures in __del__.
        self._closed = True
        self._io_loop = IOLoop(make_current=False)
        if async_client_class is None:
            async_client_class = AsyncHTTPClient

        # Create the client while our IOLoop is "current", without
        # clobbering the thread's real current IOLoop (if any).
        async def make_client() -> "AsyncHTTPClient":
            await gen.sleep(0)
            assert async_client_class is not None
            return async_client_class(**kwargs)

        self._async_client = self._io_loop.run_sync(make_client)
        self._closed = False

def main() -> None:
    from tornado.options import define, options, parse_command_line

    define("print_headers", type=bool, default=False)
    define("print_body", type=bool, default=True)
    define("follow_redirects", type=bool, default=True)
    define("validate_cert", type=bool, default=True)
    define("proxy_host", type=str)
    define("proxy_port", type=int)
    args = parse_command_line()
    client = HTTPClient()
    for arg in args:
        try:
            response = client.fetch(
                arg,
                follow_redirects=options.follow_redirects,
                validate_cert=options.validate_cert,
                proxy_host=options.proxy_host,
                proxy_port=options.proxy_port,
            )
        except HTTPError as e:
            if e.response is not None:
                response = e.response
            else:
                raise
        if options.print_headers:
            print(response.headers)
        if options.print_body:
            print(native_str(response.body))
    client.close()