Package: parso
Version: 0.8.1-1
Severity: normal
Tags: patch  pending

Dear maintainer,

I've prepared an NMU for parso (versioned as 0.8.3-0.1) and
uploaded it to DELAYED/5. Please feel free to tell me if I
should delay it longer.

Regards.

diff -Nru parso-0.8.1/AUTHORS.txt parso-0.8.3/AUTHORS.txt
--- parso-0.8.1/AUTHORS.txt	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/AUTHORS.txt	2021-11-30 22:04:12.000000000 +0100
@@ -6,6 +6,7 @@
 Code Contributors
 =================
 Alisdair Robertson (@robodair)
+Bryan Forbes (@bryanforbes) <br...@reigndropsfall.net>
 
 
 Code Contributors (to Jedi and therefore possibly to this library)
diff -Nru parso-0.8.1/CHANGELOG.rst parso-0.8.3/CHANGELOG.rst
--- parso-0.8.1/CHANGELOG.rst	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/CHANGELOG.rst	2021-11-30 22:04:12.000000000 +0100
@@ -6,6 +6,16 @@
 Unreleased
 ++++++++++
 
+0.8.3 (2021-11-30)
+++++++++++++++++++
+
+- Add basic support for Python 3.11 and 3.12
+
+0.8.2 (2021-03-30)
+++++++++++++++++++
+
+- Various small bugfixes
+
 0.8.1 (2020-12-10)
 ++++++++++++++++++
 
diff -Nru parso-0.8.1/.coveragerc parso-0.8.3/.coveragerc
--- parso-0.8.1/.coveragerc	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/.coveragerc	2021-11-30 22:04:12.000000000 +0100
@@ -4,6 +4,8 @@
 [report]
 # Regexes for lines to exclude from consideration
 exclude_lines =
+    pragma: no cover
+
     # Don't complain about missing debug-only code:
     def __repr__
 
