innerscope


Nameinnerscope JSON
Version 0.7.0 PyPI version JSON
download
home_page
SummaryExpose the inner scope of functions
upload_time2024-02-17 22:08:10
maintainer
docs_urlNone
authorInnerscope contributors
requires_python>=3.8
licenseCopyright (c) 2020 Erik Welch All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of innerscope nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
keywords introspection dict utility bytecode
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Innerscope

[![conda-forge](https://img.shields.io/conda/vn/conda-forge/innerscope.svg)](https://anaconda.org/conda-forge/innerscope)
[![pypi](https://img.shields.io/pypi/v/innerscope.svg)](https://pypi.python.org/pypi/innerscope/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/innerscope)](https://pypi.python.org/pypi/innerscope/)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/eriknw/innerscope/blob/master/LICENSE)
[![Tests](https://github.com/eriknw/innerscope/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/eriknw/innerscope/actions)
[![Coverage](https://codecov.io/gh/eriknw/innerscope/graph/badge.svg?token=7WW2DKBYSS)](https://codecov.io/gh/eriknw/innerscope)

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

**To install with pip:**
`pip install innerscope`

**To install with conda:**
`conda install -c conda-forge innerscope`

A function can be made to act like a dictionary:
```python
@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:
```python
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:
```python
@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.

```python
# 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!
```python
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()
```
```diff
- UserWarning: Undefined variables: 'global_x', 'closure_y'.
- Perhaps use `bind` method to assign values for these names before calling.
```
```python
>>> 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 *does not* use `exec`, `eval`, the AST, or source code.  It runs on [CPython](https://www.python.org/), [PyPy](https://www.pypy.org/), and [Stackless Python](https://github.com/stackless-dev/stackless/wiki).  You should feel comfortable using `innerscope`.  It actually offers *two* methods for obtaining the inner scope, and both are very reliable.  Of course we're doing *something* magical under the hood, and I would love to explain how some day.

## Why?
It's all [@mrocklin's](https://github.com/mrocklin) fault for [asking a question.](https://github.com/dask/distributed/issues/4003)
`innerscope` is exploring a data model that could be convenient for running code remotely with [dask.](https://dask.org)
I bet it would even be useful for building pipelines with dask.  I'm sure there are other creative uses for it just waiting to be discovered. **Update:** and [`afar`](https://github.com/eriknw/afar) has been born!

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

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "innerscope",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "Erik Welch <erik.n.welch@gmail.com>",
    "keywords": "introspection,dict,utility,bytecode",
    "author": "Innerscope contributors",
    "author_email": "Erik Welch <erik.n.welch@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/80/b6/49df5f146c35f17754dad7867d849ca9b8442c33f8f7e3e31177a4b71fa9/innerscope-0.7.0.tar.gz",
    "platform": null,
    "description": "# Innerscope\n\n[![conda-forge](https://img.shields.io/conda/vn/conda-forge/innerscope.svg)](https://anaconda.org/conda-forge/innerscope)\n[![pypi](https://img.shields.io/pypi/v/innerscope.svg)](https://pypi.python.org/pypi/innerscope/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/innerscope)](https://pypi.python.org/pypi/innerscope/)\n[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/eriknw/innerscope/blob/master/LICENSE)\n[![Tests](https://github.com/eriknw/innerscope/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/eriknw/innerscope/actions)\n[![Coverage](https://codecov.io/gh/eriknw/innerscope/graph/badge.svg?token=7WW2DKBYSS)](https://codecov.io/gh/eriknw/innerscope)\n\n`innerscope` exposes the inner scope of functions and offers primitives suitable for creating pipelines.  It explores a design space around functions, dictionaries, and classes.\n\n**To install with pip:**\n`pip install innerscope`\n\n**To install with conda:**\n`conda install -c conda-forge innerscope`\n\nA function can be made to act like a dictionary:\n```python\n@innerscope.call\ndef info():\n    first_name = 'Erik'\n    last_name = 'Welch'\n    full_name = f'{first_name} {last_name}'\n    return 'success!'\n\n>>> info['first_name']\n'Erik'\n>>> info['full_name']\n'Erik Welch'\n>>> info.return_value\n'success!'\n```\nSometimes we want functions to be more *functional* and accept arguments:\n```python\nif is_a_good_idea:\n    suffix = 'the amazing'\nelse:\n    suffix = 'the bewildering'\n\n@innerscope.callwith(suffix)\ndef info_with_suffix(suffix=None):\n    first_name = 'Erik'\n    last_name = 'Welch'\n    full_name = f'{first_name} {last_name}'\n    if suffix:\n        full_name = f'{full_name} {suffix}'\n\n>>> info_with_suffix['full_name']\n'Erik Welch the bewildering'\n```\nCool!\n\nBut, 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:\n```python\n@info.bindto\ndef add_suffix(suffix):\n    full_name = f'{first_name} {last_name} {suffix}'\n\n>>> scope = add_suffix('the astonishing')\n>>> scope['full_name']\n'Erik Welch the astonishing'\n```\n`add_suffix` here is a `ScopedFunction`.  It returns a `Scope`, which is the dict-like object we've already seen.\n\n## `scoped_function` ftw!\n\nExcept for the simplest tasks (as with `call` and `callwith` above), using `scoped_function` should usually be preferred.\n\n```python\n# step1 becomes a ScopedFunction that we can call\n@scoped_function\ndef step1(a):\n    b = a + 1\n\n>>> scope1 = step1(1)\n>>> scope1 == {'a': 1, 'b': 2}\nTrue\n\n# Bind any number of mappings to variables (later mappings have precedence)\n@scoped_function(scope1, {'c': 3})\ndef step2(d):\n    e = max(a + d, b + c)\n\n>>> step2.outer_scope == {'a': 1, 'b': 2, 'c': 3}\nTrue\n>>> scope2 = step2(4)\n>>> scope2 == {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}\nTrue\n>>> scope2.inner_scope == {'d': 4, 'e': 5}\nTrue\n```\nSuppose you're paranoid (like me!) and want to control whether a function uses values from closures or globals.  You're in luck!\n```python\nglobal_x = 1\n\ndef f():\n    closure_y = 2\n    def g():\n        local_z = global_x + closure_y\n    return g\n\n# If you're the trusting type...\n>>> g = f()\n>>> innerscope.call(g) == {'global_x': 1, 'closure_y': 2, 'local_z': 3}\nTrue\n\n# And for the intelligent...\n>>> paranoid_g = scoped_function(g, use_closures=False, use_globals=False)\n>>> paranoid_g.missing\n{'closure_y', 'global_x'}\n>>> paranoid_g()\n```\n```diff\n- UserWarning: Undefined variables: 'global_x', 'closure_y'.\n- Perhaps use `bind` method to assign values for these names before calling.\n```\n```python\n>>> new_g = paranoid_g.bind({'global_x': 100, 'closure_y': 200})\n>>> new_g.missing\nset()\n>>> new_g() == {'global_x': 100, 'closure_y': 200, 'local_z': 300}\nTrue\n```\n## How?\nThis library *does not* use `exec`, `eval`, the AST, or source code.  It runs on [CPython](https://www.python.org/), [PyPy](https://www.pypy.org/), and [Stackless Python](https://github.com/stackless-dev/stackless/wiki).  You should feel comfortable using `innerscope`.  It actually offers *two* methods for obtaining the inner scope, and both are very reliable.  Of course we're doing *something* magical under the hood, and I would love to explain how some day.\n\n## Why?\nIt's all [@mrocklin's](https://github.com/mrocklin) fault for [asking a question.](https://github.com/dask/distributed/issues/4003)\n`innerscope` is exploring a data model that could be convenient for running code remotely with [dask.](https://dask.org)\nI bet it would even be useful for building pipelines with dask.  I'm sure there are other creative uses for it just waiting to be discovered. **Update:** and [`afar`](https://github.com/eriknw/afar) has been born!\n\n#### *This library is totally awesome and you should use it and tell all your friends* \ud83d\ude09 *!*\n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2020 Erik Welch  All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of innerscope nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ",
    "summary": "Expose the inner scope of functions",
    "version": "0.7.0",
    "project_urls": {
        "changelog": "https://github.com/eriknw/innerscope/releases",
        "homepage": "https://github.com/eriknw/innerscope",
        "repository": "https://github.com/eriknw/innerscope"
    },
    "split_keywords": [
        "introspection",
        "dict",
        "utility",
        "bytecode"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "70fb42ce9736a3a6f54ba4a48595e55287834ba30814a5d944391a8874f5ce4b",
                "md5": "07d82b25338bbd6f839817b7d2bb2f89",
                "sha256": "a57a347e8aaf1b5c91805e57a9922a751c0f858dbc4e5b5ef510ef743222b193"
            },
            "downloads": -1,
            "filename": "innerscope-0.7.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "07d82b25338bbd6f839817b7d2bb2f89",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 19268,
            "upload_time": "2024-02-17T22:08:08",
            "upload_time_iso_8601": "2024-02-17T22:08:08.908586Z",
            "url": "https://files.pythonhosted.org/packages/70/fb/42ce9736a3a6f54ba4a48595e55287834ba30814a5d944391a8874f5ce4b/innerscope-0.7.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "80b649df5f146c35f17754dad7867d849ca9b8442c33f8f7e3e31177a4b71fa9",
                "md5": "0817a0b5641c78a42f25ea7d1f337712",
                "sha256": "f5423410867360a1d00f6533c55a4af0a2e050d43b424394e51c59b28db2ef4d"
            },
            "downloads": -1,
            "filename": "innerscope-0.7.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0817a0b5641c78a42f25ea7d1f337712",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 22866,
            "upload_time": "2024-02-17T22:08:10",
            "upload_time_iso_8601": "2024-02-17T22:08:10.724426Z",
            "url": "https://files.pythonhosted.org/packages/80/b6/49df5f146c35f17754dad7867d849ca9b8442c33f8f7e3e31177a4b71fa9/innerscope-0.7.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-17 22:08:10",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "eriknw",
    "github_project": "innerscope",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "innerscope"
}
        
Elapsed time: 1.39382s