lib-cli-exit-tools 1.1.1__tar.gz → 1.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.
Files changed (53) hide show
  1. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/.env.example +0 -0
  2. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/.github/workflows/ci.yml +1 -0
  3. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/.github/workflows/codeql.yml +0 -0
  4. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/.github/workflows/release.yml +0 -0
  5. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/.gitignore +1 -0
  6. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/AGENTS.md +10 -14
  7. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/CHANGELOG.md +8 -0
  8. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/CONTRIBUTING.md +0 -0
  9. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/DEVELOPMENT.md +0 -0
  10. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/LICENSE +0 -0
  11. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/Makefile +0 -0
  12. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/PKG-INFO +2 -2
  13. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/README.md +0 -0
  14. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/codecov.yml +0 -0
  15. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/docs/system-design/reference.md +9 -2
  16. lib_cli_exit_tools-1.3.0/notebooks/Quickstart.ipynb +209 -0
  17. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/packaging/README.md +0 -0
  18. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/packaging/brew/Formula/lib-cli-exit-tools.rb +7 -2
  19. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/packaging/conda/recipe/meta.yaml +2 -2
  20. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/packaging/nix/flake.nix +20 -22
  21. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/pyproject.toml +2 -2
  22. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/qlty.toml +0 -0
  23. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/__init__.py +0 -0
  24. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/_utils.py +131 -0
  25. lib_cli_exit_tools-1.3.0/scripts/build.py +66 -0
  26. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/bump.py +0 -0
  27. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/bump_major.py +0 -0
  28. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/bump_minor.py +0 -0
  29. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/bump_patch.py +0 -0
  30. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/bump_version.py +121 -15
  31. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/clean.py +0 -0
  32. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/dev.py +0 -0
  33. lib_cli_exit_tools-1.3.0/scripts/generate_nix_flake.py +191 -0
  34. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/install.py +0 -0
  35. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/menu.py +0 -0
  36. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/push.py +3 -0
  37. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/release.py +3 -0
  38. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/run_cli.py +0 -0
  39. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/test.py +144 -73
  40. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/scripts/version_current.py +0 -0
  41. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/__init__.py +0 -0
  42. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/__init__conf__.py +0 -0
  43. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/__main__.py +0 -0
  44. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/cli.py +49 -1
  45. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/lib_cli_exit_tools.py +59 -17
  46. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/src/lib_cli_exit_tools/py.typed +0 -0
  47. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/tests/test_cli.py +0 -0
  48. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/tests/test_cli_extra.py +0 -0
  49. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/tests/test_exit_tools.py +0 -0
  50. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/tests/test_extend_cli.py +0 -0
  51. {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.3.0}/tests/test_lib_extra.py +1 -1
  52. lib_cli_exit_tools-1.1.1/notebooks/Quickstart.ipynb +0 -121
  53. lib_cli_exit_tools-1.1.1/scripts/build.py +0 -45
@@ -31,6 +31,7 @@ jobs:
31
31
  shell: bash
32
32
  run: |
33
33
  python -m pip install --upgrade pip
34
+ python -m pip install --upgrade click rich rich-click
34
35
  pip install -e .[dev]
35
36
  - name: Run full test suite (lint, types, tests, coverage, codecov)
36
37
  shell: bash
@@ -64,6 +64,7 @@ coverage.xml
64
64
  # general cache directories (any project-level caching)
65
65
  .cache/
66
66
  .run/
67
+ .import_linter_cache/
67
68
 
68
69
  # ====================================
69
70
  # Build & distribution artifacts
@@ -2,20 +2,17 @@
2
2
 
3
3
  ## On session start
4
4
 
5
- - Connect to the `systemprompts` MCP filesystem.
6
- - Read following files and keep their guidance in working memory:
7
- - core_programming_solid.md
8
- - bash_clean_architecture.md
9
- - bash_clean_code.md
10
- - bash_small_functions.md
11
- - python_solid_architecture_enforcer.md
12
- - python_clean_architecture.md/media/srv-main-softdev/projects/libs/bitranox_template_py_cli
13
- - python_clean_code.md
14
- - python_small_functions_style.md
15
- - python_libraries_to_use.md
16
- - python_structure_template.md
17
- - self_documenting.md
5
+ Review the system prompt files in `/media/srv-main/softdev/*/projects/softwarestack/systemprompts` and keep their guidance in working memory:
18
6
 
