igntui 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {igntui-0.2.0 → igntui-0.3.0}/PKG-INFO +1 -1
- {igntui-0.2.0 → igntui-0.3.0}/pyproject.toml +1 -1
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/__init__.py +1 -1
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/cache_cmd.py +22 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/completion_cmd.py +7 -2
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/parser.py +5 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/cache.py +12 -20
- {igntui-0.2.0 → igntui-0.3.0}/LICENSE +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/README.md +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/__main__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/app.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/base.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/generate_cmd.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/list_cmd.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/test_cmd.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/commands/tui_cmd.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/cli/setup.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/client.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/errors.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/rate_limiter.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/request_handler.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/response.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/api/types.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/config.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/managed_block.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/project_config.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/repo_config.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/search.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/core/usage.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/main.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/py.typed +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/actions.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/app.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/curses_setup.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/event_handler.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/lifecycle.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/renderer.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/state.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/tui/updates.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/__init__.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/base_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/base_panel.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/confirm_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/content_panel.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/diff_preview_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/export_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/help_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/save_dialog.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/search_panel.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/selected_panel.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/splash.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/status_bar.py +0 -0
- {igntui-0.2.0 → igntui-0.3.0}/src/igntui/ui/components/templates_panel.py +0 -0
|
@@ -5,7 +5,7 @@ import platform
|
|
|
5
5
|
import sys
|
|
6
6
|
from collections.abc import Callable
|
|
7
7
|
|
|
8
|
-
__version__ = "0.
|
|
8
|
+
__version__ = "0.3.0"
|
|
9
9
|
__author__ = "Mohammad Abu Mattar"
|
|
10
10
|
__email__ = "mohammad.khaled@outlook.com"
|
|
11
11
|
__description__ = """A professional TUI application for generating .gitignore files from templates
|
|
@@ -17,6 +17,11 @@ class CacheCommand(CLICommand):
|
|
|
17
17
|
|
|
18
18
|
clear_parser = subparsers.add_parser("clear", help="Clear cache")
|
|
19
19
|
clear_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
|
|
20
|
+
clear_parser.add_argument(
|
|
21
|
+
"--expired",
|
|
22
|
+
action="store_true",
|
|
23
|
+
help="Remove only entries past their TTL, keeping everything still valid",
|
|
24
|
+
)
|
|
20
25
|
|
|
21
26
|
subparsers.add_parser("stats", help="Show cache statistics")
|
|
22
27
|
subparsers.add_parser("info", help="Show cache information")
|
|
@@ -33,6 +38,8 @@ class CacheCommand(CLICommand):
|
|
|
33
38
|
elif args.cache_action == "stats":
|
|
34
39
|
return self._show_stats(cache)
|
|
35
40
|
elif args.cache_action == "clear":
|
|
41
|
+
if getattr(args, "expired", False):
|
|
42
|
+
return self._clear_expired(cache)
|
|
36
43
|
return self._clear_cache(cache, args.force)
|
|
37
44
|
else:
|
|
38
45
|
print(f"Unknown cache action: {args.cache_action}")
|
|
@@ -85,6 +92,21 @@ class CacheCommand(CLICommand):
|
|
|
85
92
|
|
|
86
93
|
return 0
|
|
87
94
|
|
|
95
|
+
def _clear_expired(self, cache: "CacheManager") -> int:
|
|
96
|
+
"""Sweep only what is past its TTL.
|
|
97
|
+
|
|
98
|
+
Nothing prunes expired files on its own any more: reading every entry at
|
|
99
|
+
startup to find them cost every command ~19 ms over a realistic cache.
|
|
100
|
+
Re-requesting a template set overwrites its stale entry in place, so this
|
|
101
|
+
is for sets that were cached once and never asked for again.
|
|
102
|
+
"""
|
|
103
|
+
removed = cache.cleanup_expired()
|
|
104
|
+
if removed:
|
|
105
|
+
print(f"Removed {removed} expired {'entry' if removed == 1 else 'entries'}")
|
|
106
|
+
else:
|
|
107
|
+
print("No expired entries")
|
|
108
|
+
return 0
|
|
109
|
+
|
|
88
110
|
def _clear_cache(self, cache: "CacheManager", force: bool = False) -> int:
|
|
89
111
|
if not force:
|
|
90
112
|
response = input("Clear cache? This will remove all cached data. (y/N): ")
|
|
@@ -34,7 +34,7 @@ _igntui_complete() {
|
|
|
34
34
|
tui) COMPREPLY=( $(compgen -W "--no-splash" -- "$cur") ); return ;;
|
|
35
35
|
list) COMPREPLY=( $(compgen -W "--filter --count" -- "$cur") ); return ;;
|
|
36
36
|
generate) COMPREPLY=( $(compgen -W "--output --append --force --dry-run --no-sidecar" -- "$cur") ); return ;;
|
|
37
|
-
cache) COMPREPLY=( $(compgen -W "info stats clear --force" -- "$cur") ); return ;;
|
|
37
|
+
cache) COMPREPLY=( $(compgen -W "info stats clear --force --expired" -- "$cur") ); return ;;
|
|
38
38
|
test) COMPREPLY=( $(compgen -W "--timeout" -- "$cur") ); return ;;
|
|
39
39
|
completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ); return ;;
|
|
40
40
|
"") COMPREPLY=( $(compgen -W "$subcommands $global_flags" -- "$cur") ); return ;;
|
|
@@ -73,7 +73,10 @@ _igntui() {
|
|
|
73
73
|
'--force[overwrite without prompt]' \\
|
|
74
74
|
'--dry-run[print without writing]' \\
|
|
75
75
|
'--no-sidecar[skip igntui.cfg.toml]' ;;
|
|
76
|
-
cache)
|
|
76
|
+
cache) _arguments \\
|
|
77
|
+
'1:action:(info stats clear)' \\
|
|
78
|
+
'--force[skip confirmation]' \\
|
|
79
|
+
'--expired[only entries past their TTL]' ;;
|
|
77
80
|
test) _arguments '--timeout[seconds]:seconds:' ;;
|
|
78
81
|
completion) _values 'shell' bash zsh fish ;;
|
|
79
82
|
esac
|
|
@@ -110,6 +113,8 @@ complete -c igntui -n "__fish_seen_subcommand_from generate" -l dry-run -d "Prin
|
|
|
110
113
|
complete -c igntui -n "__fish_seen_subcommand_from generate" -l no-sidecar -d "Skip sidecar"
|
|
111
114
|
complete -c igntui -n "__fish_seen_subcommand_from generate" -l force -d "Overwrite without prompt"
|
|
112
115
|
complete -c igntui -n "__fish_seen_subcommand_from cache" -a "info stats clear"
|
|
116
|
+
complete -c igntui -n "__fish_seen_subcommand_from cache" -l force -d "Skip confirmation"
|
|
117
|
+
complete -c igntui -n "__fish_seen_subcommand_from cache" -l expired -d "Only expired entries"
|
|
113
118
|
complete -c igntui -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
114
119
|
"""
|
|
115
120
|
|
|
@@ -117,6 +117,11 @@ def create_command_parser(
|
|
|
117
117
|
|
|
118
118
|
cache_clear_parser = cache_subparsers.add_parser("clear", help="Clear all cache")
|
|
119
119
|
cache_clear_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
|
|
120
|
+
cache_clear_parser.add_argument(
|
|
121
|
+
"--expired",
|
|
122
|
+
action="store_true",
|
|
123
|
+
help="Remove only entries past their TTL, keeping everything still valid",
|
|
124
|
+
)
|
|
120
125
|
cache_subparsers.add_parser("stats", help="Show cache statistics")
|
|
121
126
|
cache_subparsers.add_parser("info", help="Show cache information")
|
|
122
127
|
|
|
@@ -49,8 +49,19 @@ class CacheManager:
|
|
|
49
49
|
|
|
50
50
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
51
51
|
self._purge_legacy_content_keys()
|
|
52
|
-
self._load_persistent_cache()
|
|
53
52
|
|
|
53
|
+
# Deliberately not reading the cache into memory here. `get()` already
|
|
54
|
+
# falls back to disk and promotes what it finds, so an eager load bought
|
|
55
|
+
# nothing except latency — and every command pays it, because
|
|
56
|
+
# GitIgnoreAPI constructs a manager on the way to doing anything. Over a
|
|
57
|
+
# realistic cache (300 content blobs, ~3.5 MiB) it measured ~19 ms of
|
|
58
|
+
# open + json.loads before the process could start its actual work.
|
|
59
|
+
#
|
|
60
|
+
# The eager pass also deleted expired files. Losing that is nearly free:
|
|
61
|
+
# a content key is a hash of its template set, so re-requesting the same
|
|
62
|
+
# set overwrites the stale entry in place, and `get()` deletes any
|
|
63
|
+
# expired entry it touches. Only sets never asked for again linger, and
|
|
64
|
+
# `igntui cache clear --expired` sweeps those.
|
|
54
65
|
logger.debug("Initialized cache manager with directory: %s", self.cache_dir)
|
|
55
66
|
|
|
56
67
|
def _purge_legacy_content_keys(self) -> None:
|
|
@@ -196,25 +207,6 @@ class CacheManager:
|
|
|
196
207
|
**self._stats,
|
|
197
208
|
}
|
|
198
209
|
|
|
199
|
-
def _load_persistent_cache(self) -> None:
|
|
200
|
-
try:
|
|
201
|
-
loaded_count = 0
|
|
202
|
-
for cache_file in self.cache_dir.glob("*.cache"):
|
|
203
|
-
key = cache_file.stem
|
|
204
|
-
entry = self._load_disk_cache(key)
|
|
205
|
-
|
|
206
|
-
if entry and not entry.is_expired():
|
|
207
|
-
self._memory_cache[key] = entry
|
|
208
|
-
loaded_count += 1
|
|
209
|
-
elif entry:
|
|
210
|
-
cache_file.unlink()
|
|
211
|
-
|
|
212
|
-
if loaded_count > 0:
|
|
213
|
-
logger.info("Loaded %d cache entries from disk", loaded_count)
|
|
214
|
-
|
|
215
|
-
except Exception as e:
|
|
216
|
-
logger.warning("Failed to load persistent cache: %s", e)
|
|
217
|
-
|
|
218
210
|
def _load_disk_cache(self, key: str) -> CacheEntry | None:
|
|
219
211
|
cache_file = self.cache_dir / f"{key}.cache"
|
|
220
212
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|