basa 0.1.0__py3-none-any.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.
basa/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ """
2
+ BASA - Modern NLP for Indonesian and Regional Languages
3
+ =======================================================
4
+
5
+ Top-level package. Re-exports the public API.
6
+
7
+ Quick start:
8
+ >>> from basa import normalize, quick
9
+ >>> normalize("GW GKKKK NGERTIII BNGTTTT!!!!!")
10
+ 'saya tidak mengerti banget!'
11
+ >>> quick("gw gk ngerti bngt sihhhh!!!")
12
+ 'saya tidak mengerti banget sih!'
13
+ """
14
+
15
+ from importlib.metadata import PackageNotFoundError, version
16
+
17
+ try:
18
+ __version__ = version("basa")
19
+ except PackageNotFoundError:
20
+ __version__ = "unknown"
21
+
22
+ from .core.normalize import normalize
23
+ from .core.quick import quick
24
+ from .core.slang import slang
25
+ from .core.typo import typo
26
+
27
+ __all__ = [
28
+ "__version__",
29
+ "normalize",
30
+ "quick",
31
+ "typo",
32
+ "slang",
33
+ ]
File without changes
basa/augment/noise.py ADDED
File without changes
File without changes
File without changes
basa/core/__init__.py ADDED
File without changes
basa/core/normalize.py ADDED
@@ -0,0 +1,240 @@
1
+ """
2
+ BASA - Top-level Normalization API
3
+ ====================================
4
+ Provides the simple 1-line normalization interface for the library.
5
+
6
+ Pipeline (in order):
7
+ 1. Lowercase – optional, default True
8
+ 2. Slang normalization – replaces slang + reduces repeated chars
9
+ 3. Typo correction – Levenshtein-based, strictly opt-in
10
+ 4. Punctuation reduction – collapses repeated punctuation (!!!! → !)
11
+ 5. Whitespace cleanup – trims and collapses multiple spaces
12
+
13
+ Design philosophy:
14
+ - Conservative by default: only safe, lossless transforms are enabled.
15
+ - Destructive features (typo correction) are strictly opt-in.
16
+ - ``_normalize_single`` handles one string; ``normalize`` handles both
17
+ str and List[str] inputs so adding new parameters never breaks the
18
+ batch path.
19
+
20
+ Usage:
21
+ >>> from basa import normalize
22
+ >>> normalize("GW GKKKK NGERTIII BNGTTTT!!!!!")
23
+ 'saya tidak mengerti banget!'
24
+
25
+ >>> normalize("Jokowi pergi ke Jakarta", lowercase=False)
26
+ 'Jokowi pergi ke Jakarta'
27
+
28
+ >>> normalize(["gw mkan", "dia mnum"], apply_slang=True)
29
+ ['saya makan', 'dia minum']
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import re
35
+
36
+ from .slang import slang as slang_engine
37
+ from .typo import typo as typo_engine
38
+
39
+ # ─────────────────────────────────────────────────────────────────────────────
40
+ # SLANG OUTPUT WHITELIST
41
+ # ─────────────────────────────────────────────────────────────────────────────
42
+ # All normalised values produced by the slang engine (e.g. "mahal", "terima
43
+ # kasih", "saudara") must never be treated as typos. We pre-compute the set
44
+ # once at import time and pass it to the typo engine as a protected vocab so
45
+ # that words which are already correct are not mis-corrected to whatever small
46
+ # vocabulary the caller happens to have loaded.
47
+ _SLANG_OUTPUT_WORDS: frozenset = frozenset(
48
+ token for value in slang_engine.mapping.values() for token in value.lower().split()
49
+ )
50
+ typo_engine.set_protected(_SLANG_OUTPUT_WORDS)
51
+
52
+
53
+ # ─────────────────────────────────────────────────────────────────────────────
54
+ # INTERNAL HELPERS
55
+ # ─────────────────────────────────────────────────────────────────────────────
56
+
57
+
58
+ def _reduce_punctuation(text: str) -> str:
59
+ """
60
+ Collapse runs of repeated punctuation characters to a single character.
61
+
62
+ Only targets common sentence-ending / emphasis punctuation so that
63
+ legitimate patterns (e.g. ellipsis "...") are also collapsed cleanly.
64
+ This is intentionally narrow to avoid touching hyphens, slashes, etc.
65
+
66
+ Affected characters: . , ! ? ~ * - _
67
+
68
+ Examples:
69
+ >>> _reduce_punctuation("bagus banget!!!!!")
70
+ 'bagus banget!'
71
+ >>> _reduce_punctuation("hmmm.....serius???")
72
+ 'hmmm.serius?'
73
+ >>> _reduce_punctuation("seru~~~~ banget~~")
74
+ 'seru~ banget~'
75
+ """
76
+ # Match 2+ consecutive occurrences of the same punctuation character
77
+ return re.sub(r"([.!?,~*\-_])\1+", r"\1", text)
78
+
79
+
80
+ def _normalize_single(
81
+ text: str,
82
+ apply_slang: bool,
83
+ apply_typo: bool,
84
+ lowercase: bool,
85
+ normalize_punctuation: bool,
86
+ normalize_whitespace: bool,
87
+ ) -> str:
88
+ """
89
+ Apply the full normalization pipeline to a single string.
90
+
91
+ This is the internal workhorse. ``normalize()`` delegates here for
92
+ every input string so that adding new parameters only requires
93
+ updating the signature in one place.
94
+
95
+ Args:
96
+ text: Input string (assumed to be a non-empty str).
97
+ apply_slang: If True, run slang normalization.
98
+ apply_typo: If True, run typo correction (opt-in).
99
+ lowercase: If True, convert to lowercase first.
100
+ normalize_punctuation: If True, collapse repeated punctuation.
101
+ normalize_whitespace: If True, trim and collapse multiple spaces.
102
+
103
+ Returns:
104
+ Normalized string.
105
+ """
106
+ # ── Stage 1: Lowercase ───────────────────────────────────────────────────
107
+ # Must come first so slang matching is case-insensitive.
108
+ if lowercase:
109
+ text = text.lower()
110
+
111
+ # ── Stage 2: Slang Normalization ─────────────────────────────────────────
112
+ # Handles repeated-char reduction AND slang dict lookup.
113
+ # e.g. "GKKKK" → (lower) "gkkkk" → (char reduce) "gk" → "tidak"
114
+ # We defer whitespace normalization to Stage 5.
115
+ if apply_slang:
116
+ text = slang_engine.normalize(
117
+ text, normalize_whitespace=False, lowercase=lowercase
118
+ )
119
+
120
+ # ── Stage 3: Typo Correction (Strictly Opt-in) ───────────────────────────
121
+ # Safeguard: skip if vocab is empty to prevent no-op full-corpus scans.
122
+ if apply_typo and typo_engine.vocab:
123
+ text = typo_engine.correct_text(text)
124
+
125
+ # ── Stage 4: Punctuation Reduction ───────────────────────────────────────
126
+ # e.g. "!!!!!" → "!", "?????" → "?", "....." → "."
127
+ if normalize_punctuation:
128
+ text = _reduce_punctuation(text)
129
+
130
+ # ── Stage 5: Whitespace Cleanup ──────────────────────────────────────────
131
+ if normalize_whitespace:
132
+ text = re.sub(r"\s+", " ", text).strip()
133
+
134
+ return text
135
+
136
+
137
+ # ─────────────────────────────────────────────────────────────────────────────
138
+ # PUBLIC API
139
+ # ─────────────────────────────────────────────────────────────────────────────
140
+
141
+
142
+ def normalize(
143
+ text: str | list[str],
144
+ apply_slang: bool = True,
145
+ apply_typo: bool = False,
146
+ lowercase: bool = True,
147
+ normalize_punctuation: bool = True,
148
+ normalize_whitespace: bool = True,
149
+ ) -> str | list[str]:
150
+ """
151
+ Normalize informal Indonesian text with a single line of code.
152
+
153
+ Applies a configurable pipeline of normalization stages to the input.
154
+ Accepts a single string or a list of strings.
155
+
156
+ Design Notes:
157
+ - ``lowercase=True`` by default so slang matching works reliably.
158
+ Set ``lowercase=False`` when case matters (e.g. for NER tasks).
159
+ - ``apply_typo=False`` by default. Typo correction is opt-in because
160
+ it requires a populated vocabulary and can corrupt domain-specific
161
+ terms (e.g. "xgboost", "lightgbm", proper nouns). Always load
162
+ your vocabulary via ``basa.typo.add_to_vocab()`` before enabling.
163
+
164
+ Args:
165
+ text: Input string or list of strings to normalize.
166
+ apply_slang: If True (default), apply slang dictionary
167
+ lookup and repeated-character reduction.
168
+ apply_typo: If True, apply Levenshtein-based typo
169
+ correction. Default False. Silently skipped
170
+ if the typo engine's vocabulary is empty.
171
+ lowercase: If True (default), convert text to lowercase
172
+ before processing. Set False to preserve
173
+ casing (e.g. for NER pipelines).
174
+ normalize_punctuation: If True (default), collapse repeated
175
+ punctuation marks (e.g. "!!!" → "!").
176
+ normalize_whitespace: If True (default), trim leading/trailing
177
+ whitespace and collapse internal runs of
178
+ spaces to a single space.
179
+
180
+ Returns:
181
+ Normalized string if input is str, or list of the same length if
182
+ input is a list. Non-string elements (``None``, ``""``, integers,
183
+ etc.) inside a list are passed through unchanged so that the output
184
+ index always matches the input index.
185
+
186
+ Examples:
187
+ >>> from basa import normalize
188
+
189
+ >>> normalize("GW GKKKK NGERTIII BNGTTTT!!!!!")
190
+ 'saya tidak mengerti banget!'
191
+
192
+ >>> normalize("gw gk ngerti bngt sihhhh!!!")
193
+ 'saya tidak mengerti banget sih!'
194
+
195
+ >>> normalize("Jokowi pergi ke Jakarta", lowercase=False)
196
+ 'Jokowi pergi ke Jakarta'
197
+
198
+ >>> normalize(["gw mkan", "dia mnum"])
199
+ ['saya makan', 'dia minum']
200
+
201
+ >>> normalize(["halo", "", None])
202
+ ['halo', '', None]
203
+
204
+ >>> normalize("harga naik terus????", normalize_punctuation=True)
205
+ 'harga naik terus?'
206
+ """
207
+ # ── Batch path ────────────────────────────────────────────────────────────
208
+ # Delegates each element to _normalize_single — adding new parameters
209
+ # here only requires updating _normalize_single's signature, not this call.
210
+ #
211
+ # Guard note: the isinstance check is a *ternary guard*, not a filter, so
212
+ # non-string elements (None, "", integers) are passed through unchanged and
213
+ # the output list is always the same length as the input list.
214
+ if isinstance(text, list):
215
+ return [
216
+ _normalize_single(
217
+ t,
218
+ apply_slang=apply_slang,
219
+ apply_typo=apply_typo,
220
+ lowercase=lowercase,
221
+ normalize_punctuation=normalize_punctuation,
222
+ normalize_whitespace=normalize_whitespace,
223
+ )
224
+ if isinstance(t, str) and t
225
+ else t
226
+ for t in text
227
+ ]
228
+
229
+ # ── Guard: invalid / empty input ─────────────────────────────────────────
230
+ if not text or not isinstance(text, str):
231
+ return text
232
+
233
+ return _normalize_single(
234
+ text,
235
+ apply_slang=apply_slang,
236
+ apply_typo=apply_typo,
237
+ lowercase=lowercase,
238
+ normalize_punctuation=normalize_punctuation,
239
+ normalize_whitespace=normalize_whitespace,
240
+ )
basa/core/quick.py ADDED
@@ -0,0 +1,48 @@
1
+ """
2
+ BASA - Quick API
3
+ =================
4
+ Zero-config shorthand for the most common normalize() call.
5
+
6
+ ``quick()`` always uses all default settings:
7
+ - lowercase=True
8
+ - apply_slang=True
9
+ - apply_typo=False
10
+ - normalize_punctuation=True
11
+ - normalize_whitespace=True
12
+
13
+ This is intentionally a thin alias — no new logic lives here.
14
+ For fine-grained control use ``normalize()`` directly.
15
+
16
+ Usage:
17
+ >>> from basa import quick
18
+ >>> quick("GW GKKKK NGERTIII BNGTTTT!!!!!")
19
+ 'saya tidak mengerti banget!'
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from .normalize import normalize
25
+
26
+
27
+ def quick(text: str | list[str]) -> str | list[str]:
28
+ """
29
+ Normalize informal Indonesian text with zero configuration.
30
+
31
+ A convenience alias for ``normalize()`` with all defaults applied.
32
+ Accepts a single string or a list of strings.
33
+
34
+ Args:
35
+ text: Input string or list of strings.
36
+
37
+ Returns:
38
+ Normalized string or list of normalized strings.
39
+
40
+ Examples:
41
+ >>> from basa import quick
42
+ >>> quick("gw gk ngerti bngt sihhhh!!!")
43
+ 'saya tidak mengerti banget sih!'
44
+
45
+ >>> quick(["gw mkan", "dia mnum"])
46
+ ['saya makan', 'dia minum']
47
+ """
48
+ return normalize(text)