Skip to content

Commit

Permalink
fixes #643
Browse files Browse the repository at this point in the history
  • Loading branch information
jph00 committed Oct 25, 2024
1 parent 56895f2 commit fe31209
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 5 deletions.
1 change: 1 addition & 0 deletions fastcore/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@
'fastcore.xtras.dataclass_src': ('xtras.html#dataclass_src', 'fastcore/xtras.py'),
'fastcore.xtras.dict2obj': ('xtras.html#dict2obj', 'fastcore/xtras.py'),
'fastcore.xtras.dumps': ('xtras.html#dumps', 'fastcore/xtras.py'),
'fastcore.xtras.exec_eval': ('xtras.html#exec_eval', 'fastcore/xtras.py'),
'fastcore.xtras.expand_wildcards': ('xtras.html#expand_wildcards', 'fastcore/xtras.py'),
'fastcore.xtras.flexicache': ('xtras.html#flexicache', 'fastcore/xtras.py'),
'fastcore.xtras.flexiclass': ('xtras.html#flexiclass', 'fastcore/xtras.py'),
Expand Down
30 changes: 25 additions & 5 deletions fastcore/xtras.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
__all__ = ['spark_chars', 'UNSET', 'walk', 'globtastic', 'maybe_open', 'mkdir', 'image_size', 'bunzip', 'loads', 'loads_multi',
'dumps', 'untar_dir', 'repo_details', 'run', 'open_file', 'save_pickle', 'load_pickle', 'parse_env',
'expand_wildcards', 'dict2obj', 'obj2dict', 'repr_dict', 'is_listy', 'mapped', 'IterLen',
'ReindexCollection', 'get_source_link', 'truncstr', 'sparkline', 'modify_exception', 'round_multiple',
'set_num_threads', 'join_path_file', 'autostart', 'EventTimer', 'stringfmt_names', 'PartialFormatter',
'partial_format', 'utc2local', 'local2utc', 'trace', 'modified_env', 'ContextManagers', 'shufflish',
'console_help', 'hl_md', 'type2str', 'dataclass_src', 'Unset', 'nullable_dc', 'make_nullable', 'flexiclass',
'asdict', 'is_typeddict', 'is_namedtuple', 'flexicache', 'time_policy', 'mtime_policy', 'timed_cache']
'ReindexCollection', 'exec_eval', 'get_source_link', 'truncstr', 'sparkline', 'modify_exception',
'round_multiple', 'set_num_threads', 'join_path_file', 'autostart', 'EventTimer', 'stringfmt_names',
'PartialFormatter', 'partial_format', 'utc2local', 'local2utc', 'trace', 'modified_env', 'ContextManagers',
'shufflish', 'console_help', 'hl_md', 'type2str', 'dataclass_src', 'Unset', 'nullable_dc', 'make_nullable',
'flexiclass', 'asdict', 'is_typeddict', 'is_namedtuple', 'flexicache', 'time_policy', 'mtime_policy',
'timed_cache']

# %% ../nbs/03_xtras.ipynb
from .imports import *
Expand Down Expand Up @@ -408,6 +409,25 @@ def __setstate__(self, s): self.coll,self.idxs,self.cache,self.tfm = s['coll'],s
shuffle="Randomly shuffle indices",
cache_clear="Clear LRU cache")

# %% ../nbs/03_xtras.ipynb
def exec_eval(code, # Code to exec/eval
g=None, # Globals namespace dict
l=None # Locals namespace dict
):
"Evaluate `code` in `g` (defaults to `globals()`) and `l` (defaults to `locals()`)"
import ast, inspect
frame = inspect.currentframe().f_back
if l is None: l = g if g else frame.f_locals
if g is None: g = frame.f_globals
tree = ast.parse(code, mode='exec')
if tree.body and isinstance(tree.body[-1], ast.Expr):
*statements, expr = tree.body
exec_code = compile(ast.Module(statements, []), filename="<string>", mode="exec")
expr_code = compile(ast.Expression(expr.value), filename="<string>", mode="eval")
exec(exec_code, g, l)
return eval(expr_code, g, l)
else: exec(code, g, l)

# %% ../nbs/03_xtras.ipynb
def _is_type_dispatch(x): return type(x).__name__ == "TypeDispatch"
def _unwrapped_type_dispatch_func(x): return x.first() if _is_type_dispatch(x) else x
Expand Down
104 changes: 104 additions & 0 deletions nbs/03_xtras.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,110 @@
"## Other Helpers"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"def exec_eval(code, # Code to exec/eval\n",
" g=None, # Globals namespace dict\n",
" l=None # Locals namespace dict\n",
" ):\n",
" \"Evaluate `code` in `g` (defaults to `globals()`) and `l` (defaults to `locals()`)\"\n",
" import ast, inspect\n",
" frame = inspect.currentframe().f_back\n",
" if l is None: l = g if g else frame.f_locals\n",
" if g is None: g = frame.f_globals\n",
" tree = ast.parse(code, mode='exec')\n",
" if tree.body and isinstance(tree.body[-1], ast.Expr):\n",
" *statements, expr = tree.body\n",
" exec_code = compile(ast.Module(statements, []), filename=\"<string>\", mode=\"exec\")\n",
" expr_code = compile(ast.Expression(expr.value), filename=\"<string>\", mode=\"eval\")\n",
" exec(exec_code, g, l)\n",
" return eval(expr_code, g, l)\n",
" else: exec(code, g, l)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is a combination of `eval` and `exec`, which behaves like ipython and Jupyter. If the last line is an expression, it is evaluated and the result is returned:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"exec_eval('''\n",
"def f(x): return x+1\n",
"f(1)\n",
"''')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By default, the code uses the caller's globals and locals. For instance, here `f` is available since it's been added to our symbol table:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3\n"
]
}
],
"source": [
"exec_eval('print(f(2))')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pass a dict as the `g` param in order to use an arbitrary namespace:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hi I am f.\n"
]
}
],
"source": [
"exec_eval('print(f)', {'f': 'Hi I am f.'})"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down

0 comments on commit fe31209

Please sign in to comment.