blob: b6e5d5f38fdacea98c7003be7c5812ba8e447514 [file] [log] [blame]
#
# 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.
"""
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
import sys, ctypes
from ctypes import c_char_p, c_long, py_object
import qpid_dispatch_site
from .compat import IS_PY2
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 and not IS_PY2:
# 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()
if IS_PY2:
import __builtin__ as builtins
else:
import builtins
builtin_import = builtins.__import__
builtins.__import__ = import_check