Skip to content

Conversation

@1cu
Copy link
Contributor

@1cu 1cu commented Nov 24, 2025

ℹ️ Description

Eliminate all blocking I/O operations in async contexts and modernize file path handling by migrating from os.path to pathlib.Path.

  • Link to the related issue(s): none
  • Get rid of the TODO in pyproject.toml

📋 Changes Summary

  • Enable ASYNC210, ASYNC230, ASYNC240, ASYNC250 Ruff rules
  • Wrap blocking urllib.request.urlopen() in run_in_executor
  • Wrap blocking file operations (open, write) in run_in_executor
  • Replace blocking os.path calls with async helpers using run_in_executor
  • Replace blocking input() with await ainput()
  • Migrate extract.py from os.path to pathlib.Path
  • Use Path() constructor and / operator for path joining
  • Use Path.mkdir(), Path.rename() in executor instead of os functions
  • Create mockable _path_exists() and _path_is_dir() helpers
  • Add debug logging for all file system operations

⚙️ Type of Change

Select the type(s) of change(s) included in this pull request:

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (adds new functionality without breaking existing usage)
  • 💥 Breaking change (changes that might break existing user setups, scripts, or configurations)

✅ Checklist

Before requesting a review, confirm the following:

  • I have reviewed my changes to ensure they meet the project's standards.
  • I have tested my changes and ensured that all tests pass (pdm run test).
  • I have formatted the code (pdm run format).
  • I have verified that linting passes (pdm run lint).
  • I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Summary by CodeRabbit

  • Refactor

    • Made user prompt non‑blocking to improve responsiveness.
    • Converted filesystem and path handling to async‑friendly APIs, added async existence checks and async-safe prefs writing.
    • Moved image downloads and file I/O to executor-backed background operations for more reliable saving and logging; added async port‑check integration.
  • Tests

    • Expanded unit tests for path helpers, image download success/failure, prefs writing, and directory creation/renaming workflows.

✏️ Tip: You can customize this high-level summary in your review settings.

Eliminate all blocking I/O operations in async contexts and modernize file
path handling by migrating from os.path to pathlib.Path.

BREAKING CHANGE: None - internal refactoring only

- Enable ASYNC210, ASYNC230, ASYNC240, ASYNC250 Ruff rules
- Wrap blocking urllib.request.urlopen() in run_in_executor
- Wrap blocking file operations (open, write) in run_in_executor
- Replace blocking os.path calls with async helpers using run_in_executor
- Replace blocking input() with await ainput()

- Migrate extract.py from os.path to pathlib.Path
- Use Path() constructor and / operator for path joining
- Use Path.mkdir(), Path.rename() in executor instead of os functions
- Create mockable _path_exists() and _path_is_dir() helpers
- Add debug logging for all file system operations
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

Replaced a blocking console input with an async prompt; introduced Path-based and async-friendly filesystem helpers; offloaded blocking network and file I/O to executors with a new sync image-download helper; updated browser-session prefs and port checks to use async/executor semantics; and revised unit tests to use Path and async-friendly flows.

Changes

