Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type checking with pyright #164

Merged
merged 47 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f4a3413
lower and upper more consistently
mccalluc Nov 4, 2024
12c1402
one more
mccalluc Nov 4, 2024
1274e75
handle bounds/bins/counts the same way
mccalluc Nov 5, 2024
19c3065
lots of reactive dicts, but the UI has not changed
mccalluc Nov 5, 2024
603d0bc
data dump on the results page
mccalluc Nov 5, 2024
ad32217
add a pragma: no cover
mccalluc Nov 5, 2024
c8b4ddc
reset widget values after checkbox change
mccalluc Nov 7, 2024
1247392
do not clean up values
mccalluc Nov 7, 2024
34e1170
resolve conflicts
mccalluc Nov 7, 2024
f35ed4f
resolve conflicts
mccalluc Nov 7, 2024
a11c9fa
resolve conflicts
mccalluc Nov 7, 2024
d84c4cd
use "upper" and "lower"
mccalluc Nov 7, 2024
16463b4
put tooltips in labels
mccalluc Nov 8, 2024
9cd991d
pull warning up to analysis panel. TODO: conditional
mccalluc Nov 8, 2024
f2b5192
move warning to bottom of list
mccalluc Nov 8, 2024
da6a0cb
analysis definition JSON
mccalluc Nov 8, 2024
0dbdd7b
stubs for python
mccalluc Nov 8, 2024
610404c
stub a script on results page
mccalluc Nov 8, 2024
c45585b
include column info in generated script
mccalluc Nov 8, 2024
5803715
closer to a runable notebook
mccalluc Nov 8, 2024
93c9543
stuck on split_by_weight... maybe a library bug?
mccalluc Nov 8, 2024
f27a175
margin stubs
mccalluc Nov 13, 2024
6093cfb
resolve conflicts
mccalluc Nov 13, 2024
6b8a38f
format python identifiers correctly
mccalluc Nov 13, 2024
1a9a2a7
script has gotten longer: does not make sense to check for exact equa…
mccalluc Nov 13, 2024
d3be33d
fix syntactic problems in generated code
mccalluc Nov 13, 2024
03c6dfa
fill in columns, but still WIP
mccalluc Nov 14, 2024
d560195
Merge dp_creator_ii -> dp_wizard
mccalluc Nov 15, 2024
a3abd8d
fix column names; tests pass
mccalluc Nov 15, 2024
a79dbdc
move confidence
mccalluc Nov 15, 2024
8ff945a
simplify download panel
mccalluc Nov 15, 2024
d635dd2
add markdown cells
mccalluc Nov 15, 2024
0362d38
tidy up
mccalluc Nov 15, 2024
291dcdb
switch requirements from mypy to pyright
mccalluc Nov 15, 2024
7b24d27
run pyright; currently fails
mccalluc Nov 15, 2024
6e53212
fix pyright errors
mccalluc Nov 15, 2024
b8b76e1
type input, output, session
mccalluc Nov 15, 2024
d6694c1
rm unused session
mccalluc Nov 15, 2024
79fc793
more typing
mccalluc Nov 15, 2024
72c9282
Merge main
mccalluc Nov 18, 2024
238b703
enable strict type checking; lots of errors!
mccalluc Nov 18, 2024
8fde82b
more typing. Add "finish()" for Templates
mccalluc Nov 18, 2024
ab6faf4
add a lot of ignores, but type checks pass locally
mccalluc Nov 18, 2024
6c35bca
I think Optional is needed under 3.9
mccalluc Nov 18, 2024
bb67346
Remove strict, and most of the ignores
mccalluc Nov 21, 2024
39defe6
merge main, resolve conflicts, fix tests
mccalluc Nov 21, 2024
af25659
tests in code generation
mccalluc Nov 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .mypy.ini

This file was deleted.

13 changes: 7 additions & 6 deletions dp_wizard/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from pathlib import Path
import logging

from shiny import App, ui, reactive
from shiny import App, ui, reactive, Inputs, Outputs, Session

from dp_wizard.utils.argparse_helpers import get_cli_info
from dp_wizard.utils.argparse_helpers import get_cli_info, CLIInfo
from dp_wizard.app import analysis_panel, dataset_panel, results_panel, feedback_panel


Expand All @@ -26,16 +26,17 @@ def ctrl_c_reminder(): # pragma: no cover
print("Session ended (Press CTRL+C to quit)")


def make_server_from_cli_info(cli_info):
def server(input, output, session): # pragma: no cover
csv_path = reactive.value(cli_info.csv_path)
def make_server_from_cli_info(cli_info: CLIInfo):
def server(input: Inputs, output: Outputs, session: Session): # pragma: no cover
cli_csv_path = cli_info.csv_path
csv_path = reactive.value("" if cli_csv_path is None else cli_csv_path)
contributions = reactive.value(cli_info.contributions)

lower_bounds = reactive.value({})
upper_bounds = reactive.value({})
bin_counts = reactive.value({})
weights = reactive.value({})
epsilon = reactive.value(1)
epsilon = reactive.value(1.0)

