glitchlings 0.5.0__cp311-cp311-macosx_11_0_universal2.whl → 0.5.1__cp311-cp311-macosx_11_0_universal2.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.

@@ -0,0 +1,226 @@
1
+ """Homophone substitution glitchling implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import random
7
+ from typing import Any, Iterable, Mapping, Sequence, cast
8
+
9
+ from ._rust_extensions import get_rust_operation
10
+ from ._text_utils import WordToken, collect_word_tokens, split_preserving_whitespace
11
+ from .assets import load_homophone_groups
12
+ from .core import AttackOrder, AttackWave, Glitchling
13
+
14
+ _DEFAULT_RATE = 0.02
15
+ _DEFAULT_WEIGHTING = "flat"
16
+ _VALID_WEIGHTINGS = {_DEFAULT_WEIGHTING}
17
+
18
+ _homophone_groups: tuple[tuple[str, ...], ...] = load_homophone_groups()
19
+
20
+
21
+ def _normalise_group(group: Sequence[str]) -> tuple[str, ...]:
22
+ """Return a tuple of lowercase homophones preserving original order."""
23
+
24
+ # Use dict.fromkeys to preserve the original ordering while de-duplicating.
25
+ return tuple(dict.fromkeys(word.lower() for word in group if word))
26
+
27
+
28
+ def _build_lookup(groups: Iterable[Sequence[str]]) -> Mapping[str, tuple[str, ...]]:
29
+ """Return a mapping from word -> homophone group."""
30
+
31
+ lookup: dict[str, tuple[str, ...]] = {}
32
+ for group in groups:
33
+ normalised = _normalise_group(group)
34
+ if len(normalised) < 2:
35
+ continue
36
+ for word in normalised:
37
+ lookup[word] = normalised
38
+ return lookup
39
+
40
+
41
+ _homophone_lookup = _build_lookup(_homophone_groups)
42
+ _ekkokin_rust = get_rust_operation("ekkokin_homophones")
43
+
44
+
45
+ def _normalise_weighting(weighting: str | None) -> str:
46
+ if weighting is None:
47
+ return _DEFAULT_WEIGHTING
48
+ lowered = weighting.lower()
49
+ if lowered not in _VALID_WEIGHTINGS:
50
+ options = ", ".join(sorted(_VALID_WEIGHTINGS))
51
+ raise ValueError(f"Unsupported weighting '{weighting}'. Expected one of: {options}")
52
+ return lowered
53
+
54
+
55
+ def _apply_casing(template: str, candidate: str) -> str:
56
+ """Return ``candidate`` adjusted to mirror the casing pattern of ``template``."""
57
+
58
+ if not candidate:
59
+ return candidate
60
+ if template.isupper():
61
+ return candidate.upper()
62
+ if template.islower():
63
+ return candidate.lower()
64
+ if template[:1].isupper() and template[1:].islower():
65
+ return candidate.capitalize()
66
+ return candidate
67
+
68
+
69
+ def _choose_alternative(
70
+ *,
71
+ group: Sequence[str],
72
+ source_word: str,
73
+ weighting: str,
74
+ rng: random.Random,
75
+ ) -> str | None:
76
+ """Return a replacement for ``source_word`` drawn from ``group``."""
77
+
78
+ del weighting # Reserved for future weighting strategies.
79
+ lowered = source_word.lower()
80
+ candidates = [candidate for candidate in group if candidate != lowered]
81
+ if not candidates:
82
+ return None
83
+ index = rng.randrange(len(candidates))
84
+ replacement = candidates[index]
85
+ return _apply_casing(source_word, replacement)
86
+
87
+
88
+ def _python_substitute_homophones(
89
+ text: str,
90
+ *,
91
+ rate: float,
92
+ weighting: str,
93
+ rng: random.Random,
94
+ ) -> str:
95
+ """Replace words in ``text`` with curated homophones."""
96
+
97
+ if not text:
98
+ return text
99
+
100
+ if math.isnan(rate):
101
+ return text
102
+
103
+ clamped_rate = max(0.0, min(1.0, rate))
104
+ if clamped_rate <= 0.0:
105
+ return text
106
+
107
+ tokens = split_preserving_whitespace(text)
108
+ word_tokens = collect_word_tokens(tokens)
109
+ if not word_tokens:
110
+ return text
111
+
112
+ mutated = False
113
+ for token in word_tokens:
114
+ replacement = _maybe_replace_token(token, clamped_rate, weighting, rng)
115
+ if replacement is None:
116
+ continue
117
+ tokens[token.index] = replacement
118
+ mutated = True
119
+
120
+ if not mutated:
121
+ return text
122
+ return "".join(tokens)
123
+
124
+
125
+ def _maybe_replace_token(
126
+ token: WordToken,
127
+ rate: float,
128
+ weighting: str,
129
+ rng: random.Random,
130
+ ) -> str | None:
131
+ lookup = _homophone_lookup.get(token.core.lower())
132
+ if lookup is None:
133
+ return None
134
+ if rng.random() >= rate:
135
+ return None
136
+ replacement_core = _choose_alternative(
137
+ group=lookup,
138
+ source_word=token.core,
139
+ weighting=weighting,
140
+ rng=rng,
141
+ )
142
+ if replacement_core is None:
143
+ return None
144
+ return f"{token.prefix}{replacement_core}{token.suffix}"
145
+
146
+
147
+ def substitute_homophones(
148
+ text: str,
149
+ rate: float | None = None,
150
+ seed: int | None = None,
151
+ rng: random.Random | None = None,
152
+ *,
153
+ weighting: str | None = None,
154
+ ) -> str:
155
+ """Replace words in ``text`` with curated homophones."""
156
+
157
+ effective_rate = _DEFAULT_RATE if rate is None else rate
158
+ normalized_weighting = _normalise_weighting(weighting)
159
+
160
+ active_rng = rng if rng is not None else random.Random(seed)
161
+
162
+ clamped_rate = 0.0 if math.isnan(effective_rate) else max(0.0, min(1.0, effective_rate))
163
+ if _ekkokin_rust is not None:
164
+ return cast(
165
+ str,
166
+ _ekkokin_rust(text, clamped_rate, normalized_weighting, active_rng),
167
+ )
168
+ return _python_substitute_homophones(
169
+ text,
170
+ rate=clamped_rate,
171
+ weighting=normalized_weighting,
172
+ rng=active_rng,
173
+ )
174
+
175
+
176
+ class Ekkokin(Glitchling):
177
+ """Glitchling that swaps words for curated homophones."""
178
+
179
+ def __init__(
180
+ self,
181
+ *,
182
+ rate: float | None = None,
183
+ seed: int | None = None,
184
+ weighting: str | None = None,
185
+ ) -> None:
186
+ effective_rate = _DEFAULT_RATE if rate is None else rate
187
+ normalized_weighting = _normalise_weighting(weighting)
188
+ super().__init__(
189
+ name="Ekkokin",
190
+ corruption_function=substitute_homophones,
191
+ scope=AttackWave.WORD,
192
+ order=AttackOrder.EARLY,
193
+ seed=seed,
194
+ pipeline_operation=_build_pipeline_descriptor,
195
+ rate=effective_rate,
196
+ weighting=normalized_weighting,
197
+ )
198
+
199
+ def set_param(self, key: str, value: Any) -> None:
200
+ """Normalise weighting updates before storing them on the glitchling."""
201
+ if key == "weighting":
202
+ value = _normalise_weighting(cast(str | None, value))
203
+ super().set_param(key, value)
204
+
205
+
206
+ def _build_pipeline_descriptor(glitch: Glitchling) -> dict[str, object] | None:
207
+ rate = glitch.kwargs.get("rate")
208
+ if rate is None:
209
+ return None
210
+ weighting = _normalise_weighting(cast(str | None, glitch.kwargs.get("weighting")))
211
+ return {
212
+ "type": "ekkokin",
213
+ "rate": float(rate),
214
+ "weighting": str(weighting),
215
+ }
216
+
217
+
218
+ ekkokin = Ekkokin()
219
+
220
+
221
+ __all__ = [
222
+ "Ekkokin",
223
+ "ekkokin",
224
+ "substitute_homophones",
225
+ "_python_substitute_homophones",
226
+ ]
@@ -0,0 +1,159 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ import re
5
+ from itertools import zip_longest
6
+
7
+ from .core import AttackOrder, AttackWave, Glitchling
8
+
9
+ _CANONICAL_COLOR_MAP: dict[str, str] = {
10
+ "red": "blue",
11
+ "blue": "red",
12
+ "green": "lime",
13
+ "lime": "green",
14
+ "yellow": "purple",
15
+ "purple": "yellow",
16
+ "orange": "cyan",
17
+ "cyan": "orange",
18
+ "magenta": "teal",
19
+ "teal": "magenta",
20
+ "black": "white",
21
+ "white": "black",
22
+ }
23
+
24
+ _VALID_MODES = {"literal", "drift"}
25
+
26
+ _COLOR_PATTERN = re.compile(
27
+ r"\b(?P<color>"
28
+ + "|".join(sorted(_CANONICAL_COLOR_MAP, key=len, reverse=True))
29
+ + r")(?P<suffix>[a-zA-Z]*)\b",
30
+ re.IGNORECASE,
31
+ )
32
+
33
+ _COLOR_ADJACENCY: dict[str, tuple[str, ...]] = {
34
+ "red": ("orange", "magenta", "purple"),
35
+ "blue": ("cyan", "teal", "purple"),
36
+ "green": ("teal", "cyan", "yellow"),
37
+ "lime": ("yellow", "white", "cyan"),
38
+ "yellow": ("orange", "lime", "white"),
39
+ "purple": ("magenta", "red", "blue"),
40
+ "orange": ("red", "yellow", "magenta"),
41
+ "cyan": ("blue", "green", "teal"),
42
+ "magenta": ("purple", "red", "blue"),
43
+ "teal": ("cyan", "green", "blue"),
44
+ "black": ("purple", "blue", "teal"),
45
+ "white": ("yellow", "lime", "cyan"),
46
+ }
47
+
48
+
49
+ def _apply_case(template: str, replacement: str) -> str:
50
+ if not template:
51
+ return replacement
52
+ if template.isupper():
53
+ return replacement.upper()
54
+ if template.islower():
55
+ return replacement.lower()
56
+ if template[0].isupper() and template[1:].islower():
57
+ return replacement.capitalize()
58
+
59
+ characters: list[str] = []
60
+ for repl_char, template_char in zip_longest(replacement, template, fillvalue=""):
61
+ if template_char.isupper():
62
+ characters.append(repl_char.upper())
63
+ elif template_char.islower():
64
+ characters.append(repl_char.lower())
65
+ else:
66
+ characters.append(repl_char)
67
+ return "".join(characters)
68
+
69
+
70
+ def _harmonize_suffix(original: str, replacement: str, suffix: str) -> str:
71
+ if not suffix:
72
+ return suffix
73
+
74
+ if (
75
+ original
76
+ and suffix
77
+ and original[-1].lower() == suffix[0].lower()
78
+ and replacement[-1].lower() != suffix[0].lower()
79
+ ):
80
+ return suffix[1:]
81
+ return suffix
82
+
83
+
84
+ def _normalize_mode(mode: str | None) -> str:
85
+ normalized = "literal" if mode is None else mode.lower()
86
+ if normalized not in _VALID_MODES:
87
+ valid = ", ".join(sorted(_VALID_MODES))
88
+ raise ValueError(f"Unsupported Spectroll mode '{mode}'. Expected one of: {valid}")
89
+ return normalized
90
+
91
+
92
+ def swap_colors(
93
+ text: str,
94
+ *,
95
+ seed: int | None = None,
96
+ mode: str = "literal",
97
+ rng: random.Random | None = None,
98
+ ) -> str:
99
+ """Swap canonical colour words for their partners.
100
+
101
+ Examples:
102
+ >>> swap_colors("red green blue")
103
+ 'blue lime red'
104
+ >>> swap_colors("red green blue", mode="drift", seed=42)
105
+ 'purple teal cyan'
106
+ """
107
+
108
+ normalized_mode = _normalize_mode(mode)
109
+ active_rng = rng if rng is not None else random.Random(seed)
110
+
111
+ def replace(match: re.Match[str]) -> str:
112
+ base = match.group("color")
113
+ suffix = match.group("suffix") or ""
114
+ canonical = base.lower()
115
+
116
+ replacement_base: str | None
117
+ if normalized_mode == "literal":
118
+ replacement_base = _CANONICAL_COLOR_MAP.get(canonical)
119
+ else:
120
+ palette = _COLOR_ADJACENCY.get(canonical)
121
+ if palette:
122
+ replacement_base = active_rng.choice(palette)
123
+ else:
124
+ replacement_base = _CANONICAL_COLOR_MAP.get(canonical)
125
+
126
+ if not replacement_base:
127
+ return match.group(0)
128
+
129
+ suffix_fragment = _harmonize_suffix(base, replacement_base, suffix)
130
+ adjusted = _apply_case(base, replacement_base)
131
+ return f"{adjusted}{suffix_fragment}"
132
+
133
+ return _COLOR_PATTERN.sub(replace, text)
134
+
135
+
136
+ class Spectroll(Glitchling):
137
+ """Glitchling that remaps colour terms to alternate hues."""
138
+
139
+ def __init__(
140
+ self,
141
+ *,
142
+ mode: str = "literal",
143
+ seed: int | None = None,
144
+ ) -> None:
145
+ normalized_mode = _normalize_mode(mode)
146
+ super().__init__(
147
+ name="Spectroll",
148
+ corruption_function=swap_colors,
149
+ scope=AttackWave.WORD,
150
+ order=AttackOrder.NORMAL,
151
+ seed=seed,
152
+ mode=normalized_mode,
153
+ )
154
+
155
+
156
+ spectroll = Spectroll()
157
+
158
+
159
+ __all__ = ["Spectroll", "spectroll", "swap_colors"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glitchlings
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Monsters for your language games.
5
5
  Author: osoleve
6
6
  License: Apache License
@@ -393,11 +393,13 @@ glitchlings --list
393
393
  Apostrofae — scope: Character, order: normal
394
394
  Hokey — scope: Character, order: first
395
395
  Mim1c — scope: Character, order: last
396
+ Ekkokin — scope: Word, order: early
396
397
  Jargoyle — scope: Word, order: normal
397
398
  Adjax — scope: Word, order: normal
398
399
  Reduple — scope: Word, order: normal
399
400
  Rushmore — scope: Word, order: normal
400
401
  Redactyl — scope: Word, order: normal
402
+ Spectroll — scope: Word, order: normal
401
403
  Scannequin — scope: Character, order: late
402
404
  Zeedub — scope: Character, order: last
403
405
  ```
@@ -552,6 +554,18 @@ _Watch your step around here._
552
554
  > - `characters (Sequence[str])`: Optional override for the pool of zero-width strings to inject (default: curated invisibles such as U+200B, U+200C, U+200D, U+FEFF, U+2060).
553
555
  > - `seed (int)`: The random seed for reproducibility (default: 151).
554
556
 
557
+ ### Ekkokin
558
+
559
+ _Did you hear what I heard?_
560
+
561
+ > _**Echo Chamber.**_ Ekkokin swaps words with curated homophones so the text still sounds right while the spelling drifts. Groups are normalised to prevent duplicates and casing is preserved when substitutions fire.
562
+ >
563
+ > Args
564
+ >
565
+ > - `rate (float)`: Maximum proportion of eligible words to replace with homophones (default: 0.02, 2%).
566
+ > - `weighting (str)`: Sampling strategy applied within each homophone set (default: "flat").
567
+ > - `seed (int)`: The random seed for reproducibility (default: 151).
568
+
555
569
  ### Jargoyle
556
570
 
557
571
  _Uh oh. The worst person you know just bought a thesaurus._
@@ -619,7 +633,6 @@ _Oops, that was my black highlighter._
619
633
 
620
634
  ### _Containment procedures pending_
621
635
 
622
- - `ekkokin` substitutes words with homophones (phonetic equivalents).
623
636
  - `nylingual` backtranslates portions of text.
624
637
  - `glothopper` introduces code-switching effects, blending languages or dialects.
625
638
  - `palimpsest` rewrites, but leaves accidental traces of the past.
@@ -1,18 +1,23 @@
1
- glitchlings/__init__.py,sha256=A6m-sj1Gnq5DXdaD7IRsPnMvXW9UjZh3fQGcvps22uw,1230
1
+ glitchlings/__init__.py,sha256=pDqhE33ZkTz8xzvVjc1FsSOYRBPdgaSL1es9e0v31Uc,1947
2
2
  glitchlings/__main__.py,sha256=f-P4jiVBd7ZpS6QxRpa_6SJgOG03UhZhcWasMDRWLs8,120
3
- glitchlings/_zoo_rust.cpython-311-darwin.so,sha256=k5atUaTej8cZo1fZNbJXuuW9IweCWCgtaA_8P6eyZc4,2714336
4
- glitchlings/compat.py,sha256=lswR7J3kXER5hPXeyfkO-7USJvUhdtTi1ovJgEpspKc,8892
3
+ glitchlings/_zoo_rust.cpython-311-darwin.so,sha256=t92ivOWpw2fkii6-TrrcidP1JJXUVfMPcBvnA7qP1fc,2770960
4
+ glitchlings/compat.py,sha256=_iUEErikfkgPOik1zymPCU4-Pbyt-6h5fWoF7viohXs,12065
5
5
  glitchlings/config.py,sha256=xqTc2YJGMYJHbnh1nxQBdCKhQ6sqcjWJViArjarsZa4,12303
6
6
  glitchlings/config.toml,sha256=04-Y_JCdQU68SRmwk2qZqrH_bbX4jEH9uh7URtxdIHA,99
7
7
  glitchlings/main.py,sha256=uw8VbDgxov1m-wYHPDl2dP5ItpLB4ZHpb0ChJXzcL0o,10623
8
+ glitchlings/spectroll.py,sha256=m5FRDYa-MjPfh29Zpk9bDmL6xPKOXS-kysFFz9WOTkE,150
9
+ glitchlings/assets/apostrofae_pairs.json,sha256=bfjSEaMTI_axGNJ93nI431KXU0IVp7ayO42gGcMgL6U,521
10
+ glitchlings/assets/ekkokin_homophones.json,sha256=TAvwBojTpWc-jk6jQjVDGMbHLfsjZLBPe7f78mkNLF0,17059
11
+ glitchlings/assets/hokey_assets.json,sha256=9drpOv_PHHxs7jZOcgMr9G-Nswx_UuMzC4yQ0O8mIZ0,2890
12
+ glitchlings/assets/ocr_confusions.tsv,sha256=KhtR7vJDTITpfTSGa-I7RHr6CK7LkGi2KjdhEWipI6o,183
8
13
  glitchlings/dev/__init__.py,sha256=v-qk9iE6b7VEg-4bpKH2PAifG-rkrCRgrS0C4VPR7ow,142
9
- glitchlings/dev/sync_assets.py,sha256=BDzfzRMhcvPC2N-bIjnsB8KUA5I7YrfSY-EnXQqh3WY,4677
14
+ glitchlings/dev/sync_assets.py,sha256=xtco1x9tZFfVLajronYs8Iv0_43DU7ZTrD-sRR74b88,3675
10
15
  glitchlings/dlc/__init__.py,sha256=qlY4nuagy4AAWuPMwmuhwK2m36ktp-qkeiIxC7OXg34,305
11
16
  glitchlings/dlc/_shared.py,sha256=moQwnJdHMo-dx5uK0zM8XdPy5cs-OlAzYKVWfUP0RSQ,4407
12
17
  glitchlings/dlc/huggingface.py,sha256=BWceVUm28Yd8b7Tf_lmnPGgSVN1hZEmseRjf17nAPJw,2576
13
18
  glitchlings/dlc/prime.py,sha256=zhm0oTVKNDa1ByxZTP42rmMVaJDyx77w2g6NHy4jndc,8607
14
19
  glitchlings/dlc/pytorch.py,sha256=Laqz5o0UZXE1X9P-Qxb6KE0D5lcBKAoBbKsn-fnd6c0,6233
15
- glitchlings/dlc/pytorch_lightning.py,sha256=rhDwRgOGVzaEet27QG8GigKh6hK5ZdTBOxGE2G0MPxw,8005
20
+ glitchlings/dlc/pytorch_lightning.py,sha256=BcxH6WHCl2SpDje7WuTN9V04KKfTqhaXFb2UzajNr4Y,8478
16
21
  glitchlings/lexicon/__init__.py,sha256=ooEPcAJhCI2Nw5z8OsQ0EtVpKBfiTrU0-AQJq8Zn2nQ,6007
17
22
  glitchlings/lexicon/_cache.py,sha256=oWdQtiU3csUAs-fYJRHuS_314j8JJ-T8AL73-GxVXzA,4072
18
23
  glitchlings/lexicon/metrics.py,sha256=VBFfFpxjiEwZtK-jS55H8xP7MTC_0OjY8lQ5zSQ9aTY,4572
@@ -24,7 +29,7 @@ glitchlings/util/adapters.py,sha256=psxQFYSFmh1u7NuqtIrKwQP5FOhOrZoxZzc7X7DDi9U,
24
29
  glitchlings/util/hokey_generator.py,sha256=NCbOGw55SG720VYnuwEdFAfdOvYlmjsZ0hAr5Y0Ja0Y,4483
25
30
  glitchlings/util/stretch_locator.py,sha256=INTMz7PXe-0HoDaMnQIQxJ266nABMXBYD67oJM8ur8g,4194
26
31
  glitchlings/util/stretchability.py,sha256=Rs-OHlfhRxGFlZeEQgEN66Z_CYTcWJrGypu7X5Yc9i4,12667
27
- glitchlings/zoo/__init__.py,sha256=xCVVAHLJ4lxl1JQUzff9fr84pJbB04fyXBjuX1m3YSw,5339
32
+ glitchlings/zoo/__init__.py,sha256=MU2GLhBQ64I_T8rcozVFK6LWNzF4pAbdi5BIK92Uqrw,5579
28
33
  glitchlings/zoo/_ocr_confusions.py,sha256=0Y2GvfXCUIrXggL-tsFMQqzEFYVp6p5r7F-4dGrXiT4,1139
29
34
  glitchlings/zoo/_rust_extensions.py,sha256=Bsd0kiPB1rUn5x3k7ykydFuk2YSvXS9CQGPRlE5XzXY,4211
30
35
  glitchlings/zoo/_sampling.py,sha256=KrWyUSsYXghlvktS5hQBO0bPqywEEyA49A2qDWInB7Q,1586
@@ -32,6 +37,7 @@ glitchlings/zoo/_text_utils.py,sha256=NG9Iru9k3oSCxwYFAbvY4iG5NzHRYOIEtUXtvhKe4w
32
37
  glitchlings/zoo/adjax.py,sha256=D3gadataEL7QzxrTOElwNdXHKrJmb2EB9qwNLbTVUK0,3136
33
38
  glitchlings/zoo/apostrofae.py,sha256=zfQrq_ds0N6CTdqz3zsDO4KfiVLb5gx-0S4_VU19vMM,3674
34
39
  glitchlings/zoo/core.py,sha256=dRzUTmhOswDV0hWcaD-Sx7rZdPlrszn7C_1G2xd4ECk,20675
40
+ glitchlings/zoo/ekkokin.py,sha256=jLMndJCIeoeb6fnFbVEf3FAav5FpWM6nkAXsqys56Ms,6442
35
41
  glitchlings/zoo/hokey.py,sha256=71z1JGzKGb_N8Wo7LVuS7qAqH2T0Y0h2LemIw66eprs,5298
36
42
  glitchlings/zoo/jargoyle.py,sha256=dkJpcDwBqyNJpsJDk3k1hAkLpLXKu0fH0ZJyhhllHWI,11260
37
43
  glitchlings/zoo/mim1c.py,sha256=BQ0kR-ob5TK5BrysipTUx1Yox6G5wlYi-EBH2V93xmI,3011
@@ -39,15 +45,13 @@ glitchlings/zoo/redactyl.py,sha256=lmFb3dobXpsQfwmgT-9yhoKIvrJyilu_yhj4mef0cm0,5
39
45
  glitchlings/zoo/reduple.py,sha256=uawpaq5cDBdGGbw0CwdITYp56nI6fazCTr9QIrJb0ag,3777
40
46
  glitchlings/zoo/rushmore.py,sha256=jsffZAfhpDTIUtG-jjBzM7bZUCnz2truR0fNNkpVR3M,3759
41
47
  glitchlings/zoo/scannequin.py,sha256=oaBiqU57dLruCPHo-W2ojno3lolefSAcvTjXpFwLarM,4406
48
+ glitchlings/zoo/spectroll.py,sha256=WrXkZo8saXTPL_2pBCi5K-7UmXXhTR42em5IvjrJPCQ,4476
42
49
  glitchlings/zoo/typogre.py,sha256=NqA9b3HmFl8hR6bVLe85Tjv6uEYoDoSkb0i9gnd8t2Y,6188
43
50
  glitchlings/zoo/zeedub.py,sha256=Smegw7zUBAiMapKwZUqP0MqBKSCN5RNXvpbpU_ypnfI,4618
44
- glitchlings/zoo/assets/__init__.py,sha256=kETGGz0_V1OLpqB4rEfD5SU3XX0Ea9nSZ9isNL6SXWU,1637
45
- glitchlings/zoo/assets/apostrofae_pairs.json,sha256=bfjSEaMTI_axGNJ93nI431KXU0IVp7ayO42gGcMgL6U,521
46
- glitchlings/zoo/assets/hokey_assets.json,sha256=9drpOv_PHHxs7jZOcgMr9G-Nswx_UuMzC4yQ0O8mIZ0,2890
47
- glitchlings/zoo/assets/ocr_confusions.tsv,sha256=KhtR7vJDTITpfTSGa-I7RHr6CK7LkGi2KjdhEWipI6o,183
48
- glitchlings-0.5.0.dist-info/licenses/LICENSE,sha256=YCvGip-LoaRyu6h0nPo71q6eHEkzUpsE11psDJOIRkw,11337
49
- glitchlings-0.5.0.dist-info/METADATA,sha256=mWzN0o6h8e0hFfT6ZhiDy9U1yaJNCCXWXNYLO3i6vLk,33363
50
- glitchlings-0.5.0.dist-info/WHEEL,sha256=Tgp8Vc-mmQm0KX-V22BSUoymoX1p0w13bZbX85y8hSs,114
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,,
51
+ glitchlings/zoo/assets/__init__.py,sha256=0OLRgQvfwYeRVMQTUOAFDiOnRZPgVFnmaZj2KE-AL_I,2729
52
+ glitchlings-0.5.1.dist-info/licenses/LICENSE,sha256=YCvGip-LoaRyu6h0nPo71q6eHEkzUpsE11psDJOIRkw,11337
53
+ glitchlings-0.5.1.dist-info/METADATA,sha256=wGG7OKTnMBFU4SBqIxOr8csj6nZqxY6H5GPL_2C76xk,33917
54
+ glitchlings-0.5.1.dist-info/WHEEL,sha256=Tgp8Vc-mmQm0KX-V22BSUoymoX1p0w13bZbX85y8hSs,114
55
+ glitchlings-0.5.1.dist-info/entry_points.txt,sha256=kGOwuAsjFDLtztLisaXtOouq9wFVMOJg5FzaAkg-Hto,54
56
+ glitchlings-0.5.1.dist-info/top_level.txt,sha256=VHFNBrLjtDwPCYXbGKi6o17Eueedi81eNbR3hBOoST0,12
57
+ glitchlings-0.5.1.dist-info/RECORD,,
File without changes
File without changes