1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
#
# 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.
#
from typing import Any, Callable, Optional, Union
from cproton import pn_incref, pn_decref, \
pn_py2void, pn_void2py, \
pn_record_get, pn_record_def, pn_record_set, \
PN_PYREF
from ._exceptions import ProtonException
class EmptyAttrs:
def __contains__(self, name):
return False
def __getitem__(self, name):
raise KeyError(name)
def __setitem__(self, name, value):
raise TypeError("does not support item assignment")
EMPTY_ATTRS = EmptyAttrs()
class Wrapper(object):
""" Wrapper for python objects that need to be stored in event contexts and be retrived again from them
Quick note on how this works:
The actual *python* object has only 3 attributes which redirect into the wrapped C objects:
_impl The wrapped C object itself
_attrs This is a special pn_record_t holding a PYCTX which is a python dict
every attribute in the python object is actually looked up here
_record This is the C record itself (so actually identical to _attrs really but
a different python type
Because the objects actual attributes are stored away they must be initialised *after* the wrapping
is set up. This is the purpose of the _init method in the wrapped object. Wrapper.__init__ will call
eht subclass _init to initialise attributes. So they *must not* be initialised in the subclass __init__
before calling the superclass (Wrapper) __init__ or they will not be accessible from the wrapper at all.
"""
def __init__(
self,
impl_or_constructor: Union[Any, Callable[[], Any]],
get_context: Optional[Callable[[Any], Any]] = None,
) -> None:
init = False
if callable(impl_or_constructor):
# we are constructing a new object
impl = impl_or_constructor()
if impl is None:
self.__dict__["_impl"] = impl
self.__dict__["_attrs"] = EMPTY_ATTRS
self.__dict__["_record"] = None
raise ProtonException(
"Wrapper failed to create wrapped object. Check for file descriptor or memory exhaustion.")
init = True
else:
# we are wrapping an existing object
impl = impl_or_constructor
pn_incref(impl)
if get_context:
record = get_context(impl)
attrs = pn_void2py(pn_record_get(record, PYCTX))
if attrs is None:
attrs = {}
pn_record_def(record, PYCTX, PN_PYREF)
pn_record_set(record, PYCTX, pn_py2void(attrs))
init = True
else:
attrs = EMPTY_ATTRS
init = False
record = None
self.__dict__["_impl"] = impl
self.__dict__["_attrs"] = attrs
self.__dict__["_record"] = record
if init:
self._init()
def __getattr__(self, name: str) -> Any:
attrs = self.__dict__["_attrs"]
if name in attrs:
return attrs[name]
else:
raise AttributeError(name + " not in _attrs")
def __setattr__(self, name: str, value: Any) -> None:
if hasattr(self.__class__, name):
object.__setattr__(self, name, value)
else:
attrs = self.__dict__["_attrs"]
attrs[name] = value
def __delattr__(self, name: str) -> None:
attrs = self.__dict__["_attrs"]
if attrs:
del attrs[name]
def __hash__(self) -> int:
return hash(addressof(self._impl))
def __eq__(self, other: Any) -> bool:
if isinstance(other, Wrapper):
return addressof(self._impl) == addressof(other._impl)
return False
def __ne__(self, other: Any) -> bool:
if isinstance(other, Wrapper):
return addressof(self._impl) != addressof(other._impl)
return True
def __del__(self) -> None:
pn_decref(self._impl)
def __repr__(self) -> str:
return '<%s.%s 0x%x ~ 0x%x>' % (self.__class__.__module__,
self.__class__.__name__,
id(self), addressof(self._impl))
PYCTX = int(pn_py2void(Wrapper))
addressof = int
|