Cohort / File(s) Summary
Async input update
src/kleinanzeigen_bot/__init__.py
Replaced blocking input(...) with await ainput(...) in the payment prompt flow (async control-flow change).
Ad extraction: Path & async FS helpers
src/kleinanzeigen_bot/extract.py
Added _path_exists, _path_is_dir (sync), and async wrappers _exists, _isdir; migrated string/os path usage to pathlib.Path; refactored directory create/rename/delete to run in executors; updated _extract_ad_page_info_with_directory_handling to accept/return Path.
Image download helper & executor usage
src/kleinanzeigen_bot/extract.py
Added static sync method _download_and_save_image_sync(...) and changed image download flow to call it via run_in_executor, collecting saved image names and handling failures.
Browser session async checks & prefs write
src/kleinanzeigen_bot/utils/web_scraping_mixin.py
Added _write_initial_prefs(...) and async _exists(...); replaced os.path.exists and inline prefs JSON writes with async/executor-backed checks and prefs creation; integrated port-availability retry before browser connection.
Unit tests: Path & async updates
tests/unit/test_extract.py, tests/unit/test_web_scraping_mixin.py, tests/unit/test_init.py
Added tests for new path helpers and _download_and_save_image_sync; adapted tests to use Path APIs, async-friendly assertions, and updated expectations for directory creation/rename/delete and prefs writing; minor change to config file creation in test_init.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Init as __init__.py
    participant Extract as AdExtractor
    participant Executor as Event Loop Executor
    participant Disk as Filesystem
    participant Net as Network

    App->>Init: publish_ad()
    activate Init
    Init->>App: await ainput() 
    note right of Init `#DFF2E0`: Non-blocking user prompt
    Init-->>App: payment response
    deactivate Init

    App->>Extract: _extract_ad_page_info_with_directory_handling(Path, ad_id)
    activate Extract
    Extract->>Extract: await _exists/_isdir
    rect rgb(235,245,255)
      loop for each image
        Extract->>Executor: run_in_executor(_download_and_save_image_sync, url, dir, prefix, nr)
        Executor->>Net: HTTP GET image
        Net-->>Executor: image bytes / error
        Executor->>Disk: write file (sync)
        Disk-->>Executor: saved path / error
        Executor-->>Extract: saved path or None
      end
    end
    Extract->>Executor: run_in_executor(mkdir/rename/rmtree)
    Executor->>Disk: perform filesystem ops
    Disk-->>Executor: result
    Executor-->>Extract: done
    Extract-->>App: return (AdPartial, Path)
    deactivate Extract
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~30–90 minutes

  • Review executor usage and exception handling around run_in_executor calls.
  • Verify correct and consistent conversion between Path and str at all call sites (especially external callers).
  • Inspect _download_and_save_image_sync for robust error handling, temp-file safety, and correct return values.
  • Validate async file-existence helpers and _write_initial_prefs behavior across platforms and encoding.

Suggested reviewers

  • Heavenfighter
  • sebthom

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.97% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: eliminating async safety violations and migrating to pathlib. It directly aligns with the PR objectives and changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/async-safety-pathlib

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d84639 and ac12b3d.

⛔ Files ignored due to path filters (1)
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (3)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/extract.py (6 hunks)
  • tests/unit/test_extract.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/extract.py
  • src/kleinanzeigen_bot/__init__.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_extract.py
🧬 Code graph analysis (2)
src/kleinanzeigen_bot/extract.py (2)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1)
  • _exists (134-135)
src/kleinanzeigen_bot/utils/dicts.py (1)
  • save_dict (114-132)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/misc.py (1)
  • ainput (121-122)
🪛 Ruff (0.14.5)
src/kleinanzeigen_bot/extract.py

74-74: Unused noqa directive (non-enabled: ASYNC240)

Remove unused noqa directive

(RUF100)

tests/unit/test_extract.py

74-74: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


90-90: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


112-112: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


128-128: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


