Skip to content

Commit

Permalink
Merge pull request #4 from fabianoengler/rename_vars_with_kwargs
Browse files Browse the repository at this point in the history
Rename vars with kwargs
  • Loading branch information
fabianoengler committed Nov 21, 2019
2 parents 95cec04 + 524b756 commit 8483ffa
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -12,6 +12,7 @@ __pycache__/
*.egg-info/
.installed.cfg
*.egg
/dist/

# Installer logs
pip-log.txt
Expand Down
14 changes: 14 additions & 0 deletions CHANGES.md
@@ -0,0 +1,14 @@
Version 0.2.0
-------------

Released 2019-11-21

- Added keyword args to rename variables.


Version 0.1.0
-------------

Released 2019-11-21

- First public release.
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -1,4 +1,5 @@
include README.md
include CHANGES.md
include LICENSE.txt
include .coveragerc
recursive-include docs *.py
39 changes: 35 additions & 4 deletions README.md
Expand Up @@ -120,6 +120,31 @@ Output is the same in all versions:
'myapp': 'MyApp'}
```

## Renaming variables

The standard kwargs syntax of `dict` is also supported by `dictvars`.

Suppose you have a variable `current_user` but you want to use only `user`
on your dict:

```python
def somefunc_dictvars(current_user):
form = dict(some='very', complex_='expression')
comments = ['bla', 'bla']

return dictvars(form, comments, app=myapp, user=current_user)
```

Works as expected:

```
{'app': 'MyApp',
'comments': ['bla', 'bla'],
'form': {'some': 'very', 'complex_': 'expression'},
'user': 'John Do'}
```


## Limitations

To create a dict from the passed variables, some "magic" is done to
Expand Down Expand Up @@ -158,17 +183,16 @@ additional variables returned usually can just be ignored.

I'm not sure how to fix this yet. Open to suggestions.

If this is a problem on a specific context, one can just swap
`dictvars` for `varsnamed` and pass the variable names as strings
instead:
If this is a problem on a specific context, one can just pass the
offending variable with a explicit name, just like a regular dict:

```python
def somefunc():
a = '1'
b = '2'
c = '3'
no_leaks_now = b
return varsnamed('a', 'b')
return dictvars(a, b=b)

print(somefunc())
```
Expand All @@ -178,3 +202,10 @@ Returns:
{'a': '1',
'b': '2'}
```

Yet another alternative in such cases would be to swap `dictvars` for
`varsnamed`:

```python
return varsnamed('a', 'b')
```
10 changes: 7 additions & 3 deletions dictvars/__init__.py
@@ -1,7 +1,7 @@

import inspect

__version__ = '0.1.0'
__version__ = '0.2.0'

__all__ = ('dictvars', 'varsnamed', 'compact')

Expand All @@ -10,10 +10,11 @@ def _flatten(l):
return (item for sublist in l for item in sublist)


def dictvars(*variables):
def dictvars(*variables, **kw_variables):
"""Creates a dict with the variables passed as arguments.
The keys of the dict are the inferred names of the passed variables.
The keys of the dict are the inferred names of the variables passed
as arguments, plus all keys passed as keyword arguments.
"""

caller = inspect.stack()[1][0]
Expand All @@ -26,6 +27,9 @@ def dictvars(*variables):
vars[name] = var
break

for name, var in kw_variables.items():
vars[name] = var

return vars


Expand Down
35 changes: 34 additions & 1 deletion docs/more_examples.py
Expand Up @@ -150,6 +150,21 @@ def somefunc_dictvars_global():
return dictvars(form, comments, myapp)


def somefunc_dictvars_kwargs():
# pretend this is a controller code that makes sense
current_user = dict(some='very', complex_='expression')
permission = current_user.get('permission', False)
user_has_permission = bool(permission)
form = dict(another='object', perm=user_has_permission)
comments = []
for values in [d.values() for d in [current_user, form]]:
comments.extend([v for v in values if isinstance(v, str)])

# explicitly renaming a variable using keywords syntax
# just like a regular dict
return dictvars(form, comments, user=current_user)


def somefunc_dictvars_local_leak():
# pretend this is a controller code that makes sense
user = dict(some='very', complex_='expression')
Expand Down Expand Up @@ -184,6 +199,24 @@ def somefunc_dictvars_global_leak():
return dictvars(form, comments, myapp)


