Skip to main content

Dispatch function's parameters through the callstack omitting arguments on intermediary functions. (a.k.a.: stack variable)

Project description

Dispatch function’s parameters through the callstack omitting arguments on intermediary functions.

Summary example

Let’s suppose you have 4 functions nested in the call stack like this:

def func1():
  value_for_func4 = 10
  func2()

def func2():
  func3()

def func3():
  func4()

def func4(value):
  #need to use value_for_func4 here
  #print(value_for_func4)
  ...

If you want to make value_for_func4 to reach func4 you have these standard options

  1. explicitly passing argument value_for_func4 through all 4 functions.

  2. using a global value_for_func4 variable.

  3. move all the functions func1 ... func4 inside an object and setting a shared property self.value_for_func4.

  4. passing a context object with context.value_for_func4 or using **kwargs dictionary through the functions.

With stackvar you have a fifth option, which is having a side channel to dispatch variables through the call stack. Things would be written as:

def func1():
    with stackvar.send(func4, value_for_func4=10):
        func2()

def func2():
    func3()

def func3():
    func4()

@stackvar.receive()
def send_email(value_for_func4: stackvar.Variable = None):
    print(f'value_for_func4={value_for_func4}')

Advantages: - avoids clogging functions arguments - avoids clogging the global namespace - avoids clogging the object’s namespace - each thread has its own value - no need to use objects to solve the problem (still can do functional programming) - you can define default value - you can override when calling the receiver directly - you can dispatch to a function or to a namespace (defined by a UUID)

Disadvantages - not every debugging tools are not prepared to take stackvar into account. - adds stackvar and pydantic as dependencies - single programmer project in beta state

Warnings - You can’t send stackvars between threads, this is on purpose, to avoid race conditions for sharing data between threads. You should pass the data to the thread and then send it inside the thread.

Installing

https://pypi.org/project/stackvar/

pip install -U stackvar

Full example

import stackvar

def test_stackvar():
    # sending within a context
    with stackvar.send(send_email, email='rsanchez@example.com'):
        foo()
    # Use default value
    send_email()
    # pass specific value
    send_email('jerry@example.com')

def foo():
    # intermediary function
    bar()

def bar():
    # intermediary function
    send_email()

@stackvar.receive()
def send_email(email: stackvar.Variable = 'morty@example.com'):
    print(f'Sending email to={email}')

if __name__ == '__main__':
    test_stackvar()

Will output

Sending email to=rsanchez@example.com
Sending email to=morty@example.com
Sending email to=jerry@example.com

All features showcase

import stackvar
import uuid

def cheat_sheet_doc():
    # Using namespace (recommended method)
    my_namespace = stackvar.Namespace(uuid.uuid4())
    @stackvar.receive(my_namespace)
    def send_email(email: stackvar.Variable = 'morty@example.com'):
        print(f'Sending email to={email}')
    with stackvar.send(my_namespace, email='rsanchez@example.com'):
        send_email()

    # Automatic namespace (solved from function)
    @stackvar.receive()
    def send_email2(email: stackvar.Variable = 'morty@example.com'):
        print(f'Sending email to={email}')
    with stackvar.send(send_email2, email='rsanchez@example.com'):
        send_email2()

    # Without decorator
    ns_uuid2 = stackvar.Namespace(uuid.uuid4())
    def send_email_nodecorator():
        email1 = ns_uuid2.email1
        # setting default value for a variable
        email2 = getattr(ns_uuid2, 'email2', 'jerry@example.com')
        print(f'Sending email1 to={email1} and {email2}')
        # another fancier way to set a default
        email2 = stackvar.get(ns_uuid2, email2='summer@example.com')
        print(f'Sending email1 to={email1} and {email2}')
    with stackvar.send(ns_uuid2, email1='rsanchez@example.com'):
        send_email_nodecorator()

    # No default values
    ns_uuid3 = stackvar.Namespace(uuid.uuid4())
    @stackvar.receive(ns_uuid3)
    def send_no_default(email1: stackvar.Variable, email2: stackvar.Variable):
        print(f'Sending={email1} and {email2}')
    with stackvar.send(ns_uuid3,
                       email1='rsanchez@example.com',
                       email2='summer@example.com'):
        send_no_default()

    # Using a Factory for default values
    ns_uuid4 = stackvar.Namespace(uuid.uuid4())
    @stackvar.receive(ns_uuid4)
    def send_factory(email_list: stackvar.Factory = list):
        email_list.append('squanchy@example.com')
        print(f'Sending to={email_list}')
    with stackvar.send(ns_uuid4):
        send_factory()

if __name__ == '__main__':
    cheat_sheet_doc()

More docs

Check examples at https://gitlab.com/joaduo/stackvar/-/tree/main/tests

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

stackvar-3.3.0.tar.gz (6.7 kB view details)

Uploaded Source

File details

Details for the file stackvar-3.3.0.tar.gz.

File metadata

  • Download URL: stackvar-3.3.0.tar.gz
  • Upload date:
  • Size: 6.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.12

File hashes

Hashes for stackvar-3.3.0.tar.gz
Algorithm Hash digest
SHA256 74e3d476af6863530b59ba430139ff797eb916af9a9649acbed39feb14ec9bb2
MD5 efefa1787ab54b1e484b39c03e7e24e3
BLAKE2b-256 d8e81d26f9aea60b951c7fb678d969956ce0a85bafa69fb08f56de8bc2ba821d

See more details on using hashes here.

Supported by

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