diff -Nru parso-0.8.1/debian/changelog parso-0.8.3/debian/changelog
--- parso-0.8.1/debian/changelog	2020-12-20 22:50:50.000000000 +0100
+++ parso-0.8.3/debian/changelog	2022-12-04 15:11:11.000000000 +0100
@@ -1,3 +1,14 @@
+parso (0.8.3-0.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * New upstream version 0.8.3 (Closes: #1023986)
+  * Add patch to fix unit tests in Python 3.10 (Closes: #1002353)
+  * Move to debhelper 13
+  * Use dh-sequence-*
+  * Bump policy version (no changes)
+
+ -- Jochen Sprickerhof <jspri...@debian.org>  Sun, 04 Dec 2022 15:11:11 +0100
+
 parso (0.8.1-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru parso-0.8.1/debian/changelog.dch.save parso-0.8.3/debian/changelog.dch.save
--- parso-0.8.1/debian/changelog.dch.save	2019-07-29 10:11:34.000000000 +0200
+++ parso-0.8.3/debian/changelog.dch.save	1970-01-01 01:00:00.000000000 +0100
@@ -1,36 +0,0 @@
-parso (0.5.1-0.1) unstable; urgency=medium
-
-  * Non-maintainer upload.
-  * New upstream release. 
-
- -- Georges Khaznadar <georg...@debian.org>  Mon, 29 Jul 2019 10:10:38 +0200
-
-parso (0.5.0-1) unstable; urgency=medium
-
-  * New upstream release
-
- -- Piotr Ożarowski <pi...@debian.org>  Wed, 10 Jul 2019 22:18:18 +0200
-
-parso (0.3.1-1) unstable; urgency=medium
-
-  * New upstream release
-
- -- Piotr Ożarowski <pi...@debian.org>  Mon, 31 Dec 2018 11:23:51 +0100
-
-parso (0.2.1-1) unstable; urgency=medium
-
-  * New upstream release
-
- -- Piotr Ożarowski <pi...@debian.org>  Wed, 13 Jun 2018 23:45:23 +0200
-
-parso (0.1.1-1) unstable; urgency=medium
-
-  * New upstream release
-
- -- Piotr Ożarowski <pi...@debian.org>  Wed, 20 Dec 2017 11:04:20 +0100
-
-parso (0.1.0-1) unstable; urgency=low
-
-  * Initial release
-
- -- Piotr Ożarowski <pi...@debian.org>  Wed, 25 Oct 2017 11:06:28 +0000
diff -Nru parso-0.8.1/debian/compat parso-0.8.3/debian/compat
--- parso-0.8.1/debian/compat	2017-10-25 13:06:28.000000000 +0200
+++ parso-0.8.3/debian/compat	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-10
diff -Nru parso-0.8.1/debian/control parso-0.8.3/debian/control
--- parso-0.8.1/debian/control	2020-03-16 10:59:10.000000000 +0100
+++ parso-0.8.3/debian/control	2022-12-04 15:11:11.000000000 +0100
@@ -2,12 +2,15 @@
 Section: python
 Priority: optional
 Maintainer: Piotr Ożarowski <pi...@debian.org>
-Build-Depends: debhelper (>= 10), dh-python,
+Build-Depends: debhelper-compat (= 13),
+               dh-sequence-python3,
+               dh-sequence-sphinxdoc,
                python3-all,
                python3-setuptools,
                python3-sphinx,
                python3-pytest,
-Standards-Version: 4.5.0
+Standards-Version: 4.6.1
+Rules-Requires-Root: no
 Homepage: https://github.com/davidhalter/parso
 
 Package: python3-parso
@@ -55,4 +58,4 @@
 Architecture: all
 Depends: ${misc:Depends}, ${sphinxdoc:Depends}
 Description: documentation for the parso Python library
- This package provides documentation for parso 
+ This package provides documentation for parso
diff -Nru parso-0.8.1/debian/patches/0002-Fix-unit-tests-in-Python-3.10.patch parso-0.8.3/debian/patches/0002-Fix-unit-tests-in-Python-3.10.patch
--- parso-0.8.1/debian/patches/0002-Fix-unit-tests-in-Python-3.10.patch	1970-01-01 01:00:00.000000000 +0100
+++ parso-0.8.3/debian/patches/0002-Fix-unit-tests-in-Python-3.10.patch	2022-12-04 15:07:07.000000000 +0100
@@ -0,0 +1,139 @@
+From 091149f84e4aed1b24e89d1340ce90a76c5bc46d Mon Sep 17 00:00:00 2001
+From: Jochen Sprickerhof <g...@jochen.sprickerhof.de>
+Date: Sun, 4 Dec 2022 13:29:25 +0100
+Subject: [PATCH] Fix unit tests in Python 3.10
+
+---
+ parso/python/errors.py     | 20 ++++++++++----------
+ test/test_python_errors.py | 15 +++++++++++++++
+ 2 files changed, 25 insertions(+), 10 deletions(-)
+
+diff --git a/parso/python/errors.py b/parso/python/errors.py
+index 5da046a..10f08e5 100644
+--- a/parso/python/errors.py
++++ b/parso/python/errors.py
+@@ -33,7 +33,7 @@ def _get_rhs_name(node, version):
+                 return "literal"
+             else:
+                 if second.children[1] == ":" or second.children[0] == "**":
+-                    return "dict display"
++                    return "dict literal"
+                 else:
+                     return "set display"
+         elif (
+@@ -47,7 +47,7 @@ def _get_rhs_name(node, version):
+         elif first == "[":
+             return "list"
+         elif first == "{" and second == "}":
+-            return "dict display"
++            return "dict literal"
+         elif first == "{" and len(node.children) > 2:
+             return "set display"
+     elif type_ == "keyword":
+@@ -58,7 +58,7 @@ def _get_rhs_name(node, version):
+         else:
+             return str(node.value)
+     elif type_ == "operator" and node.value == "...":
+-        return "Ellipsis"
++        return "ellipsis"
+     elif type_ == "comparison":
+         return "comparison"
+     elif type_ in ("string", "number", "strings"):
+@@ -83,7 +83,7 @@ def _get_rhs_name(node, version):
+         or "_test" in type_
+         or type_ in ("term", "factor")
+     ):
+-        return "operator"
++        return "expression"
+     elif type_ == "star_expr":
+         return "starred"
+     elif type_ == "testlist_star_expr":
+@@ -610,7 +610,7 @@ class _NameChecks(SyntaxRule):
+ 
+ @ErrorFinder.register_rule(type='string')
+ class _StringChecks(SyntaxRule):
+-    message = "bytes can only contain ASCII literal characters."
++    message = "bytes can only contain ASCII literal characters"
+ 
+     def is_issue(self, leaf):
+         string_prefix = leaf.string_prefix.lower()
+@@ -1043,14 +1043,14 @@ class _CheckAssignmentRule(SyntaxRule):
+                         error = 'literal'
+                     else:
+                         if second.children[1] == ':':
+-                            error = 'dict display'
++                            error = 'dict literal'
+                         else:
+                             error = 'set display'
+                 elif first == "{" and second == "}":
+                     if self._normalizer.version < (3, 8):
+                         error = 'literal'
+                     else:
+-                        error = "dict display"
++                        error = "dict literal"
+                 elif first == "{" and len(node.children) > 2:
+                     if self._normalizer.version < (3, 8):
+                         error = 'literal'
+@@ -1083,7 +1083,7 @@ class _CheckAssignmentRule(SyntaxRule):
+                 error = str(node.value)
+         elif type_ == 'operator':
+             if node.value == '...':
+-                error = 'Ellipsis'
++                error = 'ellipsis'
+         elif type_ == 'comparison':
+             error = 'comparison'
+         elif type_ in ('string', 'number', 'strings'):
+@@ -1098,7 +1098,7 @@ class _CheckAssignmentRule(SyntaxRule):
+             if node.children[0] == 'await':
+                 error = 'await expression'
+             elif node.children[-2] == '**':
+-                error = 'operator'
++                error = 'expression'
+             else:
+                 # Has a trailer
+                 trailer = node.children[-1]
+@@ -1120,7 +1120,7 @@ class _CheckAssignmentRule(SyntaxRule):
+         elif ('expr' in type_ and type_ != 'star_expr'  # is a substring
+               or '_test' in type_
+               or type_ in ('term', 'factor')):
+-            error = 'operator'
++            error = 'expression'
+         elif type_ == "star_expr":
+             if is_deletion:
+                 if self._normalizer.version >= (3, 9):
+diff --git a/test/test_python_errors.py b/test/test_python_errors.py
+index adf5f06..7ee1064 100644
+--- a/test/test_python_errors.py
++++ b/test/test_python_errors.py
+@@ -1,6 +1,7 @@
+ """
+ Testing if parso finds syntax errors and indentation errors.
+ """
++import re
+ import sys
+ import warnings
+ 
+@@ -136,6 +137,20 @@ def _get_actual_exception(code):
+         wanted = 'SyntaxError: invalid syntax'
+     elif wanted == "SyntaxError: f-string: single '}' is not allowed":
+         wanted = 'SyntaxError: invalid syntax'
++    elif "Maybe you meant '==' instead of '='?" in wanted:
++        wanted = wanted.removesuffix(" here. Maybe you meant '==' instead of '='?")
++    elif re.match(r'SyntaxError: unterminated string literal \(detected at line \d+\)', wanted):
++        wanted = 'SyntaxError: EOL while scanning string literal'
++    elif re.match(r'SyntaxError: unterminated triple-quoted string literal \(detected at line \d+\)', wanted):
++        wanted = 'SyntaxError: EOF while scanning triple-quoted string literal'
++    elif wanted == 'SyntaxError: cannot use starred expression here':
++        wanted = "SyntaxError: can't use starred expression here"
++    elif wanted == 'SyntaxError: f-string: cannot use starred expression here':
++        wanted = "SyntaxError: f-string: can't use starred expression here"
++    elif re.match(r"IndentationError: expected an indented block after '[^']*' statement on line \d", wanted):
++        wanted = 'IndentationError: expected an indented block'
++    elif wanted == 'SyntaxError: unterminated string literal':
++        wanted = 'SyntaxError: EOL while scanning string literal'
+     return [wanted], line_nr
+ 
+ 
+-- 
+2.38.1
+
diff -Nru parso-0.8.1/debian/patches/series parso-0.8.3/debian/patches/series
--- parso-0.8.1/debian/patches/series	2020-05-06 15:33:49.000000000 +0200
+++ parso-0.8.3/debian/patches/series	2022-12-04 15:07:07.000000000 +0100
@@ -1 +1,2 @@
 0001-remove-forkme-logo-to-avoid-privacy-breach.patch
+0002-Fix-unit-tests-in-Python-3.10.patch
diff -Nru parso-0.8.1/debian/rules parso-0.8.3/debian/rules
--- parso-0.8.1/debian/rules	2019-12-31 14:17:57.000000000 +0100
+++ parso-0.8.3/debian/rules	2022-12-04 15:10:13.000000000 +0100
@@ -2,7 +2,7 @@
 
 export PYBUILD_NAME=parso
 %:
-	dh $@ --with python3,sphinxdoc --buildsystem=pybuild
+	dh $@ --buildsystem=pybuild
 
 override_dh_auto_build-indep:
 	dh_auto_build -i
diff -Nru parso-0.8.1/docs/docs/development.rst parso-0.8.3/docs/docs/development.rst
--- parso-0.8.1/docs/docs/development.rst	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/docs/docs/development.rst	2021-11-30 22:04:12.000000000 +0100
@@ -34,5 +34,5 @@
 
     python3.9 -m pytest
 
-Tests are also run automatically on `Travis CI
-<https://travis-ci.org/davidhalter/parso/>`_.
+Tests are also run automatically on `GitHub Actions
+<https://github.com/davidhalter/parso/actions>`_.
diff -Nru parso-0.8.1/docs/index.rst parso-0.8.3/docs/index.rst
--- parso-0.8.1/docs/index.rst	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/docs/index.rst	2021-11-30 22:04:12.000000000 +0100
@@ -27,5 +27,5 @@
 ---------
 
 - `Source Code on Github <https://github.com/davidhalter/parso>`_
-- `Travis Testing <https://travis-ci.org/davidhalter/parso>`_
+- `GitHub Actions Testing <https://github.com/davidhalter/parso/actions>`_
 - `Python Package Index <http://pypi.python.org/pypi/parso/>`_
diff -Nru parso-0.8.1/parso/cache.py parso-0.8.3/parso/cache.py
--- parso-0.8.1/parso/cache.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/cache.py	2021-11-30 22:04:12.000000000 +0100
@@ -187,7 +187,7 @@
             # file system. It's still in RAM in that case. However we should
             # still warn the user that this is happening.
             warnings.warn(
-                'Tried to save a file to %s, but got permission denied.',
+                'Tried to save a file to %s, but got permission denied.' % path,
                 Warning
             )
         else:
diff -Nru parso-0.8.1/parso/__init__.py parso-0.8.3/parso/__init__.py
--- parso-0.8.1/parso/__init__.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/__init__.py	2021-11-30 22:04:12.000000000 +0100
@@ -43,7 +43,7 @@
 from parso.utils import split_lines, python_bytes_to_unicode
 
 
-__version__ = '0.8.1'
+__version__ = '0.8.3'
 
 
 def parse(code=None, **kwargs):
diff -Nru parso-0.8.1/parso/parser.py parso-0.8.3/parso/parser.py
--- parso-0.8.1/parso/parser.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/parser.py	2021-11-30 22:04:12.000000000 +0100
@@ -23,7 +23,7 @@
 complexity of the ``Parser`` (there's another parser sitting inside
 ``Statement``, which produces ``Array`` and ``Call``).
 """
-from typing import Dict
+from typing import Dict, Type
 
 from parso import tree
 from parso.pgen2.generator import ReservedString
@@ -110,10 +110,10 @@
     When a syntax error occurs, error_recovery() is called.
     """
 
-    node_map: Dict[str, type] = {}
+    node_map: Dict[str, Type[tree.BaseNode]] = {}
     default_node = tree.Node
 
-    leaf_map: Dict[str, type] = {}
+    leaf_map: Dict[str, Type[tree.Leaf]] = {}
     default_leaf = tree.Leaf
 
     def __init__(self, pgen_grammar, start_nonterminal='file_input', error_recovery=False):
@@ -156,8 +156,6 @@
             node = self.node_map[nonterminal](children)
         except KeyError:
             node = self.default_node(nonterminal, children)
-        for c in children:
-            c.parent = node
         return node
 
     def convert_leaf(self, type_, value, prefix, start_pos):
diff -Nru parso-0.8.1/parso/python/errors.py parso-0.8.3/parso/python/errors.py
--- parso-0.8.1/parso/python/errors.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/errors.py	2021-11-30 22:04:12.000000000 +0100
@@ -5,7 +5,6 @@
 from contextlib import contextmanager
 
 from parso.normalizer import Normalizer, NormalizerConfig, Issue, Rule
-from parso.python.tree import search_ancestor
 from parso.python.tokenize import _get_token_collection
 
 _BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt')
@@ -147,8 +146,20 @@
     return atom
 
 
+def _skip_parens_bottom_up(node):
+    """
+    Returns an ancestor node of an expression, skipping all levels of parens
+    bottom-up.
+    """
+    while node.parent is not None:
+        node = node.parent
+        if node.type != 'atom' or node.children[0] != '(':
+            return node
+    return None
+
+
 def _iter_params(parent_node):
-    return (n for n in parent_node.children if n.type == 'param')
+    return (n for n in parent_node.children if n.type == 'param' or n.type == 'operator')
 
 
 def _is_future_import_first(import_from):
@@ -219,7 +230,7 @@
     elif node.type == "fstring":
         return True
     else:
-        return search_ancestor(node, "fstring")
+        return node.search_ancestor("fstring")
 
 
 class _Context:
@@ -229,6 +240,7 @@
         self.parent_context = parent_context
         self._used_name_dict = {}
         self._global_names = []
+        self._local_params_names = []
         self._nonlocal_names = []
         self._nonlocal_names_in_subscopes = []
         self._add_syntax_error = add_syntax_error
@@ -252,6 +264,10 @@
             self._global_names.append(name)
         elif parent_type == 'nonlocal_stmt':
             self._nonlocal_names.append(name)
+        elif parent_type == 'funcdef':
+            self._local_params_names.extend(
+                [param.name.value for param in name.parent.get_params()]
+            )
         else:
             self._used_name_dict.setdefault(name.value, []).append(name)
 
@@ -279,6 +295,8 @@
         nonlocals_not_handled = []
         for nonlocal_name in self._nonlocal_names_in_subscopes:
             search = nonlocal_name.value
+            if search in self._local_params_names:
+                continue
             if search in global_name_strs or self.parent_context is None:
                 message = "no binding for nonlocal '%s' found" % nonlocal_name.value
                 self._add_syntax_error(nonlocal_name, message)
@@ -730,9 +748,34 @@
 @ErrorFinder.register_rule(type='star_expr')
 class _StarExprRule(SyntaxRule):
     message_iterable_unpacking = "iterable unpacking cannot be used in comprehension"
-    message_assignment = "can use starred expression only as assignment target"
 
     def is_issue(self, node):
+        def check_delete_starred(node):
+            while node.parent is not None:
+                node = node.parent
+                if node.type == 'del_stmt':
+                    return True
+                if node.type not in (*_STAR_EXPR_PARENTS, 'atom'):
+                    return False
+            return False
+
+        if self._normalizer.version >= (3, 9):
+            ancestor = node.parent
+        else:
+            ancestor = _skip_parens_bottom_up(node)
+        # starred expression not in tuple/list/set
+        if ancestor.type not in (*_STAR_EXPR_PARENTS, 'dictorsetmaker') \
+                and not (ancestor.type == 'atom' and ancestor.children[0] != '('):
+            self.add_issue(node, message="can't use starred expression here")
+            return
+
+        if check_delete_starred(node):
+            if self._normalizer.version >= (3, 9):
+                self.add_issue(node, message="cannot delete starred")
+            else:
+                self.add_issue(node, message="can't use starred expression here")
+            return
+
         if node.parent.type == 'testlist_comp':
             # [*[] for a in [1]]
             if node.parent.children[1].type in _COMP_FOR_TYPES:
@@ -742,39 +785,33 @@
 @ErrorFinder.register_rule(types=_STAR_EXPR_PARENTS)
 class _StarExprParentRule(SyntaxRule):
     def is_issue(self, node):
-        if node.parent.type == 'del_stmt':
-            if self._normalizer.version >= (3, 9):
-                self.add_issue(node.parent, message="cannot delete starred")
-            else:
-                self.add_issue(node.parent, message="can't use starred expression here")
-        else:
-            def is_definition(node, ancestor):
-                if ancestor is None:
-                    return False
+        def is_definition(node, ancestor):
+            if ancestor is None:
+                return False
 
-                type_ = ancestor.type
-                if type_ == 'trailer':
-                    return False
+            type_ = ancestor.type
+            if type_ == 'trailer':
+                return False
 
-                if type_ == 'expr_stmt':
-                    return node.start_pos < ancestor.children[-1].start_pos
+            if type_ == 'expr_stmt':
+                return node.start_pos < ancestor.children[-1].start_pos
 
-                return is_definition(node, ancestor.parent)
+            return is_definition(node, ancestor.parent)
 
-            if is_definition(node, node.parent):
-                args = [c for c in node.children if c != ',']
-                starred = [c for c in args if c.type == 'star_expr']
-                if len(starred) > 1:
-                    if self._normalizer.version < (3, 9):
-                        message = "two starred expressions in assignment"
-                    else:
-                        message = "multiple starred expressions in assignment"
-                    self.add_issue(starred[1], message=message)
-                elif starred:
-                    count = args.index(starred[0])
-                    if count >= 256:
-                        message = "too many expressions in star-unpacking assignment"
-                        self.add_issue(starred[0], message=message)
+        if is_definition(node, node.parent):
+            args = [c for c in node.children if c != ',']
+            starred = [c for c in args if c.type == 'star_expr']
+            if len(starred) > 1:
+                if self._normalizer.version < (3, 9):
+                    message = "two starred expressions in assignment"
+                else:
+                    message = "multiple starred expressions in assignment"
+                self.add_issue(starred[1], message=message)
+            elif starred:
+                count = args.index(starred[0])
+                if count >= 256:
+                    message = "too many expressions in star-unpacking assignment"
+                    self.add_issue(starred[0], message=message)
 
 
 @ErrorFinder.register_rule(type='annassign')
@@ -911,17 +948,28 @@
     def is_issue(self, node):
         param_names = set()
         default_only = False
+        star_seen = False
         for p in _iter_params(node):
+            if p.type == 'operator':
+                if p.value == '*':
+                    star_seen = True
+                    default_only = False
+                continue
+
             if p.name.value in param_names:
                 message = "duplicate argument '%s' in function definition"
                 self.add_issue(p.name, message=message % p.name.value)
             param_names.add(p.name.value)
 
-            if p.default is None and not p.star_count:
-                if default_only:
-                    return True
-            else:
-                default_only = True
+            if not star_seen:
+                if p.default is None and not p.star_count:
+                    if default_only:
+                        return True
+                elif p.star_count:
+                    star_seen = True
+                    default_only = False
+                else:
+                    default_only = True
 
 
 @ErrorFinder.register_rule(type='try_stmt')
@@ -1079,8 +1127,15 @@
                     error = "starred"
                 else:
                     self.add_issue(node, message="can't use starred expression here")
-            elif not search_ancestor(node, *_STAR_EXPR_PARENTS) and not is_aug_assign:
-                self.add_issue(node, message="starred assignment target must be in a list or tuple")
+            else:
+                if self._normalizer.version >= (3, 9):
+                    ancestor = node.parent
+                else:
+                    ancestor = _skip_parens_bottom_up(node)
+                if ancestor.type not in _STAR_EXPR_PARENTS and not is_aug_assign \
+                        and not (ancestor.type == 'atom' and ancestor.children[0] == '['):
+                    message = "starred assignment target must be in a list or tuple"
+                    self.add_issue(node, message=message)
 
             self._check_assignment(node.children[1])
 
@@ -1209,7 +1264,7 @@
         def search_all_comp_ancestors(node):
             has_ancestors = False
             while True:
-                node = search_ancestor(node, 'testlist_comp', 'dictorsetmaker')
+                node = node.search_ancestor('testlist_comp', 'dictorsetmaker')
                 if node is None:
                     break
                 for child in node.children:
diff -Nru parso-0.8.1/parso/python/grammar310.txt parso-0.8.3/parso/python/grammar310.txt
--- parso-0.8.1/parso/python/grammar310.txt	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/grammar310.txt	2021-11-30 22:04:12.000000000 +0100
@@ -97,9 +97,7 @@
 
 namedexpr_test: test [':=' test]
 test: or_test ['if' or_test 'else' test] | lambdef
-test_nocond: or_test | lambdef_nocond
 lambdef: 'lambda' [varargslist] ':' test
-lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
 or_test: and_test ('or' and_test)*
 and_test: not_test ('and' not_test)*
 not_test: 'not' not_test | comparison
@@ -155,7 +153,7 @@
 comp_iter: comp_for | comp_if
 sync_comp_for: 'for' exprlist 'in' or_test [comp_iter]
 comp_for: ['async'] sync_comp_for
-comp_if: 'if' test_nocond [comp_iter]
+comp_if: 'if' or_test [comp_iter]
 
 # not used in grammar, but may appear in "node" passed from Parser to Compiler
 encoding_decl: NAME
diff -Nru parso-0.8.1/parso/python/grammar311.txt parso-0.8.3/parso/python/grammar311.txt
--- parso-0.8.1/parso/python/grammar311.txt	1970-01-01 01:00:00.000000000 +0100
+++ parso-0.8.3/parso/python/grammar311.txt	2021-11-30 22:04:12.000000000 +0100
@@ -0,0 +1,169 @@
+# Grammar for Python
+
+# NOTE WELL: You should also follow all the steps listed at
+# https://devguide.python.org/grammar/
+
+# Start symbols for the grammar:
+#       single_input is a single interactive statement;
+#       file_input is a module or sequence of commands read from an input file;
+#       eval_input is the input for the eval() functions.
+# NB: compound_stmt in single_input is followed by extra NEWLINE!
+single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
+file_input: stmt* ENDMARKER
+eval_input: testlist NEWLINE* ENDMARKER
+
+decorator: '@' namedexpr_test NEWLINE
+decorators: decorator+
+decorated: decorators (classdef | funcdef | async_funcdef)
+
+async_funcdef: 'async' funcdef
+funcdef: 'def' NAME parameters ['->' test] ':' suite
+
+parameters: '(' [typedargslist] ')'
+typedargslist: (
+  (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] (
+        ',' tfpdef ['=' test])* ([',' [
+        '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+      | '**' tfpdef [',']]])
+  | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]])
+  | '**' tfpdef [',']]] )
+|  (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
+        '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+      | '**' tfpdef [',']]]
+  | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+  | '**' tfpdef [','])
+)
+tfpdef: NAME [':' test]
+varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+        '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+      | '**' vfpdef [',']]]
+  | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+  | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+        '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+      | '**' vfpdef [',']]]
+  | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+  | '**' vfpdef [',']
+)
+vfpdef: NAME
+
+stmt: simple_stmt | compound_stmt | NEWLINE
+simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
+small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
+             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
+expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
+                     ('=' (yield_expr|testlist_star_expr))*)
+annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
+testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
+augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
+            '<<=' | '>>=' | '**=' | '//=')
+# For normal and annotated assignments, additional restrictions enforced by the interpreter
+del_stmt: 'del' exprlist
+pass_stmt: 'pass'
+flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
+break_stmt: 'break'
+continue_stmt: 'continue'
+return_stmt: 'return' [testlist_star_expr]
+yield_stmt: yield_expr
+raise_stmt: 'raise' [test ['from' test]]
+import_stmt: import_name | import_from
+import_name: 'import' dotted_as_names
+# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
+import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
+              'import' ('*' | '(' import_as_names ')' | import_as_names))
+import_as_name: NAME ['as' NAME]
+dotted_as_name: dotted_name ['as' NAME]
+import_as_names: import_as_name (',' import_as_name)* [',']
+dotted_as_names: dotted_as_name (',' dotted_as_name)*
+dotted_name: NAME ('.' NAME)*
+global_stmt: 'global' NAME (',' NAME)*
+nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
+assert_stmt: 'assert' test [',' test]
+
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
+async_stmt: 'async' (funcdef | with_stmt | for_stmt)
+if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
+while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
+for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
+try_stmt: ('try' ':' suite
+           ((except_clause ':' suite)+
+            ['else' ':' suite]
+            ['finally' ':' suite] |
+           'finally' ':' suite))
+with_stmt: 'with' with_item (',' with_item)*  ':' suite
+with_item: test ['as' expr]
+# NB compile.c makes sure that the default except clause is last
+except_clause: 'except' [test ['as' NAME]]
+suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
+
+namedexpr_test: test [':=' test]
+test: or_test ['if' or_test 'else' test] | lambdef
+lambdef: 'lambda' [varargslist] ':' test
+or_test: and_test ('or' and_test)*
+and_test: not_test ('and' not_test)*
+not_test: 'not' not_test | comparison
+comparison: expr (comp_op expr)*
+# <> isn't actually a valid comparison operator in Python. It's here for the
+# sake of a __future__ import described in PEP 401 (which really works :-)
+comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+star_expr: '*' expr
+expr: xor_expr ('|' xor_expr)*
+xor_expr: and_expr ('^' and_expr)*
+and_expr: shift_expr ('&' shift_expr)*
+shift_expr: arith_expr (('<<'|'>>') arith_expr)*
+arith_expr: term (('+'|'-') term)*
+term: factor (('*'|'@'|'/'|'%'|'//') factor)*
+factor: ('+'|'-'|'~') factor | power
+power: atom_expr ['**' factor]
+atom_expr: ['await'] atom trailer*
+atom: ('(' [yield_expr|testlist_comp] ')' |
+       '[' [testlist_comp] ']' |
+       '{' [dictorsetmaker] '}' |
+       NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False')
+testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
+trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+subscriptlist: subscript (',' subscript)* [',']
+subscript: test [':=' test] | [test] ':' [test] [sliceop]
+sliceop: ':' [test]
+exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
+testlist: test (',' test)* [',']
+dictorsetmaker: ( ((test ':' test | '**' expr)
+                   (comp_for | (',' (test ':' test | '**' expr))* [','])) |
+                  ((test [':=' test] | star_expr)
+                   (comp_for | (',' (test [':=' test] | star_expr))* [','])) )
+
+classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
+
+arglist: argument (',' argument)*  [',']
+
+# The reason that keywords are test nodes instead of NAME is that using NAME
+# results in an ambiguity. ast.c makes sure it's a NAME.
+# "test '=' test" is really "keyword '=' test", but we have no such token.
+# These need to be in a single rule to avoid grammar that is ambiguous
+# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
+# we explicitly match '*' here, too, to give it proper precedence.
+# Illegal combinations and orderings are blocked in ast.c:
+# multiple (test comp_for) arguments are blocked; keyword unpackings
+# that precede iterable unpackings are blocked; etc.
+argument: ( test [comp_for] |
+            test ':=' test |
+            test '=' test |
+            '**' test |
+            '*' test )
+
+comp_iter: comp_for | comp_if
+sync_comp_for: 'for' exprlist 'in' or_test [comp_iter]
+comp_for: ['async'] sync_comp_for
+comp_if: 'if' or_test [comp_iter]
+
+# not used in grammar, but may appear in "node" passed from Parser to Compiler
+encoding_decl: NAME
+
+yield_expr: 'yield' [yield_arg]
+yield_arg: 'from' test | testlist_star_expr
+
+strings: (STRING | fstring)+
+fstring: FSTRING_START fstring_content* FSTRING_END
+fstring_content: FSTRING_STRING | fstring_expr
+fstring_conversion: '!' NAME
+fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}'
+fstring_format_spec: ':' fstring_content*
diff -Nru parso-0.8.1/parso/python/grammar312.txt parso-0.8.3/parso/python/grammar312.txt
--- parso-0.8.1/parso/python/grammar312.txt	1970-01-01 01:00:00.000000000 +0100
+++ parso-0.8.3/parso/python/grammar312.txt	2021-11-30 22:04:12.000000000 +0100
@@ -0,0 +1,169 @@
+# Grammar for Python
+
+# NOTE WELL: You should also follow all the steps listed at
+# https://devguide.python.org/grammar/
+
+# Start symbols for the grammar:
+#       single_input is a single interactive statement;
+#       file_input is a module or sequence of commands read from an input file;
+#       eval_input is the input for the eval() functions.
+# NB: compound_stmt in single_input is followed by extra NEWLINE!
+single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
+file_input: stmt* ENDMARKER
+eval_input: testlist NEWLINE* ENDMARKER
+
+decorator: '@' namedexpr_test NEWLINE
+decorators: decorator+
+decorated: decorators (classdef | funcdef | async_funcdef)
+
+async_funcdef: 'async' funcdef
+funcdef: 'def' NAME parameters ['->' test] ':' suite
+
+parameters: '(' [typedargslist] ')'
+typedargslist: (
+  (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [ tfpdef ['=' test] (
+        ',' tfpdef ['=' test])* ([',' [
+        '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+      | '**' tfpdef [',']]])
+  | '*' [tfpdef] (',' tfpdef ['=' test])* ([',' ['**' tfpdef [',']]])
+  | '**' tfpdef [',']]] )
+|  (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
+        '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+      | '**' tfpdef [',']]]
+  | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
+  | '**' tfpdef [','])
+)
+tfpdef: NAME [':' test]
+varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+        '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+      | '**' vfpdef [',']]]
+  | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+  | '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
+        '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+      | '**' vfpdef [',']]]
+  | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
+  | '**' vfpdef [',']
+)
+vfpdef: NAME
+
+stmt: simple_stmt | compound_stmt | NEWLINE
+simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
+small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
+             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
+expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
+                     ('=' (yield_expr|testlist_star_expr))*)
+annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
+testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
+augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
+            '<<=' | '>>=' | '**=' | '//=')
+# For normal and annotated assignments, additional restrictions enforced by the interpreter
+del_stmt: 'del' exprlist
+pass_stmt: 'pass'
+flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
+break_stmt: 'break'
+continue_stmt: 'continue'
+return_stmt: 'return' [testlist_star_expr]
+yield_stmt: yield_expr
+raise_stmt: 'raise' [test ['from' test]]
+import_stmt: import_name | import_from
+import_name: 'import' dotted_as_names
+# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
+import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
+              'import' ('*' | '(' import_as_names ')' | import_as_names))
+import_as_name: NAME ['as' NAME]
+dotted_as_name: dotted_name ['as' NAME]
+import_as_names: import_as_name (',' import_as_name)* [',']
+dotted_as_names: dotted_as_name (',' dotted_as_name)*
+dotted_name: NAME ('.' NAME)*
+global_stmt: 'global' NAME (',' NAME)*
+nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
+assert_stmt: 'assert' test [',' test]
+
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
+async_stmt: 'async' (funcdef | with_stmt | for_stmt)
+if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
+while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
+for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
+try_stmt: ('try' ':' suite
+           ((except_clause ':' suite)+
+            ['else' ':' suite]
+            ['finally' ':' suite] |
+           'finally' ':' suite))
+with_stmt: 'with' with_item (',' with_item)*  ':' suite
+with_item: test ['as' expr]
+# NB compile.c makes sure that the default except clause is last
+except_clause: 'except' [test ['as' NAME]]
+suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
+
+namedexpr_test: test [':=' test]
+test: or_test ['if' or_test 'else' test] | lambdef
+lambdef: 'lambda' [varargslist] ':' test
+or_test: and_test ('or' and_test)*
+and_test: not_test ('and' not_test)*
+not_test: 'not' not_test | comparison
+comparison: expr (comp_op expr)*
+# <> isn't actually a valid comparison operator in Python. It's here for the
+# sake of a __future__ import described in PEP 401 (which really works :-)
+comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+star_expr: '*' expr
+expr: xor_expr ('|' xor_expr)*
+xor_expr: and_expr ('^' and_expr)*
+and_expr: shift_expr ('&' shift_expr)*
+shift_expr: arith_expr (('<<'|'>>') arith_expr)*
+arith_expr: term (('+'|'-') term)*
+term: factor (('*'|'@'|'/'|'%'|'//') factor)*
+factor: ('+'|'-'|'~') factor | power
+power: atom_expr ['**' factor]
+atom_expr: ['await'] atom trailer*
+atom: ('(' [yield_expr|testlist_comp] ')' |
+       '[' [testlist_comp] ']' |
+       '{' [dictorsetmaker] '}' |
+       NAME | NUMBER | strings | '...' | 'None' | 'True' | 'False')
+testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
+trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+subscriptlist: subscript (',' subscript)* [',']
+subscript: test [':=' test] | [test] ':' [test] [sliceop]
+sliceop: ':' [test]
+exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
+testlist: test (',' test)* [',']
+dictorsetmaker: ( ((test ':' test | '**' expr)
+                   (comp_for | (',' (test ':' test | '**' expr))* [','])) |
+                  ((test [':=' test] | star_expr)
+                   (comp_for | (',' (test [':=' test] | star_expr))* [','])) )
+
+classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
+
+arglist: argument (',' argument)*  [',']
+
+# The reason that keywords are test nodes instead of NAME is that using NAME
+# results in an ambiguity. ast.c makes sure it's a NAME.
+# "test '=' test" is really "keyword '=' test", but we have no such token.
+# These need to be in a single rule to avoid grammar that is ambiguous
+# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
+# we explicitly match '*' here, too, to give it proper precedence.
+# Illegal combinations and orderings are blocked in ast.c:
+# multiple (test comp_for) arguments are blocked; keyword unpackings
+# that precede iterable unpackings are blocked; etc.
+argument: ( test [comp_for] |
+            test ':=' test |
+            test '=' test |
+            '**' test |
+            '*' test )
+
+comp_iter: comp_for | comp_if
+sync_comp_for: 'for' exprlist 'in' or_test [comp_iter]
+comp_for: ['async'] sync_comp_for
+comp_if: 'if' or_test [comp_iter]
+
+# not used in grammar, but may appear in "node" passed from Parser to Compiler
+encoding_decl: NAME
+
+yield_expr: 'yield' [yield_arg]
+yield_arg: 'from' test | testlist_star_expr
+
+strings: (STRING | fstring)+
+fstring: FSTRING_START fstring_content* FSTRING_END
+fstring_content: FSTRING_STRING | fstring_expr
+fstring_conversion: '!' NAME
+fstring_expr: '{' (testlist_comp | yield_expr) ['='] [ fstring_conversion ] [ fstring_format_spec ] '}'
+fstring_format_spec: ':' fstring_content*
diff -Nru parso-0.8.1/parso/python/grammar39.txt parso-0.8.3/parso/python/grammar39.txt
--- parso-0.8.1/parso/python/grammar39.txt	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/grammar39.txt	2021-11-30 22:04:12.000000000 +0100
@@ -97,9 +97,7 @@
 
 namedexpr_test: test [':=' test]
 test: or_test ['if' or_test 'else' test] | lambdef
