Skip to main content

A library that checks whether the running version of Python is compatible and tested. Remind the user to check for updates of the library.

Project description

Supported Python Versions pypi version Last commit Coverage Downloads

Python Compatibility Checker for Package Authors

Version guard your Python package • Check OS compatibility • Prevent runtime errors

Ensure your Python package runs on the right versions, warn users about untested versions, and gracefully handle incompatible environments.

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.

Why Use This Library?

  • Prevent cryptic runtime errors - Catch incompatible Python versions before they cause problems
  • Zero dependencies - Uses only Python's standard library
  • Fully typed - Complete type hints (PEP 484) for better IDE support
  • Multilingual - Built-in English and German messages, plus AI-translated French, Dutch, and Spanish
  • User-friendly warnings - Inform users about untested Python versions
  • OS compatibility checks - Validate Linux, macOS, and Windows support
  • Update reminders - Gently encourage users to check for package updates
  • High test coverage - 100% coverage (statements and branches) for reliability

Table of Contents

What It Does

  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.
  2. Untested Version Warnings - Warn users when running your package on Python versions newer than you've tested.
  3. Package Version Logging - Log package name, version, and release date for better debugging.
  4. Privacy-Friendly Update Reminders - Optionally remind users to check for updates after N days (without phoning home or checking if updates exist).
  5. Operating System Compatibility - Validate whether the OS (Linux, macOS, Windows) is fully supported, partially supported, or incompatible.

All messages are available in English and German (plus AI-translated French, Dutch, and Spanish), selectable per-instance. See Translations.

Key Features

  • Zero Dependencies: Pure Python stdlib - no external packages required
  • Type Safe: Full type hints (PEP 484) for excellent IDE integration
  • Well Tested: 100% coverage (statements and branches), enforced in CI
  • 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.

Installation

pip install compatibility

That's it! No other dependencies to manage.

Quick Start

Note: To see compatibility's informational messages and warnings, configure logging first:

import logging
logging.basicConfig(level=logging.INFO)

Here's a minimal example to get started:

from datetime import date
import logging
import compatibility

# Configure logging to see messages
logging.basicConfig(level=logging.INFO)

class MyPackage:
    def __init__(self):
        compatibility.Check(
            package_name='my_package',
            package_version='1.0.0',
            release_date=date(2025, 1, 1),
            python_version_support={
                'min_version': '3.10',
                'incompatible_versions': [],
                'max_tested_version': '3.14'
            }
        )

Extended Example

Here's a more complete example using all optional features:

from datetime import date
import logging
import compatibility

logging.basicConfig(level=logging.INFO)

class MyAdvancedPackage:
    def __init__(self):
        compatibility.Check(
            package_name='my_advanced_package',
            package_version='2.0.0',
            release_date=date(2025, 1, 15),
            python_version_support={
                'min_version': '3.10',
                'incompatible_versions': ['3.9'],
                'max_tested_version': '3.14'
            },
            nag_over_update={
                'nag_days_after_release': 90,  # Start reminding after 90 days
                'nag_in_hundred': 25            # Show reminder 25% of the time
            },
            language_messages='en',  # 'en', 'de', 'fr', 'nl', 'es', or 'auto'
            system_support={
                'full': {'Linux', 'MacOS'},     # Fully tested
                'partial': {'Windows'},          # Should work, less tested
                'incompatible': set()            # No known incompatibilities
            }
        )

Detailed Usage

⚠️ Important: Where to Call Compatibility

Call compatibility.Check() in your class constructor, NOT in __init__.py

# ❌ DON'T DO THIS (in __init__.py)
import compatibility
compatibility.Check(...)  # Runs at import time, before user configures logging

# ✅ DO THIS (in your class __init__)
class MyClass:
    def __init__(self):
        compatibility.Check(...)  # Runs when instantiated, after logging is configured

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.

Complete Example

Here's a real-world example from the salted package:

# [...]
from datetime import date
import logging
# [...]

import compatibility
# [...]


