Skip to content

Commit be3ab26

Browse files
committed
feat(mcp,report): harden core contracts, cleanup structural noise, and tighten MCP guidance
- implement the audit-driven cleanup across baseline/cache/report/html internals with shared JSON IO, safer normalization and path handling, and cleaner structural rendering boundaries - remove safe non-golden structural and clone noise surfaced by stricter analysis passes without touching golden fixture debt - strengthen MCP semantics with conservative-first threshold guidance, the new analysis_profile help topic, and tighter workflow/help wording - refresh core docs and contract tests for baseline, report, MCP, and stricter analysis behavior
1 parent 1b5558e commit be3ab26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2505
-2248
lines changed

codeclone/_cli_baselines.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
from __future__ import annotations
88

9-
import json
109
import sys
1110
from dataclasses import dataclass
11+
from json import JSONDecodeError
1212
from pathlib import Path
1313
from typing import TYPE_CHECKING, Protocol
1414

15+
import orjson
16+
1517
from . import ui_messages as ui
1618
from .baseline import (
1719
BASELINE_UNTRUSTED_STATUSES,
@@ -101,8 +103,8 @@ def probe_metrics_baseline_section(path: Path) -> MetricsBaselineSectionProbe:
101103
payload=None,
102104
)
103105
try:
104-
raw_payload = json.loads(path.read_text("utf-8"))
105-
except (OSError, json.JSONDecodeError):
106+
raw_payload = orjson.loads(path.read_text("utf-8"))
107+
except (OSError, JSONDecodeError):
106108
return MetricsBaselineSectionProbe(
107109
has_metrics_section=True,
108110
payload=None,

codeclone/_cli_gating.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,37 +90,37 @@ def policy_context(*, args: _GatingArgs, gate_kind: str) -> str:
9090
if args.ci:
9191
return "ci"
9292

93-
parts: list[str] = []
94-
93+
parts: tuple[str | None, ...]
9594
match gate_kind:
9695
case "metrics":
97-
if args.fail_on_new_metrics:
98-
parts.append("fail-on-new-metrics")
99-
if args.fail_complexity >= 0:
100-
parts.append(f"fail-complexity={args.fail_complexity}")
101-
if args.fail_coupling >= 0:
102-
parts.append(f"fail-coupling={args.fail_coupling}")
103-
if args.fail_cohesion >= 0:
104-
parts.append(f"fail-cohesion={args.fail_cohesion}")
105-
if args.fail_cycles:
106-
parts.append("fail-cycles")
107-
if args.fail_dead_code:
108-
parts.append("fail-dead-code")
109-
if args.fail_health >= 0:
110-
parts.append(f"fail-health={args.fail_health}")
111-
96+
parts = (
97+
"fail-on-new-metrics" if args.fail_on_new_metrics else None,
98+
f"fail-complexity={args.fail_complexity}"
99+
if args.fail_complexity >= 0
100+
else None,
101+
f"fail-coupling={args.fail_coupling}"
102+
if args.fail_coupling >= 0
103+
else None,
104+
f"fail-cohesion={args.fail_cohesion}"
105+
if args.fail_cohesion >= 0
106+
else None,
107+
"fail-cycles" if args.fail_cycles else None,
108+
"fail-dead-code" if args.fail_dead_code else None,
109+
f"fail-health={args.fail_health}" if args.fail_health >= 0 else None,
110+
)
112111
case "new-clones":
113-
if args.fail_on_new:
114-
parts.append("fail-on-new")
115-
112+
parts = ("fail-on-new" if args.fail_on_new else None,)
116113
case "threshold":
117-
if args.fail_threshold >= 0:
118-
parts.append(f"fail-threshold={args.fail_threshold}")
119-
114+
parts = (
115+
f"fail-threshold={args.fail_threshold}"
116+
if args.fail_threshold >= 0
117+
else None,
118+
)
120119
case _:
121-
pass
120+
parts = ()
122121

123-
return ", ".join(parts) if parts else "custom"
122+
enabled_parts = tuple(part for part in parts if part is not None)
123+
return ", ".join(enabled_parts) if enabled_parts else "custom"
124124

125125

126126
def print_gating_failure_block(

codeclone/_html_badges.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from collections.abc import Callable, Sequence
2020

21-
from ._html_escape import _escape_attr, _escape_html
21+
from ._html_escape import _escape_html
2222
from .domain.quality import (
2323
EFFORT_EASY,
2424
EFFORT_HARD,
@@ -63,24 +63,24 @@ def _quality_badge_html(text: str) -> str:
6363
r = text.strip().lower()
6464
if r in (RISK_LOW, RISK_HIGH, RISK_MEDIUM):
6565
return (
66-
f'<span class="risk-badge risk-{_escape_attr(r)}">{_escape_html(r)}</span>'
66+
f'<span class="risk-badge risk-{_escape_html(r)}">{_escape_html(r)}</span>'
6767
)
6868
if r in (SEVERITY_CRITICAL, SEVERITY_WARNING, SEVERITY_INFO):
6969
return (
70-
f'<span class="severity-badge severity-{_escape_attr(r)}">'
70+
f'<span class="severity-badge severity-{_escape_html(r)}">'
7171
f"{_escape_html(r)}</span>"
7272
)
7373
if r in _EFFORT_CSS:
7474
return (
75-
f'<span class="risk-badge risk-{_escape_attr(r)}">{_escape_html(r)}</span>'
75+
f'<span class="risk-badge risk-{_escape_html(r)}">{_escape_html(r)}</span>'
7676
)
7777
return _escape_html(text)
7878

7979

8080
def _source_kind_badge_html(source_kind: str) -> str:
8181
normalized = normalize_source_kind(source_kind)
8282
return (
83-
f'<span class="source-kind-badge source-kind-{_escape_attr(normalized)}">'
83+
f'<span class="source-kind-badge source-kind-{_escape_html(normalized)}">'
8484
f"{_escape_html(source_kind_label(normalized))}</span>"
8585
)
8686

@@ -117,7 +117,7 @@ def _render_chain_flow(
117117
for i, mod in enumerate(parts):
118118
short = _short_label(str(mod))
119119
nodes.append(
120-
f'<span class="chain-node" title="{_escape_attr(str(mod))}">'
120+
f'<span class="chain-node" title="{_escape_html(str(mod))}">'
121121
f"{_escape_html(short)}</span>"
122122
)
123123
if arrows and i < len(parts) - 1:
@@ -154,7 +154,7 @@ def _stat_card(
154154
if glossary_tip_fn is not None:
155155
tip_html = glossary_tip_fn(label)
156156
elif tip:
157-
tip_html = f'<span class="kpi-help" data-tip="{_escape_attr(tip)}">?</span>'
157+
tip_html = f'<span class="kpi-help" data-tip="{_escape_html(tip)}">?</span>'
158158

159159
detail_html = ""
160160
if detail:
@@ -167,7 +167,7 @@ def _stat_card(
167167
value_cls = f" meta-value--{value_tone}" if value_tone else ""
168168

169169
return (
170-
f'<div class="{_escape_attr(css_class)}">'
170+
f'<div class="{_escape_html(css_class)}">'
171171
f'<div class="meta-label">{_escape_html(label)}{tip_html}{delta_html}</div>'
172172
f'<div class="meta-value{value_cls}">{_escape_html(str(value))}</div>'
173173
f"{detail_html}"

codeclone/_html_css.py

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@
179179
.main-tab-icon{flex-shrink:0;opacity:.72}
180180
.main-tab-label{display:inline-flex;align-items:center}
181181
.tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:18px;
182-
height:18px;padding:0 5px;font-size:.7rem;font-weight:700;border-radius:9px;
182+
height:18px;padding:0 5px;font-size:.68rem;font-weight:700;border-radius:var(--radius-sm);
183183
background:var(--bg-overlay);color:var(--text-muted);margin-left:var(--sp-1)}
184184
.main-tab[aria-selected="true"] .tab-count{background:var(--accent-primary);
185185
color:#fff}
@@ -370,9 +370,9 @@
370370
.group-summary{font-size:.8rem;color:var(--text-muted)}
371371
372372
/* Badges */
373-
.clone-type-badge{font-size:.75rem;font-weight:500;padding:1px var(--sp-2);
373+
.clone-type-badge{font-size:.68rem;font-weight:500;padding:2px var(--sp-2);
374374
border-radius:var(--radius-sm);background:var(--accent-muted);color:var(--accent-primary)}
375-
.clone-count-badge{font-size:.75rem;font-weight:600;padding:1px var(--sp-2);
375+
.clone-count-badge{font-size:.68rem;font-weight:600;padding:2px var(--sp-2);
376376
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--text-secondary)}
377377
378378
/* Group body */
@@ -385,7 +385,7 @@
385385
/* Group explain */
386386
.group-explain{padding:var(--sp-2) var(--sp-4);display:flex;flex-wrap:wrap;gap:var(--sp-1);
387387
background:var(--bg-raised);border-bottom:1px solid var(--border)}
388-
.group-explain-item{font-size:.75rem;padding:1px var(--sp-2);border-radius:var(--radius-sm);
388+
.group-explain-item{font-size:.68rem;padding:2px var(--sp-2);border-radius:var(--radius-sm);
389389
background:var(--bg-overlay);color:var(--text-muted);font-family:var(--font-mono);white-space:nowrap}
390390
.group-explain-warn{color:var(--warning);background:var(--warning-muted)}
391391
.group-explain-muted{opacity:.7}
@@ -433,28 +433,28 @@
433433
# ---------------------------------------------------------------------------
434434

435435
_BADGES = """\
436-
.risk-badge,.severity-badge{display:inline-flex;align-items:center;font-size:.72rem;font-weight:600;
437-
padding:1px var(--sp-2);border-radius:var(--radius-sm);text-transform:uppercase;letter-spacing:.02em}
436+
.risk-badge,.severity-badge{display:inline-flex;align-items:center;font-size:.68rem;font-weight:600;
437+
padding:2px var(--sp-2);border-radius:var(--radius-sm);text-transform:uppercase;letter-spacing:.02em}
438438
.risk-critical,.severity-critical{background:var(--error-muted);color:var(--error)}
439439
.risk-high,.severity-high{background:var(--error-muted);color:var(--error)}
440440
.risk-warning,.severity-warning{background:var(--warning-muted);color:var(--warning)}
441441
.risk-medium,.severity-medium{background:var(--warning-muted);color:var(--warning)}
442442
.risk-low,.severity-low{background:var(--success-muted);color:var(--success)}
443443
.risk-info,.severity-info{background:var(--info-muted);color:var(--info)}
444444
445-
.source-kind-badge{display:inline-flex;align-items:center;font-size:.72rem;font-weight:500;
446-
padding:1px var(--sp-2);border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--text-muted)}
445+
.source-kind-badge{display:inline-flex;align-items:center;font-size:.68rem;font-weight:500;
446+
padding:2px var(--sp-2);border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--text-muted)}
447447
.source-kind-production{background:var(--error-muted);color:var(--error)}
448448
.source-kind-test,.source-kind-test_util{background:var(--info-muted);color:var(--info)}
449449
.source-kind-fixture,.source-kind-conftest{background:var(--warning-muted);color:var(--warning)}
450450
.source-kind-import,.source-kind-cross_kind{background:var(--accent-muted);color:var(--accent-primary)}
451-
.category-badge{display:inline-flex;align-items:center;gap:3px;font-size:.7rem;
452-
font-family:var(--font-mono);padding:1px var(--sp-2);border-radius:var(--radius-sm);
451+
.category-badge{display:inline-flex;align-items:center;gap:3px;font-size:.68rem;
452+
font-family:var(--font-mono);padding:2px var(--sp-2);border-radius:var(--radius-sm);
453453
background:var(--bg-overlay);color:var(--text-muted);white-space:nowrap}
454454
.category-badge-key{font-weight:400;color:var(--text-muted)}
455455
.category-badge-val{font-weight:600;color:var(--text-secondary)}
456456
.finding-why-chips{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin:var(--sp-1) 0}
457-
.finding-why-chips .category-badge{font-size:.72rem}
457+
.finding-why-chips .category-badge{font-size:.68rem}
458458
"""
459459

460460
# ---------------------------------------------------------------------------
@@ -587,8 +587,8 @@
587587
.kpi-micro-val{font-weight:500;font-variant-numeric:tabular-nums;color:var(--text-muted)}
588588
.kpi-micro-lbl{font-weight:400;color:var(--text-muted);text-transform:lowercase}
589589
.kpi-micro--baselined{color:var(--success);font-weight:500;font-size:.6rem}
590-
.kpi-delta{font-size:.58rem;font-weight:700;margin-left:auto;
591-
padding:1px 5px;border-radius:8px;white-space:nowrap}
590+
.kpi-delta{font-size:.62rem;font-weight:700;margin-left:auto;
591+
padding:1px 5px;border-radius:var(--radius-sm);white-space:nowrap}
592592
.kpi-delta--good{color:var(--success);background:var(--success-muted)}
593593
.kpi-delta--bad{color:var(--error);background:var(--error-muted)}
594594
.kpi-delta--neutral{color:var(--text-muted);background:var(--bg-raised)}
@@ -671,16 +671,19 @@
671671
.dir-hotspot-entry{padding:var(--sp-2) 0;border-bottom:1px solid color-mix(in srgb,var(--border) 50%,transparent)}
672672
.dir-hotspot-entry:last-child{border-bottom:none;padding-bottom:0}
673673
.dir-hotspot-entry:first-child{padding-top:0}
674-
.dir-hotspot-path{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:4px;min-width:0}
675-
.dir-hotspot-path code{font-size:.78rem;font-weight:600;color:var(--text-primary);line-height:1.3}
676-
.dir-hotspot-bar-row{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:3px}
677-
.dir-hotspot-bar-track{flex:1;height:4px;border-radius:2px;background:var(--bg-raised);
678-
overflow:hidden;display:flex}
674+
/* Row 1: path + badge */
675+
.dir-hotspot-head{display:flex;align-items:center;gap:var(--sp-2);min-width:0}
676+
.dir-hotspot-path{font-size:.78rem;font-weight:600;color:var(--text-primary);line-height:1.3;
677+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1}
678+
/* Row 2: bar + pct + meta */
679+
.dir-hotspot-detail{display:flex;align-items:center;gap:var(--sp-2);margin-top:3px}
680+
.dir-hotspot-bar-track{width:30%;flex-shrink:0;height:4px;border-radius:2px;
681+
background:var(--bg-raised);overflow:hidden;display:flex}
679682
.dir-hotspot-bar-prev{height:100%;background:var(--text-muted);opacity:.18}
680683
.dir-hotspot-bar-cur{height:100%;background:var(--accent-primary);opacity:.7}
681-
.dir-hotspot-pct{font-size:.7rem;font-weight:600;font-variant-numeric:tabular-nums;
682-
color:var(--text-muted);min-width:3.2em;text-align:right}
683-
.dir-hotspot-meta{display:flex;flex-wrap:wrap;gap:6px;font-size:.68rem;color:var(--text-muted)}
684+
.dir-hotspot-pct{font-size:.72rem;font-weight:600;font-variant-numeric:tabular-nums;
685+
color:var(--text-secondary);white-space:nowrap;flex-shrink:0}
686+
.dir-hotspot-meta{display:flex;flex-wrap:wrap;gap:4px 6px;font-size:.68rem;color:var(--text-muted)}
684687
.dir-hotspot-meta span{font-variant-numeric:tabular-nums}
685688
.dir-hotspot-meta-sep{opacity:.3}
686689
.overloaded-module-list{display:flex;flex-direction:column;gap:0}
@@ -690,15 +693,15 @@
690693
.overloaded-module-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-2);margin-bottom:4px}
691694
.overloaded-module-title{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);min-width:0}
692695
.overloaded-module-title code{font-size:.78rem;font-weight:600;color:var(--text-primary);line-height:1.35}
693-
.overloaded-module-score{flex-shrink:0;font-size:.72rem;font-weight:700;font-variant-numeric:tabular-nums;
694-
color:var(--accent-primary);background:var(--accent-muted);border-radius:999px;padding:2px 8px}
696+
.overloaded-module-score{flex-shrink:0;font-size:.68rem;font-weight:700;font-variant-numeric:tabular-nums;
697+
color:var(--accent-primary);background:var(--accent-muted);border-radius:var(--radius-sm);padding:2px var(--sp-2)}
695698
.overloaded-module-metrics{display:flex;flex-wrap:wrap;gap:6px;font-size:.68rem;color:var(--text-muted)}
696699
.overloaded-module-metrics span{font-variant-numeric:tabular-nums}
697700
.overloaded-module-reasons,.overloaded-module-signal-list{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-2)}
698701
.overloaded-module-reason-chip,.overloaded-module-signal-pill{display:inline-flex;align-items:center;gap:5px;
699702
font-size:.68rem;font-weight:500;color:var(--text-secondary);background:var(--bg-raised);
700-
border:1px solid color-mix(in srgb,var(--border) 60%,transparent);border-radius:999px;
701-
padding:2px 8px}
703+
border:1px solid color-mix(in srgb,var(--border) 60%,transparent);border-radius:var(--radius-sm);
704+
padding:2px var(--sp-2)}
702705
.overloaded-module-signal-count{font-variant-numeric:tabular-nums;color:var(--text-muted)}
703706
/* Health radar chart */
704707
.health-radar{display:flex;justify-content:center;padding:var(--sp-3) 0}
@@ -750,8 +753,8 @@
750753
.dep-hub-pill{display:inline-flex;align-items:center;gap:var(--sp-1);padding:var(--sp-1) var(--sp-2);
751754
border-radius:var(--radius-sm);background:var(--bg-overlay);font-size:.8rem}
752755
.dep-hub-name{color:var(--text-primary);font-family:var(--font-mono);font-size:.8rem}
753-
.dep-hub-deg{font-size:.72rem;font-weight:600;color:var(--accent-primary);
754-
background:var(--accent-muted);padding:0 var(--sp-1);border-radius:var(--radius-sm)}
756+
.dep-hub-deg{font-size:.68rem;font-weight:600;color:var(--accent-primary);
757+
background:var(--accent-muted);padding:2px var(--sp-2);border-radius:var(--radius-sm)}
755758
756759
/* Legend */
757760
.dep-legend{display:flex;gap:var(--sp-4);align-items:center;margin-bottom:var(--sp-4);
@@ -822,12 +825,12 @@
822825
.suggestion-sev--critical{background:var(--error-muted);color:var(--error)}
823826
.suggestion-sev--warning{background:var(--warning-muted);color:var(--warning)}
824827
.suggestion-sev--info{background:var(--info-muted);color:var(--info)}
825-
.suggestion-sev-inline{font-size:.72rem;font-weight:600;padding:1px var(--sp-1);
828+
.suggestion-sev-inline{font-size:.68rem;font-weight:600;padding:2px var(--sp-2);
826829
border-radius:var(--radius-sm)}
827830
.suggestion-title{font-weight:600;font-size:.85rem;color:var(--text-primary);flex:1;min-width:0}
828831
.suggestion-meta{display:flex;align-items:center;gap:var(--sp-2);flex-shrink:0;flex-wrap:wrap}
829832
.suggestion-meta-badge{font-size:.68rem;font-weight:600;padding:2px var(--sp-2);
830-
border-radius:999px;background:var(--bg-overlay);color:var(--text-muted);
833+
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--text-muted);
831834
white-space:nowrap;line-height:1.2;font-variant-numeric:tabular-nums}
832835
.suggestion-effort--easy{color:var(--success);background:var(--success-muted, rgba(34,197,94,.1))}
833836
.suggestion-effort--moderate{color:var(--warning);background:var(--warning-muted)}
@@ -836,7 +839,7 @@
836839
/* Body — context + summary */
837840
.suggestion-body{padding:0 var(--sp-4) var(--sp-3);display:flex;flex-direction:column;gap:var(--sp-1)}
838841
.suggestion-context{display:flex;gap:var(--sp-1);flex-wrap:wrap}
839-
.suggestion-chip{font-size:.65rem;font-weight:500;padding:1px 6px;border-radius:var(--radius-sm);
842+
.suggestion-chip{font-size:.68rem;font-weight:500;padding:2px var(--sp-2);border-radius:var(--radius-sm);
840843
background:var(--bg-overlay);color:var(--text-muted);white-space:nowrap}
841844
.suggestion-summary{font-size:.8rem;font-family:var(--font-mono);color:var(--text-secondary);line-height:1.5}
842845
.suggestion-action{display:flex;align-items:center;gap:var(--sp-1);
@@ -896,7 +899,7 @@
896899
897900
/* Header row */
898901
.sf-head{padding:var(--sp-3) var(--sp-4);display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap}
899-
.sf-kind-badge{font-size:.68rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;
902+
.sf-kind-badge{font-size:.68rem;font-weight:600;text-transform:uppercase;letter-spacing:.03em;
900903
padding:2px var(--sp-2);border-radius:var(--radius-sm);white-space:nowrap;
901904
background:var(--info-muted);color:var(--info)}
902905
.sf-title{font-weight:600;font-size:.85rem;color:var(--text-primary);flex:1;min-width:0}
@@ -976,16 +979,16 @@
976979
font-family:var(--font-mono);font-size:.72rem}
977980
978981
/* Boolean check/cross badges */
979-
.meta-bool{font-size:.7rem;font-weight:600;padding:1px 8px;border-radius:10px;
982+
.meta-bool{font-size:.68rem;font-weight:600;padding:2px var(--sp-2);border-radius:var(--radius-sm);
980983
display:inline-flex;align-items:center;gap:3px}
981984
.meta-bool-true{background:var(--success-muted);color:var(--success)}
982985
.meta-bool-false{background:var(--error-muted);color:var(--error)}
983986
984987
/* Provenance summary badges */
985988
.prov-summary{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
986989
padding:var(--sp-2) var(--sp-4);border-top:1px solid var(--border)}
987-
.prov-badge{display:inline-flex;align-items:center;gap:4px;font-size:.66rem;
988-
padding:2px 8px;border-radius:var(--radius-sm);background:var(--bg-raised);
990+
.prov-badge{display:inline-flex;align-items:center;gap:4px;font-size:.68rem;
991+
padding:2px var(--sp-2);border-radius:var(--radius-sm);background:var(--bg-raised);
989992
white-space:nowrap;line-height:1.3;border:1px solid color-mix(in srgb,var(--border) 55%,transparent)}
990993
.prov-badge-val{font-weight:600;font-variant-numeric:tabular-nums}
991994
.prov-badge-lbl{font-weight:400;color:var(--text-muted);text-transform:lowercase}

codeclone/_html_data_attrs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from __future__ import annotations
1010

11-
from ._html_escape import _escape_attr
11+
from ._html_escape import _escape_html
1212

1313
__all__ = ["_build_data_attrs"]
1414

@@ -26,5 +26,5 @@ def _build_data_attrs(attrs: dict[str, object | None]) -> str:
2626
if val is None:
2727
continue
2828
s = str(val)
29-
parts.append(f'{key}="{_escape_attr(s)}"')
29+
parts.append(f'{key}="{_escape_html(s)}"')
3030
return f" {' '.join(parts)}" if parts else ""

codeclone/_html_escape.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ def _escape_html(v: object) -> str:
1616
return text
1717

1818

19-
def _escape_attr(v: object) -> str:
20-
text = html.escape("" if v is None else str(v), quote=True)
21-
text = text.replace("`", "&#96;")
22-
text = text.replace("\u2028", "&#8232;").replace("\u2029", "&#8233;")
23-
return text
24-
25-
2619
def _meta_display(v: object) -> str:
2720
if isinstance(v, bool):
2821
return "true" if v else "false"

0 commit comments

Comments
 (0)