Skip to main content

High-level POSIX IPC named semaphores for Python

Project description

Pythonic API for POSIX IPC Named Semaphores

License PyPI Version Coverage Status Codacy Badge

Description

Use POSIX IPC named semaphores with a high-level API similar to Python's threading.Semaphore. This repository wraps around existing bindings from the posix_ipc package to provide a more Pythonic interface.

Features

  • Named Semaphores: Handle POSIX IPC named semaphores with ease.
  • Cross-Platform: Works on Linux, macOS, and Windows (via Cygwin).
  • Thread-Safe: Built on POSIX IPC, ensuring robust multi-process handling.
  • Pythonic API: Similar to Python's built-in threading.Semaphore for familiarity.
  • Flexible Creation: Choose how to handle existing semaphores (RAISE_IF_EXISTS, LINK_OR_CREATE, etc.).
  • Timeouts: Optionally specify timeouts for acquiring semaphores (platform-dependent).
  • Automatic Cleanup: Semaphore can be automatically unlinked when the object is deleted.

Installation

You can install the package from PyPI:

pip install named-semaphores

Usage

The NamedSemaphore class provides a high-level API for working with POSIX IPC named semaphores. Below are various usage examples demonstrating its flexibility and functionality.

1. Basic Semaphore Creation

Create a semaphore with a default initial value of 1.

from named_semaphores import NamedSemaphore

sem = NamedSemaphore("example_semaphore")
print(f"Semaphore '{sem.name}' created.")

You can also specify a custom initial value for the semaphore.

sem = NamedSemaphore("example_semaphore", initial_value=3)
print(f"Semaphore '{sem.name}' created with initial value of 3.")

2. Acquire, Release, and Timeouts

You can acquire and release the semaphore, specify a custom initial value, or use a timeout for acquiring (if supported on your platform).

Acquire and Release

sem = NamedSemaphore("example_semaphore")

# Acquire the semaphore
sem.acquire()
try:
    print("Critical section protected by semaphore.")
finally:
    # Release the semaphore
    sem.release()
    print("Semaphore released.")

Acquire with Timeout

# Acquire the semaphore with a 5-second timeout
acquired = sem.acquire(timeout=5)
if acquired:
    print("Semaphore acquired within timeout.")
    sem.release()
else:
    print("Failed to acquire semaphore within timeout.")

3. Using as a Context Manager

The semaphore can be used in a with statement to automatically acquire and release it.

with NamedSemaphore("example_semaphore") as sem:
    print(f"Semaphore '{sem.name}' acquired.")
    # Critical section here
print(f"Semaphore '{sem.name}' released.")

4. Unlinking a Semaphore

Unlinking a semaphore removes it from the system after all processes using it have closed it. Note some comments made by the posix_ipc author:

“If any processes have the semaphore open when unlink is called, the call to unlink returns immediately but destruction of the semaphore is postponed until all processes have closed the semaphore.

Note, however, that once a semaphore has been unlinked, calls to open() with the same name should refer to a new semaphore. Sound confusing? It is, and you'd probably be wise structure your code so as to avoid this situation.”

By default, semaphores created by this class are automatically unlinked when the object is deleted. You can override this behavior with the unlink_on_delete parameter.

  • Automatic unlinking example:
# Create a semaphore
handle_create = NamedSemaphore(
    "example_semaphore",
    handle_existence=NamedSemaphore.Flags.RAISE_IF_EXISTS
)
# Link to the semaphore
handle_link = NamedSemaphore(
    "example_semaphore",
    handle_existence=NamedSemaphore.Flags.RAISE_IF_NOT_EXISTS
)
# Trigger garbage collection
del handle_link  # Only closes the handle, does not unlink the semaphore
del handle_create  # Unlinks the semaphore, because this handle created it
  • Manual unlinking example:
# Set `unlink_on_delete` to False to prevent automatic unlinking on object deletion
sem = NamedSemaphore("example_semaphore", unlink_on_delete=False)
del sem  # Semaphore is not unlinked
sem = NamedSemaphore("example_semaphore", unlink_on_delete=False)
# Unlink the semaphore manually, handle will be closed in the destructor
sem.unlink()

