Repository: privacyidea/privacyidea

Issue:
Remove support for Python 3.6 
Python 3.6 (and Python 3.7) are end-of-life: https://devguide.python.org/versions/#versions
We should remove support for at least Python 3.6.
Python 3.6 is used as default in Ubuntu 18.04 (also end-of-life), CentOS 7 and RHEL8 (which provides several more Python versions).



Discussion:

Resolving patch:
diff --git a/privacyidea/lib/utils/__init__.py b/privacyidea/lib/utils/__init__.py
--- a/privacyidea/lib/utils/__init__.py
+++ b/privacyidea/lib/utils/__init__.py
@@ -43,7 +43,10 @@
 import hashlib
 import traceback
 import threading
-import importlib_metadata
+try:
+    from importlib import metadata
+except ImportError:
+    import importlib_metadata as metadata
 import time
 import html
 import segno
@@ -915,10 +918,10 @@ def parse_legacy_time(ts, return_date=False):
     The new timestrings are of the format YYYY-MM-DDThh:mm+oooo.
     They contain the timezone offset!
 
-    Old legacy time strings are of format DD/MM/YY hh:mm without time zone 
+    Old legacy time strings are of format DD/MM/YY hh:mm without time zone
     offset.
 
-    This function parses string and returns the new formatted time string 
+    This function parses string and returns the new formatted time string
     including the timezone offset.
 
     :param ts:
