From 63ef16eb8807ca20ee7448997ccc0094c150ec8b Mon Sep 17 00:00:00 2001 From: iphydf Date: Thu, 7 Sep 2023 15:04:02 +0000 Subject: [PATCH 1/7] refactor: Adapt pytox to the new tox_system stuff. --- .dockerignore | 1 + BUILD.bazel | 42 +++++++++++++-- Dockerfile | 34 +++++++++--- pytox.pxd | 0 pytox/.gitignore | 14 +++++ pytox/error.pyx | 15 ++++++ pytox/src/core.pyx | 112 ++++++++++++--------------------------- pytox/src/log.pxd | 18 +++++++ pytox/src/log.pyx | 35 ++++++++++++ pytox/src/memory.pxd | 10 ++++ pytox/src/memory.pyx | 32 +++++++++++ pytox/src/network.pxd | 10 ++++ pytox/src/network.pyx | 34 ++++++++++++ pytox/src/options.pxd | 8 +++ pytox/src/options.pyx | 45 ++++++++++++++++ pytox/src/random.pxd | 10 ++++ pytox/src/random.pyx | 34 ++++++++++++ pytox/src/system.pxd | 16 ++++++ pytox/src/system.pyx | 43 +++++++++++++++ pytox/src/time.pxd | 8 +++ pytox/src/time.pyx | 34 ++++++++++++ test/core_test.py | 24 +++++---- test/tox_options_test.py | 8 +-- tools/gen_api.py | 50 +++++++++++++---- 24 files changed, 524 insertions(+), 113 deletions(-) create mode 100644 .dockerignore create mode 100644 pytox.pxd create mode 100644 pytox/.gitignore create mode 100644 pytox/error.pyx create mode 100644 pytox/src/log.pxd create mode 100644 pytox/src/log.pyx create mode 100644 pytox/src/memory.pxd create mode 100644 pytox/src/memory.pyx create mode 100644 pytox/src/network.pxd create mode 100644 pytox/src/network.pyx create mode 100644 pytox/src/options.pxd create mode 100644 pytox/src/options.pyx create mode 100644 pytox/src/random.pxd create mode 100644 pytox/src/random.pyx create mode 100644 pytox/src/system.pxd create mode 100644 pytox/src/system.pyx create mode 100644 pytox/src/time.pxd create mode 100644 pytox/src/time.pyx diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d7f7a42 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.mypy_cache diff --git a/BUILD.bazel b/BUILD.bazel index e4b10db..961de80 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -5,23 +5,59 @@ package(features = ["layering_check"]) project() +SUBSYSTEMS = [ + "log", + "memory", + "network", + "options", + "random", + "system", + "time", +] + +[genrule( + name = "pytox/" + sys, + srcs = [ + "pytox/src/%s.pxd" % sys, + "pytox/src/%s.pyx" % sys, + "//c-toxcore:public", + ], + outs = [ + "pytox/%s.pxd" % sys, + "pytox/%s.pyx" % sys, + ], + cmd = " ".join([ + "$(location //py_toxcore_c/tools:gen_api)", + "$(location pytox/src/%s.pyx)" % sys, + "$(GENDIR)/c-toxcore", + "> $(location pytox/%s.pyx);" % sys, + "$(location //py_toxcore_c/tools:gen_api)", + "$(location pytox/src/%s.pxd)" % sys, + "$(GENDIR)/c-toxcore", + "> $(location pytox/%s.pxd)" % sys, + ]), + tools = ["//py_toxcore_c/tools:gen_api"], +) for sys in SUBSYSTEMS] + genrule( name = "pytox/core", srcs = [ "pytox/src/core.pyx", - "//c-toxcore:tox/tox.h", + "//c-toxcore:tox/toxcore/tox.h", ], outs = ["pytox/core.pyx"], - cmd = "$(location //py_toxcore_c/tools:gen_api) $(location pytox/src/core.pyx) $(location //c-toxcore:tox/tox.h) > $@", + cmd = "$(location //py_toxcore_c/tools:gen_api) $(location pytox/src/core.pyx) $(GENDIR)/c-toxcore > $@", tools = ["//py_toxcore_c/tools:gen_api"], ) pyx_library( name = "pytox", srcs = [ + "pytox.pxd", "pytox/av.pyx", "pytox/core.pyx", - ], + "pytox/error.pyx", + ] + ["pytox/%s.pxd" % sys for sys in SUBSYSTEMS] + ["pytox/%s.pyx" % sys for sys in SUBSYSTEMS], cdeps = ["//c-toxcore"], cython_directives = {"language_level": "3"}, visibility = ["//visibility:public"], diff --git a/Dockerfile b/Dockerfile index fdde7fd..f62af11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 LABEL maintainer="iphydf@gmail.com" RUN apt-get update \ && DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y \ ca-certificates \ cmake \ - cython \ + cython3 \ gcc \ g++ \ git \ @@ -22,20 +22,40 @@ RUN apt-get update \ && pip3 install mypy WORKDIR /build -RUN git clone --depth=1 https://github.com/TokTok/c-toxcore /build/c-toxcore \ +RUN git clone --recursive --depth=1 --branch=system https://github.com/iphydf/c-toxcore /build/c-toxcore \ && cmake -GNinja -B/build/c-toxcore/_build -H/build/c-toxcore \ -DBOOTSTRAP_DAEMON=OFF \ -DENABLE_STATIC=OFF \ -DMUST_BUILD_TOXAV=ON \ && cmake --build /build/c-toxcore/_build --target install --parallel "$(nproc)" \ - && ldconfig + && ldconfig && echo 0 -COPY pytox /build/pytox +# Tools first, they change less. COPY tools /build/tools +COPY pytox.pxd /build/ +COPY pytox /build/pytox RUN mypy --strict tools/gen_api.py \ - && tools/gen_api.py pytox/src/core.pyx /usr/local/include/tox/tox.h > pytox/core.pyx \ - && cython pytox/av.pyx pytox/core.pyx + && tools/gen_api.py pytox/src/core.pyx /usr/local/include > pytox/core.pyx \ + && tools/gen_api.py pytox/src/log.pxd /usr/local/include > pytox/log.pxd \ + && tools/gen_api.py pytox/src/log.pyx /usr/local/include > pytox/log.pyx \ + && tools/gen_api.py pytox/src/memory.pxd /usr/local/include > pytox/memory.pxd \ + && tools/gen_api.py pytox/src/memory.pyx /usr/local/include > pytox/memory.pyx \ + && tools/gen_api.py pytox/src/network.pxd /usr/local/include > pytox/network.pxd \ + && tools/gen_api.py pytox/src/network.pyx /usr/local/include > pytox/network.pyx \ + && tools/gen_api.py pytox/src/options.pxd /usr/local/include > pytox/options.pxd \ + && tools/gen_api.py pytox/src/options.pyx /usr/local/include > pytox/options.pyx \ + && tools/gen_api.py pytox/src/system.pxd /usr/local/include > pytox/system.pxd \ + && tools/gen_api.py pytox/src/system.pyx /usr/local/include > pytox/system.pyx \ + && tools/gen_api.py pytox/src/time.pxd /usr/local/include > pytox/time.pxd \ + && tools/gen_api.py pytox/src/time.pyx /usr/local/include > pytox/time.pyx \ + && cat pytox/options.pxd && cython3 -I $PWD -X "language_level=3" --line-directives pytox/av.pyx pytox/core.pyx \ + pytox/log.pyx \ + pytox/memory.pyx \ + pytox/network.pyx \ + pytox/options.pyx \ + pytox/system.pyx \ + pytox/time.pyx COPY setup.py /build/ RUN python3 setup.py install \ diff --git a/pytox.pxd b/pytox.pxd new file mode 100644 index 0000000..e69de29 diff --git a/pytox/.gitignore b/pytox/.gitignore new file mode 100644 index 0000000..9a7ca08 --- /dev/null +++ b/pytox/.gitignore @@ -0,0 +1,14 @@ +/core.pxd +/core.pyx +/log.pxd +/log.pyx +/memory.pxd +/memory.pyx +/network.pxd +/network.pyx +/random.pxd +/random.pyx +/system.pxd +/system.pyx +/time.pxd +/time.pyx diff --git a/pytox/error.pyx b/pytox/error.pyx new file mode 100644 index 0000000..cd8fff7 --- /dev/null +++ b/pytox/error.pyx @@ -0,0 +1,15 @@ +class UseAfterFreeException(Exception): + def __init__(self): + super().__init__( + "object used after it was killed/freed (or it was never initialised)") + +class ToxException(Exception): + pass + +class ApiException(ToxException): + def __init__(self, err): + super().__init__(err) + self.error = err + +class LengthException(ToxException): + pass diff --git a/pytox/src/core.pyx b/pytox/src/core.pyx index c9894e1..e0324a2 100644 --- a/pytox/src/core.pyx +++ b/pytox/src/core.pyx @@ -6,7 +6,11 @@ from libcpp cimport bool from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t from libc.stdlib cimport malloc, free -cdef extern from "tox/tox.h": pass +from pytox import error +from pytox.options cimport Tox_Options, ToxOptions +from pytox.system cimport Tox_System + +cdef extern from "tox/toxcore/tox.h": pass VERSION: str = "%d.%d.%d" % (tox_version_major(), tox_version_minor(), tox_version_patch()) @@ -28,72 +32,22 @@ MAX_FILENAME_LENGTH: int = tox_max_filename_length() MAX_HOSTNAME_LENGTH: int = tox_max_hostname_length() -class UseAfterFreeException(Exception): - def __init__(self): - super().__init__( - "object used after it was killed/freed (or it was never initialised)") - -class ToxException(Exception): - pass - -class ApiException(ToxException): - def __init__(self, err): - super().__init__(err) - self.error = err - -class LengthException(ToxException): - pass - cdef void _check_len(str name, bytes data, int expected_length) except *: if len(data) != expected_length: - raise LengthException( + raise error.LengthException( f"parameter '{name}' received bytes of invalid" f"length {len(data)}, expected {expected_length}") - -cdef class ToxOptions: - - cdef Tox_Options *_ptr - - def __init__(self): - cdef Tox_Err_Options_New err = TOX_ERR_OPTIONS_NEW_OK - self._ptr = tox_options_new(&err) - if err: raise ApiException(Tox_Err_Options_New(err)) - - def __dealloc__(self): - self.__exit__(None, None, None) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, exc_traceback): - tox_options_free(self._ptr) - self._ptr = NULL - - cdef Tox_Options *_get(self) except *: - if self._ptr is NULL: - raise UseAfterFreeException() - return self._ptr - - @property - def ipv6_enabled(self) -> bool: - return tox_options_get_ipv6_enabled(self._get()) - - @ipv6_enabled.setter - def ipv6_enabled(self, value: bool): - tox_options_set_ipv6_enabled(self._get(), value) - - cdef class Core: cdef Tox *_ptr def __init__(self, options: ToxOptions = None): - cdef Tox_Err_New err = TOX_ERR_NEW_OK + err = TOX_ERR_NEW_OK if options is None: options = ToxOptions() - self._ptr = tox_new(options._ptr, &err) - if err: raise ApiException(Tox_Err_New(err)) + self._ptr = tox_new(options._get(), &err) + if err: raise error.ApiException(Tox_Err_New(err)) install_handlers(self._get()) @@ -109,13 +63,13 @@ cdef class Core: cdef Tox *_get(self) except *: if self._ptr is NULL: - raise UseAfterFreeException() + raise error.UseAfterFreeException() return self._ptr @property def savedata(self) -> bytes: - cdef size_t size = tox_get_savedata_size(self._get()) - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_get_savedata_size(self._get()) + data = malloc(size * sizeof(uint8_t)) try: tox_get_savedata(self._get(), data) return data[:size] @@ -124,7 +78,7 @@ cdef class Core: def bootstrap(self, host: str, port: int, public_key: bytes) -> bool: _check_len("public_key", public_key, tox_public_key_size()) - cdef Tox_Err_Bootstrap err = TOX_ERR_BOOTSTRAP_OK + err = TOX_ERR_BOOTSTRAP_OK return tox_bootstrap(self._get(), host.encode("utf-8"), port, public_key, &err) @property @@ -143,8 +97,8 @@ cdef class Core: @property def address(self) -> bytes: - cdef size_t size = tox_address_size() - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_address_size() + data = malloc(size * sizeof(uint8_t)) try: tox_self_get_address(self._get(), data) return data[:size] @@ -157,8 +111,8 @@ cdef class Core: @property def public_key(self) -> bytes: - cdef size_t size = tox_public_key_size() - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_public_key_size() + data = malloc(size * sizeof(uint8_t)) try: tox_self_get_public_key(self._get(), data) return data[:tox_public_key_size()] @@ -167,8 +121,8 @@ cdef class Core: @property def secret_key(self) -> bytes: - cdef size_t size = tox_secret_key_size() - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_secret_key_size() + data = malloc(size * sizeof(uint8_t)) try: tox_self_get_secret_key(self._get(), data) return data[:tox_secret_key_size()] @@ -177,8 +131,8 @@ cdef class Core: @property def name(self) -> bytes: - cdef size_t size = tox_self_get_name_size(self._get()) - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_self_get_name_size(self._get()) + data = malloc(size * sizeof(uint8_t)) try: tox_self_get_name(self._get(), data) return data[:size] @@ -187,14 +141,14 @@ cdef class Core: @name.setter def name(self, name: bytes) -> None: - cdef Tox_Err_Set_Info err = TOX_ERR_SET_INFO_OK + err = TOX_ERR_SET_INFO_OK tox_self_set_name(self._get(), name, len(name), &err) - if err: raise ApiException(Tox_Err_Set_Info(err)) + if err: raise error.ApiException(Tox_Err_Set_Info(err)) @property def status_message(self) -> bytes: - cdef size_t size = tox_self_get_status_message_size(self._get()) - cdef uint8_t *data = malloc(size * sizeof(uint8_t)) + size = tox_self_get_status_message_size(self._get()) + data = malloc(size * sizeof(uint8_t)) try: tox_self_get_status_message(self._get(), data) return data[:size] @@ -203,9 +157,9 @@ cdef class Core: @status_message.setter def status_message(self, status_message: bytes) -> None: - cdef Tox_Err_Set_Info err = TOX_ERR_SET_INFO_OK + err = TOX_ERR_SET_INFO_OK tox_self_set_status_message(self._get(), status_message, len(status_message), &err) - if err: raise ApiException(Tox_Err_Set_Info(err)) + if err: raise error.ApiException(Tox_Err_Set_Info(err)) @property def status(self) -> Tox_User_Status: @@ -217,17 +171,17 @@ cdef class Core: def friend_add(self, address: bytes, message: bytes): _check_len("address", address, tox_address_size()) - cdef Tox_Err_Friend_Add err = TOX_ERR_FRIEND_ADD_OK + err = TOX_ERR_FRIEND_ADD_OK tox_friend_add(self._get(), address, message, len(message), &err) - if err: raise ApiException(Tox_Err_Friend_Add(err)) + if err: raise error.ApiException(Tox_Err_Friend_Add(err)) def friend_add_norequest(self, public_key: bytes): _check_len("public_key", public_key, tox_public_key_size()) - cdef Tox_Err_Friend_Add err = TOX_ERR_FRIEND_ADD_OK + err = TOX_ERR_FRIEND_ADD_OK tox_friend_add_norequest(self._get(), public_key, &err) - if err: raise ApiException(Tox_Err_Friend_Add(err)) + if err: raise error.ApiException(Tox_Err_Friend_Add(err)) def friend_delete(self, friend_number: int): - cdef Tox_Err_Friend_Delete err = TOX_ERR_FRIEND_DELETE_OK + err = TOX_ERR_FRIEND_DELETE_OK tox_friend_delete(self._get(), friend_number, &err) - if err: raise ApiException(Tox_Err_Friend_Delete(err)) + if err: raise error.ApiException(Tox_Err_Friend_Delete(err)) diff --git a/pytox/src/log.pxd b/pytox/src/log.pxd new file mode 100644 index 0000000..5ee8000 --- /dev/null +++ b/pytox/src/log.pxd @@ -0,0 +1,18 @@ +# cython: language_level=3 +cdef extern from "tox/toxcore/tox_log.h": + ctypedef struct Tox_Log + + cpdef enum Tox_Log_Level: + TOX_LOG_LEVEL_TRACE, + TOX_LOG_LEVEL_DEBUG, + TOX_LOG_LEVEL_INFO, + TOX_LOG_LEVEL_WARNING, + TOX_LOG_LEVEL_ERROR, + +cdef extern from "tox/toxcore/os_log.h": pass + +cdef class ToxLog: + + cdef Tox_Log *_ptr + + cdef Tox_Log *_get(self) except * diff --git a/pytox/src/log.pyx b/pytox/src/log.pyx new file mode 100644 index 0000000..9df476a --- /dev/null +++ b/pytox/src/log.pyx @@ -0,0 +1,35 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox cimport memory +from pytox.memory cimport Tox_Memory + +cdef extern from "tox/toxcore/tox_log.h": pass + + +cdef class ToxLog: + """Logger.""" + + def __init__(self, memory.ToxMemory mem): + self._ptr = tox_log_new(NULL, self, mem._get()) + if self._ptr is NULL: raise MemoryError("tox_log_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_log_free(self._ptr) + self._ptr = NULL + + cdef Tox_Log *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/pytox/src/memory.pxd b/pytox/src/memory.pxd new file mode 100644 index 0000000..666bfac --- /dev/null +++ b/pytox/src/memory.pxd @@ -0,0 +1,10 @@ +cdef extern from "tox/toxcore/tox_memory.h": + ctypedef struct Tox_Memory + +cdef extern from "tox/toxcore/os_memory.h": pass + +cdef class ToxMemory: + + cdef Tox_Memory *_ptr + + cdef Tox_Memory *_get(self) except * diff --git a/pytox/src/memory.pyx b/pytox/src/memory.pyx new file mode 100644 index 0000000..3fa59c4 --- /dev/null +++ b/pytox/src/memory.pyx @@ -0,0 +1,32 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error + +cdef extern from "tox/toxcore/tox_memory.h": pass + + +cdef class ToxMemory: + + def __init__(self): + self._ptr = tox_memory_new(NULL, self) + if self._ptr is NULL: raise MemoryError("tox_memory_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_memory_free(self._ptr) + self._ptr = NULL + + cdef Tox_Memory *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/pytox/src/network.pxd b/pytox/src/network.pxd new file mode 100644 index 0000000..4fb4a78 --- /dev/null +++ b/pytox/src/network.pxd @@ -0,0 +1,10 @@ +cdef extern from "tox/toxcore/tox_network.h": + ctypedef struct Tox_Network + +cdef extern from "tox/toxcore/os_network.h": pass + +cdef class ToxNetwork: + + cdef Tox_Network *_ptr + + cdef Tox_Network *_get(self) except * diff --git a/pytox/src/network.pyx b/pytox/src/network.pyx new file mode 100644 index 0000000..6d6dbf8 --- /dev/null +++ b/pytox/src/network.pyx @@ -0,0 +1,34 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox cimport memory +from pytox.memory cimport Tox_Memory + +cdef extern from "tox/toxcore/tox_network.h": pass + + +cdef class ToxNetwork: + + def __init__(self, memory.ToxMemory mem): + self._ptr = tox_network_new(NULL, self, mem._get()) + if self._ptr is NULL: raise MemoryError("tox_network_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_network_free(self._ptr) + self._ptr = NULL + + cdef Tox_Network *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/pytox/src/options.pxd b/pytox/src/options.pxd new file mode 100644 index 0000000..7161fd7 --- /dev/null +++ b/pytox/src/options.pxd @@ -0,0 +1,8 @@ +cdef extern from "tox/toxcore/tox_options.h": + cdef struct Tox_Options + +cdef class ToxOptions: + + cdef Tox_Options *_ptr + + cdef Tox_Options *_get(self) except * diff --git a/pytox/src/options.pyx b/pytox/src/options.pyx new file mode 100644 index 0000000..81077dd --- /dev/null +++ b/pytox/src/options.pyx @@ -0,0 +1,45 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox.log cimport Tox_Log_Level +from pytox.system cimport Tox_System + +cdef extern from "tox/toxcore/tox_options.h": pass + + +cdef class ToxOptions: + + def __init__(self): + err = TOX_ERR_OPTIONS_NEW_OK + self._ptr = tox_options_new(&err) + if err: raise error.ApiException(Tox_Err_Options_New(err)) + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_options_free(self._ptr) + self._ptr = NULL + + cdef Tox_Options *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr + + @property + def ipv6_enabled(self) -> bool: + return tox_options_get_ipv6_enabled(self._get()) + + @ipv6_enabled.setter + def ipv6_enabled(self, value: bool): + tox_options_set_ipv6_enabled(self._get(), value) + + diff --git a/pytox/src/random.pxd b/pytox/src/random.pxd new file mode 100644 index 0000000..99cad73 --- /dev/null +++ b/pytox/src/random.pxd @@ -0,0 +1,10 @@ +cdef extern from "tox/toxcore/tox_random.h": + ctypedef struct Tox_Random + +cdef extern from "tox/toxcore/os_random.h": pass + +cdef class ToxRandom: + + cdef Tox_Random *_ptr + + cdef Tox_Random *_get(self) except * diff --git a/pytox/src/random.pyx b/pytox/src/random.pyx new file mode 100644 index 0000000..2e830e4 --- /dev/null +++ b/pytox/src/random.pyx @@ -0,0 +1,34 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox cimport memory +from pytox.memory cimport Tox_Memory + +cdef extern from "tox/toxcore/tox_random.h": pass + + +cdef class ToxRandom: + + def __init__(self, memory.ToxMemory mem): + self._ptr = tox_random_new(NULL, self, mem._get()) + if self._ptr is NULL: raise MemoryError("tox_random_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_random_free(self._ptr) + self._ptr = NULL + + cdef Tox_Random *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/pytox/src/system.pxd b/pytox/src/system.pxd new file mode 100644 index 0000000..35039f4 --- /dev/null +++ b/pytox/src/system.pxd @@ -0,0 +1,16 @@ +from pytox.log cimport Tox_Log +from pytox.memory cimport Tox_Memory +from pytox.network cimport Tox_Network +from pytox.random cimport Tox_Random +from pytox.time cimport Tox_Time + +cdef extern from "tox/toxcore/tox_system.h": + ctypedef struct Tox_System + +cdef extern from "tox/toxcore/os_system.h": pass + +cdef class ToxSystem: + + cdef Tox_System *_ptr + + cdef Tox_System *_get(self) except * diff --git a/pytox/src/system.pyx b/pytox/src/system.pyx new file mode 100644 index 0000000..43be6ef --- /dev/null +++ b/pytox/src/system.pyx @@ -0,0 +1,43 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox.log cimport Tox_Log +from pytox.memory cimport Tox_Memory +from pytox.network cimport Tox_Network +from pytox.random cimport Tox_Random +from pytox.time cimport Tox_Time + +cdef extern from "tox/toxcore/tox_system.h": pass + + +cdef class ToxSystem: + + def __init__(self): + sys = os_system() + self._ptr = tox_system_new( + tox_system_get_log(sys), + tox_system_get_memory(sys), + tox_system_get_network(sys), + tox_system_get_random(sys), + tox_system_get_time(sys)) + if self._ptr is NULL: raise MemoryError("tox_system_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_system_free(self._ptr) + self._ptr = NULL + + cdef Tox_System *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/pytox/src/time.pxd b/pytox/src/time.pxd new file mode 100644 index 0000000..07f9841 --- /dev/null +++ b/pytox/src/time.pxd @@ -0,0 +1,8 @@ +cdef extern from "tox/toxcore/tox_time.h": + ctypedef struct Tox_Time + +cdef class ToxTime: + + cdef Tox_Time *_ptr + + cdef Tox_Time *_get(self) except * diff --git a/pytox/src/time.pyx b/pytox/src/time.pyx new file mode 100644 index 0000000..5f5fb31 --- /dev/null +++ b/pytox/src/time.pyx @@ -0,0 +1,34 @@ +# cython: language_level=3 +"""Toxcore bindings. +""" + +from libcpp cimport bool +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t + +from pytox import error +from pytox cimport memory +from pytox.memory cimport Tox_Memory + +cdef extern from "tox/toxcore/tox_time.h": pass + + +cdef class ToxTime: + + def __init__(self, memory.ToxMemory mem): + self._ptr = tox_time_new(NULL, self, mem._get()) + if self._ptr is NULL: raise MemoryError("tox_time_new") + + def __dealloc__(self): + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + tox_time_free(self._ptr) + self._ptr = NULL + + cdef Tox_Time *_get(self) except *: + if self._ptr is NULL: + raise error.UseAfterFreeException() + return self._ptr diff --git a/test/core_test.py b/test/core_test.py index ca272fd..dc3d648 100644 --- a/test/core_test.py +++ b/test/core_test.py @@ -1,6 +1,8 @@ import unittest from pytox import core +from pytox import error +from pytox import options class CoreTest(unittest.TestCase): @@ -8,25 +10,25 @@ def test_version(self): self.assertEqual(len(core.VERSION.split(".")), 3) def test_options(self): - opts = core.ToxOptions() + opts = options.ToxOptions() self.assertTrue(opts.ipv6_enabled) opts.ipv6_enabled = False self.assertFalse(opts.ipv6_enabled) def test_use_after_free(self): - opts = core.ToxOptions() - with self.assertRaises(core.UseAfterFreeException): + opts = options.ToxOptions() + with self.assertRaises(error.UseAfterFreeException): with core.Core(opts) as tox: saved_tox = tox print(saved_tox.address) def test_address(self): - opts = core.ToxOptions() + opts = options.ToxOptions() with core.Core(opts) as tox: self.assertEqual(tox.address, tox.address) def test_public_key_is_address_prefix(self): - opts = core.ToxOptions() + opts = options.ToxOptions() with core.Core(opts) as tox: self.assertEqual( tox.public_key.hex()[:72] + format(tox.nospam, "08x"), @@ -34,7 +36,7 @@ def test_public_key_is_address_prefix(self): ) def test_public_key_is_not_secret_key(self): - opts = core.ToxOptions() + opts = options.ToxOptions() with core.Core(opts) as tox: self.assertNotEqual(tox.public_key, tox.secret_key) @@ -49,7 +51,7 @@ def test_set_name(self): self.assertEqual(tox.name, b"iphy") tox.name = b"x" * core.MAX_NAME_LENGTH - with self.assertRaises(core.ApiException): + with self.assertRaises(error.ApiException): tox.name = b"x" * (core.MAX_NAME_LENGTH + 1) def test_set_status_message(self): @@ -59,7 +61,7 @@ def test_set_status_message(self): self.assertEqual(tox.status_message, b"pytox is cool now") tox.status_message = b"x" * core.MAX_STATUS_MESSAGE_LENGTH - with self.assertRaises(core.ApiException): + with self.assertRaises(error.ApiException): tox.status_message = b"x" * (core.MAX_STATUS_MESSAGE_LENGTH + 1) @@ -76,9 +78,9 @@ def test_friend_add(self): with core.Core() as tox2: tox1.friend_add(tox2.address, b"hello there!") tox2.friend_add_norequest(tox1.public_key) - with self.assertRaises(core.LengthException): + with self.assertRaises(error.LengthException): tox2.friend_add_norequest(tox1.address) - with self.assertRaises(core.LengthException): + with self.assertRaises(error.LengthException): tox2.friend_add(tox1.public_key, b"oh no!") def test_friend_delete(self): @@ -86,7 +88,7 @@ def test_friend_delete(self): with core.Core() as tox2: tox1.friend_add(tox2.address, b"hello there!") tox1.friend_delete(0) - with self.assertRaises(core.ApiException): + with self.assertRaises(error.ApiException): # Deleting again: we don't have that friend anymore. tox1.friend_delete(0) diff --git a/test/tox_options_test.py b/test/tox_options_test.py index 6659b15..8a9dcd5 100644 --- a/test/tox_options_test.py +++ b/test/tox_options_test.py @@ -1,19 +1,21 @@ import unittest from pytox import core +from pytox import error +from pytox import options class ToxOptionsTest(unittest.TestCase): def test_options(self): - opts = core.ToxOptions() + opts = options.ToxOptions() self.assertTrue(opts.ipv6_enabled) opts.ipv6_enabled = False self.assertFalse(opts.ipv6_enabled) def test_use_after_free(self): - with core.ToxOptions() as opts: + with options.ToxOptions() as opts: saved_opts = opts - with self.assertRaises(core.UseAfterFreeException): + with self.assertRaises(error.UseAfterFreeException): print(saved_opts.ipv6_enabled) diff --git a/tools/gen_api.py b/tools/gen_api.py index d3f5f83..561a824 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -76,6 +76,7 @@ def parse_params(tokens: Tuple[str, ...]) -> List[Tuple[List[str], str]]: def finalize_handler(type_prefix: str, handlers: List[str], event: str, params: List[str]) -> None: + #handlers[-1] += " noexcept" handlers[-1] += ":" self = "" array = "" @@ -114,6 +115,12 @@ def handle_ignore(tokens: Sequence[str], state: List[str]) -> bool: return False +def handle_nullable(tokens: Sequence[str], state: List[str]) -> bool: + if tokens[0] in ("non_null", "nullable"): + return True # skip the entire line + return False + + def handle_macro(tokens: Sequence[str], state: List[str]) -> bool: if tokens[0] == "#define": if tokens[-1] == "\\": @@ -141,11 +148,15 @@ def handle_types(tokens: Sequence[str], state: List[str], extern: List[str], # enums if (tokens[:2] == ("typedef", "enum") or tokens[0] == "enum") and tokens[-1] == "{": - extern.append("") - extern.append(f" cpdef enum {tokens[-2]}:") + enum_name = tokens[-2] + if enum_name != "Tox_Log_Level": + extern.append("") + extern.append(f" cpdef enum {enum_name}:") state.append("enum") elif state[-1] == "enum" and tokens[0].startswith(const_prefix): - extern.append(f" {tokens[0]},") + enumerator_name = tokens[0] + if not enumerator_name.startswith("TOX_LOG_LEVEL"): + extern.append(f" {enumerator_name},") return False @@ -207,7 +218,7 @@ def handle_functions( return event -def gen_cython(lines: Sequence[str], fun_prefix: str) -> List[str]: +def gen_cython(lines: Sequence[str], fun_prefix: str, extern_line: str) -> List[str]: const_prefix = fun_prefix.upper() type_prefix = fun_prefix.capitalize() @@ -215,11 +226,11 @@ def gen_cython(lines: Sequence[str], fun_prefix: str) -> List[str]: extern = [] handlers: List[str] = [] - install_handlers = ["cdef void install_handlers(Tox *ptr):"] + install_handlers: List[str] = [] params: List[str] = [] event = "" - extern.append('cdef extern from "tox/tox.h":') + extern.append(extern_line) for line in lines: line = line.strip() tokens = tokenize(line) @@ -233,6 +244,9 @@ def gen_cython(lines: Sequence[str], fun_prefix: str) -> List[str]: if handle_ignore(tokens, state): continue + if handle_nullable(tokens, state): + continue + if handle_macro(tokens, state): continue @@ -263,22 +277,38 @@ def gen_cython(lines: Sequence[str], fun_prefix: str) -> List[str]: install_handlers, ) + if install_handlers: + install_handlers = ( + ["cdef void install_handlers(Tox *ptr):"] + install_handlers) return extern + [""] + handlers + [""] + install_handlers +def get_fun_prefix(api: str) -> str: + return os.path.split(api)[-1].split(".")[0].split("_")[0] + "_" + + def main() -> None: src = sys.argv[1] - api = sys.argv[2] + api_base = sys.argv[2] + + cdef_extern_prefix = "cdef extern from \"" + cdef_extern_suffix = "\": pass\n" with open(src, "r", encoding="utf-8") as src_fh: for line in src_fh.readlines(): - if line.startswith("cdef extern from"): + if (line.startswith(cdef_extern_prefix) and + line.endswith(cdef_extern_suffix)): + api_file = (line + .removeprefix(cdef_extern_prefix) + .removesuffix(cdef_extern_suffix)) + api = os.path.join(api_base, api_file) + extern_line = line.removesuffix(" pass\n") with open(api, "r", encoding="utf-8") as api_fh: print("\n".join( gen_cython( api_fh.readlines(), - fun_prefix=os.path.split(api)[-1].split(".")[0] + - "_", + fun_prefix=get_fun_prefix(api), + extern_line=extern_line, ))) else: print(line.rstrip()) From 0db706675b8804fe510c17c77b905a2d74dd6932 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:21 +0000 Subject: [PATCH 2/7] Restyled by autopep8 --- tools/gen_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/gen_api.py b/tools/gen_api.py index 561a824..0f2bd9a 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -279,7 +279,7 @@ def gen_cython(lines: Sequence[str], fun_prefix: str, extern_line: str) -> List[ if install_handlers: install_handlers = ( - ["cdef void install_handlers(Tox *ptr):"] + install_handlers) + ["cdef void install_handlers(Tox *ptr):"] + install_handlers) return extern + [""] + handlers + [""] + install_handlers @@ -299,8 +299,8 @@ def main() -> None: if (line.startswith(cdef_extern_prefix) and line.endswith(cdef_extern_suffix)): api_file = (line - .removeprefix(cdef_extern_prefix) - .removesuffix(cdef_extern_suffix)) + .removeprefix(cdef_extern_prefix) + .removesuffix(cdef_extern_suffix)) api = os.path.join(api_base, api_file) extern_line = line.removesuffix(" pass\n") with open(api, "r", encoding="utf-8") as api_fh: From 889c7cf95b107b88952d7499270502a425953f7b Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:30 +0000 Subject: [PATCH 3/7] Restyled by black --- test/core_test.py | 3 +- tools/gen_api.py | 85 +++++++++++++++++++++++++---------------------- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/test/core_test.py b/test/core_test.py index dc3d648..0b453b7 100644 --- a/test/core_test.py +++ b/test/core_test.py @@ -62,8 +62,7 @@ def test_set_status_message(self): tox.status_message = b"x" * core.MAX_STATUS_MESSAGE_LENGTH with self.assertRaises(error.ApiException): - tox.status_message = b"x" * (core.MAX_STATUS_MESSAGE_LENGTH + - 1) + tox.status_message = b"x" * (core.MAX_STATUS_MESSAGE_LENGTH + 1) def test_set_status(self): with core.Core() as tox: diff --git a/tools/gen_api.py b/tools/gen_api.py index 0f2bd9a..2dce4b0 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -37,7 +37,7 @@ def needs_space(l: str, r: str) -> bool: def untokenize(tokens: Tuple[str, ...]) -> str: line = [] for i in range(len(tokens) - 1): - if tokens[i:i + 2] == ("void", ")"): + if tokens[i : i + 2] == ("void", ")"): break line.append(tokens[i]) if needs_space(tokens[i], tokens[i + 1]): @@ -74,9 +74,10 @@ def parse_params(tokens: Tuple[str, ...]) -> List[Tuple[List[str], str]]: return params -def finalize_handler(type_prefix: str, handlers: List[str], event: str, - params: List[str]) -> None: - #handlers[-1] += " noexcept" +def finalize_handler( + type_prefix: str, handlers: List[str], event: str, params: List[str] +) -> None: + # handlers[-1] += " noexcept" handlers[-1] += ":" self = "" array = "" @@ -133,8 +134,9 @@ def handle_macro(tokens: Sequence[str], state: List[str]) -> bool: return False -def handle_types(tokens: Sequence[str], state: List[str], extern: List[str], - const_prefix: str) -> bool: +def handle_types( + tokens: Sequence[str], state: List[str], extern: List[str], const_prefix: str +) -> bool: # struct definitions (members are ignored) if tokens[0] == "struct" and tokens[-1] == "{": state.append("struct") @@ -146,8 +148,7 @@ def handle_types(tokens: Sequence[str], state: List[str], extern: List[str], extern.append(f" ctypedef struct {tokens[2]}") # enums - if (tokens[:2] == ("typedef", "enum") - or tokens[0] == "enum") and tokens[-1] == "{": + if (tokens[:2] == ("typedef", "enum") or tokens[0] == "enum") and tokens[-1] == "{": enum_name = tokens[-2] if enum_name != "Tox_Log_Level": extern.append("") @@ -162,20 +163,23 @@ def handle_types(tokens: Sequence[str], state: List[str], extern: List[str], def handle_functions( - tokens: Tuple[str, ...], - state: List[str], - extern: List[str], - fun_prefix: str, - type_prefix: str, - event: str, - params: List[str], - handlers: List[str], - install_handlers: List[str], + tokens: Tuple[str, ...], + state: List[str], + extern: List[str], + fun_prefix: str, + type_prefix: str, + event: str, + params: List[str], + handlers: List[str], + install_handlers: List[str], ) -> str: # functions and callbacks - if ("(" in tokens and tokens[0].isidentifier() - and token_before("(", tokens).startswith(fun_prefix) - and tokens[0] != "typedef"): + if ( + "(" in tokens + and tokens[0].isidentifier() + and token_before("(", tokens).startswith(fun_prefix) + and tokens[0] != "typedef" + ): extern.append(f" cdef {untokenize_fun(tokens)}") if ";" not in tokens: state.append("fun") @@ -183,17 +187,17 @@ def handle_functions( if tokens[:2] == ("typedef", "void"): extern.append(f" c{untokenize_fun(tokens)}") - event = tokens[2][len(fun_prefix):-3] + event = tokens[2][len(fun_prefix) : -3] params.clear() params.extend(tokens[3:]) # TODO(iphydf): Handle this better (by checking whether we have a callback install # function for this event). if event != "log": - handlers.append( - f"cdef void handle_{untokenize_fun((event,) + tokens[3:])}") + handlers.append(f"cdef void handle_{untokenize_fun((event,) + tokens[3:])}") install_handlers.append( - f" {fun_prefix}callback_{event}(ptr, handle_{event})") + f" {fun_prefix}callback_{event}(ptr, handle_{event})" + ) if ";" not in tokens: state.append("callback") else: @@ -278,8 +282,7 @@ def gen_cython(lines: Sequence[str], fun_prefix: str, extern_line: str) -> List[ ) if install_handlers: - install_handlers = ( - ["cdef void install_handlers(Tox *ptr):"] + install_handlers) + install_handlers = ["cdef void install_handlers(Tox *ptr):"] + install_handlers return extern + [""] + handlers + [""] + install_handlers @@ -291,25 +294,29 @@ def main() -> None: src = sys.argv[1] api_base = sys.argv[2] - cdef_extern_prefix = "cdef extern from \"" - cdef_extern_suffix = "\": pass\n" + cdef_extern_prefix = 'cdef extern from "' + cdef_extern_suffix = '": pass\n' with open(src, "r", encoding="utf-8") as src_fh: for line in src_fh.readlines(): - if (line.startswith(cdef_extern_prefix) and - line.endswith(cdef_extern_suffix)): - api_file = (line - .removeprefix(cdef_extern_prefix) - .removesuffix(cdef_extern_suffix)) + if line.startswith(cdef_extern_prefix) and line.endswith( + cdef_extern_suffix + ): + api_file = line.removeprefix(cdef_extern_prefix).removesuffix( + cdef_extern_suffix + ) api = os.path.join(api_base, api_file) extern_line = line.removesuffix(" pass\n") with open(api, "r", encoding="utf-8") as api_fh: - print("\n".join( - gen_cython( - api_fh.readlines(), - fun_prefix=get_fun_prefix(api), - extern_line=extern_line, - ))) + print( + "\n".join( + gen_cython( + api_fh.readlines(), + fun_prefix=get_fun_prefix(api), + extern_line=extern_line, + ) + ) + ) else: print(line.rstrip()) From f2e8b87d72da98c6b967b3bad23fad04a6c8d0cd Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:40 +0000 Subject: [PATCH 4/7] Restyled by isort --- test/core_test.py | 4 +--- test/tox_options_test.py | 4 +--- tools/gen_api.py | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/test/core_test.py b/test/core_test.py index 0b453b7..fb7de54 100644 --- a/test/core_test.py +++ b/test/core_test.py @@ -1,8 +1,6 @@ import unittest -from pytox import core -from pytox import error -from pytox import options +from pytox import core, error, options class CoreTest(unittest.TestCase): diff --git a/test/tox_options_test.py b/test/tox_options_test.py index 8a9dcd5..93bf211 100644 --- a/test/tox_options_test.py +++ b/test/tox_options_test.py @@ -1,8 +1,6 @@ import unittest -from pytox import core -from pytox import error -from pytox import options +from pytox import core, error, options class ToxOptionsTest(unittest.TestCase): diff --git a/tools/gen_api.py b/tools/gen_api.py index 2dce4b0..86fa03a 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 import os.path import sys -from typing import Iterable -from typing import List -from typing import Sequence -from typing import Tuple +from typing import Iterable, List, Sequence, Tuple def tokenize(line: str) -> Tuple[str, ...]: From 56b682b2597153c0456a3d936fd5035c1351a1b5 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:41 +0000 Subject: [PATCH 5/7] Restyled by reorder-python-imports --- test/core_test.py | 4 +++- test/tox_options_test.py | 4 +++- tools/gen_api.py | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/core_test.py b/test/core_test.py index fb7de54..0b453b7 100644 --- a/test/core_test.py +++ b/test/core_test.py @@ -1,6 +1,8 @@ import unittest -from pytox import core, error, options +from pytox import core +from pytox import error +from pytox import options class CoreTest(unittest.TestCase): diff --git a/test/tox_options_test.py b/test/tox_options_test.py index 93bf211..8a9dcd5 100644 --- a/test/tox_options_test.py +++ b/test/tox_options_test.py @@ -1,6 +1,8 @@ import unittest -from pytox import core, error, options +from pytox import core +from pytox import error +from pytox import options class ToxOptionsTest(unittest.TestCase): diff --git a/tools/gen_api.py b/tools/gen_api.py index 86fa03a..2dce4b0 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 import os.path import sys -from typing import Iterable, List, Sequence, Tuple +from typing import Iterable +from typing import List +from typing import Sequence +from typing import Tuple def tokenize(line: str) -> Tuple[str, ...]: From 2761466d8aa51f68efda588ff0fc46d82193c099 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:48 +0000 Subject: [PATCH 6/7] Restyled by whitespace --- pytox.pxd | 1 + pytox/src/options.pyx | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pytox.pxd b/pytox.pxd index e69de29..8b13789 100644 --- a/pytox.pxd +++ b/pytox.pxd @@ -0,0 +1 @@ + diff --git a/pytox/src/options.pyx b/pytox/src/options.pyx index 81077dd..5550170 100644 --- a/pytox/src/options.pyx +++ b/pytox/src/options.pyx @@ -41,5 +41,3 @@ cdef class ToxOptions: @ipv6_enabled.setter def ipv6_enabled(self, value: bool): tox_options_set_ipv6_enabled(self._get(), value) - - From 39274e0436c8e7af15c0a9327c224d3bdded0bd7 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 11 Sep 2023 07:42:51 +0000 Subject: [PATCH 7/7] Restyled by yapf --- test/core_test.py | 3 +- tools/gen_api.py | 77 +++++++++++++++++++++-------------------------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/test/core_test.py b/test/core_test.py index 0b453b7..dc3d648 100644 --- a/test/core_test.py +++ b/test/core_test.py @@ -62,7 +62,8 @@ def test_set_status_message(self): tox.status_message = b"x" * core.MAX_STATUS_MESSAGE_LENGTH with self.assertRaises(error.ApiException): - tox.status_message = b"x" * (core.MAX_STATUS_MESSAGE_LENGTH + 1) + tox.status_message = b"x" * (core.MAX_STATUS_MESSAGE_LENGTH + + 1) def test_set_status(self): with core.Core() as tox: diff --git a/tools/gen_api.py b/tools/gen_api.py index 2dce4b0..893ae4b 100755 --- a/tools/gen_api.py +++ b/tools/gen_api.py @@ -37,7 +37,7 @@ def needs_space(l: str, r: str) -> bool: def untokenize(tokens: Tuple[str, ...]) -> str: line = [] for i in range(len(tokens) - 1): - if tokens[i : i + 2] == ("void", ")"): + if tokens[i:i + 2] == ("void", ")"): break line.append(tokens[i]) if needs_space(tokens[i], tokens[i + 1]): @@ -74,9 +74,8 @@ def parse_params(tokens: Tuple[str, ...]) -> List[Tuple[List[str], str]]: return params -def finalize_handler( - type_prefix: str, handlers: List[str], event: str, params: List[str] -) -> None: +def finalize_handler(type_prefix: str, handlers: List[str], event: str, + params: List[str]) -> None: # handlers[-1] += " noexcept" handlers[-1] += ":" self = "" @@ -134,9 +133,8 @@ def handle_macro(tokens: Sequence[str], state: List[str]) -> bool: return False -def handle_types( - tokens: Sequence[str], state: List[str], extern: List[str], const_prefix: str -) -> bool: +def handle_types(tokens: Sequence[str], state: List[str], extern: List[str], + const_prefix: str) -> bool: # struct definitions (members are ignored) if tokens[0] == "struct" and tokens[-1] == "{": state.append("struct") @@ -148,7 +146,8 @@ def handle_types( extern.append(f" ctypedef struct {tokens[2]}") # enums - if (tokens[:2] == ("typedef", "enum") or tokens[0] == "enum") and tokens[-1] == "{": + if (tokens[:2] == ("typedef", "enum") + or tokens[0] == "enum") and tokens[-1] == "{": enum_name = tokens[-2] if enum_name != "Tox_Log_Level": extern.append("") @@ -163,23 +162,20 @@ def handle_types( def handle_functions( - tokens: Tuple[str, ...], - state: List[str], - extern: List[str], - fun_prefix: str, - type_prefix: str, - event: str, - params: List[str], - handlers: List[str], - install_handlers: List[str], + tokens: Tuple[str, ...], + state: List[str], + extern: List[str], + fun_prefix: str, + type_prefix: str, + event: str, + params: List[str], + handlers: List[str], + install_handlers: List[str], ) -> str: # functions and callbacks - if ( - "(" in tokens - and tokens[0].isidentifier() - and token_before("(", tokens).startswith(fun_prefix) - and tokens[0] != "typedef" - ): + if ("(" in tokens and tokens[0].isidentifier() + and token_before("(", tokens).startswith(fun_prefix) + and tokens[0] != "typedef"): extern.append(f" cdef {untokenize_fun(tokens)}") if ";" not in tokens: state.append("fun") @@ -187,17 +183,17 @@ def handle_functions( if tokens[:2] == ("typedef", "void"): extern.append(f" c{untokenize_fun(tokens)}") - event = tokens[2][len(fun_prefix) : -3] + event = tokens[2][len(fun_prefix):-3] params.clear() params.extend(tokens[3:]) # TODO(iphydf): Handle this better (by checking whether we have a callback install # function for this event). if event != "log": - handlers.append(f"cdef void handle_{untokenize_fun((event,) + tokens[3:])}") + handlers.append( + f"cdef void handle_{untokenize_fun((event,) + tokens[3:])}") install_handlers.append( - f" {fun_prefix}callback_{event}(ptr, handle_{event})" - ) + f" {fun_prefix}callback_{event}(ptr, handle_{event})") if ";" not in tokens: state.append("callback") else: @@ -222,7 +218,8 @@ def handle_functions( return event -def gen_cython(lines: Sequence[str], fun_prefix: str, extern_line: str) -> List[str]: +def gen_cython(lines: Sequence[str], fun_prefix: str, + extern_line: str) -> List[str]: const_prefix = fun_prefix.upper() type_prefix = fun_prefix.capitalize() @@ -282,7 +279,8 @@ def gen_cython(lines: Sequence[str], fun_prefix: str, extern_line: str) -> List[ ) if install_handlers: - install_handlers = ["cdef void install_handlers(Tox *ptr):"] + install_handlers + install_handlers = ["cdef void install_handlers(Tox *ptr):" + ] + install_handlers return extern + [""] + handlers + [""] + install_handlers @@ -300,23 +298,18 @@ def main() -> None: with open(src, "r", encoding="utf-8") as src_fh: for line in src_fh.readlines(): if line.startswith(cdef_extern_prefix) and line.endswith( - cdef_extern_suffix - ): + cdef_extern_suffix): api_file = line.removeprefix(cdef_extern_prefix).removesuffix( - cdef_extern_suffix - ) + cdef_extern_suffix) api = os.path.join(api_base, api_file) extern_line = line.removesuffix(" pass\n") with open(api, "r", encoding="utf-8") as api_fh: - print( - "\n".join( - gen_cython( - api_fh.readlines(), - fun_prefix=get_fun_prefix(api), - extern_line=extern_line, - ) - ) - ) + print("\n".join( + gen_cython( + api_fh.readlines(), + fun_prefix=get_fun_prefix(api), + extern_line=extern_line, + ))) else: print(line.rstrip())