diff --git a/source/wrappers/python/CMakeLists.txt b/source/wrappers/python/CMakeLists.txt index 72e22131..7eb9e53d 100755 --- a/source/wrappers/python/CMakeLists.txt +++ b/source/wrappers/python/CMakeLists.txt @@ -9,6 +9,11 @@ install( FILES ${INSTALL_FILES} DESTINATION python_installer/pyuda ) configure_file( ${CMAKE_CURRENT_LIST_DIR}/pyuda/_version.py.in ${CMAKE_CURRENT_BINARY_DIR}/pyuda/_version.py @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/pyuda/_version.py DESTINATION python_installer/pyuda ) +file( GLOB EXAMPLE_SCRIPTS examples/*.py ) +install( FILES ${EXAMPLE_SCRIPTS} DESTINATION python_installer/examples ) +file( GLOB DEV_TEST_SCRIPTS devscripts/*.py ) +install( FILES ${DEV_TEST_SCRIPTS} DESTINATION python_installer/devscripts ) + ### extra install steps for the dummy "uda" repo which will mirror "pyuda" on pypi set( DUMMY_REPO_FILES uda/README.md diff --git a/source/wrappers/python/examples/parallel_client_demo.py b/source/wrappers/python/examples/parallel_client_demo.py new file mode 100644 index 00000000..a486be21 --- /dev/null +++ b/source/wrappers/python/examples/parallel_client_demo.py @@ -0,0 +1,46 @@ +import pyuda +import multiprocessing as mp +from enum import Enum, auto +from collections import namedtuple + +ConnectionDetails = namedtuple('ConnectionDetails', ['name', 'port']) +RequestData = namedtuple('RequestData', ['signal', 'source']) +client = pyuda.Client() + +class Result(Enum): + SOME = auto() + ERROR = auto() + + +def task(queue: mp.Queue, server: ConnectionDetails, request: RequestData): + # move client instantiation here to avoid errors + # client = pyuda.Client() + pyuda.Client.port = server.port + pyuda.Client.server = server.name + + try: + result = client.get(*request) + queue.put((Result.SOME, result)) + except Exception as e: + queue.put((Result.ERROR, e)) + + +def main(): + server = ConnectionDetails("uda2.mast.l", 56565) + request = RequestData("help::help()", "") + + # No error if start method is "spawn" or if client is only + # instantiated in task (after fork) + mp.set_start_method('fork') + print("process start method is: " + mp.get_start_method()) + + results_q = mp.Queue() + p = mp.Process(target=task, args=(results_q, server, request)) + p.start() + item = results_q.get() + print(item) + p.join() + + +if __name__ == "__main__": + main() diff --git a/source/wrappers/python/pyuda/_client.py b/source/wrappers/python/pyuda/_client.py index d5632f14..4283f99d 100644 --- a/source/wrappers/python/pyuda/_client.py +++ b/source/wrappers/python/pyuda/_client.py @@ -66,6 +66,8 @@ class Client(with_metaclass(ClientMeta, object)): __metaclass__ = ClientMeta def __init__(self, debug_level=logging.ERROR): + cpyuda.set_pid() + self.version = __version__ assert self.version == cpyuda.get_build_version().decode(), "mismatching pyuda and c-library versions" diff --git a/source/wrappers/python/pyuda/cpyuda/client.pyx b/source/wrappers/python/pyuda/cpyuda/client.pyx index b0b1c218..22859bf4 100644 --- a/source/wrappers/python/pyuda/cpyuda/client.pyx +++ b/source/wrappers/python/pyuda/cpyuda/client.pyx @@ -1,5 +1,6 @@ #cython: language_level=3 +import os import numpy as np cimport uda cimport numpy as np @@ -9,6 +10,7 @@ from libc.stdlib cimport malloc, free from libc.string cimport strlen +_pid = None _properties = { "get_datadble": ("GET_DATA_DOUBLE", False), "get_dimdble": ("GET_DIM_DOUBLE", False), @@ -39,6 +41,12 @@ if PY_MAJOR_VERSION >= 3.0: else: Properties = type(b'Properties', (), {v[0]:k for k,v in _properties.items()}) + +def set_pid(): + global _pid + _pid = os.getpid() + + def set_property(prop_name, value): if prop_name.lower() not in _properties: raise ValueError('invalid property ' + prop_name) @@ -98,6 +106,8 @@ def get_server_uuid(): def get_data(signal, source): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") handle = uda.idamGetAPI(signal.encode(), source.encode()) cdef const char* err_msg cdef int err_code @@ -114,6 +124,8 @@ def get_data(signal, source): def get_data_batch(signals, sources): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") assert len(signals) == len(sources) cdef const char** signals_array = malloc(len(signals) * sizeof(char*)) cdef const char** sources_array = malloc(len(sources) * sizeof(char*)) @@ -301,6 +313,8 @@ cdef put_string(const char* instruction, const char* data): def put_data(instruction, data=None): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") if isinstance(data, np.ndarray): if np.PyArray_TYPE(data) not in (np.NPY_STRING, np.NPY_UNICODE): return put_ndarray(instruction, data)