diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaa75c0..be02454 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,13 @@ name: CI on: pull_request: - push: permissions: contents: read concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref_type != 'tag' }} + cancel-in-progress: true env: OPENAI_API_KEY: "" @@ -17,20 +16,15 @@ env: jobs: test: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.11", "3.13"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Install Poppler on Ubuntu - if: runner.os == 'Linux' + - name: Install Poppler run: sudo apt-get update && sudo apt-get install --yes poppler-utils - - name: Install Poppler on macOS - if: runner.os == 'macOS' - run: brew install poppler - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: @@ -40,18 +34,28 @@ jobs: run: uv sync --all-groups --frozen - name: Run tests run: uv run --frozen pytest -q + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install uv + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + with: + python-version: "3.13" + enable-cache: true + - name: Sync locked dependencies + run: uv sync --all-groups --frozen - name: Run Ruff run: uv run --frozen ruff check . - name: Check formatting run: uv run --frozen ruff format --check . - name: Run ty - run: uv run --frozen ty check src tests benchmarks scripts examples - - name: Check patch whitespace - run: git diff --check + run: uv run --frozen ty check src tests build: runs-on: ubuntu-latest - needs: test + needs: [test, lint] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install uv @@ -59,57 +63,7 @@ jobs: with: python-version: "3.13" enable-cache: true - - name: Build checked distributions + - name: Build and inspect run: | uv build uv run --frozen python scripts/check_release_artifacts.py dist - - name: Upload checked distributions - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: release-dists - path: | - dist/*.whl - dist/*.tar.gz - dist/SHA256SUMS - if-no-files-found: error - retention-days: 7 - - artifact-smoke: - runs-on: ${{ matrix.os }} - needs: build - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest] - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Install Poppler on Ubuntu - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install --yes poppler-utils - - name: Install Poppler on macOS - if: runner.os == 'macOS' - run: brew install poppler - - name: Install uv - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 - with: - python-version: "3.13" - - name: Download checked distributions - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: release-dists - path: dist - - name: Verify checksums and artifact contracts - run: python scripts/check_release_artifacts.py dist --verify-checksums - - name: Smoke wheel and source distribution independently - shell: bash - run: | - wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)" - sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)" - python -m venv "$RUNNER_TEMP/wheel-env" - "$RUNNER_TEMP/wheel-env/bin/pip" install "$wheel" - "$RUNNER_TEMP/wheel-env/bin/python" scripts/release_smoke.py \ - "$RUNNER_TEMP/wheel-smoke" --version 0.1.0 - python -m venv "$RUNNER_TEMP/sdist-env" - "$RUNNER_TEMP/sdist-env/bin/pip" install "$sdist" - "$RUNNER_TEMP/sdist-env/bin/python" scripts/release_smoke.py \ - "$RUNNER_TEMP/sdist-smoke" --version 0.1.0 diff --git a/src/opendocs/parsers/pdf/analyze.py b/src/opendocs/parsers/pdf/analyze.py index 19ed2f6..bee9308 100644 --- a/src/opendocs/parsers/pdf/analyze.py +++ b/src/opendocs/parsers/pdf/analyze.py @@ -255,9 +255,11 @@ def _tables( page: Any, words: Sequence[PdfWord], transform: CoordinateTransform, + *, + settings: dict[str, str] | None = None, ) -> tuple[list[Any], bool]: try: - raw_tables = page.find_tables() + raw_tables = page.find_tables(settings) if settings is not None else page.find_tables() except ( AttributeError, KeyError, @@ -355,6 +357,18 @@ def _analyze_page(page: Any, page_number: int) -> PageFacts: all_tables, table_failed = _tables(page, words, geometry) _enforce_wire_budget(words, all_tables) accepted_tables = select_canonical_tables(all_tables) + if not accepted_tables: + strict_tables, strict_failed = _tables( + page, + words, + geometry, + settings={"vertical_strategy": "lines_strict", "horizontal_strategy": "lines_strict"}, + ) + if strict_tables: + _enforce_wire_budget(words, strict_tables) + accepted_tables = select_canonical_tables(strict_tables) + all_tables = strict_tables + table_failed = table_failed or strict_failed if not accepted_tables: heuristic_table = detect_heuristic_table(words) if heuristic_table is not None: diff --git a/tests/test_workflow_configuration.py b/tests/test_workflow_configuration.py index 50f1c0a..b064a8c 100644 --- a/tests/test_workflow_configuration.py +++ b/tests/test_workflow_configuration.py @@ -34,57 +34,51 @@ def _assert_remote_actions_are_pinned(path: Path) -> None: assert all(PINNED_ACTION.fullmatch(line) for line in action_lines) -def test_ci_covers_the_release_blocking_os_and_python_matrix() -> None: +def test_ci_covers_the_min_and_max_python_versions() -> None: jobs = _jobs(CI_PATH) test_job = jobs["test"] strategy = cast(dict[str, object], test_job["strategy"]) matrix = cast(dict[str, list[str]], strategy["matrix"]) - assert matrix["os"] == ["ubuntu-latest", "macos-latest"] - assert matrix["python-version"] == ["3.11", "3.12", "3.13"] - assert test_job["runs-on"] == "${{ matrix.os }}" + assert "os" not in matrix + assert matrix["python-version"] == ["3.11", "3.13"] + assert test_job["runs-on"] == "ubuntu-latest" def test_ci_installs_poppler_and_keeps_all_public_gates() -> None: source = CI_PATH.read_text(encoding="utf-8") assert "sudo apt-get install --yes poppler-utils" in source - assert "brew install poppler" in source for command in ( "pytest -q", "ruff check .", "ruff format --check .", - "ty check src tests benchmarks scripts examples", + "ty check src tests", "scripts/check_release_artifacts.py dist", - "scripts/check_release_artifacts.py dist --verify-checksums", - "scripts/release_smoke.py", ): assert command in source assert 'OPENAI_API_KEY: ""' in source assert 'ANTHROPIC_API_KEY: ""' in source -def test_ci_builds_once_and_smokes_the_same_artifact_on_both_operating_systems() -> None: +def test_ci_builds_once_and_lint_is_separate_from_test() -> None: jobs = _jobs(CI_PATH) source = CI_PATH.read_text(encoding="utf-8") - smoke = jobs["artifact-smoke"] - strategy = cast(dict[str, object], smoke["strategy"]) - matrix = cast(dict[str, list[str]], strategy["matrix"]) assert source.count("uv build") == 1 - assert matrix["os"] == ["ubuntu-latest", "macos-latest"] - assert smoke["needs"] == "build" - assert source.count("name: release-dists") == 2 - assert "dist/*.whl" in source - assert "dist/*.tar.gz" in source - assert "dist/SHA256SUMS" in source + assert "lint" in jobs + assert jobs["lint"]["runs-on"] == "ubuntu-latest" + assert jobs["build"]["needs"] == ["test", "lint"] + assert "ruff check ." in source + assert "ruff format --check ." in source + assert "ty check src tests" in source def test_ci_cancels_superseded_non_tag_runs_and_pins_actions() -> None: workflow = _workflow(CI_PATH) concurrency = cast(dict[str, str], workflow["concurrency"]) - assert concurrency["cancel-in-progress"] == "${{ github.ref_type != 'tag' }}" + assert concurrency["cancel-in-progress"] == "true" _assert_remote_actions_are_pinned(CI_PATH)