Skip to content

Commit d9dec9e

Browse files
authored
Merge pull request #1327 from bact/protect-literal-eval
Defensive file loading
2 parents cda4c3a + 7e4b3fe commit d9dec9e

2 files changed

Lines changed: 391 additions & 27 deletions

File tree

pythainlp/corpus/common.py

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from __future__ import annotations
88

99
import ast
10+
import warnings
1011
from typing import TYPE_CHECKING
1112

1213
if TYPE_CHECKING:
@@ -109,14 +110,31 @@ def provinces(
109110
prov_details = []
110111

111112
for line in get_corpus_as_is(_THAI_THAILAND_PROVINCES_FILENAME):
112-
p = line.split(",")
113-
114-
prov = {}
115-
prov["name_th"] = p[0]
116-
prov["abbr_th"] = p[1]
117-
prov["name_en"] = p[2]
118-
prov["abbr_en"] = p[3]
119-
113+
# Skip completely empty or whitespace-only lines without warning.
114+
if not line.strip():
115+
continue
116+
parts = line.split(",")
117+
try:
118+
prov = {
119+
"name_th": parts[0],
120+
"abbr_th": parts[1],
121+
"name_en": parts[2],
122+
"abbr_en": parts[3],
123+
}
124+
except IndexError:
125+
warnings.warn(
126+
f"Skipping malformed province entry (too few fields): {line!r}",
127+
UserWarning,
128+
stacklevel=2,
129+
)
130+
continue
131+
if not all(v.strip() for v in prov.values()):
132+
warnings.warn(
133+
f"Skipping province entry with blank or empty field(s): {line!r}",
134+
UserWarning,
135+
stacklevel=2,
136+
)
137+
continue
120138
provs.add(prov["name_th"])
121139
prov_details.append(prov)
122140

@@ -294,12 +312,23 @@ def thai_dict() -> dict[str, list[str]]:
294312
return _THAI_DICT
295313
path = str(path)
296314

297-
_THAI_DICT = {"word": [], "meaning": []}
315+
words: list[str] = []
316+
meanings: list[str] = []
298317
with open(path, newline="\n", encoding="utf-8") as csvfile:
299318
reader = csv.DictReader(csvfile, delimiter=",")
300319
for row in reader:
301-
_THAI_DICT["word"].append(row["word"])
302-
_THAI_DICT["meaning"].append(row["meaning"])
320+
word = row.get("word")
321+
meaning = row.get("meaning")
322+
if not word or not word.strip() or not meaning or not meaning.strip():
323+
warnings.warn(
324+
f"Skipping thai_dict entry with missing or empty field(s): {dict(row)!r}",
325+
UserWarning,
326+
stacklevel=2,
327+
)
328+
continue
329+
words.append(word)
330+
meanings.append(meaning)
331+
_THAI_DICT = {"word": words, "meaning": meanings}
303332

304333
return _THAI_DICT
305334

@@ -317,17 +346,35 @@ def thai_wsd_dict() -> dict[str, Union[list[str], list[list[str]]]]:
317346
return _THAI_WSD_DICT
318347

319348
thai_wsd = thai_dict()
320-
_THAI_WSD_DICT = {"word": [], "meaning": []}
321-
for i, j in zip(thai_wsd["word"], thai_wsd["meaning"]):
322-
all_value = list(ast.literal_eval(j).values())
323-
use = []
324-
for k in all_value:
325-
use.extend(k)
326-
use = list(set(use))
327-
if len(use) > 1:
328-
_THAI_WSD_DICT["word"].append(i) # type: ignore[arg-type]
329-
_THAI_WSD_DICT["meaning"].append(use) # type: ignore[arg-type]
330-
349+
words: list[str] = []
350+
meanings: list[list[str]] = []
351+
for word, meaning in zip(thai_wsd["word"], thai_wsd["meaning"]):
352+
try:
353+
parsed = ast.literal_eval(meaning)
354+
except (SyntaxError, TypeError, ValueError):
355+
warnings.warn(
356+
f"Skipping thai_wsd_dict entry for word {word!r}: "
357+
f"meaning could not be parsed: {meaning!r}",
358+
UserWarning,
359+
stacklevel=2,
360+
)
361+
continue
362+
if not isinstance(parsed, dict):
363+
warnings.warn(
364+
f"Skipping thai_wsd_dict entry for word {word!r}: "
365+
f"expected dict after parsing, got {type(parsed).__name__!r}",
366+
UserWarning,
367+
stacklevel=2,
368+
)
369+
continue
370+
senses: list[str] = []
371+
for sense_list in parsed.values():
372+
senses.extend(sense_list)
373+
senses = list(set(senses))
374+
if len(senses) > 1:
375+
words.append(word)
376+
meanings.append(senses)
377+
_THAI_WSD_DICT = {"word": words, "meaning": meanings}
331378
return _THAI_WSD_DICT
332379

333380

@@ -350,14 +397,26 @@ def thai_synonyms() -> dict[str, Union[list[str], list[list[str]]]]:
350397
return _THAI_SYNONYMS
351398
path = str(path)
352399

353-
_THAI_SYNONYMS = {"word": [], "pos": [], "synonym": []}
400+
words: list[str] = []
401+
pos_tags: list[str] = []
402+
synonym_groups: list[list[str]] = []
354403
with open(path, newline="\n", encoding="utf-8") as csvfile:
355404
reader = csv.DictReader(csvfile, delimiter=",")
356405
for row in reader:
357-
_THAI_SYNONYMS["word"].append(row["word"]) # type: ignore[arg-type]
358-
_THAI_SYNONYMS["pos"].append(row["pos"]) # type: ignore[arg-type]
359-
_THAI_SYNONYMS["synonym"].append(row["synonym"].split("|")) # type: ignore[arg-type]
360-
406+
word = row.get("word")
407+
pos = row.get("pos")
408+
synonym = row.get("synonym")
409+
if not word or not word.strip() or not pos or not pos.strip() or not synonym or not synonym.strip():
410+
warnings.warn(
411+
f"Skipping thai_synonyms entry with missing or empty field(s): {dict(row)!r}",
412+
UserWarning,
413+
stacklevel=2,
414+
)
415+
continue
416+
words.append(word)
417+
pos_tags.append(pos)
418+
synonym_groups.append(synonym.split("|"))
419+
_THAI_SYNONYMS = {"word": words, "pos": pos_tags, "synonym": synonym_groups}
361420
return _THAI_SYNONYMS
362421

363422

0 commit comments

Comments
 (0)