-test_nocond: or_test | lambdef_nocond
 lambdef: 'lambda' [varargslist] ':' test
-lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
 or_test: and_test ('or' and_test)*
 and_test: not_test ('and' not_test)*
 not_test: 'not' not_test | comparison
@@ -155,7 +153,7 @@
 comp_iter: comp_for | comp_if
 sync_comp_for: 'for' exprlist 'in' or_test [comp_iter]
 comp_for: ['async'] sync_comp_for
-comp_if: 'if' test_nocond [comp_iter]
+comp_if: 'if' or_test [comp_iter]
 
 # not used in grammar, but may appear in "node" passed from Parser to Compiler
 encoding_decl: NAME
diff -Nru parso-0.8.1/parso/python/parser.py parso-0.8.3/parso/python/parser.py
--- parso-0.8.1/parso/python/parser.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/parser.py	2021-11-30 22:04:12.000000000 +0100
@@ -96,8 +96,6 @@
                 # prefixes. Just ignore them.
                 children = [children[0]] + children[2:-1]
             node = self.default_node(nonterminal, children)
-        for c in children:
-            c.parent = node
         return node
 
     def convert_leaf(self, type, value, prefix, start_pos):
@@ -185,8 +183,6 @@
 
         if all_nodes:
             node = tree.PythonErrorNode(all_nodes)
-            for n in all_nodes:
-                n.parent = node
             self.stack[start_index - 1].nodes.append(node)
 
         self.stack[start_index:] = []
diff -Nru parso-0.8.1/parso/python/pep8.py parso-0.8.3/parso/python/pep8.py
--- parso-0.8.1/parso/python/pep8.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/pep8.py	2021-11-30 22:04:12.000000000 +0100
@@ -4,7 +4,7 @@
 
 from parso.python.errors import ErrorFinder, ErrorFinderConfig
 from parso.normalizer import Rule
-from parso.python.tree import search_ancestor, Flow, Scope
+from parso.python.tree import Flow, Scope
 
 
 _IMPORT_TYPES = ('import_name', 'import_from')
@@ -74,7 +74,7 @@
         parent_indentation = n.indentation
 
         next_leaf = leaf.get_next_leaf()
-        if '\n' in next_leaf.prefix:
+        if '\n' in next_leaf.prefix or '\r' in next_leaf.prefix:
             # This implies code like:
             # foobarbaz(
             #     a,
@@ -116,7 +116,7 @@
         self.type = IndentationTypes.IMPLICIT
 
         next_leaf = leaf.get_next_leaf()
-        if leaf == ':' and '\n' not in next_leaf.prefix:
+        if leaf == ':' and '\n' not in next_leaf.prefix and '\r' not in next_leaf.prefix:
             self.indentation += ' '
 
 
@@ -124,7 +124,7 @@
     type = IndentationTypes.BACKSLASH
 
     def __init__(self, config, parent_indentation, containing_leaf, spacing, parent=None):
-        expr_stmt = search_ancestor(containing_leaf, 'expr_stmt')
+        expr_stmt = containing_leaf.search_ancestor('expr_stmt')
         if expr_stmt is not None:
             equals = expr_stmt.children[-2]
 
@@ -216,8 +216,8 @@
             endmarker = node.children[-1]
             prev = endmarker.get_previous_leaf()
             prefix = endmarker.prefix
-            if (not prefix.endswith('\n') and (
-                    prefix or prev is None or prev.value != '\n')):
+            if (not prefix.endswith('\n') and not prefix.endswith('\r') and (
+                    prefix or prev is None or prev.value not in {'\n', '\r\n', '\r'})):
                 self.add_issue(endmarker, 292, "No newline at end of file")
 
         if typ in _IMPORT_TYPES:
@@ -465,7 +465,8 @@
                             + self._config.indentation:
                         self.add_issue(part, 129, "Line with same indent as next logical block")
                     elif indentation != should_be_indentation:
