pyapiutils 0.0.10__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.
- pyapiutils/__init__.py +10 -0
- pyapiutils/utils/__init__.py +11 -0
- pyapiutils/utils/apires.py +16 -0
- pyapiutils/utils/config.py +50 -0
- pyapiutils/utils/infoui.py +35 -0
- pyapiutils/utils/logger.py +80 -0
- pyapiutils/utils/reldir.py +21 -0
- pyapiutils-0.0.10.dist-info/METADATA +24 -0
- pyapiutils-0.0.10.dist-info/RECORD +10 -0
- pyapiutils-0.0.10.dist-info/WHEEL +4 -0
pyapiutils/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Docstring"""
|
|
2
|
+
|
|
3
|
+
from .config import ConfigUtil
|
|
4
|
+
from .infoui import ClientInfo
|
|
5
|
+
from .reldir import RelDirs
|
|
6
|
+
from .logger import Logging
|
|
7
|
+
from .apires import APIResponse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = ["ConfigUtil", "ClientInfo", "RelDirs", "Logging", "APIResponse"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Docstring"""
|
|
2
|
+
from typing import Any, Optional, Self
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
class APIResponse(BaseModel):
|
|
6
|
+
success: bool = True
|
|
7
|
+
message: str = "OK"
|
|
8
|
+
data: Optional[Any] = None
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def from_res(cls, res: Any, message: Optional[str] = None) ->Self:
|
|
12
|
+
return cls(
|
|
13
|
+
success = True,
|
|
14
|
+
message = message or "request successful",
|
|
15
|
+
data = res
|
|
16
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Docstring"""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
from typing import Optional, Union, Type, Any
|
|
5
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
6
|
+
|
|
7
|
+
__all__ = ["ConfigUtil"]
|
|
8
|
+
|
|
9
|
+
class ConfigUtil:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
base: Any,
|
|
13
|
+
env: Optional[Union[str, Path]] = None,
|
|
14
|
+
secrets: Optional[Union[str, Path]] = None,
|
|
15
|
+
prefix: Optional[str] = None,
|
|
16
|
+
):
|
|
17
|
+
self.base = base
|
|
18
|
+
self.env_path = env
|
|
19
|
+
self.secrets_path = secrets
|
|
20
|
+
self.prefix = prefix
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def config(self):
|
|
24
|
+
config = self.get_config(
|
|
25
|
+
self.base,
|
|
26
|
+
self.env_path,
|
|
27
|
+
self.secrets_path,
|
|
28
|
+
self.prefix,
|
|
29
|
+
)
|
|
30
|
+
return self.base.model_validate(config)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
@lru_cache
|
|
34
|
+
def get_config(
|
|
35
|
+
cls,
|
|
36
|
+
base: Type[BaseSettings],
|
|
37
|
+
env: Optional[Union[str, Path]] = None,
|
|
38
|
+
secrets: Optional[Union[str, Path]] = None,
|
|
39
|
+
prefix: Optional[str] = None,
|
|
40
|
+
):
|
|
41
|
+
class Config(base):
|
|
42
|
+
model_config = SettingsConfigDict(
|
|
43
|
+
env_prefix=prefix or "",
|
|
44
|
+
env_file=str(env) if env else None,
|
|
45
|
+
env_file_encoding="utf-8",
|
|
46
|
+
extra="ignore",
|
|
47
|
+
secrets_dir=str(secrets) if secrets else None,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return Config()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import user_agents
|
|
2
|
+
from fastapi import Request
|
|
3
|
+
|
|
4
|
+
class ClientInfo:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def client_ip(request: Request) -> str:
|
|
7
|
+
return (
|
|
8
|
+
request.headers.get("CF-Connecting-IP") # Cloudflare
|
|
9
|
+
or request.headers.get("True-Client-IP") # Akamai / proxies
|
|
10
|
+
or request.headers.get("X-Forwarded-For", "").split(",")[0].strip()
|
|
11
|
+
or request.headers.get("X-Real-IP")
|
|
12
|
+
or (request.client.host if request.client else "unknown")
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def user_agent(request: Request) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Returns a clean parsed User-Agent string like 'Chrome 122 on Windows 10'.
|
|
19
|
+
"""
|
|
20
|
+
ua_string = (
|
|
21
|
+
request.headers.get("X-Real-User-Agent")
|
|
22
|
+
or request.headers.get("X-Device-User-Agent")
|
|
23
|
+
or request.headers.get("X-Original-User-Agent")
|
|
24
|
+
or request.headers.get("User-Agent")
|
|
25
|
+
or "unknown"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
ua = user_agents.parse(ua_string)
|
|
29
|
+
|
|
30
|
+
browser = f"{ua.browser.family} {ua.browser.version_string}".strip()
|
|
31
|
+
os = f"{ua.os.family} {ua.os.version_string}".strip()
|
|
32
|
+
device = ua.device.family or "Unknown Device"
|
|
33
|
+
|
|
34
|
+
# Return a clean single string
|
|
35
|
+
return f"{browser} on {os} ({device})"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Docstring"""
|
|
2
|
+
import logging, sys
|
|
3
|
+
|
|
4
|
+
class Logging:
|
|
5
|
+
KAFKA_SILENT_LIST=["aiokafka"]
|
|
6
|
+
GAIMS_SILENT_LIST=["aiokafka", "botocore", "s3transfer", "urllib3"]
|
|
7
|
+
@staticmethod
|
|
8
|
+
def setup_basic(level=logging.INFO):
|
|
9
|
+
root = logging.getLogger()
|
|
10
|
+
if root.handlers:
|
|
11
|
+
root.handlers.clear()
|
|
12
|
+
root.setLevel(level)
|
|
13
|
+
|
|
14
|
+
formatter = logging.Formatter(
|
|
15
|
+
"%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
19
|
+
handler.setFormatter(formatter)
|
|
20
|
+
handler.setLevel(level)
|
|
21
|
+
|
|
22
|
+
logging.basicConfig(
|
|
23
|
+
level=level,
|
|
24
|
+
handlers=[handler],
|
|
25
|
+
force=True, # override any existing config
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def setup_process(level=logging.INFO):
|
|
30
|
+
root = logging.getLogger()
|
|
31
|
+
if root.handlers:
|
|
32
|
+
root.handlers.clear()
|
|
33
|
+
root.setLevel(level)
|
|
34
|
+
|
|
35
|
+
formatter = logging.Formatter(
|
|
36
|
+
"%(asctime)s | %(levelname)s | %(processName)s | %(name)s | %(message)s"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
40
|
+
handler.setFormatter(formatter)
|
|
41
|
+
handler.setLevel(level)
|
|
42
|
+
|
|
43
|
+
logging.basicConfig(
|
|
44
|
+
level=level,
|
|
45
|
+
handlers=[handler],
|
|
46
|
+
force=True,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def setup_service(service:str, level=logging.INFO):
|
|
51
|
+
root = logging.getLogger()
|
|
52
|
+
if root.handlers:
|
|
53
|
+
root.handlers.clear()
|
|
54
|
+
root.setLevel(level)
|
|
55
|
+
|
|
56
|
+
formatter = logging.Formatter(
|
|
57
|
+
"%(asctime)s | %(levelname)s | %(service)s | %(processName)s | %(name)s:%(lineno)d | %(message)s"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
61
|
+
handler.setFormatter(formatter)
|
|
62
|
+
handler.setLevel(level)
|
|
63
|
+
|
|
64
|
+
class ServiceFilter(logging.Filter):
|
|
65
|
+
def filter(self, record):
|
|
66
|
+
record.service = service
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
handler.addFilter(ServiceFilter())
|
|
70
|
+
|
|
71
|
+
logging.basicConfig(
|
|
72
|
+
level=level,
|
|
73
|
+
handlers=[handler],
|
|
74
|
+
force=True,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def silent_loglist(names: list[str], level=logging.ERROR):
|
|
79
|
+
for name in names:
|
|
80
|
+
logging.getLogger(name).setLevel(level)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
class RelDirs:
|
|
4
|
+
def __init__(self, path_str):
|
|
5
|
+
self.path = self._path(path_str)
|
|
6
|
+
self.dir = (
|
|
7
|
+
self.path
|
|
8
|
+
if self.path.is_dir()
|
|
9
|
+
else self.path.parent
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
def _path(self, path_str):
|
|
13
|
+
"""
|
|
14
|
+
Doc String
|
|
15
|
+
"""
|
|
16
|
+
path = Path(path_str).resolve()
|
|
17
|
+
cwd = Path.cwd().resolve()
|
|
18
|
+
try:
|
|
19
|
+
return path.relative_to(cwd)
|
|
20
|
+
except ValueError:
|
|
21
|
+
return path
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyapiutils
|
|
3
|
+
Version: 0.0.10
|
|
4
|
+
Summary:
|
|
5
|
+
Author: sharmauksa
|
|
6
|
+
Author-email: sharmaumesh791@gmail.com
|
|
7
|
+
Requires-Python: >=3.14,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# pyapiutils
|
|
13
|
+
|
|
14
|
+
**pyapiutils** is a lightweight Python library for fastapi utilities.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 🔐 Utilities to create alembic migration
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
You can install the package via pip:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install pyapilib
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pyapiutils/__init__.py,sha256=EkQkmMbWoG1faiUV77vf7TjInIcikL0popAx3eWX-qY,246
|
|
2
|
+
pyapiutils/utils/__init__.py,sha256=-HOhMYTiSubnFPCtIhT9JYuYETfvAtA2s8IIOCPskpI,255
|
|
3
|
+
pyapiutils/utils/apires.py,sha256=BAiIy3T8ZuD2prYnsKVVB_DTDbje_8JdJhDA31Bks7c,421
|
|
4
|
+
pyapiutils/utils/config.py,sha256=4Jxfox5qx63PGabzABP1LgRCD6anizWn-ip0TK3sM3Q,1414
|
|
5
|
+
pyapiutils/utils/infoui.py,sha256=yI5T4lYxKNOutbxzLBcHzMjAnfl9bld54s2bIjolc58,1299
|
|
6
|
+
pyapiutils/utils/logger.py,sha256=I9f9a4xpcsEMD0fIibRT0ZqkeIaPQwCt9UK9L7LsKAc,2345
|
|
7
|
+
pyapiutils/utils/reldir.py,sha256=jiSBC8sFJeerMckD1edIOyXxrK8o5O28s9QR1HL7B9k,522
|
|
8
|
+
pyapiutils-0.0.10.dist-info/METADATA,sha256=4IAUkvSPnTQDgUrNiES7yREOIVeevD0BT7UWgkWrke4,519
|
|
9
|
+
pyapiutils-0.0.10.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
|
|
10
|
+
pyapiutils-0.0.10.dist-info/RECORD,,
|