Repository navigation
Conversation
cragwolfe
left a comment
There was a problem hiding this comment.
Thanks for fixing single-letter list markers. I found two output regressions in the inferred-list conversion path:
- Mixed marker styles lose item boundaries (
unstructured/nlp/patterns.py:119,unstructured/partition/common/common.py:137-140). Fora. heading\n• first\n• second, the base produces three items, but the new letter match prevents the Unicode fallback and produces one item containing both bullet lines. Please preserve both marker families when they occur in the same block and add cases in both orders. - Repeated whitespace in abbreviations loses text (
unstructured/nlp/patterns.py:119). The guard checks exactly one whitespace character, soe. g. spaced examplematches and removese., although the single-space form is preserved. Please handle a same-line whitespace run and cover repeated spaces and mixed spaces/tabs without crossing line boundaries.
The intended splitting also changes public element text, counts, and boundaries for lettered blocks; removing the old mixed-case delimiter changes output for those inputs too. A maintainer should decide the compatibility and release treatment for these changes. GPT Pro reviewed the full diff and relevant source; these observations were checked against the code. No local tests or reproductions were run.
(authored by codex)
2190912 to
adfb18e
Compare
`[a-z][A-Z]` is two adjacent single-character classes, so ENUMERATED_BULLETS_RE matched exactly two characters and never a single `a` or `A`. Lettered lists therefore came out as one ListItem with the markers still in the text, while clean_ordered_bullets and extract_ordered_bullets both accept a single letter as a marker. Add a line-anchored letter branch with a literal dot, a same-line whitespace lookahead and an abbreviation guard that tolerates repeated spaces and tabs. The numeric branch is unchanged. A block can also mix enumerated markers with bullet glyphs. The glyph split only ran when the enumerated split found nothing, so the glyph items stayed glued to the last enumerated one. Split every remaining segment on the glyph pattern too. Signed-off-by: Eapp1e <163811520+Eapp1e@users.noreply.github.com>
adfb18e to
8cb3d31
Compare
|
Good catch, and you are right on both points. Both are fixed in Mixed marker families. The glyph split now also runs on every segment the enumerated split produced, so a block that mixes both families yields one item per item in either order: layout_list_to_list_items("a. heading\n\u2022 first\n\u2022 second", None, None, None, None)
# before: ['a. heading\n\u2022 first\n\u2022 second']
# after: ['heading', 'first', 'second']
layout_list_to_list_items("\u2022 first\na. heading\nb. other", None, None, None, None)
# after: ['first', 'heading', 'other']Blocks that use a single family take the same path as before, because the extra split only re-runs over segments that already had no glyphs in them. Whitespace between abbreviation letters. The lookahead and the guard now both use a run of same-line whitespace instead of a single space: ENUMERATED_BULLETS_RE = re.compile(
r"(?:(?:\d{1,3})\.?){1,3}"
rf"|(?:(?:(?<=\n)|\A){NON_NEWLINE_WHITESPACE}[a-zA-Z]\."
rf"(?=[^\S\n\r]|$)(?![^\S\n\r]+[a-zA-Z]\.))",
)
On the third point: I agree the maintainer owns the compatibility call. The letter branch only turns on for a marker that starts a line, carries a literal dot, and is followed by same-line whitespace or the end of the block, so prose and the existing numeric cases are untouched. If you would rather land the compatibility note separately (release notes or a follow-up), say so and I will keep this PR to the pattern and the mixed-block split. |
Summary
ENUMERATED_BULLETS_REcannot match a single-letter bullet, so lettered lists come out as oneListIteminstead of one per item.unstructured/nlp/patterns.py:111compiled[a-z][A-Z]is two single-character classes placed next to each other, so it matches exactly two characters (aB) and never a singleaorA. The two ordered-bullet helpers in this repo both accept a single letter as a marker:unstructured/cleaners/core.py:53clean_ordered_bullets— docstring says "accounting numeric and alphanumeric types", with the examplea.b This is a very important point -> This is a very important point.unstructured/cleaners/extract.py:104extract_ordered_bullets— docstring showsa.1 This is a very important point -> ("a", "1", None).The splitter regex used by the list path does not, so the marker stays in the text:
It is user visible through
partition_imageandpartition_pdf(strategy="ocr_only"):ocr_data_to_elements->normalize_layout_element->layout_list_to_list_items(unstructured/partition/common/common.py:137).Change
Two files change; the numeric branch and every existing behaviour stay byte for byte.
unstructured/nlp/patterns.pygains a lettered branch next to the untouched numeric one:Each restriction is load-bearing:
[a-zA-Z]makes every letter a separator and"1. foo\n2. bar"splits into[].\d{1,3}\.?keeps the dot optional for numbers, but"a"without a dot is a word, not a marker.(?=[^\S\n\r]|$)requires same-line whitespace or the end of the text after the dot.(?![^\S\n\r]+[a-zA-Z]\.)rejects a second letter and dot on the same line. Without it"e. g. spaced example"becomes['g. spaced example']and"i. e. the letter"becomes['e. the letter']. It uses[^\S\n\r]+rather than a single space because extracted text writes abbreviations with repeated spaces or tabs as often as with one space, and it must not reach across a line break.unstructured/partition/common/common.pyalso has to split mixed blocks. A block can hold enumerated markers and bullet glyphs at the same time:Before, the enumerated split produced two segments and the bullet glyphs stayed glued to the second one, because the glyph split only ran when the enumerated split found nothing. The list path now splits every remaining segment on the glyph pattern as well, so both orders of the two marker families produce one item per item, and blocks that use a single family are unaffected.
Behaviour on the existing inputs is unchanged:
"1. foo\n2. bar","1.1 ...","1.1.1 Deep point","Page 1 of 2","1 foo 2 bar","a cat sat on a mat.","See section A. Appendix","Fig. 3 shows the trend","Note. This is a note.","b) alt paren","e. g. spaced example","i. e. the letter"all behave exactly as before.Test
test_unstructured/nlp/test_patterns.pyis new and covers the regex, the converter, mixed marker families, and the abbreviation cases that must not split.ruff checkreportsAll checks passed!andruff format --checkreports3 files already formattedfor the two source files and the new test.Fixes #4635