-                        if not self._check_tabs_spaces(spacing) and part.value != '\n':
+                        if not self._check_tabs_spaces(spacing) and part.value not in \
+                                {'\n', '\r\n', '\r'}:
                             if value in '])}':
                                 if node.type == IndentationTypes.VERTICAL_BRACKET:
                                     self.add_issue(
@@ -652,7 +653,8 @@
             else:
                 prev_spacing = self._previous_spacing
                 if prev in _ALLOW_SPACE and spaces != prev_spacing.value \
-                        and '\n' not in self._previous_leaf.prefix:
+                        and '\n' not in self._previous_leaf.prefix \
+                        and '\r' not in self._previous_leaf.prefix:
                     message = "Whitespace before operator doesn't match with whitespace after"
                     self.add_issue(spacing, 229, message)
 
@@ -724,11 +726,11 @@
 
     def add_issue(self, node, code, message):
         if self._previous_leaf is not None:
-            if search_ancestor(self._previous_leaf, 'error_node') is not None:
+            if self._previous_leaf.search_ancestor('error_node') is not None:
                 return
             if self._previous_leaf.type == 'error_leaf':
                 return
-        if search_ancestor(node, 'error_node') is not None:
+        if node.search_ancestor('error_node') is not None:
             return
         if code in (901, 903):
             # 901 and 903 are raised by the ErrorFinder.
diff -Nru parso-0.8.1/parso/python/prefix.py parso-0.8.3/parso/python/prefix.py
--- parso-0.8.1/parso/python/prefix.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/prefix.py	2021-11-30 22:04:12.000000000 +0100
@@ -1,5 +1,6 @@
 import re
 from codecs import BOM_UTF8
+from typing import Tuple
 
 from parso.python.tokenize import group
 
@@ -13,11 +14,11 @@
         self.type = typ
         self.value = value
         self.spacing = spacing
-        self.start_pos = start_pos
+        self.start_pos: Tuple[int, int] = start_pos
 
     @property
-    def end_pos(self):
-        if self.value.endswith('\n'):
+    def end_pos(self) -> Tuple[int, int]:
+        if self.value.endswith('\n') or self.value.endswith('\r'):
             return self.start_pos[0] + 1, 0
         if self.value == unicode_bom:
             # The bom doesn't have a length at the start of a Python file.
@@ -39,10 +40,18 @@
             self.start_pos
         )
 
+    def search_ancestor(self, *node_types):
+        node = self.parent
+        while node is not None:
+            if node.type in node_types:
+                return node
+            node = node.parent
+        return None
+
 
 _comment = r'#[^\n\r\f]*'
-_backslash = r'\\\r?\n'
-_newline = r'\r?\n'
+_backslash = r'\\\r?\n|\\\r'
+_newline = r'\r?\n|\r'
 _form_feed = r'\f'
 _only_spacing = '$'
 _spacing = r'[ \t]*'
@@ -85,7 +94,7 @@
             bom = True
 
         start = match.end(0)
-        if value.endswith('\n'):
+        if value.endswith('\n') or value.endswith('\r'):
             line += 1
             column = -start
 
diff -Nru parso-0.8.1/parso/python/tokenize.py parso-0.8.3/parso/python/tokenize.py
--- parso-0.8.1/parso/python/tokenize.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/tokenize.py	2021-11-30 22:04:12.000000000 +0100
@@ -110,10 +110,11 @@
             _create_token_collection(version_info)
         return result
 
