pyessential 0.1.0__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.
@@ -0,0 +1,16 @@
1
+
2
+ from .decorators import timeit
3
+ from .generators import generate_random_int, generate_secret_key
4
+ from .logger import Logger
5
+
6
+ # Version and Author Info
7
+ __version__ = "0.1.0"
8
+ __author__ = "Abhishek Kumar"
9
+
10
+
11
+ __all__ = [
12
+ "timeit",
13
+ "generate_random_int",
14
+ "generate_secret_key",
15
+ "Logger",
16
+ ]
@@ -0,0 +1,3 @@
1
+ from .timer import timeit
2
+
3
+ __all__ = ["timeit"]
@@ -0,0 +1,13 @@
1
+ import time
2
+ from functools import wraps
3
+
4
+ def timeit(func):
5
+ """Decorator to measure and print function execution time."""
6
+ @wraps(func)
7
+ def wrapper(*args, **kwargs):
8
+ start_time = time.time()
9
+ result = func(*args, **kwargs)
10
+ execution_time = time.time() - start_time
11
+ print(f"[{func.__name__}] executed in {execution_time:.4f} seconds.")
12
+ return result
13
+ return wrapper
@@ -0,0 +1,31 @@
1
+ """
2
+ Name: pyessential.generators
3
+ Description: A module providing utility functions for generating random data.
4
+ Author: Abhishek Kumar
5
+ """
6
+
7
+ import secrets
8
+
9
+ def generate_random_int(min_value=0, max_value=100):
10
+ """Generate a random integer within a specified range.
11
+
12
+ Args:
13
+ min_value (int): The minimum value of the range. Default is 0.
14
+ max_value (int): The maximum value of the range. Default is 100.
15
+
16
+ Returns:
17
+ int: A randomly generated integer within the specified range.
18
+ """
19
+ return secrets.randbelow(max_value - min_value + 1) + min_value
20
+
21
+
22
+ def generate_secret_key(length=32):
23
+ """Generate a secure random secret key.
24
+
25
+ Args:
26
+ length (int): The length of the secret key. Default is 32.
27
+
28
+ Returns:
29
+ str: A securely generated random secret key.
30
+ """
31
+ return secrets.token_hex(length)
pyessential/logger.py ADDED
@@ -0,0 +1,265 @@
1
+ """
2
+ Logger utility for application logging.
3
+
4
+ This module provides the 'Logger' class, a robust logging utility that
5
+ integrates with Python's standard 'logging' library. It configures
6
+ three separate loggers/files for STDIN (ingress), STDOUT (general/info),
7
+ and STDERR (errors/critical) messages, with support for
8
+ timed log rotation and console output.
9
+
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ import traceback
15
+ import logging
16
+ from datetime import datetime
17
+ from logging.handlers import TimedRotatingFileHandler
18
+
19
+
20
+ class Logger:
21
+ """
22
+ A logging utility class for managing application logs with separate streams.
23
+
24
+ It sets up three distinct, rotation-enabled log files:
25
+ 1. STDIN (for input/ingress events)
26
+ 2. STDOUT (for general debug/info/warning messages)
27
+ 3. STDERR (for error/critical messages and exceptions)
28
+
29
+ The class is designed to be used as a context manager to ensure log closure.
30
+
31
+ Example:
32
+ >>> log = Logger(log_name="my_app", enable_console=True)
33
+ >>> log.info("Application started.")
34
+ >>> log.error("Something went wrong!")
35
+
36
+ or
37
+
38
+ >>> with Logger(log_name="my_app", enable_console=True) as log:
39
+ ... log.info("Application started.")
40
+ ... log.error("Something went wrong!")
41
+ ...
42
+ """
43
+
44
+ _LEVEL_MAP = {
45
+ "debug":logging.DEBUG,
46
+ "info": logging.INFO,
47
+ "warning": logging.WARNING,
48
+ "error": logging.ERROR,
49
+ "critical": logging.CRITICAL,
50
+ }
51
+
52
+ def __init__(
53
+ self,
54
+ log_dir="../logs",
55
+ log_name="project",
56
+ level="debug",
57
+ when="midnight",
58
+ interval=1,
59
+ backup_count=7,
60
+ enable_console=False,
61
+ fmt="%(asctime)s.%(msecs)03d - %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s",
62
+ datefmt="%Y-%m-%d %H:%M:%S",
63
+ encoding="utf-8"
64
+ ):
65
+ """
66
+ Args:
67
+ log_dir (str) : Directory to store log files. Default is '../logs'.
68
+ log_name (str) : Base name for the three log files (<log_name>_stdin.log, <log_name>_stdout.log, <log_name>_stderr.log).
69
+ level (str) : Logging level. Default is 'debug'.
70
+ when (str) : Time interval for log rotation. Default is 'midnight'.
71
+ interval (int) : Interval for log rotation. Default is 1.
72
+ backup_count (int) : Number of backup log files to keep. Default is 7.
73
+ enable_console (bool) : Enable console logging. Default is False.
74
+ fmt (str) : Log message format. Default is "%(asctime)s.%(msecs)03d - %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s".
75
+ datefmt (str) : Date format in logs. Default is "%Y-%m-%d %H:%M:%S".
76
+ encoding (str) : Encoding for log files. Default is "utf-8".
77
+ """
78
+
79
+ self.log_dir = os.path.abspath(log_dir)
80
+ self.log_name = log_name
81
+
82
+ # Filenames (with 'logs' extension as is common; can be changed if needed)
83
+ self.stdin_path = os.path.join(self.log_dir, f"{self.log_name}_stdin.log")
84
+ self.stdout_path = os.path.join(self.log_dir, f"{self.log_name}_stdout.log")
85
+ self.error_path = os.path.join(self.log_dir, f"{self.log_name}_error.log")
86
+
87
+ # Ensure Directory
88
+ os.makedirs(self.log_dir, exist_ok=True)
89
+
90
+ # Common formatter
91
+ formatter = logging.Formatter(fmt=fmt, datefmt=datefmt)
92
+
93
+ # Create three dedicated loggers so routing is explicit and simple
94
+ self._stdin_logger = self._build_logger(
95
+ name=f"{self.log_name}.stdin",
96
+ filepath=self.stdin_path,
97
+ level=level,
98
+ when=when,
99
+ interval=interval,
100
+ backup_count=backup_count,
101
+ formatter=formatter,
102
+ encoding=encoding,
103
+ enable_console=False, # stdin is typically not logged to console
104
+ )
105
+
106
+ self._stdout_logger = self._build_logger(
107
+ name=f"{self.log_name}.stdout",
108
+ filepath=self.stdout_path,
109
+ level=level,
110
+ when=when,
111
+ interval=interval,
112
+ backup_count=backup_count,
113
+ formatter=formatter,
114
+ encoding=encoding,
115
+ enable_console=enable_console,
116
+ console_to_stderr=False # stdout -> sys.stdout
117
+ )
118
+
119
+ self._error_logger = self._build_logger(
120
+ name=f"{self.log_name}.error",
121
+ filepath=self.error_path,
122
+ level=level,
123
+ when=when,
124
+ interval=interval,
125
+ backup_count=backup_count,
126
+ formatter=formatter,
127
+ encoding=encoding,
128
+ enable_console=enable_console,
129
+ console_to_stderr=True # stderr -> sys.stderr
130
+ )
131
+
132
+ # --------------- Public Methods ---------------
133
+ def debug(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
134
+ """Log a debug message to stdout logger."""
135
+ self._stdout_logger.debug(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
136
+
137
+ def info(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
138
+ """Log an info message to stdout logger."""
139
+ self._stdout_logger.info(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
140
+
141
+ def warning(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
142
+ """Log a warning message to stdout logger."""
143
+ self._stdout_logger.warning(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
144
+
145
+ def error(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
146
+ """Log an error message to error logger."""
147
+ self._error_logger.error(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
148
+
149
+ def critical(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
150
+ """Log a critical message to error logger."""
151
+ self._error_logger.critical(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
152
+
153
+ def exception(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
154
+ """Log an exception message to error logger."""
155
+ self._error_logger.error(msg, *args, exc_info=True, stacklevel=stacklevel, **kwargs)
156
+
157
+ def stdin(self, msg, *args, exc_info=False, stacklevel=2, **kwargs):
158
+ """Explicitly record 'input' or ingress events."""
159
+ self._stdin_logger.info(msg, *args, exc_info=exc_info, stacklevel=stacklevel, **kwargs)
160
+
161
+ def log_error(self, err: Exception):
162
+ """Log an exception with traceback to the error logger.
163
+
164
+ Args:
165
+ err (Exception): The exception instance to log.
166
+ """
167
+
168
+ exc_type, exc_obj, exc_tb = sys.exc_info()
169
+
170
+ if exc_tb is None:
171
+ # It means getting called outputside except block.
172
+ filename = "<unknown>"
173
+ lineno = -1
174
+ func_name = "<unknown>"
175
+ else:
176
+ filename = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
177
+ lineno = exc_tb.tb_lineno
178
+ func_name = traceback.extract_tb(exc_tb, 1)[0][2]
179
+
180
+ e_type = err.__class__.__name__
181
+
182
+ data = {
183
+ "exception_type": e_type,
184
+ "exception_message": str(err),
185
+ "filename": filename,
186
+ "lineno": lineno,
187
+ "function": func_name,
188
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
189
+ "traceback": traceback.format_exc()
190
+ }
191
+
192
+ # log to stderr file
193
+ self.error(data, exc_info=True, stacklevel=2)
194
+ return data
195
+
196
+ def close(self):
197
+ """
198
+ Flush and close all handlers explicitly.
199
+ """
200
+
201
+ for lg in (self._stdin_logger, self._stdout_logger, self._error_logger):
202
+ for h in list(lg.handlers):
203
+ try:
204
+ h.flush()
205
+ except Exception:
206
+ pass
207
+
208
+ try:
209
+ h.close()
210
+ except Exception:
211
+ pass
212
+
213
+ lg.removeHandler(h)
214
+
215
+ def __enter__(self):
216
+ return self
217
+
218
+ def __exit__(self, exc_type, exc_value, traceback):
219
+ self.close()
220
+
221
+
222
+ # --------------- Internals ---------------
223
+ @classmethod
224
+ def _build_logger(
225
+ cls,
226
+ name,
227
+ filepath,
228
+ level,
229
+ when,
230
+ interval,
231
+ backup_count,
232
+ formatter,
233
+ encoding,
234
+ enable_console=False,
235
+ console_to_stderr=False
236
+ ):
237
+
238
+ log_level = cls._LEVEL_MAP.get(level.lower(), logging.DEBUG)
239
+ logger = logging.getLogger(name)
240
+ logger.setLevel(log_level)
241
+ logger.handlers.clear() # Avoid duplicate handlers on re-initialization
242
+ logger.propagate = False # Prevent log messages from being propagated to the root logger
243
+
244
+ # Timed Rotating File Handler
245
+ fh = TimedRotatingFileHandler(
246
+ filename=filepath,
247
+ when=when,
248
+ interval=interval,
249
+ backupCount=backup_count,
250
+ encoding=encoding,
251
+ delay=True,
252
+ utc=False
253
+ )
254
+
255
+ fh.setFormatter(formatter)
256
+ logger.addHandler(fh)
257
+
258
+ if enable_console:
259
+ stream = sys.stderr if console_to_stderr else sys.stdout
260
+ sh = logging.StreamHandler(stream)
261
+ sh.setFormatter(formatter)
262
+ sh.setLevel(log_level)
263
+ logger.addHandler(sh)
264
+
265
+ return logger
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyessential
3
+ Version: 0.1.0
4
+ Summary: A growing collection of Python utilities and decorators.
5
+ Home-page: https://github.com/Abhi39054/PyEssential.git
6
+ Author: Abhishek Kumar
7
+ Author-email: abhishekkumar39054@gmail.com
8
+ Maintainer: Abhishek Kumar
9
+ License: MIT
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # 🐍 PyEssential
22
+
23
+ **A growing collection of Python utilities and decorators to simplify common development tasks.**
24
+
25
+ ---
26
+
27
+ [![PyPI version](https://img.shields.io/pypi/v/pyessential.svg)](https://pypi.org/project/pyessential/)
28
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
29
+
30
+ ## ✨ Features
31
+
32
+ `PyEssential` aims to be the go-to library for small, reusable tools that you often find yourself writing from scratch.
33
+
34
+ * **Performance:** Includes the ready-to-use `@timeit` decorator for simple function benchmarking.
35
+ * **Code Clarity:** Provides utility functions for common tasks like string manipulation (e.g., casing). (Coming soon!)
36
+ * **Scalability:** Structured for easy addition of new utility groups (e.g., `networking`, `file_io`, `validation`).
37
+
38
+ ## 🚀 Installation
39
+
40
+ `PyEssential` is currenlty still in development stage. Once development is completed, It will be published in the Pypi for the community to use.
41
+
42
+
43
+ ```bash
44
+ pip install pyessential
45
+ ```
@@ -0,0 +1,10 @@
1
+ pyessential/__init__.py,sha256=q0PBencuetHz8xKooZcLDyL256Wt6mhNJNRMbyNOYBM,316
2
+ pyessential/generators.py,sha256=4Ur_EJDvBZ5TWrqem7ZKGgNcsrHxDIXksqL92F6vi7o,895
3
+ pyessential/logger.py,sha256=4g7uO7Y1vBwlt2tkVFplcjdv7184i06kkji0HZ5ogLU,9674
4
+ pyessential/decorators/__init__.py,sha256=8c6P3y211dQcAKlYpcoHjgZNFHWPvkqv3cfzxq0gZM4,49
5
+ pyessential/decorators/timer.py,sha256=0zyAcwCVr9kHZ4JKzxx3OL4oZYrN7sGy0Us5AoR6VVw,428
6
+ pyessential-0.1.0.dist-info/licenses/LICENSE,sha256=zb0pNrxkWz5xapN3ADXzV_yIbY28-gHnSuIyFBe5URI,1092
7
+ pyessential-0.1.0.dist-info/METADATA,sha256=cFtLPmYfeWTmzjmpaWSg5B4LTyjiGoq9F8K2M1508Uo,1715
8
+ pyessential-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ pyessential-0.1.0.dist-info/top_level.txt,sha256=wCH4Sn4jN1o0cyc4-XMRNQCy15XVetATW5l-cZN2zO4,12
10
+ pyessential-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Abhishek Kumar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ pyessential