Skip to content

fix(markdown): keep multi-line body cells inside their pipe-table row - #1533

Merged
dgunning merged 2 commits into
dgunning:mainfrom
cristianchiriac:fix-markdown-multiline-table-cells
Oct 15, 2026
Merged

dgunning merged 2 commits into
dgunning:mainfrom
cristianchiriac:fix-markdown-multiline-table-cells

Conversation

@cristianchiriac

Copy link
Copy Markdown
Contributor

What this changes

Document.to_markdown() no longer breaks a pipe-table row when a body cell contains a line break (<br>, or several paragraphs in one <td>). Before, the cell was written across lines, so Markdown parsers ended the table early and put the rest of the cell and the following cells outside it. A cell's lines are now joined with a space before the row is emitted, so First<br>second renders as | First second | 10 |.

Only cells that contain a newline are touched. Single-line cells, headers (already flattened in _combine_multi_row_headers) and the escaping behaviour (#1483) are unchanged.

Fixes #1486

Verification

  • Regression test for a bug is in tests/issues/regression/test_1486_markdown_multiline_cells.py, and its docstring links the issue. It covers <br>, <br/><br/> and two <p> in one cell, checking both the pipe row and what markdown-it parses as table cells (["Item", "Value", "First second", "10"]). All three fail on main ('| First\n\n\n\nsecond | 10 |') and pass with the change.
  • Changelog: changelog.d/1486.fixed.md; assemble_changelog.py --check accepts it.
  • I ran the fast tests matching markdown, table or document before and after the change. The only difference is the new test. tests/thirteenf/test_value_units.py::test_documented_example fails in both runs because of a Unicode error in my Windows console.
  • I didn't add a ground-truth assertion against the GSBD 10-K table mentioned in the issue, because it needs a recorded cassette. The synthetic table isolates the renderer path.

Body cells were joined into the pipe row with their internal line breaks
(from <br> or several paragraphs), so the row ended early and Markdown
parsers put the rest of the cell and the following cells outside the
table. Join a body cell's lines with a space before emitting the row.

Fixes #1486
@dgunning

Copy link
Copy Markdown
Owner

Thanks for this. It fixes #1486 cleanly, and the fast markdown, table and document tests pass on the branch. A few small changes before merging:

  1. Normalise with ' '.join(cell.split()). The current check only joins when '\n' in cell, but it splits with splitlines(). A cell holding \r, 
 and similar, with no \n, goes through unjoined and still breaks the row. ' '.join(cell.split()) covers every whitespace character. It also matches how grouped headers in the same function already flatten text, so this function would have one rule for cell whitespace instead of three.
  2. Drop the markdown_it import in the test. It isn't a declared dependency and is only installed because rich depends on it. Asserting on the rendered lines directly would work; for example, check that every table line starts and ends with | and has the expected number of cells.
  3. Rename the test file to tests/issues/regression/test_issue_1486_markdown_multiline_cells.py, to match the project convention.

Not something this PR needs to fix, but related: the grid and simple table formats have the same newline-in-cell problem, and a literal | in a body cell isn't escaped in the pipe format. We'll track those separately.

#1524 also changes _render_table_pipe. The two merge cleanly today.

@cristianchiriac

Copy link
Copy Markdown
Contributor Author

Thanks for the review! All three are in 87192ca:

  1. Body cells are now normalised with ' '.join(cell.split()), for every cell, so \r, tabs and the like can't break the row either.
  2. The test no longer imports markdown_it. It asserts the rendered lines directly: the header, the separator and | First second | 10 |, each starting and ending with | and having two cells.
  3. It's renamed to tests/issues/regression/test_issue_1486_markdown_multiline_cells.py.

The three cases still fail on main and pass on the branch. I also re-ran the fast markdown/table/document tests, and the set of failures in my environment (optional MCP/SGML dependencies and a Windows console encoding issue) is the same with and without the change.

Review: normalise each body cell with ' '.join(cell.split()) so \r, tabs
and other whitespace cannot break the row either, drop the markdown_it
import from the regression test, and rename it to the test_issue_ prefix.
dgunning added a commit that referenced this pull request Oct 14, 2026
…1546)

