Generic Proxy and Pool Classes for Python
Project description
Proxy Pattern Pool
Generic Proxy and Pool classes for Python.
This module provides two classes:
-
Proxy
implements the proxy pattern, i.e. all calls to methods on the proxy are forwarded to an internally wrapped object. This allows to solve the classic chicken-and-egg importation and initialization possibly circular-dependency issue with Python modules:# File "database.py" db = Proxy() def init_app(config): db.set_obj(initialization from config)
# File "app.py" import database from database import db # db is a proxy to nothing … # delayed initialization database.init_app(config) # db is now a proxy to the initialized object
-
Pool
implements a thread-safe pool of things which can be used to store expensive-to-create objects such as database connections. The above proxy object creates a pool automatically depending on its parameters.Method
_ret_obj
must be called to return the object to the pool when done with it.This generic pool class can be used independently of the
Proxy
class.
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:
obj
a single objectSHARED
between all threads.fun
a function called for object creation, each time it is needed, for all other scopes.scope
object scope as defined byProxy.Scope
:SHARED
one shared object (process level)THREAD
one object per thread (threading
implementation)WERKZEUG
one object per greenlet (werkzeug
implementation)EVENTLET
one object per greenlet (eventlet
implementation)GEVENT
one object per greenlet (gevent
implementation)VERSATILE
same asWERKZEUG
default isSHARED
orTHREAD
depending on whether an object of a function was passed for the object.
set_name
the name of a function to set the proxy contents, default isset
. This parameter allows to avoid collisions with the proxied methods, if necessary. It is used as a prefix to haveset_obj
andset_fun
functions which allow to reset the internalobj
orfun
.log_level
set logging level, default None means no setting.max_size
of pool, default None means no pooling.max_size
and all other parameters are forwarded toPool
.
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:
fun
how to create a new object; the function is passed the creation number.max_size
maximum size of pool, 0 for unlimited (the default).min_size
minimum size of pool, that many are created and maintained in advance.timeout
maximum time to wait for something, only active undermax_size
.max_use
after how many usage to discard an object.max_avail_delay
when to discard an unused object.max_using_delay
when to warn about object kept for a long time.max_using_delay_kill
when to kill objects kept for a long time.health_freq
run health check this every house keeper rounds.hk_delay
force house keeping delay.log_level
set logging level, default None means no setting.opener
function to call when creating an object, default None means no call.getter
function to call when getting an object, default None means no call.retter
function to call when returning an object, default None means no call.closer
function to call when discarding an object, default None means no call.stats
function to call to generate a JSON-compatible structure for stats.health
function to call to check for an available object health.tracer
object debug helper, default None means less debug.
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:
- Psycopg Pool for pooling Postgres database connexions.
- Eventlet db_pool for pooling MySQL or Postgres database connexions.
- Discussion about database pool sizing (spoiler: small is beautiful).
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file proxypatternpool-11.2.tar.gz
.
File metadata
- Download URL: proxypatternpool-11.2.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | abace53f8a29c89955446ac1a61d7bb6244ccd98ad26d8cfa403039ece9afc0f |
|
MD5 | f4e97041de510ec5087c4f1a3da95679 |
|
BLAKE2b-256 | f626fb5f03b8f3df93afd9c445cb7ad5bae65bc68d4d27da831d8f08cb15bf33 |
File details
Details for the file ProxyPatternPool-11.2-py3-none-any.whl
.
File metadata
- Download URL: ProxyPatternPool-11.2-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7ecdd692a9b208f50c9bd81e0949912aae944632e58678c9d45e6ab2cf22ae06 |
|
MD5 | 9491bb7ef78cd0f667d724b79b8a2543 |
|
BLAKE2b-256 | 60e3b3b26d205f21a3f35947b04aca36adc02cd5091d195da8ca0d644e04f196 |