5. Handling of Existing Semaphores

The NamedSemaphore class provides flags to handle existing semaphores in different ways. The safest ways are the RAISE_IF_EXISTS and RAISE_IF_NOT_EXISTS flags, which raise an error if the semaphore already exists or does not exist, respectively.

Some other flags are LINK_OR_CREATE and UNLINK_AND_CREATE, which link to an existing semaphore or unlink it if it already exists. They may be useful in certain scenarios, but should be used with caution, as they may silently hide issues in the code (e.g., not properly cleaning up semaphores).

Raise an error if the semaphore already exists

Use the RAISE_IF_EXISTS flag to ensure a new semaphore is created and raise an error if one already exists.

try:
    sem = NamedSemaphore(
        "example_semaphore",
        handle_existence=NamedSemaphore.Flags.RAISE_IF_EXISTS
    )
    print(f"Semaphore '{sem.name}' created without existing conflict.")
except FileExistsError:
    print("Handling existing semaphore error...")

Link to a semaphore or create it if it doesn't exist

Use the LINK_OR_CREATE flag to link to an existing semaphore or create a new one if it doesn't exist.

sem = NamedSemaphore(
    "example_semaphore",
    handle_existence=NamedSemaphore.Flags.LINK_OR_CREATE
)
print(f"Semaphore '{sem.name}' linked or created.")

Link to a semaphore or raise an error if it doesn't exist

Use the RAISE_IF_NOT_EXISTS flag to link to an existing semaphore, raising an error if it does not exist.

try:
    sem = NamedSemaphore(
        "example_semaphore",
        handle_existence=NamedSemaphore.Flags.RAISE_IF_NOT_EXISTS
    )
    print(f"Semaphore '{sem.name}' linked.")
except FileNotFoundError:
    print("Handling non-existing semaphore error...")

Create a Semaphore, unlinking it if it already exists

Use the UNLINK_AND_CREATE flag to unlink semaphore if it already exists and create a new one.

sem = NamedSemaphore(
    "example_semaphore",
    handle_existence=NamedSemaphore.Flags.UNLINK_AND_CREATE
)
print(f"Semaphore '{sem.name}' deleted and created.")

Note: although convenient, rather than using a forced deletion and create, it is better practice to handle the existence of semaphores in a more controlled manner. An already existing semaphore usually indicates that it is being used by another process, or that it was not properly cleaned up by a previous process.


These examples cover the most common scenarios for using NamedSemaphore. For advanced use cases or troubleshooting, refer to the module's documentation or docstrings.

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feature/your-feature).
  3. Commit your changes (git commit -m "Add your feature").
  4. Push to the branch (git push origin feature/your-feature).
  5. Open a Pull Request.

License

This project is licensed under the Apache License 2.0. See the LICENSE file for more details.

Please note that this project depends on the posix_ipc package, which is licensed under a BSD-style license. For details about the posix_ipc license, refer to its license file.

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

named_semaphores-1.0.1.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

named_semaphores-1.0.1-py3-none-any.whl (12.7 kB view details)

Uploaded Python 3

File details

Details for the file named_semaphores-1.0.1.tar.gz.

File metadata

  • Download URL: named_semaphores-1.0.1.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.3

File hashes

Hashes for named_semaphores-1.0.1.tar.gz
Algorithm Hash digest
SHA256 fec94c35736ca7434a2747278a716372ff88e711bf6afc737912726e333be29b
MD5 3cc84bed82396ce388bd00951af09900
BLAKE2b-256 a7f9e5bf6e4b86cce5f0ef833ee711266b285ea9c52fb079028dfbc0739f5173

See more details on using hashes here.

File details

Details for the file named_semaphores-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for named_semaphores-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1ed172458ce431486cba543fa9194429dede7711014f5e0d108b11615ed4d7d0
MD5 d2c87f8e69838c24f91406c36c9e0cb2
BLAKE2b-256 3eeca50fb6476866791d09684e28a72535307de16eba627e3e8c50d7f458e5a6

See more details on using hashes here.

Supported by

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