149-149: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build (macos-latest, 3.10, false)
  • GitHub Check: build (ubuntu-latest, 3.14, true)
  • GitHub Check: build (macos-latest, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: analyze
  • GitHub Check: analyze
🔇 Additional comments (6)
src/kleinanzeigen_bot/__init__.py (1)

939-942: Async prompt + translation usage look correct

Using await ainput(_("Press a key to continue...")) here is async‑safe and correctly goes through the translation system; no further changes needed in this block.

tests/unit/test_extract.py (2)

972-1011: download_ad test correctly asserts Path-based behavior

This test nicely drives download_ad through the async path:

  • Uses Path to build final_dir/yaml_path, keeping expectations OS‑agnostic.
  • Mocks Path.mkdir, _extract_ad_page_info_with_directory_handling, and dicts.save_dict, and then asserts on the observable save_dict call and final path.

No changes needed here.


1012-1211: Directory-handling tests accurately cover existing/rename/reuse flows

The three _extract_ad_page_info_with_directory_handling tests validate the important cases:

  • Existing final_dir is deleted and recreated.
  • Existing temp dir is renamed to title‑suffixed dir when rename_existing_folders is True.
  • Existing temp dir is reused as‑is when rename_existing_folders is False.

They use tmp_path/Path correctly and assert on real filesystem effects (dirs/files exist or not), which keeps behavior‑focused and platform‑neutral. No adjustments needed.

src/kleinanzeigen_bot/extract.py (3)

31-50: Async path helpers are correct and testable

_path_exists / _path_is_dir plus the async wrappers _exists / _isdir use Path consistently, execute in an executor, and add debug logging, which keeps filesystem checks async‑safe and easy to mock. No changes needed here.


90-156: Image download helper and executor usage look solid

The synchronous _download_and_save_image_sync:

  • Uses urllib.request.urlopen and mimetypes.guess_extension appropriately.
  • Builds img_path via Path(directory) / ..., and returns str(img_path) to keep callers decoupled from Path.

_download_images_from_ad_page:

  • Reuses a single event‑loop instance, runs the sync helper in an executor, skips None URLs, and stores only Path(img_path).name for portability.
  • Handles missing image areas via TimeoutError and logs a clear warning.

This is consistent with the async‑safety and Path‑migration goals; no further changes needed here.


404-456: Directory handling with Path and executors is correct and readable

_extract_ad_page_info_with_directory_handling:

  • Accepts a Path for relative_directory and returns a Path final_dir, matching the tests.
  • Uses async _exists plus executor‑backed shutil.rmtree, Path.rename, and Path.mkdir to keep all filesystem changes off the event loop.
  • Cleanly implements the three behaviors: delete existing titled dir, rename temp dir when configured, or reuse/create as appropriate.

Then it delegates to _extract_ad_page_info(str(final_dir), ad_id), which keeps downstream code unchanged.

This is a straightforward, maintainable implementation; no changes needed.


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 and usage tips.

@1cu 1cu changed the title refactor(core): eliminate async safety violations and migrate to pathlib fix: eliminate async safety violations and migrate to pathlib Nov 24, 2025
@github-actions github-actions bot added the bug Something isn't working label Nov 24, 2025
@codecov
Copy link

codecov bot commented Nov 24, 2025

Codecov Report

❌ Patch coverage is 98.33333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 86.40%. Comparing base (651c894) to head (ac12b3d).

Files with missing lines Patch % Lines
src/kleinanzeigen_bot/extract.py 98.03% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #697      +/-   ##
==========================================
+ Coverage   84.24%   86.40%   +2.15%     
==========================================
  Files          20       20              
  Lines        2019     2052      +33     
  Branches      393      393              
==========================================
+ Hits         1701     1773      +72     
+ Misses        182      142      -40     
- Partials      136      137       +1     
Flag Coverage Δ
integration-tests 30.36% <26.66%> (+0.14%) ⬆️
smoke-tests 32.06% <20.00%> (+0.02%) ⬆️
unit-tests 86.30% <98.33%> (+2.15%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/kleinanzeigen_bot/utils/web_scraping_mixin.py 92.33% <100.00%> (+0.05%) ⬆️
src/kleinanzeigen_bot/extract.py 85.28% <98.03%> (+12.80%) ⬆️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 651c894...ac12b3d. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unit/test_extract.py (1)

970-1024: Reduce implementation‑specific assertions in download_ad tests

The updated download_ad tests correctly mock _path_exists/_path_is_dir and _extract_ad_page_info_with_directory_handling, and they already assert on the computed YAML path and the dict passed to save_dict (the observable behavior that matters).

The additional assertions around Path.mkdir / Path.rename / shutil.rmtree call counts are tightly coupled to the current internal implementation and will make future refactors of directory handling harder (especially since directory logic is now explicitly pushed down into _extract_ad_page_info_with_directory_handling).

Consider simplifying these tests by:

  • Keeping the assertions on mock_extract_with_dir and mock_save_dict (behavioral contract).
  • Dropping or greatly relaxing the direct assert_not_called / call_count checks on mock_mkdir, mock_rename, and mock_rmtree, unless you specifically want to pin down this internal behavior.

That keeps the tests aligned with “test observable behavior, not implementation” and makes them more robust to future path‑handling refactors.

Also applies to: 1025-1078, 1080-1132, 1134-1191

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 651c894 and aafde54.

⛔ Files ignored due to path filters (2)
  • pyproject.toml is excluded by none and included by none
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (6)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/extract.py (7 hunks)
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py (3 hunks)
  • tests/unit/test_extract.py (10 hunks)
  • tests/unit/test_init.py (1 hunks)
  • tests/unit/test_web_scraping_mixin.py (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_web_scraping_mixin.py
  • tests/unit/test_extract.py
  • tests/unit/test_init.py
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
  • src/kleinanzeigen_bot/extract.py
🧬 Code graph analysis (4)
tests/unit/test_extract.py (1)
src/kleinanzeigen_bot/extract.py (6)
  • _path_exists (30-32)
  • _path_is_dir (35-37)
  • _exists (40-43)
  • _isdir (46-49)
  • AdExtractor (52-674)
  • _download_and_save_image_sync (91-102)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/misc.py (1)
  • ainput (121-122)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (2)
src/kleinanzeigen_bot/extract.py (1)
  • _exists (40-43)
src/kleinanzeigen_bot/utils/misc.py (1)
  • ensure (19-49)
src/kleinanzeigen_bot/extract.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1)
  • _exists (134-135)
🪛 Ruff (0.14.5)
tests/unit/test_web_scraping_mixin.py

101-101: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)

tests/unit/test_extract.py

69-69: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)