+
 unicode_character_name = r'[A-Za-z0-9\-]+(?: [A-Za-z0-9\-]+)*'
 fstring_string_single_line = _compile(
-    r'(?:\{\{|\}\}|\\N\{' + unicode_character_name +
-    r'\}|\\(?:\r\n?|\n)|\\[^\r\nN]|[^{}\r\n\\])+'
+    r'(?:\{\{|\}\}|\\N\{' + unicode_character_name
+    + r'\}|\\(?:\r\n?|\n)|\\[^\r\nN]|[^{}\r\n\\])+'
 )
 fstring_string_multi_line = _compile(
     r'(?:\{\{|\}\}|\\N\{' + unicode_character_name + r'\}|\\[^N]|[^{}\\])+'
@@ -547,7 +548,7 @@
                     additional_prefix = prefix + token
                 new_line = True
             elif initial == '#':  # Comments
-                assert not token.endswith("\n")
+                assert not token.endswith("\n") and not token.endswith("\r")
                 if fstring_stack and fstring_stack[-1].is_in_expr():
                     # `#` is not allowed in f-string expressions
                     yield PythonToken(ERRORTOKEN, initial, spos, prefix)
diff -Nru parso-0.8.1/parso/python/tree.py parso-0.8.3/parso/python/tree.py
--- parso-0.8.1/parso/python/tree.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/python/tree.py	2021-11-30 22:04:12.000000000 +0100
@@ -47,9 +47,9 @@
     from collections.abc import Mapping
 except ImportError:
     from collections import Mapping
+from typing import Tuple
 
-from parso.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, \
-    search_ancestor
+from parso.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, search_ancestor  # noqa
 from parso.python.prefix import split_prefix
 from parso.utils import split_lines
 
@@ -149,7 +149,7 @@
     __slots__ = ()
 
     @property
-    def end_pos(self):
+    def end_pos(self) -> Tuple[int, int]:
         return self.line, self.column + len(self.value)
 
 
@@ -548,7 +548,11 @@
     def __init__(self, children):
         super().__init__(children)
         parameters = self.children[2]  # After `def foo`
-        parameters.children[1:-1] = _create_params(parameters, parameters.children[1:-1])
+        parameters_children = parameters.children[1:-1]
+        # If input parameters list already has Param objects, keep it as is;
+        # otherwise, convert it to a list of Param objects.
+        if not any(isinstance(child, Param) for child in parameters_children):
+            parameters.children[1:-1] = _create_params(parameters, parameters_children)
 
     def _get_param_nodes(self):
         return self.children[2].children
@@ -651,7 +655,11 @@
         # We don't want to call the Function constructor, call its parent.
         super(Function, self).__init__(children)
         # Everything between `lambda` and the `:` operator is a parameter.
-        self.children[1:-2] = _create_params(self, self.children[1:-2])
+        parameters_children = self.children[1:-2]
+        # If input children list already has Param objects, keep it as is;
+        # otherwise, convert it to a list of Param objects.
+        if not any(isinstance(child, Param) for child in parameters_children):
+            self.children[1:-2] = _create_params(self, parameters_children)
 
     @property
     def name(self):
@@ -775,7 +783,7 @@
         return names
 
     def get_test_node_from_name(self, name):
-        node = search_ancestor(name, "with_item")
+        node = name.search_ancestor("with_item")
         if node is None:
             raise ValueError('The name is not actually part of a with statement.')
         return node.children[0]
@@ -1079,11 +1087,9 @@
     """
     type = 'param'
 
-    def __init__(self, children, parent):
+    def __init__(self, children, parent=None):
         super().__init__(children)
         self.parent = parent
-        for child in children:
-            child.parent = self
 
     @property
     def star_count(self):
@@ -1170,7 +1176,7 @@
         """
         Returns the function/lambda of a parameter.
         """
-        return search_ancestor(self, 'funcdef', 'lambdef')
+        return self.search_ancestor('funcdef', 'lambdef')
 
     def get_code(self, include_prefix=True, include_comma=True):
         """
diff -Nru parso-0.8.1/parso/tree.py parso-0.8.3/parso/tree.py
--- parso-0.8.1/parso/tree.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/tree.py	2021-11-30 22:04:12.000000000 +0100
@@ -1,32 +1,41 @@
 from abc import abstractmethod, abstractproperty
+from typing import List, Optional, Tuple, Union
 
 from parso.utils import split_lines
 
 
-def search_ancestor(node, *node_types):
+def search_ancestor(node: 'NodeOrLeaf', *node_types: str) -> 'Optional[BaseNode]':
     """
     Recursively looks at the parents of a node and returns the first found node
-    that matches node_types. Returns ``None`` if no matching node is found.
+    that matches ``node_types``. Returns ``None`` if no matching node is found.
+
+    This function is deprecated, use :meth:`NodeOrLeaf.search_ancestor` instead.
 
     :param node: The ancestors of this node will be checked.
     :param node_types: type names that are searched for.
-    :type node_types: tuple of str
     """
-    while True:
-        node = node.parent
-        if node is None or node.type in node_types:
-            return node
+    n = node.parent
+    while n is not None:
+        if n.type in node_types:
+            return n
+        n = n.parent
+    return None
 
 
 class NodeOrLeaf:
     """
     The base class for nodes and leaves.
     """
-    __slots__ = ()
+    __slots__ = ('parent',)
     type: str
     '''
     The type is a string that typically matches the types of the grammar file.
     '''
+    parent: 'Optional[BaseNode]'
+    '''
+    The parent :class:`BaseNode` of this node or leaf.
+    None if this is the root node.
+    '''
 
     def get_root_node(self):
         """
@@ -125,7 +134,7 @@
                 return node
 
     @abstractproperty
-    def start_pos(self):
+    def start_pos(self) -> Tuple[int, int]:
         """
         Returns the starting position of the prefix as a tuple, e.g. `(3, 4)`.
 
@@ -133,7 +142,7 @@
         """
 
     @abstractproperty
-    def end_pos(self):
+    def end_pos(self) -> Tuple[int, int]:
         """
         Returns the end position of the prefix as a tuple, e.g. `(3, 4)`.
 
@@ -172,15 +181,119 @@
             e.g. a statement.
         """
 
+    def search_ancestor(self, *node_types: str) -> 'Optional[BaseNode]':
+        """
+        Recursively looks at the parents of this node or leaf and returns the
+        first found node that matches ``node_types``. Returns ``None`` if no
+        matching node is found.
+
+        :param node_types: type names that are searched for.
+        """
+        node = self.parent
+        while node is not None:
+            if node.type in node_types:
+                return node
+            node = node.parent
+        return None
+
+    def dump(self, *, indent: Optional[Union[int, str]] = 4) -> str:
+        """
+        Returns a formatted dump of the parser tree rooted at this node or leaf. This is
+        mainly useful for debugging purposes.
+
+        The ``indent`` parameter is interpreted in a similar way as :py:func:`ast.dump`.
+        If ``indent`` is a non-negative integer or string, then the tree will be
+        pretty-printed with that indent level. An indent level of 0, negative, or ``""``
+        will only insert newlines. ``None`` selects the single line representation.
+        Using a positive integer indent indents that many spaces per level. If
+        ``indent`` is a string (such as ``"\\t"``), that string is used to indent each
+        level.
+
+        :param indent: Indentation style as described above. The default indentation is
+            4 spaces, which yields a pretty-printed dump.
+
+        >>> import parso
+        >>> print(parso.parse("lambda x, y: x + y").dump())
+        Module([
+            Lambda([
+                Keyword('lambda', (1, 0)),
+                Param([
+                    Name('x', (1, 7), prefix=' '),
+                    Operator(',', (1, 8)),
+                ]),
+                Param([
+                    Name('y', (1, 10), prefix=' '),
+                ]),
+                Operator(':', (1, 11)),
+                PythonNode('arith_expr', [
+                    Name('x', (1, 13), prefix=' '),
+                    Operator('+', (1, 15), prefix=' '),
+                    Name('y', (1, 17), prefix=' '),
+                ]),
+            ]),
+            EndMarker('', (1, 18)),
+        ])
+        """
+        if indent is None:
+            newline = False
+            indent_string = ''
+        elif isinstance(indent, int):
+            newline = True
+            indent_string = ' ' * indent
+        elif isinstance(indent, str):
+            newline = True
+            indent_string = indent
+        else:
+            raise TypeError(f"expect 'indent' to be int, str or None, got {indent!r}")
+
+        def _format_dump(node: NodeOrLeaf, indent: str = '', top_level: bool = True) -> str:
+            result = ''
+            node_type = type(node).__name__
+            if isinstance(node, Leaf):
+                result += f'{indent}{node_type}('
+                if isinstance(node, ErrorLeaf):
+                    result += f'{node.token_type!r}, '
+                elif isinstance(node, TypedLeaf):
+                    result += f'{node.type!r}, '
+                result += f'{node.value!r}, {node.start_pos!r}'
+                if node.prefix:
+                    result += f', prefix={node.prefix!r}'
+                result += ')'
+            elif isinstance(node, BaseNode):
+                result += f'{indent}{node_type}('
+                if isinstance(node, Node):
+                    result += f'{node.type!r}, '
+                result += '['
+                if newline:
+                    result += '\n'
+                for child in node.children:
+                    result += _format_dump(child, indent=indent + indent_string, top_level=False)
+                result += f'{indent}])'
+            else:  # pragma: no cover
+                # We shouldn't ever reach here, unless:
+                # - `NodeOrLeaf` is incorrectly subclassed else where
+                # - or a node's children list contains invalid nodes or leafs
+                # Both are unexpected internal errors.
+                raise TypeError(f'unsupported node encountered: {node!r}')
+            if not top_level:
+                if newline:
+                    result += ',\n'
+                else:
+                    result += ', '
+            return result
+
+        return _format_dump(self)
+
 
 class Leaf(NodeOrLeaf):
     '''
     Leafs are basically tokens with a better API. Leafs exactly know where they
     were defined and what text preceeds them.
     '''
-    __slots__ = ('value', 'parent', 'line', 'column', 'prefix')
+    __slots__ = ('value', 'line', 'column', 'prefix')
+    prefix: str
 
-    def __init__(self, value, start_pos, prefix=''):
+    def __init__(self, value: str, start_pos: Tuple[int, int], prefix: str = '') -> None:
         self.value = value
         '''
         :py:func:`str` The value of the current token.
@@ -191,17 +304,17 @@
         :py:func:`str` Typically a mixture of whitespace and comments. Stuff
         that is syntactically irrelevant for the syntax tree.
         '''
-        self.parent = None
+        self.parent: Optional[BaseNode] = None
         '''
         The parent :class:`BaseNode` of this leaf.
         '''
 
     @property
-    def start_pos(self):
+    def start_pos(self) -> Tuple[int, int]:
         return self.line, self.column
 
     @start_pos.setter
-    def start_pos(self, value):
+    def start_pos(self, value: Tuple[int, int]) -> None:
         self.line = value[0]
         self.column = value[1]
 
@@ -226,7 +339,7 @@
             return self.value
 
     @property
-    def end_pos(self):
+    def end_pos(self) -> Tuple[int, int]:
         lines = split_lines(self.value)
         end_pos_line = self.line + len(lines) - 1
         # Check for multiline token
@@ -256,28 +369,30 @@
     The super class for all nodes.
     A node has children, a type and possibly a parent node.
     """
-    __slots__ = ('children', 'parent')
+    __slots__ = ('children',)
 
-    def __init__(self, children):
+    def __init__(self, children: List[NodeOrLeaf]) -> None:
         self.children = children
         """
         A list of :class:`NodeOrLeaf` child nodes.
         """
-        self.parent = None
+        self.parent: Optional[BaseNode] = None
         '''
-        The parent :class:`BaseNode` of this leaf.
+        The parent :class:`BaseNode` of this node.
         None if this is the root node.
         '''
+        for child in children:
+            child.parent = self
 
     @property
-    def start_pos(self):
+    def start_pos(self) -> Tuple[int, int]:
         return self.children[0].start_pos
 
     def get_start_pos_of_prefix(self):
         return self.children[0].get_start_pos_of_prefix()
 
     @property
-    def end_pos(self):
+    def end_pos(self) -> Tuple[int, int]:
         return self.children[-1].end_pos
 
     def _get_code_for_children(self, children, include_prefix):
diff -Nru parso-0.8.1/parso/utils.py parso-0.8.3/parso/utils.py
--- parso-0.8.1/parso/utils.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/parso/utils.py	2021-11-30 22:04:12.000000000 +0100
@@ -92,7 +92,7 @@
             # UTF-8 byte-order mark
             return 'utf-8'
 
-        first_two_lines = re.match(br'(?:[^\n]*\n){0,2}', source).group(0)
+        first_two_lines = re.match(br'(?:[^\r\n]*(?:\r\n|\r|\n)){0,2}', source).group(0)
         possible_encoding = re.search(br"coding[=:]\s*([-\w.]+)",
                                       first_two_lines)
         if possible_encoding:
diff -Nru parso-0.8.1/parso.egg-info/PKG-INFO parso-0.8.3/parso.egg-info/PKG-INFO
--- parso-0.8.1/parso.egg-info/PKG-INFO	2020-12-10 16:10:12.000000000 +0100
+++ parso-0.8.3/parso.egg-info/PKG-INFO	2021-11-30 22:05:45.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: parso
-Version: 0.8.1
+Version: 0.8.3
 Summary: A Python Parser
 Home-page: https://github.com/davidhalter/parso
 Author: David Halter
@@ -13,9 +13,9 @@
         ###################################################################
         
         
-        .. image:: https://travis-ci.org/davidhalter/parso.svg?branch=master
-            :target: https://travis-ci.org/davidhalter/parso
-            :alt: Travis CI build status
+        .. image:: https://github.com/davidhalter/parso/workflows/Build/badge.svg?branch=master
+            :target: https://github.com/davidhalter/parso/actions
+            :alt: GitHub Actions build status
         
         .. image:: https://coveralls.io/repos/github/davidhalter/parso/badge.svg?branch=master
             :target: https://coveralls.io/github/davidhalter/parso?branch=master
@@ -113,6 +113,16 @@
         Unreleased
         ++++++++++
         
+        0.8.3 (2021-11-30)
+        ++++++++++++++++++
+        
+        - Add basic support for Python 3.11 and 3.12
+        
+        0.8.2 (2021-03-30)
+        ++++++++++++++++++
+        
+        - Various small bugfixes
+        
         0.8.1 (2020-12-10)
         ++++++++++++++++++
         
diff -Nru parso-0.8.1/parso.egg-info/SOURCES.txt parso-0.8.3/parso.egg-info/SOURCES.txt
--- parso-0.8.1/parso.egg-info/SOURCES.txt	2020-12-10 16:10:12.000000000 +0100
+++ parso-0.8.3/parso.egg-info/SOURCES.txt	2021-11-30 22:05:45.000000000 +0100
@@ -49,6 +49,8 @@
 parso/python/diff.py
 parso/python/errors.py
 parso/python/grammar310.txt
+parso/python/grammar311.txt
+parso/python/grammar312.txt
 parso/python/grammar36.txt
 parso/python/grammar37.txt
 parso/python/grammar38.txt
@@ -64,6 +66,7 @@
 test/fuzz_diff_parser.py
 test/test_cache.py
 test/test_diff_parser.py
+test/test_dump_tree.py
 test/test_error_recovery.py
 test/test_file_python_errors.py
 test/test_fstring.py
diff -Nru parso-0.8.1/PKG-INFO parso-0.8.3/PKG-INFO
--- parso-0.8.1/PKG-INFO	2020-12-10 16:10:12.660451400 +0100
+++ parso-0.8.3/PKG-INFO	2021-11-30 22:05:45.521858000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: parso
-Version: 0.8.1
+Version: 0.8.3
 Summary: A Python Parser
 Home-page: https://github.com/davidhalter/parso
 Author: David Halter
@@ -13,9 +13,9 @@
         ###################################################################
         
         
-        .. image:: https://travis-ci.org/davidhalter/parso.svg?branch=master
-            :target: https://travis-ci.org/davidhalter/parso
-            :alt: Travis CI build status
+        .. image:: https://github.com/davidhalter/parso/workflows/Build/badge.svg?branch=master
+            :target: https://github.com/davidhalter/parso/actions
+            :alt: GitHub Actions build status
         
         .. image:: https://coveralls.io/repos/github/davidhalter/parso/badge.svg?branch=master
             :target: https://coveralls.io/github/davidhalter/parso?branch=master
@@ -113,6 +113,16 @@
         Unreleased
         ++++++++++
         
+        0.8.3 (2021-11-30)
+        ++++++++++++++++++
+        
+        - Add basic support for Python 3.11 and 3.12
+        
+        0.8.2 (2021-03-30)
+        ++++++++++++++++++
+        
+        - Various small bugfixes
+        
         0.8.1 (2020-12-10)
         ++++++++++++++++++
         
diff -Nru parso-0.8.1/README.rst parso-0.8.3/README.rst
--- parso-0.8.1/README.rst	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/README.rst	2021-11-30 22:04:12.000000000 +0100
@@ -3,9 +3,9 @@
 ###################################################################
 
 
-.. image:: https://travis-ci.org/davidhalter/parso.svg?branch=master
-    :target: https://travis-ci.org/davidhalter/parso
-    :alt: Travis CI build status
+.. image:: https://github.com/davidhalter/parso/workflows/Build/badge.svg?branch=master
+    :target: https://github.com/davidhalter/parso/actions
+    :alt: GitHub Actions build status
 
 .. image:: https://coveralls.io/repos/github/davidhalter/parso/badge.svg?branch=master
     :target: https://coveralls.io/github/davidhalter/parso?branch=master
diff -Nru parso-0.8.1/test/failing_examples.py parso-0.8.3/test/failing_examples.py
--- parso-0.8.1/test/failing_examples.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/failing_examples.py	2021-11-30 22:04:12.000000000 +0100
@@ -145,6 +145,44 @@
     '([False], a) = x',
     'def x(): from math import *',
 
+    # invalid del statements
+    'del x + y',
+    'del x(y)',
+    'async def foo(): del await x',
+    'def foo(): del (yield x)',
+    'del [x for x in range(10)]',
+    'del *x',
+    'del *x,',
+    'del (*x,)',
+    'del [*x]',
+    'del x, *y',
+    'del *x.y,',
+    'del *x[y],',
+    'del *x[y::], z',
+    'del x, (y, *z)',
+    'del (x, *[y, z])',
+    'del [x, *(y, [*z])]',
+    'del {}',
+    'del {x}',
+    'del {x, y}',
+    'del {x, *y}',
+
+    # invalid starred expressions
+    '*x',
+    '(*x)',
+    '((*x))',
+    '1 + (*x)',
+    '*x; 1',
+    '1; *x',
+    '1\n*x',
+    'x = *y',
+    'x: int = *y',
+    'def foo(): return *x',
+    'def foo(): yield *x',
+    'f"{*x}"',
+    'for *x in 1: pass',
+    '[1 for *x in 1]',
+
     # str/bytes combinations
     '"s" b""',
     '"s" b"" ""',
@@ -198,6 +236,9 @@
     '[*[] for a in [1]]',
     'async def bla():\n def x():  await bla()',
     'del None',
+    'del True',
+    'del False',
+    'del ...',
 
     # Errors of global / nonlocal
     dedent('''
@@ -296,6 +337,13 @@
                 def z():
                     nonlocal a
         '''),
+    # Name is assigned before nonlocal declaration
+    dedent('''
+        def x(a):
+            def y():
+                a = 10
+                nonlocal a
+       '''),
 ]
 
 if sys.version_info[:2] >= (3, 7):
