| # |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License |
| # |
| |
| """Interface between python and libqpid-dispatch.so. |
| |
| This module contains python ctypes definitions to directly call functions in the |
| libqpid-dispatch.so library from python. |
| |
| The C library also adds the following C extension types to this module: |
| |
| - LogAdapter: Logs to the C logging system. |
| - IoAdapter: Receives messages from the router into python. |
| |
| This module also prevents the proton python module from being accidentally loaded. |
| """ |
| import builtins |
| import ctypes |
| import sys |
| from ctypes import c_char_p, c_long, py_object |
| |
| |
| class CError(Exception): |
| """Exception raised if there is an error in a C call""" |
| pass |
| |
| |
| class QdDll(ctypes.PyDLL): |
| """ |
| Load the library, set up function prototypes. |
| |
| NOTE: We use the python calling convention because the C library |
| internally makes python calls. |
| """ |
| |
| def __init__(self, handle): |
| super(QdDll, self).__init__("qpid-dispatch", handle=handle) |
| |
| # Types |
| self.qd_dispatch_p = ctypes.c_void_p |
| |
| # No check on qd_error_* functions, it would be recursive |
| self._prototype(self.qd_error_code, c_long, [], check=False) |
| self._prototype(self.qd_error_message, c_char_p, [], check=False) |
| self._prototype(self.qd_log_entity, c_long, [py_object]) |
| self._prototype(self.qd_dispatch_configure_router, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_prepare, None, [self.qd_dispatch_p]) |
| self._prototype(self.qd_dispatch_configure_listener, ctypes.c_void_p, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_connector, ctypes.c_void_p, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_ssl_profile, ctypes.c_void_p, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_sasl_plugin, ctypes.c_void_p, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_connection_manager_delete_listener, None, [self.qd_dispatch_p, ctypes.c_void_p]) |
| self._prototype(self.qd_connection_manager_delete_connector, None, [self.qd_dispatch_p, ctypes.c_void_p]) |
| self._prototype(self.qd_connection_manager_delete_ssl_profile, ctypes.c_bool, [self.qd_dispatch_p, ctypes.c_void_p]) |
| self._prototype(self.qd_connection_manager_delete_sasl_plugin, ctypes.c_bool, [self.qd_dispatch_p, ctypes.c_void_p]) |
| |
| self._prototype(self.qd_dispatch_configure_address, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_link_route, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_auto_link, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_exchange, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_configure_binding, None, [self.qd_dispatch_p, py_object]) |
| |
| self._prototype(self.qd_dispatch_configure_policy, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_register_policy_manager, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_policy_c_counts_alloc, c_long, [], check=False) |
| self._prototype(self.qd_dispatch_policy_c_counts_free, None, [c_long], check=False) |
| self._prototype(self.qd_dispatch_policy_c_counts_refresh, None, [c_long, py_object]) |
| self._prototype(self.qd_dispatch_policy_host_pattern_add, ctypes.c_bool, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_policy_host_pattern_remove, None, [self.qd_dispatch_p, py_object]) |
| self._prototype(self.qd_dispatch_policy_host_pattern_lookup, c_char_p, [self.qd_dispatch_p, py_object]) |
| |
| self._prototype(self.qd_dispatch_register_display_name_service, None, [self.qd_dispatch_p, py_object]) |
| |
| self._prototype(self.qd_dispatch_set_agent, None, [self.qd_dispatch_p, py_object]) |
| |
| self._prototype(self.qd_router_setup_late, None, [self.qd_dispatch_p]) |
| |
| self._prototype(self.qd_dispatch_router_lock, None, [self.qd_dispatch_p]) |
| self._prototype(self.qd_dispatch_router_unlock, None, [self.qd_dispatch_p]) |
| |
| self._prototype(self.qd_connection_manager_start, None, [self.qd_dispatch_p]) |
| self._prototype(self.qd_entity_refresh_begin, c_long, [py_object]) |
| self._prototype(self.qd_entity_refresh_end, None, []) |
| |
| self._prototype(self.qd_log_recent_py, py_object, [c_long]) |
| |
| def _prototype(self, f, restype, argtypes, check=True): |
| """Set up the return and argument types and the error checker for a |
| ctypes function""" |
| |
| def _do_check(result, func, args): |
| if check and self.qd_error_code(): |
| raise CError(self.qd_error_message()) |
| if restype is c_char_p and result: |
| # in python3 c_char_p returns a byte type for the error |
| # message. We need to convert that to a string |
| result = result.decode('utf-8') |
| return result |
| |
| f.restype = restype |
| f.argtypes = argtypes |
| f.errcheck = _do_check |
| return f |
| |
| def function(self, fname, restype, argtypes, check=True): |
| return self._prototype(getattr(self, fname), restype, argtypes, check) |
| |
| |
| # Prevent accidental loading of the proton python module inside dispatch. |
| # The proton-C library is linked with the dispatch C library, loading the proton |
| # python module loads a second copy of the library and mayhem ensues. |
| # |
| # Note the FORBIDDEN list is over-written to disable this tests in mock python |
| # testing code. |
| FORBIDDEN = ["proton"] |
| |
| |
| def check_forbidden(): |
| bad = set(FORBIDDEN) & set(sys.modules) |
| if bad: |
| raise ImportError("Forbidden modules loaded: '%s'." % "', '".join(bad)) |
| |
| |
| def import_check(name, *args, **kw): |
| if name in FORBIDDEN: |
| raise ImportError("Python code running inside a dispatch router cannot import '%s', use the 'dispatch' module for internal messaging" % name) |
| return builtin_import(name, *args, **kw) |
| |
| |
| check_forbidden() |
| |
| builtin_import = builtins.__import__ |
| builtins.__import__ = import_check |