def somefunc_dictvars_kwargs_nonleak():
# pretend this is a controller code that makes sense
user = dict(some='very', complex_='expression')
permission = user.get('permission', False)
user_has_permission = bool(permission)
form = dict(another='object', perm=user_has_permission)
comments = []
global leaked_var_global
leaked_var_local = form
leaked_var_global = myapp
for values in [d.values() for d in [user, form]]:
comments.extend([v for v in values if isinstance(v, str)])

# if the vars are explicitly named (using kwargs syntax)
# any leaks can be supressed
return dictvars(comments, form=form, myapp=myapp)


def somefunc_dictvars_local_python_optimization():
some_num = 42
some_str = 'hi'
Expand All @@ -206,7 +239,7 @@ def main():
from textwrap import indent
for name in globals():
if name.startswith('somefunc_'):
print(f"\n{name}():")
print("\n{}():".format(name))
print(indent(pformat(globals()[name]()), ' '*4))


Expand Down
6 changes: 5 additions & 1 deletion docs/readme_example1.py
Expand Up @@ -44,8 +44,12 @@ def somefunc_varsnamed():
return varsnamed('form', 'comments', 'myapp')


if __name__ == '__main__':
def main():
from pprint import pprint
pprint(somefunc_regular_python())
pprint(somefunc_dictvars())
pprint(somefunc_varsnamed())


if __name__ == '__main__':
main()
13 changes: 12 additions & 1 deletion docs/readme_example2.py
Expand Up @@ -18,7 +18,18 @@ def somefunc2():
b = '2'
c = '3'
no_leaks_now = b
return varsnamed('a', 'b')
return dictvars(a, b=b)


print(somefunc2())


def somefunc3():
a = '1'
b = '2'
c = '3'
no_leaks_now = b
return varsnamed('a', 'b')


print(somefunc3())
17 changes: 17 additions & 0 deletions docs/readme_example3.py
@@ -0,0 +1,17 @@

from dictvars import dictvars, varsnamed


myapp = 'MyApp' # global var


def somefunc_dictvars(current_user):
form = dict(some='very', complex_='expression')
comments = ['bla', 'bla']

return dictvars(form, comments, app=myapp, user=current_user)


if __name__ == '__main__':
from pprint import pprint
pprint(somefunc_dictvars('John Do'))
19 changes: 19 additions & 0 deletions docs/test_examples.py
@@ -0,0 +1,19 @@

from pathlib import Path
from importlib import import_module

import pytest


modules_names = [p.stem
for p in Path(__file__).parent.glob('*example*.py')
if not p.stem.startswith('test_')]


@pytest.mark.parametrize("module_name", modules_names)
def test_examples(module_name):
"""just to make sure all examples are runnable and correct"""
print(module_name)
module = import_module(module_name)
if hasattr(module, 'main'):
module.main()
59 changes: 59 additions & 0 deletions tests/test_dictvars.py
Expand Up @@ -100,6 +100,65 @@ def doublesum(a, b):
assert rv['some_global_var'] == some_global_var


def test_dictvars_local_kwargs():

from dictvars import dictvars

def somefunc():
a = 1
b = 2
c = 3
d = 4
e = 5
non_leak = b
return dictvars(a, e, renamed=b, d=d)

rv = somefunc()
assert set(rv.keys()) == {'a', 'e', 'renamed', 'd'}
assert rv['renamed'] == 2


def test_dictvars_outer_kwargs():

from dictvars import dictvars

outer1 = 'outer variable 1'
outer2 = 'outer variable 2'
outer3 = 'outer variable 3'

def somefunc():
a = 1
b = 2
c = 3
d = 4
e = 5
non_leak = b
return dictvars(a, e, outer1, renamed=b, d=d, outer_ren=outer3)

rv = somefunc()
assert set(rv.keys()) == {'a', 'e', 'outer1', 'renamed', 'd', 'outer_ren'}
assert rv['outer1'] == outer1
assert rv['outer_ren'] == outer3


def test_dictvars_global_kwargs():

from dictvars import dictvars

def somefunc():
a = 1
b = 2
c = 3
d = 4
e = 5
non_leak = b
return dictvars(a, e, global_renamed=some_global_var)

rv = somefunc()
assert set(rv.keys()) == {'a', 'e', 'global_renamed'}
assert rv['global_renamed'] == some_global_var


def test_varsnamed_local():

from dictvars import varsnamed
Expand Down

0 comments on commit 8483ffa

Please sign in to comment.