class Salted:
    """Main class. Creates the other Objects, starts workers,
       collects results and starts the report of results. """

    VERSION = '0.6.1'

    def __init__(self,
                 [...]) -> None:

        compatibility.Check(
            package_name='salted',
            package_version=self.VERSION,
            release_date=date(2025, 6, 18),
            python_version_support={
                'min_version': '3.10',
                'incompatible_versions': [],
                'max_tested_version': '3.14'},
            nag_over_update={
                    'nag_days_after_release': 30,
                    'nag_in_hundred': 50},
            language_messages='en',
            system_support={
                'full': {'Linux', 'MacOS', 'Windows'}
            })

These settings ensure the package runs on Python 3.10 or higher, with testing confirmed through Python 3.14. 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.

Parameters

Required Parameters

  • package_name: the name of your package.
  • package_version: the version number of your package as a string.
  • release_date: requires a datetime object (like date(2021,1,1)), or a string in the exact format YYYY-MM-DD.

Optional Parameters

  • python_version_support: requires a dictionary with the three following keys:
    • min_version: a string with the number of the oldest supported version (like '3.10').
    • 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).
    • max_tested_version: the latest version of the interpreter you successfully tested your code with.
  • nag_over_update (optional): requires a dictionary with the two following keys:
    • nag_days_after_release: wait this number of days (int) since the release before reminding users to check for an update.
    • nag_in_hundred: Whether to nag over a possible update is random, but this sets the probability in the form how many times (int) out of a hundred starts the message is logged. Accordingly 100 means every time.
  • language_messages (optional): the language of the messages logged by this — en (English), de (German), fr (French), nl (Dutch), es (Spanish), or auto. Defaults to en. Any explicit code selects that language; auto detects the language from the user's environment locale (LANGUAGE/LC_ALL/LC_MESSAGES/LANG) and falls back to English when no matching catalog is available. See Translations for the quality status of each language.
  • system_support (optional): allows you to state the level of compatibility between your code and different Operating System groups. This is purposefully done on a very high level: valid inputs are only 'Linux', 'MacOS', and 'Windows' and not specific versions and distributions. The dictionary allows three keys with a set as value each:
    • full: The set of operating systems that are tested on production level.
    • 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.
    • 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).
  • on_incompatible (optional): what to do when the running Python version or operating system is incompatible. One of:
    • 'raise' (default): raise a RuntimeError — the current behaviour.
    • 'warn': log a warning and continue.
    • 'ignore': continue silently (logged at DEBUG only). You take responsibility for running in an unsupported environment.

System Support Behavior:

System in... Log Level Exception Raised? Description
full DEBUG No Production-tested, fully supported
partial WARNING No Should work, but less rigorously tested
incompatible ERROR Yes (RuntimeError) Known to fail
Not listed INFO No Support status unknown

The incompatible row reflects the default on_incompatible='raise'. With 'warn' it logs a warning and continues; with 'ignore' it continues silently.

Version strings

For compatibility checks three elements of the version string are recognized:

  • The major version
  • The minor version
  • The release level (either alpha, beta, candidate, or final)

If you provide all three elements (for example '3.10.alpha') only this very specific version will be matched. If you provide only the first two (in this case '3.10'), all release levels of this version will match. So assume your code would have issues with Python 3.5.beta and you list that exact string as an incompatible version. If you are running the script under 3.5.alpha nothing will happen. If you listed it by using the string '3.5' running the script with 3.5.alpha (or any other release level of 3.5) will raise the RuntimeError exception.

However, min_version and max_tested_version ignore the release level part.

Avoid running your package with an incompatible version of Python

In the pyproject.toml file of your package you can use the 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.

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.

Logging

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.

Basic Usage

By default, compatibility messages propagate to the root logger, so they will appear if you have configured logging in your application:

import logging
logging.basicConfig(level=logging.INFO)

# You will see compatibility's INFO, WARNING, and ERROR messages

Fine-Grained Control

You can selectively control compatibility's log level:

import logging

# Configure your application's logging
logging.basicConfig(level=logging.INFO)

