From: Boyuan Yang <byang@debian.org>
Date: Sun, 28 Jul 2024 12:46:54 -0400
Subject: Python 3.11 compatibility

Forwarded: no
---
 src/lepl/apps/rfc3696.py                    |  2 +-
 src/lepl/lexer/lines/_example/line_aware.py |  8 ++++----
 src/lepl/matchers/derived.py                |  2 +-
 src/lepl/matchers/operators.py              |  4 ++--
 src/lepl/matchers/support.py                | 20 ++++++++++----------
 src/lepl/regexp/str.py                      |  2 +-
 src/lepl/stream/factory.py                  |  5 ++++-
 src/lepl/support/graph.py                   |  6 +++++-
 8 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/src/lepl/apps/rfc3696.py b/src/lepl/apps/rfc3696.py
index 1477b44..f333139 100644
--- a/src/lepl/apps/rfc3696.py
+++ b/src/lepl/apps/rfc3696.py
@@ -425,7 +425,7 @@ def HttpUrl():
 
 
 def MailToUrl():
-    '''
+    r'''
     Generate a validator for email addresses, according to RFC3696, which 
     returns True if the URL is valid, and False otherwise.
     
diff --git a/src/lepl/lexer/lines/_example/line_aware.py b/src/lepl/lexer/lines/_example/line_aware.py
index d1421d9..d3d861c 100644
--- a/src/lepl/lexer/lines/_example/line_aware.py
+++ b/src/lepl/lexer/lines/_example/line_aware.py
@@ -67,8 +67,8 @@ class LineAwareTest(TestCase):
         
     def test_extend(self):
         #basicConfig(level=DEBUG)
-        contents = Token('[a-z]+')[:] > list
-        parens = Token('\(') & contents & Token('\)') > list
+        contents = Token(r'[a-z]+')[:] > list
+        parens = Token(r'\(') & contents & Token(r'\)') > list
         line = Line(contents & Optional(Extend(parens)))
         lines = line[:]
         lines.config.lines()
@@ -80,8 +80,8 @@ class LineAwareTest(TestCase):
         This returned None.
         '''
         #basicConfig(level=DEBUG)
-        contents = Token('[a-z]+')[:] > list
-        parens = Token('\(') & contents & Token('\)') > list
+        contents = Token(r'[a-z]+')[:] > list
+        parens = Token(r'\(') & contents & Token(r'\)') > list
         line = Line(contents & Optional(Extend(parens)))
         lines = line[:]
         lines.config.lines().record_deepest()
diff --git a/src/lepl/matchers/derived.py b/src/lepl/matchers/derived.py
index ba568c0..7d0c7a2 100644
--- a/src/lepl/matchers/derived.py
+++ b/src/lepl/matchers/derived.py
@@ -219,7 +219,7 @@ def args(function):
 
 
 def KApply(matcher, function, raw=False):
-    '''
+    r'''
     Apply an arbitrary function to named arguments (**\****).
     The function should typically expect and return a list.
     It can be used indirectly by placing ``**=`` to the right of the matcher.
diff --git a/src/lepl/matchers/operators.py b/src/lepl/matchers/operators.py
index f8c33a1..168de67 100644
--- a/src/lepl/matchers/operators.py
+++ b/src/lepl/matchers/operators.py
@@ -38,7 +38,7 @@ from lepl.support.context import Namespace, NamespaceMixin, Scope
 from lepl.support.lib import open_stop, fmt, basestring
 
 
-DIGITS = compile_('^(-?\d+)(.*)')
+DIGITS = compile_(r'^(-?\d+)(.*)')
 
 def RepeatWrapper(matcher, start, stop, step, separator, add, reduce):
     '''Parse `step` if it is a string.'''
@@ -682,7 +682,7 @@ class OperatorMixin(NamespaceMixin):
         return self._lookup(MAP)(self, function) 
         
     def __pow__(self, function):
-        '''
+        r'''
         **self \** function** - Process the results (\**kargs).
         
         Apply a function to keyword arguments
diff --git a/src/lepl/matchers/support.py b/src/lepl/matchers/support.py
index 724db36..44ac811 100644
--- a/src/lepl/matchers/support.py
+++ b/src/lepl/matchers/support.py
@@ -32,7 +32,7 @@ Support classes for matchers.
 '''
 
 from abc import ABCMeta
-from inspect import getargspec
+from inspect import getfullargspec
 
 from lepl.core.config import ParserMixin
 from lepl.core.parser import GeneratorWrapper, tagged
