proxy-pattern-pool

Proxy Pattern Pool

Generic Proxy and Pool classes for Python.

Status Tests Coverage Issues Python Version Badges License

This module provides two classes:

Documentation

Proxy

Class Proxy manages accesses to one or more objects, possibly using a Pool, depending on the expected scope of said objects.

The Proxy constructors expects the following parameters:

When max_size is not None, a Pool is created to store the created objects so as to reuse them. It is the responsability of the user to return the object when not needed anymore by calling _ret_obj explicitely. This is useful for code which keeps creating new threads, eg werkzeug. For a database connection, a good time to do that is just after a commit.

The proxy has a _has_obj method to test whether an object is available without extracting anything from the pool: this is useful to test whether returning the object is needed in some error handling pattern.

Pool

Class Pool manages a pool of objects in a thread-safe way. Its constructor expects the following parameters:

Objects are created on demand by calling fun when needed.

Proxy Example

Here is an example of a flask application with blueprints and a shared resource.

First, a shared module holds a proxy to a yet unknown object:

# file "Shared.py"
from ProxyPatternPool import Proxy
stuff = Proxy()
def init_app(s):
    stuff.set_obj(s)

This shared object is used by module with a blueprint:

# file "SubStuff.py"
from Flask import Blueprint
from Shared import stuff
sub = Blueprint()

@sub.get("/stuff")
def get_stuff():
    return str(stuff), 200

Then the application itself can load and initialize both modules in any order without risk of having some unitialized stuff imported:

# file "App.py"
from flask import Flask
app = Flask("stuff")

from SubStuff import sub
app.register_blueprint(sub, url_prefix="/sub")

import Shared
Shared.init_app("hello world!")

Notes

This module is rhetorical: because of the GIL Python is quite bad as a parallel language, so the point of creating threads which will mostly not really run in parallel is moot, thus the point of having a clever pool of stuff to be shared by these thread is even mooter! However, as the GIL is scheduled to go away in the coming years, starting from Python 3.13, it might start to make sense to have such a thing here!

In passing, it is interesting to note that the foremost driving motivation for getting read of the GIL is… data science. This tells something. In the past, people interested in parallelism, i.e. performance, say myself, would probably just turn away from this quite slow language. People from the networking www world would be satisfied with the adhoc asynchronous model, and/or just create many processes because in this context the need to communicate between active workers is limited. Now come the data scientist, who is not that interested in programming, is happy with Python and its ecosystem, in particular with the various ML libraries and the commodity of web-centric remote interfaces such as Jupyter. When confronted with a GIL-induced performance issue, they are more interested at fixing the problem than having to learn another language and port their stuff.

Shared object must be returned to the pool to avoid depleting resources. This may require some active cooperation from the infrastructure which may or may not be reliable. Consider monitoring your resources to detect unexpected status, eg database connections remaining idle in transaction and the like.

See Also:

License

This code is Public Domain.

All software has bug, this is software, hence… Beware that you may lose your hairs or your friends because of it. If you like it, feel free to send a postcard to the author.

Versions

Sources, documentation and issues are hosted on GitHub. Install package from PyPI. See version details.