mermaid-trace 0.3.1__py3-none-any.whl → 0.4.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.
@@ -1,25 +1,33 @@
1
1
  import logging
2
2
  import os
3
3
 
4
+
4
5
  class MermaidFileHandler(logging.FileHandler):
5
6
  """
6
7
  A custom logging handler that writes `FlowEvent` objects to a Mermaid (.mmd) file.
7
-
8
+
8
9
  Strategy & Optimization:
9
- 1. **Inheritance**: Inherits from `logging.FileHandler` to leverage robust,
10
+ 1. **Inheritance**: Inherits from `logging.FileHandler` to leverage robust,
10
11
  thread-safe file writing capabilities (locking, buffering) provided by the stdlib.
11
- 2. **Header Management**: Automatically handles the Mermaid file header
12
- (`sequenceDiagram`, `title`, `autonumber`) to ensure the output file
13
- is a valid Mermaid document. It smartly detects if the file is new or
12
+ 2. **Header Management**: Automatically handles the Mermaid file header
13
+ (`sequenceDiagram`, `title`, `autonumber`) to ensure the output file
14
+ is a valid Mermaid document. It smartly detects if the file is new or
14
15
  being appended to.
15
- 3. **Deferred Formatting**: The actual string conversion happens in the `emit`
16
+ 3. **Deferred Formatting**: The actual string conversion happens in the `emit`
16
17
  method (via the formatter), keeping the handler focused on I/O.
17
18
  """
18
-
19
- def __init__(self, filename: str, title: str = "Log Flow", mode: str = 'a', encoding: str = 'utf-8', delay: bool = False):
19
+
20
+ def __init__(
21
+ self,
22
+ filename: str,
23
+ title: str = "Log Flow",
24
+ mode: str = "a",
25
+ encoding: str = "utf-8",
26
+ delay: bool = False,
27
+ ):
20
28
  """
21
29
  Initialize the handler.
22
-
30
+
23
31
  Args:
24
32
  filename (str): The path to the output .mmd file.
25
33
  title (str): The title of the Mermaid diagram (written in the header).
@@ -30,22 +38,23 @@ class MermaidFileHandler(logging.FileHandler):
30
38
  """
31
39
  # Ensure directory exists to prevent FileNotFoundError on open
32
40
  os.makedirs(os.path.dirname(os.path.abspath(filename)) or ".", exist_ok=True)
33
-
41
+
34
42
  # Header Strategy:
35
43
  # We need to write the "sequenceDiagram" preamble ONLY if:
36
44
  # 1. We are overwriting the file (mode='w').
37
45
  # 2. We are appending (mode='a'), but the file doesn't exist or is empty.
46
+ # This prevents invalid Mermaid files (e.g., multiple "sequenceDiagram" lines).
38
47
  should_write_header = False
39
- if mode == 'w':
48
+ if mode == "w":
40
49
  should_write_header = True
41
- elif mode == 'a':
50
+ elif mode == "a":
42
51
  if not os.path.exists(filename) or os.path.getsize(filename) == 0:
43
52
  should_write_header = True
44
-
53
+
45
54
  # Initialize standard FileHandler (opens the file unless delay=True)
46
55
  super().__init__(filename, mode, encoding, delay)
47
56
  self.title = title
48
-
57
+
49
58
  # Write header immediately if needed.
50
59
  if should_write_header:
51
60
  self._write_header()
@@ -53,14 +62,17 @@ class MermaidFileHandler(logging.FileHandler):
53
62
  def _write_header(self) -> None:
54
63
  """
55
64
  Writes the initial Mermaid syntax lines.
56
-
65
+
57
66
  This setup is required for Mermaid JS or Live Editor to render the diagram.
67
+ It defines the diagram type (sequenceDiagram), title, and enables autonumbering.
58
68
  """
59
69
  # We use the stream directly if available, or open momentarily if delayed
60
70
  if self.stream:
61
71
  self.stream.write("sequenceDiagram\n")
62
72
  self.stream.write(f" title {self.title}\n")
63
73
  self.stream.write(" autonumber\n")
74
+ # Flush ensures the header is written to disk immediately,
75
+ # so it appears even if the program crashes right after.
64
76
  self.flush()
65
77
  else:
66
78
  # Handling 'delay=True' case:
