Skip to content

fix: literal_types 의 네 파서가 전각 숫자를 받아 같은 값이 두 표기로 갈린다 #331

Description

@SeoyunL

요약

factlog/literal_types.py 의 네 파서가 모두 \d 로 숫자를 인식한다. 파이썬의 \d
유니코드 Nd 범주 전체에 매치하므로 전각 숫자가 조용히 파싱된다. 스칼라는 ASCII
표기와 같은 값이 나오는데 KB 가 저장하는 객체 문자열은 다르다. 그래서 같은 값이
문맥에 따라 하나로도, 둘로도 취급된다.

재현 (upstream/main c6d359d, Python 3.12 로 실측)

from factlog.literal_types import (
    parse_date, parse_number_scaled, parse_ordinal, parse_amount, DEFAULT_AMOUNT_UNITS,
)

parse_date('date(2020,1)')        # 20200101
parse_date('date(2020,1)')      # 20200101   <- 전각인데 통과, 같은 스칼라
parse_number_scaled('number(123)')            # 123000
parse_ordinal('ordinal(3)')                    # 3
parse_amount('100억', DEFAULT_AMOUNT_UNITS)   # 10000000000

근거

factlog/literal_types.py:44-78 의 모든 수치 그룹이 \d 로 적혀 있다.

_DATE_RE           = re.compile(r"^(\d{4})[.\-/](\d{1,2})(?:[.\-/](\d{1,2}))?$")
_DATE_COMPOUND_RE  = re.compile(r"^date\(\s*(\d{4})(?:\s*,\s*(\d{1,2})...")
_NUMBER_RE         = re.compile(r"^-?\d[\d,]*(?:\.\d+)?$")
_ORDINAL_KO_RE     = re.compile(r"^제?(\d+)\s*(?:호|위|번|차|등|째)$")
_AMOUNT_RE         = re.compile(r"^(?P<num>-?\d[\d,]*(?:\.\d+)?) ?(?P<unit>\D+)$")

모듈 docstring 이 규정한 파서 계약은 "returns the canonical scalar, or None … never
guesses"
인데, 여기서는 정규화 없이 값을 받아들이면서 저장 문자열은 원문 그대로 둔다.
int() 가 전각을 받아주기 때문에 뒤따르는 변환도 예외 없이 통과한다.

코드베이스는 NFC 로 정규화하는데, NFC 는 전각 숫자를 접지 않는다(NFKC 만 접는다).
따라서 상류 어디에서도 두 표기가 합쳐지지 않는다.

영향

한 값이 세 갈래로 갈린다.

  1. typed 관계 아래 — 그룹핑 키가 스칼라이므로 두 표기가 같은 값으로 병합된다.
    단일값 모순 검출·비교 규칙은 둘을 하나로 본다.
  2. 객체 매치 질의 — 질의 상수는 저장 문자열과 대조되므로, ASCII 로 물으면 전각 행이
    조용히 미스한다. 검증된 부정(engine says no)과 구분되지 않는다.
  3. typed 선언이 없는 관계 — 파서를 아예 타지 않으므로 두 표기가 별개 엔티티
    남는다.

즉 "같은 값인가" 에 대한 답이 코드 경로마다 다르다. 심각도 중간 — 크래시는 없고
CJK 소스·PDF 변환·IME 전각 모드에서 유입되는 사고성 입력이 주된 경로다.

제안

두 방향 중 하나를 정책으로 선택해야 한다(둘 다 하면 안 된다).

  • (A) 거부 — 수치 그룹을 [0-9] 로 좁혀 전각 값이 파싱되지 않게 한다. 값은 "does not
    parse" 경로로 떨어져 typed projection 경고와 entity_audit 의 malformed 보고에 드러난다.
    저장 문자열을 고쳐 쓰지 않으므로 데이터를 재작성하지 않는다. 다만 (3) 의 엔티티
    분리는 그대로 남는다.
  • (B) 접기tools/merge_candidates.pynormalize_rows 에서 dedup 키 계산 전에
    전각을 ASCII 로 접는다(canonical_amount 가 이미 같은 자리에서 하는 일). 세 증상이
    한꺼번에 사라지지만 저장된 사실을 재작성하는 결정이다.

이 저장소가 --category 사전검증·silent-zero 가드에서 보인 규율(조용한 폴드보다 명시적
거부)을 따르면 (A) 가 일관된다. 어느 쪽이든 선택 자체가 문서화되어야 한다.

수용 기준

  • 네 타입(date/number/ordinal/amount) 전각 입력에 대해 선택한 정책이 일관되게 적용된다.
  • ASCII 입력의 기존 파싱 결과 불변(회귀 없음).
  • (A) 선택 시: 전각 값이 사용자에게 보이는 경로(경고 또는 audit)로 최소 한 번 표면화된다.
  • 네 타입 전각 케이스를 고정하는 단위 테스트.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions