chutils 2.3.0__tar.gz → 2.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.
chutils-2.4.0/PKG-INFO ADDED
@@ -0,0 +1,418 @@
1
+ Metadata-Version: 2.4
2
+ Name: chutils
3
+ Version: 2.4.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: dotenv (>=0.9.9,<0.10.0)
18
+ Requires-Dist: keyring (>=25.7.0,<26.0.0)
19
+ Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
20
+ Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ [Русская версия](docs/README_RU.md)
24
+
25
+ # chutils: Stop the Routine!
26
+
27
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
28
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
29
+ [![PyPI version](https://badge.fury.io/py/chutils.svg)](https://badge.fury.io/py/chutils)
30
+ [![Documentation](https://img.shields.io/badge/documentation-read-brightgreen)](https://Chu4hel.github.io/chutils/)
31
+
32
+ **chutils** is a set of simple utilities for Python designed to eliminate the repetitive setup of configuration,
33
+ logging, and secrets in your projects.
34
+
35
+ Start a new project and focus on what matters, not the routine.
36
+
37
+ Full documentation is available on [our website](https://Chu4hel.github.io/chutils/) (currently in Russian).
38
+
39
+ ## The Problem
40
+
41
+ Every time you start a new project, you have to solve the same tasks:
42
+
43
+ - How to conveniently read settings from a configuration file?
44
+ - How to configure logging to write messages to both the console and a file with daily rotation?
45
+ - How to securely store API keys without hardcoding them in the code?
46
+ - How to make it all work "out of the box" without manually defining paths?
47
+
48
+ **chutils** offers ready-made solutions for all these problems.
49
+
50
+ ## Key Features
51
+
52
+ - **✨ Zero Configuration:** The library **automatically** finds your project root and the `config.yml` or `config.ini`
53
+ file. If the file is not found, safe defaults are used.
54
+ - **⚙️ Flexible Configuration:** Support for `YAML` and `INI` formats. Simple functions for retrieving typed data.
55
+ - **✍️ Advanced Logger:** The `setup_logger()` function configures logging to the console and rotating files out of the
56
+ box. It returns a custom logger with additional debug levels (`devdebug`, `mediumdebug`).
57
+ - **🔒 Secure Secret Storage:** The `secret_manager` module provides a simple interface for saving and retrieving secrets
58
+ via the system `keyring`, with a fallback to `.env` files.
59
+ - **🚀 Ready to Use:** Just install and use.
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ poetry add chutils
65
+ ```
66
+
67
+ Or using pip:
68
+
69
+ ```bash
70
+ pip install chutils
71
+ ```
72
+
73
+ For development, clone the repository and install in editable mode:
74
+
75
+ ```bash
76
+ git clone https://github.com/Chu4hel/chutils.git
77
+ cd chutils
78
+ pip install -e .
79
+ ```
80
+
81
+ ## Examples
82
+
83
+ In the [`/examples`](./examples/) folder, you will find ready-to-run scripts demonstrating the library's key features.
84
+ Each example focuses on a specific task.
85
+
86
+ ## Quick Start
87
+
88
+ ### 1. Working with Configuration
89
+
90
+ 1. (Optional) Create a `config.yml` file in your project root. If you skip this, the library will use defaults:
91
+
92
+ ```yaml
93
+ # config.yml
94
+ Database:
95
+ host: localhost
96
+ port: 5432
97
+ user: my_user
98
+ ```
99
+
100
+ 2. Get values in your code:
101
+
102
+ ```python
103
+ # main.py
104
+ from chutils import get_config_value, get_config_int
105
+
106
+ db_host = get_config_value("Database", "host", fallback="127.0.0.1")
107
+ db_port = get_config_int("Database", "port", fallback=5433)
108
+
109
+ print(f"Connecting to DB at: {db_host}:{db_port}")
110
+ # Output: Connecting to DB at: localhost:5432
111
+ ```
112
+ `chutils` will automatically find `config.yml` and read the data.
113
+
114
+ #### Overriding Configuration with Local Files (`config.local.yml`)
115
+
116
+ You can create a local configuration file (e.g., `config.local.yml` or `config.local.ini`) next to your main file (
117
+ `config.yml` or `config.ini`). Values from the local file will **override** corresponding values from the main file.
118
+ This is useful for:
119
+ - Storing sensitive data that should not be committed to version control (add `config.local.yml` to `.gitignore`).
120
+ - Overriding settings for local development without changing the main file.
121
+
122
+ **Example:**
123
+ If `config.yml` contains:
124
+ ```yaml
125
+ # config.yml
126
+ Database:
127
+ host: production_db.com
128
+ port: 5432
129
+ App:
130
+ debug: false
131
+ ```
132
+ And `config.local.yml` contains:
133
+ ```yaml
134
+ # config.local.yml
135
+ Database:
136
+ host: localhost
137
+ App:
138
+ debug: true
139
+ developer_mode: true
140
+ ```
141
+ Then `get_config()` will return:
142
+ ```yaml
143
+ Database:
144
+ host: localhost # Overridden by local file
145
+ port: 5432 # From main file
146
+ App:
147
+ debug: true # Overridden by local file
148
+ developer_mode: true # Added from local file
149
+ ```
150
+ **Important:** Ensure you add `config.local.yml` (or `config.local.ini`) to your `.gitignore`.
151
+
152
+ ### 2. Logging Setup
153
+
154
+ 1. Add a `Logging` section to your `config.yml` (optional):
155
+
156
+ ```yaml
157
+ # config.yml
158
+ Logging:
159
+ log_level: DEBUG
160
+ log_file_name: my_app.log
161
+ ```
162
+
163
+ 2. Use the logger:
164
+
165
+ ```python
166
+ # main.py
167
+ from chutils import setup_logger, ChutilsLogger
168
+
169
+ # Configure logger. It automatically reads settings from config.
170
+ logger: ChutilsLogger = setup_logger()
171
+
172
+ logger.info("Application started.")
173
+ logger.debug("This is a debug message.")
174
+ # Output to console and writes to file logs/my_app.log
175
+ ```
176
+ The `logs` folder will be created automatically.
177
+
178
+ You can also specify the log filename directly when calling `setup_logger`, overriding the config:
179
+ ```python
180
+ # main.py
181
+ from chutils import setup_logger, ChutilsLogger
182
+
183
+ # Logger will write to custom.log, ignoring log_file_name from config.yml
184
+ logger: ChutilsLogger = setup_logger(log_file_name="custom.log")
185
+
186
+ logger.info("Message in a custom file.")
187
+ ```
188
+
189
+ #### Creating Multiple Loggers
190
+
191
+ You can create different loggers for different parts of your application by passing a unique name to `setup_logger`.
192
+ This helps filter and separate logs.
193
+
194
+ ```python
195
+ # main.py
196
+ from chutils import setup_logger
197
+
198
+ # Main app logger will write to main_app.log
199
+ main_logger = setup_logger("main_app", log_file_name="main_app.log")
200
+ # Logger for the database module will write to database.log
201
+ db_logger = setup_logger("database", log_file_name="database.log")
202
+
203
+ main_logger.info("Application started.")
204
+ db_logger.debug("Initializing DB connection...")
205
+ ```
206
+ See [`/examples/05_different_log_levels.py`](./examples/05_different_log_levels.py) for a detailed example.
207
+
208
+ #### Configuring Multiple Loggers via File
209
+
210
+ You can centrally manage settings for different loggers using the `config_section_name` parameter.
211
+
212
+ 1. **Add sections to `config.yml`**:
213
+ The `[Logging]` section is used for defaults. Other sections can be used for specific loggers.
214
+ ```yaml
215
+ # config.yml
216
+ Logging:
217
+ log_level: INFO
218
+ rotation_type: time
219
+ compress: true
220
+
221
+ AuditLogger:
222
+ log_level: DEBUG
223
+ log_file_name: "audit.log"
224
+ ```
225
+
226
+ 2. **Use `config_section_name` in code**:
227
+ ```python
228
+ # main.py
229
+ from chutils import setup_logger
230
+
231
+ # This logger takes settings from [Logging]
232
+ main_logger = setup_logger("main")
233
+ main_logger.info("Message from main logger.")
234
+
235
+ # This logger takes settings from [AuditLogger], overriding defaults
236
+ audit_logger = setup_logger("audit", config_section_name="AuditLogger")
237
+ audit_logger.debug("Detailed audit message.")
238
+ ```
239
+
240
+ ### 3. Secret Management
241
+
242
+ `SecretManager` looks for secrets in the following order:
243
+
244
+ 1. **System Storage (`keyring`)**: The most secure method.
245
+ 2. **`.env` File**: If the secret is not found in `keyring`, the manager looks in the `.env` file in the project root.
246
+ 3. **Environment Variables**: If not found there either, it checks OS environment variables.
247
+
248
+ #### Method 1: Keyring (Recommended)
249
+
250
+ 1. Initialize `SecretManager` and save your secret. **Do this once.**
251
+
252
+ ```python
253
+ # setup_secrets.py
254
+ from chutils import SecretManager
255
+
256
+ secrets = SecretManager("my_awesome_app")
257
+ secrets.save_secret("DB_PASSWORD", "MySuperSecretDbPassword123!")
258
+ print("DB password saved to system storage!")
259
+ ```
260
+
261
+ 2. Retrieve the secret in your main code without exposing it:
262
+
263
+ ```python
264
+ # main.py
265
+ from chutils import SecretManager, get_config_value
266
+
267
+ secrets = SecretManager("my_awesome_app")
268
+ db_user = get_config_value("Database", "user")
269
+
270
+ # Get password from secure storage
271
+ db_password = secrets.get_secret("DB_PASSWORD")
272
+
273
+ if db_password:
274
+ print(f"Password retrieved for user {db_user}.")
275
+ else:
276
+ print("Password not found!")
277
+ ```
278
+
279
+ #### Method 2: .env File (Useful for Docker and CI/CD)
280
+
281
+ 1. Create a `.env` file in your project root:
282
+ ```dotenv
283
+ # .env
284
+ DB_PASSWORD="AnotherSecretPassword"
285
+ API_KEY="abcdef123456"
286
+ ```
287
+
288
+ 2. `SecretManager` automatically finds this file and reads variables if not found in `keyring`.
289
+
290
+ ```python
291
+ # main.py
292
+ from chutils import SecretManager
293
+
294
+ secrets = SecretManager("my_awesome_app")
295
+
296
+ # This secret will be taken from .env if not in keyring
297
+ api_key = secrets.get_secret("API_KEY")
298
+ print(f"Found API key: {api_key}")
299
+ ```
300
+
301
+ ## Comprehensive Example
302
+
303
+ This example shows how all `chutils` components work together.
304
+
305
+ 1. **`config.yml`:**
306
+ ```yaml
307
+ API:
308
+ base_url: https://api.example.com
309
+
310
+ Database:
311
+ host: localhost
312
+ port: 5432
313
+ user: my_user
314
+
315
+ Logging:
316
+ log_level: INFO
317
+ ```
318
+
319
+ 2. **`main.py`:**
320
+ ```python
321
+ # main.py
322
+ from chutils import get_config_value, setup_logger, SecretManager, ChutilsLogger
323
+
324
+ # 1. Setup logger. It automatically reads settings from config.
325
+ logger: ChutilsLogger = setup_logger()
326
+
327
+ # 2. Initialize secret manager for our app.
328
+ secrets = SecretManager("my_awesome_app")
329
+
330
+ def setup_credentials():
331
+ """Function to save password initially if missing."""
332
+ db_user = get_config_value("Database", "user")
333
+ password_key = f"{db_user}_password"
334
+
335
+ if not secrets.get_secret(password_key):
336
+ logger.info("DB password not found. Saving new one...")
337
+ secrets.save_secret(password_key, "MySuperSecretDbPassword123!")
338
+ logger.info("DB password saved to system storage.")
339
+
340
+ def connect_to_db():
341
+ """Example DB connection using config and secrets."""
342
+ db_host = get_config_value("Database", "host")
343
+ db_user = get_config_value("Database", "user")
344
+ db_password = secrets.get_secret(f"{db_user}_password")
345
+
346
+ if not db_password:
347
+ logger.error("Failed to retrieve DB password!")
348
+ return
349
+
350
+ logger.info(f"Connecting to {db_host} as {db_user}...")
351
+ # ... connection logic ...
352
+ logger.info("Connected successfully!")
353
+
354
+ def main():
355
+ logger.info("App started.")
356
+ setup_credentials()
357
+ connect_to_db()
358
+ logger.info("App finished.")
359
+
360
+ if __name__ == "__main__":
361
+ main()
362
+ ```
363
+
364
+ ## API
365
+
366
+ ### Configuration (`chutils.config`)
367
+
368
+ - `get_config_value(section, key, fallback="")`: Get a value.
369
+ - `get_config_int(section, key, fallback=0)`: Get an integer.
370
+ - `get_config_boolean(section, key, fallback=False)`: Get a boolean.
371
+ - `get_config_list(section, key, fallback=[])`: Get a list.
372
+ - `get_config_section(section)`: Get the entire section as a dictionary.
373
+ - `save_config_value(section, key, value)`: Save a value. Works for `.yml` and `.ini`.
374
+ **Note**: comments and formatting are lost when saving to `.yml`. They are preserved for `.ini`.
375
+
376
+ ### Logging (`chutils.logger`)
377
+
378
+ - `setup_logger(name='app_logger', log_level_str='')`: Configures and returns a `ChutilsLogger` instance.
379
+ - `logger.mediumdebug("message")`: Log with level 15.
380
+ - `logger.devdebug("message")`: Log with level 9.
381
+
382
+ ### Secret Management (`chutils.secret_manager`)
383
+
384
+ - `SecretManager(service_name, prefix="Chutils_")`: Creates a manager isolated by service name.
385
+ - `secrets.save_secret(key, value)`: Saves a secret.
386
+ - `secrets.get_secret(key)`: Retrieves a secret.
387
+ - `secrets.delete_secret(key)`: Deletes a secret.
388
+
389
+ ### Decorators (`chutils.decorators`)
390
+
391
+ - `log_function_details`: Decorator for logging function call details (arguments, execution time, result).
392
+
393
+ ### Manual Initialization (`chutils.init`)
394
+
395
+ In 99% of cases, you **will not need this**. But if automation fails, you can manually specify the project path once at
396
+ the very beginning:
397
+
398
+ ```python
399
+ import chutils
400
+
401
+ chutils.init(base_dir="/path/to/my/project/root")
402
+ ```
403
+
404
+ ### Note on `secret_manager` (Keyring)
405
+
406
+ The `SecretManager` module uses the `keyring` library to securely store secrets in system storage.
407
+
408
+ - On **Windows** and **macOS**, this works "out of the box".
409
+ - **Linux Requirements**: Secure `keyring` operation on Linux requires an installed and configured backend (secret
410
+ storage), such as `GNOME Keyring` (Seahorse) or `KWallet`. On servers or minimal builds, you may need to install this
411
+ manually.
412
+ See the [official `keyring` documentation](https://keyring.readthedocs.io/en/latest/) for details.
413
+ - **Mobile OS**: This module is **not intended** for use on mobile operating systems (Android, iOS). `keyring` will
414
+ likely not find system storage and may use an **insecure** method to store your secrets.
415
+
416
+ ## License
417
+
418
+ The project is distributed under the MIT License.