future-framework 3.1.0__tar.gz → 3.1.1__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.
- {future_framework-3.1.0 → future_framework-3.1.1}/PKG-INFO +1 -1
- {future_framework-3.1.0 → future_framework-3.1.1}/future/application.py +2 -1
- future_framework-3.1.1/future/logger.py +110 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/pyproject.toml +1 -1
- {future_framework-3.1.0 → future_framework-3.1.1}/pyproject.toml.orig +1 -1
- future_framework-3.1.0/future/logger.py +0 -53
- {future_framework-3.1.0 → future_framework-3.1.1}/LICENSE +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/README.md +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/Auth0Authentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/AzureADAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/BasicAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KerberosAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KeycloakAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/OAuth2Authentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/OpenIdConnectAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/SAMLAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/cli.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/DebugController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/GraphQLController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/OpenAPIController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/WebSocketController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/WelcomeController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/database.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/ClickhouseDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/ElasticsearchDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/MongoDBDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/MySQLDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/PostgresDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/RedisDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/SQLiteDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/exceptions.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IAuthentication.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IController.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IDatabase.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IModel.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IPlugin.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/ITask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/interfacing.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/lifespan.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/BruteforcePreventionMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/CORSMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/CSRFMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/GZipMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HTAccessConfuserMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HeaderConfuserMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/RateLimitMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/ResponseCodeConfuserMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/SQLiConfuserMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/ScopeValidationMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/SessionMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareRequest.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareResponse.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/WebServerConfuserMiddleware.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/migrations.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/models/PostModel.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/models/Query.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/models/UserModel.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/models/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/openapi.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/plugins/ElasticsearchPlugin.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/plugins/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/request.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/response.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/routing.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/scheduling.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/seeder.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/settings.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckDNSTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckDiskUsageTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckHttpStatusTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckMemoryUsageTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckSSHBannerTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckSystemUptimeTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/DailyBackupTask.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/SyncTaskExample.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/__init__.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/taskscheduler.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/testclient.py +0 -0
- {future_framework-3.1.0 → future_framework-3.1.1}/future/types.py +0 -0
|
@@ -16,7 +16,7 @@ from rich.table import Table
|
|
|
16
16
|
from rich.text import Text
|
|
17
17
|
|
|
18
18
|
from future.exceptions import ErrorHandler, HTTPException
|
|
19
|
-
from future.logger import log
|
|
19
|
+
from future.logger import create_uvicorn_logging_config, log
|
|
20
20
|
from future.interfaces.IMiddleware import IMiddleware
|
|
21
21
|
from future.openapi import get_openapi_config, is_docs_path, openapi_routes, rebuild_spec_from_routes, set_openapi_config, spec_path
|
|
22
22
|
from future.request import Request
|
|
@@ -656,6 +656,7 @@ class Future:
|
|
|
656
656
|
ssl_certfile=tls_cert,
|
|
657
657
|
ssl_keyfile_password=tls_password,
|
|
658
658
|
timeout_graceful_shutdown=3,
|
|
659
|
+
log_config=create_uvicorn_logging_config(),
|
|
659
660
|
log_level="info",
|
|
660
661
|
lifespan="on",
|
|
661
662
|
access_log=access_log,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import logging
|
|
3
|
+
import logging.config
|
|
4
|
+
import logging.handlers
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from uvicorn.logging import AccessFormatter, DefaultFormatter
|
|
10
|
+
|
|
11
|
+
from future.settings import LOGGING_CONFIG
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UtcTimestampFormatter:
|
|
15
|
+
"""Render log timestamps as unambiguous ISO 8601 UTC values."""
|
|
16
|
+
|
|
17
|
+
def formatTime(self, record: logging.LogRecord, datefmt: str | None = None) -> str:
|
|
18
|
+
return datetime.fromtimestamp(record.created, timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ANSI color codes
|
|
22
|
+
class ColoredFormatter(UtcTimestampFormatter, logging.Formatter):
|
|
23
|
+
"""Custom formatter with colors like uvicorn"""
|
|
24
|
+
|
|
25
|
+
COLORS = {
|
|
26
|
+
"DEBUG": "\033[36m", # Cyan
|
|
27
|
+
"INFO": "\033[32m", # Green
|
|
28
|
+
"WARNING": "\033[33m", # Yellow
|
|
29
|
+
"ERROR": "\033[31m", # Red
|
|
30
|
+
"CRITICAL": "\033[35m", # Magenta
|
|
31
|
+
}
|
|
32
|
+
RESET = "\033[0m"
|
|
33
|
+
|
|
34
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
35
|
+
message = super().format(record)
|
|
36
|
+
timestamp = self.formatTime(record)
|
|
37
|
+
levelname = record.levelname
|
|
38
|
+
if levelname in self.COLORS:
|
|
39
|
+
# Calculate padding based on original levelname length BEFORE adding colors
|
|
40
|
+
padding = " " * (9 - len(levelname)) # 9 is max level length (CRITICAL) + 1
|
|
41
|
+
colored_levelname = f"{self.COLORS[levelname]}{levelname}{self.RESET}"
|
|
42
|
+
return f"{timestamp} {colored_levelname}:{padding}{message}"
|
|
43
|
+
return f"{timestamp} {levelname}: {message}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class UvicornDefaultFormatter(UtcTimestampFormatter, DefaultFormatter):
|
|
47
|
+
"""Uvicorn server formatter using Future's UTC timestamp format."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UvicornAccessFormatter(UtcTimestampFormatter, AccessFormatter):
|
|
51
|
+
"""Uvicorn access formatter using Future's UTC timestamp format."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def create_uvicorn_logging_config() -> dict[str, Any]:
|
|
55
|
+
"""Return a fresh Uvicorn logging config for each server invocation."""
|
|
56
|
+
return {
|
|
57
|
+
"version": 1,
|
|
58
|
+
"disable_existing_loggers": False,
|
|
59
|
+
"formatters": {
|
|
60
|
+
"default": {
|
|
61
|
+
"()": UvicornDefaultFormatter,
|
|
62
|
+
"fmt": "%(asctime)s %(levelprefix)s %(message)s",
|
|
63
|
+
"use_colors": None,
|
|
64
|
+
},
|
|
65
|
+
"access": {
|
|
66
|
+
"()": UvicornAccessFormatter,
|
|
67
|
+
"fmt": '%(asctime)s %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"handlers": {
|
|
71
|
+
"default": {
|
|
72
|
+
"formatter": "default",
|
|
73
|
+
"class": "logging.StreamHandler",
|
|
74
|
+
"stream": "ext://sys.stderr",
|
|
75
|
+
},
|
|
76
|
+
"access": {
|
|
77
|
+
"formatter": "access",
|
|
78
|
+
"class": "logging.StreamHandler",
|
|
79
|
+
"stream": "ext://sys.stdout",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
"loggers": {
|
|
83
|
+
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
|
|
84
|
+
"uvicorn.error": {"level": "INFO"},
|
|
85
|
+
"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Configure logging using settings
|
|
91
|
+
logging.config.dictConfig(LOGGING_CONFIG)
|
|
92
|
+
|
|
93
|
+
log = logging.getLogger("future")
|
|
94
|
+
formatter = ColoredFormatter("%(message)s")
|
|
95
|
+
|
|
96
|
+
# QueueHandler itself does not format; the listener's stdout handler does.
|
|
97
|
+
# Handler level NOTSET so Future's log.setLevel(APP_DEBUG) alone controls filtering.
|
|
98
|
+
queue_handler = logging.getHandlerByName("queue_handler")
|
|
99
|
+
if queue_handler is not None and isinstance(queue_handler, logging.handlers.QueueHandler):
|
|
100
|
+
listener = getattr(queue_handler, "listener", None)
|
|
101
|
+
if listener is not None:
|
|
102
|
+
for handler in listener.handlers:
|
|
103
|
+
handler.setFormatter(formatter)
|
|
104
|
+
handler.setLevel(logging.NOTSET)
|
|
105
|
+
listener.start()
|
|
106
|
+
atexit.register(listener.stop)
|
|
107
|
+
else:
|
|
108
|
+
for handler in log.handlers:
|
|
109
|
+
handler.setFormatter(formatter)
|
|
110
|
+
handler.setLevel(logging.NOTSET)
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import atexit
|
|
2
|
-
import logging
|
|
3
|
-
import logging.config
|
|
4
|
-
import logging.handlers
|
|
5
|
-
|
|
6
|
-
from future.settings import LOGGING_CONFIG
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# ANSI color codes
|
|
10
|
-
class ColoredFormatter(logging.Formatter):
|
|
11
|
-
"""Custom formatter with colors like uvicorn"""
|
|
12
|
-
|
|
13
|
-
COLORS = {
|
|
14
|
-
"DEBUG": "\033[36m", # Cyan
|
|
15
|
-
"INFO": "\033[32m", # Green
|
|
16
|
-
"WARNING": "\033[33m", # Yellow
|
|
17
|
-
"ERROR": "\033[31m", # Red
|
|
18
|
-
"CRITICAL": "\033[35m", # Magenta
|
|
19
|
-
}
|
|
20
|
-
RESET = "\033[0m"
|
|
21
|
-
|
|
22
|
-
def format(self, record: logging.LogRecord) -> str:
|
|
23
|
-
# Add color to levelname
|
|
24
|
-
levelname = record.levelname
|
|
25
|
-
if levelname in self.COLORS:
|
|
26
|
-
# Calculate padding based on original levelname length BEFORE adding colors
|
|
27
|
-
padding = " " * (9 - len(levelname)) # 9 is max level length (CRITICAL) + 1
|
|
28
|
-
colored_levelname = f"{self.COLORS[levelname]}{levelname}{self.RESET}"
|
|
29
|
-
return f"{colored_levelname}:{padding}{record.getMessage()}"
|
|
30
|
-
return super().format(record)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# Configure logging using settings
|
|
34
|
-
logging.config.dictConfig(LOGGING_CONFIG)
|
|
35
|
-
|
|
36
|
-
log = logging.getLogger("future")
|
|
37
|
-
formatter = ColoredFormatter("%(message)s")
|
|
38
|
-
|
|
39
|
-
# QueueHandler itself does not format; the listener's stdout handler does.
|
|
40
|
-
# Handler level NOTSET so Future's log.setLevel(APP_DEBUG) alone controls filtering.
|
|
41
|
-
queue_handler = logging.getHandlerByName("queue_handler")
|
|
42
|
-
if queue_handler is not None and isinstance(queue_handler, logging.handlers.QueueHandler):
|
|
43
|
-
listener = getattr(queue_handler, "listener", None)
|
|
44
|
-
if listener is not None:
|
|
45
|
-
for handler in listener.handlers:
|
|
46
|
-
handler.setFormatter(formatter)
|
|
47
|
-
handler.setLevel(logging.NOTSET)
|
|
48
|
-
listener.start()
|
|
49
|
-
atexit.register(listener.stop)
|
|
50
|
-
else:
|
|
51
|
-
for handler in log.handlers:
|
|
52
|
-
handler.setFormatter(formatter)
|
|
53
|
-
handler.setLevel(logging.NOTSET)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/Auth0Authentication.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/AzureADAuthentication.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/BasicAuthentication.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KerberosAuthentication.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KeycloakAuthentication.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/OAuth2Authentication.py
RENAMED
|
File without changes
|
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/SAMLAuthentication.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HTAccessConfuserMiddleware.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HeaderConfuserMiddleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/SQLiConfuserMiddleware.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/ScopeValidationMiddleware.py
RENAMED
|
File without changes
|
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareRequest.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareResponse.py
RENAMED
|
File without changes
|
{future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/WebServerConfuserMiddleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|