potato-util 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl

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.
potato_util/io/_sync.py CHANGED
@@ -13,13 +13,13 @@ logger = logging.getLogger(__name__)
13
13
 
14
14
 
15
15
  @validate_call
16
- def create_dir(create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
16
+ def create_dir(create_dir: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG) -> None:
17
17
  """Create directory if `create_dir` doesn't exist.
18
18
 
19
19
  Args:
20
- create_dir (str, required): Create directory path.
21
- warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
22
- Defaults to 'DEBUG'.
20
+ create_dir (str , required): Create directory path.
21
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
22
+ Defaults to 'DEBUG'.
23
23
 
24
24
  Raises:
25
25
  ValueError: If `create_dir` argument length is out of range.
@@ -34,6 +34,9 @@ def create_dir(create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
34
34
  f"must be between 1 and {MAX_PATH_LENGTH} characters!"
35
35
  )
36
36
 
37
+ if isinstance(warn_mode, str):
38
+ warn_mode = WarnEnum(warn_mode.strip().upper())
39
+
37
40
  if not os.path.isdir(create_dir):
38
41
  try:
39
42
  _message = f"Creating '{create_dir}' directory..."
@@ -63,13 +66,13 @@ def create_dir(create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
63
66
 
64
67
 
65
68
  @validate_call
66
- def remove_dir(remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
69
+ def remove_dir(remove_dir: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG) -> None:
67
70
  """Remove directory if `remove_dir` exists.
68
71
 
69
72
  Args:
70
- remove_dir (str, required): Remove directory path.
71
- warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
72
- Defaults to 'DEBUG'.
73
+ remove_dir (str , required): Remove directory path.
74
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
75
+ Defaults to 'DEBUG'.
73
76
 
74
77
  Raises:
75
78
  ValueError: If `remove_dir` argument length is out of range.
@@ -84,6 +87,9 @@ def remove_dir(remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
84
87
  f"must be between 1 and {MAX_PATH_LENGTH} characters!"
85
88
  )
86
89
 
90
+ if isinstance(warn_mode, str):
91
+ warn_mode = WarnEnum(warn_mode.strip().upper())
92
+
87
93
  if os.path.isdir(remove_dir):
88
94
  try:
89
95
  _message = f"Removing '{remove_dir}' directory..."
@@ -113,12 +119,14 @@ def remove_dir(remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
113
119
 
114
120
 
115
121
  @validate_call
116
- def remove_dirs(remove_dirs: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
122
+ def remove_dirs(
123
+ remove_dirs: list[str], warn_mode: WarnEnum | str = WarnEnum.DEBUG
124
+ ) -> None:
117
125
  """Remove directories if `remove_dirs` exist.
118
126
 
119
127
  Args:
120
- remove_dirs (List[str], required): Remove directory paths as list.
121
- warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
128
+ remove_dirs (list[str] , required): Remove directory paths as list.
129
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
122
130
  Defaults to 'DEBUG'.
123
131
  """
124
132
 
@@ -129,13 +137,13 @@ def remove_dirs(remove_dirs: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG) ->
129
137
 
130
138
 
131
139
  @validate_call
132
- def remove_file(file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
140
+ def remove_file(file_path: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG) -> None:
133
141
  """Remove file if `file_path` exists.
134
142
 
135
143
  Args:
136
- file_path (str, required): Remove file path.
137
- warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
138
- Defaults to 'DEBUG'.
144
+ file_path (str , required): Remove file path.
145
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
146
+ Defaults to 'DEBUG'.
139
147
 
140
148
  Raises:
141
149
  ValueError: If `file_path` argument length is out of range.
@@ -150,6 +158,9 @@ def remove_file(file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
150
158
  f"must be between 1 and {MAX_PATH_LENGTH} characters!"
151
159
  )
152
160
 
161
+ if isinstance(warn_mode, str):
162
+ warn_mode = WarnEnum(warn_mode.strip().upper())
163
+
153
164
  if os.path.isfile(file_path):
154
165
  try:
155
166
  _message = f"Removing '{file_path}' file..."
@@ -179,12 +190,14 @@ def remove_file(file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
179
190
 
180
191
 
181
192
  @validate_call
182
- def remove_files(file_paths: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
193
+ def remove_files(
194
+ file_paths: list[str], warn_mode: WarnEnum | str = WarnEnum.DEBUG
195
+ ) -> None:
183
196
  """Remove files if `file_paths` exist.
184
197
 
185
198
  Args:
186
- file_paths (List[str], required): Remove file paths as list.
187
- warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
199
+ file_paths (list[str] , required): Remove file paths as list.
200
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
188
201
  Defaults to 'DEBUG'.
189
202
  """
190
203
 
@@ -199,16 +212,16 @@ def get_file_checksum(
199
212
  file_path: str,
200
213
  hash_method: HashAlgoEnum = HashAlgoEnum.md5,
201
214
  chunk_size: int = 4096,
202
- warn_mode: WarnEnum = WarnEnum.DEBUG,
215
+ warn_mode: WarnEnum | str = WarnEnum.DEBUG,
203
216
  ) -> str | None:
204
217
  """Get file checksum.
205
218
 
206
219
  Args:
207
- file_path (str , required): Target file path.
208
- hash_method (HashAlgoEnum, optional): Hash method. Defaults to `HashAlgoEnum.md5`.
209
- chunk_size (int , optional): Chunk size. Defaults to 4096.
210
- warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
211
- Defaults to 'DEBUG'.
220
+ file_path (str , required): Target file path.
221
+ hash_method (HashAlgoEnum , optional): Hash method. Defaults to `HashAlgoEnum.md5`.
222
+ chunk_size (int , optional): Chunk size. Defaults to 4096.
223
+ warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
224
+ Defaults to 'DEBUG'.
212
225
 
213
226
  Raises:
214
227
  ValueError: If `file_path` argument length is out of range.
@@ -231,6 +244,9 @@ def get_file_checksum(
231
244
  f"`chunk_size` argument value {chunk_size} is invalid, must be greater than 10!"
232
245
  )
233
246
 
247
+ if isinstance(warn_mode, str):
248
+ warn_mode = WarnEnum(warn_mode.strip().upper())
249
+
234
250
  _file_checksum: str | None = None
235
251
  if os.path.isfile(file_path):
236
252
  _file_hash = hashlib.new(hash_method.value)
potato_util/sanitizer.py CHANGED
@@ -30,7 +30,7 @@ def escape_html(val: str) -> str:
30
30
 
31
31
 
32
32
  @validate_call
33
- def escape_url(val: AnyHttpUrl) -> str:
33
+ def escape_url(val: AnyHttpUrl | str) -> str:
34
34
  """Escape URL characters.
35
35
 
36
36
  Args:
@@ -40,6 +40,9 @@ def escape_url(val: AnyHttpUrl) -> str:
40
40
  str: Escaped string.
41
41
  """
42
42
 
43
+ if isinstance(val, str):
44
+ val = AnyHttpUrl(val)
45
+
43
46
  _escaped = quote(str(val))
44
47
  return _escaped
45
48
 
@@ -47,13 +50,19 @@ def escape_url(val: AnyHttpUrl) -> str:
47
50
  @validate_call
48
51
  def sanitize_special_chars(val: str, mode: str = "LOW") -> str:
49
52
  """Sanitize special characters.
53
+ Available modes:
54
+ - "BASE" or "HTML": Basic HTML special characters.
55
+ - "LOW": Low-risk special characters.
56
+ - "MEDIUM": Medium-risk special characters.
57
+ - "HIGH", "SCRIPT", or "SQL": High-risk special characters.
58
+ - "STRICT": Strict mode, removes most special characters.
50
59
 
51
60
  Args:
52
61
  val (str, required): String to sanitize.
53
62
  mode (str, optional): Sanitization mode. Defaults to "LOW".
54
63
 
55
64
  Raises:
56
- ValueError: If `mode` is unsupported.
65
+ ValueError: If `mode` argument value is invalid.
57
66
 
58
67
  Returns:
59
68
  str: Sanitized string.
@@ -72,7 +81,7 @@ def sanitize_special_chars(val: str, mode: str = "LOW") -> str:
72
81
  elif mode == "STRICT":
73
82
  _pattern = SPECIAL_CHARS_STRICT_REGEX
74
83
  else:
75
- raise ValueError(f"Unsupported mode: {mode}")
84
+ raise ValueError(f"`mode` argument value '{mode}' is invalid!")
76
85
 
77
86
  _sanitized = re.sub(pattern=_pattern, repl="", string=val)
78
87
  return _sanitized
potato_util/secure.py CHANGED
@@ -1,73 +1,19 @@
1
- import uuid
2
- import string
3
- import secrets
4
1
  import hashlib
5
2
 
6
3
  from pydantic import validate_call
7
4
 
8
5
  from .constants import HashAlgoEnum
9
- from .dt import now_ts
10
6
 
11
7
 
12
8
  @validate_call
13
- def gen_unique_id(prefix: str = "") -> str:
14
- """Generate unique id. Format: '{prefix}{datetime}_{uuid4}'.
15
-
16
- Args:
17
- prefix (str, optional): Prefix of id. Defaults to ''.
18
-
19
- Raises:
20
- ValueError: If `prefix` length is greater than 32.
21
-
22
- Returns:
23
- str: Unique id.
24
- """
25
-
26
- prefix = prefix.strip()
27
- if 32 < len(prefix):
28
- raise ValueError(
29
- f"`prefix` argument length {len(prefix)} is too long, must be less than or equal to 32!",
30
- )
31
-
32
- _id = str(f"{prefix}{now_ts()}_{uuid.uuid4().hex}").lower()
33
- return _id
34
-
35
-
36
- @validate_call
37
- def gen_random_string(length: int = 16, is_alphanum: bool = True) -> str:
38
- """Generate secure random string.
39
-
40
- Args:
41
- length (int , optional): Length of random string. Defaults to 16.
42
- is_alphanum (bool, optional): If True, generate only alphanumeric string. Defaults to True.
43
-
44
- Raises:
45
- ValueError: If `length` is less than 1.
46
-
47
- Returns:
48
- str: Generated random string.
49
- """
50
-
51
- if length < 1:
52
- raise ValueError(
53
- f"`length` argument value {length} is too small, must be greater than or equal to 1!",
54
- )
55
-
56
- _base_chars = string.ascii_letters + string.digits
57
- if not is_alphanum:
58
- _base_chars += string.punctuation
59
-
60
- _random_str = "".join(secrets.choice(_base_chars) for _i in range(length))
61
- return _random_str
62
-
63
-
64
- @validate_call
65
- def hash_str(val: str | bytes, algorithm: HashAlgoEnum = HashAlgoEnum.sha256) -> str:
9
+ def hash_str(
10
+ val: str | bytes, algorithm: HashAlgoEnum | str = HashAlgoEnum.sha256
11
+ ) -> str:
66
12
  """Hash a string using a specified hash algorithm.
67
13
 
68
14
  Args:
69
- val (str | bytes , required): The value to be hashed.
70
- algorithm (HashAlgoEnum, required): The hash algorithm to use. Defaults to `HashAlgoEnum.sha256`.
15
+ val (str | bytes , required): The value to be hashed.
16
+ algorithm (HashAlgoEnum | str, optional): The hash algorithm to use. Defaults to `HashAlgoEnum.sha256`.
71
17
 
72
18
  Returns:
73
19
  str: The hexadecimal representation of the digest.
@@ -76,6 +22,9 @@ def hash_str(val: str | bytes, algorithm: HashAlgoEnum = HashAlgoEnum.sha256) ->
76
22
  if isinstance(val, str):
77
23
  val = val.encode("utf-8")
78
24
 
25
+ if isinstance(algorithm, str):
26
+ algorithm = HashAlgoEnum(algorithm.strip().lower())
27
+
79
28
  _hash = hashlib.new(algorithm.value)
80
29
  _hash.update(val)
81
30
 
@@ -84,7 +33,5 @@ def hash_str(val: str | bytes, algorithm: HashAlgoEnum = HashAlgoEnum.sha256) ->
84
33
 
85
34
 
86
35
  __all__ = [
87
- "gen_unique_id",
88
- "gen_random_string",
89
36
  "hash_str",
90
37
  ]
potato_util/validator.py CHANGED
@@ -18,7 +18,7 @@ def is_truthy(val: str | bool | int | float | None) -> bool:
18
18
  """Check if the value is truthy.
19
19
 
20
20
  Args:
21
- val (Union[str, bool, int, float, None], required): Value to check.
21
+ val (str | bool | int | float | None, required): Value to check.
22
22
 
23
23
  Raises:
24
24
  ValueError: If `val` argument type is string and value is invalid.
@@ -45,7 +45,7 @@ def is_falsy(val: str | bool | int | float | None) -> bool:
45
45
  """Check if the value is falsy.
46
46
 
47
47
  Args:
48
- val (Union[str, bool, int, float, None], required): Value to check.
48
+ val (str | bool | int | float | None, required): Value to check.
49
49
 
50
50
  Returns:
51
51
  bool: True if the value is falsy, False otherwise.
@@ -93,8 +93,8 @@ def is_valid(val: str, pattern: Pattern | str) -> bool:
93
93
  """Check if the string is valid with given pattern.
94
94
 
95
95
  Args:
96
- val (str , required): String to check.
97
- pattern (Union[Pattern, str], required): Pattern regex to check.
96
+ val (str , required): String to check.
97
+ pattern (Pattern | str, required): Pattern regex to check.
98
98
 
99
99
  Returns:
100
100
  bool: True if the string is valid with given pattern, False otherwise.
@@ -107,13 +107,19 @@ def is_valid(val: str, pattern: Pattern | str) -> bool:
107
107
  @validate_call
108
108
  def has_special_chars(val: str, mode: str = "LOW") -> bool:
109
109
  """Check if the string has special characters.
110
+ Available modes:
111
+ - "BASE" or "HTML": Basic HTML special characters.
112
+ - "LOW": Low-risk special characters.
113
+ - "MEDIUM": Medium-risk special characters.
114
+ - "HIGH", "SCRIPT", or "SQL": High-risk special characters.
115
+ - "STRICT": Strict mode, checks for most special characters.
110
116
 
111
117
  Args:
112
118
  val (str, required): String to check.
113
119
  mode (str, optional): Check mode. Defaults to "LOW".
114
120
 
115
121
  Raises:
116
- ValueError: If `mode` is unsupported.
122
+ ValueError: If `mode` argument value is invalid.
117
123
 
118
124
  Returns:
119
125
  bool: True if the string has special characters, False otherwise.
@@ -134,7 +140,7 @@ def has_special_chars(val: str, mode: str = "LOW") -> bool:
134
140
  elif mode == "STRICT":
135
141
  _pattern = SPECIAL_CHARS_STRICT_REGEX
136
142
  else:
137
- raise ValueError(f"Unsupported mode: {mode}")
143
+ raise ValueError(f"`mode` argument value '{mode}' is invalid!")
138
144
 
139
145
  _has_special_chars = bool(re.search(pattern=_pattern, string=val))
140
146
  return _has_special_chars
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: potato_util
3
- Version: 0.0.2
3
+ Version: 0.0.4
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,13 @@ 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
+ # Python Utils (potato-util)
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
+ [![PyPI](https://img.shields.io/pypi/v/potato-util?logo=PyPi)](https://pypi.org/project/potato-util)
86
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/potato-util?logo=Python)](https://docs.conda.io/en/latest/miniconda.html)
85
87
 
86
88
  'potato_util' is collection of simple useful utils package for python.
87
89
 
@@ -89,11 +91,13 @@ Dynamic: license-file
89
91
 
90
92
  - Python utilities
91
93
  - Datetime utilities
92
- - File I/O utilities
93
- - HTTP utilities
94
- - Security utilities
94
+ - Generator utilities
95
95
  - Sanitation utilities
96
+ - Security utilities
96
97
  - Validation utilities
98
+ - HTTP utilities
99
+ - File I/O utilities
100
+ - And more...
97
101
 
98
102
  ---
99
103
 
@@ -109,7 +113,7 @@ Dynamic: license-file
109
113
  [OPTIONAL] For **DEVELOPMENT** environment:
110
114
 
111
115
  - Install [**git**](https://git-scm.com/downloads)
112
- - 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))
116
+ - Setup an [**SSH key**](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh)
113
117
 
114
118
  ### 2. 📥 Download or clone the repository
115
119
 
@@ -130,20 +134,20 @@ cd ~/workspaces/projects
130
134
  **OPTION A.** Clone the repository:
131
135
 
132
136
  ```sh
133
- git clone https://github.com/bybatkhuu/module.python-utils.git && \
134
- cd module.python-utils
137
+ git clone https://github.com/bybatkhuu/module-python-utils.git && \
138
+ cd module-python-utils
135
139
  ```
136
140
 
137
141
  **OPTION B.** Clone the repository (for **DEVELOPMENT**: git + ssh key):
138
142
 
139
143
  ```sh
140
- git clone git@github.com:bybatkhuu/module.python-utils.git && \
141
- cd module.python-utils
144
+ git clone git@github.com:bybatkhuu/module-python-utils.git && \
145
+ cd module-python-utils
142
146
  ```
143
147
 
144
148
  **OPTION C.** Download source code:
145
149
 
146
- 1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module.python-utils/releases).
150
+ 1. Download archived **zip** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases).
147
151
  2. Extract it into the projects directory.
148
152
 
149
153
  ### 3. 📦 Install the package
@@ -159,7 +163,7 @@ pip install -U potato-util
159
163
  **OPTION B.** Install latest version directly from **GitHub** repository:
160
164
 
161
165
  ```sh
162
- pip install git+https://github.com/bybatkhuu/module.python-utils.git
166
+ pip install git+https://github.com/bybatkhuu/module-python-utils.git
163
167
  ```
164
168
 
165
169
  **OPTION C.** Install from the downloaded **source code**:
@@ -176,11 +180,14 @@ pip install -e .
176
180
 
177
181
  ```sh
178
182
  pip install -e .[dev]
183
+
184
+ # Install pre-commit hooks:
185
+ pre-commit install
179
186
  ```
180
187
 
181
188
  **OPTION E.** Install from **pre-built release** files:
182
189
 
183
- 1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module.python-utils/releases)
190
+ 1. Download **`.whl`** or **`.tar.gz`** file from [**releases**](https://github.com/bybatkhuu/module-python-utils/releases)
184
191
  2. Install with pip:
185
192
 
186
193
  ```sh
@@ -210,6 +217,177 @@ cp -r ./src/potato_util /some/path/project/
210
217
  [**`examples/simple/main.py`**](./examples/simple/main.py):
211
218
 
212
219
  ```python
220
+ #!/usr/bin/env python
221
+
222
+ # Standard libraries
223
+ import os
224
+ import sys
225
+ import logging
226
+
227
+ # Third-party libraries
228
+ from pydantic import AnyHttpUrl
229
+
230
+ # Internal modules
231
+ import potato_util
232
+ import potato_util.dt as dt_utils
233
+ import potato_util.generator as gen_utils
234
+ import potato_util.sanitizer as sanitizer_utils
235
+ import potato_util.secure as secure_utils
236
+ import potato_util.validator as validator_utils
237
+ import potato_util.http as http_utils
238
+
239
+
240
+ logger = logging.getLogger(__name__)
241
+
242
+
243
+ def main() -> None:
244
+ _log_level = logging.INFO
245
+ if str(os.getenv("DEBUG", "0")).lower() in ("1", "true", "t", "yes", "y"):
246
+ _log_level = logging.DEBUG
247
+
248
+ logging.basicConfig(
249
+ stream=sys.stdout,
250
+ level=_log_level,
251
+ datefmt="%Y-%m-%d %H:%M:%S %z",
252
+ format="[%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d]: %(message)s",
253
+ )
254
+
255
+ # Base utils:
256
+ logger.info("[BASE UTILITIES]")
257
+ _dict1 = {"a": 1, "b": {"c": 2, "d": 3}, "g": [2, 3, 4]}
258
+ _dict2 = {"b": {"c": 20, "e": 30}, "f": 40, "g": [5, 6, 7]}
259
+ _merged_dict = potato_util.deep_merge(_dict1, _dict2)
260
+ logger.info(f"Merged dict: {_merged_dict}")
261
+
262
+ _camel_str = "CamelCaseString"
263
+ _snake_str = potato_util.camel_to_snake(_camel_str)
264
+ logger.info(f"Converted '{_camel_str}' to '{_snake_str}'")
265
+ logger.info("-" * 80)
266
+
267
+ # Datetime utils:
268
+ logger.info("[DATETIME UTILITIES]")
269
+ _now_local_dt = dt_utils.now_local_dt()
270
+ logger.info(f"Current local datetime: {_now_local_dt}")
271
+
272
+ _now_utc_dt = dt_utils.now_utc_dt()
273
+ logger.info(f"Current UTC datetime: {_now_utc_dt}")
274
+
275
+ _now_ny_dt = dt_utils.now_dt(tz="America/New_York")
276
+ logger.info(f"Current New York datetime: {_now_ny_dt}")
277
+
278
+ _now_ts = dt_utils.now_ts()
279
+ logger.info(f"Current UTC timestamp (seconds): {_now_ts}")
280
+
281
+ _now_ts_ms = dt_utils.now_ts(unit="MILLISECONDS")
282
+ logger.info(f"Current UTC timestamp (ms): {_now_ts_ms}")
283
+
284
+ _now_ts_micro = dt_utils.now_ts(unit="MICROSECONDS")
285
+ logger.info(f"Current UTC timestamp (microseconds): {_now_ts_micro}")
286
+
287
+ _now_ts_ns = dt_utils.now_ts(unit="NANOSECONDS")
288
+ logger.info(f"Current UTC timestamp (nanoseconds): {_now_ts_ns}")
289
+
290
+ _dt_ts = dt_utils.dt_to_ts(dt=_now_local_dt)
291
+ logger.info(f"Converted local datetime to UTC timestamp (seconds): {_dt_ts}")
292
+
293
+ _replaced_tz_dt = dt_utils.replace_tz(dt=_now_local_dt, tz="Asia/Ulaanbaatar")
294
+ logger.info(f"Add or replace timezone with Asia/Ulaanbaatar: {_replaced_tz_dt}")
295
+
296
+ _converted_dt = dt_utils.convert_tz(dt=_now_ny_dt, tz="Asia/Seoul")
297
+ logger.info(
298
+ f"Calculated and converted timezone from New York to Seoul: {_converted_dt}"
299
+ )
300
+
301
+ _dt_iso = dt_utils.dt_to_iso(dt=_now_local_dt)
302
+ logger.info(f"Parsing datetime to ISO 8601 format string: {_dt_iso}")
303
+
304
+ _future_dt = dt_utils.calc_future_dt(delta=3600, dt=_now_local_dt, tz="Asia/Tokyo")
305
+ logger.info(f"Calculated future datetime after 3600 seconds in Tokyo: {_future_dt}")
306
+ logger.info("-" * 80)
307
+
308
+ # Generator utils:
309
+ logger.info("[GENERATOR UTILITIES]")
310
+ _unique_id = gen_utils.gen_unique_id(prefix="item_")
311
+ logger.info(f"Generated unique ID based on datetime and UUIDv4: {_unique_id}")
312
+
313
+ _random_str = gen_utils.gen_random_string(length=32, is_alphanum=False)
314
+ logger.info(f"Generated secure random string: {_random_str}")
315
+ logger.info("-" * 80)
316
+
317
+ # Sanitizer utils:
318
+ logger.info("[SANITIZER UTILITIES]")
319
+ _raw_html = ' <script>alert("XSS Attack!");</script> '
320
+ _escaped_html = sanitizer_utils.escape_html(val=_raw_html)
321
+ logger.info(f"Escaped HTML: {_escaped_html}")
322
+
323
+ _raw_url = "https://www.example.com/search?q=potato util&body=<script>alert('Attack!')</script>&lang=한국어"
324
+ _escaped_url = sanitizer_utils.escape_url(val=_raw_url)
325
+ logger.info(f"Escaped URL: {_escaped_url}")
326
+
327
+ _raw_str = "Hello@World! This is a test_string with special#chars$%&*()[]{};:'\",.<>?/\\|`~"
328
+ _sanitized_str = sanitizer_utils.sanitize_special_chars(val=_raw_str, mode="STRICT")
329
+ logger.info(f"Sanitized string: {_sanitized_str}")
330
+ logger.info("-" * 80)
331
+
332
+ # Secure utils:
333
+ logger.info("[SECURE UTILITIES]")
334
+ _input_str = "SensitiveInformation123!"
335
+ _hashed_str_sha256 = secure_utils.hash_str(val=_input_str, algorithm="sha256")
336
+ logger.info(f"SHA-256 hashed string: {_hashed_str_sha256}")
337
+ logger.info("-" * 80)
338
+
339
+ # Validator utils:
340
+ logger.info("[VALIDATOR UTILITIES]")
341
+ _is_yes_truthy = validator_utils.is_truthy(val="Yes")
342
+ logger.info(f"Is 'Yes' truthy: {_is_yes_truthy}")
343
+ _is_off_truthy = validator_utils.is_truthy(val="OFF")
344
+ logger.info(f"Is 'OFF' truthy: {_is_off_truthy}")
345
+
346
+ _is_no_falsy = validator_utils.is_falsy(val="f")
347
+ logger.info(f"Is 'f' falsy: {_is_no_falsy}")
348
+ _is_1_falsy = validator_utils.is_falsy(val="1")
349
+ logger.info(f"Is '1' falsy: {_is_1_falsy}")
350
+
351
+ _request_id = "f058ebd6-02f7-4d3f-942e-904344e8cde5"
352
+ _is_valid_request_id = validator_utils.is_request_id(val=_request_id)
353
+ logger.info(f"Is '{_request_id}' a valid request ID: {_is_valid_request_id}")
354
+
355
+ _blacklist = ["hacker", "guest"]
356
+ _input_username = "hacker"
357
+ _is_blacklisted = validator_utils.is_blacklisted(
358
+ val=_input_username, blacklist=_blacklist
359
+ )
360
+ logger.info(f"Is '{_input_username}' blacklisted: {_is_blacklisted}")
361
+
362
+ _pattern = r"^[a-zA-Z0-9_]{3,16}$" # Alphanumeric and underscores, 3-16 chars
363
+ _test_username = "valid_user123"
364
+ _is_valid_username = validator_utils.is_valid(val=_test_username, pattern=_pattern)
365
+ logger.info(f"Is '{_test_username}' a valid username: {_is_valid_username}")
366
+
367
+ _string_with_special_chars = "Hello@World!"
368
+ _has_special_chars = validator_utils.has_special_chars(
369
+ val=_string_with_special_chars, mode="STRICT"
370
+ )
371
+ logger.info(
372
+ f"Does '{_string_with_special_chars}' have special chars: {_has_special_chars}"
373
+ )
374
+ logger.info("-" * 80)
375
+
376
+ # HTTP utils:
377
+ logger.info("[HTTP UTILITIES]")
378
+ _http_status_tuple = http_utils.get_http_status(status_code=403)
379
+ logger.info(f"HTTP status and known: {_http_status_tuple}")
380
+
381
+ _url = AnyHttpUrl("https://www.google.com")
382
+ _is_connectable = http_utils.is_connectable(url=_url, timeout=3, check_status=True)
383
+ logger.info(f"Is '{_url}' connectable: {_is_connectable}")
384
+ logger.info("-" * 80)
385
+
386
+ return
387
+
388
+
389
+ if __name__ == "__main__":
390
+ main()
213
391
  ```
214
392
 
215
393
  👍
@@ -0,0 +1,25 @@
1
+ potato_util/__init__.py,sha256=xl4th2Z_OmTk-3aO1w05Vh8ob0BnX4RcY1fT9tGX61c,74
2
+ potato_util/__version__.py,sha256=1mptEzQihbdyqqzMgdns_j5ZGK9gz7hR2bsgA_TnjO4,22
3
+ potato_util/_base.py,sha256=557Dj7eYmPIPE2s5yNtCKbJPOEj3lieHY4ZE1roxvFQ,1315
4
+ potato_util/dt.py,sha256=DatLzuXOxdanplBv-Kf3qOaomrTCUUBSYv_QgTtQB6U,7635
5
+ potato_util/generator.py,sha256=vdvHj1UrCOQI2WXQHQ2H63PMLQ6yOXAZPNl0m-6BgtY,1572
6
+ potato_util/sanitizer.py,sha256=pZuMDvZxKKIdwFKTcqfmHb9ji-UefSgRgJ8ynHG6UFs,2267
7
+ potato_util/secure.py,sha256=HxlUGyJl15qiL5WFrTZOJLgBZzuEr3Eu--qvBPmj2So,848
8
+ potato_util/validator.py,sha256=TANw7uWHi0PANPkrNDOFFvRcYL9Ge75b4xGSf3PTW1A,4083
9
+ potato_util/constants/__init__.py,sha256=lhxZ2k-QotMR_w9w-Gv-9pCEFrQSKxe1YEIvV83pf8E,80
10
+ potato_util/constants/_base.py,sha256=J1i611erzcrPRZ6USkVgnNZ_IvvN9YLbcltvyIngpcg,61
11
+ potato_util/constants/_enum.py,sha256=tThmf7lFsGfFUNNQE0hOUZ0JKc821ZGZnSyJIj2jVTQ,515
12
+ potato_util/constants/_regex.py,sha256=T7SSsqD0y8jXJJPURwAK2uA59AjLt1uqGfqqUd1a22o,710
13
+ potato_util/http/__init__.py,sha256=qWOzScMVJEHjzClEqOYmGtfkZyU8f8Hz8nT054Dbblo,229
14
+ potato_util/http/_async.py,sha256=dfs-ynchPKdnMNQXqyoqhcz-jhtQ7g_4JnNfVr3bXk0,1178
15
+ potato_util/http/_base.py,sha256=qoW5U8gxihVMx3v1gSLLmptMjScVsDS9PowvJ6tTc9c,1436
16
+ potato_util/http/_sync.py,sha256=i8oyRmh-WraOzotaUbssIjOPvBh1pvLPupdoCvyHTK0,1169
17
+ potato_util/http/fastapi.py,sha256=iqCxZcQDIVtcqCN9dZ4itcoUs6KzsfRGgK7sRd5EmOA,675
18
+ potato_util/io/__init__.py,sha256=FoGcN7t0uArQV4hbMR1X5aeC8Yq-h_ds4xooNpkmgG0,209
19
+ potato_util/io/_async.py,sha256=XVNFDCRII-ib32tksxj5G3nDLsV-ylTW2Hw3_seIY-k,10012
20
+ potato_util/io/_sync.py,sha256=ZVRukOYWWvXtVlU_Gf8GoKnQZqSI2LISHPMmxXi4-2U,9644
21
+ potato_util-0.0.4.dist-info/licenses/LICENSE.txt,sha256=CUTK-r0BWIg1r0bBiemAcMhakgV0N7HuRhw6rQ-A9A4,1074
22
+ potato_util-0.0.4.dist-info/METADATA,sha256=GE6OpxAvbyH4Bd7psAp9p9Q6qyUGPouja2jKzgTnVM4,15634
23
+ potato_util-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ potato_util-0.0.4.dist-info/top_level.txt,sha256=pLMnSfT6rhlYBpo2Gnd8kKMDxcuvxdVizqsv1dd1frw,12
25
+ potato_util-0.0.4.dist-info/RECORD,,