7
+ - core_programming_solid.md
8
+ - python_solid_architecture_enforcer.md
9
+ - python_clean_architecture.md
10
+ - python_clean_code.md
11
+ - python_small_functions_style.md
12
+ - python_libraries_to_use.md
13
+ - python_structure_template.md
14
+ - self_documenting.md
15
+ - self_documenting_template.md
19
16
 
20
17
  always apply those Rules :
21
18
 
@@ -37,7 +34,6 @@ when writing or refracturing Python scripts, apply those Rules :
37
34
  - python_libraries_to_use.md
38
35
  - python_lib_structure_template.md
39
36
 
40
-
41
37
  ## Project Structure & Module Organization
42
38
 
43
39
  - `src/lib_cli_exit_tools/`: Python package.
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.0] - 2025-09-25
4
+
5
+ - colored traceback
6
+
7
+ ## [1.2.0] - 2025-09-25
8
+
9
+ - use rich-click instead of click, minor fixes
10
+
3
11
  ## [1.1.1] - 2025-09-18
4
12
 
5
13
  - Documentation and doctest updates
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lib_cli_exit_tools
3
- Version: 1.1.1
3
+ Version: 1.3.0
4
4
  Summary: CLI exit handling helpers: clean signals, exit codes, and error printing
5
5
  Project-URL: Homepage, https://github.com/bitranox/lib_cli_exit_tools
6
6
  Project-URL: Repository, https://github.com/bitranox/lib_cli_exit_tools.git
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3 :: Only
17
17
  Classifier: Typing :: Typed
18
18
  Requires-Python: >=3.10
19
- Requires-Dist: click>=8.1
19
+ Requires-Dist: rich-click>=1.9.1
20
20
  Provides-Extra: dev
21
21
  Requires-Dist: build>=1.2; extra == 'dev'
22
22
  Requires-Dist: codecov>=2.1.13; extra == 'dev'
@@ -138,9 +138,9 @@ The package is the core runtime library for handling CLI exits within the broade
138
138
  **Location:** `lib_cli_exit_tools/lib_cli_exit_tools.py`
139
139
 
140
140
  #### Function: print_exception_message
141
- **Purpose:** Display the current exception message (and optional traceback) with output truncation safeguards.
141
+ **Purpose:** Display the current exception message (and optional rich-formatted traceback) with output truncation safeguards.
142
142
  **Input:** `trace_back`, `length_limit`, optional `stream`.
143
- **Output:** Writes formatted error details to the target stream; flushes stdout/stderr.
143
+ **Output:** Writes formatted error details to the target stream using a `rich` console and flushes stdout/stderr.
144
144
  **Location:** `lib_cli_exit_tools/lib_cli_exit_tools.py`
145
145
 
146
146
  #### Function: _print_output
@@ -167,6 +167,13 @@ The package is the core runtime library for handling CLI exits within the broade
167
167
  **Output:** Dict passed to Click context settings.
168
168
  **Location:** `lib_cli_exit_tools/cli.py`
169
169
 
170
+ #### Function: _configure_rich_click_output
171
+ **Purpose:** Neutralise rich-click styling when stdout is a pipe or uses non-UTF encodings so help text stays ASCII-safe on Windows CI.
172
+ **Input:** None; inspects the active stdout stream via `click.get_text_stream`.
173
+ **Output:** Adjusts rich-click globals (forced terminal flag, colour system, panel boxes) to avoid emitting characters that legacy encodings cannot represent.
174
+ **Location:** `lib_cli_exit_tools/cli.py`
175
+
176
+
170
177
  #### Function: cli
171
178
  **Purpose:** Root Click group that stores global options (currently `--traceback`) and updates library configuration.
172
179
  **Input:** Click `Context`, boolean `traceback` option.
