Skip to main content

High-level POSIX IPC named semaphores for Python

Project description

Pythonic API for POSIX IPC Named Semaphores

License PyPI Version Dependencies 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.2.tar.gz (25.8 kB view details)

Uploaded Source

Built Distribution

named_semaphores-1.0.2-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for named_semaphores-1.0.2.tar.gz
Algorithm Hash digest
SHA256 2125d96c158f06618737a8c2551dbcf5b336cc6f096668b1da9dc44ffecd7437
MD5 d29e2a65cc20edea0faaf51d323d623a
BLAKE2b-256 f87b0a2ed30e70d65c550cf5b4603f94a8a83fc7fb0b541be846c7d8612a82f9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for named_semaphores-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e747380ee737d6842f31099859a3be2eeb355d7dcaf6e61833058fdff56c0d08
MD5 11a95a2d73dcba534d2b7ec52b0af228
BLAKE2b-256 287d6d5808dd715b0b3446b2851cbd7b6ae7385068bec358694fe2410e8e345c

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