|
| 1 | +# classmods |
| 2 | + |
| 3 | +`classmods` is a lightweight Python package designed to enhance class behavior with minimal effort. It provides modular decorators and descriptors to automate and simplify class-related tasks like environment variable management, creating example env files, monitoring, logging, and more. |
| 4 | + |
| 5 | +# Documentation |
| 6 | + |
| 7 | +All features are well documented and use a high level of `type_hints` for easy understanding and usage. |
| 8 | + |
| 9 | +## Features |
| 10 | + |
| 11 | +* `ConstantAttrib`: A descriptor that acts like a constant. Once set, the value cannot be changed. Raises `AttributeError` on change detection. |
| 12 | +* `RemoteAttrib`: A descriptor that acts as a remote attribute. You can modify the mapped value on-the-fly. |
| 13 | +* `ENVMod`: The main API class for managing `.env` variables. Supports manual and decorator-based registration of environment items, type-safe value loading, and `.env_example` generation. |
| 14 | +* `MethodMonitor`: A class to monitor method calls of a target class, triggering a handler function after the method is called. |
| 15 | +* `logwrap`: A dynamic decorator to log function calls. Uses the `logging` module with your current project configurations. |
| 16 | +* `suppress_errors`: A decorator that suppresses exceptions raised by the wrapped function and returns a fallback value instead. |
| 17 | + |
| 18 | +## Installation |
| 19 | + |
| 20 | +1. Easy install with pip |
| 21 | + |
| 22 | +```bash |
| 23 | +pip install classmods |
| 24 | +``` |
| 25 | + |
| 26 | +2. Install with git+pip |
| 27 | + |
| 28 | +```bash |
| 29 | +pip install git+https://github.com/hmohammad2520-org/classmods |
| 30 | +``` |
| 31 | + |
| 32 | +## Examples |
| 33 | + |
| 34 | +### Constant Attribute |
| 35 | + |
| 36 | +```python |
| 37 | +from classmods import ConstantAttrib |
| 38 | + |
| 39 | +class Config: |
| 40 | + app_name = ConstantAttrib[str]() |
| 41 | + |
| 42 | + def __init__(self, app_name): |
| 43 | + self.app_name = app_name |
| 44 | + |
| 45 | +config = Config('new app') |
| 46 | +config.app_name = 'my app' # This will raise AttributeError |
| 47 | +``` |
| 48 | + |
| 49 | +### Remote Attribute |
| 50 | + |
| 51 | +```python |
| 52 | +import requests |
| 53 | +from classmods import RemoteAttrib |
| 54 | + |
| 55 | +class Config: |
| 56 | + token = RemoteAttrib[str]( |
| 57 | + get=lambda: requests.get("https://api.example.com/auth").json()["token"], |
| 58 | + cache_timeout=10, # keeps result for 10 seconds |
| 59 | + ) |
| 60 | + |
| 61 | +config = Config() |
| 62 | +token = config.token # This will send a request and return the result |
| 63 | +``` |
| 64 | + |
| 65 | +### ENVMod |
| 66 | + |
| 67 | +```python |
| 68 | +from os import PathLike |
| 69 | +from requests import Session |
| 70 | +from classmods import ENVMod |
| 71 | + |
| 72 | +class Config: |
| 73 | + ENVMod.register(exclude=['session'], cast={'log_path': str}) |
| 74 | + def __init__( |
| 75 | + self, |
| 76 | + app_name: str, |
| 77 | + session: Session, # Excluded non-parsable object |
| 78 | + log_path: PathLike, |
| 79 | + log_level: Optional[str] = None, |
| 80 | + port: int = 10, |
| 81 | + ): |
| 82 | + ''' |
| 83 | + Args: |
| 84 | + app_name (str): Application name. |
| 85 | + session (Session): Requests session. |
| 86 | + log_path (PathLike): Path of log file. |
| 87 | + log_level (Optional[str]): Log level, e.g., info. |
| 88 | + port (int): Session port, defaults to 10. |
| 89 | + ''' |
| 90 | + |
| 91 | +ENVMod.save_example('.my_example_path') |
| 92 | +ENVMod.load_dotenv('.my_env') |
| 93 | +ENVMod.sync_env_file('.my_env') |
| 94 | +config = Config(**ENVMod.load_args(Config.__init__), session=Session()) |
| 95 | +``` |
| 96 | + |
| 97 | +### Method Monitor |
| 98 | + |
| 99 | +```python |
| 100 | +from classmods import MethodMonitor |
| 101 | + |
| 102 | +class MyClass: |
| 103 | + def my_method(self): |
| 104 | + pass |
| 105 | + |
| 106 | +def my_handler(instance): |
| 107 | + print(f"Monitor triggered on {instance}") |
| 108 | + |
| 109 | +monitor = MethodMonitor(MyClass, my_handler, target_method='my_method') |
| 110 | +obj = MyClass() |
| 111 | +obj.my_method() |
| 112 | +``` |
| 113 | + |
| 114 | +### logwrap |
| 115 | + |
| 116 | +```python |
| 117 | +from classmods import logwrap |
| 118 | + |
| 119 | +@logwrap(before=('INFO', '{func} starting, args={args} kwargs={kwargs}'), after=('INFO', '{func} ended')) |
| 120 | +def my_func(my_arg, my_kwarg=None): |
| 121 | + ... |
| 122 | + |
| 123 | +my_func('hello', my_kwarg=123) # Check logs to see the output |
| 124 | +``` |
| 125 | + |
| 126 | +### Suppress Errors |
| 127 | + |
| 128 | +```python |
| 129 | +from classmods import suppress_errors |
| 130 | + |
| 131 | +@suppress_errors(Exception) |
| 132 | +def risky_op() -> int: |
| 133 | + return 1 / 0 |
| 134 | + |
| 135 | +result = risky_op() # result = ZeroDivisionError |
| 136 | + |
| 137 | +@suppress_errors(False) |
| 138 | +def safe_op() -> bool: |
| 139 | + raise ValueError("error") |
| 140 | + |
| 141 | +result = safe_op() # result = False |
| 142 | +``` |
| 143 | + |
| 144 | +## License |
| 145 | + |
| 146 | +MIT License |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +Made with ❤️ by [hmohammad2520](https://github.com/hmohammad2520-org) |
0 commit comments