@@ -0,0 +1,209 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "7fb27b941602401d91542211134fc71a",
6
+ "metadata": {},
7
+ "source": [
8
+ "# lib_cli_exit_tools Quickstart\n",
9
+ "\n",
10
+ "Use this notebook to install the package, explore the CLI, and observe how rich-formatted tracebacks behave when you enable debugging output.\n"
11
+ ]
12
+ },
13
+ {
14
+ "cell_type": "markdown",
15
+ "id": "acae54e37e7d407bbb7b55eff062a284",
16
+ "metadata": {},
17
+ "source": [
18
+ "## 1) Install latest from GitHub (default)\n"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "id": "9a63283cbaf04dbcab1f6479b197f3a8",
25
+ "metadata": {},
26
+ "outputs": [],
27
+ "source": [
28
+ "# update pip and setuptools\n",
29
+ "import sys\n",
30
+ "\n",
31
+ "!{sys.executable} -m pip install --upgrade pip\n",
32
+ "!{sys.executable} -m pip install --upgrade setuptools\n",
33
+ "\n",
34
+ "# install lib_cli_exit_tools from GitHub (default)\n",
35
+ "!{sys.executable} -m pip install --upgrade git+https://github.com/bitranox/lib_cli_exit_tools.git"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "markdown",
40
+ "id": "8dd0d8092fe74a7c96281538738b07e2",
41
+ "metadata": {},
42
+ "source": [
43
+ "## Or: Install from PyPI (alternative)\n",
44
+ "Uncomment and run the cell below if you prefer the PyPI release.\n"
45
+ ]
46
+ },
47
+ {
48
+ "cell_type": "code",
49
+ "execution_count": null,
50
+ "id": "72eea5119410473aa328ad9291626812",
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "# !{sys.executable} -m pip install --upgrade lib_cli_exit_tools\n"
55
+ ]
56
+ },
57
+ {
58
+ "cell_type": "markdown",
59
+ "id": "8edb47106e1a46a883d545849b8ab81b",
60
+ "metadata": {},
61
+ "source": [
62
+ "## 2) CLI: version and help\n"
63
+ ]
64
+ },
65
+ {
66
+ "cell_type": "code",
67
+ "execution_count": null,
68
+ "id": "10185d26023b46108eb7d9f57d49d2b3",
69
+ "metadata": {},
70
+ "outputs": [],
71
+ "source": [
72
+ "!lib_cli_exit_tools --version\n",
73
+ "!lib_cli_exit_tools --help\n",
74
+ "!lib_cli_exit_tools info"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "markdown",
79
+ "id": "8763a12b2bbd4a93a75aff182afb95dc",
80
+ "metadata": {},
81
+ "source": [
82
+ "## 3) Demo command for failure handling\n",
83
+ "The helper below builds a tiny Click command that purposely raises an error so you can compare short messages with rich tracebacks.\n"
84
+ ]
85
+ },
86
+ {
87
+ "cell_type": "code",
88
+ "execution_count": null,
89
+ "id": "7623eae2785240b9bd12b16a66d81610",
90
+ "metadata": {},
91
+ "outputs": [],
92
+ "source": [
93
+ "import click\n",
94
+ "from lib_cli_exit_tools.lib_cli_exit_tools import config, run_cli\n",
95
+ "\n",
96
+ "\n",
97
+ "@click.command()\n",
98
+ "def quickstart_fail() -> None:\n",
99
+ " \"\"\"Raise a demo error to exercise lib_cli_exit_tools.\"\"\"\n",
100
+ " raise RuntimeError(\"demo failure from Quickstart\")\n",
101
+ "\n",
102
+ "\n",
103
+ "def run_quickstart_demo(traceback_enabled: bool) -> int:\n",
104
+ " \"\"\"Execute the demo command with the desired traceback mode.\n",
105
+ "\n",
106
+ " The helper keeps tests deterministic by disabling signal handlers\n",
107
+ " in notebook contexts and resets global flags after execution.\n",
108
+ " \"\"\"\n",
109
+ "\n",
110
+ " original_flag = config.traceback\n",
111
+ " config.traceback = traceback_enabled\n",
112
+ " try:\n",
113
+ " return run_cli(quickstart_fail, argv=[], install_signals=False)\n",
114
+ " finally:\n",
115
+ " config.traceback = original_flag"
116
+ ]
117
+ },
118
+ {
119
+ "cell_type": "markdown",
120
+ "id": "7cdc8c89c7104fffa095e18ddfef8986",
121
+ "metadata": {},
122
+ "source": [
123
+ "## 4) CLI failure with short message (default)\n",
124
+ "Tracebacks are suppressed by default; the CLI emits a concise summary and returns a deterministic exit code.\n"
125
+ ]
126
+ },
127
+ {
128
+ "cell_type": "code",
129
+ "execution_count": null,
130
+ "id": "b118ea5561624da68c537baed56e602f",
131
+ "metadata": {},
132
+ "outputs": [],
133
+ "source": [
134
+ "exit_code = run_quickstart_demo(False)\n",
135
+ "print(f\"Exit code: {exit_code}\")"
136
+ ]
137
+ },
138
+ {
139
+ "cell_type": "markdown",
140
+ "id": "938c804e27f84196a10c8828c723f798",
141
+ "metadata": {},
142
+ "source": [
143
+ "## 5) CLI failure with rich traceback\n",
144
+ "Enable tracebacks to surface a full Rich-formatted stack trace for debugging.\n"
145
+ ]
146
+ },
147
+ {
148
+ "cell_type": "code",
149
+ "execution_count": null,
150
+ "id": "504fb2a444614c0babb325280ed9130a",
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "import lib_cli_exit_tools\n",
155
+ "\n",
156
+ "try:\n",
157
+ " run_quickstart_demo(True)\n",
158
+ "except RuntimeError:\n",
159
+ " lib_cli_exit_tools.print_exception_message(trace_back=True)"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "markdown",
164
+ "id": "59bbdb311c014d738909a11f9e486628",
165
+ "metadata": {},
166
+ "source": [
167
+ "## 6) Library usage: exit codes and traceback formatting\n",
168
+ "Use the functional API to translate exceptions into exit codes and to opt in to rich tracebacks programmatically.\n"
169
+ ]
170
+ },
171
+ {
172
+ "cell_type": "code",
173
+ "execution_count": null,
174
+ "id": "b43b363d81ae4b689946ece5c682cd59",
175
+ "metadata": {},
176
+ "outputs": [],
177
+ "source": [
178
+ "import lib_cli_exit_tools\n",
179
+ "\n",
180
+ "# Short message (default)\n",
181
+ "try:\n",
182
+ " raise FileNotFoundError(\"missing.txt\")\n",
183
+ "except Exception as exc:\n",
184
+ " exit_code = lib_cli_exit_tools.get_system_exit_code(exc)\n",
185
+ " lib_cli_exit_tools.print_exception_message(trace_back=False)\n",
186
+ " print(f\"Exit code would be: {exit_code}\")\n",
187
+ "\n",
188
+ "# Rich traceback\n",
189
+ "try:\n",
190
+ " raise ValueError(\"boom\")\n",
191
+ "except Exception:\n",
192
+ " lib_cli_exit_tools.print_exception_message(trace_back=True)"
193
+ ]
194
+ }
195
+ ],
196
+ "metadata": {
197
+ "kernelspec": {
198
+ "display_name": "Python 3",
199
+ "language": "python",
200
+ "name": "python3"
201
+ },
202
+ "language_info": {
203
+ "name": "python",
204
+ "version": "3"
205
+ }
206
+ },
207
+ "nbformat": 4,
208
+ "nbformat_minor": 5
209
+ }
@@ -3,7 +3,7 @@ class LibCliExitTools < Formula
3
3
 