@@ -74,13 +86,14 @@ class MermaidFileHandler(logging.FileHandler):
74
86
  def emit(self, record: logging.LogRecord) -> None:
75
87
  """
76
88
  Process a log record.
77
-
89
+
78
90
  Optimization:
79
- - Checks for `flow_event` attribute first. This allows this handler
91
+ - Checks for `flow_event` attribute first. This allows this handler
80
92
  to be attached to the root logger without processing irrelevant system logs.
81
- - Delegates the actual writing to `super().emit()`, which handles
82
- thread locking and stream flushing.
93
+ It acts as a high-performance filter before formatting.
94
+ - Delegates the actual writing to `super().emit()`, which handles
95
+ thread locking and stream flushing safely.
83
96
  """
84
97
  # Only process records that contain our structured FlowEvent data
85
- if hasattr(record, 'flow_event'):
98
+ if hasattr(record, "flow_event"):
86
99
  super().emit(record)
@@ -12,7 +12,10 @@ if TYPE_CHECKING:
12
12
  else:
13
13
  try:
14
14
  from fastapi import Request, Response
15
- from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
15
+ from starlette.middleware.base import (
16
+ BaseHTTPMiddleware,
17
+ RequestResponseEndpoint,
18
+ )
16
19
  except ImportError:
17
20
  # Handle the case where FastAPI/Starlette are not installed.
18
21
  # We define dummy types to prevent NameErrors at import time,
@@ -22,37 +25,43 @@ else:
22
25
  Response = Any # type: ignore[assignment]
23
26
  RequestResponseEndpoint = Any # type: ignore[assignment]
24
27
 
28
+
25
29
  class MermaidTraceMiddleware(BaseHTTPMiddleware):
26
30
  """
27
31
  FastAPI Middleware to trace HTTP requests as interactions in the sequence diagram.
28
-
32
+
29
33
  This middleware acts as the entry point for tracing a web request. It:
30
34
  1. Identifies the client (Source).
31
35
  2. Logs the incoming request.
32
36
  3. Initializes the `LogContext` for the request lifecycle.
33
37
  4. Logs the response or error.
34
38
  """
39
+
35
40
  def __init__(self, app: Any, app_name: str = "FastAPI"):
36
41
  """
37
42
  Initialize the middleware.
38
-
43
+
39
44
  Args:
40
45
  app: The FastAPI application instance.
41
46
  app_name: The name of this service to appear in the diagram (e.g., "UserAPI").
42
47
  """
43
- if BaseHTTPMiddleware is object: # type: ignore[comparison-overlap]
44
- raise ImportError("FastAPI/Starlette is required to use MermaidTraceMiddleware")
48
+ if BaseHTTPMiddleware is object: # type: ignore[comparison-overlap]
49
+ raise ImportError(
50
+ "FastAPI/Starlette is required to use MermaidTraceMiddleware"
51
+ )
45
52
  super().__init__(app)
46
53
  self.app_name = app_name
47
54
 
48
- async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
55
+ async def dispatch(
56
+ self, request: Request, call_next: RequestResponseEndpoint
57
+ ) -> Response:
49
58
  """
50
59
  Intercepts the incoming request.
51
-
60
+
52
61
  Args:
53
62
  request (Request): The incoming HTTP request.
54
63
  call_next (Callable): The function to call the next middleware or endpoint.
55
-
64
+
56
65
  Returns:
57
66
  Response: The HTTP response.
58
67
  """
@@ -60,17 +69,17 @@ class MermaidTraceMiddleware(BaseHTTPMiddleware):
60
69
  # Try to get a specific ID from headers (useful for distributed tracing),
61
70
  # otherwise fallback to "Client".
62
71
  source = request.headers.get("X-Source", "Client")
63
-
72
+
64
73
  # Determine Trace ID
65
74
  # Check X-Trace-ID header or generate new UUID
66
75
  trace_id = request.headers.get("X-Trace-ID") or str(uuid.uuid4())
67
-
76
+
68
77
  # 2. Determine Action
69
78
  # Format: "METHOD /path" (e.g., "GET /users")
70
79
  action = f"{request.method} {request.url.path}"
71
-
80
+
72
81
  logger = get_flow_logger()
73
-
82
+
74
83
  # 3. Log Request (Source -> App)
