potato-util 0.0.2__tar.gz → 0.0.3__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 (43) hide show
  1. {potato_util-0.0.2/src/potato_util.egg-info → potato_util-0.0.3}/PKG-INFO +191 -18
  2. potato_util-0.0.3/README.md +381 -0
  3. {potato_util-0.0.2 → potato_util-0.0.3}/pyproject.toml +13 -17
  4. potato_util-0.0.3/requirements/requirements.dev.txt +2 -0
  5. {potato_util-0.0.2 → potato_util-0.0.3}/requirements/requirements.test.txt +0 -1
  6. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/__init__.py +3 -5
  7. potato_util-0.0.3/src/potato_util/__version__.py +1 -0
  8. potato_util-0.0.3/src/potato_util/_base.py +57 -0
  9. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/dt.py +128 -104
  10. potato_util-0.0.2/src/potato_util/secure.py → potato_util-0.0.3/src/potato_util/generator.py +3 -28
  11. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/http/_base.py +3 -1
  12. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/http/fastapi.py +1 -1
  13. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/io/_async.py +36 -24
  14. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/io/_sync.py +40 -24
  15. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/sanitizer.py +12 -3
  16. potato_util-0.0.3/src/potato_util/secure.py +37 -0
  17. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/validator.py +12 -6
  18. {potato_util-0.0.2 → potato_util-0.0.3/src/potato_util.egg-info}/PKG-INFO +191 -18
  19. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util.egg-info/SOURCES.txt +1 -0
  20. potato_util-0.0.2/README.md +0 -208
  21. potato_util-0.0.2/requirements/requirements.dev.txt +0 -6
  22. potato_util-0.0.2/src/potato_util/__version__.py +0 -1
  23. potato_util-0.0.2/src/potato_util/_base.py +0 -117
  24. {potato_util-0.0.2 → potato_util-0.0.3}/.python-version +0 -0
  25. {potato_util-0.0.2 → potato_util-0.0.3}/LICENSE.txt +0 -0
  26. {potato_util-0.0.2 → potato_util-0.0.3}/requirements/requirements.async.txt +0 -0
  27. {potato_util-0.0.2 → potato_util-0.0.3}/requirements/requirements.build.txt +0 -0
  28. {potato_util-0.0.2 → potato_util-0.0.3}/requirements/requirements.docs.txt +0 -0
  29. {potato_util-0.0.2 → potato_util-0.0.3}/requirements/requirements.fastapi.txt +0 -0
  30. {potato_util-0.0.2 → potato_util-0.0.3}/requirements.txt +0 -0
  31. {potato_util-0.0.2 → potato_util-0.0.3}/setup.cfg +0 -0
  32. {potato_util-0.0.2 → potato_util-0.0.3}/setup.py +0 -0
  33. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/constants/__init__.py +0 -0
  34. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/constants/_base.py +0 -0
  35. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/constants/_enum.py +0 -0
  36. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/constants/_regex.py +0 -0
  37. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/http/__init__.py +0 -0
  38. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/http/_async.py +0 -0
  39. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/http/_sync.py +0 -0
  40. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util/io/__init__.py +0 -0
  41. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util.egg-info/dependency_links.txt +0 -0
  42. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util.egg-info/requires.txt +0 -0
  43. {potato_util-0.0.2 → potato_util-0.0.3}/src/potato_util.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: potato_util
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: 'potato_util' is collection of simple useful utils package for python.
5
5
  Author-email: Batkhuu Byambajav <batkhuu10@gmail.com>
6
- Project-URL: Homepage, https://github.com/bybatkhuu/module.python-utils
6
+ Project-URL: Homepage, https://github.com/bybatkhuu/module-python-utils
7
7
  Project-URL: Documentation, https://pyutils-docs.bybatkhuu.dev
