Drop Py 3.9- support

PiperOrigin-RevId: 830342213
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index 4533dde..fe24532 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -336,38 +336,6 @@
         pip install setuptools==51.3.3
         python setup.py ${{ matrix.py_setuptools_cmd || 'test'}}
 
-  build_test_py27:
-    name: Build and test with Python 2.7
-    runs-on: ubuntu-latest
-    container:
-      image: ubuntu:22.04
-    steps:
-
-    - name: Harden Runner
-      uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
-      with:
-        egress-policy: audit
-
-    - name: Install deps
-      run: |
-        apt update
-        apt install -y curl gcc python2.7 python2.7-dev
-        curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
-        python2.7 get-pip.py
-        python2.7 -m pip install distutils-pytest==0.1
-
-    - name: Checkout the source
-      uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
-      with:
-        submodules: false
-        fetch-depth: 1
-
-    - name: Build / Test
-      run: |
-        python2.7 -VV
-        python2.7 -c "import sys; sys.exit('Invalid python version') if '.'.join(map(str,sys.version_info[0:2])) != '2.7' else True"
-        python2.7 setup.py test
-
   build_test_dotnet:
     name: Build and test with .NET
     runs-on: ubuntu-latest
diff --git a/python/_brotli.c b/python/_brotli.c
index 0ecc7aa..e456672 100644
--- a/python/_brotli.c
+++ b/python/_brotli.c
@@ -12,18 +12,18 @@
 #include <brotli/decode.h>
 #include <brotli/encode.h>
 
-#if PY_MAJOR_VERSION >= 3
-#define PY_GET_TYPE(Obj) (Py_TYPE(Obj))
-#else
-#define PY_GET_TYPE(Obj) ((Obj)->ob_type)
+// 3.9  end-of-life is 2025-10-31.
+// 3.10 end-of-life is 2026-10.
+// 3.11 end-of-life is 2027-10.
+// 3.12 end-of-life is 2028-10.
+// 3.13 end-of-life is 2029-10.
+// 3.14 end-of-life is 2030-10.
+#if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 10)
+#error "Only Python 3.10+ is supported"
 #endif
 
 static const char kErrorAttr[] = "error";
-#if PY_MAJOR_VERSION >= 3
 static const char kModuleAttr[] = "_module";
-#else
-static PyObject* BrotliError;
-#endif
 
 static const char kInvalidBufferError[] =
     "brotli: data must be a C-contiguous buffer";
@@ -201,7 +201,6 @@
 /* clang-format on */
 
 static void set_brotli_exception(PyObject* t, const char* msg) {
-#if PY_MAJOR_VERSION >= 3
   PyObject* error = NULL;
   PyObject* module = NULL;
   assert(t != NULL);
@@ -213,13 +212,9 @@
   if (error == NULL) return; /* AttributeError raised. */
   PyErr_SetString(error, msg);
   Py_DECREF(error);
-#else
-  PyErr_SetString(BrotliError, msg);
-#endif
 }
 
 static void set_brotli_exception_from_module(PyObject* m, const char* msg) {
-#if PY_MAJOR_VERSION >= 3
   PyObject* error = NULL;
   assert(m != NULL);
   assert(PyModule_Check(m));
@@ -227,9 +222,6 @@
   if (error == NULL) return; /* AttributeError raised. */
   PyErr_SetString(error, msg);
   Py_DECREF(error);
-#else
-  PyErr_SetString(BrotliError, msg);
-#endif
 }
 
 /*
@@ -354,7 +346,7 @@
   }
   if (len == 0) return result;
 
-  out = PyBytes_AS_STRING(result);
+  out = (uint8_t*)PyBytes_AS_STRING(result);
   block = buffer->head;
   while (block != buffer->tail) {
     memcpy(out + pos, block->payload, block->size);
@@ -389,7 +381,7 @@
   self->enc = BrotliEncoderCreateInstance(0, 0, 0);
   if (self->enc == NULL) {
     set_brotli_exception(self_type, kCompressCreateError);
-    PY_GET_TYPE(self)->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
     return NULL;
   }
   self->healthy = 1;
@@ -401,7 +393,7 @@
                                   PyObject* keywds) {
   static const char* kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
 
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   unsigned char mode = BROTLI_DEFAULT_MODE;
   unsigned char quality = BROTLI_DEFAULT_QUALITY;
   unsigned char lgwin = BROTLI_DEFAULT_WINDOW;
@@ -454,7 +446,7 @@
 
 static void brotli_Compressor_dealloc(PyBrotli_Compressor* self) {
   if (self->enc) BrotliEncoderDestroyInstance(self->enc);
-  PY_GET_TYPE(self)->tp_free((PyObject*)self);
+  Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 /*
@@ -466,7 +458,7 @@
 static PyObject* compress_stream(PyBrotli_Compressor* self,
                                  BrotliEncoderOperation op, uint8_t* input,
                                  size_t input_length) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   size_t available_in = input_length;
   const uint8_t* next_in = input;
   Buffer buffer;
@@ -526,7 +518,7 @@
 
 static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
                                            PyObject* args) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   PyObject* ret = NULL;
   PyObject* input_object = NULL;
   Py_buffer input;
@@ -556,7 +548,7 @@
 }
 
 static PyObject* brotli_Compressor_flush(PyBrotli_Compressor* self) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   PyObject* ret = NULL;
 
   if (self->healthy == 0) {
@@ -575,7 +567,7 @@
 }
 
 static PyObject* brotli_Compressor_finish(PyBrotli_Compressor* self) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   PyObject* ret = NULL;
 
   if (self->healthy == 0) {
@@ -619,7 +611,7 @@
   self->dec = BrotliDecoderCreateInstance(0, 0, 0);
   if (self->dec == NULL) {
     set_brotli_exception(self_type, kDecompressCreateError);
-    PY_GET_TYPE(self)->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
     return NULL;
   }
 
@@ -653,14 +645,14 @@
     free(self->unconsumed_data);
     self->unconsumed_data = NULL;
   }
-  PY_GET_TYPE(self)->tp_free((PyObject*)self);
+  Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
                                              PyObject* args, PyObject* keywds) {
   static const char* kwlist[] = {"", "output_buffer_limit", NULL};
 
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   PyObject* ret = NULL;
   PyObject* input_object = NULL;
   Py_buffer input;
@@ -720,7 +712,7 @@
     if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
       assert(buffer.avail_out == 0);
       /* All allocated is used -> reached the output length limit. */
