Generated: 2026-02-12
Scope: tensors/ (all modules) + tests/
11 issues found: 3 high priority, 5 medium priority, 3 low priority.
- Location:
api.py:38-108 - Problem:
fetch_civitai_model_version,fetch_civitai_model, andfetch_civitai_by_hashshare nearly identical try/except/error-handling blocks (3x duplication). - Impact: Changing error handling or adding retry logic requires editing 3+ places. Easy to introduce inconsistencies (e.g.,
fetch_civitai_model_versionlacks the Progress spinner that the other two have). - Action: Extract a
_api_get(url, api_key, console, spinner_msg=None) -> dict | Nonehelper that handles GET, 404 check, error printing, and optional Progress spinner. Each fetch function becomes a one-liner calling this helper.
- Location:
cli.py:67 - Problem:
console = Console()at module level is used by 6 helper functions (_output_info_json,_save_metadata,_resolve_by_hash,_resolve_by_model_id,_prepare_download_dir,_display_download_info) via closure rather than parameter passing. - Impact: Impossible to inject a test console into helpers, prevents output capture in tests, creates hidden coupling. Commands pass
consoleto API/display functions but helpers silently use the global. - Action: Add
console: Consoleparameter to all helper functions. Pass the module-level console from command functions. This makes the dependency explicit and testable.
- Location:
pyproject.toml:8 - Problem:
safetensors>=0.4.0is listed as a runtime dependency but never imported anywhere. The code does manual binary parsing insafetensor.py. - Impact: Unnecessary install bloat. Users install a native extension library they don't need. Confusing for contributors.
- Action: Remove
safetensorsfrom[project.dependencies]since manual parsing is the intended approach.
- Location:
cli.py:126-133andcli.py:154-161 - Problem:
_output_info_jsonand_save_metadataconstruct the same dict structure independently. - Impact: Adding a new field to info output requires editing two places.
- Action: Extract
_build_info_dict(file_path, sha256_hash, local_metadata, civitai_data) -> dictand call it from both functions.
- Location:
api.py:43,70,97,175andsafetensor.py:67 - Problem:
timeout=30.0appears 4 times in api.py. Chunk sizes (1024 * 1024in api.py,1024 * 1024 * 8in safetensor.py) are inline magic numbers. - Impact: Changing timeout or chunk size requires hunting through code.
- Action: Add
API_TIMEOUT = 30.0,DOWNLOAD_CHUNK_SIZE = 1024 * 1024to config.py or at module top. AddHASH_CHUNK_SIZE = 1024 * 1024 * 8to safetensor.py constants section.
- Location:
display.py:23,32 - Problem:
_format_sizeis imported and used bycli.py:36as a cross-module API but has a private underscore prefix. Same for_format_countwhich could be useful externally. - Impact: Violates Python naming convention. Underscore-prefixed names signal "don't import this" but the codebase does.
- Action: Rename to
format_sizeandformat_count. Update all imports.
- Location:
api.py:61-66,88-93,166-171 - Problem: The
Progress(SpinnerColumn(), TextColumn(...), console=..., transient=True)pattern is repeated 3 times with identical configuration. - Impact: Changing spinner appearance requires 3 edits.
- Action: Extract
_spinner(console, description)context manager helper or a factory function. This pairs well with the HTTP helper extraction in High Priority #1.
- Location:
tests/test_tensors.py:284,291,298,... - Problem:
Console(force_terminal=True, width=80)is created ~15 times across test methods. - Impact: Changing test console config requires editing every test.
- Action: Add a
consolefixture toconftest.pyand use it across all test classes.
- Location:
cli.py:403 - Problem:
arg not in ("info", "search", "get", "dl", "download", "config")is a manually maintained list of known commands for legacy invocation detection. - Impact: Adding a new command requires remembering to update this list, or legacy mode will swallow it.
- Action: Derive the command list from
app.registered_commandsor Typer's command registry dynamically.
- Location:
tests/test_tensors.py:279-385 - Problem: Most display tests only assert "should not raise" with no output verification.
- Impact: Tests won't catch regressions in output format or content.
- Action: Use
Console(record=True)to capture output, then assert key strings appear inconsole.export_text().
- Location:
api.py:111-142,145-150,187-199,202-209 - Problem:
_build_search_params,_filter_results,_setup_resume,_get_dest_from_responsehave no test coverage. These are pure/near-pure functions that are easy to test. - Impact: Logic errors in search parameter building or resume setup won't be caught.
- Action: Add direct unit tests for each function. These don't need HTTP mocking since they're pure logic.
- High Priority #1 (HTTP helper) and Medium #6 (spinner dedup) should be done together since the extracted helper will naturally absorb the spinner logic.
- High Priority #2 (console singleton) should be done before Medium #7 (test fixture) since making console a parameter enables proper test injection.
- Medium #4 (output dict) is a quick win that can be done independently.
- Start with High Priority #3 (remove unused dep) as it's the simplest and lowest risk change.