#1524 promotes the first non-empty row of a headerless table to the header
so the table gets a delimiter row. A table with only one non-empty row (page
footers, single footnotes) then became a header with a lone `| --- |` and no
body: 182 such tables in the bac 10-K fixture, 117 in abbv. Those now keep the
plain row they had before #1524; the count of header-only tables per fixture
is back to its pre-#1524 value (bac 2, abbv 1).

The promoted header now collapses whitespace with ' '.join(cell.split()), the
same rule #1533 applies to body cells. The changelog fragment is rewritten to
describe the change against the last release (headerless tables had no
delimiter row) instead of against the PR's first draft.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dgunning
dgunning merged commit 49d0da6 into dgunning:main Oct 15, 2026
11 checks passed
dgunning added a commit that referenced this pull request Oct 22, 2026
…alue fixes (#1673)

* fix(bdc): keep a position whose identifier ends in its coupon (#1504) (#1509)

_parse_investment_identifier types any identifier ending in a bracketed
percent as Unknown, a rule written for HTGC's category roll-ups ("Debt
Investments Software (52.80%)"), and from_xbrl drops Unknown rows. PhenixFIN
ends a real loan with its coupon ("... Delayed Draw Term Loan (14.00%)",
10-Q 0001213900-26-085667), so it left the portfolio with no warning.

A suffixed row that states a principal amount is now re-parsed without the
suffix and keeps the type and company that parse gives. HTGC's roll-ups state
cost but never principal: measured over 46 filings from 27 BDCs, the only row
this moves is PhenixFIN's loan. Keying on cost instead re-admits 51 HTGC
roll-ups (+$18,335.9M on the FY2024 10-K); stripping and re-parsing alone
re-admits 48 (+$10,979.9M), since "Debt Investments Software" parses as
Unclassified.

Co-authored-by: Matthew Mitchell <matthewprestonmitchell@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f307d6a)

* fix(markdown): promote the first row in headerless pipe tables (#1524)

* fix(markdown): keep the delimiter row for headerless tables (#1484)

The pipe renderer wrote the header row and the separator only when the
table had headers, so a headerless table came out as loose rows. Write a
blank header row and a delimiter row for it.

* Promote headerless table data into Markdown headers

* Preserve literal promoted Markdown table cell contents

(cherry picked from commit 9600182)

* fix(markdown): leave a one-row headerless table unpromoted (#1484) (#1546)

#1524 promotes the first non-empty row of a headerless table to the header
so the table gets a delimiter row. A table with only one non-empty row (page
footers, single footnotes) then became a header with a lone `| --- |` and no
body: 182 such tables in the bac 10-K fixture, 117 in abbv. Those now keep the
plain row they had before #1524; the count of header-only tables per fixture
is back to its pre-#1524 value (bac 2, abbv 1).

The promoted header now collapses whitespace with ' '.join(cell.split()), the
same rule #1533 applies to body cells. The changelog fragment is rewritten to
describe the change against the last release (headerless tables had no
delimiter row) instead of against the PR's first draft.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit df56141)

* fix(markdown): keep multi-line body cells inside their pipe-table row (#1533)

* fix(markdown): keep multi-line body cells inside their pipe-table row

Body cells were joined into the pipe row with their internal line breaks
(from <br> or several paragraphs), so the row ended early and Markdown
parsers put the rest of the cell and the following cells outside the
table. Join a body cell's lines with a space before emitting the row.

Fixes #1486

* fix(markdown): collapse every whitespace run in pipe-table body cells

Review: normalise each body cell with ' '.join(cell.split()) so \r, tabs
and other whitespace cannot break the row either, drop the markdown_it
import from the regression test, and rename it to the test_issue_ prefix.

(cherry picked from commit 49d0da6)

* fix(xbrl): a decrease row was folded into the increase row sharing its label (#1549)

* fix(xbrl): a decrease row was folded into the increase row sharing its label (#1471)

Statement.get_raw_data() applies _merge_complementary_rows() on top of
XBRL.get_statement(), to join the rows a company leaves behind when it renames
a concept between years. It matched on label, level and dimension status, and
required the period values to be complementary. Oracle's FY2026 10-K
(0001193125-26-277521) labels both its Increases and Decreases foreign-currency
translation concepts "Cumulative translation adjustments and other, net", files
the increase for FY2026 and the decrease for FY2025 and FY2024, and so satisfied
every test. The merge copied the decrease values into the increase row and
deleted the decrease row, which carried the only -1 preferred_sign.
to_dataframe(presentation=False) then exported the $11M and $8M decreases as
increases with preferred_sign 1.

Two changes, both in _merge_complementary_rows:

- Rows whose display signs differ are no longer merged (_display_signs, beside
  _period_kinds). The assumption is that a rename keeps the direction of the line
  item it renames; the fixture corpus holds no cross-concept rename of a
  non-dimensional row to test it against, and Oracle is the only cross-concept
  merge known. A skipped merge leaves two rows with one label and loses no value.
- When rows do merge, the second row's preferred_signs are now copied across with
  its values (a union; the first row's entries win), as units and period_types
  already were. Before, a negated row kept its -1 only for the periods that came
  from the first row.

The visible change reaches the rendered statement and the default to_dataframe()
as well: Oracle's note used to render one row, "22  11  8", and now renders two
rows with the same label, "22" and "11  8".

Measured over the fixture corpus (39 fixture filings, 2,544 statements) before and after:
the same 2 merges, both of one concept with itself, both sign +1.

Tests (7): the Oracle replay, the synthetic increase/decrease pair, the same pair
with an empty preferred_signs on the increase row and in reverse order, a
same-direction rename control (sign +1 and -1, which also checks the sign union),
and the helper. Against origin/main's merge function, 6 fail and the helper test
passes. A blanket refusal to merge across concepts fails the two controls, and so
does dropping the sign union.

Cassette: tests/cassettes/test_oracle_keeps_its_increase_and_decrease_rows_apart.yaml
was recorded from live SEC on 2026-10-15 against unpatched main and not edited.
It holds the two XBRL documents of the filing (orcl-20260531.xsd and
orcl-20260531_htm.xml, 9.5 MB; Oracle embeds its linkbases in the schema) rather
than the 29 MB full submission, and loads the same 91 roles and 2,195 facts as
filing.xbrl(). Ground truth is the filed table in the income taxes note, in
millions: 22, (11), (8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): note the rendered and default-export change for #1471

The fragment named only get_raw_data() and to_dataframe(presentation=False), but
the merge also feeds the rendered statement and the default to_dataframe(): the
Oracle row renders as two rows with the same label now, where it was one.
Say so, and shorten the rest to stay under the 500-character limit (457 now).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a7d6336)

* fix(financials): get_revenue() returns a bank's total net revenue, not a component (#1567)

* fix(financials): get_revenue() returns a bank's total net revenue, not a component (edgartools-u9yc9)

Banks report total revenue as us-gaap:RevenuesNetOfInterestExpense, which the
standardization's 'Revenue' mapping does not carry, so get_revenue() fell to
the next thing that matched. Measured on FY2025 10-Ks against
data.sec.gov companyconcept:
- JPM: no mapped concept on the face statement, so the label patterns ran and
  `Revenue$` took "Noninterest revenue": 87.0B of 182.4B
- WFC: fee lines are tagged contract revenue, which was searched before any
  bank total: "Fee income", 16.1B of 83.7B
- AXP: 37.4B of 72.2B
- USB: no total row at all; the label patterns took "Card revenue", 1.7B
GS and MS were right only because a label pattern happened to hit
"Net revenues".

Search RevenuesNetOfInterestExpense after Revenue and before contract
revenue. On a bank's statement (a us-gaap:NoninterestIncome row), skip the
label patterns, since every "... revenue" label there is a component, so USB
reports None rather than a wrong number (follow-up edgartools-hmj0j reads
the filed us-gaap:Revenues fact instead). Add the concept to
Statement.REVENUE_CONCEPTS so the ratio and trend code agrees.

Before/after over 23 filers: JPM, WFC, AXP corrected; USB 1.7B -> None;
BAC, C, GS, MS, PNC, SCHW, MET, AIG, ALL, COF (None before and after),
AAPL, PFE, XOM, BRK.A, KO, WMT, TSLA, UNH unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(financials): correct a pin that snapshotted JPM's noninterest revenue as revenue (edgartools-u9yc9)

test_y4yp4_metrics_render_reuse pinned get_financial_metrics()['revenue'] for
the JPM FY2023 fixture at 68,837M, the value get_revenue() returned before
this fix. That is us-gaap:NoninterestIncome; the bank's total net revenue in
the same filing (0000019617-24-000225) is 158,104M. The pin was a
behaviour-preservation snapshot for the render-reuse refactor, not a value
checked against SEC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2b7953b)

* fix(financials): get_revenue() reads a bank's filed total when the face statement has none (edgartools-hmj0j) (#1580)

After u9yc9, a bank-layout income statement with no total-revenue row
returned None rather than a component. U.S. Bancorp and Capital One are that
shape, but both file the total off the face statement as a non-dimensional
us-gaap:Revenues fact. get_revenue() now reads it for the period that
period_offset selects among the statement's columns.

Instrumented first: U.S. Bancorp's instance (0000036104-26-000011) has 18
us-gaap:Revenues facts. 15 are segment values (is_dimensioned=True, e.g.
OperatingSegmentsMember x a business segment: 12,083M, 7,607M, ...), and
exactly one per year is non-dimensional: 28,656M / 27,455M / 28,144M for
2025/2024/2023. Capital One (0000927628-26-000024): 53,434M / 39,112M /
36,787M. No answer when the filing has no such fact or more than one
distinct value. Summing the face rows would be wrong: U.S. Bancorp's NII
plus noninterest income is 28,540M.

The period column naming in _order_period_columns moves to
_period_column_name so the new lookup maps a column back to its period by
the same rule. EntityFacts already returns both totals; no change there.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8cd5799)

* fix(8-k): detect items headed "Item - 5.02" with a dash (#1590)

An 8-K whose item heading puts a dash after "Item" was read as having no items.
Liaoning Shuiyun Qinghe Rice Industry's 8-K 0000710782-24-000005 (filed 2024-12-20)
heads its item "Item - 5.02 Departure of Directors of Principal Officers; Election of
Directors; Appointment of Principal Officers." On 8cd5799, CurrentReport.items was [],
content_type was "other", and report["5.02"] raised SectionNotFoundError, so the MCP
reader answered "No items detected in this 8-K."

Root cause: the text-based item patterns in edgar/company_reports/current_report.py
required whitespace between "Item" and the number (Item\s+). Three regexes did: the
item finder (_extract_items_from_text), the start of an item's text
(_extract_item_content_from_text) and the end of an item, at the next item heading.

Fix: one shared label, _ITEM_LABEL = r'Item\s*[-–—:]?\s*', used by all three, so a
dash, en dash, em dash or colon is accepted between "Item" and the number. The end
rule must change too: with only the other two widened, a dash-headed item runs on
into the next dash heading.

Measured on the saved primary document of 0000710782-24-000005: items == ['Item 5.02'],
content_type == 'director_change', and report["5.02"] is 382 characters, from the
heading to "List of shareholder:\nRu Fan", before the SIGNATURE block. The same change
gives ['Item 5.02'] for 0000710782-24-000003 ("Item - 5.02") and ['Item 8.01'] for
0000710782-24-000004 ("Item - 8.01"). Mid-sentence references stay unmatched, because
the line-start or horizontal-rule anchor is unchanged, and a lookup for an item the
filing lacks still raises SectionNotFoundError.

Regression test: tests/issues/regression/test_issue_1589.py, with the saved primary
document in tests/fixtures/issues/regression/issue_1589/. On 8cd5799, 9 of its 12 tests
fail; with this change all 12 pass. The changelog fragment is changelog.d/1589.fixed.md.

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
(cherry picked from commit b6e0014)

* fix(funds): read an N-PORT "Yes" securities-lending answer and its amount (GH #1585) (#1594)

Item C.12 states a No as an element (<isCashCollateral>N</isCashCollateral>)
and a Yes as attributes of another element (<cashCollateralCondition
isCashCollateral="Y" cashCollateralVal="..."/>); non-cash collateral and loans
by the fund take the same two shapes (SECURITY_LENDING_GROUP in
eis_NPORT_Filer.xsd). SecurityLending.from_xml read only the elements, so every
Yes read None and the amount was lost.

SGOV NPORT-P 0002071691-26-016719: cash collateral Y on 1 of 24 holdings
($441,347,500), loan by fund Y on 2 of 24. IVV 0002071691-26-019760: 1 and 59
of 508.

The flags now fall back to the condition element, and the amounts are three
new optional fields: cash_collateral_value, non_cash_collateral_value,
loan_value.

Co-authored-by: mmitc <266342770+mmitc@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c27f094)

* fix(markdown): escape | and \ in every pipe-table cell (GH #1483) (#1632)

_render_table_pipe escaped pipes and backslashes only in grouped (multi-row)
headers (#1474) and in a promoted header row (#1484). A flat header and every
body cell went out raw, so a literal pipe opened an extra column and a lone
backslash escaped the character after it. The issue's two-column header
emitted three cells over a two-column delimiter, and markdown-it then read
the whole table as a paragraph. "A | B" in a body cell split the same way.

One helper, _escape_pipe_cell, now escapes every header and body cell,
backslashes first. Over the 85 tracked HTML fixtures one filing changes:
AbbVie's FY2024 10-K (0001551152-25-000020), whose page-footer cells read
"| 2024 Form 10-K" beside the page number, about 114 lines, all footers.

The #1474 suite pinned the old flat-header bytes by design ("does not assert
Markdown escape correctness"); its flat-existing-escape-policy case now pins
the escaped bytes instead.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 888acb0)

* fix(search): search_filings with only start_date or end_date now applies the range (edgartools-329yu) (#1566)

EFTS applies dateRange=custom only when it receives both startdt and enddt;
with one it silently searches every year. search_filings sent only the bound
it was given, so search_filings("tariff", forms="10-K",
start_date="2025-01-01") returned 10,000+ hits led by 2010 and 2003 filings
(raw EFTS, 2026-10-16); with enddt the same query has 3,841, all 2025-26.
The docstring's own date-range example passed start_date alone.

Fill the open bound: start -> 2001-01-01 (EFTS coverage), end -> tomorrow
(EFTS dates are US Eastern; tomorrow keeps a caller west of it, or a
slightly slow clock, from cutting off SEC's current day). Pagination reuses
the stored params, so next()/fetch_more() are covered.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 446d447)

* fix(facts): EntityFacts revenue reads the filed total before contract revenue (edgartools-jrqvs) (#1630)

Financials.get_revenue() searches us-gaap:Revenues first since gh #1294.
The EntityFacts side still ranked RevenueFromContractWithCustomerExcludingAssessedTax
first and only gave way to a total at least 2x larger (edgartools-fdye). That
caught MetLife (31.6x) and American Tower (11.4x) but nothing within 2x.
Contract revenue can be one face line of the total (GM "Automotive" $167,971M
of $185,019M) or a note-only figure (COP $51,824M vs the face $58,944M). It can
also exceed the total (Williams $14,899M vs $11,950M), so "take the larger"
is not the rule either.

Measured over 106 large filers' FY2025 10-Ks: 15 tag both concepts for the
same year, 3 with equal values. MET and AMT were already right. The other
10 (CVX COP OXY KMI WMB D AEP SRE WMT GM) now match their face income
statement and Financials.get_revenue() through get_revenue(),
get_revenue_detailed(), the statement's Total Revenue and get_ttm_revenue().

Revenues now comes first in every facts-side list: _REVENUE_CONCEPTS, the
gross-profit fallbacks, the display order, get_ttm_revenue() and the 'revenue'
synonym group. get_revenue() and get_revenue_detailed() now share one
_REVENUE_VARIANTS constant, and get_revenue_detailed() gains the fdye
consolidated-total rule it was missing. Recency still settles which year
answers (#1149, NVIDIA's tag migration); the order only breaks same-filing
ties.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2b92e2a)

* fix(xbrl): an instant-only note renders its own instants, and render_statement honours period_filter (GH #1469) (#1633)

An unclassified note role (statement_type None) fell through select_periods
to the duration selector, so a note holding only instant facts got annual
columns with every cell blank. IBM's long-term debt components note
(0000051143-25-000015) showed three empty FY columns instead of the filed
$824M / $838M unamortized discount. XBRL.render_statement also called
select_periods directly and dropped period_filter, so an explicit instant
filter still produced duration headers.

select_periods now takes the statement's rows. When every value sits in an
instant period, and the type is not already an instant or equity type, it
picks the statement's own instants that carry at least half as many rows
as the densest one, newest first. render_statement and
Statement.to_dataframe route through determine_periods_to_display and pass
the rows; render_statement now forwards period_filter. period_view stays
unwired: IBM's "Three Recent Periods" view names quarterly keys absent from
reporting_periods and would cut three columns to one (edgartools-vsmia).

Across 2,754 roles in the 31 XBRL fixtures, 519 change, every one from
duration to instant columns on a statement with no duration facts; none of
the new columns is empty and 93% of their cells hold a value (0% before).
Balance sheet and income statement selections are unchanged.

The #1305 test pinned the old behaviour for an uncatalogued Apple balance
sheet parenthetical ("stays untyped, including its generic periods"); it
still stays untyped, and now shows its instant share-count dates.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7ce2a58)

* fix(http): configure_http(proxy=...) keeps the rate limiter and the cache (edgartools-kd2ap)

configure_http put the proxy into HTTP_MGR.httpx_params. httpx2 turns a
client-level proxy into an all:// mount that takes precedence over the
transport passed in, so after the call every request went to a bare
HTTPTransport through the proxy: no CachingTransport, no
RateLimitingTransport, in the shared and the async client alike, while
get_http_config() looked normal. The documented corporate-proxy recipe
therefore sent unthrottled traffic to SEC.

Set HTTP_MGR.proxy instead; httpxthrottlecache applies it on the innermost
transport, under the cache and the rate limiter, for the shared, async and
uncached clients. get_http_config() and diagnose_ssl report HTTP_MGR.proxy,
which also reflects HTTPS_PROXY (it read None before).

tests/core/test_httprequests.py::test_configure_http_proxy asserted the old
placement (httpx_params["proxy"]); it now asserts the proxy is on the
manager and not a client param. The regression test compares each client's
transport chain with and without a proxy: identical, except the connection
pool at the bottom is the proxy. All 5 fail against main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6e6be36)

Backport to 5.62.x: httpx instead of httpx2 in the comment and test; the
uncached-client case is dropped because 5.62.x has no uncached_http_client;
get_http_config() keeps its 5.x shape and reports HTTP_MGR.proxy.

* fix(ssl): floor certifi at 2026.10.19, which drops the compromised emSign root (edgartools-a9bak)

On 5.x, httpx verifies SEC connections against certifi's bundle unless
EDGAR_USE_SYSTEM_CERTS is set, so an installed certifi older than 2026.10.19
kept trusting "emSign ECC Root CA - G3" (eMudhra emSign CAs with
compromised EC private keys, advisory 2026-10-20). certifi was only an
indirect dependency through httpx; declare it with the fixed floor so
upgrading edgartools upgrades it.

5.62.x only: on main (6.0) httpx2 verifies against the OS store and never
reads certifi; the exposure there is a hand-built SSL_CERT_FILE bundle,
handled in PR #1647.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(8-k): #1590's missing-item test asserts 5.x's contract on 5.62.x

On 5.x a missing item returns None with a FutureWarning and raises only
under EDGARTOOLS_STRICT_ERRORS=1; the main version asserts the 6.0 raise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(http): read httpclient.HTTP_MGR through the module in the proxy test

tests/conftest.py swaps in a fresh httpclient.HTTP_MGR, so a reference
imported at module load can be stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(a9bak): read the certifi floor without tomllib, which Python 3.10 lacks

5.x still supports Python 3.10 (requires-python >=3.10), where tomllib does
not exist, so the test module failed to import and test-fast (3.10) went red
on #1673. The floor is now checked with a regex on pyproject.toml. A skip
would hide the check from exactly the 3.10 users 5.x exists for, and skips
are banned under tests/issues/regression/.

Python 3.10.20 clean uv venv (httpx only): pytest -m fast 8,623 passed,
0 failed; lowering the floor to 2026.7.22 makes the test fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Matthew Mitchell <realmatthewmitchell@gmail.com>
Co-authored-by: Matthew Mitchell <matthewprestonmitchell@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Michael <51033404+theluckystrike@users.noreply.github.com>
Co-authored-by: Cristian <80774326+cristianchiriac@users.noreply.github.com>
Co-authored-by: Max Haro <122497486+MaxHaro@users.noreply.github.com>
Co-authored-by: Brian Stevenson @ synfonia <brian@synfonia.io>
Co-authored-by: mmitc <266342770+mmitc@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document.to_markdown() splits multiline body cells outside their table rows

2 participants