72-72: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


85-85: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)


88-88: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


108-108: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)


110-110: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


124-124: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)


126-126: Unused noqa directive (non-enabled: PLC0415, PLC2701)

Remove unused noqa directive

(RUF100)


145-145: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)


147-147: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


173-173: Dynamically typed expressions (typing.Any) are disallowed in tmp_path

(ANN401)

src/kleinanzeigen_bot/__init__.py

940-940: Unused noqa directive (non-enabled: ASYNC240)

Remove unused noqa directive

(RUF100)

src/kleinanzeigen_bot/extract.py

41-41: Unused noqa directive (non-enabled: ASYNC240)

Remove unused noqa directive

(RUF100)


47-47: Unused noqa directive (non-enabled: ASYNC240)

Remove unused noqa directive

(RUF100)


100-100: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build (ubuntu-latest, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (macos-latest, 3.10, false)
🔇 Additional comments (3)
tests/unit/test_init.py (1)

641-652: Path-based config write in test_verify_command looks good

Using Path(tmp_path) / "config.yaml" with write_text(..., encoding="utf-8") is clear, encoding-safe, and aligns with the move toward pathlib-based IO. No issues found.

src/kleinanzeigen_bot/__init__.py (1)

1108-1112: Formatting-only change in __set_shipping is fine

The wrapped web_find call keeps the same arguments and control flow; no behavioral change or async-safety concern here.

tests/unit/test_web_scraping_mixin.py (1)

10-10: Path/async adjustments in browser prefs and extensions tests look good

  • Adding import asyncio and using run_in_executor in test_browser_extension_loading to check Path(ext_path).exists / .is_dir keeps those filesystem checks off the event loop and aligns with the async-safety pattern used in the main code.
  • Switching to Path.write_text / read_text with explicit encodings for the preferences and session state files (prefs_file and state_file) simplifies the tests and mirrors the pathlib-based IO in the src modules.

These changes are straightforward, English-only, and keep the tests simple and maintainable.

Also applies to: 790-812, 919-921, 1001-1001, 1013-1014

@1cu 1cu force-pushed the refactor/async-safety-pathlib branch from 2acb8f4 to 0d84639 Compare November 24, 2025 16:27
Coverage improvements:
- Add tests for image download with None URLs (lines 124-136)
- Add tests for directory handling scenarios (lines 420-454)
  - Test deletion when final_dir exists
  - Test renaming when rename_existing_folders=True
  - Test using existing directory when renaming disabled
- Simplify download_ad tests to focus on observable behavior
- Improve type annotations (tmp_path: Path instead of Any)
- Coverage increased to 87.70%

Async safety improvements:
- Remove unused # noqa: ASYNC240 from _exists, _isdir, ainput
- Eliminate TOCTOU race in directory creation using mkdir(exist_ok=True)
- Offload dicts.save_dict to executor (blocking I/O)

Path handling improvements:
- Migrate to pathlib.Path for OS-agnostic file operations
- Replace string concatenation with Path operations
- Use Path.name instead of rsplit for filename extraction

Exception handling improvements:
- Narrow exception handling in _download_and_save_image_sync
- Catch only expected errors (URLError, HTTPError, OSError, shutil.Error)
- Don't swallow programming errors

Translation updates:
- Update German translation for directory creation message
@1cu 1cu force-pushed the refactor/async-safety-pathlib branch from 0d84639 to ac12b3d Compare November 24, 2025 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Development

Successfully merging this pull request may close these issues.

2 participants