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.
Files changed (86) hide show
  1. {future_framework-3.1.0 → future_framework-3.1.1}/PKG-INFO +1 -1
  2. {future_framework-3.1.0 → future_framework-3.1.1}/future/application.py +2 -1
  3. future_framework-3.1.1/future/logger.py +110 -0
  4. {future_framework-3.1.0 → future_framework-3.1.1}/pyproject.toml +1 -1
  5. {future_framework-3.1.0 → future_framework-3.1.1}/pyproject.toml.orig +1 -1
  6. future_framework-3.1.0/future/logger.py +0 -53
  7. {future_framework-3.1.0 → future_framework-3.1.1}/LICENSE +0 -0
  8. {future_framework-3.1.0 → future_framework-3.1.1}/README.md +0 -0
  9. {future_framework-3.1.0 → future_framework-3.1.1}/future/__init__.py +0 -0
  10. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/Auth0Authentication.py +0 -0
  11. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/AzureADAuthentication.py +0 -0
  12. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/BasicAuthentication.py +0 -0
  13. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KerberosAuthentication.py +0 -0
  14. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/KeycloakAuthentication.py +0 -0
  15. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/OAuth2Authentication.py +0 -0
  16. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/OpenIdConnectAuthentication.py +0 -0
  17. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/SAMLAuthentication.py +0 -0
  18. {future_framework-3.1.0 → future_framework-3.1.1}/future/authentication/__init__.py +0 -0
  19. {future_framework-3.1.0 → future_framework-3.1.1}/future/cli.py +0 -0
  20. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/DebugController.py +0 -0
  21. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/GraphQLController.py +0 -0
  22. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/OpenAPIController.py +0 -0
  23. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/WebSocketController.py +0 -0
  24. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/WelcomeController.py +0 -0
  25. {future_framework-3.1.0 → future_framework-3.1.1}/future/controllers/__init__.py +0 -0
  26. {future_framework-3.1.0 → future_framework-3.1.1}/future/database.py +0 -0
  27. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/ClickhouseDatabase.py +0 -0
  28. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/ElasticsearchDatabase.py +0 -0
  29. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/MongoDBDatabase.py +0 -0
  30. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/MySQLDatabase.py +0 -0
  31. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/PostgresDatabase.py +0 -0
  32. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/RedisDatabase.py +0 -0
  33. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/SQLiteDatabase.py +0 -0
  34. {future_framework-3.1.0 → future_framework-3.1.1}/future/databases/__init__.py +0 -0
  35. {future_framework-3.1.0 → future_framework-3.1.1}/future/exceptions.py +0 -0
  36. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IAuthentication.py +0 -0
  37. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IController.py +0 -0
  38. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IDatabase.py +0 -0
  39. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IMiddleware.py +0 -0
  40. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IModel.py +0 -0
  41. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/IPlugin.py +0 -0
  42. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/ITask.py +0 -0
  43. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfaces/__init__.py +0 -0
  44. {future_framework-3.1.0 → future_framework-3.1.1}/future/interfacing.py +0 -0
  45. {future_framework-3.1.0 → future_framework-3.1.1}/future/lifespan.py +0 -0
  46. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/BruteforcePreventionMiddleware.py +0 -0
  47. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/CORSMiddleware.py +0 -0
  48. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/CSRFMiddleware.py +0 -0
  49. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/GZipMiddleware.py +0 -0
  50. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HTAccessConfuserMiddleware.py +0 -0
  51. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/HeaderConfuserMiddleware.py +0 -0
  52. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/RateLimitMiddleware.py +0 -0
  53. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/ResponseCodeConfuserMiddleware.py +0 -0
  54. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/SQLiConfuserMiddleware.py +0 -0
  55. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/ScopeValidationMiddleware.py +0 -0
  56. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/SessionMiddleware.py +0 -0
  57. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareRequest.py +0 -0
  58. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/TestMiddlewareResponse.py +0 -0
  59. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/WebServerConfuserMiddleware.py +0 -0
  60. {future_framework-3.1.0 → future_framework-3.1.1}/future/middleware/__init__.py +0 -0
  61. {future_framework-3.1.0 → future_framework-3.1.1}/future/migrations.py +0 -0
  62. {future_framework-3.1.0 → future_framework-3.1.1}/future/models/PostModel.py +0 -0
  63. {future_framework-3.1.0 → future_framework-3.1.1}/future/models/Query.py +0 -0
  64. {future_framework-3.1.0 → future_framework-3.1.1}/future/models/UserModel.py +0 -0
  65. {future_framework-3.1.0 → future_framework-3.1.1}/future/models/__init__.py +0 -0
  66. {future_framework-3.1.0 → future_framework-3.1.1}/future/openapi.py +0 -0
  67. {future_framework-3.1.0 → future_framework-3.1.1}/future/plugins/ElasticsearchPlugin.py +0 -0
  68. {future_framework-3.1.0 → future_framework-3.1.1}/future/plugins/__init__.py +0 -0
  69. {future_framework-3.1.0 → future_framework-3.1.1}/future/request.py +0 -0
  70. {future_framework-3.1.0 → future_framework-3.1.1}/future/response.py +0 -0
  71. {future_framework-3.1.0 → future_framework-3.1.1}/future/routing.py +0 -0
  72. {future_framework-3.1.0 → future_framework-3.1.1}/future/scheduling.py +0 -0
  73. {future_framework-3.1.0 → future_framework-3.1.1}/future/seeder.py +0 -0
  74. {future_framework-3.1.0 → future_framework-3.1.1}/future/settings.py +0 -0
  75. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckDNSTask.py +0 -0
  76. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckDiskUsageTask.py +0 -0
  77. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckHttpStatusTask.py +0 -0
  78. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckMemoryUsageTask.py +0 -0
  79. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckSSHBannerTask.py +0 -0
  80. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/CheckSystemUptimeTask.py +0 -0
  81. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/DailyBackupTask.py +0 -0
  82. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/SyncTaskExample.py +0 -0
  83. {future_framework-3.1.0 → future_framework-3.1.1}/future/tasks/__init__.py +0 -0
  84. {future_framework-3.1.0 → future_framework-3.1.1}/future/taskscheduler.py +0 -0
  85. {future_framework-3.1.0 → future_framework-3.1.1}/future/testclient.py +0 -0
  86. {future_framework-3.1.0 → future_framework-3.1.1}/future/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: future-framework
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Summary: ASGI-based framework for web APIs
5
5
  Author: nicolaipre
6
6
  License-Expression: MIT
@@ -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,6 +1,6 @@
1
1
  [project]
2
2
  name = "future-framework"
3
- version = "3.1.0"
3
+ version = "3.1.1"
4
4
  description = "ASGI-based framework for web APIs"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "future-framework"
3
- version = "3.1.0"
3
+ version = "3.1.1"
4
4
  description = "ASGI-based framework for web APIs"
5
5
  authors = [{ name = "nicolaipre" }]
6
6
  license = "MIT"
@@ -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)