From 219d9cdcc5e3315d8929ed43b371847203ee2c6b Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Sat, 21 Sep 2024 09:55:06 +0200 Subject: [PATCH 1/5] add python type information for MessageQueue class --- posix_ipc.pyi | 19 +++++++++++++++++++ py.typed | 0 setup.py | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 posix_ipc.pyi create mode 100644 py.typed diff --git a/posix_ipc.pyi b/posix_ipc.pyi new file mode 100644 index 0000000..2e0f89e --- /dev/null +++ b/posix_ipc.pyi @@ -0,0 +1,19 @@ +from collections.abc import Callable +from typing import Any + +class MessageQueue: + def __init__( + self, + name: str | None, + flags: int = ..., + mode: int = ..., + max_messages: int = ..., + max_message_size: int = ..., + read: bool = ..., + write: bool = ..., + ) -> None: ... + def send(self, message: str | bytes, timeout: float | None = ..., priority: int = ...) -> None: ... + def receive(self, timeout: float | None = ...) -> tuple[bytes, int]: ... + def request_notification(self, notification: int | tuple[Callable[[Any], None], Any] | None = ...) -> None: ... + def close(self) -> None: ... + def unlink(self) -> None: ... diff --git a/py.typed b/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py index c1a6826..7286d50 100644 --- a/setup.py +++ b/setup.py @@ -71,5 +71,6 @@ classifiers=classifiers, license=license, keywords=keywords, - ext_modules=ext_modules + ext_modules=ext_modules, + package_data={"posix_ipc": ["*.pyi", "py.typed"]} ) From f3537a55cfb3286c5b51e1894d21c610affbb8e5 Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Wed, 25 Sep 2024 19:47:15 +0200 Subject: [PATCH 2/5] add python type information for the rest of the objects mentioned in USAGE.md --- posix_ipc.pyi | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/posix_ipc.pyi b/posix_ipc.pyi index 2e0f89e..4bcdfc2 100644 --- a/posix_ipc.pyi +++ b/posix_ipc.pyi @@ -1,6 +1,67 @@ from collections.abc import Callable from typing import Any +# module functions + +def unlink_semaphore(name: str) -> None: ... +def unlink_shared_memory(name: str) -> None: ... +def unlink_message_queue(name: str) -> None: ... + +# module constants + +O_CREX: int = ... +O_CREAT: int = ... +O_EXCL: int = ... +O_TRUNC: int = ... +PAGE_SIZE: int = ... +SEMAPHORE_TIMEOUT_SUPPORTED: bool = ... +SEMAPHORE_VALUE_SUPPORTED: bool = ... +SEMAPHORE_VALUE_MAX: int = ... +MESSAGE_QUEUES_SUPPORTED: bool = ... +QUEUE_MESSAGES_MAX_DEFAULT: int = ... +QUEUE_MESSAGE_SIZE_MAX_DEFAULT: int = ... +QUEUE_PRIORITY_MAX: int = ... +USER_SIGNAL_MIN: int = ... +USER_SIGNAL_MAX: int = ... +VERSION: str = ... + +# errors + +class Error: ... +class SignalError: ... +class PermissionsError: ... +class ExistentialError: ... +class BusyError: ... + +# classes + +class Semaphore: + def __init__( + self, + name: str, + flags: int = ..., + mode: int = ..., + initial_value: int = ... + ) -> None: ... + def acquire(self, timeout: float | None = ...) -> None: ... + def release(self) -> None: ... + def close(self) -> None: ... + def unlink(self) -> None: ... + def __enter__(self) -> None: ... + def __exit__(self) -> None: ... + +class SharedMemory: + def __init__( + self, + name: str, + flags: int = ..., + mode: int = ..., + size: int = ..., + read_only: bool = ... + ) -> None: ... + def close_fd(self) -> None: ... + def unlink(self) -> None: ... + class MessageQueue: def __init__( self, From 2b2970fe4edd88794d34dfd76bb6f6ac633a92f7 Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Thu, 26 Sep 2024 07:17:44 +0200 Subject: [PATCH 3/5] include type info in the packaging --- .gitignore | 1 + MANIFEST.in | 1 + posix_ipc.pyi | 80 ----------------------------- posix_ipc/__init__.pyi | 114 +++++++++++++++++++++++++++++++++++++++++ py.typed | 0 setup.py | 4 +- 6 files changed, 119 insertions(+), 81 deletions(-) delete mode 100644 posix_ipc.pyi create mode 100644 posix_ipc/__init__.pyi delete mode 100644 py.typed diff --git a/.gitignore b/.gitignore index 14995ec..15f8629 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ demo/*.o demo/premise demo/conclusion post_dist.py +**/*.egg-info diff --git a/MANIFEST.in b/MANIFEST.in index e8d7585..9f048e5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include INSTALL LICENSE VERSION include README.md USAGE.md history.md include setup.py prober.py include posix_ipc_module.c +include posix_ipc.pyi recursive-include prober *.c recursive-include demo *.h *.c *.py *.sh *.png *.txt *.md recursive-include demo2 *.py *.txt *.png *.md diff --git a/posix_ipc.pyi b/posix_ipc.pyi deleted file mode 100644 index 4bcdfc2..0000000 --- a/posix_ipc.pyi +++ /dev/null @@ -1,80 +0,0 @@ -from collections.abc import Callable -from typing import Any - -# module functions - -def unlink_semaphore(name: str) -> None: ... -def unlink_shared_memory(name: str) -> None: ... -def unlink_message_queue(name: str) -> None: ... - -# module constants - -O_CREX: int = ... -O_CREAT: int = ... -O_EXCL: int = ... -O_TRUNC: int = ... -PAGE_SIZE: int = ... -SEMAPHORE_TIMEOUT_SUPPORTED: bool = ... -SEMAPHORE_VALUE_SUPPORTED: bool = ... -SEMAPHORE_VALUE_MAX: int = ... -MESSAGE_QUEUES_SUPPORTED: bool = ... -QUEUE_MESSAGES_MAX_DEFAULT: int = ... -QUEUE_MESSAGE_SIZE_MAX_DEFAULT: int = ... -QUEUE_PRIORITY_MAX: int = ... -USER_SIGNAL_MIN: int = ... -USER_SIGNAL_MAX: int = ... -VERSION: str = ... - -# errors - -class Error: ... -class SignalError: ... -class PermissionsError: ... -class ExistentialError: ... -class BusyError: ... - -# classes - -class Semaphore: - def __init__( - self, - name: str, - flags: int = ..., - mode: int = ..., - initial_value: int = ... - ) -> None: ... - def acquire(self, timeout: float | None = ...) -> None: ... - def release(self) -> None: ... - def close(self) -> None: ... - def unlink(self) -> None: ... - def __enter__(self) -> None: ... - def __exit__(self) -> None: ... - -class SharedMemory: - def __init__( - self, - name: str, - flags: int = ..., - mode: int = ..., - size: int = ..., - read_only: bool = ... - ) -> None: ... - def close_fd(self) -> None: ... - def unlink(self) -> None: ... - -class MessageQueue: - def __init__( - self, - name: str | None, - flags: int = ..., - mode: int = ..., - max_messages: int = ..., - max_message_size: int = ..., - read: bool = ..., - write: bool = ..., - ) -> None: ... - def send(self, message: str | bytes, timeout: float | None = ..., priority: int = ...) -> None: ... - def receive(self, timeout: float | None = ...) -> tuple[bytes, int]: ... - def request_notification(self, notification: int | tuple[Callable[[Any], None], Any] | None = ...) -> None: ... - def close(self) -> None: ... - def unlink(self) -> None: ... diff --git a/posix_ipc/__init__.pyi b/posix_ipc/__init__.pyi new file mode 100644 index 0000000..29fd9dd --- /dev/null +++ b/posix_ipc/__init__.pyi @@ -0,0 +1,114 @@ +from collections.abc import Callable +from typing import Any + +__all__ = [ + "BusyError", + "Error", + "ExistentialError", + "MESSAGE_QUEUES_SUPPORTED", + "MessageQueue", + "O_CREAT", + "O_CREX", + "O_EXCL", + "O_NONBLOCK", + "O_RDONLY", + "O_RDWR", + "O_TRUNC", + "O_WRONLY", + "PAGE_SIZE", + "PermissionsError", + "QUEUE_MESSAGES_MAX_DEFAULT", + "QUEUE_MESSAGE_SIZE_MAX_DEFAULT", + "QUEUE_PRIORITY_MAX", + "SEMAPHORE_TIMEOUT_SUPPORTED", + "SEMAPHORE_VALUE_MAX", + "SEMAPHORE_VALUE_SUPPORTED", + "Semaphore", + "SharedMemory", + "SignalError", + "USER_SIGNAL_MAX", + "USER_SIGNAL_MIN", + "unlink_message_queue", + "unlink_semaphore", + "unlink_shared_memory", +] + +# module functions + +def unlink_semaphore(name: str) -> None: ... +def unlink_shared_memory(name: str) -> None: ... +def unlink_message_queue(name: str) -> None: ... + +# module constants + +O_CREX: int +O_CREAT: int +O_EXCL: int +O_TRUNC: int +PAGE_SIZE: int +SEMAPHORE_TIMEOUT_SUPPORTED: bool +SEMAPHORE_VALUE_SUPPORTED: bool +SEMAPHORE_VALUE_MAX: int +MESSAGE_QUEUES_SUPPORTED: bool +QUEUE_MESSAGES_MAX_DEFAULT: int +QUEUE_MESSAGE_SIZE_MAX_DEFAULT: int +QUEUE_PRIORITY_MAX: int +USER_SIGNAL_MIN: int +USER_SIGNAL_MAX: int +VERSION: str + +# errors + +class SignalError(Error): + pass + +class PermissionsError(Error): + pass + +class BusyError(Error): + pass + +class Error(Exception): + pass + +class ExistentialError(Error): + pass + +# classes + +class Semaphore: + def __init__(self, name: str, flags: int = ..., mode: int = ..., initial_value: int = ...) -> None: ... + def acquire(self, timeout: float | None = ...) -> None: ... + def release(self) -> None: ... + def close(self) -> None: ... + def unlink(self) -> None: ... + def __enter__(self) -> None: ... + def __exit__(self) -> None: ... + +class SharedMemory: + """POSIX semaphore object.""" + + def __init__( + self, name: str, flags: int = ..., mode: int = ..., size: int = ..., read_only: bool = ... + ) -> None: ... + def close_fd(self) -> None: ... + def fileno(self) -> None: ... + def unlink(self) -> None: ... + +class MessageQueue: + def __init__( + self, + name: str | None, + flags: int = ..., + mode: int = ..., + max_messages: int = ..., + max_message_size: int = ..., + read: bool = ..., + write: bool = ..., + ) -> None: ... + def send(self, message: str | bytes, timeout: float | None = ..., priority: int = ...) -> None: ... + def receive(self, timeout: float | None = ...) -> tuple[bytes, int]: ... + def request_notification(self, notification: int | tuple[Callable[[Any], None], Any] | None = ...) -> None: ... + def close(self) -> None: ... + def fileno(self) -> None: ... + def unlink(self) -> None: ... diff --git a/py.typed b/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/setup.py b/setup.py index 7286d50..c1d295b 100644 --- a/setup.py +++ b/setup.py @@ -72,5 +72,7 @@ license=license, keywords=keywords, ext_modules=ext_modules, - package_data={"posix_ipc": ["*.pyi", "py.typed"]} + packages=[name], + package_data={"": ["*.pyi"]}, + include_package_data=True, ) From 311bae20951a39225f3994097e75fe1bd789f160 Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Sat, 28 Sep 2024 10:43:27 +0200 Subject: [PATCH 4/5] add stuff i missed, but was caught by pybind11 --- posix_ipc/__init__.pyi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/posix_ipc/__init__.pyi b/posix_ipc/__init__.pyi index 29fd9dd..f19bdb3 100644 --- a/posix_ipc/__init__.pyi +++ b/posix_ipc/__init__.pyi @@ -56,6 +56,10 @@ QUEUE_PRIORITY_MAX: int USER_SIGNAL_MIN: int USER_SIGNAL_MAX: int VERSION: str +O_NONBLOCK: int +O_RDONLY: int +O_RDWR: int +O_WRONLY: int # errors From 50329f14f4b52fa53d4e80fb62da71b2a951e421 Mon Sep 17 00:00:00 2001 From: Asger Gitz-Johansen Date: Sun, 29 Sep 2024 09:43:10 +0200 Subject: [PATCH 5/5] add unit test that ensures type-compatibility --- .gitignore | 2 ++ .travis.yml | 2 +- tests/test_types.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/test_types.py diff --git a/.gitignore b/.gitignore index 15f8629..c17aaf6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ demo/premise demo/conclusion post_dist.py **/*.egg-info +tests/stubs/ +posix_ipc*.so diff --git a/.travis.yml b/.travis.yml index 4aad81f..611f41d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,5 @@ matrix: osx_image: xcode11.2 # Python 3.7 language: shell # 'language: python' is an error on Travis CI macOS -install: python setup.py install +install: python setup.py install && python pip install pybind11-stubgen script: python -m unittest discover diff --git a/tests/test_types.py b/tests/test_types.py new file mode 100644 index 0000000..cc84b3b --- /dev/null +++ b/tests/test_types.py @@ -0,0 +1,38 @@ +"""Test that the stubs generated by pybind11-stubgen are compatible with the hand-crafted ones.""" + +import inspect +import os +import sys +import unittest +from unittest.mock import patch + +import posix_ipc as handcrafted +from pybind11_stubgen import main + +from . import base as tests_base + + +class TestPybind11TypesCompatibility(tests_base.Base): + """Test that the stubs generated by pybind11-stubgen are compatible with the hand-crafted ones.""" + + def setUp(self): + """Generate the type stubs.""" + # mock the sys.argv - I dont want to do a subprocess.run because it can be error prone depending on which + # packages you may have installed. + with patch.object(sys, 'argv', ["pybind11-stubgen", "posix_ipc", "-o", "tests/stubs"]): + main() + os.rename("tests/stubs/posix_ipc.pyi", "tests/stubs/__init__.py") # to make the stubs importable + + def test_messagequeue(self): + """Test the generated MessageQueue is the same as the hand-crafted one.""" + from . import stubs as generated + generated_methods = inspect.getmembers(generated.MessageQueue, predicate=inspect.ismethod) + crafted_methods = inspect.getmembers(handcrafted.MessageQueue, predicate=inspect.ismethod) + crafted_method_names = [m[0] for m in crafted_methods] + for method in generated_methods: + self.assertTrue(method[0] in crafted_method_names) + # NOTE: We can't compare the func signatures, because pybind11-stubgen doesnt generate types for parameters. + + +if __name__ == '__main__': + unittest.main()