A suite of automated scripts to backport Python projects from 3.9+ to Python 3.8, covering both Python source code and C/C++ extension modules.
When you have a Python project that requires Python 3.9+ but need to run it on Python 3.8, you face two categories of incompatibilities:
- Python syntax and standard library changes (PEP 585, 604, 584, 616, etc.)
- Python C API changes (new functions, changed signatures, removed macros, etc.)
This suite provides two scripts that automatically detect and fix these issues:
fix_py38_python.py— Fixes Python source code (.pyfiles)fix_py38_c.py— Fixes C/C++ extension source code (.c,.h,.cppfiles)
Both scripts are i18n-aware: output messages are displayed in your system language (English, Chinese, or Russian). You can add more languages by creating JSON files in the lang/ directory.
We have successfully used this suite to backport major scientific computing and AI libraries to Python 3.8:
| Project | Version | Status | Repository |
|---|---|---|---|
| NumPy | 2.x (latest main) | Compiled & tested on Python 3.8 | numpy_backport_py38 |
| SciPy | 1.x (latest main) | Compiled & tested on Python 3.8 | scipy_backport_py38 |
| PyTorch | 2.13.0a0 (latest main) | Compiled & tested on Python 3.8 | pytorch_backport_py38 |
| Transformers | 5.8.0.dev0 (latest main) | Compiled & tested on Python 3.8 | — |
| HuggingFace Hub | 1.17.0.dev0 (latest main) | Compiled & tested on Python 3.8 | — |
All projects were compiled with maximum optimization flags and released as installable wheels. PyTorch was installed in editable (development) mode for testing.
The latest Transformers 5.8.0.dev0 and HuggingFace Hub 1.17.0.dev0 were verified with comprehensive testing on Python 3.8.10 + PyTorch 2.13:
✅ Verified Functionality:
- Core imports:
AutoConfig,AutoTokenizer,AutoModel,AutoModelForCausalLM,AutoModelForSequenceClassification - Specific models:
BertModel,BertTokenizer,GPT2Model,GPT2Tokenizer,T5Model,T5Config,LlamaConfig - Model forward inference: BertModel, GPT2Model (with random tensors)
- Config operations:
to_dict(),to_json_string(),from_dict(),for_model() - Training infrastructure:
Trainer,TrainingArguments,pipeline - Processors:
ProcessorMixin,FeatureExtractionMixin,ImageProcessingMixin - HuggingFace Hub API:
HfApi,hf_hub_download,snapshot_download
🔧 Additional Manual Fixes Required (beyond automated script):
match/case→if/elifconversion (in huggingface_hub)isinstance(x, A|B)→isinstance(x, (A, B))conversionfrom typing import Annotated→from typing_extensions import Annotated(22 files)torch.distributedconditional imports (for PyTorch builds without distributed support)functools.cached_propertyfallback for Python 3.8get_type_hints()try/except protection for PEP 585/604 annotationsdataclass(kw_only=True)conditional handling- Module-level PEP 585 type aliases in base classes (e.g.,
OrderedDict[str, str | None])
| # | Feature | PEP/Version | Fix Strategy |
|---|---|---|---|
| 1 | Built-in generics (list[X], dict[K,V], etc.) |
PEP 585 / 3.9+ | Replace with typing.List[X], typing.Dict[K,V], etc. |
| 2 | Union types (X | Y in annotations) |
PEP 604 / 3.10+ | Replace with Union[X, Y] |
| 3 | Dictionary merge operators (d1 | d2, d1 |= d2) |
PEP 584 / 3.9+ | Replace with {**d1, **d2} and d1.update(d2) |
| 4 | str.removeprefix() / str.removesuffix() |
PEP 616 / 3.9+ | Fallback implementation with str.startswith()/str.endswith() |
| 5 | typing.Annotated |
PEP 593 / 3.9+ | try/except fallback to typing_extensions |
| 6 | functools.cache |
3.9+ | Replace with functools.lru_cache(maxsize=None) |
| 7 | importlib.metadata |
3.9+ | try/except fallback to importlib_metadata |
| 8 | typing.TypeAlias / TypeGuard / ParamSpec / Concatenate |
3.9+ | try/except fallback to typing_extensions |
| 9 | isinstance(x, A | B) / issubclass(x, A | B) |
3.10+ | Replace with isinstance(x, (A, B)) |
| 10 | zoneinfo |
3.9+ | try/except fallback to backports.zoneinfo |
| 11 | graphlib |
3.9+ | try/except fallback |
| 12 | math.lcm() |
3.9+ | try/except fallback implementation |
| 13 | math.nextafter() / math.ulp() |
3.9+ | try/except fallback implementation |
| 14 | collections.XXX → collections.abc.XXX |
3.9+ deprecation | Replace deprecated imports |
| 15 | random.randbytes() |
3.9+ | try/except fallback implementation |
| 16 | ast.unparse() |
3.9+ | try/except fallback to astunparse |
| 17 | bytes/bytearray.removeprefix/removesuffix |
3.9+ | Runtime monkey-patch fallback |
| 18 | Parenthesized context managers | 3.10+ | Unparenthesize |
| 19 | setup.py / pyproject.toml Python version constraints |
— | Update version requirements |
| 20 | zip(..., strict=True) |
3.10+ | _zip_strict() fallback implementation |
| 21 | int.bit_count() |
3.10+ | _int_bit_count() fallback implementation |
| 22 | aiter() / anext() |
3.10+ | _aiter_compat() / _anext_compat() fallback |
| 23 | bisect module key= parameter |
3.10+ | Fallback implementation |
| 24 | dataclass(slots=True) |
3.10+ | Remove slots parameter |
| 25 | collections.abc.Callable[...] subscripting |
3.9+ | Replace with typing.Callable[...] |
| 26 | functools.lru_cached_property |
3.9+ | try/except with full fallback class |
| 27 | functools.cached_property |
3.8+ | Auto-add missing import |
| 28 | types.GenericAlias / EllipsisType / NotImplementedType |
3.9+ | Monkey-patch in __init__.py |
| 29 | Duplicate imports | — | Merge from X import statements |
| 30 | array_api_compat PEP 585 typing |
3.9+ | Replace built-in generics in type annotations |
| 31 | AttributeError(msg, name=..., obj=...) keyword-only args |
3.10+ | Remove name=None/obj=None or use _AttributeError_compat() helper |
| 32 | Type alias union (X: TypeAlias = A | B) |
3.10+ | Convert to X: TypeAlias = Union[A, B] with auto Union import |
| 33 | dataclass(kw_only=True) |
3.10+ | Remove kw_only parameter |
| 34 | inspect.get_annotations() |
3.10+ | try/except fallback to manual annotation extraction |
| 35 | TypeAliasType (PEP 695 type X = Y) |
3.12+ | Convert to typing.TypeAlias assignment |
| 36 | Runtime type union (X | Y outside annotations) |
3.10+ | Convert to Union[X, Y] at runtime-evaluated positions |
| 37 | PEP 604 non-annotation union (class body, default values) | 3.10+ | Convert to Union[X, Y] with from __future__ import annotations awareness |
| 38 | PEP 695 generic class (class X[T]:) |
PEP 695 / 3.12+ | Convert to Generic[T] base class with TypeVar |
| 39 | PEP 695 generic function (def f[T]():) |
PEP 695 / 3.12+ | Convert to TypeVar with @overload where needed |
| 40 | PEP 695 type statement (type X = Y) |
PEP 695 / 3.12+ | Convert to X: TypeAlias = Y |
| 41 | Lambda decorator (@lambda x: x) |
3.9+ syntax | Wrap in regular function |
| 42 | match/case statement |
PEP 634 / 3.10+ | Mark with TODO comment (requires manual rewrite as if/elif) |
| 43 | typing 3.11+ features (NotRequired, TypeVarTuple, etc.) |
3.11+ | try/except fallback to typing_extensions |
| 44 | enum.StrEnum |
3.11+ | try/except fallback with manual implementation |
| 45 | contextlib.chdir() |
3.11+ | try/except fallback implementation |
| 46 | operator.call() |
3.11+ | try/except fallback implementation |
| 47 | hashlib.file_digest() |
3.11+ | try/except fallback implementation |
| 48 | collections.abc.Iterator[...] subscripting |
3.9+ | Replace with typing.Iterator[...] |
| 49 | Regex flag merge (re.X | re.I etc. in module-level constants) |
3.11+ | Convert to re.compile() with combined flags |
| 50 | from __future__ import annotations position fix |
— | Move to top of file if not first import |
| 51 | Broken # noqa comments after import rewrites |
— | Clean up stale # noqa directives |
| 52 | Type alias PEP 585 + 604 combined (X = dict[str, int | str]) |
3.9+ | Recursive conversion: dict → Dict, | → Union, with nested bracket handling |
| 53 | Dict merge with improved variable detection (self.kwargs | other) |
3.9+ | Detect dotted variable names and kwargs-style variables for dict merge conversion |
| 54 | ExceptionGroup / BaseExceptionGroup |
3.11+ | try/except fallback to exceptiongroup |
The script detects the following features but does not automatically fix them, as they require manual intervention:
| Feature | Version | Reason |
|---|---|---|
typing.NotRequired |
3.11+ | Requires typing_extensions with version check |
collections.abc.Buffer |
3.12+ | No simple fallback |
BaseException.add_note() |
3.11+ | No fallback possible |
tomllib |
3.11+ | Use tomli as fallback (manual) |
asyncio.TaskGroup |
3.11+ | Structural change, no simple fallback |
math.exp2() / math.cbrt() |
3.11+ | Manual fallback needed |
datetime.UTC |
3.11+ | Manual fallback needed |
itertools.batched() |
3.12+ | Manual fallback needed |
pathlib.Path.walk() |
3.12+ | Manual fallback needed |
distutils (removed in 3.12) |
3.12 | Use setuptools as replacement |
warnings.deprecated() |
3.13+ | No simple fallback |
copy.replace() |
3.13+ | No simple fallback |
| PEP 594 removed modules | 3.13 | Manual migration needed |
compression.zstd |
3.15+ | No fallback |
concurrent interpreters module |
3.14+ | No fallback |
annotationlib |
3.14+ | No fallback |
frozendict |
3.15+ | No fallback |
dbm.sqlite3 |
3.15+ | No fallback |
base64.z85 |
3.15+ | No fallback |
sentinel |
3.14+ | No fallback |
profiling module |
3.15+ | No fallback |
typing 3.13+ / 3.15+ features |
3.13+ | Requires manual typing_extensions version check |
Strategy:
- Deploy
pythoncapi_compat.hcompatibility header to the project - Add
#include "pythoncapi_compat.h"to files using Python 3.9+ C API - Fix direct usage of Python 3.9+ C API without going through the compat layer
- Fix
CMakeLists.txt/setup.pyPython version constraints - Fix static/non-static declaration conflicts in
pythoncapi_compat.hfor functions already present in Python 3.8 system headers - Auto-detect and update incomplete
pythoncapi_compat.hfiles - Add per-function
#ifndefguards topythoncapi_compat.hto prevent redefinition errors when multiple projects (e.g., numpy + scipy) each include their own copy
| API | Version | Compat Strategy |
|---|---|---|
PyObject_CallNoArgs() |
3.9+ | Inline wrapper via pythoncapi_compat.h |
PyObject_CallOneArg() |
3.9+ | Inline wrapper |
Py_IS_TYPE() |
3.9+ | Macro wrapper |
Py_SET_TYPE() / Py_SET_SIZE() / Py_SET_REFCNT() |
3.9+ | Macro wrappers |
PyModule_AddType() |
3.9+ | Inline wrapper |
PyModule_AddObjectRef() |
3.10+ | Inline wrapper |
PyObject_Vectorcall() |
3.9+ | Inline wrapper |
PyType_GetModule() |
3.9+ | Compat implementation via __module__ lookup |
PyType_GetModuleByDef() |
3.9+ | Stub implementation (needs manual review) |
PyType_GetSlot() |
3.9+ | Full compat via tp_base chain traversal |
Py_NewRef() / Py_XNewRef() |
3.10+ | Inline wrappers |
Py_Is() / Py_IsNone() / Py_IsTrue() / Py_IsFalse() |
3.10+ | Macro wrappers |
PyFrame_GetCode() / PyFrame_GetBack() |
3.9+ | Inline wrappers |
PyErr_GetRaisedException() / PyErr_SetRaisedException() |
3.12+ | Inline wrappers |
PyObject_VectorcallDict() / PyObject_VectorcallMethod() |
3.12+ | PyObject_Call fallback |
PyDict_GetItemRef() / PyDict_GetItemStringRef() |
3.13+ | PyDict_GetItemWithError fallback |
PyList_GetItemRef() |
3.13+ | PyList_GetItem + Py_XINCREF fallback |
PyLong_AsInt() |
3.13+ | PyLong_AsLong + range check fallback |
Py_MOD_GIL_NOT_USED / PyUnstable_Module_SetGIL() |
3.13+ | #ifdef Py_GIL_DISABLED or version check guard |
PyObject_GetAIter() |
3.10+ | PyObject_CallMethod(o, "__aiter__", NULL) fallback |
Py_tp_* slot constants |
3.9+ | #ifndef guarded definitions for PyType_GetSlot compat |
The following functions are already present in Python 3.8 system headers. The script automatically adjusts version checks in pythoncapi_compat.h to avoid static/non-static declaration conflicts:
PyType_GetSlot— present inobject.hsince Python 3.8PyModule_AddFunctions— present inmodsupport.hsince Python 3.8PyInterpreterState_GetDict— present inpystate.hsince Python 3.8PyErr_GetExcInfo/PyErr_SetExcInfo— present inpyerrors.hsince Python 3.8
#pragma warning(disable:...)→ wrap with#ifdef _MSC_VER__declspec(deprecated)→ add GCC__attribute__((deprecated))alternative_aligned_malloc→ wrap with#ifdef _MSC_VERguardPY_SSIZE_T_CLEAN→ ensure defined before#include <Python.h>
The pythoncapi_compat.h file included in this suite is sourced from the python/pythoncapi-compat project, licensed under the Zero Clause BSD (0BSD) license.
We have added extra compatibility implementations (EXTRA_COMPAT section) for APIs not covered by the upstream header, including Python 3.12–3.15 APIs.
All compatibility functions in pythoncapi_compat.h (both upstream and our extra implementations) are wrapped with per-function #ifndef guards:
#ifndef _PYCAPI_COMPAT_PyType_GetSlot
#define _PYCAPI_COMPAT_PyType_GetSlot
// ... implementation ...
#endif /* _PYCAPI_COMPAT_PyType_GetSlot */This prevents redefinition errors when multiple projects (e.g., NumPy + SciPy + PyTorch) each include their own copy of pythoncapi_compat.h. The fix_py38_c.py script automatically adds these guards to any existing pythoncapi_compat.h that lacks them.
The scripts support multiple languages for console output. Language files are stored as JSON in the lang/ directory:
lang/
├── en.json # English (default)
├── zh.json # Chinese (中文)
└── ru.json # Russian (Русский)
How it works:
- The script auto-detects your system language via
locale.getdefaultlocale() - If your language has a translation file, it will be used
- If no translation file is found, English is used as the default
- You can add new languages by creating a
lang/<code>.jsonfile with the same keys asen.json
To add a new language:
- Copy
lang/en.jsontolang/<your_language_code>.json - Translate the values (not the keys) to your language
- Place the file in the
lang/directory — it will be auto-detected
- Python 3.8+
- The
i18n.pymodule andlang/directory must be in the same directory as the scripts
# Fix the current directory
python fix_py38_python.py
# Fix a specific project directory
python fix_py38_python.py /path/to/project# Fix the current directory
python fix_py38_c.py
# Fix a specific project directory
python fix_py38_c.py /path/to/project- Backup your code (git commit or copy)
- Run
fix_py38_python.pyfirst to fix Python syntax - Run
fix_py38_c.pyto fix C/C++ extensions - Manually review all changes
- Compile and test
- Fix any remaining issues manually
- These scripts modify source files in-place. Always backup or use version control before running.
- Manual review is required. Automated fixes cannot handle all cases correctly, especially:
- Complex type annotations with nested generics
PyCMethod/METH_METHOD(Python 3.9+) — replaced with comment markersPyType_GetModuleByDef— stub implementation, may fail at runtime- Dictionary merge operators in non-dict contexts (numpy arrays, sets, etc.)
- Not all Python 3.9+ features can be automatically fixed. See the detection-only lists above.
- Test thoroughly after running. Compile all C extensions and run the project's test suite.
- The
pythoncapi_compat.his from python/pythoncapi-compat under the Zero Clause BSD license. We added extra compat implementations on top of it.
python38_compat_fix_suite/
├── fix_py38_python.py # Python source fix script
├── fix_py38_c.py # C/C++ source fix script
├── pythoncapi_compat.h # C API compatibility header (upstream + extras)
├── i18n.py # Internationalization module
├── lang/
│ ├── en.json # English translations
│ ├── zh.json # Chinese translations
│ └── ru.json # Russian translations
├── README.md # This file (English)
├── README_ru.md # Russian documentation
└── README_zh.md # Chinese documentation
- Scripts (
fix_py38_python.py,fix_py38_c.py,i18n.py): MIT License pythoncapi_compat.h: Zero Clause BSD (0BSD) — from python/pythoncapi-compat