igntui 0.3.0__tar.gz → 0.4.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.3.0 → igntui-0.4.0}/PKG-INFO +4 -3
- igntui-0.4.0/pyproject.toml +149 -0
- igntui-0.3.0/pyproject.toml → igntui-0.4.0/pyproject.toml.orig +7 -3
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/__init__.py +1 -1
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/cache.py +25 -1
- {igntui-0.3.0 → igntui-0.4.0}/LICENSE +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/README.md +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/__main__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/app.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/base.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/cache_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/completion_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/generate_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/list_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/test_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/commands/tui_cmd.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/parser.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/cli/setup.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/client.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/errors.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/rate_limiter.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/request_handler.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/response.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/api/types.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/config.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/managed_block.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/project_config.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/repo_config.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/search.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/core/usage.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/main.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/py.typed +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/actions.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/app.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/curses_setup.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/event_handler.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/lifecycle.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/renderer.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/state.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/tui/updates.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/__init__.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/base_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/base_panel.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/confirm_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/content_panel.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/diff_preview_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/export_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/help_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/save_dialog.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/search_panel.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/selected_panel.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/splash.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/status_bar.py +0 -0
- {igntui-0.3.0 → igntui-0.4.0}/src/igntui/ui/components/templates_panel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: igntui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Interactive Terminal User Interface for GitIgnore template generation
|
|
5
5
|
Keywords: gitignore,tui,terminal,git,templates,cli
|
|
6
6
|
Author: Mohammad Abu Mattar
|
|
@@ -15,11 +15,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
20
|
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
20
21
|
Classifier: Topic :: Terminals
|
|
21
22
|
Classifier: Topic :: Utilities
|
|
22
|
-
Requires-Dist: windows-curses>=2.4.
|
|
23
|
+
Requires-Dist: windows-curses>=2.4.2 ; sys_platform == 'win32'
|
|
23
24
|
Requires-Dist: pyfiglet>=1.0.2
|
|
24
25
|
Requires-Dist: tomli-w>=1.0.0
|
|
25
26
|
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
@@ -30,7 +31,7 @@ Requires-Dist: pytest>=7.0.0 ; extra == 'test'
|
|
|
30
31
|
Requires-Dist: pytest-cov>=4.0.0 ; extra == 'test'
|
|
31
32
|
Requires-Dist: pytest-mock>=3.10.0 ; extra == 'test'
|
|
32
33
|
Requires-Dist: freezegun>=1.4.0 ; extra == 'test'
|
|
33
|
-
Requires-Dist: windows-curses>=2.4.
|
|
34
|
+
Requires-Dist: windows-curses>=2.4.2 ; sys_platform == 'win32' and extra == 'windows'
|
|
34
35
|
Maintainer: Mohammad Abu Mattar
|
|
35
36
|
Maintainer-email: Mohammad Abu Mattar <mohammad.khaled@outlook.com>
|
|
36
37
|
Requires-Python: >=3.11
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "igntui"
|
|
3
|
+
version = "0.4.0"
|
|
4
|
+
description = "Interactive Terminal User Interface for GitIgnore template generation"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "GPL-3.0-or-later"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"windows-curses>=2.4.2; sys_platform == 'win32'",
|
|
11
|
+
"pyfiglet>=1.0.2",
|
|
12
|
+
"tomli-w>=1.0.0",
|
|
13
|
+
]
|
|
14
|
+
keywords = [
|
|
15
|
+
"gitignore",
|
|
16
|
+
"tui",
|
|
17
|
+
"terminal",
|
|
18
|
+
"git",
|
|
19
|
+
"templates",
|
|
20
|
+
"cli",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Environment :: Console :: Curses",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
34
|
+
"Topic :: Terminals",
|
|
35
|
+
"Topic :: Utilities",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[[project.authors]]
|
|
39
|
+
name = "Mohammad Abu Mattar"
|
|
40
|
+
email = "mohammad.khaled@outlook.com"
|
|
41
|
+
|
|
42
|
+
[[project.maintainers]]
|
|
43
|
+
name = "Mohammad Abu Mattar"
|
|
44
|
+
email = "mohammad.khaled@outlook.com"
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/MKAbuMattar/igntui"
|
|
48
|
+
Repository = "https://github.com/MKAbuMattar/igntui"
|
|
49
|
+
Documentation = "https://github.com/MKAbuMattar/igntui/blob/main/README.md"
|
|
50
|
+
Issues = "https://github.com/MKAbuMattar/igntui/issues"
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
igntui = "igntui.main:cli_main"
|
|
54
|
+
gitignore-tui = "igntui.main:tui_main"
|
|
55
|
+
|
|
56
|
+
[project.optional-dependencies]
|
|
57
|
+
dev = [
|
|
58
|
+
"pytest>=7.0.0",
|
|
59
|
+
"pytest-cov>=4.0.0",
|
|
60
|
+
"pytest-mock>=3.10.0",
|
|
61
|
+
"ruff>=0.1.0",
|
|
62
|
+
]
|
|
63
|
+
test = [
|
|
64
|
+
"pytest>=7.0.0",
|
|
65
|
+
"pytest-cov>=4.0.0",
|
|
66
|
+
"pytest-mock>=3.10.0",
|
|
67
|
+
"freezegun>=1.4.0",
|
|
68
|
+
]
|
|
69
|
+
windows = ["windows-curses>=2.4.2; sys_platform == 'win32'"]
|
|
70
|
+
|
|
71
|
+
[build-system]
|
|
72
|
+
requires = ["uv_build>=0.9.5,<0.13.0"]
|
|
73
|
+
build-backend = "uv_build"
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
line-length = 100
|
|
77
|
+
target-version = "py311"
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = [
|
|
81
|
+
"E",
|
|
82
|
+
"W",
|
|
83
|
+
"F",
|
|
84
|
+
"I",
|
|
85
|
+
"B",
|
|
86
|
+
"C4",
|
|
87
|
+
"UP",
|
|
88
|
+
]
|
|
89
|
+
ignore = [
|
|
90
|
+
"E501",
|
|
91
|
+
"B008",
|
|
92
|
+
"C901",
|
|
93
|
+
"E402",
|
|
94
|
+
"W291",
|
|
95
|
+
"W293",
|
|
96
|
+
"C408",
|
|
97
|
+
"C414",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.per-file-ignores]
|
|
101
|
+
"__init__.py" = ["F401"]
|
|
102
|
+
|
|
103
|
+
[tool.mypy]
|
|
104
|
+
python_version = "3.11"
|
|
105
|
+
warn_return_any = true
|
|
106
|
+
warn_unused_configs = true
|
|
107
|
+
disallow_untyped_defs = false
|
|
108
|
+
ignore_missing_imports = true
|
|
109
|
+
|
|
110
|
+
[[tool.mypy.overrides]]
|
|
111
|
+
module = [
|
|
112
|
+
"curses",
|
|
113
|
+
"curses.*",
|
|
114
|
+
"windows_curses",
|
|
115
|
+
"windows_curses.*",
|
|
116
|
+
]
|
|
117
|
+
ignore_missing_imports = true
|
|
118
|
+
ignore_errors = true
|
|
119
|
+
|
|
120
|
+
[tool.ty.environment]
|
|
121
|
+
python-version = "3.11"
|
|
122
|
+
|
|
123
|
+
[tool.ty.src]
|
|
124
|
+
include = ["src/igntui"]
|
|
125
|
+
|
|
126
|
+
[tool.pytest.ini_options]
|
|
127
|
+
testpaths = ["tests"]
|
|
128
|
+
python_files = ["test_*.py"]
|
|
129
|
+
addopts = "-v --strict-markers"
|
|
130
|
+
markers = ["integration: tests that hit the network or filesystem extensively"]
|
|
131
|
+
|
|
132
|
+
[tool.coverage.run]
|
|
133
|
+
source = ["src/igntui"]
|
|
134
|
+
omit = [
|
|
135
|
+
"*/tests/*",
|
|
136
|
+
"*/__main__.py",
|
|
137
|
+
"*/tui/curses_setup.py",
|
|
138
|
+
"*/tui/renderer.py",
|
|
139
|
+
"*/ui/*",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
[tool.coverage.report]
|
|
143
|
+
exclude_lines = [
|
|
144
|
+
"pragma: no cover",
|
|
145
|
+
"raise NotImplementedError",
|
|
146
|
+
"if __name__ == .__main__.:",
|
|
147
|
+
"if TYPE_CHECKING:",
|
|
148
|
+
]
|
|
149
|
+
show_missing = true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "igntui"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Interactive Terminal User Interface for GitIgnore template generation"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "GPL-3.0-or-later"
|
|
@@ -16,7 +16,10 @@ maintainers = [
|
|
|
16
16
|
]
|
|
17
17
|
requires-python = ">=3.11"
|
|
18
18
|
dependencies = [
|
|
19
|
-
|
|
19
|
+
# 2.4.2 is the first release with cp314 wheels. The floor matters on
|
|
20
|
+
# Windows: an older pin can resolve to a version with no wheel for the
|
|
21
|
+
# running interpreter, which fails at install time rather than degrading.
|
|
22
|
+
"windows-curses>=2.4.2; sys_platform == 'win32'",
|
|
20
23
|
"pyfiglet>=1.0.2",
|
|
21
24
|
"tomli-w>=1.0.0",
|
|
22
25
|
]
|
|
@@ -30,6 +33,7 @@ classifiers = [
|
|
|
30
33
|
"Programming Language :: Python :: 3.11",
|
|
31
34
|
"Programming Language :: Python :: 3.12",
|
|
32
35
|
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Programming Language :: Python :: 3.14",
|
|
33
37
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
38
|
"Topic :: Software Development :: Version Control :: Git",
|
|
35
39
|
"Topic :: Terminals",
|
|
@@ -63,7 +67,7 @@ test = [
|
|
|
63
67
|
"freezegun>=1.4.0",
|
|
64
68
|
]
|
|
65
69
|
windows = [
|
|
66
|
-
"windows-curses>=2.4.
|
|
70
|
+
"windows-curses>=2.4.2; sys_platform == 'win32'",
|
|
67
71
|
]
|
|
68
72
|
|
|
69
73
|
[build-system]
|
|
@@ -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.4.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
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
|
+
import os
|
|
7
8
|
import re
|
|
9
|
+
import tempfile
|
|
8
10
|
import time
|
|
9
11
|
from dataclasses import asdict, dataclass
|
|
10
12
|
from pathlib import Path
|
|
@@ -228,16 +230,38 @@ class CacheManager:
|
|
|
228
230
|
return None
|
|
229
231
|
|
|
230
232
|
def _save_disk_cache(self, key: str, entry: CacheEntry) -> None:
|
|
233
|
+
"""Write an entry to disk atomically.
|
|
234
|
+
|
|
235
|
+
Writing in place meant a crash, a full disk, or two processes saving the
|
|
236
|
+
same key could leave truncated JSON behind. That was survivable — a read
|
|
237
|
+
catches the JSONDecodeError and unlinks the file — but the entry was lost
|
|
238
|
+
and the failure was silent. Writing a temp file in the same directory and
|
|
239
|
+
renaming makes the swap atomic on POSIX and Windows: a reader sees either
|
|
240
|
+
the old entry or the new one, never half of either.
|
|
241
|
+
"""
|
|
231
242
|
cache_file = self.cache_dir / f"{key}.cache"
|
|
243
|
+
tmp_path: str | None = None
|
|
232
244
|
|
|
233
245
|
try:
|
|
234
|
-
|
|
246
|
+
# Same directory, so os.replace is a rename rather than a cross-device copy.
|
|
247
|
+
fd, tmp_path = tempfile.mkstemp(dir=self.cache_dir, suffix=".tmp")
|
|
248
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
235
249
|
json.dump(asdict(entry), f, separators=(",", ":"))
|
|
250
|
+
os.replace(tmp_path, cache_file)
|
|
251
|
+
tmp_path = None
|
|
236
252
|
|
|
237
253
|
self._stats["disk_writes"] += 1
|
|
238
254
|
|
|
239
255
|
except (OSError, TypeError) as e:
|
|
240
256
|
logger.warning("Failed to save cache file %s: %s", cache_file, e)
|
|
257
|
+
finally:
|
|
258
|
+
# A serialisation failure leaves the temp file behind; the previous
|
|
259
|
+
# good entry is still in place, which is the point.
|
|
260
|
+
if tmp_path is not None:
|
|
261
|
+
try:
|
|
262
|
+
os.unlink(tmp_path)
|
|
263
|
+
except OSError:
|
|
264
|
+
pass
|
|
241
265
|
|
|
242
266
|
def _delete_disk_cache(self, key: str) -> bool:
|
|
243
267
|
cache_file = self.cache_dir / f"{key}.cache"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|