Skip to main content

Callback-based object proxies in Python.

Project description

proxyvars
skeleton Supported Python versions Package version

Tests Coverage Documentation Status Lifted?

Callback-based object proxies in Python.

Example Usage

The library might have many use cases.

Asynchronous Apps

Imagine a web app that processes many requests asynchronously. A fun design we can learn from contextvars is that we can run request handlers in proper contexts with all the request-related data held in context variables instead of requiring endpoint functions to accept parameters like request. This approach heavily used in Flask, where one can access request data using a global proxy flask.request. With bare contextvars, we could achieve this:

from contextvars import ContextVar
from proxyvars import proxy
from asgi_framework import App  # some random ASGI web framework

class Request:
    args: dict[str, str]
    headers: dict[str, str]
    data: str
    # etc...

request_context: ContextVar[Request] = ContextVar("request_context")
app = App()

# the app will manage to set the appropriate Request object in the context
# before actually triggering the index() coroutine
@app.get("/")
async def echo():
    request: Request = request_context.get()
    # do something with request...
    return request.data

With proxyvars, we can skip the assignment and simply create a dynamic-lookup proxy:

# below the Request class
request = lookup_proxy(request_context)

@app.get("/")
async def echo():
    return request.data  # request delegated attribute access to request_context.get()

Flexible "Global" State

One of the crucial caveats of using global variables is reduction of modularity and flexibility. Typically we want to test our code in an easily-parametrizable environment, which becomes unobvious in case of some global state that other parts of your program might depend on. Problems arise in multi-threaded or asynchronous applications, where some global data typically should be thread- or task-local. Python offers threading.local and contextvars (respectively) for achieving these goals.

But sometimes global state is just the most convenient solution. Imagine running an app with a core App class with a config read from a file. We don't want to populate the configuration into attributes of the App instance and rather store configuration data properly in a class named Config.

We can store Config instance as an attribute config of our main class App that manages the whole application life cycle and then, everytime we need a config value, we can request the app's config attribute. With a lot of codebase, you can find this solution more and more tedious.

Creating a global app_config variable, not bound to an app, is not a good direction either though. What if you want to 2 apps with distinct configurations? Will you modify the global app_config to the proper Config object everytime?

Get That "Global" Experience With Proxyvars!

Simply create a global context variable.

app_config_var: ContextVar[Config] = ContextVar("app_config_var")
app_config: Config = lookup_proxy(app_config_var)

Now just run your tests in properly set-up contexts with app_config_var holding objects local to every thread, task or just a custom copy of the current context.

Mutable Immutables

Ever dreamt of thread-safe, mutable integers in Python? I don't think anybody did, but it's possible with lookup proxies & beloved contextvars just in case.

from contextvars import ContextVar
from proxyvars import proxy

my_iq_var = ContextVar("my_iq_var", 100)
my_iq = lookup_proxy(my_iq_var)

print(my_iq)  # 100

# Users of proxyvars typically have 200 IQ. Patch our current variable.
my_iq_var.set(200)

print(my_iq)  # 200

# Alright, let's be real. Assuming we have 200 IQ is a non-200 IQ behavior.
# Subtract 60 IQ points. We are entitled to 140 as programmers.
my_iq -= 60

print(my_iq_var.get())  # 140
print(my_iq)  # 60

This way, we got a mutable immutable. Or, more correctly, we got a proxy object that can change its state which is represented by an immutable object.

Have fun with proxyvars!

For Enterprise

Tidelift Available as part of the Tidelift Subscription.
This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. Learn more here.

To report a security vulnerability, please use the Tidelift security contact.
Tidelift will coordinate the fix and disclosure.

Installation

You might simply install it with pip:

pip install proxyvars

If you use Poetry, then you might want to run:

poetry add proxyvars

For Contributors

Poetry Ruff Pre-commit

[!Note] If you use Windows, it is highly recommended to complete the installation in the way presented below through WSL2.

  1. Fork the proxyvars repository on GitHub.

  2. Install Poetry.
    Poetry is an amazing tool for managing dependencies & virtual environments, building packages and publishing them. You might use pipx to install it globally (recommended):

    pipx install poetry
    

    If you encounter any problems, refer to the official documentation for the most up-to-date installation instructions.

    Be sure to have Python 3.8 installed—if you use pyenv, simply run:

    pyenv install 3.8
    
  3. Clone your fork locally and install dependencies.

    git clone https://github.com/your-username/proxyvars path/to/proxyvars
    cd path/to/proxyvars
    poetry env use $(cat .python-version)
    poetry install
    

    Next up, simply activate the virtual environment and install pre-commit hooks:

    poetry shell
    pre-commit install
    

For more information on how to contribute, check out CONTRIBUTING.md.
Always happy to accept contributions! ❤️

Legal Info

© Copyright by Bartosz Sławecki (@bswck).
This software is licensed under the terms of MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

proxyvars-0.10.0.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

proxyvars-0.10.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

Details for the file proxyvars-0.10.0.tar.gz.

File metadata

  • Download URL: proxyvars-0.10.0.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.0.0 CPython/3.12.3

File hashes

Hashes for proxyvars-0.10.0.tar.gz
Algorithm Hash digest
SHA256 19a0edf3513e4f0d43262346063ff75b301ff828f659ffba5021eaa1903f3f96
MD5 eab7f47699150cb739b0dfac3bc300c8
BLAKE2b-256 eb50ec329bf276e94465f72ef522e63f7d310a6b61649c244147254bcc1f0d97

See more details on using hashes here.

File details

Details for the file proxyvars-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: proxyvars-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 10.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.0.0 CPython/3.12.3

File hashes

Hashes for proxyvars-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1224945422901b6b271bdae1b50a5d58e16baa1eb90017c9b79b5ba2aff17282
MD5 8af812b6308bb248e371a645d3b01953
BLAKE2b-256 96bd792db932e09bd2ec12562ae809571f7109c44522aab2c77f5ca31b59424f

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page