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.
Files changed (22) hide show
  1. {compatibility-2.2.0 → compatibility-2.4.0}/PKG-INFO +185 -27
  2. {compatibility-2.2.0 → compatibility-2.4.0}/README.md +178 -21
  3. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/__init__.py +1 -1
  4. compatibility-2.4.0/compatibility/__main__.py +33 -0
  5. compatibility-2.2.0/compatibility/__main__.py → compatibility-2.4.0/compatibility/core.py +164 -51
  6. compatibility-2.4.0/compatibility/locales/compatibility.pot +197 -0
  7. compatibility-2.4.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
  8. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/de/LC_MESSAGES/compatibility.po +50 -37
  9. compatibility-2.4.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
  10. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/es/LC_MESSAGES/compatibility.po +50 -37
  11. compatibility-2.4.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
  12. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/fr/LC_MESSAGES/compatibility.po +50 -37
  13. compatibility-2.4.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
  14. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/locales/nl/LC_MESSAGES/compatibility.po +50 -37
  15. {compatibility-2.2.0 → compatibility-2.4.0}/pyproject.toml +9 -10
  16. compatibility-2.2.0/compatibility/locales/de/LC_MESSAGES/compatibility.mo +0 -0
  17. compatibility-2.2.0/compatibility/locales/es/LC_MESSAGES/compatibility.mo +0 -0
  18. compatibility-2.2.0/compatibility/locales/fr/LC_MESSAGES/compatibility.mo +0 -0
  19. compatibility-2.2.0/compatibility/locales/nl/LC_MESSAGES/compatibility.mo +0 -0
  20. {compatibility-2.2.0 → compatibility-2.4.0}/LICENSE +0 -0
  21. {compatibility-2.2.0 → compatibility-2.4.0}/compatibility/err.py +0 -0
  22. {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.2.0
4
- Summary: A library that checks whether the running version of Python is compatible and tested. Remind the user to check for updates of the library.
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.14.1,<8) ; extra == "dev"
30
- Requires-Dist: mypy (>=2.1.0,<3) ; extra == "dev"
31
- Requires-Dist: pytest (>=9.0.3,<10) ; extra == "dev"
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.15.16,<0.16) ; extra == "dev"
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
  ![Supported Python Versions](https://img.shields.io/pypi/pyversions/compatibility)
42
43
  ![pypi version](https://img.shields.io/pypi/v/compatibility)
43
44
  ![Last commit](https://img.shields.io/github/last-commit/RuedigerVoigt/compatibility)
44
- [![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://www.ruediger-voigt.eu/coverage/compatibility/index.html)
45
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
45
46
  [![Downloads](https://pepy.tech/badge/compatibility/month)](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 prevent cryptic errors and provide helpful guidance to users.
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
- - **Prevent cryptic runtime errors** - Catch incompatible Python versions before they cause problems
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. **Package Version Logging** - Log package name, version, and release date for better debugging.
89
- 4. **Privacy-Friendly Update Reminders** - Optionally remind users to check for updates after N days (without phoning home or checking if updates exist).
90
- 5. **Operating System Compatibility** - Validate whether the OS (Linux, macOS, Windows) is fully supported, partially supported, or incompatible.
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.14. Python 3.15 is already tested against its betas and will be officially supported once it is released.
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.14'
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.9'],
162
- 'max_tested_version': '3.14'
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 users can configure their logging levels. This means users might see unwanted DEBUG messages. Calling it in the class constructor lets users set up logging first.
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.14'},
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.14.
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`: requires a `datetime` object (like `date(2021,1,1)`), or a string in the exact format `YYYY-MM-DD`.
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 you can use the [python_requires](https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires) parameter (or the `python` field in Poetry's `[tool.poetry.dependencies]`) to tell `pip` about incompatible versions of the interpreter. This should block installation on incompatible systems. However, users can circumvent this by setting the flag `--python-version`. More likely is a system upgrade that installs an incompatible version with the system's package manager.
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.15.final, but your version of my_package is only tested up to 3.14. Please check for updates.
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.9.final, but need at least Python 3.10 to run my_package.
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 prevent your package from running on older Python versions that lack required features (like match statements, structural pattern matching, or newer typing features).
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.9 support, use this library to give users a clear error message instead of cryptic import errors or runtime failures.
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.