Skip to content

Commit 829fc90

Browse files
yilibinbinAaronhaohaoclaude
authored
fix(audit-r2): misc findings (F4/F8/F9/F15) (#77)
* fix(audit-r2): close 4 remaining swarm-audit findings (F4/F8/F9/F15) F4 — remove dead extrapolation controls. richardson_p / levin_order / levin_weight / levin_beta were collected from both UIs, serialized into the payload and persisted to workspaces, but mpmath's mp.richardson(seq) and mp.levin(variant) accept no such parameters, so the compute layer silently ignored them. Removed the controls end-to-end (desktop views/window/mixin/ workers_core/workspace_controller, datalab_core builder, web logic, the ExtrapolationOptions dataclass, shared ui_specs, and help_specs.json), keeping the one Levin knob the backend honors — levin_variant. The richardson page and the emptied levin section stay as layout-parity stack pages, matching the existing shanks/wynn (fields=[]) convention. F8 — update_payload._urlopen now verifies HTTPS with certifi's CA bundle (ssl.create_default_context(cafile=certifi.where())), mirroring update_checker._urlopen. On frozen builds whose default OpenSSL context has no system CA store the check path could advertise an update the download path could never fetch. F9 — install_tinytex.sh rejects a non-https TINYTEX_INSTALLER_URL (the script is downloaded and executed, so http is remote code execution) and honors an optional TINYTEX_INSTALLER_SHA256 pin verified before execution. Upstream regenerates the installer, so the pin is opt-in rather than hard-coded. F15 — render_statistics_plot_from_spec's series_with_mean path filters non-finite values before handing them to matplotlib, matching its sibling histogram/box/qq renderers; a single NaN/Inf previously blanked the plot. TDD throughout (RED tests first). ruff clean; 407 neighborhood tests pass; GUI builds offscreen with the dead controls gone and the method stack intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(audit-r2): drop stale Levin visibility rule, fix Richardson label, sha256 case Review follow-ups on the F4/F9 changes: - get_parameter_visibility_rules() still returned a levin_u.beta rule that depended on the removed levin_u.weight control — a dangling reference after the F4 dead-control removal. Return {} (no dynamic rules remain); the web /api/ui_specs payload and desktop imports handle an empty map. - Richardson was labeled "(3-point)" / "(三点外推)" in the method spec, help text, and the desktop i18n map, but it is an accelerator that needs >=4 columns (the F4 help edit made the contradiction explicit). Rename to plain "Richardson" / "Richardson 序列加速" across ui_specs, help_specs, and window_i18n_mixin so the label and the documented minimum input agree. - install_tinytex.sh compared the SHA-256 pin against the raw digest; shasum emits lowercase but a pin may be pasted uppercase, causing a false mismatch. Normalize both with `tr` (Bash 3.2 safe, avoids the Bash-4 ${x,,} expansion). Verified: uppercase pin now matches under bash and system /bin/bash 3.2, a genuine mismatch still exits 2. ruff clean; 86 tests pass; GUI builds with a consistent Richardson label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: fanghao <fanghaoaa@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0bb3100 commit 829fc90

21 files changed

Lines changed: 265 additions & 392 deletions

app_desktop/views/extrapolation.py

Lines changed: 6 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from PySide6.QtCore import Qt
66
from PySide6.QtWidgets import (
77
QComboBox,
8-
QDoubleSpinBox,
98
QFormLayout,
109
QGroupBox,
1110
QHBoxLayout,
@@ -14,7 +13,6 @@
1413
QPlainTextEdit,
1514
QPushButton,
1615
QSizePolicy,
17-
QSpinBox,
1816
QVBoxLayout,
1917
QWidget,
2018
)
@@ -192,73 +190,15 @@ def build_extrapolation_mode_view(owner: Any) -> QGroupBox:
192190
],
193191
)
194192
levin_layout.addRow(lbl_variant, owner.levin_variant_combo)
193+
# order / weight / beta controls removed (audit F4): mpmath's mp.levin honors
194+
# only the variant, so those inputs were silently ignored.
195195

