-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bdc0ea8
Showing
11 changed files
with
1,039 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Vi | ||
*.swp | ||
*.swo | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
db.sqlite3 | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# celery beat schedule file | ||
celerybeat-schedule | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
# PyCharm | ||
.idea | ||
|
||
# Mac | ||
.DS_Store | ||
|
||
# VSCode | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
sudo: false | ||
language: python | ||
os: linux | ||
dist: xenial | ||
|
||
python: | ||
- "3.8" | ||
- "3.9-dev" | ||
|
||
install: | ||
- pip install toolz coverage pytest flake8 black | ||
- pip install -e . | ||
|
||
script: | ||
- coverage run --branch -m pytest --doctest-modules | ||
- flake8 | ||
- black innerscope --check --diff | ||
|
||
after_success: | ||
- coverage report --show-missing | ||
- pip install coveralls | ||
- coveralls | ||
|
||
notifications: | ||
email: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# 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. | ||
|
||
A function can be made like a dictionary: | ||
```python | ||
@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: | ||
```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, '__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! | ||
```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() | ||
``` | ||
<pre style='color:red'>NameError: Undefined variables: 'global_x', 'closure_y'. | ||
Use `bind` method to assign values for these names before calling.</pre> | ||
```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 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](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. | ||
|
||
#### *This library is totally awesome and you should use it and tell all your friends* 😉 *!* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .core import bindwith, call, callwith, scoped_function # noqa |
Oops, something went wrong.