@@ -241,9 +241,9 @@ class BaseFactoryMatcher(FactoryMatcher):
             # factory is a dummy generated in make_factory below)
             def empty(): return
             document(empty, self.factory.factory)
-            spec = getargspec(empty)
+            spec = getfullargspec(empty)
         except:
-            spec = getargspec(self.factory)
+            spec = getfullargspec(self.factory)
         names = list(spec.args)
         defaults = dict(zip(names[::-1], spec.defaults[::-1] if spec.defaults else []))
         for name in names:
@@ -267,8 +267,8 @@ class BaseFactoryMatcher(FactoryMatcher):
                                        "{0!r} in {1}(...)", 
                                        self.__args[0], self._small_str))
         if self.__kargs:
-            if spec.keywords:
-                self.__kargs(**{spec.keywords: self.__kargs})
+            if spec.varkw:
+                self.__kargs(**{spec.varkw: self.__kargs})
             else:
                 name = list(self.__kargs.keys())[0]
                 value = self.__kargs[name]
@@ -460,7 +460,7 @@ def check_matcher(matcher):
     Check that the signature takes support + stream.
     '''
     check_args(matcher)
-    spec = getargspec(matcher)
+    spec = getfullargspec(matcher)
     if len(spec.args) != 2:
         raise TypeError(fmt(
 '''The function {0} cannot be used as a matcher because it does not have
@@ -482,7 +482,7 @@ def check_args(func):
     extensions...
     '''
     try:
-        getargspec(func)
+        getfullargspec(func)
     except Exception as e:
         raise TypeError(fmt(
 '''The function {0} uses Python 3 style parameters (keyword only, etc).
@@ -494,7 +494,7 @@ def check_modifiers(func, modifiers):
     '''
     Check that any modifiers match the function declaration.
     '''
-    argspec = getargspec(func)
+    argspec = getfullargspec(func)
     for name in modifiers:
         if name not in argspec.args:
             raise TypeError(
@@ -506,7 +506,7 @@ def apply_modifiers(func, args, kargs, modifiers, margs, mkargs):
     '''
     Modify values in args and kargs.
     '''
-    spec = getargspec(func)
+    spec = getfullargspec(func)
     names = list(spec.args)
     defaults = dict(zip(names[::-1], spec.defaults[::-1] if spec.defaults else []))
     newargs = []
@@ -537,7 +537,7 @@ def apply_modifiers(func, args, kargs, modifiers, margs, mkargs):
     elif args:
         raise TypeError(fmt("Unexpected argument {0!r} for {1}(...)", 
                                args[0], func.__name__))
-    if spec.keywords:
+    if spec.varkw:
         for name in kargs:
             newkargs[name] = mkargs(kargs[name])
     elif kargs:
diff --git a/src/lepl/regexp/str.py b/src/lepl/regexp/str.py
index 88e94d3..866b4ce 100644
--- a/src/lepl/regexp/str.py
+++ b/src/lepl/regexp/str.py
@@ -45,7 +45,7 @@ Characters that must be escaped.
 
 
 class StrParser(LogMixin):
-    '''
+    r'''
     Construct a parser for string based expressions.
     
     We need a clear policy on backslashes.  To be as backwards compatible as
diff --git a/src/lepl/stream/factory.py b/src/lepl/stream/factory.py
index af8f3e9..2eb0dee 100644
--- a/src/lepl/stream/factory.py
+++ b/src/lepl/stream/factory.py
@@ -28,7 +28,10 @@
 # MPL or the LGPL License.
 
 
-from collections import Iterable
+try:
+    from collections.abc import Iterable
+except ImportError:
+    from collections import Iterable
 
 from lepl.stream.simple import SequenceHelper, StringHelper, ListHelper
 from lepl.stream.iter import IterableHelper, Cons
diff --git a/src/lepl/support/graph.py b/src/lepl/support/graph.py
index 12ac358..e1005e2 100644
--- a/src/lepl/support/graph.py
+++ b/src/lepl/support/graph.py
@@ -59,7 +59,11 @@ on constructors described above: the walker takes a visitor sub-class and
 calls it in a way that replicates the original calls to the node constructors.
 '''
 
-from collections import Sequence, deque
+try:
+    from collections import Sequence, deque
+except ImportError:
+    from collections.abc import Sequence
+    from collections import deque
 
 from lepl.support.lib import compose, safe_in, safe_add, empty, fmt,\
     fallback_add
