chutils 2.5.0__tar.gz → 2.6.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.
- chutils-2.5.0/README.md → chutils-2.6.0/PKG-INFO +146 -2
- chutils-2.5.0/PKG-INFO → chutils-2.6.0/README.md +114 -25
- chutils-2.6.0/pyproject.toml +70 -0
- {chutils-2.5.0 → chutils-2.6.0}/src/chutils/__init__.py +24 -11
- chutils-2.6.0/src/chutils/cli.py +71 -0
- chutils-2.6.0/src/chutils/config/__init__.py +602 -0
- chutils-2.6.0/src/chutils/config/manager.py +98 -0
- chutils-2.6.0/src/chutils/config/providers.py +234 -0
- chutils-2.6.0/src/chutils/config/utils.py +123 -0
- chutils-2.6.0/src/chutils/config/watcher.py +137 -0
- chutils-2.6.0/src/chutils/context.py +60 -0
- chutils-2.6.0/src/chutils/decorators.py +198 -0
- {chutils-2.5.0 → chutils-2.6.0}/src/chutils/logger.py +196 -26
- {chutils-2.5.0 → chutils-2.6.0}/src/chutils/secret_manager.py +24 -6
- chutils-2.5.0/pyproject.toml +0 -62
- chutils-2.5.0/src/chutils/config.py +0 -682
- chutils-2.5.0/src/chutils/decorators.py +0 -65
- {chutils-2.5.0 → chutils-2.6.0}/LICENSE +0 -0
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chutils
|
|
3
|
+
Version: 2.6.0
|
|
4
|
+
Summary: Набор простых и удобных утилит для Python, который избавляет от рутины при работе с конфигурацией и логированием в новых проектах.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Chu4hel
|
|
8
|
+
Author-email: sergeiivanov636@gmail.com
|
|
9
|
+
Requires-Python: >=3.9, !=3.9.0, !=3.9.1
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Provides-Extra: full
|
|
17
|
+
Provides-Extra: json
|
|
18
|
+
Provides-Extra: pydantic
|
|
19
|
+
Provides-Extra: watch
|
|
20
|
+
Requires-Dist: keyring (>=25.7.0)
|
|
21
|
+
Requires-Dist: pydantic (>=2.13.4) ; extra == "full"
|
|
22
|
+
Requires-Dist: pydantic (>=2.13.4,<3.0.0) ; extra == "pydantic"
|
|
23
|
+
Requires-Dist: python-dotenv (>=1.2.1) ; python_full_version < "3.10.0"
|
|
24
|
+
Requires-Dist: python-dotenv (>=1.2.2) ; python_full_version >= "3.10.0"
|
|
25
|
+
Requires-Dist: python-json-logger (>=3.2.1) ; extra == "full"
|
|
26
|
+
Requires-Dist: python-json-logger (>=3.2.1) ; extra == "json"
|
|
27
|
+
Requires-Dist: pyyaml (>=6.0.3)
|
|
28
|
+
Requires-Dist: watchdog (>=6.0.0) ; extra == "full"
|
|
29
|
+
Requires-Dist: watchdog (>=6.0.0) ; extra == "watch"
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
1
32
|
[Русская версия](docs/README_RU.md)
|
|
2
33
|
|
|
3
34
|
# chutils: Stop the Routine!
|
|
@@ -34,9 +65,28 @@ Every time you start a new project, you have to solve the same tasks:
|
|
|
34
65
|
box. It returns a custom logger with additional debug levels (`devdebug`, `mediumdebug`).
|
|
35
66
|
- **🔒 Secure Secret Storage:** The `secret_manager` module provides a simple interface for saving and retrieving secrets
|
|
36
67
|
via the system `keyring`, with a fallback to `.env` files.
|
|
68
|
+
- **🔄 Hot-Reload:** Support for automatic configuration reloading on file changes without restart (requires
|
|
69
|
+
`pip install chutils[watch]`).
|
|
37
70
|
- **⚡ Async Ready:** Most core functions have asynchronous versions (prefixed with `a`) for non-blocking execution.
|
|
38
71
|
- **🚀 Ready to Use:** Just install and use.
|
|
39
72
|
|
|
73
|
+
## Command Line Interface (CLI)
|
|
74
|
+
|
|
75
|
+
The library provides a `chutils` console command for convenient secret management without writing code.
|
|
76
|
+
|
|
77
|
+
### Secret Management
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Save a secret to the system storage (keyring)
|
|
81
|
+
chutils secrets set MY_API_KEY "super-secret-value"
|
|
82
|
+
|
|
83
|
+
# Delete a secret
|
|
84
|
+
chutils secrets delete MY_API_KEY
|
|
85
|
+
|
|
86
|
+
# Explicitly specify service name
|
|
87
|
+
chutils secrets set DB_PASSWORD "12345" --service my_custom_app
|
|
88
|
+
```
|
|
89
|
+
|
|
40
90
|
## Installation
|
|
41
91
|
|
|
42
92
|
```bash
|
|
@@ -76,13 +126,60 @@ Each example focuses on a specific task.
|
|
|
76
126
|
db_port = get_config_int("Database", "port", fallback=5432)
|
|
77
127
|
```
|
|
78
128
|
|
|
129
|
+
#### Validation via Pydantic (Optional)
|
|
130
|
+
|
|
131
|
+
For strict typing and validation, you can use Pydantic models (requires `pip install chutils[pydantic]`):
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pydantic import BaseModel
|
|
135
|
+
from chutils import get_config
|
|
136
|
+
|
|
137
|
+
class AppConfig(BaseModel):
|
|
138
|
+
app_name: str
|
|
139
|
+
version: str
|
|
140
|
+
|
|
141
|
+
# Returns an instance of AppConfig
|
|
142
|
+
cfg = get_config(model=AppConfig)
|
|
143
|
+
print(cfg.app_name)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can also validate specific sections:
|
|
147
|
+
```python
|
|
148
|
+
from chutils import get_config_section
|
|
149
|
+
db_cfg = get_config_section("Database", model=MyDbModel)
|
|
150
|
+
```
|
|
151
|
+
|
|
79
152
|
#### Overriding Configuration with Local Files (`config.local.yml`)
|
|
80
153
|
|
|
81
154
|
You can create a `config.local.yml` next to your main file. Values from the local file will **override**
|
|
82
155
|
corresponding values from the main file. This is perfect for local development or storing sensitive data (ensure
|
|
83
156
|
`*.local.*` is in your `.gitignore`).
|
|
84
157
|
|
|
85
|
-
### 2.
|
|
158
|
+
### 2. Hot-Reload
|
|
159
|
+
|
|
160
|
+
You can make your application react to configuration file changes in real-time.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from chutils import start_config_watcher, on_config_change, get_config_value
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def reload_logic():
|
|
167
|
+
print("Configuration updated!")
|
|
168
|
+
# Update app state here
|
|
169
|
+
db_url = get_config_value("Database", "url")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Register callback
|
|
173
|
+
on_config_change(reload_logic)
|
|
174
|
+
|
|
175
|
+
# Start watcher (requires watchdog package)
|
|
176
|
+
start_config_watcher()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
To use this feature, install `watchdog`:
|
|
180
|
+
`pip install chutils[watch]`
|
|
181
|
+
|
|
182
|
+
### 3. Logging Setup
|
|
86
183
|
|
|
87
184
|
1. Configure and use the logger:
|
|
88
185
|
|
|
@@ -96,8 +193,39 @@ Each example focuses on a specific task.
|
|
|
96
193
|
logger.devdebug("Deep debug message (level 9).")
|
|
97
194
|
```
|
|
98
195
|
|
|
196
|
+
#### Structured Logging (JSON)
|
|
197
|
+
|
|
198
|
+
If you need to output logs in JSON format for ELK, Splunk, or cloud logging (requires `pip install chutils[json]`):
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
# Via code
|
|
202
|
+
logger = setup_logger(json_format=True)
|
|
203
|
+
|
|
204
|
+
# Or via config in the [Logging] section
|
|
205
|
+
# json_format: true
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Contextual Logging (ContextVar)
|
|
209
|
+
|
|
210
|
+
You can bind metadata to the current execution context (thread or coroutine), and it will be automatically
|
|
211
|
+
included in all log messages.
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from chutils import setup_logger, bind_context
|
|
215
|
+
|
|
216
|
+
logger = setup_logger()
|
|
217
|
+
|
|
218
|
+
# Bind request ID and user to the current context
|
|
219
|
+
bind_context(request_id="REQ-123", user="admin")
|
|
220
|
+
|
|
221
|
+
logger.info("Action performed")
|
|
222
|
+
# Text: ... [request_id=REQ-123 user=admin] Action performed
|
|
223
|
+
# JSON: {..., "message": "Action performed", "context": {"request_id": "REQ-123", "user": "admin"}}
|
|
224
|
+
```
|
|
225
|
+
|
|
99
226
|
#### Controlling Logging via Environment Variables
|
|
100
227
|
|
|
228
|
+
- `CH_LOG_JSON=true`: Forces JSON format.
|
|
101
229
|
- `CH_LOG_NO_TIME=true`: Removes the date/time from the log format (for clean Docker logs).
|
|
102
230
|
- `CH_LOG_NO_FILE=true`: Disables creating log files.
|
|
103
231
|
|
|
@@ -132,7 +260,8 @@ In environments like Docker or CI/CD where `keyring` is unavailable, you can sup
|
|
|
132
260
|
|
|
133
261
|
- `get_config_value(section, key, fallback)` / `aget_config()`
|
|
134
262
|
- `get_config_int`, `get_config_boolean`, `get_config_list`, `get_config_path`
|
|
135
|
-
- `save_config_value(section, key, value)` / `asave_config_value()`
|
|
263
|
+
- `save_config_value(section, key, value, notify=True)` / `asave_config_value()`
|
|
264
|
+
- Use `notify=False` to update the file without triggering Hot-Reload callbacks.
|
|
136
265
|
|
|
137
266
|
### Logging (`chutils.logger`)
|
|
138
267
|
|
|
@@ -149,7 +278,22 @@ In environments like Docker or CI/CD where `keyring` is unavailable, you can sup
|
|
|
149
278
|
### Decorators (`chutils.decorators`)
|
|
150
279
|
|
|
151
280
|
- `@log_function_details`: Logs arguments, execution time, and result (uses `DEVDEBUG` level).
|
|
281
|
+
- `@timeout(seconds, fallback)`: Limits function execution time. Supports sync/async and optional fallback.
|
|
282
|
+
- `@retry`: Automatically retries a function if it fails. Supports sync/async, backoff, jitter, and exception filtering.
|
|
283
|
+
|
|
284
|
+
#### Example of @retry usage:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from chutils.decorators import retry
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@retry(retries=3, delay=1.0, backoff=2.0)
|
|
291
|
+
def fetch_data():
|
|
292
|
+
# Will be retried up to 3 times on any Exception
|
|
293
|
+
...
|
|
294
|
+
```
|
|
152
295
|
|
|
153
296
|
## License
|
|
154
297
|
|
|
155
298
|
The project is distributed under the MIT License.
|
|
299
|
+
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: chutils
|
|
3
|
-
Version: 2.5.0
|
|
4
|
-
Summary: Набор простых и удобных утилит для Python, который избавляет от рутины при работе с конфигурацией и логированием в новых проектах.
|
|
5
|
-
License: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Author: Chu4hel
|
|
8
|
-
Author-email: sergeiivanov636@gmail.com
|
|
9
|
-
Requires-Python: >=3.9, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
-
Requires-Dist: keyring (>=25.7.0,<26.0.0)
|
|
18
|
-
Requires-Dist: python-dotenv (==1.2.1) ; python_version < "3.10"
|
|
19
|
-
Requires-Dist: python-dotenv (>=1.2.2,<2.0.0) ; python_version >= "3.10"
|
|
20
|
-
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
1
|
[Русская версия](docs/README_RU.md)
|
|
24
2
|
|
|
25
3
|
# chutils: Stop the Routine!
|
|
@@ -56,9 +34,28 @@ Every time you start a new project, you have to solve the same tasks:
|
|
|
56
34
|
box. It returns a custom logger with additional debug levels (`devdebug`, `mediumdebug`).
|
|
57
35
|
- **🔒 Secure Secret Storage:** The `secret_manager` module provides a simple interface for saving and retrieving secrets
|
|
58
36
|
via the system `keyring`, with a fallback to `.env` files.
|
|
37
|
+
- **🔄 Hot-Reload:** Support for automatic configuration reloading on file changes without restart (requires
|
|
38
|
+
`pip install chutils[watch]`).
|
|
59
39
|
- **⚡ Async Ready:** Most core functions have asynchronous versions (prefixed with `a`) for non-blocking execution.
|
|
60
40
|
- **🚀 Ready to Use:** Just install and use.
|
|
61
41
|
|
|
42
|
+
## Command Line Interface (CLI)
|
|
43
|
+
|
|
44
|
+
The library provides a `chutils` console command for convenient secret management without writing code.
|
|
45
|
+
|
|
46
|
+
### Secret Management
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Save a secret to the system storage (keyring)
|
|
50
|
+
chutils secrets set MY_API_KEY "super-secret-value"
|
|
51
|
+
|
|
52
|
+
# Delete a secret
|
|
53
|
+
chutils secrets delete MY_API_KEY
|
|
54
|
+
|
|
55
|
+
# Explicitly specify service name
|
|
56
|
+
chutils secrets set DB_PASSWORD "12345" --service my_custom_app
|
|
57
|
+
```
|
|
58
|
+
|
|
62
59
|
## Installation
|
|
63
60
|
|
|
64
61
|
```bash
|
|
@@ -98,13 +95,60 @@ Each example focuses on a specific task.
|
|
|
98
95
|
db_port = get_config_int("Database", "port", fallback=5432)
|
|
99
96
|
```
|
|
100
97
|
|
|
98
|
+
#### Validation via Pydantic (Optional)
|
|
99
|
+
|
|
100
|
+
For strict typing and validation, you can use Pydantic models (requires `pip install chutils[pydantic]`):
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from pydantic import BaseModel
|
|
104
|
+
from chutils import get_config
|
|
105
|
+
|
|
106
|
+
class AppConfig(BaseModel):
|
|
107
|
+
app_name: str
|
|
108
|
+
version: str
|
|
109
|
+
|
|
110
|
+
# Returns an instance of AppConfig
|
|
111
|
+
cfg = get_config(model=AppConfig)
|
|
112
|
+
print(cfg.app_name)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
You can also validate specific sections:
|
|
116
|
+
```python
|
|
117
|
+
from chutils import get_config_section
|
|
118
|
+
db_cfg = get_config_section("Database", model=MyDbModel)
|
|
119
|
+
```
|
|
120
|
+
|
|
101
121
|
#### Overriding Configuration with Local Files (`config.local.yml`)
|
|
102
122
|
|
|
103
123
|
You can create a `config.local.yml` next to your main file. Values from the local file will **override**
|
|
104
124
|
corresponding values from the main file. This is perfect for local development or storing sensitive data (ensure
|
|
105
125
|
`*.local.*` is in your `.gitignore`).
|
|
106
126
|
|
|
107
|
-
### 2.
|
|
127
|
+
### 2. Hot-Reload
|
|
128
|
+
|
|
129
|
+
You can make your application react to configuration file changes in real-time.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from chutils import start_config_watcher, on_config_change, get_config_value
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def reload_logic():
|
|
136
|
+
print("Configuration updated!")
|
|
137
|
+
# Update app state here
|
|
138
|
+
db_url = get_config_value("Database", "url")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# Register callback
|
|
142
|
+
on_config_change(reload_logic)
|
|
143
|
+
|
|
144
|
+
# Start watcher (requires watchdog package)
|
|
145
|
+
start_config_watcher()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To use this feature, install `watchdog`:
|
|
149
|
+
`pip install chutils[watch]`
|
|
150
|
+
|
|
151
|
+
### 3. Logging Setup
|
|
108
152
|
|
|
109
153
|
1. Configure and use the logger:
|
|
110
154
|
|
|
@@ -118,8 +162,39 @@ Each example focuses on a specific task.
|
|
|
118
162
|
logger.devdebug("Deep debug message (level 9).")
|
|
119
163
|
```
|
|
120
164
|
|
|
165
|
+
#### Structured Logging (JSON)
|
|
166
|
+
|
|
167
|
+
If you need to output logs in JSON format for ELK, Splunk, or cloud logging (requires `pip install chutils[json]`):
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# Via code
|
|
171
|
+
logger = setup_logger(json_format=True)
|
|
172
|
+
|
|
173
|
+
# Or via config in the [Logging] section
|
|
174
|
+
# json_format: true
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Contextual Logging (ContextVar)
|
|
178
|
+
|
|
179
|
+
You can bind metadata to the current execution context (thread or coroutine), and it will be automatically
|
|
180
|
+
included in all log messages.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from chutils import setup_logger, bind_context
|
|
184
|
+
|
|
185
|
+
logger = setup_logger()
|
|
186
|
+
|
|
187
|
+
# Bind request ID and user to the current context
|
|
188
|
+
bind_context(request_id="REQ-123", user="admin")
|
|
189
|
+
|
|
190
|
+
logger.info("Action performed")
|
|
191
|
+
# Text: ... [request_id=REQ-123 user=admin] Action performed
|
|
192
|
+
# JSON: {..., "message": "Action performed", "context": {"request_id": "REQ-123", "user": "admin"}}
|
|
193
|
+
```
|
|
194
|
+
|
|
121
195
|
#### Controlling Logging via Environment Variables
|
|
122
196
|
|
|
197
|
+
- `CH_LOG_JSON=true`: Forces JSON format.
|
|
123
198
|
- `CH_LOG_NO_TIME=true`: Removes the date/time from the log format (for clean Docker logs).
|
|
124
199
|
- `CH_LOG_NO_FILE=true`: Disables creating log files.
|
|
125
200
|
|
|
@@ -154,7 +229,8 @@ In environments like Docker or CI/CD where `keyring` is unavailable, you can sup
|
|
|
154
229
|
|
|
155
230
|
- `get_config_value(section, key, fallback)` / `aget_config()`
|
|
156
231
|
- `get_config_int`, `get_config_boolean`, `get_config_list`, `get_config_path`
|
|
157
|
-
- `save_config_value(section, key, value)` / `asave_config_value()`
|
|
232
|
+
- `save_config_value(section, key, value, notify=True)` / `asave_config_value()`
|
|
233
|
+
- Use `notify=False` to update the file without triggering Hot-Reload callbacks.
|
|
158
234
|
|
|
159
235
|
### Logging (`chutils.logger`)
|
|
160
236
|
|
|
@@ -171,8 +247,21 @@ In environments like Docker or CI/CD where `keyring` is unavailable, you can sup
|
|
|
171
247
|
### Decorators (`chutils.decorators`)
|
|
172
248
|
|
|
173
249
|
- `@log_function_details`: Logs arguments, execution time, and result (uses `DEVDEBUG` level).
|
|
250
|
+
- `@timeout(seconds, fallback)`: Limits function execution time. Supports sync/async and optional fallback.
|
|
251
|
+
- `@retry`: Automatically retries a function if it fails. Supports sync/async, backoff, jitter, and exception filtering.
|
|
252
|
+
|
|
253
|
+
#### Example of @retry usage:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from chutils.decorators import retry
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@retry(retries=3, delay=1.0, backoff=2.0)
|
|
260
|
+
def fetch_data():
|
|
261
|
+
# Will be retried up to 3 times on any Exception
|
|
262
|
+
...
|
|
263
|
+
```
|
|
174
264
|
|
|
175
265
|
## License
|
|
176
266
|
|
|
177
267
|
The project is distributed under the MIT License.
|
|
178
|
-
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "chutils"
|
|
3
|
+
version = "2.6.0"
|
|
4
|
+
description = "Набор простых и удобных утилит для Python, который избавляет от рутины при работе с конфигурацией и логированием в новых проектах."
|
|
5
|
+
authors = [{name = "Chu4hel", email = "sergeiivanov636@gmail.com"}]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.9, !=3.9.0, !=3.9.1"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"keyring (>=25.7.0)",
|
|
11
|
+
"pyyaml (>=6.0.3)",
|
|
12
|
+
"python-dotenv (>=1.2.1) ; python_full_version < '3.10'",
|
|
13
|
+
"python-dotenv (>=1.2.2) ; python_full_version >= '3.10'"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
pydantic = ["pydantic (>=2.13.4,<3.0.0)"]
|
|
18
|
+
json = ["python-json-logger (>=3.2.1)"]
|
|
19
|
+
watch = ["watchdog (>=6.0.0)"]
|
|
20
|
+
full = [
|
|
21
|
+
"pydantic (>=2.13.4)",
|
|
22
|
+
"python-json-logger (>=3.2.1)",
|
|
23
|
+
"watchdog (>=6.0.0)"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
chutils = "chutils.cli:main"
|
|
28
|
+
|
|
29
|
+
[tool.poetry]
|
|
30
|
+
packages = [{ include = "chutils", from = "src" }]
|
|
31
|
+
exclude = [
|
|
32
|
+
"tests/",
|
|
33
|
+
"site/",
|
|
34
|
+
"examples/",
|
|
35
|
+
"docs/",
|
|
36
|
+
".github/",
|
|
37
|
+
".idea/",
|
|
38
|
+
"__pycache__/",
|
|
39
|
+
"*.pyc",
|
|
40
|
+
"*.log",
|
|
41
|
+
"*.egg-info/",
|
|
42
|
+
".vscode/",
|
|
43
|
+
".ruff_cache/",
|
|
44
|
+
"coverage.xml",
|
|
45
|
+
".coverage",
|
|
46
|
+
"PUBLISHING.md",
|
|
47
|
+
"project.txt",
|
|
48
|
+
"changelog.txt",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.poetry.group.dev.dependencies]
|
|
52
|
+
pytest = { version = "^9.0.3", python = ">=3.10" }
|
|
53
|
+
pytest-mock = "^3.15.1"
|
|
54
|
+
pyfakefs = { version = "^6.2.0", python = ">=3.10" }
|
|
55
|
+
mkdocs = "^1.6.1"
|
|
56
|
+
mkdocs-material = "^9.7.5"
|
|
57
|
+
mkdocstrings = { version = "^1.0.4", python = ">=3.10" }
|
|
58
|
+
mkdocstrings-python = { version = "^2.0.3", python = ">=3.10" }
|
|
59
|
+
pytest-cov = "^7.1.0"
|
|
60
|
+
pytest-asyncio = { version = "^1.3.0", python = ">=3.10" }
|
|
61
|
+
ruff = "^0.15.12"
|
|
62
|
+
|
|
63
|
+
[build-system]
|
|
64
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
65
|
+
build-backend = "poetry.core.masonry.api"
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
pythonpath = [
|
|
69
|
+
"src"
|
|
70
|
+
]
|
|
@@ -36,10 +36,6 @@ import os
|
|
|
36
36
|
|
|
37
37
|
from . import config
|
|
38
38
|
from . import logger
|
|
39
|
-
|
|
40
|
-
# --- Импорт публичных функций и классов ---
|
|
41
|
-
# Явно импортируем все, что должно быть доступно пользователю напрямую из пакета chutils.
|
|
42
|
-
|
|
43
39
|
from .config import (
|
|
44
40
|
get_config,
|
|
45
41
|
get_config_value,
|
|
@@ -50,11 +46,20 @@ from .config import (
|
|
|
50
46
|
get_config_section,
|
|
51
47
|
get_config_path,
|
|
52
48
|
aget_config,
|
|
53
|
-
|
|
49
|
+
save_config_value,
|
|
50
|
+
asave_config_value,
|
|
51
|
+
start_config_watcher,
|
|
52
|
+
stop_config_watcher,
|
|
53
|
+
on_config_change,
|
|
54
|
+
)
|
|
55
|
+
from .context import bind_context, unbind_context, clear_context
|
|
56
|
+
from .decorators import log_function_details, retry, timeout
|
|
57
|
+
from .logger import (
|
|
58
|
+
setup_logger,
|
|
59
|
+
ChutilsLogger,
|
|
60
|
+
SafeTimedRotatingFileHandler
|
|
54
61
|
)
|
|
55
|
-
from .logger import setup_logger, ChutilsLogger, SafeTimedRotatingFileHandler
|
|
56
62
|
from .secret_manager import SecretManager
|
|
57
|
-
from .decorators import log_function_details
|
|
58
63
|
|
|
59
64
|
|
|
60
65
|
def init(base_dir: str):
|
|
@@ -74,10 +79,9 @@ def init(base_dir: str):
|
|
|
74
79
|
if not os.path.isdir(base_dir):
|
|
75
80
|
raise ValueError(f"Указанная директория base_dir не существует или не является директорией: {base_dir}")
|
|
76
81
|
|
|
77
|
-
# Вручную устанавливаем базовую
|
|
78
|
-
|
|
79
|
-
config.
|
|
80
|
-
config._paths_initialized = True
|
|
82
|
+
# Вручную устанавливаем базовую директорию через менеджер состояний.
|
|
83
|
+
config._cm.base_dir = base_dir
|
|
84
|
+
config._cm.paths_initialized = True
|
|
81
85
|
|
|
82
86
|
print(f"Пакет chutils вручную инициализирован с базовой директорией: {base_dir}")
|
|
83
87
|
|
|
@@ -99,16 +103,25 @@ __all__ = [
|
|
|
99
103
|
'get_config_section',
|
|
100
104
|
'get_config_path',
|
|
101
105
|
'aget_config',
|
|
106
|
+
"save_config_value",
|
|
102
107
|
'asave_config_value',
|
|
108
|
+
'start_config_watcher',
|
|
109
|
+
'stop_config_watcher',
|
|
110
|
+
'on_config_change',
|
|
103
111
|
|
|
104
112
|
# Функции и классы из модуля logger
|
|
105
113
|
'setup_logger',
|
|
106
114
|
'ChutilsLogger',
|
|
107
115
|
'SafeTimedRotatingFileHandler',
|
|
116
|
+
'bind_context',
|
|
117
|
+
'unbind_context',
|
|
118
|
+
'clear_context',
|
|
108
119
|
|
|
109
120
|
# Классы из модуля secret_manager
|
|
110
121
|
'SecretManager',
|
|
111
122
|
|
|
112
123
|
# Декораторы
|
|
113
124
|
'log_function_details',
|
|
125
|
+
'retry',
|
|
126
|
+
'timeout',
|
|
114
127
|
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from chutils import config
|
|
5
|
+
from chutils.secret_manager import SecretManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def handle_secrets_set(args: argparse.Namespace):
|
|
9
|
+
"""Обработчик команды сохранения секрета."""
|
|
10
|
+
service_name = args.service or config.get_config_value("Secrets", "service_name", "")
|
|
11
|
+
sm = SecretManager(service_name)
|
|
12
|
+
|
|
13
|
+
if sm.save_secret(args.key, args.value):
|
|
14
|
+
print(f"[OK] Секрет '{args.key}' успешно сохранен в системном хранилище.")
|
|
15
|
+
else:
|
|
16
|
+
print(f"[ERROR] Не удалось сохранить секрет '{args.key}'. Проверьте доступность keyring.")
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handle_secrets_delete(args: argparse.Namespace):
|
|
21
|
+
"""Обработчик команды удаления секрета."""
|
|
22
|
+
service_name = args.service or config.get_config_value("Secrets", "service_name", "")
|
|
23
|
+
sm = SecretManager(service_name)
|
|
24
|
+
|
|
25
|
+
if sm.delete_secret(args.key):
|
|
26
|
+
print(f"[OK] Секрет '{args.key}' успешно удален.")
|
|
27
|
+
else:
|
|
28
|
+
print(f"[ERROR] Не удалось удалить секрет '{args.key}' или он не существовал.")
|
|
29
|
+
sys.exit(1)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main():
|
|
33
|
+
"""Точка входа в CLI."""
|
|
34
|
+
parser = argparse.ArgumentParser(
|
|
35
|
+
prog="chutils",
|
|
36
|
+
description="Набор утилит chutils для командной строки."
|
|
37
|
+
)
|
|
38
|
+
subparsers = parser.add_subparsers(dest="command", help="Команды")
|
|
39
|
+
|
|
40
|
+
# Секция секретов
|
|
41
|
+
secrets_parser = subparsers.add_parser("secrets", help="Управление секретами")
|
|
42
|
+
secrets_subparsers = secrets_parser.add_subparsers(dest="subcommand", help="Действия с секретами")
|
|
43
|
+
|
|
44
|
+
# secrets set <key> <value>
|
|
45
|
+
set_parser = secrets_subparsers.add_parser("set", help="Сохранить секрет")
|
|
46
|
+
set_parser.add_argument("key", help="Имя ключа")
|
|
47
|
+
set_parser.add_argument("value", help="Значение секрета")
|
|
48
|
+
set_parser.add_argument("-s", "--service", help="Имя сервиса (service_name) для keyring")
|
|
49
|
+
|
|
50
|
+
# secrets delete <key>
|
|
51
|
+
delete_parser = secrets_subparsers.add_parser("delete", help="Удалить секрет")
|
|
52
|
+
delete_parser.add_argument("key", help="Имя ключа")
|
|
53
|
+
delete_parser.add_argument("-s", "--service", help="Имя сервиса (service_name) для keyring")
|
|
54
|
+
|
|
55
|
+
args = parser.parse_args()
|
|
56
|
+
|
|
57
|
+
if args.command == "secrets":
|
|
58
|
+
if args.subcommand == "set":
|
|
59
|
+
handle_secrets_set(args)
|
|
60
|
+
elif args.subcommand == "delete":
|
|
61
|
+
handle_secrets_delete(args)
|
|
62
|
+
else:
|
|
63
|
+
secrets_parser.print_help()
|
|
64
|
+
else:
|
|
65
|
+
parser.print_help()
|
|
66
|
+
|
|
67
|
+
sys.exit(0)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
main()
|