Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-repoze.who for 
openSUSE:Factory checked in at 2023-12-08 22:33:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-repoze.who (Old)
 and      /work/SRC/openSUSE:Factory/.python-repoze.who.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-repoze.who"

Fri Dec  8 22:33:34 2023 rev:9 rq:1132101 version:3.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-repoze.who/python-repoze.who.changes      
2023-06-12 15:27:29.475421104 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-repoze.who.new.25432/python-repoze.who.changes
   2023-12-08 22:34:27.065293134 +0100
@@ -1,0 +2,9 @@
+Fri Dec  8 13:40:57 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 3.0.0:
+  * Add support for Python 3.9, 3.10 and 3.11.
+  * Drop support for Python 2.7, 3.4, 3.5, and 3.6.
+  * Add Github Actions workflow to exercise unit tests /
+    coverage.
+
+-------------------------------------------------------------------

Old:
----
  repoze.who-2.4.1.tar.gz

New:
----
  repoze.who-3.0.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-repoze.who.spec ++++++
--- /var/tmp/diff_new_pack.aPQL0d/_old  2023-12-08 22:34:27.681315800 +0100
+++ /var/tmp/diff_new_pack.aPQL0d/_new  2023-12-08 22:34:27.681315800 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-repoze.who
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %global modname repoze.who
 %{?sle15_python_module_pythons}
 Name:           python-repoze.who
-Version:        2.4.1
+Version:        3.0.0
 Release:        0
 Summary:        Identification and authentication framework for WSGI
 License:        SUSE-Repoze

++++++ repoze.who-2.4.1.tar.gz -> repoze.who-3.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/.gitignore 
new/repoze.who-3.0.0/.gitignore
--- old/repoze.who-2.4.1/.gitignore     2015-03-18 20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/.gitignore     2022-02-01 18:00:08.000000000 +0100
@@ -1,8 +1,10 @@
+build/
+dist/
+docs/.build/
+docs/_build/
+.tox/
 *.pyc
 *.egg-info
 .coverage
-docs/.build
-.tox
 coverage.xml
 nosetests.xml
-docs/_build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/.travis.yml 
new/repoze.who-3.0.0/.travis.yml
--- old/repoze.who-2.4.1/.travis.yml    2022-01-31 17:53:31.000000000 +0100
+++ new/repoze.who-3.0.0/.travis.yml    1970-01-01 01:00:00.000000000 +0100
@@ -1,31 +0,0 @@
-# Wire up travis
-language: python
-sudo : false
-
-matrix:
-  include:
-    - python: 2.7
-      env: TOXENV=py27
-    - python: 3.4
-      env: TOXENV=py34
-    - python: 3.5
-      env: TOXENV=py35
-    - python: 3.6
-      env: TOXENV=py36
-    - python: 3.7
-      env: TOXENV=py37
-    - python: 3.8
-      env: TOXENV=py38
-    - python: pypy
-      env: TOXENV=pypy
-    - python: pypy3
-      env: TOXENV=pypy3
-    - python: 3.8
-      env: TOXENV=cover
-
-install:
-  - travis_retry pip install tox
-
-script:
-  - travis_retry tox
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/CHANGES.rst 
new/repoze.who-3.0.0/CHANGES.rst
--- old/repoze.who-2.4.1/CHANGES.rst    2022-02-01 17:56:46.000000000 +0100
+++ new/repoze.who-3.0.0/CHANGES.rst    2023-01-25 15:56:30.000000000 +0100
@@ -1,6 +1,20 @@
 repoze.who Changelog
 ====================
 
+3.0.0 (2023-01-16)
+------------------
+
+- No changes from 3.0.0b1.
+
+3.0.0b1 (2023-01-16)
+--------------------
+
+- Add support for Python 3.9, 3.10 and 3.11.
+
+- Drop support for Python 2.7, 3.4, 3.5, and 3.6.
+
+- Add Github Actions workflow to exercise unit tests / coverage.
+
 2.4.1 (2022-02-01)
 ------------------
 
@@ -14,7 +28,7 @@
 2.4 (2020-06-03)
 ----------------
 
-- Add upport for Python 3.6, 3.7, and 3.8.
+- Add support for Python 3.6, 3.7, and 3.8.
 
 - Drop support for Python 3.3.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/PKG-INFO 
new/repoze.who-3.0.0/PKG-INFO
--- old/repoze.who-2.4.1/PKG-INFO       2022-02-01 17:57:35.634601400 +0100
+++ new/repoze.who-3.0.0/PKG-INFO       2023-01-25 15:57:22.017574000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: repoze.who
-Version: 2.4.1
+Version: 3.0.0
 Summary: repoze.who is an identification and authentication framework for WSGI.
 Home-page: http://www.repoze.org
 Author: Agendaless Consulting
@@ -75,6 +75,20 @@
         repoze.who Changelog
         ====================
         
+        3.0.0 (2023-01-16)
+        ------------------
+        
+        - No changes from 3.0.0b1.
+        
+        3.0.0b1 (2023-01-16)
+        --------------------
+        
+        - Add support for Python 3.9, 3.10 and 3.11.
+        
+        - Drop support for Python 2.7, 3.4, 3.5, and 3.6.
+        
+        - Add Github Actions workflow to exercise unit tests / coverage.
+        
         2.4.1 (2022-02-01)
         ------------------
         
@@ -88,7 +102,7 @@
         2.4 (2020-06-03)
         ----------------
         
-        - Add upport for Python 3.6, 3.7, and 3.8.
+        - Add support for Python 3.6, 3.7, and 3.8.
         
         - Drop support for Python 3.3.
         
@@ -796,14 +810,12 @@
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Internet :: WWW/HTTP
@@ -811,4 +823,3 @@
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
 Provides-Extra: docs