# Silence compatibility warnings (only show errors)
logging.getLogger('compatibility').setLevel(logging.ERROR)

# Or make compatibility more verbose (show debug messages)
logging.getLogger('compatibility').setLevel(logging.DEBUG)

Message Levels

The compatibility package logs at different levels:

  • DEBUG: Informational messages about full platform support
  • INFO: Package version information and update reminders
  • WARNING: Running newer Python than tested, or partial platform support
  • ERROR: Incompatible Python version or operating system (also raises exceptions)

Example Messages

With logging configured, the messages look like the following (the level/name prefix comes from your logging setup; here it is Python's basicConfig default):

DEBUG:compatibility:my_package fully supports Linux.
INFO:compatibility:You are using my_package 1.0.0 (released: 2025-01-01)
INFO:compatibility:Your version of my_package was released 120 days ago. Please check for updates.
WARNING:compatibility:my_package has only partial support on Windows.
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.
ERROR:compatibility:This version of my_package is incompatible with Windows!

An incompatible Python version (below min_version, or listed in incompatible_versions) is logged the same way and, with the default on_incompatible='raise', additionally raises RuntimeError:

ERROR:compatibility:You use Python 3.9.final, but need at least Python 3.10 to run my_package.

The same messages appear translated when you select another language (for example language_messages='de').

Translations

Each available language has one of the following quality levels:

Language Code Status
English en Default / source language
German de Reviewed by a native speaker
French fr Translated by AI (pending native review)
Dutch nl Translated by AI (pending native review)
Spanish es Translated by AI (pending native review)

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.

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.

Exceptions

The compatibility package may raise the following exceptions:

  • RuntimeError: Raised when the Python version or operating system is incompatible with your package.
  • ValueError: Raised when invalid parameters are provided to the Check class.
  • compatibility.err.BadDate: Raised when the release_date parameter contains an invalid or malformed date.
  • compatibility.err.BadDateType: Raised when the release_date parameter is neither a datetime.date nor a string. Subclasses TypeError.
  • compatibility.err.ParameterContradiction: Raised when conflicting parameters are provided (e.g., a system marked as both fully supported and incompatible).

Use Cases

When Your Package Requires Specific Python Versions

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).

When You Know Specific Python Versions Are Broken

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).

When Users Report Bugs on Untested Python Versions

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.

When You Drop Support for Old Python Versions

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.

When Your Package Only Works on Certain Operating Systems

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.

When You Want Users to Update Old Package Versions

Use nag_over_update to gently remind users to check for updates after your package has been out for a while, without any privacy concerns (no network calls, no tracking).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compatibility-2.1.0.tar.gz (29.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

compatibility-2.1.0-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file compatibility-2.1.0.tar.gz.

File metadata

  • Download URL: compatibility-2.1.0.tar.gz
  • Upload date:
  • Size: 29.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for compatibility-2.1.0.tar.gz
Algorithm Hash digest
SHA256 7a063aac56c02578914f3d60b4d8d621e963d9a4fa85cd31e15bf3bc749fbe64
MD5 55c03ea670d9c4b19de7f6cda86a650f
BLAKE2b-256 24bbeb40de8f5c1226ab89ad109cf6047b7ba7c9f6df1360d28cd0d43d611935

See more details on using hashes here.

Provenance

The following attestation bundles were made for compatibility-2.1.0.tar.gz:

Publisher: release.yml on RuedigerVoigt/compatibility

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file compatibility-2.1.0-py3-none-any.whl.

File metadata

  • Download URL: compatibility-2.1.0-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for compatibility-2.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2daf030901b238dfef4cd116479dce587c0daf6cab8c9021ee1f7be47e0af4f7
MD5 b50de49b5dea18b686ca0e59ad49cb9c
BLAKE2b-256 6629e2f71cc77c45378dd8aa1f6114205789b9664e58520fb62e33eff17878ef

See more details on using hashes here.

Provenance

The following attestation bundles were made for compatibility-2.1.0-py3-none-any.whl:

Publisher: release.yml on RuedigerVoigt/compatibility

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page