@@ -1248,9 +1251,9 @@ def get_version_number():
     """
     version = "unknown"
     try:
-        version = importlib_metadata.version("privacyidea")
-    except:
-        log.info("We are not able to determine the privacyidea version number.")
+        version = metadata.version("privacyidea")
+    except metadata.PackageNotFoundError as e:
+        log.info(f"We are not able to determine the privacyidea version number: {e}")
     return version
 
 
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
 import sys
 
 #VERSION = "2.1dev4"
-VERSION = "3.9"
+VERSION = "3.10dev1"
 
 # Taken from kennethreitz/requests/setup.py
 package_directory = os.path.realpath(os.path.dirname(__file__))
@@ -29,38 +29,42 @@ def get_file_list(file_path):
     return [file_path + f for f in file_list]
 
 
-install_requires = ["beautifulsoup4[lxml]>=4.3.2",
-                    "cbor2>=5.0.1",
-                    "configobj>=5.0.6",
-                    "croniter>=0.3.8",
-                    "cryptography>=2.4.2",
-                    "defusedxml>=0.4.1",
-                    "Flask>=0.10.1,<2.0",
-                    "Flask-Babel>=0.9",
-                    "Flask-Migrate>=1.2.0,<3.0",
-                    "Flask-Script>=2.0.5",
-                    "Flask-SQLAlchemy>=2.0",
-                    "Flask-Versioned>=0.9.4",
-                    "google-auth>=1.23.0",
-                    "huey[redis]>=1.11.0",
-                    "importlib_metadata>=2.1.1",
-                    "ldap3>=2.6",
-                    "netaddr>=0.7.12",
-                    "passlib[bcrypt]>=1.7.0",
-                    "argon2_cffi>=20.1.0",
-                    "pydash>=4.7.4",
-                    "PyJWT>=1.3.0",
-                    "PyMySQL>=0.6.6",
-                    "pyOpenSSL>=17.5",
-                    "pyrad>=2.0",
-                    "python-dateutil>=2.7.3",
-                    "python-gnupg>=0.4.4",
-                    "PyYAML>=5.1",
-                    "requests>=2.7.0",
-                    "segno>=1.5",
-                    "smpplib>=2.0",
-                    "SQLAlchemy>=1.4.0,<2.0",
-                    "MarkupSafe<2.1"]
+install_requires = [
+    "argon2_cffi>=20.1.0",
+    "beautifulsoup4[lxml]>=4.3.2",
+    "cbor2>=5.0.1",
+    "configobj>=5.0.6",
+    "croniter>=0.3.8",
+    "cryptography>=2.4.2",
+    "defusedxml>=0.4.1",
+    "Flask>=0.10.1,<2.0",
+    "Flask-Babel>=0.9,<3.0",
+    "Flask-Migrate>=1.2.0,<3.0",
+    "Flask-Script>=2.0.5",
+    "Flask-SQLAlchemy>=2.0,<3.0",
+    "Flask-Versioned>=0.9.4",
+    "google-auth>=1.23.0",
+    "grpcio>=1.46",
+    "huey[redis]>=1.11.0",
+    "importlib_metadata>=2.1.1; python_version=='3.7'",
+    "ldap3>=2.8,<2.9",
+    "MarkupSafe<2.1",
+    "netaddr>=0.7.12",
+    "passlib[bcrypt]>=1.7.0",
+    "protobuf>=3.20",
+    "pydash>=4.7.4",
+    "PyJWT>=1.3.0",
+    "PyMySQL>=0.6.6",
+    "pyOpenSSL>=17.5",
+    "pyrad>=2.0",
+    "python-dateutil>=2.7.3",
+    "python-gnupg>=0.4.4",
+    "PyYAML>=5.1",
+    "requests>=2.7.0",
+    "segno>=1.5",
+    "smpplib>=2.0",
+    "SQLAlchemy>=1.4.0,<2.0"
+]
 
 
 def get_man_pages(dir):
@@ -94,14 +98,13 @@ def get_scripts(dir):
 setup(
     name='privacyIDEA',
     version=VERSION,
-    description='privacyIDEA: identity, multifactor authentication (OTP), '
-                'authorization, audit',
+    description='privacyIDEA: multifactor authentication management system',
     author='privacyidea.org',
     license='AGPLv3',
     author_email='cornelius@privacyidea.org',
-    url='http://www.privacyidea.org',
+    url='https://www.privacyidea.org',
     keywords='OTP, two factor authentication, management, security',
-    python_requires='>=3.6',
+    python_requires='>=3.7',
     packages=find_packages(),
     scripts=["pi-manage"] + get_scripts("tools"),
     extras_require={
@@ -111,6 +114,7 @@ def get_scripts(dir):
                 "sphinxcontrib-plantuml>=0.18",
                 "sphinxcontrib-spelling>=7.0.0"],
         'test': ["mock>=2.0.0",
+                 "pyparsing>=3.0",
                  "pytest>=3.6.0",
                  "pytest-cov>=2.5.1",
                  "responses>=0.9.0",
@@ -145,7 +149,6 @@ def get_scripts(dir):
                  " Systems Administration :: Authentication/Directory",
                  'Programming Language :: Python',
                  'Programming Language :: Python :: 3',
-                 'Programming Language :: Python :: 3.6',
                  'Programming Language :: Python :: 3.7',
                  'Programming Language :: Python :: 3.8',
                  'Programming Language :: Python :: 3.9',


Test patch:
diff --git a/tests/requirements.txt b/tests/requirements.txt
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,21 +1,41 @@
+#
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
+#
+#    pip-compile --allow-unsafe --extra=test --output-file=tests/requirements.txt
+#
 -r ../requirements.txt
-attrs==22.2.0
-coverage==6.2; python_version == '3.6'
-coverage==7.2.2; python_version > '3.6'
-exceptiongroup==1.1.1; python_version > '3.6'
-iniconfig==1.1.1; python_version == '3.6'
-iniconfig==2.0.0; python_version > '3.6'
-mock==5.0.1
-packaging==23.0; python_version > '3.6'
-pluggy==1.0.0
-py==1.11.0; python_version == '3.6'
-pyparsing==3.0.9; python_version > '3.6'
-pytest==7.0.1; python_version == '3.6'
-pytest==7.2.2; python_version > '3.6'
-pytest-cov==4.0.0
-responses==0.17.0; python_version == '3.6'
-responses==0.23.1; python_version > '3.6'
-testfixtures==7.1.0
-tomli==1.2.3; python_version == '3.6'
-tomli==2.0.1; python_version > '3.6'
-types-PyYAML==6.0.12.9; python_version > '3.6'
+coverage[toml]==7.2.7; python_version == "3.7"
+coverage[toml]==7.3.2; python_version > "3.7"
+    # via
+    #   coverage
+    #   pytest-cov
+exceptiongroup==1.1.3
+    # via pytest
+iniconfig==2.0.0
+    # via pytest
+mock==5.1.0
+    # via privacyIDEA (setup.py)
+packaging==23.2
+    # via pytest
+pluggy==1.2.0; python_version == "3.7"
+pluggy==1.3.0; python_version > "3.7"
+    # via pytest
+pyparsing==3.1.1
+    # via privacyIDEA (setup.py)
+pytest==7.4.3
+    # via
+    #   privacyIDEA (setup.py)
+    #   pytest-cov
+pytest-cov==4.1.0
+    # via privacyIDEA (setup.py)
+responses==0.23.3
+    # via privacyIDEA (setup.py)
+testfixtures==7.2.2
+    # via privacyIDEA (setup.py)
+tomli==2.0.1
+    # via
+    #   coverage
+    #   pytest
+types-pyyaml==6.0.12.12
+    # via responses