dataset_panel.dataset_server(
input,
Expand Down
51 changes: 27 additions & 24 deletions dp_wizard/app/analysis_panel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from math import pow
from typing import Iterable, Any

from shiny import ui, reactive, render, req
from shiny import ui, reactive, render, req, Inputs, Outputs, Session

from dp_wizard.app.components.inputs import log_slider
from dp_wizard.app.components.column_module import column_ui, column_server
Expand Down Expand Up @@ -39,7 +40,9 @@ def analysis_ui():
)


def _cleanup_reactive_dict(reactive_dict, keys_to_keep): # pragma: no cover
def _cleanup_reactive_dict(
reactive_dict: reactive.Value[dict[str, Any]], keys_to_keep: Iterable[str]
): # pragma: no cover
reactive_dict_copy = {**reactive_dict()}
keys_to_del = set(reactive_dict_copy.keys()) - set(keys_to_keep)
for key in keys_to_del:
Expand All @@ -48,25 +51,25 @@ def _cleanup_reactive_dict(reactive_dict, keys_to_keep): # pragma: no cover


def analysis_server(
input,
output,
session,
csv_path,
contributions,
is_demo,
lower_bounds,
upper_bounds,
bin_counts,
weights,
epsilon,
input: Inputs,
output: Outputs,
session: Session,
csv_path: reactive.Value[str],
contributions: reactive.Value[int],
is_demo: bool,
lower_bounds: reactive.Value[dict[str, float]],
upper_bounds: reactive.Value[dict[str, float]],
bin_counts: reactive.Value[dict[str, int]],
weights: reactive.Value[dict[str, str]],
epsilon: reactive.Value[float],
): # pragma: no cover
@reactive.calc
def button_enabled():
column_ids_selected = input.columns_checkbox_group()
return len(column_ids_selected) > 0

@reactive.effect
def _update_checkbox_group():
def _update_checkbox_group(): # type: ignore unused function
ui.update_checkbox_group(
"columns_checkbox_group",
label=None,
Expand All @@ -75,15 +78,15 @@ def _update_checkbox_group():

@reactive.effect
@reactive.event(input.columns_checkbox_group)
def _on_column_set_change():
def _on_column_set_change(): # type: ignore unused function
column_ids_selected = input.columns_checkbox_group()
# We only clean up the weights, and everything else is left in place,
# so if you restore a column, you see the original values.
# (Except for weight, which goes back to the default.)
_cleanup_reactive_dict(weights, column_ids_selected)

@render.ui
def columns_checkbox_group_tooltip_ui():
def columns_checkbox_group_tooltip_ui(): # type: ignore unused function
return demo_tooltip(
is_demo,
"""
Expand All @@ -94,7 +97,7 @@ def columns_checkbox_group_tooltip_ui():
)

@render.ui
def columns_ui():
def columns_ui(): # type: ignore unused function
column_ids = input.columns_checkbox_group()
column_ids_to_names = csv_ids_names_calc()
column_ids_to_labels = csv_ids_labels_calc()
Expand Down Expand Up @@ -133,7 +136,7 @@ def columns_ui():
"""
)
],
col_widths=col_widths,
col_widths=col_widths, # type: ignore
)
if column_ids
else []
Expand All @@ -150,7 +153,7 @@ def csv_ids_labels_calc():
return read_csv_ids_labels(req(csv_path()))