75
84
  req_event = FlowEvent(
76
85
  source=source,
@@ -78,20 +87,27 @@ class MermaidTraceMiddleware(BaseHTTPMiddleware):
78
87
  action=action,
79
88
  message=action,
80
89
  params=f"query={request.query_params}" if request.query_params else None,
81
- trace_id=trace_id
90
+ trace_id=trace_id,
82
91
  )
83
- logger.info(f"{source}->{self.app_name}: {action}", extra={"flow_event": req_event})
84
-
92
+ logger.info(
93
+ f"{source}->{self.app_name}: {action}", extra={"flow_event": req_event}
94
+ )
95
+
85
96
  # 4. Set Context and Process Request
86
97
  # We set the current participant to the app name.
87
98
  # `ascope` ensures this context applies to all code running within `call_next`.
88
- async with LogContext.ascope({"participant": self.app_name, "trace_id": trace_id}):
99
+ # This includes route handlers, dependencies, and other middlewares called after this one.
100
+ async with LogContext.ascope(
101
+ {"participant": self.app_name, "trace_id": trace_id}
102
+ ):
89
103
  start_time = time.time()
90
104
  try:
91
105
  # Pass control to the application
106
+ # This executes the actual route logic
92
107
  response = await call_next(request)
93
-
108
+
94
109
  # 5. Log Response (App -> Source)
110
+ # Calculate execution duration for the response label
95
111
  duration = (time.time() - start_time) * 1000
96
112
  resp_event = FlowEvent(
97
113
  source=self.app_name,
@@ -100,14 +116,19 @@ class MermaidTraceMiddleware(BaseHTTPMiddleware):
100
116
  message="Return",
101
117
  is_return=True,
102
118
  result=f"{response.status_code} ({duration:.1f}ms)",
103
- trace_id=trace_id
119
+ trace_id=trace_id,
120
+ )
121
+ logger.info(
122
+ f"{self.app_name}->{source}: Return",
123
+ extra={"flow_event": resp_event},
104
124
  )
105
- logger.info(f"{self.app_name}->{source}: Return", extra={"flow_event": resp_event})
106
125
  return response
107
-
126
+
108
127
  except Exception as e:
109
128
  # 6. Log Error (App --x Source)
110
129
  # This captures unhandled exceptions that bubble up to the middleware
130
+ # Note: FastAPI's ExceptionHandlers might catch this before it reaches here.
131
+ # If so, you might see a successful return with 500 status instead.
111
132
  err_event = FlowEvent(
112
133
  source=self.app_name,
113
134
  target=source,
@@ -116,7 +137,9 @@ class MermaidTraceMiddleware(BaseHTTPMiddleware):
116
137
  is_return=True,
117
138
  is_error=True,
118
139
  error_message=str(e),
119
- trace_id=trace_id
140
+ trace_id=trace_id,
141
+ )
142
+ logger.error(
143
+ f"{self.app_name}-x{source}: Error", extra={"flow_event": err_event}
120
144
  )
121
- logger.error(f"{self.app_name}-x{source}: Error", extra={"flow_event": err_event})
122
145
  raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mermaid-trace
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Visualize your Python code execution flow as Mermaid Sequence Diagrams.
5
5
  Project-URL: Documentation, https://github.com/xt765/mermaid-trace#readme
6
6
  Project-URL: Changelog, https://github.com/xt765/mermaid-trace/blob/main/docs/en/CHANGELOG.md
@@ -38,6 +38,8 @@ Classifier: Programming Language :: Python
38
38
  Classifier: Programming Language :: Python :: 3.10
39
39
  Classifier: Programming Language :: Python :: 3.11
40
40
  Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Programming Language :: Python :: 3.13
42
+ Classifier: Programming Language :: Python :: 3.14
41
43
  Classifier: Programming Language :: Python :: Implementation :: CPython
42
44
  Classifier: Programming Language :: Python :: Implementation :: PyPy
43
45
  Classifier: Topic :: Software Development :: Debuggers
@@ -151,7 +153,7 @@ mermaid-trace serve my_flow.mmd
151
153
 
152
154
  ## 📂 Documentation
153
155
 
154
- - [English Documentation](docs/en/README.md)
156
+ - [English Documentation](docs/en/USER_GUIDE.md)
155
157
  - [中文文档](README_CN.md)
156
158
 
157
159
  ## 🤝 Contributing
