Skip to content

Commit

Permalink
add assignment path
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnMount committed Feb 4, 2024
1 parent 6bfb982 commit cd3a0cb
Show file tree
Hide file tree
Showing 13 changed files with 2,344 additions and 1,476 deletions.
7 changes: 4 additions & 3 deletions coverage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ tests/test_render_main.py . [100%]
Name Stmts Miss Cover
---------------------------------------------
wvpy/__init__.py 3 0 100%
wvpy/jtools.py 393 116 70%
wvpy/assignment.py 30 30 0%
wvpy/jtools.py 390 115 71%
wvpy/ptools.py 66 6 91%
wvpy/pysheet.py 99 49 51%
wvpy/render_workbook.py 63 32 49%
wvpy/util.py 4 0 100%
---------------------------------------------
TOTAL 628 203 68%
TOTAL 655 232 65%


============================= 18 passed in 38.91s ==============================
============================= 18 passed in 36.61s ==============================
161 changes: 161 additions & 0 deletions examples/declare_variables/record_example.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Example parameterized Jupyter notebook."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from wvpy.assignment import (\n",
" assign_values_from_map, \n",
" dict_to_assignments_str,\n",
" ensure_names_not_already_assigned,\n",
" record_assignments,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# overrides (usually this comes from external driver)\n",
"# In practice sheet_vars will be set by the system runner, this form informs Jupyter or the IDE \n",
"# that sheet_vars is a defined variable.\n",
"sheet_vars = globals().get(\"sheet_vars\", {}) # take previous value if there is one, else empty dictionary"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# check that none of the variables we want to override are already assigned\n",
"# this check is to cut down confusion of having these assigned somewhere other than\n",
"# the context manager that is the next block\n",
"ensure_names_not_already_assigned(globals(), keys=sheet_vars.keys())"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# set some variables to default values we are willing to override\n",
"# we do this with the with context manager so that our Jupyter or IDE thinks these variables are defined in our environment\n",
"# this defines both the set of names we allow overriding of and default values so we can debug in and IDE\n",
"assignments = {}\n",
"with record_assignments(globals(), result=assignments):\n",
" # default values to be overridden\n",
" city = \"Los Angeles\"\n",
" state = \"CA\"\n",
" country = \"USA\""
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"default assignments are:\n",
"{'city': 'Los Angeles', 'country': 'USA', 'state': 'CA'}\n"
]
}
],
"source": [
"print(\"default assignments are:\")\n",
"print(assignments)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# override explicit assignments with values from sheet_vars\n",
"assign_values_from_map(\n",
" globals(),\n",
" values=sheet_vars,\n",
" expected_keys=assignments.keys(),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"running with the following actual values\n",
"\n",
"city = 'Los Angeles'\n",
"country = 'USA'\n",
"state = 'CA'\n",
"\n"
]
}
],
"source": [
"print(\"running with the following actual values\")\n",
"print(dict_to_assignments_str({k: globals()[k] for k in assignments.keys()}))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Analysis and results for Los Angeles.\n"
]
}
],
"source": [
"# small simulation of parameterized analysis process\n",
"print(f\"Analysis and results for {city}.\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "wvpy_dev_env",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
101 changes: 101 additions & 0 deletions pkg/build/lib/wvpy/assignment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Classes and functions for working with variable assignments"""

from typing import Any, Dict, Iterable, Optional
from contextlib import contextmanager


def dict_to_assignments_str(values: Dict[str, Any]) -> str:
"""
Format a dictionary as a block of Python assignment statements.
Example:
```python
from wvpy.assignment import dict_to_assignments_str
print(dict_to_assignments_str({"a": 1, "b": 2}))
```
:param values: dictionary
:return: assignment statements
"""
assert isinstance(values, dict)
return (
"\n"
+ "\n".join([f"{k} = {repr(values[k])}" for k in sorted(values.keys())])
+ "\n"
)


@contextmanager
def record_assignments(
env,
*,
result: Dict[str, Any],
) -> None:
"""
Context manager to record all assignments to new variables in a with-block.
New variables being variables not set prior to entering the with block.
Example:
```python
from wvpy.assignment import dict_to_assignments_str, record_assignments
assignments = {}
with record_assignments(globals(), result=assignments):
a = 1
b = 2
print(assignments)
print(dict_to_assignments_str(assignments))
```
:param env: working environment, setting to `globals()` is usually the correct choice.
:param result: dictionary to store results in. function calls `.clear()` on result.
:return None:
"""
assert isinstance(result, dict)
pre_known_vars = set(env.keys())
result.clear()
try:
yield
finally:
post_known_vars = set(env.keys())
declared_vars = post_known_vars - pre_known_vars
for k in sorted(declared_vars):
result[k] = env[k]


def ensure_names_not_already_assigned(env, *, keys: Iterable[str]) -> None:
"""
Check that no key in keys is already set in the environment env.
Raises ValueError if keys are already assigned.
:param env: working environment, setting to `globals()` is usually the correct choice.
:param keys: keys to confirm not already set.
:return None:
"""
already_assigned_vars = set(keys).intersection(set(env.keys()))
if len(already_assigned_vars) > 0:
raise ValueError(f"variables already set: {sorted(already_assigned_vars)}")


def assign_values_from_map(
env, *, values: Dict[str, Any], expected_keys: Optional[Iterable[str]]
) -> None:
"""
Assign values from map into environment.
:param env: working environment, setting to `globals()` is usually the correct choice.
:param values: dictionary to copy into environment.
:param expected_keys: if not null a set of keys we are restricting to
:return None:
"""
assert isinstance(values, dict)
for k in values.keys():
assert isinstance(k, str)
if expected_keys is not None:
unexpected_vars = set(values.keys()) - set(expected_keys)
if len(unexpected_vars) > 0:
raise ValueError(
f"attempting to assign undeclared variables: {sorted(unexpected_vars)}"
)
# do the assignments
for k, v in values.items():
env[k] = v
13 changes: 0 additions & 13 deletions pkg/build/lib/wvpy/jtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from typing import Any, Dict, Iterable, List, Optional
from functools import total_ordering
from contextlib import contextmanager
from collections import namedtuple

from wvpy.util import escape_ansi
from wvpy.ptools import execute_py
Expand Down Expand Up @@ -656,18 +655,6 @@ def run_pool(
return res


def write_dict_as_assignments(values: Dict[str, Any]) -> str:
"""
Write a dictionary as a block of Python assignment statements.
:param values: dictionary
:return: assignment statements
"""
return "\n" + "\n".join([
f"{k} = {repr(values[k])}" for k in sorted(values.keys())
]) + "\n"


@contextmanager
def declare_task_variables(
env,
Expand Down
Binary file modified pkg/dist/wvpy-1.1.1-py3-none-any.whl
Binary file not shown.
Binary file modified pkg/dist/wvpy-1.1.1.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion pkg/docs/search.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pkg/docs/wvpy.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

<h2>Submodules</h2>
<ul>
<li><a href="wvpy/assignment.html">assignment</a></li>
<li><a href="wvpy/jtools.html">jtools</a></li>
<li><a href="wvpy/ptools.html">ptools</a></li>
<li><a href="wvpy/pysheet.html">pysheet</a></li>
Expand Down
585 changes: 585 additions & 0 deletions pkg/docs/wvpy/assignment.html

Large diffs are not rendered by default.

Loading

0 comments on commit cd3a0cb

Please sign in to comment.