4
4
  desc "CLI exit handling helpers: clean signals, exit codes, and error printing"
5
5
  homepage "https://github.com/bitranox/lib_cli_exit_tools"
6
- url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.1.1.tar.gz"
6
+ url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.3.0.tar.gz"
7
7
  k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
8
8
  license "MIT"
9
9
 
@@ -11,10 +11,15 @@ k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
11
11
 
12
12
  # Vendor Python deps (fill versions/sha256 for an actual formula)
13
13
  resource "click" do
14
- url "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz"
14
+ url "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz"
15
15
  k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
16
16
  end
17
17
 
18
+ resource "rich-click" do
19
+ url "https://files.pythonhosted.org/packages/29/c2/f08b5e7c1a33af8a115be640aa0796ba01c4732696da6d2254391376b314/rich_click-1.9.1.tar.gz"
20
+ ab87be0f5a4c171b43778922b5ed1f99611a0db5a4bf4d0f36770c18ef848d4"
21
+ end
22
+
18
23
  def install
19
24
  virtualenv_install_with_resources
20
25
  end
@@ -1,5 +1,5 @@
1
1
  {% set name = "lib_cli_exit_tools" %}
2
- {% set version = "1.1.1" %}
2
+ {% set version = "1.3.0" %}
3
3
 