-      if (buffer.total_allocated >= output_buffer_limit) break;
+      if (buffer.total_allocated >= (uint64_t)output_buffer_limit) break;
       if (Buffer_Grow(&buffer) < 0) {
         oom = 1;
         break;
@@ -782,7 +774,7 @@
 }
 
 static PyObject* brotli_Decompressor_is_finished(PyBrotli_Decompressor* self) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   if (self->healthy == 0) {
     set_brotli_exception(self_type, kDecompressUnhealthyError);
     return NULL;
@@ -800,7 +792,7 @@
 
 static PyObject* brotli_Decompressor_can_accept_more_data(
     PyBrotli_Decompressor* self) {
-  PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
+  PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
   if (self->healthy == 0) {
     set_brotli_exception(self_type, kDecompressUnhealthyError);
     return NULL;
@@ -892,13 +884,6 @@
 
 /* Module definition */
 
-static int init_brotli_mod(PyObject* m);
-
-static PyMethodDef brotli_methods[] = {
-    {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS,
-     brotli_decompress__doc__},
-    {NULL, NULL, 0, NULL}};
-
 static PyMethodDef brotli_Compressor_methods[] = {
     {"process", (PyCFunction)brotli_Compressor_process, METH_VARARGS,
      brotli_Compressor_process_doc},
@@ -909,44 +894,6 @@
     {NULL} /* Sentinel */
 };
 
-static PyMethodDef brotli_Decompressor_methods[] = {
-    {"process", (PyCFunction)brotli_Decompressor_process,
-     METH_VARARGS | METH_KEYWORDS, brotli_Decompressor_process_doc},
-    {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS,
-     brotli_Decompressor_is_finished_doc},
-    {"can_accept_more_data",
-     (PyCFunction)brotli_Decompressor_can_accept_more_data, METH_NOARGS,
-     brotli_Decompressor_can_accept_more_data_doc},
-    {NULL} /* Sentinel */
-};
-
-#if PY_MAJOR_VERSION >= 3
-
-#if PY_MINOR_VERSION >= 5
-static PyModuleDef_Slot brotli_mod_slots[] = {
-    {Py_mod_exec, init_brotli_mod},
-#if PY_MINOR_VERSION >= 12
-    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-#endif
-    {0, NULL}};
-#endif
-
-static struct PyModuleDef brotli_module = {
-    PyModuleDef_HEAD_INIT,
-    "_brotli",      /* m_name */
-    brotli_doc,     /* m_doc */
-    0,              /* m_size */
-    brotli_methods, /* m_methods */
-#if PY_MINOR_VERSION >= 5
-    brotli_mod_slots, /* m_slots */
-#else
-    NULL, /* m_reload */
-#endif
-    NULL, /* m_traverse */
-    NULL, /* m_clear */
-    NULL  /* m_free */
-};
-
 static PyType_Slot brotli_Compressor_slots[] = {
     {Py_tp_dealloc, (destructor)brotli_Compressor_dealloc},
     {Py_tp_doc, (void*)brotli_Compressor_doc},
@@ -960,6 +907,17 @@
     "brotli.Compressor", sizeof(PyBrotli_Compressor), 0,
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, brotli_Compressor_slots};
 
+static PyMethodDef brotli_Decompressor_methods[] = {
+    {"process", (PyCFunction)brotli_Decompressor_process,
+     METH_VARARGS | METH_KEYWORDS, brotli_Decompressor_process_doc},
+    {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS,
+     brotli_Decompressor_is_finished_doc},
+    {"can_accept_more_data",
+     (PyCFunction)brotli_Decompressor_can_accept_more_data, METH_NOARGS,
+     brotli_Decompressor_can_accept_more_data_doc},
+    {NULL} /* Sentinel */
+};
+
 static PyType_Slot brotli_Decompressor_slots[] = {
     {Py_tp_dealloc, (destructor)brotli_Decompressor_dealloc},
     {Py_tp_doc, (void*)brotli_Decompressor_doc},
@@ -973,127 +931,17 @@
     "brotli.Decompressor", sizeof(PyBrotli_Decompressor), 0,
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, brotli_Decompressor_slots};
 
-PyMODINIT_FUNC PyInit__brotli(void) {
-#if PY_MINOR_VERSION < 5
-  PyObject* m = PyModule_Create(&brotli_module);
-  if (m == NULL) return NULL;
-  if (init_brotli_mod(m) < 0) {
-    Py_DECREF(m);
-    m = NULL;
-  }
-  return m;
-#else
-  return PyModuleDef_Init(&brotli_module);
-#endif
-}
-
-#else
-
-static PyTypeObject brotli_CompressorType = {
-    PyObject_HEAD_INIT(NULL) 0,            /* ob_size */
-    "brotli.Compressor",                   /* tp_name */
-    sizeof(PyBrotli_Compressor),           /* tp_basicsize */
-    0,                                     /* tp_itemsize */
-    (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
-    0,                                     /* tp_print */
-    0,                                     /* tp_getattr */
-    0,                                     /* tp_setattr */
-    0,                                     /* tp_compare */
-    0,                                     /* tp_repr */
-    0,                                     /* tp_as_number */
-    0,                                     /* tp_as_sequence */
-    0,                                     /* tp_as_mapping */
-    0,                                     /* tp_hash  */
-    0,                                     /* tp_call */
-    0,                                     /* tp_str */
-    0,                                     /* tp_getattro */
-    0,                                     /* tp_setattro */
-    0,                                     /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT,                    /* tp_flags */
-    brotli_Compressor_doc,                 /* tp_doc */
-    0,                                     /* tp_traverse */
-    0,                                     /* tp_clear */
-    0,                                     /* tp_richcompare */
-    0,                                     /* tp_weaklistoffset */
-    0,                                     /* tp_iter */
-    0,                                     /* tp_iternext */
-    brotli_Compressor_methods,             /* tp_methods */
-    0,                                     /* tp_members */
-    0,                                     /* tp_getset */
-    0,                                     /* tp_base */
-    0,                                     /* tp_dict */
-    0,                                     /* tp_descr_get */
-    0,                                     /* tp_descr_set */
-    0,                                     /* tp_dictoffset */
-    (initproc)brotli_Compressor_init,      /* tp_init */
-    0,                                     /* tp_alloc */
-    brotli_Compressor_new,                 /* tp_new */
-};
-
-static PyTypeObject brotli_DecompressorType = {
-    PyObject_HEAD_INIT(NULL) 0,              /* ob_size */
-    "brotli.Decompressor",                   /* tp_name */
-    sizeof(PyBrotli_Decompressor),           /* tp_basicsize */
-    0,                                       /* tp_itemsize */
-    (destructor)brotli_Decompressor_dealloc, /* tp_dealloc */
-    0,                                       /* tp_print */
-    0,                                       /* tp_getattr */
-    0,                                       /* tp_setattr */
-    0,                                       /* tp_compare */
-    0,                                       /* tp_repr */
-    0,                                       /* tp_as_number */
-    0,                                       /* tp_as_sequence */
-    0,                                       /* tp_as_mapping */
-    0,                                       /* tp_hash  */
-    0,                                       /* tp_call */
-    0,                                       /* tp_str */
-    0,                                       /* tp_getattro */
-    0,                                       /* tp_setattro */
-    0,                                       /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT,                      /* tp_flags */
-    brotli_Decompressor_doc,                 /* tp_doc */
-    0,                                       /* tp_traverse */
-    0,                                       /* tp_clear */
-    0,                                       /* tp_richcompare */
-    0,                                       /* tp_weaklistoffset */
-    0,                                       /* tp_iter */
-    0,                                       /* tp_iternext */
-    brotli_Decompressor_methods,             /* tp_methods */
-    0,                                       /* tp_members */
-    0,                                       /* tp_getset */
-    0,                                       /* tp_base */
-    0,                                       /* tp_dict */
-    0,                                       /* tp_descr_get */
-    0,                                       /* tp_descr_set */
-    0,                                       /* tp_dictoffset */
-    (initproc)brotli_Decompressor_init,      /* tp_init */
-    0,                                       /* tp_alloc */
-    brotli_Decompressor_new,                 /* tp_new */
-};
-
-PyMODINIT_FUNC init_brotli(void) {
-  PyObject* m = Py_InitModule3("_brotli", brotli_methods, brotli_doc);
-  if (m == NULL) return;
-  init_brotli_mod(m);
-}
-
-#endif
-
 /* Emulates PyModule_AddObject */
 static int RegisterObject(PyObject* mod, const char* name, PyObject* value) {
   assert(value != NULL);
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 10
   int ret = PyModule_AddObjectRef(mod, name, value);
   /* Emulates PyModule_AddObject, i.e. decrements the reference count on
      success. */
   if (ret == 0) Py_DECREF(value);
   return ret;
-#else
-  return PyModule_AddObject(mod, name, value);
-#endif
 }
 
-static int init_brotli_mod(PyObject* m) {
+static int brotli_init_mod(PyObject* m) {
   PyObject* error_type = NULL;
   PyObject* compressor_type = NULL;
   PyObject* decompressor_type = NULL;
@@ -1104,34 +952,19 @@
   if (error_type == NULL) goto error;
 
   if (RegisterObject(m, kErrorAttr, error_type) < 0) goto error;
-#if PY_MAJOR_VERSION < 3
-  /* Assumption: pointer is used only while module is alive and well. */
-  BrotliError = error_type;
-#endif
   error_type = NULL;
 
-#if PY_MAJOR_VERSION >= 3
   compressor_type = PyType_FromSpec(&brotli_Compressor_spec);
   decompressor_type = PyType_FromSpec(&brotli_Decompressor_spec);
-#else
-  compressor_type = (PyObject*)&brotli_CompressorType;
-  Py_INCREF(compressor_type);
-  decompressor_type = (PyObject*)&brotli_DecompressorType;
-  Py_INCREF(decompressor_type);
-#endif
   if (compressor_type == NULL) goto error;
   if (PyType_Ready((PyTypeObject*)compressor_type) < 0) goto error;
-#if PY_MAJOR_VERSION >= 3
   if (PyObject_SetAttrString(compressor_type, kModuleAttr, m) < 0) goto error;
-#endif
   if (RegisterObject(m, "Compressor", compressor_type) < 0) goto error;
   compressor_type = NULL;
 
   if (decompressor_type == NULL) goto error;
   if (PyType_Ready((PyTypeObject*)decompressor_type) < 0) goto error;
-#if PY_MAJOR_VERSION >= 3
   if (PyObject_SetAttrString(decompressor_type, kModuleAttr, m) < 0) goto error;
-#endif
   if (RegisterObject(m, "Decompressor", decompressor_type) < 0) goto error;
   decompressor_type = NULL;
 
@@ -1162,3 +995,29 @@
   }
   return -1;
 }
+
+static PyMethodDef brotli_methods[] = {
+    {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS,
+     brotli_decompress__doc__},
+    {NULL, NULL, 0, NULL}};
+
+static PyModuleDef_Slot brotli_mod_slots[] = {
+    {Py_mod_exec, brotli_init_mod},
+#if (PY_MAJOR_VERSION > 3) || (PY_MINOR_VERSION >= 12)
+    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+#endif
+    {0, NULL}};
+
+static struct PyModuleDef brotli_module = {
+    PyModuleDef_HEAD_INIT,
+    "_brotli",        /* m_name */
+    brotli_doc,       /* m_doc */
+    0,                /* m_size */
+    brotli_methods,   /* m_methods */
+    brotli_mod_slots, /* m_slots */
+    NULL,             /* m_traverse */
+    NULL,             /* m_clear */
+    NULL              /* m_free */
+};
+
+PyMODINIT_FUNC PyInit__brotli(void) { return PyModuleDef_Init(&brotli_module); }
diff --git a/python/tests/_test_utils.py b/python/tests/_test_utils.py
index 36102b8..c451968 100644
--- a/python/tests/_test_utils.py
+++ b/python/tests/_test_utils.py
@@ -5,13 +5,13 @@
 import glob
 import itertools
 import os
+import pathlib
 import sys
 import sysconfig
 import tempfile
 import unittest
 
-# TODO(eustas): use str(pathlib.PurePath(file).parent.parent) for Python 3.4+
-project_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+project_dir = str(pathlib.PurePath(__file__).parent.parent.parent)
 test_dir = os.getenv('BROTLI_TESTS_PATH')
 BRO_ARGS = [os.getenv('BROTLI_WRAPPER')]
 
diff --git a/python/tests/compressor_test.py b/python/tests/compressor_test.py
index 8f92405..2c08f8f 100644
--- a/python/tests/compressor_test.py
+++ b/python/tests/compressor_test.py
@@ -20,7 +20,7 @@
 
   def tearDown(self):
     self.compressor = None
-    # super().tearDown()  # Requires Py3+
+    super().tearDown()
 
   def _check_decompression(self, test_data):
     # Write decompression to temp file and verify it matches the original.
@@ -71,28 +71,28 @@
 class TestCompressorQuality1(_TestCompressor, _test_utils.TestCase):
 
   def setUp(self):
-    # super().setUp()  # Requires Py3+
+    super().setUp()
     self.compressor = brotli.Compressor(quality=1)
 
 
 class TestCompressorQuality6(_TestCompressor, _test_utils.TestCase):
 
   def setUp(self):
-    # super().setUp()  # Requires Py3+
+    super().setUp()
     self.compressor = brotli.Compressor(quality=6)
 
 
 class TestCompressorQuality9(_TestCompressor, _test_utils.TestCase):
 
   def setUp(self):
-    # super().setUp()  # Requires Py3+
+    super().setUp()
     self.compressor = brotli.Compressor(quality=9)
 
 
 class TestCompressorQuality11(_TestCompressor, _test_utils.TestCase):
 
   def setUp(self):
-    # super().setUp()  # Requires Py3+
+    super().setUp()
     self.compressor = brotli.Compressor(quality=11)
 
 
diff --git a/python/tests/decompressor_test.py b/python/tests/decompressor_test.py
index 67844b3..1aa9ecb 100644
--- a/python/tests/decompressor_test.py
+++ b/python/tests/decompressor_test.py
@@ -22,12 +22,12 @@
   MIN_OUTPUT_BUFFER_SIZE = 32768  # Actually, several bytes less.
 
   def setUp(self):
-    # super().setUp()  # Requires Py3+
+    super().setUp()
     self.decompressor = brotli.Decompressor()
 
   def tearDown(self):
     self.decompressor = None
-    # super().tearDown()  # Requires Py3+
+    super().tearDown()
 
   def _check_decompression(self, test_data):
     # Verify decompression matches the original.
diff --git a/setup.py b/setup.py
index 5c82320..60cee8a 100644
--- a/setup.py
+++ b/setup.py
@@ -4,16 +4,11 @@
 # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
 
 import os
-import platform
 import re
 import unittest
 
-try:
-    from setuptools import Extension
-    from setuptools import setup
-except:
-    from distutils.core import Extension
-    from distutils.core import setup
+from setuptools import Extension
+from setuptools import setup
 from distutils.command.build_ext import build_ext
 from distutils import errors
 from distutils import dep_util
@@ -166,12 +161,12 @@
     "Programming Language :: C",
     "Programming Language :: C++",
     "Programming Language :: Python",
-    "Programming Language :: Python :: 2",
-    "Programming Language :: Python :: 2.7",
     "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.3",
-    "Programming Language :: Python :: 3.4",
-    "Programming Language :: Python :: 3.5",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Programming Language :: Python :: 3.14",
     "Programming Language :: Unix Shell",
     "Topic :: Software Development :: Libraries",
     "Topic :: Software Development :: Libraries :: Python Modules",