196-
lbl_order = QLabel("变换阶数:")
197-
owner._register_text(lbl_order, "变换阶数:", "Transform order:")
198-
owner.levin_order_spin = QSpinBox()
199-
owner.levin_order_spin.setRange(1, 10)
200-
owner.levin_order_spin.setValue(2)
201-
owner.levin_order_spin.setToolTip(
202-
owner._tr(
203-
"变换阶数(越高越精确但需要更多项,至少需要 2N+1 项数据)",
204-
"Transform order (higher = more accurate but needs more terms, requires at least 2N+1 data points)",
205-
)
206-
)
207-
levin_layout.addRow(lbl_order, owner.levin_order_spin)
208-
209-
lbl_weight = QLabel("权重函数:")
210-
owner._register_text(lbl_weight, "权重函数:", "Weight function:")
211-
owner.levin_weight_combo = QComboBox()
212-
owner.levin_weight_combo.addItem("默认 (1)", "default")
213-
owner.levin_weight_combo.addItem("1/(n+1)", "reciprocal")
214-
owner.levin_weight_combo.addItem("1/(n+β)", "reciprocal_beta")
215-
owner._register_combo(
216-
owner.levin_weight_combo,
217-
[
218-
("默认 (1)", "Default (1)", "default"),
219-
("1/(n+1)", "1/(n+1)", "reciprocal"),
220-
("1/(n+β)", "1/(n+β)", "reciprocal_beta"),
221-
],
222-
)
223-
owner.levin_weight_combo.currentIndexChanged.connect(owner._update_levin_weight_state)
224-
levin_layout.addRow(lbl_weight, owner.levin_weight_combo)
225-
226-
lbl_beta = QLabel("β 参数:")
227-
owner._register_text(lbl_beta, "β 参数:", "β parameter:")
228-
owner.levin_beta_spin = QDoubleSpinBox()
229-
owner.levin_beta_spin.setRange(0.01, 100.0)
230-
owner.levin_beta_spin.setValue(1.0)
231-
owner.levin_beta_spin.setSingleStep(0.1)
232-
owner.levin_beta_spin.setDecimals(2)
233-
owner.levin_beta_spin.setToolTip(
234-
owner._tr(
235-
"权重函数 ω(n) = 1/(n+β) 中的 β 参数",
236-
"β parameter in weight function ω(n) = 1/(n+β)",
237-
)
238-
)
239-
levin_layout.addRow(lbl_beta, owner.levin_beta_spin)
240-
lbl_beta.setVisible(False)
241-
owner.levin_beta_spin.setVisible(False)
242-
owner.levin_beta_label = lbl_beta
243-
196+
# Richardson has no tunable knobs (mp.richardson takes only the sequence),
197+
# so its page carries no fields — kept as a stack page for layout parity
198+
# with the other method pages. The former "p" control was dead (audit F4).
244199
owner.richardson_box = QGroupBox("Richardson 序列加速参数")
245200
owner._register_title(owner.richardson_box, "Richardson 序列加速参数", "Richardson acceleration parameters")
246-
richardson_layout = QFormLayout(owner.richardson_box)
247-
248-
lbl_richardson_p = QLabel("收敛幂指数 p:")
249-
owner._register_text(lbl_richardson_p, "收敛幂指数 p:", "Convergence power p:")
250-
owner.richardson_p_spin = QDoubleSpinBox()
251-
owner.richardson_p_spin.setRange(0.1, 10.0)
252-
owner.richardson_p_spin.setValue(2.0)
253-
owner.richardson_p_spin.setSingleStep(0.1)
254-
owner.richardson_p_spin.setDecimals(2)
255-
owner.richardson_p_spin.setToolTip(
256-
owner._tr(
257-
"误差展开的幂指数(f(h) ≈ f∞ + c·h^p),常见值 p=2(二阶方法)",
258-
"Power exponent in error expansion (f(h) ≈ f∞ + c·h^p), common value p=2 (second-order method)",
259-
)
260-
)
261-
richardson_layout.addRow(lbl_richardson_p, owner.richardson_p_spin)
201+
QFormLayout(owner.richardson_box)
262202