-Provides-Extra: testing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/_auth_tkt.py 
new/repoze.who-3.0.0/repoze/who/_auth_tkt.py
--- old/repoze.who-2.4.1/repoze/who/_auth_tkt.py        2022-02-01 
17:55:33.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/_auth_tkt.py        2022-02-01 
19:25:16.000000000 +0100
@@ -37,12 +37,11 @@
 non-Python code run under Apache.
 """
 import hashlib
+import http.cookies
 import time as time_mod
+import urllib.parse
 
-from repoze.who._compat import encodestring
-from repoze.who._compat import SimpleCookie
-from repoze.who._compat import url_quote
-from repoze.who._compat import url_unquote
+from repoze.who._helpers import encodestring
 
 DEFAULT_DIGEST = hashlib.md5
 
@@ -132,14 +131,14 @@
 
     def cookie_value(self):
         v = '%s%08x%s!' % (self.digest(), int(self.time),
-                           url_quote(self.userid))
+                           urllib.parse.quote(self.userid))
         if self.tokens:
             v += self.tokens + '!'
         v += self.user_data
         return v
 
     def cookie(self):
-        c = SimpleCookie()
+        c = http.cookies.SimpleCookie()
         c_val = encodestring(self.cookie_value())
         c_val = c_val.strip().replace('\n', '')
         c[self.cookie_name] = c_val
@@ -182,7 +181,7 @@
         userid, data = ticket[digest_hexa_size + 8:].split('!', 1)
     except ValueError:
         raise BadTicket('userid is not followed by !')
-    userid = url_unquote(userid)
+    userid = urllib.parse.unquote(userid)
     if '!' in data:
         tokens, user_data = data.split('!', 1)
     else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/_compat.py 
new/repoze.who-3.0.0/repoze/who/_compat.py
--- old/repoze.who-2.4.1/repoze/who/_compat.py  2015-03-18 20:33:59.000000000 
+0100
+++ new/repoze.who-3.0.0/repoze/who/_compat.py  1970-01-01 01:00:00.000000000 
+0100
@@ -1,134 +0,0 @@
-try:
-    STRING_TYPES = (str, unicode)
-except NameError: #pragma NO COVER Python >= 3.0
-    STRING_TYPES = (str,)
-
-try:
-    u = unicode
-except NameError: #pragma NO COVER Python >= 3.0
-    u = str
-    b = bytes
-else: #pragma NO COVER Python < 3.0
-    b = str
-
-import base64
-if 'decodebytes' in base64.__dict__: #pragma NO COVER Python >= 3.0
-    decodebytes = base64.decodebytes
-    encodebytes = base64.encodebytes
-    def decodestring(value):
-        return base64.decodebytes(bytes(value, 'ascii')).decode('ascii')
-    def encodestring(value):
-        return base64.encodebytes(bytes(value, 'ascii')).decode('ascii')
-else: #pragma NO COVER Python < 3.0
-    decodebytes = base64.decodestring
-    encodebytes = base64.encodestring
-    decodestring = base64.decodestring
-    encodestring = base64.encodestring
-
-try:
-    from urllib.parse import parse_qs
-except ImportError: #pragma NO COVER Python < 3.0
-    from cgi import parse_qs
-    from cgi import parse_qsl
-else: #pragma NO COVER Python >= 3.0
-    from urllib.parse import parse_qsl
-
-try:
-    import ConfigParser
-except ImportError: #pragma NO COVER Python >= 3.0
-    from configparser import ConfigParser
-    from configparser import ParsingError
-else: #pragma NO COVER Python < 3.0
-    from ConfigParser import SafeConfigParser as ConfigParser
-    from ConfigParser import ParsingError
-
-try:
-    from Cookie import SimpleCookie
-except ImportError: #pragma NO COVER Python >= 3.0
-    from http.cookies import SimpleCookie
-    from http.cookies import CookieError
-else: #pragma NO COVER Python < 3.0
-    from Cookie import CookieError
-
-try:
-    from itertools import izip_longest
-except ImportError: #pragma NO COVER Python >= 3.0
-    from itertools import zip_longest as izip_longest
-
-try:
-    from StringIO import StringIO
-except ImportError: #pragma NO COVER Python >= 3.0
-    from io import StringIO
-
-try:
-    from urllib import urlencode
-except ImportError: #pragma NO COVER Python >= 3.0
-    from urllib.parse import urlencode
-    from urllib.parse import quote as url_quote
-    from urllib.parse import unquote as url_unquote
-else: #pragma NO COVER Python < 3.0
-    from urllib import quote as url_quote
-    from urllib import unquote as url_unquote
-
-try:
-    from urlparse import urlparse
-except ImportError: #pragma NO COVER Python >= 3.0
-    from urllib.parse import urlparse
-    from urllib.parse import urlunparse
-else: #pragma NO COVER Python < 3.0
-    from urlparse import urlunparse
-
-import wsgiref.util
-import wsgiref.headers
-
-def REQUEST_METHOD(environ):
-    return environ['REQUEST_METHOD']
-
-def CONTENT_TYPE(environ):
-    return environ.get('CONTENT_TYPE', '')
-
-def USER_AGENT(environ):
-    return environ.get('HTTP_USER_AGENT')
-
-def AUTHORIZATION(environ):
-    return environ.get('HTTP_AUTHORIZATION', '')
-
-def get_cookies(environ):
-    header = environ.get('HTTP_COOKIE', '')
-    if 'paste.cookies' in environ:
-        cookies, check_header = environ['paste.cookies']
-        if check_header == header:
-            return cookies
-    cookies = SimpleCookie()
-    try:
-        cookies.load(header)
-    except CookieError: #pragma NO COVER (can't see how to provoke this)
-        pass
-    environ['paste.cookies'] = (cookies, header)
-    return cookies
-
-def construct_url(environ):
-    return wsgiref.util.request_uri(environ)
-
-def header_value(environ, key):
-    headers = wsgiref.headers.Headers(environ)
-    values = headers.get(key)
-    if not values:
-        return ""
-    if isinstance(values, list): #pragma NO COVER can't be true under Py3k.
-        return ",".join(values)
-    else:
-        return values
-
-def must_decode(value):
-    if type(value) is b:
-        try:
-            return value.decode('utf-8')
-        except UnicodeDecodeError:
-            return value.decode('latin1')
-    return value
-
-def must_encode(value):
-    if type(value) is u:
-        return value.encode('utf-8')
-    return value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/_helpers.py 
new/repoze.who-3.0.0/repoze/who/_helpers.py
--- old/repoze.who-2.4.1/repoze/who/_helpers.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/repoze.who-3.0.0/repoze/who/_helpers.py 2022-02-01 19:25:16.000000000 
+0100
@@ -0,0 +1,54 @@
+import base64
+import http.cookies
+import wsgiref.util
+import wsgiref.headers
+
+def encodestring(value):
+    return base64.encodebytes(bytes(value, 'ascii')).decode('ascii')
+
+def REQUEST_METHOD(environ):
+    return environ['REQUEST_METHOD']
+
+def CONTENT_TYPE(environ):
+    return environ.get('CONTENT_TYPE', '')
+
+def USER_AGENT(environ):
+    return environ.get('HTTP_USER_AGENT')
+
+def AUTHORIZATION(environ):
+    return environ.get('HTTP_AUTHORIZATION', '')
+
+def get_cookies(environ):
+    header = environ.get('HTTP_COOKIE', '')
+    if 'paste.cookies' in environ:
+        cookies, check_header = environ['paste.cookies']
+        if check_header == header:
+            return cookies
+    cookies = http.cookies.SimpleCookie()
+    try:
+        cookies.load(header)
+    except http.cookies.CookieError: #pragma NO COVER (can't see how to 
provoke this)
+        pass
+    environ['paste.cookies'] = (cookies, header)
+    return cookies
+
+def construct_url(environ):
+    return wsgiref.util.request_uri(environ)
+
+def header_value(environ, key):
+    headers = wsgiref.headers.Headers(environ)
+    values = headers.get(key)
+    return values or ""
+
+def must_decode(value):
+    if type(value) is bytes:
+        try:
+            return value.decode('utf-8')
+        except UnicodeDecodeError:
+            return value.decode('latin1')
+    return value
+
+def must_encode(value):
+    if type(value) is str:
+        return value.encode('utf-8')
+    return value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/classifiers.py 
new/repoze.who-3.0.0/repoze/who/classifiers.py
--- old/repoze.who-2.4.1/repoze/who/classifiers.py      2015-03-18 
20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/classifiers.py      2022-02-01 
19:25:16.000000000 +0100
@@ -1,6 +1,6 @@
-from repoze.who._compat import CONTENT_TYPE
-from repoze.who._compat import REQUEST_METHOD
-from repoze.who._compat import USER_AGENT
+from repoze.who._helpers import CONTENT_TYPE
+from repoze.who._helpers import REQUEST_METHOD
+from repoze.who._helpers import USER_AGENT
 
 from zope.interface import directlyProvides
 from repoze.who.interfaces import IRequestClassifier
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/config.py 
new/repoze.who-3.0.0/repoze/who/config.py
--- old/repoze.who-2.4.1/repoze/who/config.py   2016-05-28 02:45:11.000000000 
+0200
+++ new/repoze.who-3.0.0/repoze/who/config.py   2022-02-01 19:25:16.000000000 
+0100
@@ -1,5 +1,7 @@
 """ Configuration parser
 """
+import configparser
+from io import StringIO
 import logging
 from pkg_resources import EntryPoint
 import sys
@@ -14,9 +16,6 @@
 from repoze.who.interfaces import IPlugin
 from repoze.who.interfaces import IRequestClassifier
 from repoze.who.middleware import PluggableAuthenticationMiddleware
-from repoze.who._compat import StringIO
-from repoze.who._compat import ConfigParser
-from repoze.who._compat import ParsingError
 
 def _resolve(name):
     if name:
@@ -71,7 +70,7 @@
     def parse(self, text):
         if getattr(text, 'readline', None) is None:
             text = StringIO(text)
-        cp = ConfigParser(defaults={'here': self.here})
+        cp = configparser.ConfigParser(defaults={'here': self.here})
         try:
             cp.read_file(text)
         except AttributeError: #pragma NO COVER Python < 3.0
@@ -161,7 +160,7 @@
         try:
             try:
                 parser.parse(opened)
-            except ParsingError:
+            except configparser.ParsingError:
                 warnings.warn('Invalid who config file: %s' % config_file,
                             stacklevel=2)
             else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/middleware.py 
new/repoze.who-3.0.0/repoze/who/middleware.py
--- old/repoze.who-2.4.1/repoze/who/middleware.py       2016-05-31 
19:28:55.000000000 +0200
+++ new/repoze.who-3.0.0/repoze/who/middleware.py       2022-02-01 
19:25:16.000000000 +0100
@@ -1,9 +1,9 @@
+from io import StringIO
 import logging
 import sys
 
 from repoze.who.api import APIFactory
 from repoze.who.interfaces import IChallenger
-from repoze.who._compat import StringIO
 
 _STARTED = '-- repoze.who request started (%s) --'
 _ENDED = '-- repoze.who request ended (%s) --'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/plugins/auth_tkt.py 
new/repoze.who-3.0.0/repoze/who/plugins/auth_tkt.py
--- old/repoze.who-2.4.1/repoze/who/plugins/auth_tkt.py 2020-06-02 
22:54:09.000000000 +0200
+++ new/repoze.who-3.0.0/repoze/who/plugins/auth_tkt.py 2022-02-01 
19:25:16.000000000 +0100
@@ -4,21 +4,17 @@
 import hashlib
 import os
 import time
+from urllib.parse import parse_qsl
+from urllib.parse import urlencode
 from wsgiref.handlers import _monthname     # Locale-independent, RFC-2616
 from wsgiref.handlers import _weekdayname   # Locale-independent, RFC-2616
-try:
-    from urllib.parse import urlencode, parse_qsl
-except ImportError:
-    from urllib import urlencode
-    from urlparse import parse_qsl
 
 from zope.interface import implementer
 
 from repoze.who.interfaces import IIdentifier
 from repoze.who.interfaces import IAuthenticator
-from repoze.who._compat import get_cookies
+from repoze.who._helpers import get_cookies
 import repoze.who._auth_tkt as auth_tkt
-from repoze.who._compat import STRING_TYPES
 
 _UTCNOW = None  # unit tests can replace
 def _utcnow():  #pragma NO COVERAGE
@@ -220,7 +216,7 @@
                             id(self)) #pragma NO COVERAGE
 
 def _bool(value):
-    if isinstance(value, STRING_TYPES):
+    if isinstance(value, str):
         return value.lower() in ('yes', 'true', '1')
     return value
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/plugins/basicauth.py 
new/repoze.who-3.0.0/repoze/who/plugins/basicauth.py
--- old/repoze.who-2.4.1/repoze/who/plugins/basicauth.py        2015-03-18 
20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/basicauth.py        2022-02-01 
19:25:16.000000000 +0100
@@ -1,3 +1,4 @@
+import base64
 import binascii
 
 from webob.exc import HTTPUnauthorized
@@ -5,9 +6,8 @@
 
 from repoze.who.interfaces import IIdentifier
 from repoze.who.interfaces import IChallenger
-from repoze.who._compat import AUTHORIZATION
-from repoze.who._compat import decodebytes
-from repoze.who._compat import must_decode
+from repoze.who._helpers import AUTHORIZATION
+from repoze.who._helpers import must_decode
 
 @implementer(IIdentifier, IChallenger)
 class BasicAuthPlugin(object):
@@ -28,7 +28,7 @@
         if authmeth.lower() == b'basic':
             try:
                 auth = auth.strip()
-                auth = decodebytes(auth)
+                auth = base64.decodebytes(auth)
             except binascii.Error: # can't decode
                 return None
             try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/plugins/htpasswd.py 
new/repoze.who-3.0.0/repoze/who/plugins/htpasswd.py
--- old/repoze.who-2.4.1/repoze/who/plugins/htpasswd.py 2022-02-01 
17:23:22.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/htpasswd.py 2022-02-01 
19:25:16.000000000 +0100
@@ -4,7 +4,6 @@
 
 from repoze.who.interfaces import IAuthenticator
 from repoze.who.utils import resolveDotted
-from repoze.who._compat import izip_longest
 
 
 def _padding_for_file_lines():
@@ -86,7 +85,7 @@
 def _same_string(x, y):
     # Attempt at isochronous string comparison.
     mismatches = filter(None, [a != b for a, b, ignored
-                                    in izip_longest(x, y, PADDING)])
+                                    in itertools.zip_longest(x, y, PADDING)])
     if type(mismatches) != list: #pragma NO COVER Python >= 3.0
         mismatches = list(mismatches)
     return len(mismatches) == 0
@@ -99,7 +98,7 @@
 def sha1_check(password, hashed):
     from hashlib import sha1
     from base64 import standard_b64encode
-    from repoze.who._compat import must_encode
+    from repoze.who._helpers import must_encode
     b_password = must_encode(password)
     b_sha1_digest = sha1(b_password).digest()
     b_b64_sha1_digest = standard_b64encode(b_sha1_digest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/plugins/redirector.py 
new/repoze.who-3.0.0/repoze/who/plugins/redirector.py
--- old/repoze.who-2.4.1/repoze/who/plugins/redirector.py       2015-03-18 
20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/redirector.py       2022-02-01 
19:25:16.000000000 +0100
@@ -1,14 +1,14 @@
+from urllib.parse import parse_qs
+from urllib.parse import urlencode
+from urllib.parse import urlparse
+from urllib.parse import urlunparse
 from webob.exc import HTTPFound
+
 from zope.interface import implementer
 
 from repoze.who.interfaces import IChallenger
-from repoze.who._compat import construct_url
-from repoze.who._compat import header_value
-from repoze.who._compat import parse_qs
-from repoze.who._compat import u
-from repoze.who._compat import urlencode
-from repoze.who._compat import urlparse
-from repoze.who._compat import urlunparse
+from repoze.who._helpers import construct_url
+from repoze.who._helpers import header_value
 
 @implementer(IChallenger)
 class RedirectorPlugin(object):
@@ -62,7 +62,7 @@
                 reason_param=None,
                 reason_header=None,
                ):
-    if login_url in (u(''), b'', None):
+    if login_url in (u'', b'', None):
         raise ValueError("No 'login_url'")
     if reason_header is not None and reason_param is None:
         raise Exception("Can't set 'reason_header' without 'reason_param'.")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/fixtures/testapp.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/fixtures/testapp.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/fixtures/testapp.py   
2014-12-13 00:37:43.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/fixtures/testapp.py   
1970-01-01 01:00:00.000000000 +0100
@@ -1,53 +0,0 @@
-def tack_environ(environ, msg):
-    import pprint
-    penv = pprint.pformat(environ)
-    return msg + '\n\n' + penv
-
-def deny(start_response, environ, msg):
-    ct = 'text/plain'
-    msg = tack_environ(environ, msg)
-    cl = str(len(msg))
-    start_response('401 Unauthorized',
-                   [ ('Content-Type', ct),
-                   ('Content-Length', cl) ],
-                   )
-
-def allow(start_response, environ, msg):
-    ct = 'text/plain'
-    msg = tack_environ(environ, msg)
-    cl = str(len(msg))
-    start_response('200 OK',
-                   [ ('Content-Type', ct),
-                   ('Content-Length', cl) ],
-                   )
-    return [msg]
-
-def app(environ, start_response):
-    path_info = environ['PATH_INFO']
-    remote_user = environ.get('REMOTE_USER')
-    if path_info.endswith('/shared'):
-        if not remote_user:
-            return deny(start_response, environ, 'You cant do that')
-        else:
-            return allow(start_response, environ,
-                         'Welcome to the shared area, %s' % remote_user)
-    elif path_info.endswith('/admin'):
-        if remote_user != 'admin':
-            return deny(start_response, environ, 'Only admin can do that')
-        else:
-            return allow(start_response, environ, 'Hello, admin!')
-    elif path_info.endswith('/chris'):
-        if remote_user != 'chris':
-            return deny(start_response, environ, 'Only chris can do that')
-        else:
-            return allow(start_response, environ, 'Hello, chris!')
-    else:
-        return allow(start_response, environ, 'Unprotected page')
-    
-def make_app(global_config, **kw):
-    return app
-
-            
-        
-            
-            
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/test_authtkt.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/test_authtkt.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/test_authtkt.py       
2020-06-02 22:54:09.000000000 +0200
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/test_authtkt.py       
2022-02-01 19:25:16.000000000 +0100
@@ -501,31 +501,12 @@
                          ('Set-Cookie',
                           'auth_tkt="%s"; Path=/' % new_val))
 
-    def test_remember_creds_different_long_userid(self):
-        try:
-            long
-        except NameError: #pragma NO COVER Python >= 3.0
-            return
-        plugin = self._makeOne('secret')
-        old_val = self._makeTicket(userid='userid')
-        environ = self._makeEnviron({'HTTP_COOKIE':'auth_tkt=%s' % old_val})
-        new_val = self._makeTicket(userid='1', userdata='userid_type=int')
-        result = plugin.remember(environ, {'repoze.who.userid':long(1),
-                                           'userdata':{}})
-        self.assertEqual(len(result), 3)
-        self.assertEqual(result[0],
-                         ('Set-Cookie',
-                          'auth_tkt="%s"; Path=/' % new_val))
-
     def test_remember_creds_different_unicode_userid(self):
         plugin = self._makeOne('secret')
         old_val = self._makeTicket(userid='userid')
         environ = self._makeEnviron({'HTTP_COOKIE':'auth_tkt=%s' % old_val})
         userid = b'\xc2\xa9'.decode('utf-8')
-        if type(b'') == type(''):
-            userdata = 'userid_type=unicode'
-        else: # pragma: no cover Py3k
-            userdata = ''
+        userdata = ''
         new_val = self._makeTicket(userid=userid.encode('utf-8'),
                                    userdata=userdata)
         result = plugin.remember(environ, {'repoze.who.userid':userid,
@@ -739,12 +720,11 @@
                           secret="fiddly", digest_algo='foo23')
 
     def test_remember_max_age_unicode(self):
-        from repoze.who._compat import u
         plugin = self._makeOne('secret')
         environ = {'HTTP_HOST':'example.com'}
         tkt = self._makeTicket(userid='chris', userdata='')
         result = plugin.remember(environ, {'repoze.who.userid': 'chris',
-                                           'max_age': u('500')})
+                                           'max_age': u'500'})
         name, value = result.pop(0)
         self.assertEqual('Set-Cookie', name)
         self.assertTrue(isinstance(value, str))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/test_basicauth.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/test_basicauth.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/test_basicauth.py     
2015-03-18 20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/test_basicauth.py     
2022-02-01 19:25:16.000000000 +0100
@@ -58,7 +58,7 @@
         self.assertEqual(creds, None)
 
     def test_identify_basic_badrepr(self):
-        from repoze.who._compat import encodebytes
+        from base64 import encodebytes
         plugin = self._makeOne('realm')
         value = encodebytes(b'foo').decode('ascii')
         environ = self._makeEnviron({'HTTP_AUTHORIZATION':'Basic %s' % value})
@@ -66,7 +66,7 @@
         self.assertEqual(creds, None)
 
     def test_identify_basic_ok(self):
-        from repoze.who._compat import encodebytes
+        from base64 import encodebytes
         plugin = self._makeOne('realm')
         value = encodebytes(b'foo:bar').decode('ascii')
         environ = self._makeEnviron({'HTTP_AUTHORIZATION':'Basic %s' % value})
@@ -74,7 +74,7 @@
         self.assertEqual(creds, {'login':'foo', 'password':'bar'})
 
     def test_identify_basic_ok_utf8_values(self):
-        from repoze.who._compat import encodebytes
+        from base64 import encodebytes
         LOGIN = b'b\xc3\xa2tard'
         PASSWD = b'l\xc3\xa0 demain'
         plugin = self._makeOne('realm')
@@ -85,7 +85,7 @@
                                  'password': PASSWD.decode('utf-8')})
 
     def test_identify_basic_ok_latin1_values(self):
-        from repoze.who._compat import encodebytes
+        from base64 import encodebytes
         LOGIN = b'b\xe2tard'
         PASSWD = b'l\xe0 demain'
         plugin = self._makeOne('realm')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/test_htpasswd.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/test_htpasswd.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/test_htpasswd.py      
2022-02-01 17:23:22.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/test_htpasswd.py      
2022-02-01 19:25:16.000000000 +0100
@@ -23,7 +23,7 @@
         verifyClass(IAuthenticator, klass)
 
     def test_authenticate_nocreds(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         io = StringIO()
         plugin = self._makeOne(io, None)
         environ = self._makeEnviron()
@@ -32,7 +32,7 @@
         self.assertEqual(result, None)
 
     def test_authenticate_nolines(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         io = StringIO()
         def check(password, hashed):
             return True
@@ -43,7 +43,7 @@
         self.assertEqual(result, None)
 
     def test_authenticate_nousermatch(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         io = StringIO('nobody:foo')
         def check(password, hashed):
             return True
@@ -54,7 +54,7 @@
         self.assertEqual(result, None)
 
     def test_authenticate_match(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         io = StringIO('chrism:pass')
         def check(password, hashed):
             return True
@@ -65,7 +65,7 @@
         self.assertEqual(result, 'chrism')
 
     def test_authenticate_badline(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         io = StringIO('badline\nchrism:pass')
         def check(password, hashed):
             return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/test_redirector.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/test_redirector.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/test_redirector.py    
2015-03-18 20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/test_redirector.py    
2022-02-01 19:25:16.000000000 +0100
@@ -19,7 +19,7 @@
                                       reason_header=reason_header)
 
     def _makeEnviron(self, path_info='/', identifier=None):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         if identifier is None:
             credentials = {'login':'chris', 'password':'password'}
             identifier = DummyIdentifier(credentials)
@@ -59,8 +59,8 @@
                                         reason_header='X-Reason')
 
     def test_challenge(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from',
                                reason_param='reason',
                                reason_header='X-Authorization-Failure-Reason',
@@ -93,8 +93,8 @@
         self.assertEqual(sr.status, '302 Found')
 
     def test_challenge_with_reason_header(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from',
                                reason_param='reason',
                                reason_header='X-Authorization-Failure-Reason',
@@ -125,8 +125,8 @@
         self.assertEqual(reason_value, 'you are ugly')
 
     def test_challenge_with_custom_reason_header(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from',
                                reason_param='reason',
                                reason_header='X-Custom-Auth-Failure',
@@ -154,8 +154,8 @@
         self.assertEqual(came_from_value, 'http://www.example.com/?default=1')
 
     def test_challenge_w_reason_no_reason_param_no_came_from_param(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne()
         environ = self._makeEnviron()
         app = plugin.challenge(
@@ -178,8 +178,8 @@
         self.assertEqual(parts[3], '')
 
     def test_challenge_w_reason_no_reason_param_w_came_from_param(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from',
                               )
         environ = self._makeEnviron()
@@ -205,8 +205,8 @@
         self.assertEqual(came_from_value, 'http://www.example.com/?default=1')
 
     def test_challenge_with_reason_and_custom_reason_param(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from',
                                reason_param='auth_failure',
                                reason_header='X-Custom-Auth-Failure',
@@ -238,8 +238,8 @@
         self.assertEqual(reason_value, 'you are ugly')
 
     def test_challenge_wo_reason_w_came_from_param(self):
-        from ..._compat import parse_qsl
-        from ..._compat import urlparse
+        from urllib.parse import parse_qsl
+        from urllib.parse import urlparse
         plugin = self._makeOne(came_from_param='came_from')
         environ = self._makeEnviron()
         app = plugin.challenge(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/repoze.who-2.4.1/repoze/who/plugins/tests/test_sql.py 
new/repoze.who-3.0.0/repoze/who/plugins/tests/test_sql.py
--- old/repoze.who-2.4.1/repoze/who/plugins/tests/test_sql.py   2015-03-18 
20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/plugins/tests/test_sql.py   2022-02-01 
19:25:16.000000000 +0100
@@ -88,10 +88,9 @@
         self.assertEqual(result, True)
 
     def test_shaprefix_w_unicode_cleartext(self):
-        from repoze.who._compat import u
         stored = '{SHA}' +  self._get_sha_hex_digest()
         compare = self._getFUT()
-        result = compare(u('password'), stored)
+        result = compare(u'password', stored)
         self.assertEqual(result, True)
 
     def test_shaprefix_fail(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/restrict.py 
new/repoze.who-3.0.0/repoze/who/restrict.py
--- old/repoze.who-2.4.1/repoze/who/restrict.py 2016-05-28 02:45:45.000000000 
+0200
+++ new/repoze.who-3.0.0/repoze/who/restrict.py 2022-02-01 19:25:16.000000000 
+0100
@@ -1,8 +1,6 @@
 # Authorization middleware
 from pkg_resources import EntryPoint
 
-from repoze.who._compat import STRING_TYPES
-
 def authenticated_predicate():
     def _predicate(environ):
         return 'REMOTE_USER' in environ or 'repoze.who.identity' in environ
@@ -28,6 +26,6 @@
 
 def make_predicate_restriction(app, global_config,
                                predicate, enabled=True, **kw):
-    if isinstance(predicate, STRING_TYPES):
+    if isinstance(predicate, str):
         predicate = EntryPoint.parse('x=%s' % predicate).resolve()
     return PredicateRestriction(app, predicate, enabled, **kw)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/tests/test__auth_tkt.py 
new/repoze.who-3.0.0/repoze/who/tests/test__auth_tkt.py
--- old/repoze.who-2.4.1/repoze/who/tests/test__auth_tkt.py     2022-02-01 
17:55:33.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/tests/test__auth_tkt.py     2022-02-01 
19:25:16.000000000 +0100
@@ -4,7 +4,7 @@
 class AuthTicketTests(unittest.TestCase):
 
     def _getTargetClass(self):
-        from .._auth_tkt import AuthTicket
+        from repoze.who._auth_tkt import AuthTicket
         return AuthTicket
 
     def _makeOne(self, *args, **kw):
@@ -12,7 +12,7 @@
 
     def test_ctor_defaults(self):
         import hashlib
-        from .. import _auth_tkt
+        from repoze.who import _auth_tkt
         with _Monkey(_auth_tkt, time_mod=_Timemod):
             tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4')
         self.assertEqual(tkt.secret, 'SEEKRIT')
@@ -94,7 +94,7 @@
         self.assertEqual(tkt.digest_algo, hashlib.sha1)
 
     def test_digest(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4', tokens=('a', 'b'),
                             user_data='DATA', time=_WHEN,
                             cookie_name='oatmeal', secure=True)
@@ -103,7 +103,7 @@
         self.assertEqual(tkt.digest(), digest)
 
     def test_cookie_value_wo_tokens_or_userdata(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4', time=_WHEN)
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
                                   '', '', hashlib.md5)
@@ -111,7 +111,7 @@
                          '%s%08xUSERID!' % (digest, _WHEN))
 
     def test_cookie_value_w_tokens_and_userdata(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4', tokens=('a', 'b'),
                             user_data='DATA', time=_WHEN)
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
@@ -120,8 +120,8 @@
                          '%s%08xUSERID!a,b!DATA' % (digest, _WHEN))
 
     def test_cookie_not_secure_wo_tokens_or_userdata(self):
-        from .._auth_tkt import calculate_digest, hashlib
-        from .._compat import encodestring
+        from repoze.who._auth_tkt import calculate_digest, hashlib
+        from repoze.who._helpers import encodestring
         tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4', time=_WHEN,
                             cookie_name='oatmeal')
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
@@ -134,8 +134,8 @@
         self.assertEqual(cookie['oatmeal']['secure'], '')
 
     def test_cookie_secure_w_tokens_and_userdata(self):
-        from .._auth_tkt import calculate_digest, hashlib
-        from .._compat import encodestring
+        from repoze.who._auth_tkt import calculate_digest, hashlib
+        from repoze.who._helpers import encodestring
         tkt = self._makeOne('SEEKRIT', 'USERID', '1.2.3.4', tokens=('a', 'b'),
                             user_data='DATA', time=_WHEN,
                             cookie_name='oatmeal', secure=True)
@@ -152,7 +152,7 @@
 class BadTicketTests(unittest.TestCase):
 
     def _getTargetClass(self):
-        from .._auth_tkt import BadTicket
+        from repoze.who._auth_tkt import BadTicket
         return BadTicket
 
     def _makeOne(self, *args, **kw):
@@ -173,11 +173,11 @@
 
     def _callFUT(self, secret='SEEKRIT', ticket=None,
                  ip='1.2.3.4', digest="md5"):
-        from .._auth_tkt import parse_ticket
+        from repoze.who._auth_tkt import parse_ticket
         return parse_ticket(secret, ticket, ip, digest)
 
     def test_bad_timestamp(self):
-        from .._auth_tkt import BadTicket
+        from repoze.who._auth_tkt import BadTicket
         TICKET = '12345678901234567890123456789012XXXXXXXXuserid!'
         try:
             self._callFUT(ticket=TICKET)
@@ -188,7 +188,7 @@
             self.fail('Did not raise')
 
     def test_no_bang_after_userid(self):
-        from .._auth_tkt import BadTicket
+        from repoze.who._auth_tkt import BadTicket
         TICKET = '1234567890123456789012345678901201020304userid'
         try:
             self._callFUT(ticket=TICKET)
@@ -198,7 +198,7 @@
             self.fail('Did not raise')
 
     def test_wo_tokens_or_data_bad_digest(self):
-        from .._auth_tkt import BadTicket
+        from repoze.who._auth_tkt import BadTicket
         TICKET = '1234567890123456789012345678901201020304userid!'
         try:
             self._callFUT(ticket=TICKET)
@@ -208,7 +208,7 @@
             self.fail('Did not raise')
 
     def test_wo_tokens_or_data_ok_digest(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
                                   '', '', hashlib.md5)
         TICKET = '%s%08xUSERID!' % (digest, _WHEN)
@@ -219,7 +219,7 @@
         self.assertEqual(user_data, '')
 
     def test_w_tokens_and_data_ok_digest(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
                                   'a,b', 'DATA', hashlib.md5)
         TICKET = '%s%08xUSERID!a,b!DATA' % (digest, _WHEN)
@@ -230,7 +230,7 @@
         self.assertEqual(user_data, 'DATA')
 
     def test_w_tokens_and_data_ok_alternate_digest(self):
-        from .._auth_tkt import calculate_digest, hashlib
+        from repoze.who._auth_tkt import calculate_digest, hashlib
         digest = calculate_digest('1.2.3.4', _WHEN, 'SEEKRIT', 'USERID',
                                   'a,b', 'DATA', hashlib.sha256)
         TICKET = '%s%08xUSERID!a,b!DATA' % (digest, _WHEN)
@@ -248,29 +248,28 @@
 
     def test_ints_to_bytes(self):
         from struct import pack
-        from .._auth_tkt import ints2bytes
+        from repoze.who._auth_tkt import ints2bytes
         self.assertEqual(ints2bytes([1, 2, 3, 4]), pack('>BBBB', 1, 2, 3, 4))
         
     def test_encode_ip_timestamp(self):
         from struct import pack
-        from .._auth_tkt import encode_ip_timestamp
+        from repoze.who._auth_tkt import encode_ip_timestamp
         self.assertEqual(encode_ip_timestamp('1.2.3.4', _WHEN),
                          pack('>BBBBL', 1, 2, 3, 4, _WHEN))
 
     def test_maybe_encode_bytes(self):
-        from .._auth_tkt import maybe_encode
+        from repoze.who._auth_tkt import maybe_encode
         foo = b'foo'
         self.assertTrue(maybe_encode(foo) is foo)
 
     def test_maybe_encode_native_string(self):
-        from .._auth_tkt import maybe_encode
+        from repoze.who._auth_tkt import maybe_encode
         foo = 'foo'
         self.assertEqual(maybe_encode(foo), b'foo')
 
     def test_maybe_encode_unicode(self):
-        from .._auth_tkt import maybe_encode
-        from .._compat import u
-        foo = u('foo')
+        from repoze.who._auth_tkt import maybe_encode
+        foo = u'foo'
         self.assertEqual(maybe_encode(foo), b'foo')
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/tests/test__compat.py 
new/repoze.who-3.0.0/repoze/who/tests/test__compat.py
--- old/repoze.who-2.4.1/repoze/who/tests/test__compat.py       2015-03-18 
20:33:59.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/tests/test__compat.py       1970-01-01 
01:00:00.000000000 +0100
@@ -1,131 +0,0 @@
-import unittest
-
-
-class CompatTests(unittest.TestCase):
-
-    def test_REQUEST_METHOD_miss(self):
-        # PEP 3333 says CONTENT_TYPE is mandatory
-        from .._compat import REQUEST_METHOD
-        self.assertRaises(KeyError, REQUEST_METHOD, {})
-
-    def test_REQUEST_METHOD_hit(self):
-        from .._compat import REQUEST_METHOD
-        self.assertEqual(REQUEST_METHOD({'REQUEST_METHOD': 'FOO'}), 'FOO')
-
-    def test_CONTENT_TYPE_miss(self):
-        # PEP 3333 says CONTENT_TYPE is optional
-        from .._compat import CONTENT_TYPE
-        self.assertEqual(CONTENT_TYPE({}), '')
-
-    def test_CONTENT_TYPE_hit(self):
-        from .._compat import CONTENT_TYPE
-        self.assertEqual(CONTENT_TYPE({'CONTENT_TYPE': 'text/html'}),
-                         'text/html')
-
-    def test_USER_AGENT_miss(self):
-        from .._compat import USER_AGENT
-        self.assertEqual(USER_AGENT({}), None)
-
-    def test_USER_AGENT_hit(self):
-        from .._compat import USER_AGENT
-        self.assertEqual(USER_AGENT({'HTTP_USER_AGENT': 'FOO'}), 'FOO')
-
-    def test_AUTHORIZATION_miss(self):
-        from .._compat import AUTHORIZATION
-        self.assertEqual(AUTHORIZATION({}), '')
-
-    def test_AUTHORIZATION_hit(self):
-        from .._compat import AUTHORIZATION
-        self.assertEqual(AUTHORIZATION({'HTTP_AUTHORIZATION': 'FOO'}), 'FOO')
-
-    def test_get_cookies_no_cache_ok_header_value(self):
-        from .._compat import get_cookies
-        from .._compat import SimpleCookie
-        environ = {'HTTP_COOKIE': 'qux=spam'}
-        cookies = get_cookies(environ)
-        self.assertTrue(isinstance(cookies, SimpleCookie))
-        self.assertEqual(len(cookies), 1)
-        self.assertEqual(cookies['qux'].value, 'spam')
-        self.assertEqual(environ['paste.cookies'], (cookies, 'qux=spam'))
-
-    def test_get_cookies_w_cache_miss(self):
-        from .._compat import get_cookies
-        from .._compat import SimpleCookie
-        environ = {'HTTP_COOKIE': 'qux=spam',
-                   'paste.cookies': (object(), 'foo=bar'),
-                  }
-        cookies = get_cookies(environ)
-        self.assertTrue(isinstance(cookies, SimpleCookie))
-        self.assertEqual(len(cookies), 1)
-        self.assertEqual(cookies['qux'].value, 'spam')
-        self.assertEqual(environ['paste.cookies'], (cookies, 'qux=spam'))
-
-    def test_get_cookies_w_cache_hit(self):
-        from .._compat import get_cookies
-        from .._compat import SimpleCookie
-        existing = SimpleCookie()
-        existing['foo'] = 'bar'
-        environ = {'HTTP_COOKIE': 'qux=spam',
-                   'paste.cookies': (existing, 'qux=spam'),
-                  }
-        cookies = get_cookies(environ)
-        self.assertTrue(cookies is existing)
-
-    def test_construct_url(self):
-        from .._compat import construct_url
-        environ = {'wsgi.url_scheme': 'http',
-                   'HTTP_HOST': 'example.com',
-                  }
-        self.assertEqual(construct_url(environ), 'http://example.com/')
-
-    def test_header_value_miss(self):
-        from .._compat import header_value
-        self.assertEqual(header_value([], 'nonesuch'), '')
-
-    def test_header_value_simple(self):
-        from .._compat import header_value
-        self.assertEqual(header_value([('simple', 'SIMPLE')], 'simple'),
-                         'SIMPLE')
-
-    def test_must_decode_non_string(self):
-        from .._compat import must_decode
-        foo = object()
-        self.assertTrue(must_decode(foo) is foo)
-
-    def test_must_decode_unicode(self):
-        from .._compat import must_decode
-        from .._compat import u
-        foo = u('foo')
-        self.assertTrue(must_decode(foo) is foo)
-
-    def test_must_decode_utf8(self):
-        from .._compat import must_decode
-        foo = b'b\xc3\xa2tard'
-        self.assertEqual(must_decode(foo), foo.decode('utf-8'))
-
-    def test_must_decode_latin1(self):
-        from .._compat import must_decode
-        foo = b'b\xe2tard'
-        self.assertEqual(must_decode(foo), foo.decode('latin1'))
-
-    def test_must_encode_non_string(self):
-        from .._compat import must_encode
-        foo = object()
-        self.assertTrue(must_encode(foo) is foo)
-
-    def test_must_encode_unicode(self):
-        from .._compat import must_encode
-        from .._compat import u
-        foo = u('foo')
-        self.assertEqual(must_encode(foo), foo.encode('utf-8'))
-
-    def test_must_encode_utf8(self):
-        from .._compat import must_encode
-        foo = b'b\xc3\xa2tard'
-        self.assertTrue(must_encode(foo) is foo)
-
-    def test_must_encode_latin1(self):
-        from .._compat import must_encode
-        foo = b'b\xe2tard'
-        self.assertTrue(must_encode(foo) is foo)
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/tests/test__helpers.py 
new/repoze.who-3.0.0/repoze/who/tests/test__helpers.py
--- old/repoze.who-2.4.1/repoze/who/tests/test__helpers.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/tests/test__helpers.py      2022-02-01 
19:25:16.000000000 +0100
@@ -0,0 +1,129 @@
+import unittest
+
+
+class CompatTests(unittest.TestCase):
+
+    def test_REQUEST_METHOD_miss(self):
+        # PEP 3333 says CONTENT_TYPE is mandatory
+        from repoze.who._helpers import REQUEST_METHOD
+        self.assertRaises(KeyError, REQUEST_METHOD, {})
+
+    def test_REQUEST_METHOD_hit(self):
+        from repoze.who._helpers import REQUEST_METHOD
+        self.assertEqual(REQUEST_METHOD({'REQUEST_METHOD': 'FOO'}), 'FOO')
+
+    def test_CONTENT_TYPE_miss(self):
+        # PEP 3333 says CONTENT_TYPE is optional
+        from repoze.who._helpers import CONTENT_TYPE
+        self.assertEqual(CONTENT_TYPE({}), '')
+
+    def test_CONTENT_TYPE_hit(self):
+        from repoze.who._helpers import CONTENT_TYPE
+        self.assertEqual(CONTENT_TYPE({'CONTENT_TYPE': 'text/html'}),
+                         'text/html')
+
+    def test_USER_AGENT_miss(self):
+        from repoze.who._helpers import USER_AGENT
+        self.assertEqual(USER_AGENT({}), None)
+
+    def test_USER_AGENT_hit(self):
+        from repoze.who._helpers import USER_AGENT
+        self.assertEqual(USER_AGENT({'HTTP_USER_AGENT': 'FOO'}), 'FOO')
+
+    def test_AUTHORIZATION_miss(self):
+        from repoze.who._helpers import AUTHORIZATION
+        self.assertEqual(AUTHORIZATION({}), '')
+
+    def test_AUTHORIZATION_hit(self):
+        from repoze.who._helpers import AUTHORIZATION
+        self.assertEqual(AUTHORIZATION({'HTTP_AUTHORIZATION': 'FOO'}), 'FOO')
+
+    def test_get_cookies_no_cache_ok_header_value(self):
+        from http.cookies import SimpleCookie
+        from repoze.who._helpers import get_cookies
+        environ = {'HTTP_COOKIE': 'qux=spam'}
+        cookies = get_cookies(environ)
+        self.assertTrue(isinstance(cookies, SimpleCookie))
+        self.assertEqual(len(cookies), 1)
+        self.assertEqual(cookies['qux'].value, 'spam')
+        self.assertEqual(environ['paste.cookies'], (cookies, 'qux=spam'))
+
+    def test_get_cookies_w_cache_miss(self):
+        from http.cookies import SimpleCookie
+        from repoze.who._helpers import get_cookies
+        environ = {'HTTP_COOKIE': 'qux=spam',
+                   'paste.cookies': (object(), 'foo=bar'),
+                  }
+        cookies = get_cookies(environ)
+        self.assertTrue(isinstance(cookies, SimpleCookie))
+        self.assertEqual(len(cookies), 1)
+        self.assertEqual(cookies['qux'].value, 'spam')
+        self.assertEqual(environ['paste.cookies'], (cookies, 'qux=spam'))
+
+    def test_get_cookies_w_cache_hit(self):
+        from http.cookies import SimpleCookie
+        from repoze.who._helpers import get_cookies
+        existing = SimpleCookie()
+        existing['foo'] = 'bar'
+        environ = {'HTTP_COOKIE': 'qux=spam',
+                   'paste.cookies': (existing, 'qux=spam'),
+                  }
+        cookies = get_cookies(environ)
+        self.assertTrue(cookies is existing)
+
+    def test_construct_url(self):
+        from repoze.who._helpers import construct_url
+        environ = {'wsgi.url_scheme': 'http',
+                   'HTTP_HOST': 'example.com',
+                  }
+        self.assertEqual(construct_url(environ), 'http://example.com/')
+
+    def test_header_value_miss(self):
+        from repoze.who._helpers import header_value
+        self.assertEqual(header_value([], 'nonesuch'), '')
+
+    def test_header_value_simple(self):
+        from repoze.who._helpers import header_value
+        self.assertEqual(header_value([('simple', 'SIMPLE')], 'simple'),
+                         'SIMPLE')
+
+    def test_must_decode_non_string(self):
+        from repoze.who._helpers import must_decode
+        foo = object()
+        self.assertTrue(must_decode(foo) is foo)
+
+    def test_must_decode_unicode(self):
+        from repoze.who._helpers import must_decode
+        foo = u'foo'
+        self.assertTrue(must_decode(foo) is foo)
+
+    def test_must_decode_utf8(self):
+        from repoze.who._helpers import must_decode
+        foo = b'b\xc3\xa2tard'
+        self.assertEqual(must_decode(foo), foo.decode('utf-8'))
+
+    def test_must_decode_latin1(self):
+        from repoze.who._helpers import must_decode
+        foo = b'b\xe2tard'
+        self.assertEqual(must_decode(foo), foo.decode('latin1'))
+
+    def test_must_encode_non_string(self):
+        from repoze.who._helpers import must_encode
+        foo = object()
+        self.assertTrue(must_encode(foo) is foo)
+
+    def test_must_encode_unicode(self):
+        from repoze.who._helpers import must_encode
+        foo = u'foo'
+        self.assertEqual(must_encode(foo), foo.encode('utf-8'))
+
+    def test_must_encode_utf8(self):
+        from repoze.who._helpers import must_encode
+        foo = b'b\xc3\xa2tard'
+        self.assertTrue(must_encode(foo) is foo)
+
+    def test_must_encode_latin1(self):
+        from repoze.who._helpers import must_encode
+        foo = b'b\xe2tard'
+        self.assertTrue(must_encode(foo) is foo)
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/tests/test_config.py 
new/repoze.who-3.0.0/repoze/who/tests/test_config.py
--- old/repoze.who-2.4.1/repoze/who/tests/test_config.py        2015-03-18 
20:34:00.000000000 +0100
+++ new/repoze.who-3.0.0/repoze/who/tests/test_config.py        2022-02-01 
19:25:16.000000000 +0100
@@ -40,7 +40,7 @@
         self.assertEqual(len(config.mdproviders), 0)
 
     def test_parse_empty_file(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         config = self._makeOne()
         config.parse(StringIO())
         self.assertEqual(config.request_classifier, None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze/who/tests/test_middleware.py 
new/repoze.who-3.0.0/repoze/who/tests/test_middleware.py
--- old/repoze.who-2.4.1/repoze/who/tests/test_middleware.py    2016-05-31 
19:33:27.000000000 +0200
+++ new/repoze.who-3.0.0/repoze/who/tests/test_middleware.py    2022-02-01 
19:25:16.000000000 +0100
@@ -511,7 +511,7 @@
         self.assertTrue(wrapper.buffer)
 
     def test_finish_response(self):
-        from repoze.who._compat import StringIO
+        from io import StringIO
         statuses = []
         headerses = []
         datases = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze.who.egg-info/PKG-INFO 
new/repoze.who-3.0.0/repoze.who.egg-info/PKG-INFO
--- old/repoze.who-2.4.1/repoze.who.egg-info/PKG-INFO   2022-02-01 
17:57:35.000000000 +0100
+++ new/repoze.who-3.0.0/repoze.who.egg-info/PKG-INFO   2023-01-25 
15:57:21.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: repoze.who
-Version: 2.4.1
+Version: 3.0.0
 Summary: repoze.who is an identification and authentication framework for WSGI.
 Home-page: http://www.repoze.org
 Author: Agendaless Consulting
@@ -75,6 +75,20 @@
         repoze.who Changelog
         ====================
         
+        3.0.0 (2023-01-16)
+        ------------------
+        
+        - No changes from 3.0.0b1.
+        
+        3.0.0b1 (2023-01-16)
+        --------------------
+        
+        - Add support for Python 3.9, 3.10 and 3.11.
+        
+        - Drop support for Python 2.7, 3.4, 3.5, and 3.6.
+        
+        - Add Github Actions workflow to exercise unit tests / coverage.
+        
         2.4.1 (2022-02-01)
         ------------------
         
@@ -88,7 +102,7 @@
         2.4 (2020-06-03)
         ----------------
         
-        - Add upport for Python 3.6, 3.7, and 3.8.
+        - Add support for Python 3.6, 3.7, and 3.8.
         
         - Drop support for Python 3.3.
         
@@ -796,14 +810,12 @@
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Internet :: WWW/HTTP
@@ -811,4 +823,3 @@
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
 Provides-Extra: docs
-Provides-Extra: testing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze.who.egg-info/SOURCES.txt 
new/repoze.who-3.0.0/repoze.who.egg-info/SOURCES.txt
--- old/repoze.who-2.4.1/repoze.who.egg-info/SOURCES.txt        2022-02-01 
17:57:35.000000000 +0100
+++ new/repoze.who-3.0.0/repoze.who.egg-info/SOURCES.txt        2023-01-25 
15:57:21.000000000 +0100
@@ -1,6 +1,5 @@
 .bzrignore
 .gitignore
-.travis.yml
 CHANGES.rst
 CONTRIBUTORS.txt
 COPYRIGHT.txt
@@ -40,7 +39,7 @@
 repoze.who.egg-info/top_level.txt
 repoze/who/__init__.py
 repoze/who/_auth_tkt.py
-repoze/who/_compat.py
+repoze/who/_helpers.py
 repoze/who/api.py
 repoze/who/classifiers.py
 repoze/who/config.py
@@ -62,10 +61,9 @@
 repoze/who/plugins/tests/test_sql.py
 repoze/who/plugins/tests/fixtures/__init__.py
 repoze/who/plugins/tests/fixtures/test.htpasswd
-repoze/who/plugins/tests/fixtures/testapp.py
 repoze/who/tests/__init__.py
 repoze/who/tests/test__auth_tkt.py
-repoze/who/tests/test__compat.py
+repoze/who/tests/test__helpers.py
 repoze/who/tests/test_api.py
 repoze/who/tests/test_classifiers.py
 repoze/who/tests/test_config.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/repoze.who.egg-info/requires.txt 
new/repoze.who-3.0.0/repoze.who.egg-info/requires.txt
--- old/repoze.who-2.4.1/repoze.who.egg-info/requires.txt       2022-02-01 
17:57:35.000000000 +0100
+++ new/repoze.who-3.0.0/repoze.who.egg-info/requires.txt       2023-01-25 
15:57:21.000000000 +0100
@@ -7,9 +7,3 @@
 WebOb
 repoze.sphinx.autointerface
 zope.interface
-
-[testing]
-WebOb
-coverage
-nose
-zope.interface
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/setup.cfg 
new/repoze.who-3.0.0/setup.cfg
--- old/repoze.who-2.4.1/setup.cfg      2022-02-01 17:57:35.634601400 +0100
+++ new/repoze.who-3.0.0/setup.cfg      2023-01-25 15:57:22.017574000 +0100
@@ -1,14 +1,8 @@
 [easy_install]
 zip_ok = false
 
-[nosetests]
-nocapture = 1
-cover-package = repoze.who
-cover-erase = 1
-cover-min-percentage = 100
-
 [aliases]
-dev = develop easy_install repoze.who[testing]
+dev = develop
 
 [egg_info]
 tag_build = 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/setup.py 
new/repoze.who-3.0.0/setup.py
--- old/repoze.who-2.4.1/setup.py       2022-02-01 17:56:22.000000000 +0100
+++ new/repoze.who-3.0.0/setup.py       2023-01-25 15:56:36.000000000 +0100
@@ -27,25 +27,22 @@
 README = _read_file('README.rst')
 CHANGES = _read_file('CHANGES.rst')
 tests_require = ['WebOb', 'zope.interface']
-testing_extras = tests_require + ['nose', 'coverage']
 docs_extras = tests_require + ['Sphinx', 'repoze.sphinx.autointerface']
 
 setup(name='repoze.who',
-      version='2.4.1',
+      version='3.0.0',
       description=('repoze.who is an identification and authentication '
                    'framework for WSGI.'),
       long_description='\n\n'.join([README, CHANGES]),
       classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Developers",
-        "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.4",
-        "Programming Language :: Python :: 3.5",
-        "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Topic :: Internet :: WWW/HTTP",
@@ -73,7 +70,6 @@
       authenticated = repoze.who.restrict:make_authenticated_restriction
       """,
       extras_require = {
-        'testing': testing_extras,
         'docs': docs_extras,
       },
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/repoze.who-2.4.1/tox.ini new/repoze.who-3.0.0/tox.ini
--- old/repoze.who-2.4.1/tox.ini        2022-01-31 18:41:51.000000000 +0100
+++ new/repoze.who-3.0.0/tox.ini        2023-01-17 01:05:52.000000000 +0100
@@ -1,6 +1,6 @@
 [tox]
 envlist = 
-    py27,pypy,py34,py35,py36,py37,py38,pypy3,cover,docs
+    py37,py38,py39,py310,py311,pypy3,cover,docs
 
 [testenv]
 commands = 
@@ -18,10 +18,10 @@
 [testenv:cover]
 skip_install = true
 basepython =
-    python3.8
+    python3.10
 commands = 
     coverage combine
-    coverage report --fail-under=100 --show-missing --omit="*fixture*"
+    coverage report --fail-under=100 --show-missing
     coverage xml
 deps =
     coverage
@@ -30,7 +30,7 @@
 
 [testenv:docs]
 basepython =
-    python3.8
+    python3.10
 commands = 
     sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
     sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest

Reply via email to