Skip to content

refactor: group flat root modules into scan/ sync/ viz/ subpackages (#625)#641

Open
nlebovits wants to merge 2 commits into
mainfrom
refactor/group-root-modules-625
Open

refactor: group flat root modules into scan/ sync/ viz/ subpackages (#625)#641
nlebovits wants to merge 2 commits into
mainfrom
refactor/group-root-modules-625

Conversation

@nlebovits

@nlebovits nlebovits commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #625. Part of the #618 epic — the sequenced-last, mechanical namespace flatten (all prior tickets #619#624 have landed).

What

Relocates 20 flat portolan_cli/ root modules into three responsibility-scoped subpackages. Flat root shrinks 64 → 44 .py files.

Package Members
sync/ push, pull, upload, upload_progress, download, checksums + core (clone/sync orchestration)
scan/ classify, detect, infer, fix, output, progress, check + core (scan orchestrator)
viz/ thumbnail, thumbnail_style, style, pmtiles, pmtiles_links

Inside scan/ and sync/ the redundant scan_/upload_ prefixes are dropped to match the existing metadata/, extract/ convention (metadata/scan.py, not metadata/metadata_scan.py).

Why core.py instead of a fat __init__.py

The orchestrators (formerly scan.py / sync.py) live in <pkg>/core.py with an import-free __init__.py. Putting the orchestrator in __init__.py looked tempting (zero public-API churn) but is wrong here: importing any leaf submodule (e.g. scan.detect) forces Python to run the parent __init__ first, eagerly loading the 1440-line orchestrator — which reintroduces the collection_id ⇄ scan and add ⇄ sync import cycles. Verified: importing a leaf submodule first no longer triggers the orchestrator.

Public symbols now come from portolan_cli.scan.core / portolan_cli.sync.core.

Mechanics

  • All 20 moves are pure git mv renames — history preserved.
  • The repo uses absolute imports exclusively, so relocating a file leaves its own imports valid; only references to the moved modules were rewritten (single-pass, \b-bounded regex, so sync.push submodule stays distinct from the sync.push_async orchestrator symbol).
  • portolan_cli:cli entry point unchanged.

Import-linter boundaries (new)

  • viz-is-a-leafviz imports neither scan nor sync (transitive; viz truly is a leaf).
  • scan-below-syncscan never directly imports sync/viz (allow_indirect_imports, because the check command legitimately fans out through shared root modules like catalog/metadata/convert, which themselves use sync/viz).

The validation-seam-for-reis documentary comments are updated to the new paths (scan.classify, viz.pmtiles_links).

Verification (local)

  • lint-imports7/7 kept, 0 broken
  • mypy --strict — clean (151 files)
  • ruff check + ruff format — clean
  • Targeted scan/sync/push/pull/viz/validation suites — 336 passed, 1 skipped
  • Leaf-first import smoke test confirms cycles are broken; portolan --help works

Full suite runs in CI.

Acceptance criteria

  • Flat root shrinks substantially (64 → 44); each subpackage has a clear responsibility.
  • New import-linter contracts encode the boundaries and pass.
  • lint-imports green; CLI entry point unchanged. (Full test suite → CI.)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Expanded directory scanning file classification (10 categories), including GeoParquet detection and more informative skip reasons for unknown/unrecognized files.
    • Added vector and raster style generation, plus style/legend discovery and automatic collection.json asset/manifest registration and COG render enrichment.
    • Added checksum utilities for deterministic asset tracking, including directory fingerprints and directory size calculations.
  • Documentation
    • Updated scan and sync/visualization package usage documentation/examples to match the current module layout.

…625)

Part of #618. Relocate 20 flat portolan_cli/ root modules into three
responsibility-scoped subpackages, shrinking the flat root from 64 to 44 .py
files:

- sync/  <- push, pull, upload, upload_progress, download, checksums; the
           sync/clone orchestrator lives in sync/core.py
- scan/  <- scan_classify->classify, scan_detect->detect, scan_infer->infer,
           scan_fix->fix, scan_output->output, scan_progress->progress, check;
           the scan orchestrator lives in scan/core.py
- viz/   <- thumbnail, thumbnail_style, style, pmtiles, pmtiles_links

The two orchestrators (formerly scan.py / sync.py) live in <pkg>/core.py with
import-free __init__.py. This is deliberate: making the orchestrator the
package __init__ would force every leaf-submodule import to eagerly run it,
reintroducing the collection_id<->scan and add<->sync import cycles. Public
symbols are now imported from portolan_cli.scan.core / portolan_cli.sync.core.

All moves are pure git renames (history preserved); the repo uses absolute
imports exclusively, so relocating files left their own imports valid and only
references TO the moved modules were rewritten.

Import-linter contracts lock the new boundaries:
- viz-is-a-leaf: viz imports neither scan nor sync (transitive).
- scan-below-sync: scan never directly imports sync/viz (direct-only, since
  the check command legitimately fans out through shared root modules).

The validation-seam ignore_imports comments are updated to the new paths.
CLI entry point (portolan_cli:cli) unchanged.

Verified locally: lint-imports (7/7), mypy --strict (151 files), ruff, and
targeted scan/sync/push/pull/viz/validation suites (336 passed) green.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cd78e74d-4be6-4332-92c2-9313ddfde5da

📥 Commits

Reviewing files that changed from the base of the PR and between 2d67ce9 and 13e5036.

📒 Files selected for processing (5)
  • tests/integration/test_clone_ergonomics_integration.py
  • tests/integration/test_nested_catalog_clone.py
  • tests/unit/test_clone_ergonomics.py
  • tests/unit/test_extension_registry.py
  • tests/unit/test_sync.py
✅ Files skipped from review due to trivial changes (2)
  • tests/unit/test_extension_registry.py
  • tests/unit/test_clone_ergonomics.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/integration/test_nested_catalog_clone.py
  • tests/integration/test_clone_ergonomics_integration.py
  • tests/unit/test_sync.py

📝 Walkthrough

Walkthrough

The change groups scan, sync, and visualization functionality into dedicated subpackages. New checksum, classification, style, and thumbnail-style modules are added, while existing production imports, CLI wiring, backend integrations, validation rules, documentation examples, tests, and mocks are updated to the new paths. Import-linter contracts document the package boundaries and prevent prohibited direct imports.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main refactor: moving flat root modules into scan, sync, and viz subpackages.
Description check ✅ Passed The description covers the refactor, rationale, verification, and linked issue, though it omits some template sections.
Linked Issues check ✅ Passed The changes match #625 by reorganizing root modules into scan/sync/viz, adding import-linter boundaries, and keeping the CLI entry point stable.
Out of Scope Changes check ✅ Passed The remaining edits are support work for the refactor, mainly import rewrites, new package modules, tests, and boundary docs.
Docstring Coverage ✅ Passed Docstring coverage is 96.64% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/group-root-modules-625

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.05882% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
portolan_cli/status.py 50.00% 1 Missing ⚠️
portolan_cli/viz/thumbnail.py 66.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@portolan_cli/sync/checksums.py`:
- Around line 43-91: The compute_dir_checksum function currently hashes only
file metadata, so same-size edits with preserved mtimes are missed. Update it to
incorporate each contained file’s actual bytes into the SHA-256 checksum while
retaining deterministic relative-path handling and recursive traversal; ensure
unreadable files are handled consistently rather than silently producing a valid
content checksum.

In `@tests/integration/test_nested_catalog_clone.py`:
- Around line 90-93: Update all three relevant test call sites to patch
portolan_cli.sync.core.pull rather than portolan_cli.sync.pull, matching the
binding used by portolan_cli.sync.core. Keep the existing mock behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a61b91a-407f-417d-b0f5-d92bfab74b32

📥 Commits

Reviewing files that changed from the base of the PR and between f82dc2e and 2d67ce9.

📒 Files selected for processing (120)
  • portolan_cli/add.py
  • portolan_cli/backends/iceberg/backend.py
  • portolan_cli/backends/json_file.py
  • portolan_cli/cli.py
  • portolan_cli/collection_id.py
  • portolan_cli/convert.py
  • portolan_cli/discovery.py
  • portolan_cli/extract/common/orchestrator_base.py
  • portolan_cli/metadata/fix.py
  • portolan_cli/metadata/scan.py
  • portolan_cli/preparation.py
  • portolan_cli/query.py
  • portolan_cli/scan/__init__.py
  • portolan_cli/scan/check.py
  • portolan_cli/scan/classify.py
  • portolan_cli/scan/core.py
  • portolan_cli/scan/detect.py
  • portolan_cli/scan/fix.py
  • portolan_cli/scan/infer.py
  • portolan_cli/scan/output.py
  • portolan_cli/scan/progress.py
  • portolan_cli/status.py
  • portolan_cli/sync/__init__.py
  • portolan_cli/sync/checksums.py
  • portolan_cli/sync/core.py
  • portolan_cli/sync/download.py
  • portolan_cli/sync/pull.py
  • portolan_cli/sync/push.py
  • portolan_cli/sync/upload.py
  • portolan_cli/sync/upload_progress.py
  • portolan_cli/validation/rules.py
  • portolan_cli/viz/__init__.py
  • portolan_cli/viz/pmtiles.py
  • portolan_cli/viz/pmtiles_links.py
  • portolan_cli/viz/style.py
  • portolan_cli/viz/thumbnail.py
  • portolan_cli/viz/thumbnail_style.py
  • pyproject.toml
  • tests/benchmark/test_scan_performance.py
  • tests/iceberg/unit/test_on_post_add.py
  • tests/iceberg/unit/test_pull.py
  • tests/integration/test_check_conversion_config.py
  • tests/integration/test_clone_ergonomics_integration.py
  • tests/integration/test_cog_reoptimize.py
  • tests/integration/test_concurrency_enforcement.py
  • tests/integration/test_download_integration.py
  • tests/integration/test_nested_catalog_clone.py
  • tests/integration/test_partition_paths.py
  • tests/integration/test_pmtiles_integration.py
  • tests/integration/test_pull_integration.py
  • tests/integration/test_pull_parallel_integration.py
  • tests/integration/test_push_integration.py
  • tests/integration/test_push_parallel_integration.py
  • tests/integration/test_s3_moto.py
  • tests/integration/test_scan_unrecognized_files.py
  • tests/integration/test_sync_integration.py
  • tests/integration/test_thumbnail_workflow.py
  • tests/integration/test_upload_integration.py
  • tests/spec_compliance/test_extensions_doc_parity.py
  • tests/unit/extract/common/test_orchestrator_base.py
  • tests/unit/test_add.py
  • tests/unit/test_asset_role_media.py
  • tests/unit/test_check_remove_legacy.py
  • tests/unit/test_check_workflow.py
  • tests/unit/test_cli_check.py
  • tests/unit/test_cli_profile_default.py
  • tests/unit/test_cli_push_chunk_concurrency.py
  • tests/unit/test_cli_push_max_connections.py
  • tests/unit/test_cli_push_workers.py
  • tests/unit/test_clone_ergonomics.py
  • tests/unit/test_collection_id.py
  • tests/unit/test_concurrency_defaults.py
  • tests/unit/test_concurrency_flag_issue_323.py
  • tests/unit/test_discover_nested_collections.py
  • tests/unit/test_download.py
  • tests/unit/test_filegdb_add_support.py
  • tests/unit/test_hive_partition_collection_id.py
  • tests/unit/test_issue_252_catalog_json_roundtrip.py
  • tests/unit/test_json_geojson_detection.py
  • tests/unit/test_nested_catalog_inference.py
  • tests/unit/test_partitioning.py
  • tests/unit/test_partitioning_arbitrary.py
  • tests/unit/test_pmtiles.py
  • tests/unit/test_pull.py
  • tests/unit/test_pull_async.py
  • tests/unit/test_pull_parallel.py
  • tests/unit/test_pull_restore.py
  • tests/unit/test_push.py
  • tests/unit/test_push_async.py
  • tests/unit/test_push_catalog_wide.py
  • tests/unit/test_push_default_remote.py
  • tests/unit/test_push_intermediate_catalogs.py
  • tests/unit/test_push_metadata_sync.py
  • tests/unit/test_push_root_files.py
  • tests/unit/test_push_verbose.py
  • tests/unit/test_scan.py
  • tests/unit/test_scan_classify.py
  • tests/unit/test_scan_coverage.py
  • tests/unit/test_scan_detect.py
  • tests/unit/test_scan_edge_cases.py
  • tests/unit/test_scan_filegdb_discovery.py
  • tests/unit/test_scan_fix.py
  • tests/unit/test_scan_fix_coverage.py
  • tests/unit/test_scan_fix_normalization.py
  • tests/unit/test_scan_infer.py
  • tests/unit/test_scan_infer_coverage.py
  • tests/unit/test_scan_nested.py
  • tests/unit/test_scan_output.py
  • tests/unit/test_scan_output_batching.py
  • tests/unit/test_scan_progress.py
  • tests/unit/test_scan_structure.py
  • tests/unit/test_scan_unrecognized_files_property.py
  • tests/unit/test_style.py
  • tests/unit/test_sync.py
  • tests/unit/test_tabular_support.py
  • tests/unit/test_thumbnail.py
  • tests/unit/test_thumbnail_style.py
  • tests/unit/test_upload.py
  • tests/unit/test_upload_progress.py
  • tests/unit/test_validation_rules.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@portolan_cli/sync/checksums.py`:
- Around line 43-91: The compute_dir_checksum function currently hashes only
file metadata, so same-size edits with preserved mtimes are missed. Update it to
incorporate each contained file’s actual bytes into the SHA-256 checksum while
retaining deterministic relative-path handling and recursive traversal; ensure
unreadable files are handled consistently rather than silently producing a valid
content checksum.

In `@tests/integration/test_nested_catalog_clone.py`:
- Around line 90-93: Update all three relevant test call sites to patch
portolan_cli.sync.core.pull rather than portolan_cli.sync.pull, matching the
binding used by portolan_cli.sync.core. Keep the existing mock behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a61b91a-407f-417d-b0f5-d92bfab74b32

📥 Commits

Reviewing files that changed from the base of the PR and between f82dc2e and 2d67ce9.

📒 Files selected for processing (120)
  • portolan_cli/add.py
  • portolan_cli/backends/iceberg/backend.py
  • portolan_cli/backends/json_file.py
  • portolan_cli/cli.py
  • portolan_cli/collection_id.py
  • portolan_cli/convert.py
  • portolan_cli/discovery.py
  • portolan_cli/extract/common/orchestrator_base.py
  • portolan_cli/metadata/fix.py
  • portolan_cli/metadata/scan.py
  • portolan_cli/preparation.py
  • portolan_cli/query.py
  • portolan_cli/scan/__init__.py
  • portolan_cli/scan/check.py
  • portolan_cli/scan/classify.py
  • portolan_cli/scan/core.py
  • portolan_cli/scan/detect.py
  • portolan_cli/scan/fix.py
  • portolan_cli/scan/infer.py
  • portolan_cli/scan/output.py
  • portolan_cli/scan/progress.py
  • portolan_cli/status.py
  • portolan_cli/sync/__init__.py
  • portolan_cli/sync/checksums.py
  • portolan_cli/sync/core.py
  • portolan_cli/sync/download.py
  • portolan_cli/sync/pull.py
  • portolan_cli/sync/push.py
  • portolan_cli/sync/upload.py
  • portolan_cli/sync/upload_progress.py
  • portolan_cli/validation/rules.py
  • portolan_cli/viz/__init__.py
  • portolan_cli/viz/pmtiles.py
  • portolan_cli/viz/pmtiles_links.py
  • portolan_cli/viz/style.py
  • portolan_cli/viz/thumbnail.py
  • portolan_cli/viz/thumbnail_style.py
  • pyproject.toml
  • tests/benchmark/test_scan_performance.py
  • tests/iceberg/unit/test_on_post_add.py
  • tests/iceberg/unit/test_pull.py
  • tests/integration/test_check_conversion_config.py
  • tests/integration/test_clone_ergonomics_integration.py
  • tests/integration/test_cog_reoptimize.py
  • tests/integration/test_concurrency_enforcement.py
  • tests/integration/test_download_integration.py
  • tests/integration/test_nested_catalog_clone.py
  • tests/integration/test_partition_paths.py
  • tests/integration/test_pmtiles_integration.py
  • tests/integration/test_pull_integration.py
  • tests/integration/test_pull_parallel_integration.py
  • tests/integration/test_push_integration.py
  • tests/integration/test_push_parallel_integration.py
  • tests/integration/test_s3_moto.py
  • tests/integration/test_scan_unrecognized_files.py
  • tests/integration/test_sync_integration.py
  • tests/integration/test_thumbnail_workflow.py
  • tests/integration/test_upload_integration.py
  • tests/spec_compliance/test_extensions_doc_parity.py
  • tests/unit/extract/common/test_orchestrator_base.py
  • tests/unit/test_add.py
  • tests/unit/test_asset_role_media.py
  • tests/unit/test_check_remove_legacy.py
  • tests/unit/test_check_workflow.py
  • tests/unit/test_cli_check.py
  • tests/unit/test_cli_profile_default.py
  • tests/unit/test_cli_push_chunk_concurrency.py
  • tests/unit/test_cli_push_max_connections.py
  • tests/unit/test_cli_push_workers.py
  • tests/unit/test_clone_ergonomics.py
  • tests/unit/test_collection_id.py
  • tests/unit/test_concurrency_defaults.py
  • tests/unit/test_concurrency_flag_issue_323.py
  • tests/unit/test_discover_nested_collections.py
  • tests/unit/test_download.py
  • tests/unit/test_filegdb_add_support.py
  • tests/unit/test_hive_partition_collection_id.py
  • tests/unit/test_issue_252_catalog_json_roundtrip.py
  • tests/unit/test_json_geojson_detection.py
  • tests/unit/test_nested_catalog_inference.py
  • tests/unit/test_partitioning.py
  • tests/unit/test_partitioning_arbitrary.py
  • tests/unit/test_pmtiles.py
  • tests/unit/test_pull.py
  • tests/unit/test_pull_async.py
  • tests/unit/test_pull_parallel.py
  • tests/unit/test_pull_restore.py
  • tests/unit/test_push.py
  • tests/unit/test_push_async.py
  • tests/unit/test_push_catalog_wide.py
  • tests/unit/test_push_default_remote.py
  • tests/unit/test_push_intermediate_catalogs.py
  • tests/unit/test_push_metadata_sync.py
  • tests/unit/test_push_root_files.py
  • tests/unit/test_push_verbose.py
  • tests/unit/test_scan.py
  • tests/unit/test_scan_classify.py
  • tests/unit/test_scan_coverage.py
  • tests/unit/test_scan_detect.py
  • tests/unit/test_scan_edge_cases.py
  • tests/unit/test_scan_filegdb_discovery.py
  • tests/unit/test_scan_fix.py
  • tests/unit/test_scan_fix_coverage.py
  • tests/unit/test_scan_fix_normalization.py
  • tests/unit/test_scan_infer.py
  • tests/unit/test_scan_infer_coverage.py
  • tests/unit/test_scan_nested.py
  • tests/unit/test_scan_output.py
  • tests/unit/test_scan_output_batching.py
  • tests/unit/test_scan_progress.py
  • tests/unit/test_scan_structure.py
  • tests/unit/test_scan_unrecognized_files_property.py
  • tests/unit/test_style.py
  • tests/unit/test_sync.py
  • tests/unit/test_tabular_support.py
  • tests/unit/test_thumbnail.py
  • tests/unit/test_thumbnail_style.py
  • tests/unit/test_upload.py
  • tests/unit/test_upload_progress.py
  • tests/unit/test_validation_rules.py
🛑 Comments failed to post (2)
portolan_cli/sync/checksums.py (1)

43-91: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## File outline\n'
ast-grep outline portolan_cli/sync/checksums.py --view expanded || true

printf '\n## Relevant file section\n'
nl -ba portolan_cli/sync/checksums.py | sed -n '1,220p'

printf '\n## Usages of compute_dir_checksum and is_current\n'
rg -n "compute_dir_checksum|is_current\\(" portolan_cli -S

Repository: portolan-sdi/portolan-cli

Length of output: 457


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## checksums.py with line numbers\n'
awk '{printf "%4d  %s\n", NR, $0}' portolan_cli/sync/checksums.py | sed -n '1,220p'

printf '\n## usages\n'
rg -n "compute_dir_checksum|is_current\\(" portolan_cli -S

printf '\n## nearby sync logic\n'
rg -n "checksum|currentness|mtime|fingerprint" portolan_cli/sync -S

Repository: portolan-sdi/portolan-cli

Length of output: 9425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## query.py is_current section\n'
awk '{printf "%4d  %s\n", NR, $0}' portolan_cli/query.py | sed -n '160,290p'

printf '\n## preparation.py usage section\n'
awk '{printf "%4d  %s\n", NR, $0}' portolan_cli/preparation.py | sed -n '170,230p'

Repository: portolan-sdi/portolan-cli

Length of output: 8388


Directory checksum can miss content changes
compute_dir_checksum() is used as asset.sha256 and by is_current(), but it only hashes path/size/mtime. A same-size edit with preserved mtimes will be treated as unchanged, so modified FileGDB directories can be skipped. Hash file contents here, or rename this to a cache-only fingerprint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@portolan_cli/sync/checksums.py` around lines 43 - 91, The
compute_dir_checksum function currently hashes only file metadata, so same-size
edits with preserved mtimes are missed. Update it to incorporate each contained
file’s actual bytes into the SHA-256 checksum while retaining deterministic
relative-path handling and recursive traversal; ensure unreadable files are
handled consistently rather than silently producing a valid content checksum.
tests/integration/test_nested_catalog_clone.py (1)

90-93: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how portolan_cli.sync.core imports the pull function
rg -n 'import.*pull' portolan_cli/sync/core.py

Repository: portolan-sdi/portolan-cli

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== core import ==\n'
sed -n '1,60p' portolan_cli/sync/core.py

printf '\n== test patch sites ==\n'
rg -n 'patch\("portolan_cli\.sync\.pull|patch\("portolan_cli\.sync\.core\.pull|patch\("portolan_cli\.sync\.pull\.pull' tests/integration/test_nested_catalog_clone.py

printf '\n== surrounding test context ==\n'
sed -n '80,240p' tests/integration/test_nested_catalog_clone.py

Repository: portolan-sdi/portolan-cli

Length of output: 7999


Patch portolan_cli.sync.core.pull in these tests portolan_cli.sync.core imports pull directly, so patch("portolan_cli.sync.pull") won’t replace the binding used here. Update the three call sites to patch portolan_cli.sync.core.pull instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/test_nested_catalog_clone.py` around lines 90 - 93, Update
all three relevant test call sites to patch portolan_cli.sync.core.pull rather
than portolan_cli.sync.pull, matching the binding used by
portolan_cli.sync.core. Keep the existing mock behavior unchanged.

Follow-up to the scan/sync/viz grouping. Two categories of test reference broke
because the sync orchestrator now lives in portolan_cli.sync.core:

- 32 sites patched "portolan_cli.sync.pull" — the orchestrator's bound `pull`
  (clone() calls it). After the move that path resolves to the `pull` *submodule*
  instead of the name bound in the orchestrator, so the patch no longer
  intercepted the call. Redirect to "portolan_cli.sync.core.pull". `pull` was the
  only submodule name the orchestrator also re-binds, so it's the only collision
  the mechanical rewrite couldn't disambiguate.
- test_extension_registry used `from portolan_cli import scan_classify` (a
  module-object import, not a dotted path), which the dotted-path rewrite didn't
  catch. Point it at `from portolan_cli.scan import classify`.

Verified: full unit suite (5046 passed) and all scan/check/pull/push/sync/clone
suites green; the two remaining local integration timeouts are pre-existing
slow tests (77s symlink add, loguru-queue duckdb glob) tripping an aggressive
local --timeout, and pass in isolation — not refactor regressions.
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.

refactor: group flat portolan_cli/ root modules into subpackages (sync/, scan/, viz/)

1 participant