diff -Nru parso-0.8.1/test/normalizer_issue_files/allowed_syntax.py parso-0.8.3/test/normalizer_issue_files/allowed_syntax.py
--- parso-0.8.1/test/normalizer_issue_files/allowed_syntax.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/normalizer_issue_files/allowed_syntax.py	2021-11-30 22:04:12.000000000 +0100
@@ -46,6 +46,28 @@
     global a
 
 
+def x(*args, c=2, d):
+    pass
+
+
+def x(*, c=2, d):
+    pass
+
+
+def x(a, b=1, *args, c=2, d):
+    pass
+
+
+def x(a, b=1, *, c=2, d):
+    pass
+
+
+lambda *args, c=2, d: (c, d)
+lambda *, c=2, d: (c, d)
+lambda a, b=1, *args, c=2, d: (c, d)
+lambda a, b=1, *, c=2, d: (c, d)
+
+
 *foo, a = (1,)
 *foo[0], a = (1,)
 *[], a = (1,)
@@ -113,6 +135,29 @@
             nonlocal a
 
 
+def x(a):
+    def y():
+        nonlocal a
+
+
+def x(a, b):
+    def y():
+        nonlocal b
+        nonlocal a
+
+
+def x(a):
+    def y():
+        def z():
+            nonlocal a
+
+
+def x():
+    def y(a):
+        def z():
+            nonlocal a
+
+
 a = *args, *args
 error[(*args, *args)] = 3
 *args, *args
diff -Nru parso-0.8.1/test/test_cache.py parso-0.8.3/test/test_cache.py
--- parso-0.8.1/test/test_cache.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_cache.py	2021-11-30 22:04:12.000000000 +0100
@@ -137,7 +137,7 @@
     parse('somecode', cache=True, path=p)
     node_cache_item = next(iter(parser_cache.values()))[p]
     now = time.time()
-    assert node_cache_item.last_used < now
+    assert node_cache_item.last_used <= now
 
     if use_file_io:
         f = _FixedTimeFileIO(p, 'code', node_cache_item.last_used - 10)
@@ -146,7 +146,7 @@
         parse('somecode2', cache=True, path=p, diff_cache=diff_cache)
 
     node_cache_item = next(iter(parser_cache.values()))[p]
-    assert now < node_cache_item.last_used < time.time()
+    assert now <= node_cache_item.last_used <= time.time()
 
 
 @skip_pypy
@@ -185,6 +185,9 @@
     was_called = False
 
     monkeypatch.setattr(cache, '_save_to_file_system', save)
-    with pytest.warns(Warning):
-        parse(path=__file__, cache=True, diff_cache=True)
-    assert was_called
+    try:
+        with pytest.warns(Warning):
+            parse(path=__file__, cache=True, diff_cache=True)
+        assert was_called
+    finally:
+        parser_cache.clear()
diff -Nru parso-0.8.1/test/test_dump_tree.py parso-0.8.3/test/test_dump_tree.py
--- parso-0.8.1/test/test_dump_tree.py	1970-01-01 01:00:00.000000000 +0100
+++ parso-0.8.3/test/test_dump_tree.py	2021-11-30 22:04:12.000000000 +0100
@@ -0,0 +1,182 @@
+from textwrap import dedent
+
+import pytest
+
+from parso import parse
+# Using star import for easier eval testing below.
+from parso.python.tree import *  # noqa: F403
+from parso.tree import *  # noqa: F403
+from parso.tree import ErrorLeaf, TypedLeaf
+
+
+@pytest.mark.parametrize(
+    'indent,expected_dump', [
+        (None, "Module(["
+               "Lambda(["
+               "Keyword('lambda', (1, 0)), "
+               "Param(["
+               "Name('x', (1, 7), prefix=' '), "
+               "Operator(',', (1, 8)), "
+               "]), "
+               "Param(["
+               "Name('y', (1, 10), prefix=' '), "
+               "]), "
+               "Operator(':', (1, 11)), "
+               "PythonNode('arith_expr', ["
+               "Name('x', (1, 13), prefix=' '), "
+               "Operator('+', (1, 15), prefix=' '), "
+               "Name('y', (1, 17), prefix=' '), "
+               "]), "
+               "]), "
+               "EndMarker('', (1, 18)), "
+               "])"),
+        (0, dedent('''\
+            Module([
+            Lambda([
+            Keyword('lambda', (1, 0)),
+            Param([
+            Name('x', (1, 7), prefix=' '),
+            Operator(',', (1, 8)),
+            ]),
+            Param([
+            Name('y', (1, 10), prefix=' '),
+            ]),
+            Operator(':', (1, 11)),
+            PythonNode('arith_expr', [
+            Name('x', (1, 13), prefix=' '),
+            Operator('+', (1, 15), prefix=' '),
+            Name('y', (1, 17), prefix=' '),
+            ]),
+            ]),
+            EndMarker('', (1, 18)),
+            ])''')),
+        (4, dedent('''\
+            Module([
+                Lambda([
+                    Keyword('lambda', (1, 0)),
+                    Param([
+                        Name('x', (1, 7), prefix=' '),
+                        Operator(',', (1, 8)),
+                    ]),
+                    Param([
+                        Name('y', (1, 10), prefix=' '),
+                    ]),
+                    Operator(':', (1, 11)),
+                    PythonNode('arith_expr', [
+                        Name('x', (1, 13), prefix=' '),
+                        Operator('+', (1, 15), prefix=' '),
+                        Name('y', (1, 17), prefix=' '),
+                    ]),
+                ]),
+                EndMarker('', (1, 18)),
+            ])''')),
+        ('\t', dedent('''\
+            Module([
+            \tLambda([
+            \t\tKeyword('lambda', (1, 0)),
+            \t\tParam([
+            \t\t\tName('x', (1, 7), prefix=' '),
+            \t\t\tOperator(',', (1, 8)),
+            \t\t]),
+            \t\tParam([
+            \t\t\tName('y', (1, 10), prefix=' '),
+            \t\t]),
+            \t\tOperator(':', (1, 11)),
+            \t\tPythonNode('arith_expr', [
+            \t\t\tName('x', (1, 13), prefix=' '),
+            \t\t\tOperator('+', (1, 15), prefix=' '),
+            \t\t\tName('y', (1, 17), prefix=' '),
+            \t\t]),
+            \t]),
+            \tEndMarker('', (1, 18)),
+            ])''')),
+    ]
+)
+def test_dump_parser_tree(indent, expected_dump):
+    code = "lambda x, y: x + y"
+    module = parse(code)
+    assert module.dump(indent=indent) == expected_dump
+
+    # Check that dumped tree can be eval'd to recover the parser tree and original code.
+    recovered_code = eval(expected_dump).get_code()
+    assert recovered_code == code
+
+
+@pytest.mark.parametrize(
+    'node,expected_dump,expected_code', [
+        (  # Dump intermediate node (not top level module)
+            parse("def foo(x, y): return x + y").children[0], dedent('''\
+                Function([
+                    Keyword('def', (1, 0)),
+                    Name('foo', (1, 4), prefix=' '),
+                    PythonNode('parameters', [
+                        Operator('(', (1, 7)),
+                        Param([
+                            Name('x', (1, 8)),
+                            Operator(',', (1, 9)),
+                        ]),
+                        Param([
+                            Name('y', (1, 11), prefix=' '),
+                        ]),
+                        Operator(')', (1, 12)),
+                    ]),
+                    Operator(':', (1, 13)),
+                    ReturnStmt([
+                        Keyword('return', (1, 15), prefix=' '),
+                        PythonNode('arith_expr', [
+                            Name('x', (1, 22), prefix=' '),
+                            Operator('+', (1, 24), prefix=' '),
+                            Name('y', (1, 26), prefix=' '),
+                        ]),
+                    ]),
+                ])'''),
+            "def foo(x, y): return x + y",
+        ),
+        (  # Dump leaf
+            parse("def foo(x, y): return x + y").children[0].children[0],
+            "Keyword('def', (1, 0))",
+            'def',
+        ),
+        (  # Dump ErrorLeaf
+            ErrorLeaf('error_type', 'error_code', (1, 1), prefix=' '),
+            "ErrorLeaf('error_type', 'error_code', (1, 1), prefix=' ')",
+            ' error_code',
+        ),
+        (  # Dump TypedLeaf
+            TypedLeaf('type', 'value', (1, 1)),
+            "TypedLeaf('type', 'value', (1, 1))",
+            'value',
+        ),
+    ]
+)
+def test_dump_parser_tree_not_top_level_module(node, expected_dump, expected_code):
+    dump_result = node.dump()
+    assert dump_result == expected_dump
+
+    # Check that dumped tree can be eval'd to recover the parser tree and original code.
+    recovered_code = eval(dump_result).get_code()
+    assert recovered_code == expected_code
+
+
+def test_dump_parser_tree_invalid_args():
+    module = parse("lambda x, y: x + y")
+
+    with pytest.raises(TypeError):
+        module.dump(indent=1.1)
+
+
+def test_eval_dump_recovers_parent():
+    module = parse("lambda x, y: x + y")
+    module2 = eval(module.dump())
+    assert module2.parent is None
+    lambda_node = module2.children[0]
+    assert lambda_node.parent is module2
+    assert module2.children[1].parent is module2
+    assert lambda_node.children[0].parent is lambda_node
+    param_node = lambda_node.children[1]
+    assert param_node.parent is lambda_node
+    assert param_node.children[0].parent is param_node
+    assert param_node.children[1].parent is param_node
+    arith_expr_node = lambda_node.children[-1]
+    assert arith_expr_node.parent is lambda_node
+    assert arith_expr_node.children[0].parent is arith_expr_node
diff -Nru parso-0.8.1/test/test_normalizer_issues_files.py parso-0.8.3/test/test_normalizer_issues_files.py
--- parso-0.8.1/test/test_normalizer_issues_files.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_normalizer_issues_files.py	2021-11-30 22:04:12.000000000 +0100
@@ -6,6 +6,7 @@
 import difflib
 import re
 from functools import total_ordering
