| # |
| # 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 __future__ import absolute_import |
| |
| 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, get_context=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): |
| attrs = self.__dict__["_attrs"] |
| if name in attrs: |
| return attrs[name] |
| else: |
| raise AttributeError(name + " not in _attrs") |
| |
| def __setattr__(self, name, value): |
| if hasattr(self.__class__, name): |
| object.__setattr__(self, name, value) |
| else: |
| attrs = self.__dict__["_attrs"] |
| attrs[name] = value |
| |
| def __delattr__(self, name): |
| attrs = self.__dict__["_attrs"] |
| if attrs: |
| del attrs[name] |
| |
| def __hash__(self): |
| return hash(addressof(self._impl)) |
| |
| def __eq__(self, other): |
| if isinstance(other, Wrapper): |
| return addressof(self._impl) == addressof(other._impl) |
| return False |
| |
| def __ne__(self, other): |
| if isinstance(other, Wrapper): |
| return addressof(self._impl) != addressof(other._impl) |
| return True |
| |
| def __del__(self): |
| pn_decref(self._impl) |
| |
| def __repr__(self): |
| 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 |