-
Notifications
You must be signed in to change notification settings - Fork 0
Dev #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev #232
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,9 @@ | ||
| ## v2.2.0 (2025-12-12) | ||
|
|
||
| ### Feat | ||
|
|
||
| - **cache**: add cache utilities and tests | ||
|
|
||
| ## v2.1.0 (2025-12-01) | ||
|
|
||
| ### Feat | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||
| import tempfile | ||||||||||||||||||||||||||||||||
| import shutil | ||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||
| from typing import Any, Optional | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| from .. import config as fnconfig | ||||||||||||||||||||||||||||||||
| from .files import write_json_secure | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def get_cache_dir(subdir: str = "cache") -> Path: | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| Return (and ensure) a cache directory under the funcnodes config dir. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||
| subdir: Subdirectory name under the config dir. | ||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||
| cache_dir = fnconfig.get_config_dir() / subdir | ||||||||||||||||||||||||||||||||
| cache_dir.mkdir(parents=True, exist_ok=True) | ||||||||||||||||||||||||||||||||
| return cache_dir | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def get_cache_path(filename: str, subdir: str = "cache") -> Path: | ||||||||||||||||||||||||||||||||
| """Return full path for a cache file within the cache dir.""" | ||||||||||||||||||||||||||||||||
| return get_cache_dir(subdir) / filename | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def get_cache_meta_path_for(cache_path: Path) -> Path: | ||||||||||||||||||||||||||||||||
| """Return a sidecar meta file path next to a cache file.""" | ||||||||||||||||||||||||||||||||
| return cache_path.with_suffix(cache_path.suffix + ".meta.json") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def get_cache_meta_for(cache_path: Path) -> Optional[dict[str, Any]]: | ||||||||||||||||||||||||||||||||
| """Read JSON metadata for a given cache file, if present.""" | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| """Read JSON metadata for a given cache file, if present.""" | |
| """ | |
| Read JSON metadata for a given cache file, if present. | |
| Args: | |
| cache_path: Path to the cache file whose metadata should be read. | |
| """ |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type annotation for the meta parameter is dict[str, Any], but the function actually accepts any type as demonstrated in the test where a string "hello" is passed. The type annotation should be Any to match the actual behavior, or the function should validate that only dict types are accepted.
| def set_cache_meta_for(cache_path: Path, meta: dict[str, Any]): | |
| def set_cache_meta_for(cache_path: Path, meta: Any): |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring is missing documentation for the meta parameter. According to best practices, all function parameters should be documented, especially for public API functions.
| """Write JSON metadata for a given cache file (atomic).""" | |
| """ | |
| Write JSON metadata for a given cache file (atomic). | |
| Args: | |
| cache_path: Path to the cache file. | |
| meta: Metadata dictionary to write as JSON. | |
| """ |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring is missing documentation for parameters (cache_path, text, encoding) and lacks a description of the return value or behavior. According to best practices, all function parameters should be documented, especially for public API functions.
| Write text to cache_path using a temp file + atomic replace. | |
| This avoids partially-written cache files if the process crashes mid-write. | |
| Write text to a cache file atomically. | |
| This function writes the given text to the specified cache_path using a temporary file and atomic replace, | |
| which avoids partially-written cache files if the process crashes mid-write. | |
| Args: | |
| cache_path (Path): The path to the cache file to write. | |
| text (str): The text content to write to the cache file. | |
| encoding (str, optional): The encoding to use when writing the file. Defaults to "utf-8". | |
| Returns: | |
| None |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | ||||||
| import funcnodes_core as fn | ||||||
| from pytest_funcnodes import funcnodes_test | ||||||
|
|
||||||
|
|
||||||
| @funcnodes_test | ||||||
| def test_get_cache_dir_creates_under_config_dir(): | ||||||
| from funcnodes_core.utils.cache import get_cache_dir | ||||||
|
|
||||||
| cache_dir = get_cache_dir() | ||||||
| assert cache_dir.exists() | ||||||
| assert cache_dir.is_dir() | ||||||
| assert fn.config.get_config_dir() in cache_dir.parents | ||||||
|
|
||||||
|
|
||||||
| @funcnodes_test | ||||||
| def test_cache_meta_roundtrip(): | ||||||
| from funcnodes_core.utils.cache import ( | ||||||
| get_cache_path, | ||||||
| get_cache_meta_for, | ||||||
| set_cache_meta_for, | ||||||
| ) | ||||||
|
|
||||||
| cache_path = get_cache_path("example.txt") | ||||||
| meta = {"foo": "bar", "num": 1} | ||||||
| set_cache_meta_for(cache_path, meta) | ||||||
|
|
||||||
| loaded = get_cache_meta_for(cache_path) | ||||||
| assert loaded == meta | ||||||
|
|
||||||
|
|
||||||
| @funcnodes_test | ||||||
| def test_write_cache_text_writes_file(): | ||||||
| from funcnodes_core.utils.cache import get_cache_path, write_cache_text | ||||||
|
|
||||||
| cache_path = get_cache_path("example.txt") | ||||||
| write_cache_text(cache_path, "hello") | ||||||
|
|
||||||
| assert cache_path.exists() | ||||||
| assert cache_path.read_text(encoding="utf-8") == "hello" | ||||||
|
|
||||||
|
|
||||||
| @funcnodes_test | ||||||
| def test_clear_cache_clears_cache(): | ||||||
| from funcnodes_core.utils.cache import clear_cache, get_cache_dir, write_cache_text | ||||||
|
|
||||||
| # check that it is empty | ||||||
| assert len(list(get_cache_dir().glob("*"))) == 0 | ||||||
| # write a file to the cache | ||||||
| write_cache_text(get_cache_dir() / "test.txt", "hello") | ||||||
| assert (get_cache_dir() / "test.txt").exists() | ||||||
| # check that it is not empty | ||||||
| assert len(list(get_cache_dir().glob("*"))) == 1 | ||||||
| # clear the cache | ||||||
| clear_cache() | ||||||
|
|
||||||
| # check that it is empty | ||||||
| assert get_cache_dir().exists() | ||||||
| assert len(list(get_cache_dir().glob("*"))) == 0 | ||||||
|
|
||||||
|
|
||||||
| @funcnodes_test | ||||||
| def test_chache_meta_exception_handling(): | ||||||
|
||||||
| def test_chache_meta_exception_handling(): | |
| def test_cache_meta_exception_handling(): |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type annotation is
Optional[dict[str, Any]], but the function can return any type that was stored viaset_cache_meta_for. As shown in the test, a string "hello" can be stored and retrieved. The return type should beOptional[Any]to accurately reflect the actual behavior.