@render.ui
def epsilon_tooltip_ui():
def epsilon_tooltip_ui(): # type: ignore unused function
return demo_tooltip(
is_demo,
"""
Expand All @@ -162,24 +165,24 @@ def epsilon_tooltip_ui():

@reactive.effect
@reactive.event(input.log_epsilon_slider)
def _set_epsilon():
def _set_epsilon(): # type: ignore unused function
epsilon.set(pow(10, input.log_epsilon_slider()))

@render.text
def epsilon_text():
def epsilon_text(): # type: ignore unused function
return f"Epsilon: {epsilon():0.3}"

@render.code
def privacy_loss_python():
def privacy_loss_python(): # type: ignore unused function
return make_privacy_loss_block(epsilon())

@reactive.effect
@reactive.event(input.go_to_results)
def go_to_results():
def go_to_results(): # type: ignore unused function
ui.update_navs("top_level_nav", selected="results_panel")

@render.ui
def download_results_button_ui():
def download_results_button_ui(): # type: ignore unused function
button = ui.input_action_button(
"go_to_results", "Download results", disabled=not button_enabled()
)
Expand Down
66 changes: 33 additions & 33 deletions dp_wizard/app/components/column_module.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from logging import info

from shiny import ui, render, module, reactive
from shiny import ui, render, module, reactive, Inputs, Outputs, Session

from dp_wizard.utils.dp_helper import make_confidence_accuracy_histogram
from dp_wizard.utils.shared import plot_histogram
from dp_wizard.utils.templates import make_column_config_block
from dp_wizard.app.components.outputs import output_code_sample, demo_tooltip


default_weight = 2
default_weight = "2"

col_widths = {
# Controls stay roughly a constant width;
# Graph expands to fill space.
"sm": (4, 8),
"md": (3, 9),
"lg": (2, 10),
"sm": [4, 8],
"md": [3, 9],
"lg": [2, 10],
}


Expand All @@ -37,9 +37,9 @@ def column_ui(): # pragma: no cover
"weight",
["Weight", ui.output_ui("weight_tooltip_ui")],
choices={
1: "Less accurate",
"1": "Less accurate",
default_weight: "Default",
4: "More accurate",
"4": "More accurate",
},
selected=default_weight,
width=width,
Expand All @@ -50,54 +50,54 @@ def column_ui(): # pragma: no cover
# Make plot smaller than default: about the same size as the other column.
output_code_sample("Column Definition", "column_code"),
],
col_widths=col_widths,
col_widths=col_widths, # type: ignore
)


@module.server
def column_server(
input,
output,
session,
name,
contributions,
epsilon,
lower_bounds,
upper_bounds,
bin_counts,
weights,
is_demo,
input: Inputs,
output: Outputs,
session: Session,
name: str,
contributions: int,
epsilon: float,
lower_bounds: reactive.Value[dict[str, float]],
upper_bounds: reactive.Value[dict[str, float]],
bin_counts: reactive.Value[dict[str, int]],
weights: reactive.Value[dict[str, str]],
is_demo: bool,
): # pragma: no cover
@reactive.effect
def _set_all_inputs():
def _set_all_inputs(): # type: ignore
with reactive.isolate(): # Without isolate, there is an infinite loop.
ui.update_numeric("lower", value=lower_bounds().get(name, 0))
ui.update_numeric("upper", value=upper_bounds().get(name, 10))
ui.update_numeric("bins", value=bin_counts().get(name, 10))
ui.update_numeric("weight", value=weights().get(name, default_weight))
ui.update_numeric("weight", value=int(weights().get(name, default_weight)))

@reactive.effect
@reactive.event(input.lower)
def _set_lower():
def _set_lower(): # type: ignore
lower_bounds.set({**lower_bounds(), name: float(input.lower())})

@reactive.effect
@reactive.event(input.upper)
def _set_upper():
def _set_upper(): # type: ignore
upper_bounds.set({**upper_bounds(), name: float(input.upper())})

@reactive.effect
@reactive.event(input.bins)
def _set_bins():
bin_counts.set({**bin_counts(), name: float(input.bins())})
def _set_bins(): # type: ignore
bin_counts.set({**bin_counts(), name: int(input.bins())})

@reactive.effect
@reactive.event(input.weight)
def _set_weight():
weights.set({**weights(), name: float(input.weight())})
def _set_weight(): # type: ignore
weights.set({**weights(), name: input.weight()})

@render.ui
def bounds_tooltip_ui():
def bounds_tooltip_ui(): # type: ignore
return demo_tooltip(
is_demo,
"""
Expand All @@ -111,7 +111,7 @@ def bounds_tooltip_ui():
)

@render.ui
def bins_tooltip_ui():
def bins_tooltip_ui(): # type: ignore
return demo_tooltip(
is_demo,
"""
Expand All @@ -124,7 +124,7 @@ def bins_tooltip_ui():
)

@render.ui
def weight_tooltip_ui():
def weight_tooltip_ui(): # type: ignore
return demo_tooltip(
is_demo,
"""
Expand All @@ -135,7 +135,7 @@ def weight_tooltip_ui():
)

@render.code
def column_code():
def column_code(): # type: ignore
return make_column_config_block(
name=name,
lower_bound=float(input.lower()),
Expand All @@ -144,12 +144,12 @@ def column_code():
)

@render.plot()
def column_plot():
def column_plot(): # type: ignore
lower_x = float(input.lower())
upper_x = float(input.upper())
bin_count = int(input.bins())
weight = float(input.weight())
weights_sum = sum(weights().values())
weights_sum = sum(float(weight) for weight in weights().values())
info(f"Weight ratio for {name}: {weight}/{weights_sum}")
if weights_sum == 0:
# This function is triggered when column is removed;
Expand Down
2 changes: 1 addition & 1 deletion dp_wizard/app/components/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from shiny import ui


def log_slider(id, lower, upper):
def log_slider(id: str, lower: float, upper: float):
# Rather than engineer a new widget, hide the numbers we don't want.
# The rendered widget doesn't have a unique ID, but the following
# element does, so we can use some fancy CSS to get the preceding element.
Expand Down
4 changes: 2 additions & 2 deletions dp_wizard/app/components/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from faicons import icon_svg


def output_code_sample(title, name_of_render_function):
def output_code_sample(title: str, name_of_render_function: str):
return details(
summary(f"Code sample: {title}"),
ui.output_code(name_of_render_function),
)


def demo_tooltip(is_demo, text): # pragma: no cover
def demo_tooltip(is_demo: bool, text: str): # pragma: no cover
if is_demo:
return ui.tooltip(
icon_svg("circle-question"),
Expand Down
Loading
Loading