Skip to main content

Expose the inner scope of functions

Project description

Innerscope

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}'

>>> info['first_name']
'Erik'
>>> info['full_name']
'Erik Welch'

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, '__builtins__': __builtins__}
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 requires surprisingly little magic. Perhaps I'll explain it some day. Here's a hint: the wrapped function shouldn't have any return statements.

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.0.2.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

innerscope-0.0.2-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for innerscope-0.0.2.tar.gz
Algorithm Hash digest
SHA256 3b152c670095a3c28e46c716400b5da19cd0ef34e7d7bc292b1e75f28f17e7b6
MD5 ec90be8ad328d0a5d1b9005d9525442a
BLAKE2b-256 1aecf95a054707a4a3e0e6205b4a4536b1b728378ad5264808927dffe9f0ed5e

See more details on using hashes here.

Provenance

File details

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

File metadata

  • Download URL: innerscope-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 9.4 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/49.3.1 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.0

File hashes

Hashes for innerscope-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0eafd95cc016b80133cd9f950549270cd5d2584dba0530b7acca53a3b6892ee8
MD5 a36a281bce19efd4cb756a2d9b9d6841
BLAKE2b-256 b4a303bb06f626f2b3603b2df0775a7141169c477c6a2b4d160197933eda3ec0

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