@@ -0,0 +1,16 @@
1
+ mermaid_trace/__init__.py,sha256=yhc-shETKioVEQEfqmCGFyVelRR3_ypeClbMR_D2oBQ,5267
2
+ mermaid_trace/cli.py,sha256=F5-QfnKp_Et719IKWvU5IAWZTpq9ft01ow62DqNpHdA,9477
3
+ mermaid_trace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mermaid_trace/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ mermaid_trace/core/context.py,sha256=WyTKbC_I1ge802VUj9PdUiqa-VgL1r3DQUW_y8PlG8M,6618
6
+ mermaid_trace/core/decorators.py,sha256=13-n6qsmWnInpKgQ0Bgnnn5aQIWkRZ7raTD2xVtKVmU,12303
7
+ mermaid_trace/core/events.py,sha256=TVtarp7IwfTR_C404ZWoyqBZmTtScROz5hWL0uel3G4,2857
8
+ mermaid_trace/core/formatter.py,sha256=6k79eBU09TSCEUcDJN1mfn-_KMld0xXfKtQTKZb8Ogw,3178
9
+ mermaid_trace/handlers/async_handler.py,sha256=uGC27TCgfxyvQQEiJ_7Ir1EFJdsLHBUzHEsLktEaFtM,1893
10
+ mermaid_trace/handlers/mermaid_handler.py,sha256=JUq5gSQepNISreUYkyucS_rk27zGIiwSYFw_Lb8lL28,4314
11
+ mermaid_trace/integrations/fastapi.py,sha256=GJL2H0Ypi4HqiAR-kkV4kSUTitdOj4RkhPi26GGNORM,5515
12
+ mermaid_trace-0.4.0.dist-info/METADATA,sha256=4oszQifzCHhgCEI0Ly1nKAUuvcF-Bs-ejCReGUOsTbo,6287
13
+ mermaid_trace-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ mermaid_trace-0.4.0.dist-info/entry_points.txt,sha256=WS57KT_870v0A4B87QDjQUqJcddMQxbCQyYeczDAX34,57
15
+ mermaid_trace-0.4.0.dist-info/licenses/LICENSE,sha256=BrBog1Etiq9PdWy0SVQNVByIMD9ss4Edz-R0oXt49zA,1062
16
+ mermaid_trace-0.4.0.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- mermaid_trace/__init__.py,sha256=yEtdgB0Cl2qAuhGnU0zp9_YANzT3ZIrDsnQu-i3qUAc,2741
2
- mermaid_trace/cli.py,sha256=_6Bm6oGIUQFbb-tr4yDoZIa9_5PQ-qjftCd7JNUm4F8,7033
3
- mermaid_trace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- mermaid_trace/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- mermaid_trace/core/context.py,sha256=H2aXivdarZgpvfAdATRWAAPpf7KHSNqhoJOepd1FK_E,6711
6
- mermaid_trace/core/decorators.py,sha256=vjTnqxOCbIZXBahpgvXHBp3BFIjnIRk15O2jR9V8Ls4,9274
7
- mermaid_trace/core/events.py,sha256=SG-S0hZYNa_gEpwB4Cuaahwj9WAMdzS7xXIpThoZzGQ,2999
8
- mermaid_trace/core/formatter.py,sha256=WKXQeNj4l-LvbF2_Jz8FS3dyWmvtD3CHX2bQKj8p7D0,2765
9
- mermaid_trace/handlers/mermaid_handler.py,sha256=dZ_T57qWUQlVNWWH5se52mB4ZioECOFwGJF52V-OOps,3931
10
- mermaid_trace/integrations/fastapi.py,sha256=DeBodoPxXz2nLUIAiqmdVfFrwNVo2P0jhe7rcdF60u4,4991
11
- mermaid_trace-0.3.1.dist-info/METADATA,sha256=rRBCzhTbof4unI0MPJkj-eHzKTazsodNpdfHpzDPDi0,6181
12
- mermaid_trace-0.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
13
- mermaid_trace-0.3.1.dist-info/entry_points.txt,sha256=WS57KT_870v0A4B87QDjQUqJcddMQxbCQyYeczDAX34,57
14
- mermaid_trace-0.3.1.dist-info/licenses/LICENSE,sha256=BrBog1Etiq9PdWy0SVQNVByIMD9ss4Edz-R0oXt49zA,1062
15
- mermaid_trace-0.3.1.dist-info/RECORD,,