Skip to main content

Expose the inner scope of functions

Project description

Innerscope

Python Version Version License Build Status Coverage Status Code style

innerscope exposes the inner scope of functions and offers primitives suitable for creating pipelines. It explores a design space around functions, dictionaries, and classes.

A function can be made to act like a dictionary:

@innerscope.call
def info():
    first_name = 'Erik'
    last_name = 'Welch'
    full_name = f'{first_name} {last_name}'
    return 'success!'

>>> info['first_name']
'Erik'
>>> info['full_name']
'Erik Welch'
>>> info.return_value
'success!'

Sometimes we want functions to be more functional and accept arguments:

if is_a_good_idea:
    suffix = 'the amazing'
else:
    suffix = 'the bewildering'

@innerscope.callwith(suffix)
def info_with_suffix(suffix=None):
    first_name = 'Erik'
    last_name = 'Welch'
    full_name = f'{first_name} {last_name}'
    if suffix:
        full_name = f'{full_name} {suffix}'

>>> info_with_suffix['full_name']
'Erik Welch the bewildering'

Cool!

But, what if we want to reuse the data computed in info? We can control exactly what values are within scope inside of a function (including from closures and globals; more on these later). Let's bind the variables in info to a new function:

@info.bindto
def add_suffix(suffix):
    full_name = f'{first_name} {last_name} {suffix}'

>>> scope = add_suffix('the astonishing')
>>> scope['full_name']
'Erik Welch the astonishing'

add_suffix here is a ScopedFunction. It returns a Scope, which is the dict-like object we've already seen.

scoped_function ftw!

Except for the simplest tasks (as with call and callwith above), using scoped_function should usually be preferred.

# step1 becomes a ScopedFunction that we can call
@scoped_function
def step1(a):
    b = a + 1

>>> scope1 = step1(1)
>>> scope1 == {'a': 1, 'b': 2}
True

# Bind any number of mappings to variables (later mappings have precedence)
@scoped_function(scope1, {'c': 3})
def step2(d):
    e = max(a + d, b + c)

>>> step2.outer_scope == {'a': 1, 'b': 2, 'c': 3}
True
>>> scope2 = step2(4)
>>> scope2 == {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
True
>>> scope2.inner_scope == {'d': 4, 'e': 5}
True

Suppose you're paranoid (like me!) and want to control whether a function uses values from closures or globals. You're in luck!

global_x = 1

def f():
    closure_y = 2
    def g():
        local_z = global_x + closure_y
    return g

# If you're the trusting type...
>>> g = f()
>>> innerscope.call(g) == {'global_x': 1, 'closure_y': 2, 'local_z': 3}
True

# And for the intelligent...
>>> paranoid_g = scoped_function(g, use_closures=False, use_globals=False)
>>> paranoid_g.missing
{'closure_y', 'global_x'}
>>> paranoid_g()
- NameError: Undefined variables: 'global_x', 'closure_y'.
- Use `bind` method to assign values for these names before calling.
>>> new_g = paranoid_g.bind({'global_x': 100, 'closure_y': 200})
>>> new_g.missing
set()
>>> new_g() == {'global_x': 100, 'closure_y': 200, 'local_z': 300}
True

How?

This library required surprisingly little magic at first. Perhaps I'll explain it some day. I mean, it does modify the bytecode, so it's a little magical and sinful, but it does so in a very reliable way, so you should feel comfortable using this library.

Why?

It's all @mrocklin's fault for asking a question. innerscope is exploring a data model that could be convenient for running code remotely with dask. I bet it would even be useful for building pipelines with dask.

This library is totally awesome and you should use it and tell all your friends 😉 !

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

innerscope-0.1.0.tar.gz (32.0 kB view details)

Uploaded Source

Built Distribution

innerscope-0.1.0-py3-none-any.whl (14.7 kB view details)

Uploaded Python 3

File details

Details for the file innerscope-0.1.0.tar.gz.

File metadata

  • Download URL: innerscope-0.1.0.tar.gz
  • Upload date:
  • Size: 32.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.0

File hashes

Hashes for innerscope-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8877d0ac3fb5fe49e11194cd8c0444a64ba6c0e32c0aefac25798b5ca9458fa7
MD5 6b4ec8380d55e4ffe7df7e938c7ee965
BLAKE2b-256 f8a39363a7d7bb8dbf868f20a9cdb0d34b6db826d60373433eae50ee0f4cc7a3

See more details on using hashes here.

Provenance

File details

Details for the file innerscope-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: innerscope-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.0

File hashes

Hashes for innerscope-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35e8a0937d35d33e0ab7b2062e876030ad805b2a758e2e7b7495cf9aa42200e2
MD5 6f5941df88de3c3744a8eda1e02e3708
BLAKE2b-256 d99c114a1eba0ab077bd469e4677f1e640b66834453bc33c54940f75d5706b26

See more details on using hashes here.

Provenance

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