4
4
  package:
5
5
  name: {{ name|lower }}
@@ -28,7 +28,7 @@ requirements:
28
28
  - hatchling >=1.25
29
29
  run:
30
30
  - python >=3.10
31
- - click >=8.1
31
+ - rich-click >=1.9.1
32
32
 
33
33
  test:
34
34
  commands:
@@ -1,5 +1,5 @@
1
1
  {
2
- description = "lib_cli_exit_tools Nix flake";
2
+ description = "bitranox_template_py_cli Nix flake";
3
3
 
4
4
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
5
5
  inputs.flake-utils.url = "github:numtide/flake-utils";
@@ -11,15 +11,13 @@
11
11
  lib = pkgs.lib;
12
12
  pypkgs = pkgs.python310Packages;
13
13
 
14
- # Vendor hatchling>=1.25 from PyPI (wheel) to satisfy PEP 517 build
15
14
  hatchlingVendor = pypkgs.buildPythonPackage rec {
16
15
  pname = "hatchling";
17
16
  version = "1.25.0";
18
- format = "wheel"; # install straight from wheel to avoid circular build-backend
19
- # Use explicit URL for py3 wheel; nixpkgs 24.05 fetchPypi may choose a py2.py3 path.
17
+ format = "wheel";
20
18
  src = pkgs.fetchurl {
21
- url = "https://files.pythonhosted.org/packages/py3/h/hatchling/${pname}-${version}-py3-none-any.whl";
22
- hash = "sha256-tHlI5F1NlzA0WE3UyznBS2pwInzyh6t+wK15g0CKiCw=";
19
+ url = "https://files.pythonhosted.org/packages/py3/h/hatchling/hatchling-1.25.0-py3-none-any.whl";
20
+ hash = "sha256-tHlI5F1NlzA0WE3UyznBS2pwInzyh6t+wK15g0CKiCw";
23
21
  };
24
22
  propagatedBuildInputs = [
25
23
  pypkgs.packaging
@@ -31,30 +29,30 @@
31
29
  ];
32
30
  doCheck = false;
33
31
  };
32
+ richClickVendor = pypkgs.buildPythonPackage rec {
33
+ pname = "rich-click";
34
+ version = "1.9.1";
35
+ format = "wheel";
36
+ src = pkgs.fetchurl {
37
+ url = "https://files.pythonhosted.org/packages/a8/77/e9144dcf68a0b3f3f4386986f97255c3d9f7c659be58bb7a5fe8f26f3efa/rich_click-1.9.1-py3-none-any.whl";
38
+ sha256 = "sha256-6mEUqeCBt9aMwHsxUHA5j4BvAbsODEnaVvEp5nKHeBc=";
39
+ };
40
+ doCheck = false;
41
+ };
42
+
34
43
  in
35
44
  {
36
45
  packages.default = pypkgs.buildPythonPackage {
37
46
  pname = "lib_cli_exit_tools";
38
- version = "1.1.1";
47
+ version = "1.3.0";
39
48
  pyproject = true;
40
- # Build from the repository root (two levels up from packaging/nix)
41
49
  src = ../..;
42
- # For pinned releases, swap src for fetchFromGitHub with a rev/sha256.
43
- # src = pkgs.fetchFromGitHub {
44
- # owner = "bitranox";
45
- # repo = "lib_cli_exit_tools";
46
- # rev = "v1.1.1";
47
- # sha256 = "<fill-me>";
48
- # };
49
-
50
- # Ensure PEP 517 backend is available at required version
51
- # Ensure PEP 517 backend available at required version (>=1.25)
52
50
  nativeBuildInputs = [ hatchlingVendor ];
53
- propagatedBuildInputs = [ pypkgs.click ];
51
+ propagatedBuildInputs = [ richClickVendor ];
54
52
 
55
53
  meta = with pkgs.lib; {
56
- description = "CLI exit handling helpers: clean signals, exit codes, and error printing";
57
- homepage = "https://github.com/bitranox/lib_cli_exit_tools";
54
+ description = "Rich-powered logging helpers for colorful terminal output";
55
+ homepage = "https://github.com/bitranox/bitranox_template_py_cli";
58
56
  license = licenses.mit;
59
57
  maintainers = [];
60
58
  platforms = platforms.unix ++ platforms.darwin;
@@ -65,7 +63,7 @@
65
63
  packages = [
66
64
  pkgs.python310
67
65
  hatchlingVendor
68
- pypkgs.click
66
+ richClickVendor
69
67
  pypkgs.pytest
70
68
  pkgs.ruff
71
69
  pkgs.nodejs
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "lib_cli_exit_tools"
3
- version = "1.1.1"
3
+ version = "1.3.0"
4
4
  description = "CLI exit handling helpers: clean signals, exit codes, and error printing"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
- dependencies = ["click>=8.1"]
7
+ dependencies = ["rich-click>=1.9.1"]
8
8
  license = { text = "MIT" }
9
9
  authors = [{ name = "bitranox", email = "bitranox@gmail.com" }]
10
10
  keywords = ["cli", "exit", "signals", "errors", "click"]
@@ -1,9 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import os
4
+ import platform
3
5
  import re
4
6
  import shlex
5
7
  import subprocess
6
8
  import sys
9
+ import tempfile
10
+ import hashlib
11
+ import json
12
+ from urllib.request import urlopen
7
13
  from dataclasses import dataclass
8
14
  from pathlib import Path
9
15
  from subprocess import CompletedProcess
@@ -194,6 +200,95 @@ def read_version_from_pyproject(pyproject: Path = Path("pyproject.toml")) -> str
194
200
  return match.group(1) if match else ""
195
201
 
196
202
 
203
+ def ensure_conda(auto_install: bool = True) -> bool:
204
+ """Ensure the `conda` CLI is available, optionally bootstrapping Miniforge."""
205
+
206
+ if cmd_exists("conda"):
207
+ return True
208
+
209
+ if not auto_install:
210
+ return False
211
+
212
+ system = platform.system().lower()
213
+ machine = platform.machine().lower()
214
+
215
+ if system.startswith("linux"):
216
+ os_name = "Linux"
217
+ elif system == "darwin":
218
+ os_name = "MacOSX"
219
+ else:
220
+ return False
221
+
222
+ if machine in {"x86_64", "amd64"}:
223
+ arch = "x86_64"
224
+ elif machine in {"aarch64", "arm64"}:
225
+ arch = "aarch64" if os_name == "Linux" else "arm64"
226
+ else:
227
+ return False
228
+
229
+ filename = f"Miniforge3-{os_name}-{arch}.sh"
230
+ base = "https://github.com/conda-forge/miniforge/releases/latest/download/"
231
+ asset_url = base + filename
232
+ sums_url = base + "SHA256SUMS"
233
+
234
+ try:
235
+ with urlopen("https://api.github.com/repos/conda-forge/miniforge/releases/latest", timeout=10) as resp:
236
+ release = json.loads(resp.read().decode("utf-8"))
237
+ for asset in release.get("assets", []):
238
+ name = asset.get("name")
239
+ if name == filename:
240
+ asset_url = asset.get("browser_download_url", asset_url)
241
+ elif name == "SHA256SUMS":
242
+ sums_url = asset.get("browser_download_url", sums_url)
243
+ except Exception:
244
+ pass
245
+
246
+ conda_root = Path.home() / "miniforge3"
247
+
248
+ if not conda_root.exists():
249
+ attempts = 3
250
+ for _ in range(attempts):
251
+ try:
252
+ installer_bytes = urlopen(asset_url, timeout=30).read()
253
+ sums_text = urlopen(sums_url, timeout=30).read().decode("utf-8")
254
+ except Exception:
255
+ continue
256
+
257
+ expected_hash = None
258
+ for line in sums_text.splitlines():
259
+ line = line.strip()
260
+ if line.endswith(f" {filename}"):
261
+ parts = line.split()
262
+ if parts:
263
+ expected_hash = parts[0]
264
+ break
265
+
266
+ if not expected_hash:
267
+ continue
268
+
269
+ actual_hash = hashlib.sha256(installer_bytes).hexdigest()
270
+ if actual_hash != expected_hash:
271
+ continue
272
+
273
+ with tempfile.TemporaryDirectory() as tmp:
274
+ installer = Path(tmp) / filename
275
+ installer.write_bytes(installer_bytes)
276
+ installer.chmod(0o755)
277
+ install = run(["bash", str(installer), "-b"], check=False, capture=False)
278
+ if install.code == 0:
279
+ break
280
+ else:
281
+ return False
282
+ candidates = [conda_root / "condabin", conda_root / "bin"]
283
+
284
+ existing = os.environ.get("PATH", "").split(os.pathsep)
285
+ new_entries = [str(p) for p in candidates if p.exists()]
286
+ if new_entries:
287
+ os.environ["PATH"] = os.pathsep.join(new_entries + [p for p in existing if p])
288
+
289
+ return cmd_exists("conda")
290
+
291
+
197
292
  def ensure_clean_git_tree() -> None:
198
293
  dirty = subprocess.call(["bash", "-lc", "! git diff --quiet || ! git diff --cached --quiet"], stdout=subprocess.DEVNULL)
199
294
  if dirty == 0:
@@ -241,6 +336,7 @@ def gh_release_edit(tag: str, title: str, body: str) -> None:
241
336
 
242
337
  def sync_packaging() -> None:
243
338
  run([sys.executable, "scripts/bump_version.py", "--sync-packaging"], check=False)
339
+ run([sys.executable, "scripts/generate_nix_flake.py"], check=False)
244
340
 
245
341
 
246
342
  def bootstrap_dev() -> None:
@@ -253,3 +349,38 @@ def bootstrap_dev() -> None:
253
349
  import_module("sqlite3")
254
350
  except Exception:
255
351
  run([sys.executable, "-m", "pip", "install", "pysqlite3-binary"], check=False)
352
+
353
+
354
+ def ensure_nix(auto_install: bool = True) -> bool:
355
+ """Ensure the `nix` command is available, optionally bootstrapping it."""
356
+
357
+ if cmd_exists("nix"):
358
+ return True
359
+
360
+ if not auto_install:
361
+ return False
362
+
363
+ import os
364
+ import sys
365
+
366
+ platform = sys.platform
367
+ if not (platform.startswith("linux") or platform == "darwin"):
368
+ return False
369
+
370
+ # Install single-user Nix (no-daemon) for non-root environments.
371
+ install_cmd = "curl -L https://nixos.org/nix/install | sh -s -- --no-daemon"
372
+ run(["bash", "-lc", install_cmd], check=False, capture=False)
373
+
374
+ profile_dir = Path.home() / ".nix-profile"
375
+ profile_env = profile_dir / "etc" / "profile.d" / "nix.sh"
376
+ bin_dir = profile_dir / "bin"
377
+
378
+ if bin_dir.exists():
379
+ path_entries = os.environ.get("PATH", "").split(os.pathsep)
380
+ if str(bin_dir) not in path_entries:
381
+ os.environ["PATH"] = os.pathsep.join([str(bin_dir)] + [p for p in path_entries if p])
382
+
383
+ if profile_env.exists():
384
+ run(["bash", "-lc", f". {profile_env} >/dev/null 2>&1 && nix --version"], check=False, capture=False)
385
+
386
+ return cmd_exists("nix")