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 ✅ 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 - 99%+ coverage 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, selectable per-instance.

Key Features

  • Zero Dependencies: Pure Python stdlib - no external packages required
  • Type Safe: Full type hints (PEP 484) for excellent IDE integration
  • Well Tested: 97% minimum coverage enforced; typically 98-99%
  • Python 3.10+: Supports Python 3.10 through 3.14

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',  # or 'de' for German
            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 will raise the RuntimeError exception if they try to run your package.
    • 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 (en for English or de for German) of the messages logged by this. Defaults to English log messages.
  • 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. If an OS in this set tries to run the code, this will yield a RuntimeError exception.

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

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)

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.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 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.0.0.tar.gz (20.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.0.0-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for compatibility-2.0.0.tar.gz
Algorithm Hash digest
SHA256 f740b7a7e9f8d20f76ee3b62232b4b5492bcd63d3bc8291d2079c4a4fe95ec01
MD5 3f16e8d8e858ae8dd9eb70c41c75f8c0
BLAKE2b-256 d0c7f8250d2c0937b851caf3769a07d4cceba96d3e807e99408ba4b0f5e7f635

See more details on using hashes here.

Provenance

The following attestation bundles were made for compatibility-2.0.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.0.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for compatibility-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1300784f64555b46c76d8c86cfb36c32e042a2b28110d501d5dc19b65bb85b0e
MD5 269cdef3058062a8b17cbafcd7d714d3
BLAKE2b-256 b6d9a59d5f5cd3187d6d4bb92579d949ff41811ab4e95d2a01722b663b4d3f71

See more details on using hashes here.

Provenance

The following attestation bundles were made for compatibility-2.0.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