glitchlings 0.4.5__cp313-cp313-win_amd64.whl → 0.5.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of glitchlings might be problematic. Click here for more details.
- glitchlings/_zoo_rust.cp313-win_amd64.pyd +0 -0
- glitchlings/config.py +12 -24
- glitchlings/dev/__init__.py +5 -0
- glitchlings/dev/sync_assets.py +153 -0
- glitchlings/util/stretchability.py +4 -9
- glitchlings/zoo/_ocr_confusions.py +3 -3
- glitchlings/zoo/_text_utils.py +10 -9
- glitchlings/zoo/adjax.py +3 -18
- glitchlings/zoo/apostrofae.py +2 -5
- glitchlings/zoo/assets/__init__.py +54 -0
- glitchlings/zoo/jargoyle.py +2 -16
- glitchlings/zoo/mim1c.py +2 -17
- glitchlings/zoo/redactyl.py +3 -17
- glitchlings/zoo/reduple.py +3 -17
- glitchlings/zoo/rushmore.py +3 -20
- glitchlings/zoo/scannequin.py +3 -20
- glitchlings/zoo/typogre.py +2 -19
- glitchlings/zoo/zeedub.py +2 -13
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/METADATA +8 -6
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/RECORD +26 -26
- glitchlings/data/__init__.py +0 -1
- glitchlings/zoo/_rate.py +0 -131
- /glitchlings/{data → zoo/assets}/hokey_assets.json +0 -0
- /glitchlings/zoo/{ocr_confusions.tsv → assets/ocr_confusions.tsv} +0 -0
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/WHEEL +0 -0
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/entry_points.txt +0 -0
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {glitchlings-0.4.5.dist-info → glitchlings-0.5.0.dist-info}/top_level.txt +0 -0
|
Binary file
|
glitchlings/config.py
CHANGED
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import importlib
|
|
6
6
|
import os
|
|
7
|
-
import warnings
|
|
8
7
|
from dataclasses import dataclass, field
|
|
9
8
|
from io import TextIOBase
|
|
10
9
|
from pathlib import Path
|
|
@@ -57,17 +56,6 @@ ATTACK_CONFIG_SCHEMA: dict[str, Any] = {
|
|
|
57
56
|
"required": ["name"],
|
|
58
57
|
"properties": {
|
|
59
58
|
"name": {"type": "string", "minLength": 1},
|
|
60
|
-
"type": {"type": "string", "minLength": 1},
|
|
61
|
-
"parameters": {"type": "object"},
|
|
62
|
-
},
|
|
63
|
-
"additionalProperties": True,
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"type": "object",
|
|
67
|
-
"required": ["type"],
|
|
68
|
-
"properties": {
|
|
69
|
-
"name": {"type": "string", "minLength": 1},
|
|
70
|
-
"type": {"type": "string", "minLength": 1},
|
|
71
59
|
"parameters": {"type": "object"},
|
|
72
60
|
},
|
|
73
61
|
"additionalProperties": True,
|
|
@@ -263,7 +251,12 @@ def _validate_attack_config_schema(data: Any, *, source: str) -> Mapping[str, An
|
|
|
263
251
|
|
|
264
252
|
for index, entry in enumerate(raw_glitchlings, start=1):
|
|
265
253
|
if isinstance(entry, Mapping):
|
|
266
|
-
|
|
254
|
+
if "type" in entry:
|
|
255
|
+
raise ValueError(
|
|
256
|
+
f"{source}: glitchling #{index} uses unsupported 'type'; use 'name'."
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
name_candidate = entry.get("name")
|
|
267
260
|
if not isinstance(name_candidate, str) or not name_candidate.strip():
|
|
268
261
|
raise ValueError(f"{source}: glitchling #{index} is missing a 'name'.")
|
|
269
262
|
parameters = entry.get("parameters")
|
|
@@ -326,17 +319,12 @@ def _build_glitchling(entry: Any, source: str, index: int) -> "Glitchling":
|
|
|
326
319
|
raise ValueError(f"{source}: glitchling #{index}: {exc}") from exc
|
|
327
320
|
|
|
328
321
|
if isinstance(entry, Mapping):
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
warnings.warn(
|
|
333
|
-
f"{source}: glitchling #{index} uses 'type'; prefer 'name'.",
|
|
334
|
-
DeprecationWarning,
|
|
335
|
-
stacklevel=2,
|
|
322
|
+
if "type" in entry:
|
|
323
|
+
raise ValueError(
|
|
324
|
+
f"{source}: glitchling #{index} uses unsupported 'type'; use 'name'."
|
|
336
325
|
)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
name_value = legacy_type
|
|
326
|
+
|
|
327
|
+
name_value = entry.get("name")
|
|
340
328
|
|
|
341
329
|
if not isinstance(name_value, str) or not name_value.strip():
|
|
342
330
|
raise ValueError(f"{source}: glitchling #{index} is missing a 'name'.")
|
|
@@ -352,7 +340,7 @@ def _build_glitchling(entry: Any, source: str, index: int) -> "Glitchling":
|
|
|
352
340
|
kwargs = {
|
|
353
341
|
key: value
|
|
354
342
|
for key, value in entry.items()
|
|
355
|
-
if key not in {"name", "
|
|
343
|
+
if key not in {"name", "parameters"}
|
|
356
344
|
}
|
|
357
345
|
|
|
358
346
|
try:
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Synchronise canonical glitchling assets with the vendored Rust copies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Iterator, Sequence
|
|
10
|
+
|
|
11
|
+
RUST_VENDORED_ASSETS: frozenset[str] = frozenset({
|
|
12
|
+
"hokey_assets.json",
|
|
13
|
+
"ocr_confusions.tsv",
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _project_root(default: Path | None = None) -> Path:
|
|
18
|
+
if default is not None:
|
|
19
|
+
return default
|
|
20
|
+
return Path(__file__).resolve().parents[3]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _canonical_asset_dir(project_root: Path) -> Path:
|
|
24
|
+
canonical = project_root / "src" / "glitchlings" / "zoo" / "assets"
|
|
25
|
+
if not canonical.is_dir():
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
"expected canonical assets under 'src/glitchlings/zoo/assets'; "
|
|
28
|
+
"run this command from the repository root"
|
|
29
|
+
)
|
|
30
|
+
return canonical
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _rust_asset_dir(project_root: Path) -> Path:
|
|
34
|
+
return project_root / "rust" / "zoo" / "assets"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _iter_extraneous_assets(rust_dir: Path) -> Iterator[Path]:
|
|
38
|
+
if not rust_dir.exists():
|
|
39
|
+
return
|
|
40
|
+
for path in rust_dir.iterdir():
|
|
41
|
+
if path.is_file() and path.name not in RUST_VENDORED_ASSETS:
|
|
42
|
+
yield path
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def sync_assets(
|
|
46
|
+
project_root: Path | None = None,
|
|
47
|
+
*,
|
|
48
|
+
check: bool = False,
|
|
49
|
+
quiet: bool = False,
|
|
50
|
+
) -> bool:
|
|
51
|
+
"""Synchronise the vendored Rust asset copies with the canonical sources."""
|
|
52
|
+
|
|
53
|
+
root = _project_root(project_root)
|
|
54
|
+
canonical_dir = _canonical_asset_dir(root)
|
|
55
|
+
rust_dir = _rust_asset_dir(root)
|
|
56
|
+
|
|
57
|
+
missing_sources = [
|
|
58
|
+
name
|
|
59
|
+
for name in RUST_VENDORED_ASSETS
|
|
60
|
+
if not (canonical_dir / name).is_file()
|
|
61
|
+
]
|
|
62
|
+
if missing_sources:
|
|
63
|
+
missing_list = ", ".join(sorted(missing_sources))
|
|
64
|
+
raise RuntimeError(f"missing canonical assets: {missing_list}")
|
|
65
|
+
|
|
66
|
+
extraneous = list(_iter_extraneous_assets(rust_dir))
|
|
67
|
+
|
|
68
|
+
mismatched: list[tuple[str, str]] = []
|
|
69
|
+
for name in sorted(RUST_VENDORED_ASSETS):
|
|
70
|
+
source = canonical_dir / name
|
|
71
|
+
target = rust_dir / name
|
|
72
|
+
if not target.exists():
|
|
73
|
+
mismatched.append((name, "missing"))
|
|
74
|
+
continue
|
|
75
|
+
if source.read_bytes() != target.read_bytes():
|
|
76
|
+
mismatched.append((name, "outdated"))
|
|
77
|
+
|
|
78
|
+
if check:
|
|
79
|
+
if mismatched or extraneous:
|
|
80
|
+
if not quiet:
|
|
81
|
+
for name, reason in mismatched:
|
|
82
|
+
target = rust_dir / name
|
|
83
|
+
print(
|
|
84
|
+
f"{target.relative_to(root)} is {reason}; run sync_assets to refresh it",
|
|
85
|
+
file=sys.stderr,
|
|
86
|
+
)
|
|
87
|
+
for extra in extraneous:
|
|
88
|
+
print(
|
|
89
|
+
(
|
|
90
|
+
"unexpected vendored asset "
|
|
91
|
+
f"{extra.relative_to(root)}; run sync_assets to prune it"
|
|
92
|
+
),
|
|
93
|
+
file=sys.stderr,
|
|
94
|
+
)
|
|
95
|
+
return False
|
|
96
|
+
if not quiet:
|
|
97
|
+
print("Rust asset bundle is up to date.")
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
rust_dir.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
|
|
102
|
+
for name, reason in mismatched:
|
|
103
|
+
source = canonical_dir / name
|
|
104
|
+
target = rust_dir / name
|
|
105
|
+
shutil.copy2(source, target)
|
|
106
|
+
if not quiet:
|
|
107
|
+
verb = "Copied" if reason == "missing" else "Updated"
|
|
108
|
+
print(
|
|
109
|
+
f"{verb} {source.relative_to(root)} -> {target.relative_to(root)}",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
for extra in extraneous:
|
|
113
|
+
extra.unlink()
|
|
114
|
+
if not quiet:
|
|
115
|
+
print(f"Removed extraneous vendored asset {extra.relative_to(root)}")
|
|
116
|
+
|
|
117
|
+
if not mismatched and not extraneous and not quiet:
|
|
118
|
+
print("Rust asset bundle already aligned with canonical copies.")
|
|
119
|
+
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
124
|
+
parser = argparse.ArgumentParser(
|
|
125
|
+
description="Synchronise canonical glitchling assets with the vendored Rust copies.",
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--check",
|
|
129
|
+
action="store_true",
|
|
130
|
+
help="exit with a non-zero status when vendored assets diverge",
|
|
131
|
+
)
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--quiet",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="suppress status output",
|
|
136
|
+
)
|
|
137
|
+
parser.add_argument(
|
|
138
|
+
"--project-root",
|
|
139
|
+
type=Path,
|
|
140
|
+
help="override the detected project root (useful for testing)",
|
|
141
|
+
)
|
|
142
|
+
return parser
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
146
|
+
parser = build_parser()
|
|
147
|
+
args = parser.parse_args(argv)
|
|
148
|
+
ok = sync_assets(project_root=args.project_root, check=args.check, quiet=args.quiet)
|
|
149
|
+
return 0 if ok else 1
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__": # pragma: no cover - CLI entry point
|
|
153
|
+
raise SystemExit(main())
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
import re
|
|
7
6
|
from dataclasses import dataclass
|
|
8
|
-
from
|
|
9
|
-
|
|
7
|
+
from typing import Protocol, Sequence, TypedDict, cast
|
|
8
|
+
|
|
9
|
+
from glitchlings.zoo import assets
|
|
10
10
|
|
|
11
11
|
# Regexes reused across the module
|
|
12
12
|
TOKEN_REGEX = re.compile(r"\w+|\W+")
|
|
@@ -32,12 +32,7 @@ class RandomLike(Protocol):
|
|
|
32
32
|
|
|
33
33
|
# Lexical prior probabilities and pragmatic lexica shared with the Rust fast path.
|
|
34
34
|
def _load_assets() -> HokeyAssets:
|
|
35
|
-
|
|
36
|
-
resources.files("glitchlings.data")
|
|
37
|
-
.joinpath("hokey_assets.json")
|
|
38
|
-
.open("r", encoding="utf-8") as payload
|
|
39
|
-
):
|
|
40
|
-
data: Any = json.load(payload)
|
|
35
|
+
data = assets.load_json("hokey_assets.json")
|
|
41
36
|
return cast(HokeyAssets, data)
|
|
42
37
|
|
|
43
38
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from .assets import read_text
|
|
4
4
|
|
|
5
5
|
_CONFUSION_TABLE: list[tuple[str, list[str]]] | None = None
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def load_confusion_table() -> list[tuple[str, list[str]]]:
|
|
9
9
|
"""Load the OCR confusion table shared by Python and Rust implementations."""
|
|
10
|
+
|
|
10
11
|
global _CONFUSION_TABLE
|
|
11
12
|
if _CONFUSION_TABLE is not None:
|
|
12
13
|
return _CONFUSION_TABLE
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
text = data.read_text(encoding="utf-8")
|
|
15
|
+
text = read_text("ocr_confusions.tsv")
|
|
16
16
|
indexed_entries: list[tuple[int, tuple[str, list[str]]]] = []
|
|
17
17
|
for line_number, line in enumerate(text.splitlines()):
|
|
18
18
|
stripped = line.strip()
|
glitchlings/zoo/_text_utils.py
CHANGED
|
@@ -21,9 +21,9 @@ def split_token_edges(token: str) -> tuple[str, str, str]:
|
|
|
21
21
|
return match.group(1), match.group(2), match.group(3)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
"""Return
|
|
26
|
-
|
|
24
|
+
def _resolve_core_length(core: str, token: str) -> int:
|
|
25
|
+
"""Return a stable core-length measurement used by weighting heuristics."""
|
|
26
|
+
|
|
27
27
|
candidate = core if core else token
|
|
28
28
|
length = len(candidate)
|
|
29
29
|
if length <= 0:
|
|
@@ -34,6 +34,12 @@ def token_core_length(token: str) -> int:
|
|
|
34
34
|
return length
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def token_core_length(token: str) -> int:
|
|
38
|
+
"""Return the length of the main word characters for weighting heuristics."""
|
|
39
|
+
_, core, _ = split_token_edges(token)
|
|
40
|
+
return _resolve_core_length(core, token)
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
@dataclass(frozen=True)
|
|
38
44
|
class WordToken:
|
|
39
45
|
"""Metadata describing a non-whitespace token yielded by word splitters."""
|
|
@@ -71,12 +77,7 @@ def collect_word_tokens(
|
|
|
71
77
|
continue
|
|
72
78
|
|
|
73
79
|
prefix, core, suffix = split_token_edges(token)
|
|
74
|
-
core_length =
|
|
75
|
-
if core_length <= 0:
|
|
76
|
-
stripped = token.strip()
|
|
77
|
-
core_length = len(stripped) if stripped else len(token)
|
|
78
|
-
if core_length <= 0:
|
|
79
|
-
core_length = 1
|
|
80
|
+
core_length = _resolve_core_length(core, token)
|
|
80
81
|
|
|
81
82
|
collected.append(
|
|
82
83
|
WordToken(
|
glitchlings/zoo/adjax.py
CHANGED
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
import random
|
|
4
4
|
from typing import Any, cast
|
|
5
5
|
|
|
6
|
-
from ._rate import resolve_rate
|
|
7
6
|
from ._rust_extensions import get_rust_operation
|
|
8
7
|
from ._text_utils import split_preserving_whitespace, split_token_edges
|
|
9
8
|
from .core import AttackWave, Glitchling
|
|
@@ -66,16 +65,9 @@ def swap_adjacent_words(
|
|
|
66
65
|
rate: float | None = None,
|
|
67
66
|
seed: int | None = None,
|
|
68
67
|
rng: random.Random | None = None,
|
|
69
|
-
*,
|
|
70
|
-
swap_rate: float | None = None,
|
|
71
68
|
) -> str:
|
|
72
69
|
"""Swap adjacent word cores while preserving spacing and punctuation."""
|
|
73
|
-
effective_rate =
|
|
74
|
-
rate=rate,
|
|
75
|
-
legacy_value=swap_rate,
|
|
76
|
-
default=0.5,
|
|
77
|
-
legacy_name="swap_rate",
|
|
78
|
-
)
|
|
70
|
+
effective_rate = 0.5 if rate is None else rate
|
|
79
71
|
clamped_rate = max(0.0, min(effective_rate, 1.0))
|
|
80
72
|
|
|
81
73
|
if rng is None:
|
|
@@ -94,16 +86,9 @@ class Adjax(Glitchling):
|
|
|
94
86
|
self,
|
|
95
87
|
*,
|
|
96
88
|
rate: float | None = None,
|
|
97
|
-
swap_rate: float | None = None,
|
|
98
89
|
seed: int | None = None,
|
|
99
90
|
) -> None:
|
|
100
|
-
|
|
101
|
-
effective_rate = resolve_rate(
|
|
102
|
-
rate=rate,
|
|
103
|
-
legacy_value=swap_rate,
|
|
104
|
-
default=0.5,
|
|
105
|
-
legacy_name="swap_rate",
|
|
106
|
-
)
|
|
91
|
+
effective_rate = 0.5 if rate is None else rate
|
|
107
92
|
super().__init__(
|
|
108
93
|
name="Adjax",
|
|
109
94
|
corruption_function=swap_adjacent_words,
|
|
@@ -118,7 +103,7 @@ class Adjax(Glitchling):
|
|
|
118
103
|
return None
|
|
119
104
|
return {
|
|
120
105
|
"type": "swap_adjacent",
|
|
121
|
-
"
|
|
106
|
+
"rate": float(rate),
|
|
122
107
|
}
|
|
123
108
|
|
|
124
109
|
|
glitchlings/zoo/apostrofae.py
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
import random
|
|
7
6
|
from functools import cache
|
|
8
|
-
from importlib import resources
|
|
9
7
|
from typing import Any, Sequence, cast
|
|
10
8
|
|
|
11
9
|
from ._rust_extensions import get_rust_operation
|
|
10
|
+
from .assets import load_json
|
|
12
11
|
from .core import AttackOrder, AttackWave, Gaggle, Glitchling
|
|
13
12
|
|
|
14
13
|
# Load Rust-accelerated operation if available
|
|
@@ -19,9 +18,7 @@ _apostrofae_rust = get_rust_operation("apostrofae")
|
|
|
19
18
|
def _load_replacement_pairs() -> dict[str, list[tuple[str, str]]]:
|
|
20
19
|
"""Load the curated mapping of straight quotes to fancy pairs."""
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
with resource.open("r", encoding="utf-8") as handle:
|
|
24
|
-
data: dict[str, list[Sequence[str]]] = json.load(handle)
|
|
21
|
+
data: dict[str, list[Sequence[str]]] = load_json("apostrofae_pairs.json")
|
|
25
22
|
|
|
26
23
|
parsed: dict[str, list[tuple[str, str]]] = {}
|
|
27
24
|
for straight, replacements in data.items():
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from hashlib import blake2b
|
|
5
|
+
from importlib import resources
|
|
6
|
+
from importlib.resources.abc import Traversable
|
|
7
|
+
from typing import Any, BinaryIO, TextIO, cast
|
|
8
|
+
|
|
9
|
+
_DEFAULT_DIGEST_SIZE = 32
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _asset(name: str) -> Traversable:
|
|
13
|
+
asset = resources.files(__name__).joinpath(name)
|
|
14
|
+
if not asset.is_file(): # pragma: no cover - defensive guard for packaging issues
|
|
15
|
+
raise FileNotFoundError(f"Asset '{name}' not found at {asset}")
|
|
16
|
+
return asset
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def read_text(name: str, *, encoding: str = "utf-8") -> str:
|
|
20
|
+
"""Return the decoded contents of a bundled text asset."""
|
|
21
|
+
|
|
22
|
+
return cast(str, _asset(name).read_text(encoding=encoding))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def open_text(name: str, *, encoding: str = "utf-8") -> TextIO:
|
|
26
|
+
"""Open a bundled text asset for reading."""
|
|
27
|
+
|
|
28
|
+
return cast(TextIO, _asset(name).open("r", encoding=encoding))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def open_binary(name: str) -> BinaryIO:
|
|
32
|
+
"""Open a bundled binary asset for reading."""
|
|
33
|
+
|
|
34
|
+
return cast(BinaryIO, _asset(name).open("rb"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_json(name: str, *, encoding: str = "utf-8") -> Any:
|
|
38
|
+
"""Deserialize a JSON asset using the shared loader helpers."""
|
|
39
|
+
|
|
40
|
+
with open_text(name, encoding=encoding) as handle:
|
|
41
|
+
return json.load(handle)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def hash_asset(name: str) -> str:
|
|
45
|
+
"""Return a BLAKE2b digest for the bundled asset ``name``."""
|
|
46
|
+
|
|
47
|
+
digest = blake2b(digest_size=_DEFAULT_DIGEST_SIZE)
|
|
48
|
+
with open_binary(name) as handle:
|
|
49
|
+
for chunk in iter(lambda: handle.read(8192), b""):
|
|
50
|
+
digest.update(chunk)
|
|
51
|
+
return digest.hexdigest()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = ["read_text", "open_text", "open_binary", "load_json", "hash_asset"]
|
glitchlings/zoo/jargoyle.py
CHANGED
|
@@ -7,7 +7,6 @@ from typing import Any, Literal, cast
|
|
|
7
7
|
|
|
8
8
|
from glitchlings.lexicon import Lexicon, get_default_lexicon
|
|
9
9
|
|
|
10
|
-
from ._rate import resolve_rate
|
|
11
10
|
from .core import AttackWave, Glitchling
|
|
12
11
|
|
|
13
12
|
_wordnet_module: ModuleType | None
|
|
@@ -119,7 +118,6 @@ def substitute_random_synonyms(
|
|
|
119
118
|
seed: int | None = None,
|
|
120
119
|
rng: random.Random | None = None,
|
|
121
120
|
*,
|
|
122
|
-
replacement_rate: float | None = None,
|
|
123
121
|
lexicon: Lexicon | None = None,
|
|
124
122
|
) -> str:
|
|
125
123
|
"""Replace words with random lexicon-driven synonyms.
|
|
@@ -144,12 +142,7 @@ def substitute_random_synonyms(
|
|
|
144
142
|
deterministic subsets per word and part-of-speech using the active seed.
|
|
145
143
|
|
|
146
144
|
"""
|
|
147
|
-
effective_rate =
|
|
148
|
-
rate=rate,
|
|
149
|
-
legacy_value=replacement_rate,
|
|
150
|
-
default=0.1,
|
|
151
|
-
legacy_name="replacement_rate",
|
|
152
|
-
)
|
|
145
|
+
effective_rate = 0.1 if rate is None else rate
|
|
153
146
|
|
|
154
147
|
active_rng: random.Random
|
|
155
148
|
if rng is not None:
|
|
@@ -258,23 +251,16 @@ class Jargoyle(Glitchling):
|
|
|
258
251
|
self,
|
|
259
252
|
*,
|
|
260
253
|
rate: float | None = None,
|
|
261
|
-
replacement_rate: float | None = None,
|
|
262
254
|
part_of_speech: PartOfSpeechInput = "n",
|
|
263
255
|
seed: int | None = None,
|
|
264
256
|
lexicon: Lexicon | None = None,
|
|
265
257
|
) -> None:
|
|
266
|
-
self._param_aliases = {"replacement_rate": "rate"}
|
|
267
258
|
self._owns_lexicon = lexicon is None
|
|
268
259
|
self._external_lexicon_original_seed = (
|
|
269
260
|
lexicon.seed if isinstance(lexicon, Lexicon) else None
|
|
270
261
|
)
|
|
271
262
|
self._initializing = True
|
|
272
|
-
effective_rate =
|
|
273
|
-
rate=rate,
|
|
274
|
-
legacy_value=replacement_rate,
|
|
275
|
-
default=0.01,
|
|
276
|
-
legacy_name="replacement_rate",
|
|
277
|
-
)
|
|
263
|
+
effective_rate = 0.01 if rate is None else rate
|
|
278
264
|
prepared_lexicon = lexicon or get_default_lexicon(seed=seed)
|
|
279
265
|
if lexicon is not None and seed is not None:
|
|
280
266
|
prepared_lexicon.reseed(seed)
|
glitchlings/zoo/mim1c.py
CHANGED
|
@@ -4,7 +4,6 @@ from typing import Literal
|
|
|
4
4
|
|
|
5
5
|
from confusable_homoglyphs import confusables
|
|
6
6
|
|
|
7
|
-
from ._rate import resolve_rate
|
|
8
7
|
from .core import AttackOrder, AttackWave, Glitchling
|
|
9
8
|
|
|
10
9
|
|
|
@@ -15,8 +14,6 @@ def swap_homoglyphs(
|
|
|
15
14
|
banned_characters: Collection[str] | None = None,
|
|
16
15
|
seed: int | None = None,
|
|
17
16
|
rng: random.Random | None = None,
|
|
18
|
-
*,
|
|
19
|
-
replacement_rate: float | None = None,
|
|
20
17
|
) -> str:
|
|
21
18
|
"""Replace characters with visually confusable homoglyphs.
|
|
22
19
|
|
|
@@ -37,12 +34,7 @@ def swap_homoglyphs(
|
|
|
37
34
|
- Maintains determinism by shuffling candidates and sampling via the provided RNG.
|
|
38
35
|
|
|
39
36
|
"""
|
|
40
|
-
effective_rate =
|
|
41
|
-
rate=rate,
|
|
42
|
-
legacy_value=replacement_rate,
|
|
43
|
-
default=0.02,
|
|
44
|
-
legacy_name="replacement_rate",
|
|
45
|
-
)
|
|
37
|
+
effective_rate = 0.02 if rate is None else rate
|
|
46
38
|
|
|
47
39
|
if rng is None:
|
|
48
40
|
rng = random.Random(seed)
|
|
@@ -79,18 +71,11 @@ class Mim1c(Glitchling):
|
|
|
79
71
|
self,
|
|
80
72
|
*,
|
|
81
73
|
rate: float | None = None,
|
|
82
|
-
replacement_rate: float | None = None,
|
|
83
74
|
classes: list[str] | Literal["all"] | None = None,
|
|
84
75
|
banned_characters: Collection[str] | None = None,
|
|
85
76
|
seed: int | None = None,
|
|
86
77
|
) -> None:
|
|
87
|
-
|
|
88
|
-
effective_rate = resolve_rate(
|
|
89
|
-
rate=rate,
|
|
90
|
-
legacy_value=replacement_rate,
|
|
91
|
-
default=0.02,
|
|
92
|
-
legacy_name="replacement_rate",
|
|
93
|
-
)
|
|
78
|
+
effective_rate = 0.02 if rate is None else rate
|
|
94
79
|
super().__init__(
|
|
95
80
|
name="Mim1c",
|
|
96
81
|
corruption_function=swap_homoglyphs,
|
glitchlings/zoo/redactyl.py
CHANGED
|
@@ -2,7 +2,6 @@ import random
|
|
|
2
2
|
import re
|
|
3
3
|
from typing import Any, cast
|
|
4
4
|
|
|
5
|
-
from ._rate import resolve_rate
|
|
6
5
|
from ._rust_extensions import get_rust_operation
|
|
7
6
|
from ._sampling import weighted_sample_without_replacement
|
|
8
7
|
from ._text_utils import (
|
|
@@ -97,16 +96,10 @@ def redact_words(
|
|
|
97
96
|
seed: int = 151,
|
|
98
97
|
rng: random.Random | None = None,
|
|
99
98
|
*,
|
|
100
|
-
redaction_rate: float | None = None,
|
|
101
99
|
unweighted: bool = False,
|
|
102
100
|
) -> str:
|
|
103
101
|
"""Redact random words by replacing their characters."""
|
|
104
|
-
effective_rate =
|
|
105
|
-
rate=rate,
|
|
106
|
-
legacy_value=redaction_rate,
|
|
107
|
-
default=0.025,
|
|
108
|
-
legacy_name="redaction_rate",
|
|
109
|
-
)
|
|
102
|
+
effective_rate = 0.025 if rate is None else rate
|
|
110
103
|
|
|
111
104
|
if rng is None:
|
|
112
105
|
rng = random.Random(seed)
|
|
@@ -148,18 +141,11 @@ class Redactyl(Glitchling):
|
|
|
148
141
|
*,
|
|
149
142
|
replacement_char: str = FULL_BLOCK,
|
|
150
143
|
rate: float | None = None,
|
|
151
|
-
redaction_rate: float | None = None,
|
|
152
144
|
merge_adjacent: bool = False,
|
|
153
145
|
seed: int = 151,
|
|
154
146
|
unweighted: bool = False,
|
|
155
147
|
) -> None:
|
|
156
|
-
|
|
157
|
-
effective_rate = resolve_rate(
|
|
158
|
-
rate=rate,
|
|
159
|
-
legacy_value=redaction_rate,
|
|
160
|
-
default=0.025,
|
|
161
|
-
legacy_name="redaction_rate",
|
|
162
|
-
)
|
|
148
|
+
effective_rate = 0.025 if rate is None else rate
|
|
163
149
|
super().__init__(
|
|
164
150
|
name="Redactyl",
|
|
165
151
|
corruption_function=redact_words,
|
|
@@ -181,7 +167,7 @@ class Redactyl(Glitchling):
|
|
|
181
167
|
return {
|
|
182
168
|
"type": "redact",
|
|
183
169
|
"replacement_char": str(replacement_char),
|
|
184
|
-
"
|
|
170
|
+
"rate": float(rate),
|
|
185
171
|
"merge_adjacent": bool(merge_adjacent),
|
|
186
172
|
"unweighted": unweighted,
|
|
187
173
|
}
|
glitchlings/zoo/reduple.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import random
|
|
2
2
|
from typing import Any, cast
|
|
3
3
|
|
|
4
|
-
from ._rate import resolve_rate
|
|
5
4
|
from ._rust_extensions import get_rust_operation
|
|
6
5
|
from ._text_utils import WordToken, collect_word_tokens, split_preserving_whitespace
|
|
7
6
|
from .core import AttackWave, Glitchling
|
|
@@ -71,7 +70,6 @@ def reduplicate_words(
|
|
|
71
70
|
seed: int | None = None,
|
|
72
71
|
rng: random.Random | None = None,
|
|
73
72
|
*,
|
|
74
|
-
reduplication_rate: float | None = None,
|
|
75
73
|
unweighted: bool = False,
|
|
76
74
|
) -> str:
|
|
77
75
|
"""Randomly reduplicate words in the text.
|
|
@@ -79,12 +77,7 @@ def reduplicate_words(
|
|
|
79
77
|
Falls back to the Python implementation when the optional Rust
|
|
80
78
|
extension is unavailable.
|
|
81
79
|
"""
|
|
82
|
-
effective_rate =
|
|
83
|
-
rate=rate,
|
|
84
|
-
legacy_value=reduplication_rate,
|
|
85
|
-
default=0.01,
|
|
86
|
-
legacy_name="reduplication_rate",
|
|
87
|
-
)
|
|
80
|
+
effective_rate = 0.01 if rate is None else rate
|
|
88
81
|
|
|
89
82
|
if rng is None:
|
|
90
83
|
rng = random.Random(seed)
|
|
@@ -110,17 +103,10 @@ class Reduple(Glitchling):
|
|
|
110
103
|
self,
|
|
111
104
|
*,
|
|
112
105
|
rate: float | None = None,
|
|
113
|
-
reduplication_rate: float | None = None,
|
|
114
106
|
seed: int | None = None,
|
|
115
107
|
unweighted: bool = False,
|
|
116
108
|
) -> None:
|
|
117
|
-
|
|
118
|
-
effective_rate = resolve_rate(
|
|
119
|
-
rate=rate,
|
|
120
|
-
legacy_value=reduplication_rate,
|
|
121
|
-
default=0.01,
|
|
122
|
-
legacy_name="reduplication_rate",
|
|
123
|
-
)
|
|
109
|
+
effective_rate = 0.01 if rate is None else rate
|
|
124
110
|
super().__init__(
|
|
125
111
|
name="Reduple",
|
|
126
112
|
corruption_function=reduplicate_words,
|
|
@@ -137,7 +123,7 @@ class Reduple(Glitchling):
|
|
|
137
123
|
unweighted = bool(self.kwargs.get("unweighted", False))
|
|
138
124
|
return {
|
|
139
125
|
"type": "reduplicate",
|
|
140
|
-
"
|
|
126
|
+
"rate": float(rate),
|
|
141
127
|
"unweighted": unweighted,
|
|
142
128
|
}
|
|
143
129
|
|
glitchlings/zoo/rushmore.py
CHANGED
|
@@ -3,7 +3,6 @@ import random
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import Any, cast
|
|
5
5
|
|
|
6
|
-
from ._rate import resolve_rate
|
|
7
6
|
from ._rust_extensions import get_rust_operation
|
|
8
7
|
from ._text_utils import WordToken, collect_word_tokens, split_preserving_whitespace
|
|
9
8
|
from .core import AttackWave, Glitchling
|
|
@@ -74,20 +73,13 @@ def delete_random_words(
|
|
|
74
73
|
rate: float | None = None,
|
|
75
74
|
seed: int | None = None,
|
|
76
75
|
rng: random.Random | None = None,
|
|
77
|
-
*,
|
|
78
|
-
max_deletion_rate: float | None = None,
|
|
79
76
|
unweighted: bool = False,
|
|
80
77
|
) -> str:
|
|
81
78
|
"""Delete random words from the input text.
|
|
82
79
|
|
|
83
80
|
Uses the optional Rust implementation when available.
|
|
84
81
|
"""
|
|
85
|
-
effective_rate =
|
|
86
|
-
rate=rate,
|
|
87
|
-
legacy_value=max_deletion_rate,
|
|
88
|
-
default=0.01,
|
|
89
|
-
legacy_name="max_deletion_rate",
|
|
90
|
-
)
|
|
82
|
+
effective_rate = 0.01 if rate is None else rate
|
|
91
83
|
|
|
92
84
|
if rng is None:
|
|
93
85
|
rng = random.Random(seed)
|
|
@@ -113,17 +105,10 @@ class Rushmore(Glitchling):
|
|
|
113
105
|
self,
|
|
114
106
|
*,
|
|
115
107
|
rate: float | None = None,
|
|
116
|
-
max_deletion_rate: float | None = None,
|
|
117
108
|
seed: int | None = None,
|
|
118
109
|
unweighted: bool = False,
|
|
119
110
|
) -> None:
|
|
120
|
-
|
|
121
|
-
effective_rate = resolve_rate(
|
|
122
|
-
rate=rate,
|
|
123
|
-
legacy_value=max_deletion_rate,
|
|
124
|
-
default=0.01,
|
|
125
|
-
legacy_name="max_deletion_rate",
|
|
126
|
-
)
|
|
111
|
+
effective_rate = 0.01 if rate is None else rate
|
|
127
112
|
super().__init__(
|
|
128
113
|
name="Rushmore",
|
|
129
114
|
corruption_function=delete_random_words,
|
|
@@ -135,14 +120,12 @@ class Rushmore(Glitchling):
|
|
|
135
120
|
|
|
136
121
|
def pipeline_operation(self) -> dict[str, Any] | None:
|
|
137
122
|
rate = self.kwargs.get("rate")
|
|
138
|
-
if rate is None:
|
|
139
|
-
rate = self.kwargs.get("max_deletion_rate")
|
|
140
123
|
if rate is None:
|
|
141
124
|
return None
|
|
142
125
|
unweighted = bool(self.kwargs.get("unweighted", False))
|
|
143
126
|
return {
|
|
144
127
|
"type": "delete",
|
|
145
|
-
"
|
|
128
|
+
"rate": float(rate),
|
|
146
129
|
"unweighted": unweighted,
|
|
147
130
|
}
|
|
148
131
|
|
glitchlings/zoo/scannequin.py
CHANGED
|
@@ -3,7 +3,6 @@ import re
|
|
|
3
3
|
from typing import Any, cast
|
|
4
4
|
|
|
5
5
|
from ._ocr_confusions import load_confusion_table
|
|
6
|
-
from ._rate import resolve_rate
|
|
7
6
|
from ._rust_extensions import get_rust_operation
|
|
8
7
|
from .core import AttackOrder, AttackWave, Glitchling
|
|
9
8
|
|
|
@@ -102,8 +101,6 @@ def ocr_artifacts(
|
|
|
102
101
|
rate: float | None = None,
|
|
103
102
|
seed: int | None = None,
|
|
104
103
|
rng: random.Random | None = None,
|
|
105
|
-
*,
|
|
106
|
-
error_rate: float | None = None,
|
|
107
104
|
) -> str:
|
|
108
105
|
"""Introduce OCR-like artifacts into text.
|
|
109
106
|
|
|
@@ -112,12 +109,7 @@ def ocr_artifacts(
|
|
|
112
109
|
if not text:
|
|
113
110
|
return text
|
|
114
111
|
|
|
115
|
-
effective_rate =
|
|
116
|
-
rate=rate,
|
|
117
|
-
legacy_value=error_rate,
|
|
118
|
-
default=0.02,
|
|
119
|
-
legacy_name="error_rate",
|
|
120
|
-
)
|
|
112
|
+
effective_rate = 0.02 if rate is None else rate
|
|
121
113
|
|
|
122
114
|
if rng is None:
|
|
123
115
|
rng = random.Random(seed)
|
|
@@ -137,16 +129,9 @@ class Scannequin(Glitchling):
|
|
|
137
129
|
self,
|
|
138
130
|
*,
|
|
139
131
|
rate: float | None = None,
|
|
140
|
-
error_rate: float | None = None,
|
|
141
132
|
seed: int | None = None,
|
|
142
133
|
) -> None:
|
|
143
|
-
|
|
144
|
-
effective_rate = resolve_rate(
|
|
145
|
-
rate=rate,
|
|
146
|
-
legacy_value=error_rate,
|
|
147
|
-
default=0.02,
|
|
148
|
-
legacy_name="error_rate",
|
|
149
|
-
)
|
|
134
|
+
effective_rate = 0.02 if rate is None else rate
|
|
150
135
|
super().__init__(
|
|
151
136
|
name="Scannequin",
|
|
152
137
|
corruption_function=ocr_artifacts,
|
|
@@ -158,11 +143,9 @@ class Scannequin(Glitchling):
|
|
|
158
143
|
|
|
159
144
|
def pipeline_operation(self) -> dict[str, Any] | None:
|
|
160
145
|
rate = self.kwargs.get("rate")
|
|
161
|
-
if rate is None:
|
|
162
|
-
rate = self.kwargs.get("error_rate")
|
|
163
146
|
if rate is None:
|
|
164
147
|
return None
|
|
165
|
-
return {"type": "ocr", "
|
|
148
|
+
return {"type": "ocr", "rate": float(rate)}
|
|
166
149
|
|
|
167
150
|
|
|
168
151
|
scannequin = Scannequin()
|
glitchlings/zoo/typogre.py
CHANGED
|
@@ -5,7 +5,6 @@ import random
|
|
|
5
5
|
from typing import Any, Optional, cast
|
|
6
6
|
|
|
7
7
|
from ..util import KEYNEIGHBORS
|
|
8
|
-
from ._rate import resolve_rate
|
|
9
8
|
from ._rust_extensions import get_rust_operation
|
|
10
9
|
from .core import AttackOrder, AttackWave, Glitchling
|
|
11
10
|
|
|
@@ -144,16 +143,9 @@ def fatfinger(
|
|
|
144
143
|
keyboard: str = "CURATOR_QWERTY",
|
|
145
144
|
seed: int | None = None,
|
|
146
145
|
rng: random.Random | None = None,
|
|
147
|
-
*,
|
|
148
|
-
max_change_rate: float | None = None,
|
|
149
146
|
) -> str:
|
|
150
147
|
"""Introduce character-level "fat finger" edits with a Rust fast path."""
|
|
151
|
-
effective_rate =
|
|
152
|
-
rate=rate,
|
|
153
|
-
legacy_value=max_change_rate,
|
|
154
|
-
default=0.02,
|
|
155
|
-
legacy_name="max_change_rate",
|
|
156
|
-
)
|
|
148
|
+
effective_rate = 0.02 if rate is None else rate
|
|
157
149
|
|
|
158
150
|
if rng is None:
|
|
159
151
|
rng = random.Random(seed)
|
|
@@ -182,17 +174,10 @@ class Typogre(Glitchling):
|
|
|
182
174
|
self,
|
|
183
175
|
*,
|
|
184
176
|
rate: float | None = None,
|
|
185
|
-
max_change_rate: float | None = None,
|
|
186
177
|
keyboard: str = "CURATOR_QWERTY",
|
|
187
178
|
seed: int | None = None,
|
|
188
179
|
) -> None:
|
|
189
|
-
|
|
190
|
-
effective_rate = resolve_rate(
|
|
191
|
-
rate=rate,
|
|
192
|
-
legacy_value=max_change_rate,
|
|
193
|
-
default=0.02,
|
|
194
|
-
legacy_name="max_change_rate",
|
|
195
|
-
)
|
|
180
|
+
effective_rate = 0.02 if rate is None else rate
|
|
196
181
|
super().__init__(
|
|
197
182
|
name="Typogre",
|
|
198
183
|
corruption_function=fatfinger,
|
|
@@ -205,8 +190,6 @@ class Typogre(Glitchling):
|
|
|
205
190
|
|
|
206
191
|
def pipeline_operation(self) -> dict[str, Any] | None:
|
|
207
192
|
rate = self.kwargs.get("rate")
|
|
208
|
-
if rate is None:
|
|
209
|
-
rate = self.kwargs.get("max_change_rate")
|
|
210
193
|
if rate is None:
|
|
211
194
|
return None
|
|
212
195
|
|
glitchlings/zoo/zeedub.py
CHANGED
|
@@ -5,7 +5,6 @@ import random
|
|
|
5
5
|
from collections.abc import Sequence
|
|
6
6
|
from typing import Any, cast
|
|
7
7
|
|
|
8
|
-
from ._rate import resolve_rate
|
|
9
8
|
from ._rust_extensions import get_rust_operation
|
|
10
9
|
from .core import AttackOrder, AttackWave, Glitchling
|
|
11
10
|
|
|
@@ -77,12 +76,7 @@ def insert_zero_widths(
|
|
|
77
76
|
characters: Sequence[str] | None = None,
|
|
78
77
|
) -> str:
|
|
79
78
|
"""Inject zero-width characters between non-space character pairs."""
|
|
80
|
-
effective_rate =
|
|
81
|
-
rate=rate,
|
|
82
|
-
legacy_value=None,
|
|
83
|
-
default=0.02,
|
|
84
|
-
legacy_name="rate",
|
|
85
|
-
)
|
|
79
|
+
effective_rate = 0.02 if rate is None else rate
|
|
86
80
|
|
|
87
81
|
if rng is None:
|
|
88
82
|
rng = random.Random(seed)
|
|
@@ -142,12 +136,7 @@ class Zeedub(Glitchling):
|
|
|
142
136
|
seed: int | None = None,
|
|
143
137
|
characters: Sequence[str] | None = None,
|
|
144
138
|
) -> None:
|
|
145
|
-
effective_rate =
|
|
146
|
-
rate=rate,
|
|
147
|
-
legacy_value=None,
|
|
148
|
-
default=0.02,
|
|
149
|
-
legacy_name="rate",
|
|
150
|
-
)
|
|
139
|
+
effective_rate = 0.02 if rate is None else rate
|
|
151
140
|
super().__init__(
|
|
152
141
|
name="Zeedub",
|
|
153
142
|
corruption_function=insert_zero_widths,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glitchlings
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Monsters for your language games.
|
|
5
5
|
Author: osoleve
|
|
6
6
|
License: Apache License
|
|
@@ -294,11 +294,14 @@ Dynamic: license-file
|
|
|
294
294
|
Every language game breeds monsters.
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
+

|
|
297
298
|
[](https://pypi.org/project/glitchlings/)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+

|
|
300
|
+

|
|
301
|
+

|
|
302
|
+

|
|
303
|
+

|
|
304
|
+

|
|
302
305
|
|
|
303
306
|
`Glitchlings` are **utilities for corrupting the text inputs to your language models in deterministic, _linguistically principled_** ways.
|
|
304
307
|
Each embodies a different way that documents can be compromised in the wild.
|
|
@@ -596,7 +599,6 @@ _Keep your hands and punctuation where I can see them._
|
|
|
596
599
|
> Args
|
|
597
600
|
>
|
|
598
601
|
> - `rate (float)`: Probability that each adjacent pair swaps cores (default: 0.5, 50%).
|
|
599
|
-
> - `swap_rate (float)`: Alias for `rate`, retained for backward compatibility.
|
|
600
602
|
> - `seed (int)`: The random seed for reproducibility (default: 151).
|
|
601
603
|
|
|
602
604
|
### Redactyl
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
glitchlings/__init__.py,sha256=pNL6Vx6ggpRq-vW0CXJjnWOe_LktP6Zz9xNhqDHj3Dw,1301
|
|
2
2
|
glitchlings/__main__.py,sha256=nB7btO_T4wBFOcyawfWpjEindVrUfTqqV5hdeeS1HT8,128
|
|
3
|
-
glitchlings/_zoo_rust.cp313-win_amd64.pyd,sha256=
|
|
3
|
+
glitchlings/_zoo_rust.cp313-win_amd64.pyd,sha256=OFN9vx0K5-NueJ-sOSQKwhWudU2mRqGcHK4AkWxZ8-c,2349056
|
|
4
4
|
glitchlings/compat.py,sha256=j4lkWNtyox5sen5j7u0SHfnk8QUn-yicaqvuLlZp1-s,9174
|
|
5
|
-
glitchlings/config.py,sha256=
|
|
5
|
+
glitchlings/config.py,sha256=00BAHPbfOFnGHvC7FCcz1YpJjdsfpn1ET5SJJdsVQog,12677
|
|
6
6
|
glitchlings/config.toml,sha256=OywXmEpuOPtyJRbcRt4cwQkHiZ__5axEHoCaX9ye-uA,102
|
|
7
7
|
glitchlings/main.py,sha256=eCUEFsu8-NLDz1xyKNDIucVm975HNQZJYm6YCv8RIyg,10987
|
|
8
|
-
glitchlings/
|
|
9
|
-
glitchlings/
|
|
8
|
+
glitchlings/dev/__init__.py,sha256=Pr2tXDwfa6SHW1wPseLHRXPINi7lEs6QNClreU0rAiw,147
|
|
9
|
+
glitchlings/dev/sync_assets.py,sha256=QgUNrNpNloxqVGTCX0kVYwQMkJ58zGiZd37svteeS5M,4830
|
|
10
10
|
glitchlings/dlc/__init__.py,sha256=iFDTwkaWl2C0_QUYykIXfmOUzy__oURX_BiJhexf-8o,312
|
|
11
11
|
glitchlings/dlc/_shared.py,sha256=q1xMclEsbR0KIEn9chwZXRubMnIvU-uIc_0PxCFpmqE,4560
|
|
12
12
|
glitchlings/dlc/huggingface.py,sha256=6wD4Vu2jp1d8GYSUiAvOAXqCO9w4HBxCOSfbJM8Ylzw,2657
|
|
@@ -23,31 +23,31 @@ glitchlings/util/__init__.py,sha256=Q5lkncOaM6f2eJK3HAtZyxpCjGnekCpwPloqasS3JDo,
|
|
|
23
23
|
glitchlings/util/adapters.py,sha256=mFhPlE8JaFuO_C-3_aqhgwkqa6isV8Y2ifqVh3Iv9JM,720
|
|
24
24
|
glitchlings/util/hokey_generator.py,sha256=hNWVbscVeKvcqwFtJ1oaWYf2Z0qHSxy0-iMLjht6zuM,4627
|
|
25
25
|
glitchlings/util/stretch_locator.py,sha256=tM4XsQ_asNXQq2Yee8STybFMCC2HbSKCu9I49G0yJ3c,4334
|
|
26
|
-
glitchlings/util/stretchability.py,sha256=
|
|
26
|
+
glitchlings/util/stretchability.py,sha256=WWGMN9MTbHhgQIBB_p_cz-JKb51rCHZqyDRjPGkUOjY,13037
|
|
27
27
|
glitchlings/zoo/__init__.py,sha256=Ab69SD_RUXfiWUEzqU3wteGWobpm2kkbmwndYLEbv_0,5511
|
|
28
|
-
glitchlings/zoo/_ocr_confusions.py,sha256=
|
|
29
|
-
glitchlings/zoo/_rate.py,sha256=KxFDFJEGWsv76v1JcoHXIETj9kGdbbAiUqCPwAcWcDw,3710
|
|
28
|
+
glitchlings/zoo/_ocr_confusions.py,sha256=Kyo3W6w-FGs4C7jXp4bLFHFCaa0RUJWvUrWKqfBOdBk,1171
|
|
30
29
|
glitchlings/zoo/_rust_extensions.py,sha256=SdU06m-qjf-rRHnqM5OMophx1IacoI4KbyRu2HXrTUc,4354
|
|
31
30
|
glitchlings/zoo/_sampling.py,sha256=AAPLObjqKrmX882TX8hdvPHReBOcv0Z4pUuW6AxuGgU,1640
|
|
32
|
-
glitchlings/zoo/_text_utils.py,sha256=
|
|
33
|
-
glitchlings/zoo/adjax.py,sha256=
|
|
34
|
-
glitchlings/zoo/apostrofae.py,sha256=
|
|
31
|
+
glitchlings/zoo/_text_utils.py,sha256=DRjPuC-nFoxFeA7KIbpuIB5k8DXbvvvcHNJniL_8KoQ,2872
|
|
32
|
+
glitchlings/zoo/adjax.py,sha256=Oz3WGSsa4qpH7HLgBQC0r4JktJYGl3A0vkVknqeSG5I,3249
|
|
33
|
+
glitchlings/zoo/apostrofae.py,sha256=C9x9_jj9CidYEWL9OEoNJU_Jzr3Vk4UQ8tITThVbwvY,3798
|
|
35
34
|
glitchlings/zoo/core.py,sha256=tkJFqGwpVa7qQxkUr9lsHOB8zS3lDCo987R6Nvz375U,21257
|
|
36
35
|
glitchlings/zoo/hokey.py,sha256=h-yjCLqYAZFksIYw4fMniuTWUywFw9GU9yUFs_uECHo,5471
|
|
37
|
-
glitchlings/zoo/jargoyle.py,sha256=
|
|
38
|
-
glitchlings/zoo/mim1c.py,sha256=
|
|
39
|
-
glitchlings/zoo/
|
|
40
|
-
glitchlings/zoo/
|
|
41
|
-
glitchlings/zoo/
|
|
42
|
-
glitchlings/zoo/
|
|
43
|
-
glitchlings/zoo/
|
|
44
|
-
glitchlings/zoo/
|
|
45
|
-
glitchlings/zoo/
|
|
46
|
-
glitchlings/zoo/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
glitchlings/zoo/jargoyle.py,sha256=2FwL4A_DpI2Lz6pnZo5iSJZzgd8wDNM9bMeOsMVFSmw,11581
|
|
37
|
+
glitchlings/zoo/mim1c.py,sha256=86lCawTFWRWsO_vH6GMRJfM09TMTUy5MNhO9gsBEkk0,3105
|
|
38
|
+
glitchlings/zoo/redactyl.py,sha256=2BYQfcKwp1WFPwXomQCmh-z0UPN9sNx6Px84WilFs3k,5323
|
|
39
|
+
glitchlings/zoo/reduple.py,sha256=YukTZvLMBoarx96T4LHLGHr_KJVmFkAb0OBsO13ws8A,3911
|
|
40
|
+
glitchlings/zoo/rushmore.py,sha256=CiVkLlskOmfKtzFanmYe6wxKJkCSMbLeKTWDrwZZaXw,3895
|
|
41
|
+
glitchlings/zoo/scannequin.py,sha256=s2wno1_7aGebXvG3o9WvDQ5RRwp5Upl2dejbDUGIXkY,4560
|
|
42
|
+
glitchlings/zoo/typogre.py,sha256=B1-kMfOygKtiKXDBQOHJxvheielBWGbbdWk-cgoONgs,6402
|
|
43
|
+
glitchlings/zoo/zeedub.py,sha256=4x9bNQEq4VSsfol77mYx8jyBRyTugjRQPyk-ANy6gpY,4792
|
|
44
|
+
glitchlings/zoo/assets/__init__.py,sha256=6dOYQnMw-Hk9Tb3lChgZ4aoRr9qacZtbXR8ROnDfBq8,1691
|
|
47
45
|
glitchlings/zoo/assets/apostrofae_pairs.json,sha256=lPLFLndzn_f7_5wZizxsLMnwBY4O63zsCvDjyJ56MLA,553
|
|
48
|
-
glitchlings
|
|
49
|
-
glitchlings
|
|
50
|
-
glitchlings-0.
|
|
51
|
-
glitchlings-0.
|
|
52
|
-
glitchlings-0.
|
|
53
|
-
glitchlings-0.
|
|
46
|
+
glitchlings/zoo/assets/hokey_assets.json,sha256=1GaSEzXwtT1nvf0B9mFyLzHOcqzKbPreibsC6iBWAHA,3083
|
|
47
|
+
glitchlings/zoo/assets/ocr_confusions.tsv,sha256=S-IJEYCIXYKT1Uu7Id8Lnvg5pw528yNigTtWUdnMv9k,213
|
|
48
|
+
glitchlings-0.5.0.dist-info/licenses/LICENSE,sha256=EFEP1evBfHaxsMTBjxm0sZVRp2wct8QLvHE1saII5FI,11538
|
|
49
|
+
glitchlings-0.5.0.dist-info/METADATA,sha256=Nu4n4Oy-LoMko5-2ncvshT_OKMWxr-k9YOm5uMLIHBk,34013
|
|
50
|
+
glitchlings-0.5.0.dist-info/WHEEL,sha256=qV0EIPljj1XC_vuSatRWjn02nZIz3N1t8jsZz7HBr2U,101
|
|
51
|
+
glitchlings-0.5.0.dist-info/entry_points.txt,sha256=kGOwuAsjFDLtztLisaXtOouq9wFVMOJg5FzaAkg-Hto,54
|
|
52
|
+
glitchlings-0.5.0.dist-info/top_level.txt,sha256=VHFNBrLjtDwPCYXbGKi6o17Eueedi81eNbR3hBOoST0,12
|
|
53
|
+
glitchlings-0.5.0.dist-info/RECORD,,
|
glitchlings/data/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Static data assets shared across Glitchlings implementations."""
|
glitchlings/zoo/_rate.py
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"""Utilities for handling legacy parameter names across glitchling classes."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import warnings
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def resolve_rate(
|
|
9
|
-
*,
|
|
10
|
-
rate: float | None,
|
|
11
|
-
legacy_value: float | None,
|
|
12
|
-
default: float,
|
|
13
|
-
legacy_name: str,
|
|
14
|
-
) -> float:
|
|
15
|
-
"""Return the effective rate while enforcing mutual exclusivity.
|
|
16
|
-
|
|
17
|
-
This function centralizes the handling of legacy parameter names, allowing
|
|
18
|
-
glitchlings to maintain backwards compatibility while encouraging migration
|
|
19
|
-
to the standardized 'rate' parameter.
|
|
20
|
-
|
|
21
|
-
Parameters
|
|
22
|
-
----------
|
|
23
|
-
rate : float | None
|
|
24
|
-
The preferred parameter value.
|
|
25
|
-
legacy_value : float | None
|
|
26
|
-
The deprecated legacy parameter value.
|
|
27
|
-
default : float
|
|
28
|
-
Default value if neither parameter is specified.
|
|
29
|
-
legacy_name : str
|
|
30
|
-
Name of the legacy parameter for error/warning messages.
|
|
31
|
-
|
|
32
|
-
Returns
|
|
33
|
-
-------
|
|
34
|
-
float
|
|
35
|
-
The resolved rate value.
|
|
36
|
-
|
|
37
|
-
Raises
|
|
38
|
-
------
|
|
39
|
-
ValueError
|
|
40
|
-
If both rate and legacy_value are specified simultaneously.
|
|
41
|
-
|
|
42
|
-
Warnings
|
|
43
|
-
--------
|
|
44
|
-
DeprecationWarning
|
|
45
|
-
If the legacy parameter is used, a deprecation warning is issued.
|
|
46
|
-
|
|
47
|
-
Examples
|
|
48
|
-
--------
|
|
49
|
-
>>> resolve_rate(rate=0.5, legacy_value=None, default=0.1, legacy_name="old_rate")
|
|
50
|
-
0.5
|
|
51
|
-
>>> resolve_rate(rate=None, legacy_value=0.3, default=0.1, legacy_name="old_rate")
|
|
52
|
-
0.3 # Issues deprecation warning
|
|
53
|
-
>>> resolve_rate(rate=None, legacy_value=None, default=0.1, legacy_name="old_rate")
|
|
54
|
-
0.1
|
|
55
|
-
|
|
56
|
-
"""
|
|
57
|
-
if rate is not None and legacy_value is not None:
|
|
58
|
-
raise ValueError(f"Specify either 'rate' or '{legacy_name}', not both.")
|
|
59
|
-
|
|
60
|
-
if rate is not None:
|
|
61
|
-
return rate
|
|
62
|
-
|
|
63
|
-
if legacy_value is not None:
|
|
64
|
-
warnings.warn(
|
|
65
|
-
f"The '{legacy_name}' parameter is deprecated and will be removed in version 0.6.0. "
|
|
66
|
-
f"Use 'rate' instead.",
|
|
67
|
-
DeprecationWarning,
|
|
68
|
-
stacklevel=3,
|
|
69
|
-
)
|
|
70
|
-
return legacy_value
|
|
71
|
-
|
|
72
|
-
return default
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def resolve_legacy_param(
|
|
76
|
-
*,
|
|
77
|
-
preferred_value: object,
|
|
78
|
-
legacy_value: object,
|
|
79
|
-
default: object,
|
|
80
|
-
preferred_name: str,
|
|
81
|
-
legacy_name: str,
|
|
82
|
-
) -> object:
|
|
83
|
-
"""Resolve a parameter that has both preferred and legacy names.
|
|
84
|
-
|
|
85
|
-
This is a generalized version of resolve_rate() that works with any type.
|
|
86
|
-
|
|
87
|
-
Parameters
|
|
88
|
-
----------
|
|
89
|
-
preferred_value : object
|
|
90
|
-
The value from the preferred parameter name.
|
|
91
|
-
legacy_value : object
|
|
92
|
-
The value from the legacy parameter name.
|
|
93
|
-
default : object
|
|
94
|
-
Default value if neither parameter is specified.
|
|
95
|
-
preferred_name : str
|
|
96
|
-
Name of the preferred parameter.
|
|
97
|
-
legacy_name : str
|
|
98
|
-
Name of the legacy parameter for warning messages.
|
|
99
|
-
|
|
100
|
-
Returns
|
|
101
|
-
-------
|
|
102
|
-
object
|
|
103
|
-
The resolved parameter value.
|
|
104
|
-
|
|
105
|
-
Raises
|
|
106
|
-
------
|
|
107
|
-
ValueError
|
|
108
|
-
If both preferred and legacy values are specified simultaneously.
|
|
109
|
-
|
|
110
|
-
Warnings
|
|
111
|
-
--------
|
|
112
|
-
DeprecationWarning
|
|
113
|
-
If the legacy parameter is used.
|
|
114
|
-
|
|
115
|
-
"""
|
|
116
|
-
if preferred_value is not None and legacy_value is not None:
|
|
117
|
-
raise ValueError(f"Specify either '{preferred_name}' or '{legacy_name}', not both.")
|
|
118
|
-
|
|
119
|
-
if preferred_value is not None:
|
|
120
|
-
return preferred_value
|
|
121
|
-
|
|
122
|
-
if legacy_value is not None:
|
|
123
|
-
warnings.warn(
|
|
124
|
-
f"The '{legacy_name}' parameter is deprecated and will be removed in version 0.6.0. "
|
|
125
|
-
f"Use '{preferred_name}' instead.",
|
|
126
|
-
DeprecationWarning,
|
|
127
|
-
stacklevel=3,
|
|
128
|
-
)
|
|
129
|
-
return legacy_value
|
|
130
|
-
|
|
131
|
-
return default
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|