8
- Project-URL: Repository, https://github.com/bybatkhuu/module.python-utils.git
9
- Project-URL: Issues, https://github.com/bybatkhuu/module.python-utils/issues
10
- Project-URL: Changelog, https://github.com/bybatkhuu/module.python-utils/blob/main/CHANGELOG.md
8
+ Project-URL: Repository, https://github.com/bybatkhuu/module-python-utils.git
9
+ Project-URL: Issues, https://github.com/bybatkhuu/module-python-utils/issues
10
+ Project-URL: Changelog, https://github.com/bybatkhuu/module-python-utils/blob/main/CHANGELOG.md
11
11
  Keywords: potato_util,utils,utilities,tools,helpers
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
@@ -77,11 +77,11 @@ Requires-Dist: pyright<2.0.0,>=1.1.392; extra == "dev"
77
77
  Requires-Dist: pre-commit<5.0.0,>=4.0.1; extra == "dev"
78
78
  Dynamic: license-file
79
79
 
80
- # Potato Utils (Python Utils)
80
+ # Potato Util (Python Utils)
81
81
 
82
82
  [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit)
83
- [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bybatkhuu/module.python-utils/2.build-publish.yml?logo=GitHub)](https://github.com/bybatkhuu/module.python-utils/actions/workflows/2.build-publish.yml)
84
- [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bybatkhuu/module.python-utils?logo=GitHub&color=blue)](https://github.com/bybatkhuu/module.python-utils/releases)
83
+ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bybatkhuu/module-python-utils/2.build-publish.yml?logo=GitHub)](https://github.com/bybatkhuu/module-python-utils/actions/workflows/2.build-publish.yml)
84
+ [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bybatkhuu/module-python-utils?logo=GitHub&color=blue)](https://github.com/bybatkhuu/module-python-utils/releases)
85
85
 
86
86
  'potato_util' is collection of simple useful utils package for python.
87
87
 
@@ -89,11 +89,13 @@ Dynamic: license-file
89
89
 
90
90
  - Python utilities
91
91
  - Datetime utilities
92
- - File I/O utilities
93
- - HTTP utilities
94
- - Security utilities
92
+ - Generator utilities
95
93
  - Sanitation utilities
94
+ - Security utilities
96
95
  - Validation utilities
96
+ - HTTP utilities
97
+ - File I/O utilities
98
+ - And more...
97
99
 
98
100
  ---
99
101
 
@@ -130,20 +132,20 @@ cd ~/workspaces/projects
130
132
  **OPTION A.** Clone the repository:
131
133
 
132
134
  ```sh
133
- git clone https://github.com/bybatkhuu/module.python-utils.git && \
134
- cd module.python-utils
135
+ git clone https://github.com/bybatkhuu/module-python-utils.git && \
136
+ cd module-python-utils
135
137
  ```
136
138
 
137
139
  **OPTION B.** Clone the repository (for **DEVELOPMENT**: git + ssh key):
138
140
 
139
141
  ```sh
140
- git clone git@github.com:bybatkhuu/module.python-utils.git && \
141
- cd module.python-utils
142
+ git clone git@github.com:bybatkhuu/module-python-utils.git && \
143
+ cd module-python-utils
142
144
  ```
143
145
 
144
146
  **OPTION C.** Download source code:
145
147
 
146
- 1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module.python-utils/releases).
148
+ 1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases).
147
149
  2. Extract it into the projects directory.
148
150
 
149
151
  ### 3. 📦 Install the package
@@ -159,7 +161,7 @@ pip install -U potato-util
159
161
  **OPTION B.** Install latest version directly from **GitHub** repository:
160
162
 
161
163
  ```sh
162
- pip install git+https://github.com/bybatkhuu/module.python-utils.git
164
+ pip install git+https://github.com/bybatkhuu/module-python-utils.git
163
165
  ```
164
166
 
165
167
  **OPTION C.** Install from the downloaded **source code**:
@@ -180,7 +182,7 @@ pip install -e .[dev]
180
182
 
181
183
  **OPTION E.** Install from **pre-built release** files:
182
184
 
183
- 1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module.python-utils/releases)
185
+ 1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases)
184
186
  2. Install with pip:
185
187
 
186
188
  ```sh
@@ -210,6 +212,177 @@ cp -r ./src/potato_util /some/path/project/
210
212
  [**`examples/simple/main.py`**](./examples/simple/main.py):
211
213
 
212
214
  ```python
