summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--python-objproxies.spec777
-rw-r--r--sources1
3 files changed, 779 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..a593b56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/objproxies-0.9.4.tar.gz
diff --git a/python-objproxies.spec b/python-objproxies.spec
new file mode 100644
index 0000000..fea142c
--- /dev/null
+++ b/python-objproxies.spec
@@ -0,0 +1,777 @@
+%global _empty_manifest_terminate_build 0
+Name: python-objproxies
+Version: 0.9.4
+Release: 1
+Summary: General purpose proxy and wrapper types
+License: PSF or ZPL
+URL: http://github.com/soulrebel/objproxies
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/1d/6b/0434d100d411760d4d634e21dc256e259af720887b0c856c5a9b2c97ea30/objproxies-0.9.4.tar.gz
+BuildArch: noarch
+
+
+%description
+The ``objproxies`` module provides some useful base classes for creating
+proxies and wrappers for ordinary Python objects. Proxy objects automatically
+delegate all attribute access and operations to the proxied object. Wrappers
+are similar, but can be subclassed to allow additional attributes and
+operations to be added to the wrapped object.
+Note that these proxy types are not intended to be tamper-proof; the unproxied
+form of an object can be readily accessed using a proxy's ``__subject__``
+attribute, and some proxy types even allow this attribute to be set (This can
+be handy for algorithms that lazily create circular structures and thus need to
+be able to hand out "forward reference" proxies.)
+Development status
+******************
+This is Python 3 port of `ProxyTypes
+<http://cheeseshop.python.org/pypi/ProxyTypes>`_ wrote by Phillip J. Eby as
+part of `PEAK <http://www.eby-sarna.com/mailman/listinfo/peak>`_ for Python 2.
+The namespace was changed from ``peak.util.proxies`` to ``objproxies``. Other
+than that it should be a compatible replacement.
+So far the following was accomplished:
+* Streamlined files and setup
+* Ported unittests and doctests
+* Cleaned up syntax
+v1.0 TODO
++++++++++
+* Turn the module in a package, separate functionalities in different modules
+* Simplify code wherever possible
+* Get positive feedback from a couple of users
+Contributions and bug reports are welcome.
+Testing
++++++++
+When nose is available all tests can be run using:
+ nosetests3 --with-doctest --doctest-extension=rst .
+Otherwise standard python will suffice:
+ python -m unittest objproxies_tests.py
+ python -m doctest README.rst
+Proxy Basics
+************
+Here's a quick demo of the ``ObjectProxy`` type::
+ >>> from objproxies import ObjectProxy
+ >>> p = ObjectProxy(42)
+ >>> p
+ 42
+ >>> isinstance(p, int)
+ True
+ >>> p.__class__
+ <class 'int'>
+ >>> p*2
+ 84
+ >>> 'X' * p
+ 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+ >>> hex(p)
+ '0x2a'
+ >>> chr(p)
+ '*'
+ >>> p ^ 1
+ 43
+ >>> p ** 2
+ 1764
+As you can see, a proxy is virtually indistinguishable from the object it
+proxies, except via its ``__subject__`` attribute, and its ``type()``::
+ >>> p.__subject__
+ 42
+ >>> type(p)
+ <class 'objproxies.ObjectProxy'>
+You can change the ``__subject__`` of an ``ObjectProxy``, and it will then
+refer to something else::
+ >>> p.__subject__ = 99
+ >>> p
+ 99
+ >>> p-33
+ 66
+ >>> p.__subject__ = "foo"
+ >>> p
+ 'foo'
+All operations are delegated to the subject, including ``setattr`` and
+``delattr``::
+ >>> class Dummy: pass
+ >>> d = Dummy()
+ >>> p = ObjectProxy(d)
+ >>> p.foo = "bar"
+ >>> d.foo
+ 'bar'
+ >>> del p.foo
+ >>> hasattr(d,'foo')
+ False
+Callback Proxies
+****************
+Sometimes, you may want a proxy's subject to be determined dynamically whenever
+the proxy is used. For this purpose, you can use the ``CallbackProxy`` type,
+which accepts a callback function and creates a proxy that will invoke the
+callback in order to get the target. Here's a quick example of a counter that
+gets incremented each time it's used, from zero to three::
+ >>> from objproxies import CallbackProxy
+ >>> callback = iter(range(4)).__next__
+ >>> counter = CallbackProxy(callback)
+ >>> counter
+ 0
+ >>> counter
+ 1
+ >>> str(counter)
+ '2'
+ >>> hex(counter)
+ '0x3'
+ >>> counter
+ Traceback (most recent call last):
+ StopIteration
+As you can see, the callback is automatically invoked on any attempt to use the
+proxy. This is a somewhat silly example; a better one would be something like
+a ``thread_id`` proxy that is always equal to the ID # of the thread it's
+running in.
+A callback proxy's callback can be obtained or changed via the ``get_callback``
+and ``set_callback`` functions::
+ >>> from objproxies import get_callback, set_callback
+ >>> set_callback(counter, lambda: 42)
+ >>> counter
+ 42
+ >>> type(get_callback(counter))
+ <class 'function'>
+Lazy Proxies
+************
+A ``LazyProxy`` is similar to a ``CallbackProxy``, but its callback is called
+at most once, and then cached::
+ >>> from objproxies import LazyProxy
+ >>> def callback():
+ >>> lazy = LazyProxy(callback)
+ >>> lazy
+ called
+ 42
+ >>> lazy
+ 42
+You can use the ``get_callback`` and ``set_callback`` functions on lazy
+proxies, but it has no effect if the callback was already called::
+ >>> set_callback(lazy, lambda: 99)
+ >>> lazy
+ 42
+But you can use the ``get_cache`` and ``set_cache`` functions to tamper with
+the cached value::
+ >>> from objproxies import get_cache, set_cache
+ >>> get_cache(lazy)
+ 42
+ >>> set_cache(lazy, 99)
+ >>> lazy
+ 99
+Wrappers
+********
+The ``ObjectWrapper``, ``CallbackWrapper`` and ``LazyWrapper`` classes are
+similar to their proxy counterparts, except that they are intended to be
+subclassed in order to add custom extra attributes or methods. Any attribute
+that exists in a subclass of these classes will be read or written from the
+wrapper instance, instead of the wrapped object. For example::
+ >>> from objproxies import ObjectWrapper
+ >>> class NameWrapper(ObjectWrapper):
+ >>> w = NameWrapper(42, "The Ultimate Answer")
+ >>> w
+ 42
+ >>> print(w)
+ The Ultimate Answer
+ >>> w * 2
+ 84
+ >>> w.name
+ 'The Ultimate Answer'
+Notice that any attributes you add must be defined *in the class*. You can't
+add arbitrary attributes at runtime, because they'll be set on the wrapped
+object instead of the wrapper::
+ >>> w.foo = 'bar'
+ Traceback (most recent call last):
+ AttributeError: 'int' object has no attribute 'foo'
+Note that this means that all instance attributes must be implemented as either
+slots, properties, or have a default value defined in the class body (like the
+``name = None`` shown in the example above.
+The ``CallbackWrapper`` and ``LazyWrapper`` base classes are basically the same
+as ``ObjectWrapper``, except that they use a callback or cached lazy callback
+instead of expecting an object as their subject.
+``LazyWrapper`` objects are particularly useful when working with expensive
+resources, like connections or web browsers, to avoid their creation unless
+absolutely needed.
+However resources usually must be released after use by calling a "``close``"
+method of some sort. In this case the lazy creation could be triggered just
+when the object is not needed anymore, by the call to ``close`` itself. For
+this reason when extending ``LazyWrapper`` these methods can be overridden with
+a ``@lazymethod`` replacement::
+ >>> from objproxies import LazyWrapper, lazymethod
+ >>> class LazyCloseable(LazyWrapper):
+ >>> import tempfile
+ >>> def openf():
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.tell()
+ 0
+ >>> lazyfile.close()
+ bye
+ >>> bool(lazyfile)
+ False
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.write('wake up')
+ called
+ 7
+ >>> lazyfile.tell()
+ 7
+ >>> lazyfile.close() # close for real
+ >>> bool(lazyfile)
+ True
+Advanced: custom subclasses and mixins
+**************************************
+In addition to all the concrete classes described above, there are also two
+abstract base classes: ``AbstractProxy`` and ``AbstractWrapper``. If you want
+to create a mixin type that can be used with any of the concrete types, you
+should subclass the abstract version and set ``__slots__`` to an empty list::
+ >>> from objproxies import AbstractWrapper
+ >>> class NamedMixin(AbstractWrapper):
+Then, when you mix it in with the respective base class, you can add back in
+any necessary slots, or leave off ``__slots__`` to give the subclass instances
+a dictionary of their own::
+ >>> from objproxies import CallbackWrapper, LazyWrapper
+ >>> class NamedObject(NamedMixin, ObjectWrapper): pass
+ >>> class NamedCallback(NamedMixin, CallbackWrapper): pass
+ >>> class NamedLazy(NamedMixin, LazyWrapper): pass
+ >>> print(NamedObject(42, "The Answer"))
+ The Answer
+ >>> n = NamedCallback(callback, "Test")
+ >>> n
+ called
+ 42
+ >>> n
+ called
+ 42
+ >>> n = NamedLazy(callback, "Once")
+ >>> n
+ called
+ 42
+ >>> n
+ 42
+Both the ``AbstractProxy`` and ``AbstractWrapper`` base classes work by
+assuming that ``self.__subject__`` will be the wrapped or proxed object. If
+you don't want to use any of the standard three ways of defining
+``__subject__`` (i.e., as an object, callback, or lazy callback), you will need
+to subclass ``AbstractProxy`` or ``AbstractWrapper`` and provide your own way
+of defining ``__subject__``.
+
+%package -n python3-objproxies
+Summary: General purpose proxy and wrapper types
+Provides: python-objproxies
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-objproxies
+The ``objproxies`` module provides some useful base classes for creating
+proxies and wrappers for ordinary Python objects. Proxy objects automatically
+delegate all attribute access and operations to the proxied object. Wrappers
+are similar, but can be subclassed to allow additional attributes and
+operations to be added to the wrapped object.
+Note that these proxy types are not intended to be tamper-proof; the unproxied
+form of an object can be readily accessed using a proxy's ``__subject__``
+attribute, and some proxy types even allow this attribute to be set (This can
+be handy for algorithms that lazily create circular structures and thus need to
+be able to hand out "forward reference" proxies.)
+Development status
+******************
+This is Python 3 port of `ProxyTypes
+<http://cheeseshop.python.org/pypi/ProxyTypes>`_ wrote by Phillip J. Eby as
+part of `PEAK <http://www.eby-sarna.com/mailman/listinfo/peak>`_ for Python 2.
+The namespace was changed from ``peak.util.proxies`` to ``objproxies``. Other
+than that it should be a compatible replacement.
+So far the following was accomplished:
+* Streamlined files and setup
+* Ported unittests and doctests
+* Cleaned up syntax
+v1.0 TODO
++++++++++
+* Turn the module in a package, separate functionalities in different modules
+* Simplify code wherever possible
+* Get positive feedback from a couple of users
+Contributions and bug reports are welcome.
+Testing
++++++++
+When nose is available all tests can be run using:
+ nosetests3 --with-doctest --doctest-extension=rst .
+Otherwise standard python will suffice:
+ python -m unittest objproxies_tests.py
+ python -m doctest README.rst
+Proxy Basics
+************
+Here's a quick demo of the ``ObjectProxy`` type::
+ >>> from objproxies import ObjectProxy
+ >>> p = ObjectProxy(42)
+ >>> p
+ 42
+ >>> isinstance(p, int)
+ True
+ >>> p.__class__
+ <class 'int'>
+ >>> p*2
+ 84
+ >>> 'X' * p
+ 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+ >>> hex(p)
+ '0x2a'
+ >>> chr(p)
+ '*'
+ >>> p ^ 1
+ 43
+ >>> p ** 2
+ 1764
+As you can see, a proxy is virtually indistinguishable from the object it
+proxies, except via its ``__subject__`` attribute, and its ``type()``::
+ >>> p.__subject__
+ 42
+ >>> type(p)
+ <class 'objproxies.ObjectProxy'>
+You can change the ``__subject__`` of an ``ObjectProxy``, and it will then
+refer to something else::
+ >>> p.__subject__ = 99
+ >>> p
+ 99
+ >>> p-33
+ 66
+ >>> p.__subject__ = "foo"
+ >>> p
+ 'foo'
+All operations are delegated to the subject, including ``setattr`` and
+``delattr``::
+ >>> class Dummy: pass
+ >>> d = Dummy()
+ >>> p = ObjectProxy(d)
+ >>> p.foo = "bar"
+ >>> d.foo
+ 'bar'
+ >>> del p.foo
+ >>> hasattr(d,'foo')
+ False
+Callback Proxies
+****************
+Sometimes, you may want a proxy's subject to be determined dynamically whenever
+the proxy is used. For this purpose, you can use the ``CallbackProxy`` type,
+which accepts a callback function and creates a proxy that will invoke the
+callback in order to get the target. Here's a quick example of a counter that
+gets incremented each time it's used, from zero to three::
+ >>> from objproxies import CallbackProxy
+ >>> callback = iter(range(4)).__next__
+ >>> counter = CallbackProxy(callback)
+ >>> counter
+ 0
+ >>> counter
+ 1
+ >>> str(counter)
+ '2'
+ >>> hex(counter)
+ '0x3'
+ >>> counter
+ Traceback (most recent call last):
+ StopIteration
+As you can see, the callback is automatically invoked on any attempt to use the
+proxy. This is a somewhat silly example; a better one would be something like
+a ``thread_id`` proxy that is always equal to the ID # of the thread it's
+running in.
+A callback proxy's callback can be obtained or changed via the ``get_callback``
+and ``set_callback`` functions::
+ >>> from objproxies import get_callback, set_callback
+ >>> set_callback(counter, lambda: 42)
+ >>> counter
+ 42
+ >>> type(get_callback(counter))
+ <class 'function'>
+Lazy Proxies
+************
+A ``LazyProxy`` is similar to a ``CallbackProxy``, but its callback is called
+at most once, and then cached::
+ >>> from objproxies import LazyProxy
+ >>> def callback():
+ >>> lazy = LazyProxy(callback)
+ >>> lazy
+ called
+ 42
+ >>> lazy
+ 42
+You can use the ``get_callback`` and ``set_callback`` functions on lazy
+proxies, but it has no effect if the callback was already called::
+ >>> set_callback(lazy, lambda: 99)
+ >>> lazy
+ 42
+But you can use the ``get_cache`` and ``set_cache`` functions to tamper with
+the cached value::
+ >>> from objproxies import get_cache, set_cache
+ >>> get_cache(lazy)
+ 42
+ >>> set_cache(lazy, 99)
+ >>> lazy
+ 99
+Wrappers
+********
+The ``ObjectWrapper``, ``CallbackWrapper`` and ``LazyWrapper`` classes are
+similar to their proxy counterparts, except that they are intended to be
+subclassed in order to add custom extra attributes or methods. Any attribute
+that exists in a subclass of these classes will be read or written from the
+wrapper instance, instead of the wrapped object. For example::
+ >>> from objproxies import ObjectWrapper
+ >>> class NameWrapper(ObjectWrapper):
+ >>> w = NameWrapper(42, "The Ultimate Answer")
+ >>> w
+ 42
+ >>> print(w)
+ The Ultimate Answer
+ >>> w * 2
+ 84
+ >>> w.name
+ 'The Ultimate Answer'
+Notice that any attributes you add must be defined *in the class*. You can't
+add arbitrary attributes at runtime, because they'll be set on the wrapped
+object instead of the wrapper::
+ >>> w.foo = 'bar'
+ Traceback (most recent call last):
+ AttributeError: 'int' object has no attribute 'foo'
+Note that this means that all instance attributes must be implemented as either
+slots, properties, or have a default value defined in the class body (like the
+``name = None`` shown in the example above.
+The ``CallbackWrapper`` and ``LazyWrapper`` base classes are basically the same
+as ``ObjectWrapper``, except that they use a callback or cached lazy callback
+instead of expecting an object as their subject.
+``LazyWrapper`` objects are particularly useful when working with expensive
+resources, like connections or web browsers, to avoid their creation unless
+absolutely needed.
+However resources usually must be released after use by calling a "``close``"
+method of some sort. In this case the lazy creation could be triggered just
+when the object is not needed anymore, by the call to ``close`` itself. For
+this reason when extending ``LazyWrapper`` these methods can be overridden with
+a ``@lazymethod`` replacement::
+ >>> from objproxies import LazyWrapper, lazymethod
+ >>> class LazyCloseable(LazyWrapper):
+ >>> import tempfile
+ >>> def openf():
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.tell()
+ 0
+ >>> lazyfile.close()
+ bye
+ >>> bool(lazyfile)
+ False
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.write('wake up')
+ called
+ 7
+ >>> lazyfile.tell()
+ 7
+ >>> lazyfile.close() # close for real
+ >>> bool(lazyfile)
+ True
+Advanced: custom subclasses and mixins
+**************************************
+In addition to all the concrete classes described above, there are also two
+abstract base classes: ``AbstractProxy`` and ``AbstractWrapper``. If you want
+to create a mixin type that can be used with any of the concrete types, you
+should subclass the abstract version and set ``__slots__`` to an empty list::
+ >>> from objproxies import AbstractWrapper
+ >>> class NamedMixin(AbstractWrapper):
+Then, when you mix it in with the respective base class, you can add back in
+any necessary slots, or leave off ``__slots__`` to give the subclass instances
+a dictionary of their own::
+ >>> from objproxies import CallbackWrapper, LazyWrapper
+ >>> class NamedObject(NamedMixin, ObjectWrapper): pass
+ >>> class NamedCallback(NamedMixin, CallbackWrapper): pass
+ >>> class NamedLazy(NamedMixin, LazyWrapper): pass
+ >>> print(NamedObject(42, "The Answer"))
+ The Answer
+ >>> n = NamedCallback(callback, "Test")
+ >>> n
+ called
+ 42
+ >>> n
+ called
+ 42
+ >>> n = NamedLazy(callback, "Once")
+ >>> n
+ called
+ 42
+ >>> n
+ 42
+Both the ``AbstractProxy`` and ``AbstractWrapper`` base classes work by
+assuming that ``self.__subject__`` will be the wrapped or proxed object. If
+you don't want to use any of the standard three ways of defining
+``__subject__`` (i.e., as an object, callback, or lazy callback), you will need
+to subclass ``AbstractProxy`` or ``AbstractWrapper`` and provide your own way
+of defining ``__subject__``.
+
+%package help
+Summary: Development documents and examples for objproxies
+Provides: python3-objproxies-doc
+%description help
+The ``objproxies`` module provides some useful base classes for creating
+proxies and wrappers for ordinary Python objects. Proxy objects automatically
+delegate all attribute access and operations to the proxied object. Wrappers
+are similar, but can be subclassed to allow additional attributes and
+operations to be added to the wrapped object.
+Note that these proxy types are not intended to be tamper-proof; the unproxied
+form of an object can be readily accessed using a proxy's ``__subject__``
+attribute, and some proxy types even allow this attribute to be set (This can
+be handy for algorithms that lazily create circular structures and thus need to
+be able to hand out "forward reference" proxies.)
+Development status
+******************
+This is Python 3 port of `ProxyTypes
+<http://cheeseshop.python.org/pypi/ProxyTypes>`_ wrote by Phillip J. Eby as
+part of `PEAK <http://www.eby-sarna.com/mailman/listinfo/peak>`_ for Python 2.
+The namespace was changed from ``peak.util.proxies`` to ``objproxies``. Other
+than that it should be a compatible replacement.
+So far the following was accomplished:
+* Streamlined files and setup
+* Ported unittests and doctests
+* Cleaned up syntax
+v1.0 TODO
++++++++++
+* Turn the module in a package, separate functionalities in different modules
+* Simplify code wherever possible
+* Get positive feedback from a couple of users
+Contributions and bug reports are welcome.
+Testing
++++++++
+When nose is available all tests can be run using:
+ nosetests3 --with-doctest --doctest-extension=rst .
+Otherwise standard python will suffice:
+ python -m unittest objproxies_tests.py
+ python -m doctest README.rst
+Proxy Basics
+************
+Here's a quick demo of the ``ObjectProxy`` type::
+ >>> from objproxies import ObjectProxy
+ >>> p = ObjectProxy(42)
+ >>> p
+ 42
+ >>> isinstance(p, int)
+ True
+ >>> p.__class__
+ <class 'int'>
+ >>> p*2
+ 84
+ >>> 'X' * p
+ 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+ >>> hex(p)
+ '0x2a'
+ >>> chr(p)
+ '*'
+ >>> p ^ 1
+ 43
+ >>> p ** 2
+ 1764
+As you can see, a proxy is virtually indistinguishable from the object it
+proxies, except via its ``__subject__`` attribute, and its ``type()``::
+ >>> p.__subject__
+ 42
+ >>> type(p)
+ <class 'objproxies.ObjectProxy'>
+You can change the ``__subject__`` of an ``ObjectProxy``, and it will then
+refer to something else::
+ >>> p.__subject__ = 99
+ >>> p
+ 99
+ >>> p-33
+ 66
+ >>> p.__subject__ = "foo"
+ >>> p
+ 'foo'
+All operations are delegated to the subject, including ``setattr`` and
+``delattr``::
+ >>> class Dummy: pass
+ >>> d = Dummy()
+ >>> p = ObjectProxy(d)
+ >>> p.foo = "bar"
+ >>> d.foo
+ 'bar'
+ >>> del p.foo
+ >>> hasattr(d,'foo')
+ False
+Callback Proxies
+****************
+Sometimes, you may want a proxy's subject to be determined dynamically whenever
+the proxy is used. For this purpose, you can use the ``CallbackProxy`` type,
+which accepts a callback function and creates a proxy that will invoke the
+callback in order to get the target. Here's a quick example of a counter that
+gets incremented each time it's used, from zero to three::
+ >>> from objproxies import CallbackProxy
+ >>> callback = iter(range(4)).__next__
+ >>> counter = CallbackProxy(callback)
+ >>> counter
+ 0
+ >>> counter
+ 1
+ >>> str(counter)
+ '2'
+ >>> hex(counter)
+ '0x3'
+ >>> counter
+ Traceback (most recent call last):
+ StopIteration
+As you can see, the callback is automatically invoked on any attempt to use the
+proxy. This is a somewhat silly example; a better one would be something like
+a ``thread_id`` proxy that is always equal to the ID # of the thread it's
+running in.
+A callback proxy's callback can be obtained or changed via the ``get_callback``
+and ``set_callback`` functions::
+ >>> from objproxies import get_callback, set_callback
+ >>> set_callback(counter, lambda: 42)
+ >>> counter
+ 42
+ >>> type(get_callback(counter))
+ <class 'function'>
+Lazy Proxies
+************
+A ``LazyProxy`` is similar to a ``CallbackProxy``, but its callback is called
+at most once, and then cached::
+ >>> from objproxies import LazyProxy
+ >>> def callback():
+ >>> lazy = LazyProxy(callback)
+ >>> lazy
+ called
+ 42
+ >>> lazy
+ 42
+You can use the ``get_callback`` and ``set_callback`` functions on lazy
+proxies, but it has no effect if the callback was already called::
+ >>> set_callback(lazy, lambda: 99)
+ >>> lazy
+ 42
+But you can use the ``get_cache`` and ``set_cache`` functions to tamper with
+the cached value::
+ >>> from objproxies import get_cache, set_cache
+ >>> get_cache(lazy)
+ 42
+ >>> set_cache(lazy, 99)
+ >>> lazy
+ 99
+Wrappers
+********
+The ``ObjectWrapper``, ``CallbackWrapper`` and ``LazyWrapper`` classes are
+similar to their proxy counterparts, except that they are intended to be
+subclassed in order to add custom extra attributes or methods. Any attribute
+that exists in a subclass of these classes will be read or written from the
+wrapper instance, instead of the wrapped object. For example::
+ >>> from objproxies import ObjectWrapper
+ >>> class NameWrapper(ObjectWrapper):
+ >>> w = NameWrapper(42, "The Ultimate Answer")
+ >>> w
+ 42
+ >>> print(w)
+ The Ultimate Answer
+ >>> w * 2
+ 84
+ >>> w.name
+ 'The Ultimate Answer'
+Notice that any attributes you add must be defined *in the class*. You can't
+add arbitrary attributes at runtime, because they'll be set on the wrapped
+object instead of the wrapper::
+ >>> w.foo = 'bar'
+ Traceback (most recent call last):
+ AttributeError: 'int' object has no attribute 'foo'
+Note that this means that all instance attributes must be implemented as either
+slots, properties, or have a default value defined in the class body (like the
+``name = None`` shown in the example above.
+The ``CallbackWrapper`` and ``LazyWrapper`` base classes are basically the same
+as ``ObjectWrapper``, except that they use a callback or cached lazy callback
+instead of expecting an object as their subject.
+``LazyWrapper`` objects are particularly useful when working with expensive
+resources, like connections or web browsers, to avoid their creation unless
+absolutely needed.
+However resources usually must be released after use by calling a "``close``"
+method of some sort. In this case the lazy creation could be triggered just
+when the object is not needed anymore, by the call to ``close`` itself. For
+this reason when extending ``LazyWrapper`` these methods can be overridden with
+a ``@lazymethod`` replacement::
+ >>> from objproxies import LazyWrapper, lazymethod
+ >>> class LazyCloseable(LazyWrapper):
+ >>> import tempfile
+ >>> def openf():
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.tell()
+ 0
+ >>> lazyfile.close()
+ bye
+ >>> bool(lazyfile)
+ False
+ >>> lazyfile = LazyCloseable(openf)
+ >>> lazyfile.write('wake up')
+ called
+ 7
+ >>> lazyfile.tell()
+ 7
+ >>> lazyfile.close() # close for real
+ >>> bool(lazyfile)
+ True
+Advanced: custom subclasses and mixins
+**************************************
+In addition to all the concrete classes described above, there are also two
+abstract base classes: ``AbstractProxy`` and ``AbstractWrapper``. If you want
+to create a mixin type that can be used with any of the concrete types, you
+should subclass the abstract version and set ``__slots__`` to an empty list::
+ >>> from objproxies import AbstractWrapper
+ >>> class NamedMixin(AbstractWrapper):
+Then, when you mix it in with the respective base class, you can add back in
+any necessary slots, or leave off ``__slots__`` to give the subclass instances
+a dictionary of their own::
+ >>> from objproxies import CallbackWrapper, LazyWrapper
+ >>> class NamedObject(NamedMixin, ObjectWrapper): pass
+ >>> class NamedCallback(NamedMixin, CallbackWrapper): pass
+ >>> class NamedLazy(NamedMixin, LazyWrapper): pass
+ >>> print(NamedObject(42, "The Answer"))
+ The Answer
+ >>> n = NamedCallback(callback, "Test")
+ >>> n
+ called
+ 42
+ >>> n
+ called
+ 42
+ >>> n = NamedLazy(callback, "Once")
+ >>> n
+ called
+ 42
+ >>> n
+ 42
+Both the ``AbstractProxy`` and ``AbstractWrapper`` base classes work by
+assuming that ``self.__subject__`` will be the wrapped or proxed object. If
+you don't want to use any of the standard three ways of defining
+``__subject__`` (i.e., as an object, callback, or lazy callback), you will need
+to subclass ``AbstractProxy`` or ``AbstractWrapper`` and provide your own way
+of defining ``__subject__``.
+
+%prep
+%autosetup -n objproxies-0.9.4
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-objproxies -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Wed May 10 2023 Python_Bot <Python_Bot@openeuler.org> - 0.9.4-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..8e605b8
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+cc6108a5a0b74fd89a90c87ee58849d3 objproxies-0.9.4.tar.gz