+from typing import Iterator, Tuple
 
 import parso
 from parso.utils import python_bytes_to_unicode
@@ -13,7 +14,7 @@
 
 @total_ordering
 class WantedIssue:
-    def __init__(self, code, line, column):
+    def __init__(self, code: str, line: int, column: int) -> None:
         self.code = code
         self._line = line
         self._column = column
@@ -21,18 +22,18 @@
     def __eq__(self, other):
         return self.code == other.code and self.start_pos == other.start_pos
 
-    def __lt__(self, other):
+    def __lt__(self, other: 'WantedIssue') -> bool:
         return self.start_pos < other.start_pos or self.code < other.code
 
-    def __hash__(self):
+    def __hash__(self) -> int:
         return hash(str(self.code) + str(self._line) + str(self._column))
 
     @property
-    def start_pos(self):
+    def start_pos(self) -> Tuple[int, int]:
         return self._line, self._column
 
 
-def collect_errors(code):
+def collect_errors(code: str) -> Iterator[WantedIssue]:
     for line_nr, line in enumerate(code.splitlines(), 1):
         match = re.match(r'(\s*)#: (.*)$', line)
         if match is not None:
diff -Nru parso-0.8.1/test/test_parser_tree.py parso-0.8.3/test/test_parser_tree.py
--- parso-0.8.1/test/test_parser_tree.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_parser_tree.py	2021-11-30 22:04:12.000000000 +0100
@@ -6,6 +6,7 @@
 
 from parso import parse
 from parso.python import tree
+from parso.tree import search_ancestor
 
 
 class TestsFunctionAndLambdaParsing:
@@ -239,3 +240,27 @@
         for name in with_stmt.get_defined_names(include_setitem=True)
     ]
     assert tests == ["A", "B", "C", "D"]
+
+
+sample_module = parse('x + y')
+sample_node = sample_module.children[0]
+sample_leaf = sample_node.children[0]
+
+
+@pytest.mark.parametrize(
+    'node,node_types,expected_ancestor', [
+        (sample_module, ('file_input',), None),
+        (sample_node, ('arith_expr',), None),
+        (sample_node, ('file_input', 'eval_input'), sample_module),
+        (sample_leaf, ('name',), None),
+        (sample_leaf, ('arith_expr',), sample_node),
+        (sample_leaf, ('file_input',), sample_module),
+        (sample_leaf, ('file_input', 'arith_expr'), sample_node),
+        (sample_leaf, ('shift_expr',), None),
+        (sample_leaf, ('name', 'shift_expr',), None),
+        (sample_leaf, (), None),
+    ]
+)
+def test_search_ancestor(node, node_types, expected_ancestor):
+    assert node.search_ancestor(*node_types) is expected_ancestor
+    assert search_ancestor(node, *node_types) is expected_ancestor  # deprecated
diff -Nru parso-0.8.1/test/test_pep8.py parso-0.8.3/test/test_pep8.py
--- parso-0.8.1/test/test_pep8.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_pep8.py	2021-11-30 22:04:12.000000000 +0100
@@ -15,6 +15,8 @@
         assert issue.code == 292
 
     assert not issues('asdf = 1\n')
+    assert not issues('asdf = 1\r\n')
+    assert not issues('asdf = 1\r')
     assert_issue('asdf = 1')
     assert_issue('asdf = 1\n# foo')
     assert_issue('# foobar')
diff -Nru parso-0.8.1/test/test_pgen2.py parso-0.8.3/test/test_pgen2.py
--- parso-0.8.1/test/test_pgen2.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_pgen2.py	2021-11-30 22:04:12.000000000 +0100
@@ -339,7 +339,7 @@
 @pytest.mark.parametrize(
     'grammar, error_match', [
         ['foo: bar | baz\nbar: NAME\nbaz: NAME\n',
-         r"foo is ambiguous.*given a PythonTokenTypes\.NAME.*bar or baz"],
+         r"foo is ambiguous.*given a (PythonTokenTypes\.)?NAME.*bar or baz"],
         ['''foo: bar | baz\nbar: 'x'\nbaz: "x"\n''',
          r"foo is ambiguous.*given a ReservedString\(x\).*bar or baz"],
         ['''foo: bar | 'x'\nbar: 'x'\n''',
diff -Nru parso-0.8.1/test/test_prefix.py parso-0.8.3/test/test_prefix.py
--- parso-0.8.1/test/test_prefix.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_prefix.py	2021-11-30 22:04:12.000000000 +0100
@@ -19,6 +19,7 @@
     (' \f ', ['\f', ' ']),
     (' \f ', ['\f', ' ']),
     (' \r\n', ['\r\n', '']),
+    (' \r', ['\r', '']),
     ('\\\n', ['\\\n', '']),
     ('\\\r\n', ['\\\r\n', '']),
     ('\t\t\n\t', ['\n', '\t']),
@@ -34,7 +35,7 @@
         assert pt.value == expected
 
         # Calculate the estimated end_pos
-        if expected.endswith('\n'):
+        if expected.endswith('\n') or expected.endswith('\r'):
             end_pos = start_pos[0] + 1, 0
         else:
             end_pos = start_pos[0], start_pos[1] + len(expected) + len(pt.spacing)
diff -Nru parso-0.8.1/test/test_python_errors.py parso-0.8.3/test/test_python_errors.py
--- parso-0.8.1/test/test_python_errors.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_python_errors.py	2021-11-30 22:04:12.000000000 +0100
@@ -57,10 +57,10 @@
         error, = errors
         actual = error.message
     assert actual in wanted
-    if sys.version_info[:2] < (3, 8):
+    if sys.version_info[:2] not in ((3, 8), (3, 9)):
         assert line_nr == error.start_pos[0]
     else:
-        assert line_nr == 0  # For whatever reason this is zero in Python 3.8+
+        assert line_nr == 0  # For whatever reason this is zero in Python 3.8/3.9
 
 
 @pytest.mark.parametrize(
@@ -140,13 +140,16 @@
 
 
 def test_default_except_error_postition():
-    # For this error the position seemed to be one line off, but that doesn't
-    # really matter.
+    # For this error the position seemed to be one line off in Python < 3.10,
+    # but that doesn't really matter.
     code = 'try: pass\nexcept: pass\nexcept X: pass'
     wanted, line_nr = _get_actual_exception(code)
     error, = _get_error_list(code)
     assert error.message in wanted
-    assert line_nr != error.start_pos[0]
+    if sys.version_info[:2] >= (3, 10):
+        assert line_nr == error.start_pos[0]
+    else:
+        assert line_nr != error.start_pos[0]
     # I think this is the better position.
     assert error.start_pos[0] == 2
 
@@ -415,11 +418,28 @@
         ('*x = 2', False),
         ('(*y) = 1', False),
         ('((*z)) = 1', False),
+        ('*a,', True),
+        ('*a, = 1', True),
+        ('(*a,)', True),
+        ('(*a,) = 1', True),
+        ('[*a]', True),
+        ('[*a] = 1', True),
+        ('a, *b', True),
         ('a, *b = 1', True),
+        ('a, *b, c', True),
         ('a, *b, c = 1', True),
-        ('a, (*b), c = 1', True),
-        ('a, ((*b)), c = 1', True),
+        ('a, (*b, c), d', True),
         ('a, (*b, c), d = 1', True),
+        ('*a.b,', True),
+        ('*a.b, = 1', True),
+        ('*a[b],', True),
+        ('*a[b], = 1', True),
+        ('*a[b::], c', True),
+        ('*a[b::], c = 1', True),
+        ('(a, *[b, c])', True),
+        ('(a, *[b, c]) = 1', True),
+        ('[a, *(b, [*c])]', True),
+        ('[a, *(b, [*c])] = 1', True),
         ('[*(1,2,3)]', True),
         ('{*(1,2,3)}', True),
         ('[*(1,2,3),]', True),
@@ -432,3 +452,59 @@
 )
 def test_starred_expr(source, no_errors):
     assert bool(_get_error_list(source, version="3")) ^ no_errors
+
+
+@pytest.mark.parametrize(
+    'code', [
+        'a, (*b), c',
+        'a, (*b), c = 1',
+        'a, ((*b)), c',
+        'a, ((*b)), c = 1',
+    ]
+)
+def test_parenthesized_single_starred_expr(code):
+    assert not _get_error_list(code, version='3.8')
+    assert _get_error_list(code, version='3.9')
+
+
+@pytest.mark.parametrize(
+    'code', [
+        '() = ()',
+        '() = []',
+        '[] = ()',
+        '[] = []',
+    ]
+)
+def test_valid_empty_assignment(code):
+    assert not _get_error_list(code)
+
+
+@pytest.mark.parametrize(
+    'code', [
+        'del ()',
+        'del []',
+        'del x',
+        'del x,',
+        'del x, y',
+        'del (x, y)',
+        'del [x, y]',
+        'del (x, [y, z])',
+        'del x.y, x[y]',
+        'del f(x)[y::]',
+        'del x[[*y]]',
+        'del x[[*y]::]',
+    ]
+)
+def test_valid_del(code):
+    assert not _get_error_list(code)
+
+
+@pytest.mark.parametrize(
+    ('source', 'version', 'no_errors'), [
+        ('[x for x in range(10) if lambda: 1]', '3.8', True),
+        ('[x for x in range(10) if lambda: 1]', '3.9', False),
+        ('[x for x in range(10) if (lambda: 1)]', '3.9', True),
+    ]
+)
+def test_lambda_in_comp_if(source, version, no_errors):
+    assert bool(_get_error_list(source, version=version)) ^ no_errors
diff -Nru parso-0.8.1/test/test_utils.py parso-0.8.3/test/test_utils.py
--- parso-0.8.1/test/test_utils.py	2020-12-10 16:09:15.000000000 +0100
+++ parso-0.8.3/test/test_utils.py	2021-11-30 22:04:12.000000000 +0100
@@ -74,6 +74,10 @@
     ('code', 'errors'), [
         (b'# coding: wtf-12\nfoo', 'strict'),
         (b'# coding: wtf-12\nfoo', 'replace'),
+        (b'# coding: wtf-12\r\nfoo', 'strict'),
+        (b'# coding: wtf-12\r\nfoo', 'replace'),
+        (b'# coding: wtf-12\rfoo', 'strict'),
+        (b'# coding: wtf-12\rfoo', 'replace'),
     ]
 )
 def test_bytes_to_unicode_failing_encoding(code, errors):

Attachment: signature.asc
Description: PGP signature

Reply via email to