215
+ #!/usr/bin/env python
216
+
217
+ # Standard libraries
218
+ import os
219
+ import sys
220
+ import logging
221
+
222
+ # Third-party libraries
223
+ from pydantic import AnyHttpUrl
224
+
225
+ # Internal modules
226
+ import potato_util
227
+ import potato_util.dt as dt_utils
228
+ import potato_util.generator as gen_utils
229
+ import potato_util.sanitizer as sanitizer_utils
230
+ import potato_util.secure as secure_utils
231
+ import potato_util.validator as validator_utils
232
+ import potato_util.http as http_utils
233
+
234
+
235
+ logger = logging.getLogger(__name__)
236
+
237
+
238
+ def main() -> None:
239
+ _log_level = logging.INFO
240
+ if str(os.getenv("DEBUG", "0")).lower() in ("1", "true", "t", "yes", "y"):
241
+ _log_level = logging.DEBUG
242
+
243
+ logging.basicConfig(
244
+ stream=sys.stdout,
245
+ level=_log_level,
246
+ datefmt="%Y-%m-%d %H:%M:%S %z",
247
+ format="[%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d]: %(message)s",
248
+ )
249
+
250
+ # Base utils:
251
+ logger.info("[BASE UTILITIES]")
252
+ _dict1 = {"a": 1, "b": {"c": 2, "d": 3}, "g": [2, 3, 4]}
253
+ _dict2 = {"b": {"c": 20, "e": 30}, "f": 40, "g": [5, 6, 7]}
254
+ _merged_dict = potato_util.deep_merge(_dict1, _dict2)
255
+ logger.info(f"Merged dict: {_merged_dict}")
256
+
257
+ _camel_str = "CamelCaseString"
258
+ _snake_str = potato_util.camel_to_snake(_camel_str)
259
+ logger.info(f"Converted '{_camel_str}' to '{_snake_str}'")
260
+ logger.info("-" * 80)
261
+
262
+ # Datetime utils:
263
+ logger.info("[DATETIME UTILITIES]")
264
+ _now_local_dt = dt_utils.now_local_dt()
265
+ logger.info(f"Current local datetime: {_now_local_dt}")
266
+
267
+ _now_utc_dt = dt_utils.now_utc_dt()
268
+ logger.info(f"Current UTC datetime: {_now_utc_dt}")
269
+
270
+ _now_ny_dt = dt_utils.now_dt(tz="America/New_York")
271
+ logger.info(f"Current New York datetime: {_now_ny_dt}")
272
+
273
+ _now_ts = dt_utils.now_ts()
274
+ logger.info(f"Current UTC timestamp (seconds): {_now_ts}")
275
+
276
+ _now_ts_ms = dt_utils.now_ts(unit="MILLISECONDS")
277
+ logger.info(f"Current UTC timestamp (ms): {_now_ts_ms}")
278
+
279
+ _now_ts_micro = dt_utils.now_ts(unit="MICROSECONDS")
280
+ logger.info(f"Current UTC timestamp (microseconds): {_now_ts_micro}")
281
+
282
+ _now_ts_ns = dt_utils.now_ts(unit="NANOSECONDS")
283
+ logger.info(f"Current UTC timestamp (nanoseconds): {_now_ts_ns}")
284
+
285
+ _dt_ts = dt_utils.dt_to_ts(dt=_now_local_dt)
286
+ logger.info(f"Converted local datetime to UTC timestamp (seconds): {_dt_ts}")
287
+
288
+ _replaced_tz_dt = dt_utils.replace_tz(dt=_now_local_dt, tz="Asia/Ulaanbaatar")
289
+ logger.info(f"Add or replace timezone with Asia/Ulaanbaatar: {_replaced_tz_dt}")
290
+
291
+ _converted_dt = dt_utils.convert_tz(dt=_now_ny_dt, tz="Asia/Seoul")
292
+ logger.info(
293
+ f"Calculated and converted timezone from New York to Seoul: {_converted_dt}"
294
+ )
295
+
296
+ _dt_iso = dt_utils.dt_to_iso(dt=_now_local_dt)
297
+ logger.info(f"Parsing datetime to ISO 8601 format string: {_dt_iso}")
298
+
299
+ _future_dt = dt_utils.calc_future_dt(delta=3600, dt=_now_local_dt, tz="Asia/Tokyo")
300
+ logger.info(f"Calculated future datetime after 3600 seconds in Tokyo: {_future_dt}")
301
+ logger.info("-" * 80)
302
+
303
+ # Generator utils:
304
+ logger.info("[GENERATOR UTILITIES]")
305
+ _unique_id = gen_utils.gen_unique_id(prefix="item_")
306
+ logger.info(f"Generated unique ID based on datetime and UUIDv4: {_unique_id}")
307
+
308
+ _random_str = gen_utils.gen_random_string(length=32, is_alphanum=False)
309
+ logger.info(f"Generated secure random string: {_random_str}")
310
+ logger.info("-" * 80)
311
+
312
+ # Sanitizer utils:
313
+ logger.info("[SANITIZER UTILITIES]")
314
+ _raw_html = ' <script>alert("XSS Attack!");</script> '
315
+ _escaped_html = sanitizer_utils.escape_html(val=_raw_html)
316
+ logger.info(f"Escaped HTML: {_escaped_html}")
317
+
318
+ _raw_url = "https://www.example.com/search?q=potato util&body=<script>alert('Attack!')</script>&lang=한국어"
319
+ _escaped_url = sanitizer_utils.escape_url(val=_raw_url)
320
+ logger.info(f"Escaped URL: {_escaped_url}")
321
+
322
+ _raw_str = "Hello@World! This is a test_string with special#chars$%&*()[]{};:'\",.<>?/\\|`~"
323
+ _sanitized_str = sanitizer_utils.sanitize_special_chars(val=_raw_str, mode="STRICT")
324
+ logger.info(f"Sanitized string: {_sanitized_str}")
325
+ logger.info("-" * 80)
326
+
327
+ # Secure utils:
328
+ logger.info("[SECURE UTILITIES]")
329
+ _input_str = "SensitiveInformation123!"
330
+ _hashed_str_sha256 = secure_utils.hash_str(val=_input_str, algorithm="sha256")
331
+ logger.info(f"SHA-256 hashed string: {_hashed_str_sha256}")
332
+ logger.info("-" * 80)
333
+
334
+ # Validator utils:
335
+ logger.info("[VALIDATOR UTILITIES]")
336
+ _is_yes_truthy = validator_utils.is_truthy(val="Yes")
337
+ logger.info(f"Is 'Yes' truthy: {_is_yes_truthy}")
338
+ _is_off_truthy = validator_utils.is_truthy(val="OFF")
339
+ logger.info(f"Is 'OFF' truthy: {_is_off_truthy}")
340
+
341
+ _is_no_falsy = validator_utils.is_falsy(val="f")
342
+ logger.info(f"Is 'f' falsy: {_is_no_falsy}")
343
+ _is_1_falsy = validator_utils.is_falsy(val="1")
344
+ logger.info(f"Is '1' falsy: {_is_1_falsy}")
345
+
346
+ _request_id = "f058ebd6-02f7-4d3f-942e-904344e8cde5"
347
+ _is_valid_request_id = validator_utils.is_request_id(val=_request_id)
348
+ logger.info(f"Is '{_request_id}' a valid request ID: {_is_valid_request_id}")
349
+
350
+ _blacklist = ["hacker", "guest"]
351
+ _input_username = "hacker"
352
+ _is_blacklisted = validator_utils.is_blacklisted(
353
+ val=_input_username, blacklist=_blacklist
354
+ )
355
+ logger.info(f"Is '{_input_username}' blacklisted: {_is_blacklisted}")
356
+
357
+ _pattern = r"^[a-zA-Z0-9_]{3,16}$" # Alphanumeric and underscores, 3-16 chars
358
+ _test_username = "valid_user123"
359
+ _is_valid_username = validator_utils.is_valid(val=_test_username, pattern=_pattern)
360
+ logger.info(f"Is '{_test_username}' a valid username: {_is_valid_username}")
361
+
362
+ _string_with_special_chars = "Hello@World!"
363
+ _has_special_chars = validator_utils.has_special_chars(
364
+ val=_string_with_special_chars, mode="STRICT"
365
+ )
366
+ logger.info(
367
+ f"Does '{_string_with_special_chars}' have special chars: {_has_special_chars}"
368
+ )
369
+ logger.info("-" * 80)
370
+
371
+ # HTTP utils:
372
+ logger.info("[HTTP UTILITIES]")
373
+ _http_status_tuple = http_utils.get_http_status(status_code=403)
374
+ logger.info(f"HTTP status and known: {_http_status_tuple}")
375
+
376
+ _url = AnyHttpUrl("https://www.google.com")
377
+ _is_connectable = http_utils.is_connectable(url=_url, timeout=3, check_status=True)
378
+ logger.info(f"Is '{_url}' connectable: {_is_connectable}")
379
+ logger.info("-" * 80)
380
+
381
+ return
382
+
383
+
384
+ if __name__ == "__main__":
385
+ main()
213
386
  ```
214
387
 
215
388
  👍
@@ -0,0 +1,381 @@
1
+ # Potato Util (Python Utils)
2
+
3
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit)
4
+ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bybatkhuu/module-python-utils/2.build-publish.yml?logo=GitHub)](https://github.com/bybatkhuu/module-python-utils/actions/workflows/2.build-publish.yml)
5
+ [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bybatkhuu/module-python-utils?logo=GitHub&color=blue)](https://github.com/bybatkhuu/module-python-utils/releases)
6
+
7
+ 'potato_util' is collection of simple useful utils package for python.
8
+
9
+ ## ✨ Features
10
+
11
+ - Python utilities
12
+ - Datetime utilities
13
+ - Generator utilities
14
+ - Sanitation utilities
15
+ - Security utilities
16
+ - Validation utilities
17
+ - HTTP utilities
18
+ - File I/O utilities
19
+ - And more...
20
+
21
+ ---
22
+
23
+ ## 🛠 Installation
24
+
25
+ ### 1. 🚧 Prerequisites
26
+
27
+ - Install **Python (>= v3.10)** and **pip (>= 23)**:
28
+ - **[RECOMMENDED] [Miniconda (v3)](https://www.anaconda.com/docs/getting-started/miniconda/install)**
29
+ - *[arm64/aarch64] [Miniforge (v3)](https://github.com/conda-forge/miniforge)*
30
+ - *[Python virutal environment] [venv](https://docs.python.org/3/library/venv.html)*
31
+
32
+ [OPTIONAL] For **DEVELOPMENT** environment:
33
+
34
+ - Install [**git**](https://git-scm.com/downloads)
35
+ - Setup an [**SSH key**](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh) ([video tutorial](https://www.youtube.com/watch?v=snCP3c7wXw0))
36
+
37
+ ### 2. 📥 Download or clone the repository
38
+
39
+ [TIP] Skip this step, if you're going to install the package directly from **PyPi** or **GitHub** repository.
40
+
41
+ **2.1.** Prepare projects directory (if not exists):
42
+
43
+ ```sh
44
+ # Create projects directory:
45
+ mkdir -pv ~/workspaces/projects
46
+
47
+ # Enter into projects directory:
48
+ cd ~/workspaces/projects
49
+ ```
50
+
51
+ **2.2.** Follow one of the below options **[A]**, **[B]** or **[C]**:
52
+
53
+ **OPTION A.** Clone the repository:
54
+
55
+ ```sh
56
+ git clone https://github.com/bybatkhuu/module-python-utils.git && \
57
+ cd module-python-utils
58
+ ```
59
+
60
+ **OPTION B.** Clone the repository (for **DEVELOPMENT**: git + ssh key):
61
+
62
+ ```sh
63
+ git clone git@github.com:bybatkhuu/module-python-utils.git && \
64
+ cd module-python-utils
65
+ ```
66
+
67
+ **OPTION C.** Download source code:
68
+
69
+ 1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases).
70
+ 2. Extract it into the projects directory.
71
+
72
+ ### 3. 📦 Install the package
73
+
74
+ [NOTE] Choose one of the following methods to install the package **[A ~ F]**:
75
+
76
+ **OPTION A.** [**RECOMMENDED**] Install from **PyPi**:
77
+
78
+ ```sh
79
+ pip install -U potato-util
80
+ ```
81
+
82
+ **OPTION B.** Install latest version directly from **GitHub** repository:
83
+
84
+ ```sh
85
+ pip install git+https://github.com/bybatkhuu/module-python-utils.git
86
+ ```
87
+
88
+ **OPTION C.** Install from the downloaded **source code**:
89
+
90
+ ```sh
91
+ # Install directly from the source code:
92
+ pip install .
93
+
94
+ # Or install with editable mode:
95
+ pip install -e .
96
+ ```
97
+
98
+ **OPTION D.** Install for **DEVELOPMENT** environment:
99
+
100
+ ```sh
101
+ pip install -e .[dev]
102
+ ```
103
+
104
+ **OPTION E.** Install from **pre-built release** files:
105
+
106
+ 1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases)
107
+ 2. Install with pip:
108
+
109
+ ```sh
110
+ # Install from .whl file:
111
+ pip install ./potato_util-[VERSION]-py3-none-any.whl
112
+
113
+ # Or install from .tar.gz file:
114
+ pip install ./potato_util-[VERSION].tar.gz
115
+ ```
116
+
117
+ **OPTION F.** Copy the **module** into the project directory (for **testing**):
118
+
119
+ ```sh
120
+ # Install python dependencies:
121
+ pip install -r ./requirements.txt
122
+
123
+ # Copy the module source code into the project:
124
+ cp -r ./src/potato_util [PROJECT_DIR]
125
+ # For example:
126
+ cp -r ./src/potato_util /some/path/project/
127
+ ```
128
+
129
+ ## 🚸 Usage/Examples
130
+
131
+ ### Simple
132
+
133
+ [**`examples/simple/main.py`**](./examples/simple/main.py):
134
+
135
+ ```python
136
+ #!/usr/bin/env python
137
+
138
+ # Standard libraries
139
+ import os
140
+ import sys
141
+ import logging
142
+
143
+ # Third-party libraries
144
+ from pydantic import AnyHttpUrl
145
+
146
+ # Internal modules
147
+ import potato_util
148
+ import potato_util.dt as dt_utils
149
+ import potato_util.generator as gen_utils
150
+ import potato_util.sanitizer as sanitizer_utils
151
+ import potato_util.secure as secure_utils
152
+ import potato_util.validator as validator_utils
153
+ import potato_util.http as http_utils
154
+
155
+
156
+ logger = logging.getLogger(__name__)
157
+
158
+
159
+ def main() -> None:
160
+ _log_level = logging.INFO
161
+ if str(os.getenv("DEBUG", "0")).lower() in ("1", "true", "t", "yes", "y"):
162
+ _log_level = logging.DEBUG
163
+
164
+ logging.basicConfig(
165
+ stream=sys.stdout,
166
+ level=_log_level,
167
+ datefmt="%Y-%m-%d %H:%M:%S %z",
168
+ format="[%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d]: %(message)s",
169
+ )
170
+
171
+ # Base utils:
172
+ logger.info("[BASE UTILITIES]")
173
+ _dict1 = {"a": 1, "b": {"c": 2, "d": 3}, "g": [2, 3, 4]}
174
+ _dict2 = {"b": {"c": 20, "e": 30}, "f": 40, "g": [5, 6, 7]}
175
+ _merged_dict = potato_util.deep_merge(_dict1, _dict2)
176
+ logger.info(f"Merged dict: {_merged_dict}")
177
+
178
+ _camel_str = "CamelCaseString"
179
+ _snake_str = potato_util.camel_to_snake(_camel_str)
180
+ logger.info(f"Converted '{_camel_str}' to '{_snake_str}'")
181
+ logger.info("-" * 80)
182
+
183
+ # Datetime utils:
184
+ logger.info("[DATETIME UTILITIES]")
185
+ _now_local_dt = dt_utils.now_local_dt()
186
+ logger.info(f"Current local datetime: {_now_local_dt}")
187
+
188
+ _now_utc_dt = dt_utils.now_utc_dt()
189
+ logger.info(f"Current UTC datetime: {_now_utc_dt}")
190
+
191
+ _now_ny_dt = dt_utils.now_dt(tz="America/New_York")
192
+ logger.info(f"Current New York datetime: {_now_ny_dt}")
193
+
194
+ _now_ts = dt_utils.now_ts()
195
+ logger.info(f"Current UTC timestamp (seconds): {_now_ts}")
196
+
197
+ _now_ts_ms = dt_utils.now_ts(unit="MILLISECONDS")
198
+ logger.info(f"Current UTC timestamp (ms): {_now_ts_ms}")
199
+
200
+ _now_ts_micro = dt_utils.now_ts(unit="MICROSECONDS")
201
+ logger.info(f"Current UTC timestamp (microseconds): {_now_ts_micro}")
202
+
203
+ _now_ts_ns = dt_utils.now_ts(unit="NANOSECONDS")
204
+ logger.info(f"Current UTC timestamp (nanoseconds): {_now_ts_ns}")
205
+
206
+ _dt_ts = dt_utils.dt_to_ts(dt=_now_local_dt)
207
+ logger.info(f"Converted local datetime to UTC timestamp (seconds): {_dt_ts}")
208
+
209
+ _replaced_tz_dt = dt_utils.replace_tz(dt=_now_local_dt, tz="Asia/Ulaanbaatar")
210
+ logger.info(f"Add or replace timezone with Asia/Ulaanbaatar: {_replaced_tz_dt}")
211
+
212
+ _converted_dt = dt_utils.convert_tz(dt=_now_ny_dt, tz="Asia/Seoul")
213
+ logger.info(
214
+ f"Calculated and converted timezone from New York to Seoul: {_converted_dt}"
215
+ )
216
+
217
+ _dt_iso = dt_utils.dt_to_iso(dt=_now_local_dt)
218
+ logger.info(f"Parsing datetime to ISO 8601 format string: {_dt_iso}")
219
+
220
+ _future_dt = dt_utils.calc_future_dt(delta=3600, dt=_now_local_dt, tz="Asia/Tokyo")
221
+ logger.info(f"Calculated future datetime after 3600 seconds in Tokyo: {_future_dt}")
222
+ logger.info("-" * 80)
223
+
224
+ # Generator utils:
225
+ logger.info("[GENERATOR UTILITIES]")
226
+ _unique_id = gen_utils.gen_unique_id(prefix="item_")
227
+ logger.info(f"Generated unique ID based on datetime and UUIDv4: {_unique_id}")
228
+
229
+ _random_str = gen_utils.gen_random_string(length=32, is_alphanum=False)
230
+ logger.info(f"Generated secure random string: {_random_str}")
231
+ logger.info("-" * 80)
232
+
233
+ # Sanitizer utils:
234
+ logger.info("[SANITIZER UTILITIES]")
235
+ _raw_html = ' <script>alert("XSS Attack!");</script> '
236
+ _escaped_html = sanitizer_utils.escape_html(val=_raw_html)
237
+ logger.info(f"Escaped HTML: {_escaped_html}")
238
+
239
+ _raw_url = "https://www.example.com/search?q=potato util&body=<script>alert('Attack!')</script>&lang=한국어"
240
+ _escaped_url = sanitizer_utils.escape_url(val=_raw_url)
241
+ logger.info(f"Escaped URL: {_escaped_url}")
242
+
243
+ _raw_str = "Hello@World! This is a test_string with special#chars$%&*()[]{};:'\",.<>?/\\|`~"
244
+ _sanitized_str = sanitizer_utils.sanitize_special_chars(val=_raw_str, mode="STRICT")
245
+ logger.info(f"Sanitized string: {_sanitized_str}")
246
+ logger.info("-" * 80)
247
+
248
+ # Secure utils:
249
+ logger.info("[SECURE UTILITIES]")
250
+ _input_str = "SensitiveInformation123!"
251
+ _hashed_str_sha256 = secure_utils.hash_str(val=_input_str, algorithm="sha256")
252
+ logger.info(f"SHA-256 hashed string: {_hashed_str_sha256}")
253
+ logger.info("-" * 80)
254
+
255
+ # Validator utils:
256
+ logger.info("[VALIDATOR UTILITIES]")
257
+ _is_yes_truthy = validator_utils.is_truthy(val="Yes")
258
+ logger.info(f"Is 'Yes' truthy: {_is_yes_truthy}")
259
+ _is_off_truthy = validator_utils.is_truthy(val="OFF")
260
+ logger.info(f"Is 'OFF' truthy: {_is_off_truthy}")
261
+
262
+ _is_no_falsy = validator_utils.is_falsy(val="f")
263
+ logger.info(f"Is 'f' falsy: {_is_no_falsy}")
264
+ _is_1_falsy = validator_utils.is_falsy(val="1")
265
+ logger.info(f"Is '1' falsy: {_is_1_falsy}")
266
+
267
+ _request_id = "f058ebd6-02f7-4d3f-942e-904344e8cde5"
268
+ _is_valid_request_id = validator_utils.is_request_id(val=_request_id)
269
+ logger.info(f"Is '{_request_id}' a valid request ID: {_is_valid_request_id}")
270
+
271
+ _blacklist = ["hacker", "guest"]
272
+ _input_username = "hacker"
273
+ _is_blacklisted = validator_utils.is_blacklisted(
274
+ val=_input_username, blacklist=_blacklist
275
+ )
276
+ logger.info(f"Is '{_input_username}' blacklisted: {_is_blacklisted}")
277
+
278
+ _pattern = r"^[a-zA-Z0-9_]{3,16}$" # Alphanumeric and underscores, 3-16 chars
279
+ _test_username = "valid_user123"
280
+ _is_valid_username = validator_utils.is_valid(val=_test_username, pattern=_pattern)
281
+ logger.info(f"Is '{_test_username}' a valid username: {_is_valid_username}")
282
+
283
+ _string_with_special_chars = "Hello@World!"
284
+ _has_special_chars = validator_utils.has_special_chars(
285
+ val=_string_with_special_chars, mode="STRICT"
286
+ )
287
+ logger.info(
288
+ f"Does '{_string_with_special_chars}' have special chars: {_has_special_chars}"
289
+ )
290
+ logger.info("-" * 80)
291
+
292
+ # HTTP utils:
293
+ logger.info("[HTTP UTILITIES]")
294
+ _http_status_tuple = http_utils.get_http_status(status_code=403)
295
+ logger.info(f"HTTP status and known: {_http_status_tuple}")
296
+
297
+ _url = AnyHttpUrl("https://www.google.com")
298
+ _is_connectable = http_utils.is_connectable(url=_url, timeout=3, check_status=True)
299
+ logger.info(f"Is '{_url}' connectable: {_is_connectable}")
300
+ logger.info("-" * 80)
301
+
302
+ return
303
+
304
+
305
+ if __name__ == "__main__":
306
+ main()
307
+ ```
308
+
309
+ 👍
310
+
311
+ ---
312
+
313
+ ### 🌎 Environment Variables
314
+
315
+ [**`.env.example`**](./.env.example):
316
+
317
+ ```sh
318
+ # ENV=LOCAL
319
+ # DEBUG=false
320
+ # TZ=UTC
321
+ ```
322
+
323
+ ---
324
+
325
+ ## 🧪 Running Tests
326
+
327
+ To run tests, run the following command:
328
+
329
+ ```sh
330
+ # Install python test dependencies:
331
+ pip install .[test]
332
+
333
+ # Run tests:
334
+ python -m pytest -sv -o log_cli=true
335
+ # Or use the test script:
336
+ ./scripts/test.sh -l -v -c
337
+ ```
338
+
339
+ ## 🏗️ Build Package
340
+
341
+ To build the python package, run the following command:
342
+
343
+ ```sh
344
+ # Install python build dependencies:
345
+ pip install -r ./requirements/requirements.build.txt
346
+
347
+ # Build python package:
348
+ python -m build
349
+ # Or use the build script:
350
+ ./scripts/build.sh
351
+ ```
352
+
353
+ ## 📝 Generate Docs
354
+
355
+ To build the documentation, run the following command:
356
+
357
+ ```sh
358
+ # Install python documentation dependencies:
359
+ pip install -r ./requirements/requirements.docs.txt
360
+
361
+ # Serve documentation locally (for development):
362
+ mkdocs serve -a 0.0.0.0:8000
363
+ # Or use the docs script:
364
+ ./scripts/docs.sh
365
+
366
+ # Or build documentation:
367
+ mkdocs build
368
+ # Or use the docs script:
369
+ ./scripts/docs.sh -b
370
+ ```
371
+
372
+ ## 📚 Documentation
373
+
374
+ - [Docs](./docs)
375
+
376
+ ---
377
+
378
+ ## 📑 References
379
+
380
+ - <https://packaging.python.org/en/latest/tutorials/packaging-projects>
381
+ - <https://python-packaging.readthedocs.io/en/latest>