compatibility 2.2.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {compatibility-2.2.0 → compatibility-2.4.0}/PKG-INFO +185 -27
- {compatibility-2.2.0 → compatibility-2.4.0}/README.md +178 -21
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/__init__.py +1 -1
- compatibility-2.4.0/compatibility/__main__.py +33 -0
- compatibility-2.2.0/compatibility/__main__.py → compatibility-2.4.0/compatibility/core.py +164 -51
- compatibility-2.4.0/compatibility/locales/compatibility.pot +197 -0
- compatibility-2.4.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/de/LC_MESSAGES/compatibility.po +50 -37
- compatibility-2.4.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/es/LC_MESSAGES/compatibility.po +50 -37
- compatibility-2.4.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/fr/LC_MESSAGES/compatibility.po +50 -37
- compatibility-2.4.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/nl/LC_MESSAGES/compatibility.po +50 -37
- {compatibility-2.2.0 → compatibility-2.4.0}/pyproject.toml +9 -10
- compatibility-2.2.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
- compatibility-2.2.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
- compatibility-2.2.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
- compatibility-2.2.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/LICENSE +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/err.py +0 -0
- {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/py.typed +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compatibility
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: A library that checks whether the running version of Python is compatible and tested.
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: A library that checks whether the running version of Python is compatible and tested. Can remind the user to check for updates of the library.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Keywords: compatibility,version-check,python-version,cross-platform,update-notifier
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
18
|
Classifier: Operating System :: OS Independent
|
|
18
19
|
Classifier: Operating System :: POSIX :: Linux
|
|
19
20
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -26,11 +27,11 @@ Classifier: Topic :: Software Development :: Quality Assurance
|
|
|
26
27
|
Classifier: Topic :: Utilities
|
|
27
28
|
Classifier: Typing :: Typed
|
|
28
29
|
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: coverage[toml] (>=7.
|
|
30
|
-
Requires-Dist: mypy (>=2.
|
|
31
|
-
Requires-Dist: pytest (>=9.0
|
|
30
|
+
Requires-Dist: coverage[toml] (>=7.16.2,<8) ; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy (>=2.4.0,<3) ; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest (>=9.2.0,<10) ; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest-cov (>=7.1.0,<8) ; extra == "dev"
|
|
33
|
-
Requires-Dist: ruff (>=0.
|
|
34
|
+
Requires-Dist: ruff (>=0.17.3,<0.18) ; extra == "dev"
|
|
34
35
|
Project-URL: Bug Tracker, https://github.com/RuedigerVoigt/compatibility/issues
|
|
35
36
|
Project-URL: Changelog, https://github.com/RuedigerVoigt/compatibility/blob/main/CHANGELOG.md
|
|
36
37
|
Project-URL: Documentation, https://github.com/RuedigerVoigt/compatibility
|
|
@@ -41,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
41
42
|

|
|
42
43
|

|
|
43
44
|

|
|
44
|
-
|
|
45
|
+

|
|
45
46
|
[](https://pepy.tech/project/compatibility)
|
|
46
47
|
|
|
47
48
|
# Python Compatibility Checker for Package Authors
|
|
@@ -50,11 +51,11 @@ Description-Content-Type: text/markdown
|
|
|
50
51
|
|
|
51
52
|
Ensure your Python package runs on the right versions, warn users about untested versions, and gracefully handle incompatible environments.
|
|
52
53
|
|
|
53
|
-
Compatibility is a lightweight, zero-dependency library that helps Python package authors and library developers provide a better user experience by checking Python version compatibility, operating system compatibility (Linux, macOS, Windows), and gently reminding users to update. Uses Python's standard `gettext` for translations and follows PEP 561 (typed package). Perfect for PyPI package maintainers who want to
|
|
54
|
+
Compatibility is a lightweight, zero-dependency library that helps Python package authors and library developers provide a better user experience by checking Python version compatibility, operating system compatibility (Linux, macOS, Windows), and gently reminding users to update. Uses Python's standard `gettext` for translations and follows PEP 561 (typed package). Perfect for PyPI package maintainers who want to replace cryptic errors with helpful guidance to users.
|
|
54
55
|
|
|
55
56
|
## Why Use This Library?
|
|
56
57
|
|
|
57
|
-
- **
|
|
58
|
+
- **Clear messages instead of cryptic errors** - Tell users when they run your package on an incompatible Python version
|
|
58
59
|
- **Zero dependencies** - Uses only Python's standard library
|
|
59
60
|
- **Fully typed** - Complete type hints (PEP 484) for better IDE support
|
|
60
61
|
- **Multilingual** - Built-in English and German messages, plus AI-translated French, Dutch, and Spanish
|
|
@@ -66,12 +67,17 @@ Compatibility is a lightweight, zero-dependency library that helps Python packag
|
|
|
66
67
|
## Table of Contents
|
|
67
68
|
|
|
68
69
|
- [What It Does](#what-it-does)
|
|
70
|
+
- [What It Does Not Do](#what-it-does-not-do)
|
|
69
71
|
- [Key Features](#key-features)
|
|
70
72
|
- [Installation](#installation)
|
|
71
73
|
- [Quick Start](#quick-start)
|
|
72
74
|
- [Detailed Usage](#detailed-usage)
|
|
73
75
|
- [⚠️ Important: Where to Call Compatibility](#️-important-where-to-call-compatibility)
|
|
74
76
|
- [Complete Example](#complete-example)
|
|
77
|
+
- [Recipes](#recipes)
|
|
78
|
+
- [Command-Line Tool: Configure Logging First](#command-line-tool-configure-logging-first)
|
|
79
|
+
- [Warn Instead of Raising](#warn-instead-of-raising)
|
|
80
|
+
- [Typed Configuration](#typed-configuration)
|
|
75
81
|
- [Parameters](#parameters)
|
|
76
82
|
- [Required Parameters](#required-parameters)
|
|
77
83
|
- [Optional Parameters](#optional-parameters)
|
|
@@ -85,18 +91,27 @@ Compatibility is a lightweight, zero-dependency library that helps Python packag
|
|
|
85
91
|
|
|
86
92
|
1. **Python Version Validation** - Check if the running Python interpreter meets minimum requirements and isn't in your list of incompatible versions. Raises `RuntimeError` for incompatible versions.
|
|
87
93
|
2. **Untested Version Warnings** - Warn users when running your package on Python versions newer than you've tested.
|
|
88
|
-
3. **
|
|
89
|
-
4. **
|
|
90
|
-
5. **
|
|
94
|
+
3. **End-of-Life Warnings** - Optionally warn users whose Python version has reached its end of life and no longer receives security updates (built-in dates, no network access).
|
|
95
|
+
4. **Package Version Logging** - Log package name, version, and release date for better debugging.
|
|
96
|
+
5. **Privacy-Friendly Update Reminders** - Optionally remind users to check for updates after N days (without phoning home or checking if updates exist).
|
|
97
|
+
6. **Operating System Compatibility** - Validate whether the OS (Linux, macOS, Windows) is fully supported, partially supported, or incompatible.
|
|
91
98
|
|
|
92
99
|
All messages are available in English and German (plus AI-translated French, Dutch, and Spanish), selectable per-instance. See [Translations](#translations).
|
|
93
100
|
|
|
101
|
+
## What It Does Not Do
|
|
102
|
+
|
|
103
|
+
- **No update lookup** - The update reminder only counts the days since your release date. It never contacts PyPI or any other server, so it cannot tell whether a newer version exists.
|
|
104
|
+
- **No dependency checks** - It checks the Python interpreter and the operating system, not the versions of other installed packages. Declare those as dependencies in your `pyproject.toml`, so `pip` resolves them.
|
|
105
|
+
- **No patch versions** - Python versions are matched on major and minor version, optionally with the release level (like `'3.12'` or `'3.12.beta'`). You cannot target a single patch release such as 3.12.1.
|
|
106
|
+
- **No OS versions or distributions** - Operating systems are only distinguished as Linux, MacOS, and Windows.
|
|
107
|
+
- **No protection before your code is imported** - The check runs in your constructor. An error while Python imports your modules comes first (see [Avoid running your package with an incompatible version of Python](#avoid-running-your-package-with-an-incompatible-version-of-python)).
|
|
108
|
+
|
|
94
109
|
## Key Features
|
|
95
110
|
|
|
96
111
|
- **Zero Dependencies**: Pure Python stdlib - no external packages required
|
|
97
112
|
- **Type Safe**: Full type hints ([PEP 484](https://www.python.org/dev/peps/pep-0484/)) for excellent IDE integration
|
|
98
113
|
- **Well Tested**: 100% coverage (statements and branches), enforced in CI
|
|
99
|
-
- **Python 3.10+**: Supports Python 3.10 through 3.
|
|
114
|
+
- **Python 3.10+**: Supports Python 3.10 through 3.15.
|
|
100
115
|
|
|
101
116
|
## Installation
|
|
102
117
|
|
|
@@ -117,6 +132,7 @@ logging.basicConfig(level=logging.INFO)
|
|
|
117
132
|
|
|
118
133
|
Here's a minimal example to get started:
|
|
119
134
|
|
|
135
|
+
<!-- tested -->
|
|
120
136
|
```python
|
|
121
137
|
from datetime import date
|
|
122
138
|
import logging
|
|
@@ -134,15 +150,24 @@ class MyPackage:
|
|
|
134
150
|
python_version_support={
|
|
135
151
|
'min_version': '3.10',
|
|
136
152
|
'incompatible_versions': [],
|
|
137
|
-
'max_tested_version': '3.
|
|
153
|
+
'max_tested_version': '3.15'
|
|
138
154
|
}
|
|
139
155
|
)
|
|
156
|
+
|
|
157
|
+
MyPackage() # The check runs when the class is instantiated
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Output:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
INFO:compatibility:You are using my_package 1.0.0 (released: 2025-01-01)
|
|
140
164
|
```
|
|
141
165
|
|
|
142
166
|
### Extended Example
|
|
143
167
|
|
|
144
168
|
Here's a more complete example using all optional features:
|
|
145
169
|
|
|
170
|
+
<!-- tested -->
|
|
146
171
|
```python
|
|
147
172
|
from datetime import date
|
|
148
173
|
import logging
|
|
@@ -158,8 +183,8 @@ class MyAdvancedPackage:
|
|
|
158
183
|
release_date=date(2025, 1, 15),
|
|
159
184
|
python_version_support={
|
|
160
185
|
'min_version': '3.10',
|
|
161
|
-
'incompatible_versions': ['3.
|
|
162
|
-
'max_tested_version': '3.
|
|
186
|
+
'incompatible_versions': ['3.13.alpha'], # e.g. a pre-release with a known bug
|
|
187
|
+
'max_tested_version': '3.15'
|
|
163
188
|
},
|
|
164
189
|
nag_over_update={
|
|
165
190
|
'nag_days_after_release': 90, # Start reminding after 90 days
|
|
@@ -170,8 +195,11 @@ class MyAdvancedPackage:
|
|
|
170
195
|
'full': {'Linux', 'MacOS'}, # Fully tested
|
|
171
196
|
'partial': {'Windows'}, # Should work, less tested
|
|
172
197
|
'incompatible': set() # No known incompatibilities
|
|
173
|
-
}
|
|
198
|
+
},
|
|
199
|
+
warn_eol=True # Warn if the running Python has reached its end of life
|
|
174
200
|
)
|
|
201
|
+
|
|
202
|
+
MyAdvancedPackage()
|
|
175
203
|
```
|
|
176
204
|
|
|
177
205
|
## Detailed Usage
|
|
@@ -191,7 +219,7 @@ class MyClass:
|
|
|
191
219
|
compatibility.Check(...) # Runs when instantiated, after logging is configured
|
|
192
220
|
```
|
|
193
221
|
|
|
194
|
-
**Why?** If you call it in `__init__.py`, it runs immediately on import before
|
|
222
|
+
**Why?** If you call it in `__init__.py`, it runs immediately on import, usually before the application has configured logging. Its messages are then lost: the `compatibility` logger has no handler of its own, so nothing is shown until logging is set up. Calling it in the class constructor lets users set up logging first. It also keeps your package importable on an incompatible system (for example for documentation tools), as the `RuntimeError` is only raised when your class is used.
|
|
195
223
|
|
|
196
224
|
### Complete Example
|
|
197
225
|
|
|
@@ -223,7 +251,7 @@ class Salted:
|
|
|
223
251
|
python_version_support={
|
|
224
252
|
'min_version': '3.10',
|
|
225
253
|
'incompatible_versions': [],
|
|
226
|
-
'max_tested_version': '3.
|
|
254
|
+
'max_tested_version': '3.15'},
|
|
227
255
|
nag_over_update={
|
|
228
256
|
'nag_days_after_release': 30,
|
|
229
257
|
'nag_in_hundred': 50},
|
|
@@ -232,19 +260,135 @@ class Salted:
|
|
|
232
260
|
'full': {'Linux', 'MacOS', 'Windows'}
|
|
233
261
|
})
|
|
234
262
|
```
|
|
235
|
-
These settings ensure the package runs on Python 3.10 or higher, with testing confirmed through Python 3.
|
|
263
|
+
These settings ensure the package runs on Python 3.10 or higher, with testing confirmed through Python 3.15.
|
|
236
264
|
Salted in that specific version is a relatively young package that will receive frequent updates. So beginning a month after the release this will nag the user over looking for an update every second time - provided the user activated logging.
|
|
237
265
|
|
|
266
|
+
## Recipes
|
|
267
|
+
|
|
268
|
+
The test suite runs every example in this section, as well as the Quick Start and the Extended Example.
|
|
269
|
+
|
|
270
|
+
### Command-Line Tool: Configure Logging First
|
|
271
|
+
|
|
272
|
+
In a command-line tool, configure logging first and create the class that runs the check afterwards. Otherwise the messages are lost:
|
|
273
|
+
|
|
274
|
+
<!-- tested -->
|
|
275
|
+
```python
|
|
276
|
+
import argparse
|
|
277
|
+
from datetime import date
|
|
278
|
+
import logging
|
|
279
|
+
|
|
280
|
+
import compatibility
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class MyTool:
|
|
284
|
+
def __init__(self):
|
|
285
|
+
compatibility.Check(
|
|
286
|
+
package_name='my_tool',
|
|
287
|
+
package_version='1.0.0',
|
|
288
|
+
release_date=date(2025, 1, 1))
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def main(argv=None):
|
|
292
|
+
parser = argparse.ArgumentParser(prog='my_tool')
|
|
293
|
+
parser.add_argument('--verbose', action='store_true')
|
|
294
|
+
args = parser.parse_args(argv)
|
|
295
|
+
# Configure logging before anything creates MyTool:
|
|
296
|
+
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
|
|
297
|
+
MyTool()
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
if __name__ == '__main__':
|
|
301
|
+
main()
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Output:
|
|
305
|
+
|
|
306
|
+
```text
|
|
307
|
+
INFO:compatibility:You are using my_tool 1.0.0 (released: 2025-01-01)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Warn Instead of Raising
|
|
311
|
+
|
|
312
|
+
By default an incompatible Python version or operating system raises `RuntimeError`. With `on_incompatible='warn'` it only logs a warning, and your package keeps running. Use this if your users should decide whether to continue:
|
|
313
|
+
|
|
314
|
+
<!-- tested -->
|
|
315
|
+
```python
|
|
316
|
+
from datetime import date
|
|
317
|
+
import logging
|
|
318
|
+
|
|
319
|
+
import compatibility
|
|
320
|
+
|
|
321
|
+
logging.basicConfig(level=logging.INFO)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class MyPackage:
|
|
325
|
+
def __init__(self):
|
|
326
|
+
compatibility.Check(
|
|
327
|
+
package_name='my_package',
|
|
328
|
+
package_version='1.0.0',
|
|
329
|
+
release_date=date(2025, 1, 1),
|
|
330
|
+
python_version_support={
|
|
331
|
+
'min_version': '3.10',
|
|
332
|
+
'incompatible_versions': [],
|
|
333
|
+
'max_tested_version': '3.15'},
|
|
334
|
+
system_support={'full': {'Linux', 'MacOS', 'Windows'}},
|
|
335
|
+
on_incompatible='warn')
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
MyPackage()
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
With `on_incompatible='ignore'` the message is only logged at DEBUG level.
|
|
342
|
+
|
|
343
|
+
### Typed Configuration
|
|
344
|
+
|
|
345
|
+
The parameter dictionaries have matching `TypedDict` types: `PythonVersionSupport`, `SystemSupport`, and `NagOverUpdate`. Annotate your configuration with them, and a type checker such as mypy reports a misspelled or missing key before your code runs:
|
|
346
|
+
|
|
347
|
+
<!-- tested -->
|
|
348
|
+
```python
|
|
349
|
+
from datetime import date
|
|
350
|
+
import logging
|
|
351
|
+
|
|
352
|
+
import compatibility
|
|
353
|
+
from compatibility import NagOverUpdate, PythonVersionSupport, SystemSupport
|
|
354
|
+
|
|
355
|
+
logging.basicConfig(level=logging.INFO)
|
|
356
|
+
|
|
357
|
+
PYTHON_SUPPORT: PythonVersionSupport = {
|
|
358
|
+
'min_version': '3.10',
|
|
359
|
+
'incompatible_versions': [],
|
|
360
|
+
'max_tested_version': '3.15',
|
|
361
|
+
}
|
|
362
|
+
SYSTEM_SUPPORT: SystemSupport = {'full': {'Linux', 'MacOS', 'Windows'}}
|
|
363
|
+
NAG: NagOverUpdate = {'nag_days_after_release': 90, 'nag_in_hundred': 25}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class MyPackage:
|
|
367
|
+
def __init__(self) -> None:
|
|
368
|
+
compatibility.Check(
|
|
369
|
+
package_name='my_package',
|
|
370
|
+
package_version='1.0.0',
|
|
371
|
+
release_date=date(2025, 1, 1),
|
|
372
|
+
python_version_support=PYTHON_SUPPORT,
|
|
373
|
+
system_support=SYSTEM_SUPPORT,
|
|
374
|
+
nag_over_update=NAG)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
MyPackage()
|
|
378
|
+
```
|
|
379
|
+
|
|
238
380
|
## Parameters
|
|
239
381
|
|
|
240
382
|
### Required Parameters
|
|
241
383
|
|
|
242
384
|
* `package_name`: the name of your package.
|
|
243
385
|
* `package_version`: the version number of your package as a string.
|
|
244
|
-
* `release_date`:
|
|
386
|
+
* `release_date`: a `datetime.date` (like `date(2021, 1, 1)`), or a string in the format `YYYY-MM-DD` (like `'2021-01-01'`; leading zeros are optional). A `datetime.datetime` is accepted too; its time is ignored.
|
|
245
387
|
|
|
246
388
|
### Optional Parameters
|
|
247
389
|
|
|
390
|
+
For `python_version_support`, `nag_over_update`, and `system_support`, leaving the parameter out, passing `None`, or passing an empty dictionary `{}` all skip that check. Any other value that is not a dictionary raises a `ValueError`.
|
|
391
|
+
|
|
248
392
|
* `python_version_support`: requires a dictionary with the three following keys:
|
|
249
393
|
* `min_version`: a string with the number of the oldest supported version (like `'3.10'`).
|
|
250
394
|
* `incompatible_versions`: a list of incompatible versions that, by default, raise the `RuntimeError` exception if they try to run your package (configurable with `on_incompatible`).
|
|
@@ -257,10 +401,13 @@ Salted in that specific version is a relatively young package that will receive
|
|
|
257
401
|
* `full`: The set of operating systems that are tested on production level.
|
|
258
402
|
* `partial`: The set of systems that should work, but are not as rigorously tested as those with full support. A system found running here logs a warning.
|
|
259
403
|
* `incompatible`: The set of systems of which you know they will fail to run the code properly. By default, if an OS in this set tries to run the code, this will yield a `RuntimeError` exception (configurable with `on_incompatible`).
|
|
404
|
+
|
|
405
|
+
An operating system may appear in only one of the three sets; listing it in two raises `ParameterContradiction`.
|
|
260
406
|
* `on_incompatible` (optional): what to do when the running Python version or operating system is incompatible. One of:
|
|
261
407
|
* `'raise'` (default): raise a `RuntimeError` — the current behaviour.
|
|
262
408
|
* `'warn'`: log a warning and continue.
|
|
263
409
|
* `'ignore'`: continue silently (logged at DEBUG only). You take responsibility for running in an unsupported environment.
|
|
410
|
+
* `warn_eol` (optional): set to `True` to log a warning if the running Python version has reached its [end of life](https://devguide.python.org/versions/) and no longer receives security updates. Defaults to `False`. The end-of-life dates are built in, so the check works offline; a Python version newer than the installed release of this library is not reported.
|
|
264
411
|
|
|
265
412
|
**System Support Behavior:**
|
|
266
413
|
|
|
@@ -287,10 +434,15 @@ However, `min_version` and `max_tested_version` ignore the release level part.
|
|
|
287
434
|
|
|
288
435
|
## Avoid running your package with an incompatible version of Python
|
|
289
436
|
|
|
290
|
-
In the `pyproject.toml` file of your package
|
|
437
|
+
In the `pyproject.toml` file of your package, set [`requires-python`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#python-requires) in the `[project]` table (for example `requires-python = ">=3.10"`) to tell `pip` about the supported versions of the interpreter. This blocks installation on incompatible systems. However, users can circumvent this with pip's `--ignore-requires-python` flag. More likely is a system upgrade that replaces the interpreter with an incompatible version through the system's package manager.
|
|
291
438
|
|
|
292
439
|
If you define incompatible versions while initializing the `compatibility` package, you add another layer of control. Even if your user ended up with an incompatible interpreter, that will trigger a `RuntimeError` exception once the user tries to run your package.
|
|
293
440
|
|
|
441
|
+
This works only if Python gets as far as running the check:
|
|
442
|
+
|
|
443
|
+
* `compatibility` itself runs on Python 3.10 or newer. On an older interpreter it cannot import, so it cannot show any message.
|
|
444
|
+
* Your package's modules are imported before your constructor runs. If one of them uses syntax or a feature that the running Python lacks, the user gets a `SyntaxError` or `ImportError` before the check. For example, `except*` and `typing.Self` need Python 3.11 and `class C[T]:` needs Python 3.12.
|
|
445
|
+
|
|
294
446
|
## Logging
|
|
295
447
|
|
|
296
448
|
The `compatibility` package uses Python's standard logging module with a dedicated logger named `'compatibility'`. This allows you to control the verbosity of compatibility messages independently from your application's logging.
|
|
@@ -328,7 +480,7 @@ logging.getLogger('compatibility').setLevel(logging.DEBUG)
|
|
|
328
480
|
The compatibility package logs at different levels:
|
|
329
481
|
* **DEBUG**: Informational messages about full platform support
|
|
330
482
|
* **INFO**: Package version information and update reminders
|
|
331
|
-
* **WARNING**: Running newer Python than tested, or partial platform support
|
|
483
|
+
* **WARNING**: Running newer Python than tested, a Python past its end of life (with `warn_eol=True`), or partial platform support
|
|
332
484
|
* **ERROR**: Incompatible Python version or operating system (also raises exceptions)
|
|
333
485
|
|
|
334
486
|
### Example Messages
|
|
@@ -341,7 +493,8 @@ DEBUG:compatibility:my_package fully supports Linux.
|
|
|
341
493
|
INFO:compatibility:You are using my_package 1.0.0 (released: 2025-01-01)
|
|
342
494
|
INFO:compatibility:Your version of my_package was released 120 days ago. Please check for updates.
|
|
343
495
|
WARNING:compatibility:my_package has only partial support on Windows.
|
|
344
|
-
WARNING:compatibility:You are running Python 3.
|
|
496
|
+
WARNING:compatibility:You are running Python 3.16.final, but your version of my_package is only tested up to 3.15. Please check for updates.
|
|
497
|
+
WARNING:compatibility:Python 3.10 reached its end of life (2026-10-01) and no longer receives security updates. Please upgrade to a newer version of Python.
|
|
345
498
|
ERROR:compatibility:This version of my_package is incompatible with Windows!
|
|
346
499
|
```
|
|
347
500
|
|
|
@@ -350,7 +503,7 @@ An incompatible Python version (below `min_version`, or listed in
|
|
|
350
503
|
`on_incompatible='raise'`, additionally raises `RuntimeError`:
|
|
351
504
|
|
|
352
505
|
```text
|
|
353
|
-
ERROR:compatibility:You use Python 3.
|
|
506
|
+
ERROR:compatibility:You use Python 3.10.final, but need at least Python 3.11 to run my_package.
|
|
354
507
|
```
|
|
355
508
|
|
|
356
509
|
The same messages appear translated when you select another language (for
|
|
@@ -370,6 +523,8 @@ Each available language has one of the following quality levels:
|
|
|
370
523
|
|
|
371
524
|
The AI-translated languages are marked as such in the message catalog (`.po`) header until a native speaker has reviewed them. Corrections and reviews from native speakers are very welcome.
|
|
372
525
|
|
|
526
|
+
To add a new language, start from the template `compatibility/locales/compatibility.pot`, which lists every message.
|
|
527
|
+
|
|
373
528
|
Set `language_messages='auto'` to follow the user's environment locale (`LANGUAGE`/`LC_ALL`/`LC_MESSAGES`/`LANG`); if no matching catalog is available, messages fall back to English.
|
|
374
529
|
|
|
375
530
|
## Exceptions
|
|
@@ -385,7 +540,7 @@ The `compatibility` package may raise the following exceptions:
|
|
|
385
540
|
## Use Cases
|
|
386
541
|
|
|
387
542
|
### When Your Package Requires Specific Python Versions
|
|
388
|
-
Use `min_version` to
|
|
543
|
+
Use `min_version` to stop your package from running on older Python versions that lack features it relies on at runtime, for example a standard library function or behavior added in a later version. A check in your constructor cannot catch syntax your modules use, as that fails on import (see [Avoid running your package with an incompatible version of Python](#avoid-running-your-package-with-an-incompatible-version-of-python)).
|
|
389
544
|
|
|
390
545
|
### When You Know Specific Python Versions Are Broken
|
|
391
546
|
Use `incompatible_versions` to block specific Python versions where your package has known issues (e.g., bugs in Python itself, or dependencies that break on certain versions).
|
|
@@ -393,8 +548,11 @@ Use `incompatible_versions` to block specific Python versions where your package
|
|
|
393
548
|
### When Users Report Bugs on Untested Python Versions
|
|
394
549
|
Use `max_tested_version` to warn users when they're running your package on newer Python versions you haven't tested yet. This helps manage expectations and reduces false bug reports.
|
|
395
550
|
|
|
551
|
+
### When Your Users Still Run an Unmaintained Python
|
|
552
|
+
Set `warn_eol=True` to warn users whose Python version has reached its end of life. Such a version gets no security fixes anymore, even if your package still supports it. The warning is opt-in, so it never surprises users of a package that did not ask for it.
|
|
553
|
+
|
|
396
554
|
### When You Drop Support for Old Python Versions
|
|
397
|
-
After dropping (for example) Python 3.
|
|
555
|
+
After dropping (for example) Python 3.10 support, set `min_version` to `'3.11'`. Users who still end up on 3.10 get a clear error message instead of a cryptic runtime failure, as long as your package still imports on 3.10.
|
|
398
556
|
|
|
399
557
|
### When Your Package Only Works on Certain Operating Systems
|
|
400
558
|
Use `system_support` to declare which operating systems (Linux, macOS, Windows) are fully supported, partially supported, or incompatible. For example, if your package uses Linux-specific system calls.
|