263203
owner.extrap_method_stack.addWidget(owner.power_box)
264204
owner.extrap_method_stack.addWidget(owner.levin_box)
@@ -294,10 +234,6 @@ def build_extrapolation_mode_view(owner: Any) -> QGroupBox:
294234
lbl_p=lbl_p,
295235
lbl_seed=lbl_seed,
296236
lbl_variant=lbl_variant,
297-
lbl_order=lbl_order,
298-
lbl_weight=lbl_weight,
299-
lbl_beta=lbl_beta,
300-
lbl_richardson_p=lbl_richardson_p,
301237
lbl_uncert=lbl_uncert,
302238
combo_items=combo_items,
303239
)
@@ -313,10 +249,6 @@ def _bind_extrapolation_schema_fields(
313249
lbl_p: QLabel,
314250
lbl_seed: QLabel,
315251
lbl_variant: QLabel,
316-
lbl_order: QLabel,
317-
lbl_weight: QLabel,
318-
lbl_beta: QLabel,
319-
lbl_richardson_p: QLabel,
320252
lbl_uncert: QLabel,
321253
combo_items: list[tuple[str, str, str]],
322254
) -> None:
@@ -429,51 +361,6 @@ def _bind_extrapolation_schema_fields(
429361
ChoiceSpec(value="v", label=LocalizedText("v (积分)", "v (integrals)")),
430362
],
431363
)
432-
levin_order_field = FormFieldSpec(
433-
key="extrapolation.levin.order",
434-
widget_kind="number",
435-
label=LocalizedText("变换阶数:", "Transform order:"),
436-
tooltip=LocalizedText(
437-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[1].tooltip_zh,
438-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[1].tooltip_en,
439-
),
440-
required=True,
441-
)
442-
levin_weight_field = FormFieldSpec(
443-
key="extrapolation.levin.weight",
444-
widget_kind="select",
445-
label=LocalizedText("权重函数:", "Weight function:"),
446-
tooltip=LocalizedText(
447-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[2].tooltip_zh,
448-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[2].tooltip_en,
449-
),
450-
required=True,
451-
choices=[
452-
ChoiceSpec(value="default", label=LocalizedText("默认 (1)", "Default (1)")),
453-
ChoiceSpec(value="reciprocal", label=LocalizedText("1/(n+1)", "1/(n+1)")),
454-
ChoiceSpec(value="reciprocal_beta", label=LocalizedText("1/(n+β)", "1/(n+β)")),
455-
],
456-
)
457-
levin_beta_field = FormFieldSpec(
458-
key="extrapolation.levin.beta",
459-
widget_kind="number",
460-
label=LocalizedText("β 参数:", "β parameter:"),
461-
tooltip=LocalizedText(
462-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[3].tooltip_zh,
463-
EXTRAPOLATION_METHOD_SPECS["levin_u"].parameter_groups[0].parameters[3].tooltip_en,
464-
),
465-
required=False,
466-
)
467-
richardson_p_field = FormFieldSpec(
468-
key="extrapolation.richardson.p",
469-
widget_kind="number",
470-
label=LocalizedText("收敛幂指数 p:", "Convergence power p:"),
471-
tooltip=LocalizedText(
472-
EXTRAPOLATION_METHOD_SPECS["richardson"].parameter_groups[0].parameters[0].tooltip_zh,
473-
EXTRAPOLATION_METHOD_SPECS["richardson"].parameter_groups[0].parameters[0].tooltip_en,
474-
),
475-
required=True,
476-
)
477364
uncertainty_field = FormFieldSpec(
478365
key="extrapolation.uncertainty.reference_column",
479366
widget_kind="select",
@@ -531,15 +418,6 @@ def _bind_extrapolation_schema_fields(
531418
bind_field(field=levin_variant_field, label=lbl_variant, widget=owner.levin_variant_combo, lang=lang)
532419
bind_choices(owner.levin_variant_combo, levin_variant_field.choices, lang=lang)
533420
register_schema_text_refresh(owner, levin_variant_field, widget=owner.levin_variant_combo)
534-
bind_field(field=levin_order_field, label=lbl_order, widget=owner.levin_order_spin, lang=lang)
535-
register_schema_text_refresh(owner, levin_order_field, widget=owner.levin_order_spin)
536-
bind_field(field=levin_weight_field, label=lbl_weight, widget=owner.levin_weight_combo, lang=lang)
537-
bind_choices(owner.levin_weight_combo, levin_weight_field.choices, lang=lang)
538-
register_schema_text_refresh(owner, levin_weight_field, widget=owner.levin_weight_combo)
539-
bind_field(field=levin_beta_field, label=lbl_beta, widget=owner.levin_beta_spin, lang=lang)
540-
register_schema_text_refresh(owner, levin_beta_field, widget=owner.levin_beta_spin)
541-
bind_field(field=richardson_p_field, label=lbl_richardson_p, widget=owner.richardson_p_spin, lang=lang)
542-
register_schema_text_refresh(owner, richardson_p_field, widget=owner.richardson_p_spin)
543421
bind_field(
544422
field=uncertainty_field,
545423
label=lbl_uncert,

app_desktop/window.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,6 @@ def _initialize_workspace_tracking(self) -> None:
796796
"mode_combo",
797797
"method_combo",
798798
"levin_variant_combo",
799-
"levin_weight_combo",
800799
"error_method_combo",
801800
"error_units_mode_combo",
802801
"stats_workflow_combo",
@@ -848,9 +847,6 @@ def _initialize_workspace_tracking(self) -> None:
848847
"display_digits_spin",
849848
"latex_input_precision_spin",
850849
"latex_group_size_spin",
851-
"levin_order_spin",
852-
"levin_beta_spin",
853-
"richardson_p_spin",
854850
"error_order_spin",
855851
"error_mc_samples_spin",
856852
"inverse_min_spin",
@@ -2341,16 +2337,6 @@ def _update_method_state(self):
23412337
if hasattr(self, "refresh_workbench_formula_panel"):
23422338
self.refresh_workbench_formula_panel()
23432339

2344-
def _update_levin_weight_state(self):
2345-
"""Show/hide Levin beta parameter based on weight function selection."""
2346-
if not hasattr(self, "levin_weight_combo") or not hasattr(self, "levin_beta_spin"):
2347-
return
2348-
weight_type = self.levin_weight_combo.currentData()
2349-
show_beta = weight_type == "reciprocal_beta"
2350-
if hasattr(self, "levin_beta_label"):
2351-
self.levin_beta_label.setVisible(show_beta)
2352-
self.levin_beta_spin.setVisible(show_beta)
2353-
23542340
def _set_corner_example_hint(self, example: str) -> None:
23552341
"""Show the per-mode example as a tooltip on the table's
23562342
corner button (row-header × column-header intersection —

app_desktop/window_extrapolation_mixin.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@
3333
}
3434

3535

36-
def _widget_value(owner, attr_name: str, default: object) -> object:
37-
widget = getattr(owner, attr_name, None)
38-
value_func = getattr(widget, "value", None)
39-
if callable(value_func):
40-
return value_func()
41-
return default
42-
43-
4436
def _combo_current_data(owner, attr_name: str, default: str) -> str:
4537
combo = getattr(owner, attr_name, None)
4638
data_func = getattr(combo, "currentData", None)
@@ -114,15 +106,6 @@ def _label_with_unit(label: str, unit: str) -> str:
114106
return f"{label} [{unit_text}]" if unit_text else label
115107

116108

117-
def _read_extrapolation_method_options_from_controls(owner) -> dict[str, object]:
118-
return {
119-
"richardson_p": float(_widget_value(owner, "richardson_p_spin", 2.0)),
120-
"levin_order": int(_widget_value(owner, "levin_order_spin", 2)),
121-
"levin_weight": _combo_current_data(owner, "levin_weight_combo", "default"),
122-
"levin_beta": float(_widget_value(owner, "levin_beta_spin", 1.0)),
123-
}
124-
125-
126109
class WindowExtrapolationMixin:
127110
# --------------------------------------------------------- Worker Control Methods --
128111
def _has_running_worker(self) -> bool:
@@ -325,8 +308,6 @@ def run_calculation(self):
325308
if hasattr(self, "levin_variant_combo"):
326309
levin_variant = self.levin_variant_combo.currentData() or "u"
327310

328-
method_option_values = _read_extrapolation_method_options_from_controls(self)
329-
330311
options = ExtrapolationOptions(
331312
method=method_choice,
332313
power_law_config=power_config,
@@ -335,7 +316,6 @@ def run_calculation(self):
335316
levin_variant=levin_variant,
336317
custom_formula=custom_formula,
337318
uncertainty_digits=uncertainty_digits,
338-
**method_option_values,
339319
)
340320

341321
capture_stream = io.StringIO() if verbose else None

app_desktop/window_i18n_mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _localize_label(self, label: str) -> str:
8585
"x 级数 / 1/x series": "1/x series",
8686
"1/x^3~1/x^5 / 1/x^3~1/x^5": "1/x^3~1/x^5",
8787
"幂律外推(三点外推)": "Power-law (3-point)",
88-
"Richardson 序列加速(三点外推)": "Richardson (3-point)",
88+
"Richardson 序列加速": "Richardson",
8989
"Shanks 变换": "Shanks transform",
9090
"Levin u-transform": "Levin u-transform",
9191
"自定义公式(三点外推) (A,B,C)": "Custom (A,B,C)",

app_desktop/workers_core.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
normalize_uncertainty_propagation_config,
3535
uncertainty_payload_to_results,
3636
)
37-
from shared.integer_validation import strict_int
3837
from shared.extrapolation_engine import parse_extrapolation_string
3938
from shared.fitting_engine import (
4039
DirectFitInput,
@@ -604,17 +603,10 @@ def _safe_extrapolation_core_request(
604603

605604
def _extrapolation_method_options(options: ExtrapolationOptions) -> dict[str, object]:
606605
payload: dict[str, object] = {}
607-
for attr in ("uncertainty_column", "levin_variant", "custom_formula", "levin_weight"):
606+
for attr in ("uncertainty_column", "levin_variant", "custom_formula"):
608607
value = getattr(options, attr, None)
609608
if value is not None and str(value).strip():
610609
payload[attr] = str(value)
611-
for attr in ("richardson_p", "levin_beta"):
612-
value = getattr(options, attr, None)
613-
if value is not None:
614-
payload[attr] = str(value)
615-
levin_order = getattr(options, "levin_order", None)
616-
if levin_order is not None:
617-
payload["levin_order"] = strict_int(levin_order, field_name="levin_order")
618610
power_config = getattr(options, "power_law_config", None)
619611
if power_config is not None:
620612
power_payload: dict[str, object] = {}

app_desktop/workspace_controller.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,6 @@ def _restore_extrapolation_config(window: Any, config: Any) -> None:
777777
levin = config.get("levin") or {}
778778
if isinstance(levin, dict):
779779
_set_combo_data(getattr(window, "levin_variant_combo", None), str(levin.get("variant") or "u"))
780-
_set_value(getattr(window, "levin_order_spin", None), levin.get("order"))
781-
_set_combo_data(getattr(window, "levin_weight_combo", None), str(levin.get("weight") or "default"))
782-
_set_value(getattr(window, "levin_beta_spin", None), levin.get("beta"))
783-
richardson = config.get("richardson") or {}
784-
if isinstance(richardson, dict):
785-
_set_value(getattr(window, "richardson_p_spin", None), richardson.get("p"))
786780
uncertainty_column = str(config.get("uncertainty_column") or "")
787781
if uncertainty_column:
788782
_set_combo_data(getattr(window, "uncertainty_combo", None), uncertainty_column)
@@ -1137,11 +1131,7 @@ def _capture_config(window: Any) -> dict[str, Any]:
11371131
},
11381132
"levin": {
11391133
"variant": _combo_data(getattr(window, "levin_variant_combo", None), "u"),
1140-
"order": _value(getattr(window, "levin_order_spin", None), 2),
1141-
"weight": _combo_data(getattr(window, "levin_weight_combo", None), "default"),
1142-
"beta": str(_value(getattr(window, "levin_beta_spin", None), 1.0)),
11431134
},
1144-
"richardson": {"p": str(_value(getattr(window, "richardson_p_spin", None), 2.0))},
11451135
"uncertainty_column": _combo_data(getattr(window, "uncertainty_combo", None), "A"),
11461136
},
11471137
"error": _capture_error_config(window),

app_web/logic/extrapolation.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
_format_rows,
3232
_generate_csv_from_rows,
3333
_is_checked,
34-
_parse_float,
3534
_parse_int,
3635
)
3736
from .plots import _render_extrapolation_plot
@@ -144,17 +143,9 @@ def _method_options_payload(
144143
reference_column: str | None,
145144
levin_variant: str,
146145
custom_formula: str | None,
147-
richardson_p: float,
148-
levin_order: int,
149-
levin_weight: str,
150-
levin_beta: float,
151146
) -> dict[str, object]:
152147
payload: dict[str, object] = {
153148
"levin_variant": levin_variant,
154-
"richardson_p": str(richardson_p),
155-
"levin_order": levin_order,
156-
"levin_weight": levin_weight,
157-
"levin_beta": str(levin_beta),
158149
}
159150
if reference_column:
160151
payload["uncertainty_column"] = reference_column
@@ -188,16 +179,6 @@ def _run_extrapolation(data_text: str, form, lang: str = "zh") -> ExtrapolationR
188179
latex_engine = (form.get("latex_engine") or "xelatex").strip() or "xelatex"
189180
# Dynamic (ui_specs) fields may use shorter names; keep legacy names as fallback.
190181
levin_variant = (form.get("variant") or form.get("levin_variant") or "u").strip() or "u"
191-
richardson_p = _parse_float(form.get("p"))
192-
if richardson_p is None:
193-
richardson_p = 2.0
194-
levin_order = _parse_int(form.get("order"))
195-
if levin_order is None:
196-
levin_order = 2
197-
levin_weight = (form.get("weight") or "default").strip() or "default"
198-
levin_beta = _parse_float(form.get("beta"))
199-
if levin_beta is None:
200-
levin_beta = 1.0
201182
custom_formula = (form.get("custom_formula") or "").strip() or None
202183

203184
accelerators = {"richardson", "shanks", "levin_u", "wynn_epsilon"}
@@ -214,10 +195,6 @@ def _run_extrapolation(data_text: str, form, lang: str = "zh") -> ExtrapolationR
214195
levin_variant=levin_variant,
215196
custom_formula=custom_formula,
216197
uncertainty_digits=result_digits,
217-
richardson_p=richardson_p,
218-
levin_order=levin_order,
219-
levin_weight=levin_weight,
220-
levin_beta=levin_beta,
221198
)
222199
with _precision_guard(mp_precision) as applied_precision:
223200
headers, parsed_rows = parse_extrapolation_string(
@@ -230,10 +207,6 @@ def _run_extrapolation(data_text: str, form, lang: str = "zh") -> ExtrapolationR
230207
reference_column=reference_column,
231208
levin_variant=levin_variant,
232209
custom_formula=custom_formula,
233-
richardson_p=richardson_p,
234-
levin_order=levin_order,
235-
levin_weight=levin_weight,
236-
levin_beta=levin_beta,
237210
)
238211
request = build_extrapolation_request(
239212
headers=headers,

0 commit comments

Comments
 (0)