mermaid-trace 0.4.1__py3-none-any.whl → 0.5.3.post0__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.
mermaid_trace/__init__.py CHANGED
@@ -27,16 +27,41 @@ Usage Example:
27
27
  """
28
28
 
29
29
  from .core.decorators import trace_interaction, trace
30
- from .handlers.mermaid_handler import MermaidFileHandler
30
+ from .core.utils import trace_class, patch_object
31
+ from .handlers.mermaid_handler import (
32
+ MermaidFileHandler,
33
+ RotatingMermaidFileHandler,
34
+ TimedRotatingMermaidFileHandler,
35
+ )
31
36
  from .handlers.async_handler import AsyncMermaidHandler
32
37
  from .core.events import Event, FlowEvent
33
38
  from .core.context import LogContext
34
39
  from .core.formatter import BaseFormatter, MermaidFormatter
40
+ from .core.config import config, MermaidConfig
41
+
42
+ __all__ = [
43
+ "trace_interaction",
44
+ "trace",
45
+ "trace_class",
46
+ "patch_object",
47
+ "MermaidFileHandler",
48
+ "RotatingMermaidFileHandler",
49
+ "TimedRotatingMermaidFileHandler",
50
+ "AsyncMermaidHandler",
51
+ "Event",
52
+ "FlowEvent",
53
+ "LogContext",
54
+ "BaseFormatter",
55
+ "MermaidFormatter",
56
+ "config",
57
+ "MermaidConfig",
58
+ "configure_flow",
59
+ ]
35
60
  # We don't import integrations by default to avoid hard dependencies
36
61
  # Integrations (like FastAPI) must be imported explicitly by the user if needed.
37
62
 
38
63
  from importlib.metadata import PackageNotFoundError, version
39
- from typing import List, Optional
64
+ from typing import List, Optional, Dict, Any
40
65
 
41
66
  import logging
42
67
 
@@ -45,8 +70,12 @@ def configure_flow(
45
70
  output_file: str = "flow.mmd",
46
71
  handlers: Optional[List[logging.Handler]] = None,
47
72
  append: bool = False,
73
+ overwrite: bool = True,
48
74
  async_mode: bool = False,
49
- ) -> logging.Logger:
75
+ level: int = logging.INFO,
76
+ config_overrides: Optional[Dict[str, Any]] = None,
77
+ queue_size: Optional[int] = None,
78
+ ) -> logging.Logger: # noqa: PLR0913
50
79
  """
51
80
  Configures the flow logger to output to a Mermaid file.
52
81
 
@@ -64,18 +93,30 @@ def configure_flow(
64
93
  Useful if you want to stream logs to other destinations.
65
94
  append (bool): If True, adds the new handler(s) without removing existing ones.
66
95
  Defaults to False (clears existing handlers to prevent duplicate logging).
96
+ overwrite (bool): If True, overwrites the output file if it already exists.
97
+ If False, appends to the existing file. Defaults to True.
67
98
  async_mode (bool): If True, uses a non-blocking background thread for logging (QueueHandler).
68
99
  Recommended for high-performance production environments to avoid
69
100
  blocking the main execution thread during file I/O.
70
101
  Defaults to False.
102
+ level (int): Logging level. Defaults to logging.INFO.
103
+ config_overrides (Dict[str, Any], optional): Dictionary to override default configuration settings.
104
+ Keys should match MermaidConfig attributes.
105
+ queue_size (int, optional): Size of the async queue. If provided, overrides config.queue_size.
71
106
 
72
107
  Returns:
73
108
  logging.Logger: The configured logger instance used for flow tracing.
74
109
  """
110
+ # Apply configuration overrides
111
+ if config_overrides:
112
+ for k, v in config_overrides.items():
113
+ if hasattr(config, k):
114
+ setattr(config, k, v)
115
+
75
116
  # Get the specific logger used by the tracing decorators
76
117
  # This logger is isolated from the root logger to prevent pollution
77
118
  logger = logging.getLogger("mermaid_trace.flow")
78
- logger.setLevel(logging.INFO)
119
+ logger.setLevel(level)
79
120
 
80
121
  # Remove existing handlers to avoid duplicate logs if configured multiple times
81
122
  # unless 'append' is requested. This ensures idempotency when calling configure_flow multiple times.
@@ -91,15 +132,21 @@ def configure_flow(
91
132
  else:
92
133
  # Create default Mermaid handler
93
134
  # This handler knows how to write the Mermaid header and format events
94
- handler = MermaidFileHandler(output_file)
135
+ mode = "w" if overwrite else "a"
136
+ handler = MermaidFileHandler(output_file, mode=mode)
95
137
  handler.setFormatter(MermaidFormatter())
96
138
  target_handlers = [handler]
97
139
 
98
140
  if async_mode:
141
+ # Determine queue size
142
+ final_queue_size = queue_size if queue_size is not None else config.queue_size
143
+
99
144
  # Wrap the target handlers in an AsyncMermaidHandler (QueueHandler)
100
145
  # The QueueListener will pick up logs from the queue and dispatch to target_handlers
101
146
  # This decouples the application execution from the logging I/O
102
- async_handler = AsyncMermaidHandler(target_handlers)
147
+ async_handler = AsyncMermaidHandler(
148
+ target_handlers, queue_size=final_queue_size
149
+ )
103
150
  logger.addHandler(async_handler)
104
151
  else:
105
152
  # Attach handlers directly to the logger for synchronous logging
@@ -116,18 +163,3 @@ try:
116
163
  except PackageNotFoundError:
117
164
  # Fallback version if the package is not installed (e.g., local development)
118
165
  __version__ = "0.0.0"
119
-
120
-
121
- # Export public API for easy access
122
- __all__ = [
123
- "trace_interaction",
124
- "trace",
125
- "configure_flow",
126
- "MermaidFileHandler",
127
- "AsyncMermaidHandler",
128
- "LogContext",
129
- "Event",
130
- "FlowEvent",
131
- "BaseFormatter",
132
- "MermaidFormatter",
133
- ]
mermaid_trace/cli.py CHANGED
@@ -1,8 +1,20 @@
1
1
  """
2
- Command Line Interface Module
3
-
4
- This module provides command-line functionality for MermaidTrace, primarily for
5
- previewing generated Mermaid diagrams in a web browser with live reload capabilities.
2
+ Command Line Interface (CLI) Module for MermaidTrace.
3
+
4
+ This module serves as the entry point for the MermaidTrace command-line tools.
5
+ It provides functionality to:
6
+ 1. **Serve** Mermaid diagram files (.mmd) via a local HTTP server.
7
+ 2. **Preview** diagrams in a web browser with live-reload capabilities.
8
+ 3. **Monitor** file changes using polling or filesystem events (via `watchdog`).
9
+
10
+ Key Components:
11
+ - `serve`: The primary command function that sets up the HTTP server and file watcher.
12
+ - `_create_handler`: A factory function that generates a custom request handler with access to the target file.
13
+ - `HTML_TEMPLATE`: A self-contained HTML page that renders Mermaid diagrams using the Mermaid.js CDN.
14
+
15
+ Usage:
16
+ Run this module directly or via the `mermaid-trace` command (if installed).
17
+ Example: `python -m mermaid_trace.cli serve diagram.mmd --port 8080`
6
18
  """
7
19
 
8
20
  import argparse
@@ -14,22 +26,28 @@ import os
14
26
  from pathlib import Path
15
27
  from typing import Type, Any
16
28
 
29
+ # Attempt to import `watchdog` for efficient file system monitoring.
30
+ # `watchdog` is an external library that allows the program to react to file events (like modifications) immediately.
31
+ # We handle the ImportError gracefully to allow the CLI to function (via polling) even if `watchdog` is not installed.
17
32
  try:
18
- # Watchdog is an optional dependency for efficient file monitoring
19
- # If installed, it enables instant file change detection
20
33
  from watchdog.observers import Observer
21
34
  from watchdog.events import FileSystemEventHandler
22
35
 
23
36
  HAS_WATCHDOG = True
24
37
  except ImportError:
25
- # Fallback when watchdog is not installed (minimal install case)
38
+ # If watchdog is missing, we fall back to a simpler polling mechanism in the browser
26
39
  HAS_WATCHDOG = False
27
40
 
28
- # HTML Template for the diagram preview page
29
- # Provides a self-contained environment to render Mermaid diagrams with:
30
- # 1. Mermaid.js library from CDN for diagram rendering
31
- # 2. Basic CSS styling for readability and layout
32
- # 3. JavaScript for auto-refreshing when the source file changes
41
+ # HTML Template for the diagram preview page.
42
+ # This string contains the full HTML structure served to the browser.
43
+ #
44
+ # It includes:
45
+ # 1. **Mermaid.js CDN**: Loads the library required to render the diagrams client-side.
46
+ # 2. **CSS Styling**: Basic styles for layout, readability, and the "Refresh" button.
47
+ # 3. **JavaScript Logic**:
48
+ # - Initializes Mermaid.js.
49
+ # - Implements a polling mechanism (`checkUpdate`) that hits the `/_status` endpoint.
50
+ # - Reloads the page automatically if the server reports a newer file modification time.
33
51
  HTML_TEMPLATE = """
34
52
  <!DOCTYPE html>
35
53
  <html lang="en">
@@ -37,20 +55,30 @@ HTML_TEMPLATE = """
37
55
  <meta charset="UTF-8">
38
56
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
39
57
  <title>MermaidTrace Flow Preview</title>
40
- <!-- Load Mermaid.js from CDN -->
58
+ <!-- Load Mermaid.js from CDN (Content Delivery Network) -->
59
+ <!-- This library parses the text-based diagram definition and renders it as an SVG -->
41
60
  <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
42
61
  <style>
43
62
  /* Basic styling for readability and layout */
44
63
  body {{ font-family: sans-serif; padding: 20px; background: #f4f4f4; }}
64
+
65
+ /* Container for the diagram to give it a "card" look */
45
66
  .container {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
67
+
46
68
  h1 {{ color: #333; }}
47
- #diagram {{ overflow-x: auto; }} /* Allow horizontal scrolling for wide diagrams */
69
+
70
+ /* Allow horizontal scrolling for wide diagrams that might overflow the screen */
71
+ #diagram {{ overflow-x: auto; }}
72
+
73
+ /* Floating refresh button for manual reloads */
48
74
  .refresh-btn {{
49
75
  position: fixed; bottom: 20px; right: 20px;
50
76
  padding: 10px 20px; background: #007bff; color: white;
51
77
  border: none; border-radius: 5px; cursor: pointer; font-size: 16px;
52
78
  }}
53
79
  .refresh-btn:hover {{ background: #0056b3; }}
80
+
81
+ /* Status indicator to show the user that the live-reload is active */
54
82
  .status {{
55
83
  position: fixed; bottom: 20px; left: 20px;
56
84
  font-size: 12px; color: #666;
@@ -60,29 +88,38 @@ HTML_TEMPLATE = """
60
88
  <body>
61
89
  <div class="container">
62
90
  <h1>MermaidTrace Flow Preview: {filename}</h1>
63
- <!-- The Mermaid diagram content will be injected here -->
91
+ <!-- The Mermaid diagram content will be injected here by the Python server -->
92
+ <!-- The 'mermaid' class triggers the Mermaid.js library to process this div -->
64
93
  <div class="mermaid" id="diagram">
65
94
  {content}
66
95
  </div>
67
96
  </div>
68
- <!-- Button to manually reload the page/diagram -->
97
+
98
+ <!-- Button to manually reload the page/diagram if auto-reload fails or is slow -->
69
99
  <button class="refresh-btn" onclick="location.reload()">Refresh Diagram</button>
70
100
  <div class="status" id="status">Monitoring for changes...</div>
71
101
 
72
102
  <script>
73
- // Initialize Mermaid with default settings
103
+ // Initialize Mermaid with default settings.
104
+ // 'startOnLoad: true' tells Mermaid to find all .mermaid classes and render them immediately.
74
105
  mermaid.initialize({{ startOnLoad: true, theme: 'default' }});
75
106
 
76
- // Live Reload Logic
77
- // We track the file's modification time (mtime) sent by the server.
107
+ // --- Live Reload Logic ---
108
+
109
+ // We track the file's modification time (mtime) sent by the server during the initial page load.
110
+ // This value is injected into the template by Python.
78
111
  const currentMtime = "{mtime}";
79
112
 
113
+ /**
114
+ * Checks the server for updates to the source file.
115
+ * It fetches the '/_status' endpoint which returns the current file mtime.
116
+ */
80
117
  function checkUpdate() {{
81
- // Poll the /_status endpoint to check if the file has changed on disk
82
118
  fetch('/_status')
83
119
  .then(response => response.text())
84
120
  .then(mtime => {{
85
- // If the server reports a different mtime, reload the page
121
+ // If the server reports a different mtime than what we loaded with,
122
+ // it means the file has changed on disk. We reload the page to see the new diagram.
86
123
  if (mtime && mtime !== currentMtime) {{
87
124
  console.log("File changed, reloading...");
88
125
  location.reload();
@@ -91,8 +128,9 @@ HTML_TEMPLATE = """
91
128
  .catch(err => console.error("Error checking status:", err));
92
129
  }}
93
130
 
94
- // Poll every 1 second
95
- // This is a simple alternative to WebSockets for local dev tools
131
+ // Poll every 1 second (1000 milliseconds).
132
+ // This is a simple, robust alternative to WebSockets for local development tools.
133
+ // It creates minimal load for a local server.
96
134
  setInterval(checkUpdate, 1000);
97
135
  </script>
98
136
  </body>
@@ -106,81 +144,89 @@ def _create_handler(
106
144
  """
107
145
  Factory function to create a custom HTTP request handler class.
108
146
 
109
- This uses a closure to inject `filename` and `path` into the handler's scope,
110
- allowing the `do_GET` method to access them without global variables.
147
+ We use a factory function (a function that returns a class) because `socketserver`
148
+ expects a class type, not an instance. This allows us to "close over" the `filename`
149
+ and `path` variables, making them available to the handler class without using globals.
111
150
 
112
151
  Args:
113
- filename (str): Display name of the file being served
114
- path (Path): Path object pointing to the file on disk
152
+ filename (str): The display name of the file being served (used in the HTML title).
153
+ path (Path): The `pathlib.Path` object pointing to the actual file on disk.
115
154
 
116
155
  Returns:
117
- Type[SimpleHTTPRequestHandler]: A custom request handler class
156
+ Type[SimpleHTTPRequestHandler]: A custom class inheriting from `SimpleHTTPRequestHandler`.
118
157
  """
119
158
 
120
159
  class Handler(http.server.SimpleHTTPRequestHandler):
121
160
  """
122
161
  Custom HTTP Request Handler for serving Mermaid diagram previews.
123
162
 
124
- This handler intercepts GET requests to:
125
- - Serve the HTML wrapper with embedded diagram content at the root path ('/')
126
- - Provide file modification time for live reload at '/_status'
127
- - Fall back to default behavior for other paths
163
+ This handler overrides standard methods to provide two specific endpoints:
164
+ 1. `/` (Root): Serves the HTML wrapper with the embedded diagram content.
165
+ 2. `/_status`: Returns the file's current modification time (used for live reload).
128
166
  """
129
167
 
130
168
  def log_message(self, format: str, *args: Any) -> None:
131
169
  """
132
- Suppress default request logging to keep the console clean.
170
+ Override `log_message` to suppress default HTTP request logging.
133
171
 
134
- Only application logs are shown, not every HTTP request.
172
+ By default, `SimpleHTTPRequestHandler` logs every request to stderr.
173
+ We override this to keep the console output clean, showing only important application logs.
135
174
  """
136
175
  pass
137
176
 
138
177
  def do_GET(self) -> None:
139
178
  """
140
- Handle GET requests for different paths.
179
+ Handle HTTP GET requests.
141
180
 
142
181
  Routes:
143
- - '/' : Serves HTML wrapper with embedded Mermaid content
144
- - '/_status' : Returns current file modification time for live reload
145
- - other paths : Falls back to default SimpleHTTPRequestHandler behavior
182
+ - **/**: Reads the target file, injects it into `HTML_TEMPLATE`, and serves the HTML.
183
+ - **/_status**: Checks the file's modification time and returns it as plain text.
184
+ - **Others**: Falls back to the default file serving behavior (though typically not used here).
146
185
  """
147
186
  if self.path == "/":
148
- # Serve the main HTML page with embedded diagram
187
+ # --- Root Endpoint: Serve the HTML Page ---
149
188
  self.send_response(200)
150
189
  self.send_header("Content-type", "text/html")
151
190
  self.end_headers()
152
191
 
153
192
  try:
154
- # Read current content of the Mermaid file
193
+ # Read the current content of the Mermaid file from disk.
194
+ # We read it every time the page is requested to ensure we get the latest version.
155
195
  content = path.read_text(encoding="utf-8")
196
+ # Get the modification time to embed in the page for the JS poller.
156
197
  mtime = str(path.stat().st_mtime)
157
198
  except Exception as e:
158
- # Fallback if reading fails (e.g., file locked, permission error)
159
- # Display error directly in the diagram area
199
+ # Error Handling:
200
+ # If reading fails (e.g., file locked, permissions, deleted),
201
+ # we render a special Mermaid diagram showing the error message.
202
+ # This provides immediate visual feedback in the browser.
160
203
  content = f"sequenceDiagram\nNote right of Error: Failed to read file: {e}"
161
204
  mtime = "0"
162
205
 
163
- # Inject content into the HTML template
206
+ # Inject variables into the HTML template
164
207
  html = HTML_TEMPLATE.format(
165
208
  filename=filename, content=content, mtime=mtime
166
209
  )
167
210
  self.wfile.write(html.encode("utf-8"))
168
211
 
169
212
  elif self.path == "/_status":
170
- # API endpoint for client-side polling
171
- # Returns current file modification time as plain text
213
+ # --- Status Endpoint: Live Reload Polling ---
214
+ # The client-side JavaScript calls this endpoint periodically.
172
215
  self.send_response(200)
173
216
  self.send_header("Content-type", "text/plain")
174
217
  self.end_headers()
175
218
  try:
219
+ # Return the current modification time as the response body.
176
220
  mtime = str(path.stat().st_mtime)
177
221
  except OSError:
178
- # Fallback if file can't be accessed
222
+ # If the file cannot be accessed (e.g., deleted), return "0".
179
223
  mtime = "0"
180
224
  self.wfile.write(mtime.encode("utf-8"))
181
225
 
182
226
  else:
183
- # Serve other static files if needed, or return 404
227
+ # --- Fallback: Default Behavior ---
228
+ # Useful if the HTML template referenced other static assets (images, css files),
229
+ # though currently everything is embedded.
184
230
  super().do_GET()
185
231
 
186
232
  return Handler
@@ -188,40 +234,43 @@ def _create_handler(
188
234
 
189
235
  def serve(filename: str, port: int = 8000) -> None:
190
236
  """
191
- Starts a local HTTP server to preview Mermaid diagrams in a web browser.
237
+ Starts the local HTTP server and file watcher to preview a Mermaid diagram.
192
238
 
193
- This function blocks the main thread while running a TCP server. It automatically
194
- opens the default web browser to the preview URL and supports live reload when
195
- the source .mmd file changes.
239
+ This is the core logic for the `serve` command. It sets up the environment,
240
+ opens the browser, and enters a blocking loop to serve requests.
196
241
 
197
- Features:
198
- - Serves .mmd files wrapped in an HTML viewer with Mermaid.js
199
- - Live reload functionality using Watchdog (if available) or client-side polling
200
- - Graceful shutdown handling on Ctrl+C
201
- - Automatic browser opening
242
+ Workflow:
243
+ 1. Validates the input file.
244
+ 2. Sets up a `watchdog` observer (if installed) for console logging of changes.
245
+ 3. Creates the custom HTTP handler using `_create_handler`.
246
+ 4. Opens the user's default web browser to the server URL.
247
+ 5. Starts a threaded TCP server to handle HTTP requests.
202
248
 
203
249
  Args:
204
- filename (str): Path to the .mmd file to serve
205
- port (int, optional): Port to bind the server to. Defaults to 8000.
250
+ filename (str): The path to the .mmd file to serve.
251
+ port (int): The port number to bind the server to (default: 8000).
206
252
  """
207
- # Resolve the file path
253
+ # Create a Path object for robust file path handling
208
254
  path = Path(filename)
255
+
256
+ # 1. Validation
209
257
  if not path.exists():
210
258
  print(f"Error: File '{filename}' not found.")
211
259
  sys.exit(1)
212
260
 
213
- # Setup Watchdog file watcher if available
214
- # Watchdog provides immediate file change notifications in the terminal
215
- # The actual browser reload is handled by client-side polling
261
+ # 2. Watchdog Setup (Optional)
262
+ # If `watchdog` is installed, we use it to print immediate feedback to the console when the file changes.
263
+ # Note: The browser reload is driven by the JS polling the `/_status` endpoint, not by this observer.
264
+ # This observer is primarily for developer feedback in the terminal.
216
265
  observer = None
217
266
  if HAS_WATCHDOG:
218
267
 
219
268
  class FileChangeHandler(FileSystemEventHandler):
220
- """Watchdog event handler for detecting changes to the served file"""
269
+ """Internal handler class for Watchdog events."""
221
270
 
222
271
  def on_modified(self, event: Any) -> None:
223
- """Called when a file is modified"""
224
- # Filter only for modifications to our specific file
272
+ """Triggered when a file is modified in the watched directory."""
273
+ # We only care about modifications to the specific file we are serving.
225
274
  if not event.is_directory and os.path.abspath(event.src_path) == str(
226
275
  path.resolve()
227
276
  ):
@@ -229,6 +278,7 @@ def serve(filename: str, port: int = 8000) -> None:
229
278
 
230
279
  print("Initializing file watcher...")
231
280
  observer = Observer()
281
+ # Watch the directory containing the file, but filter events in the handler
232
282
  observer.schedule(FileChangeHandler(), path=str(path.parent), recursive=False)
233
283
  observer.start()
234
284
  else:
@@ -236,52 +286,55 @@ def serve(filename: str, port: int = 8000) -> None:
236
286
  "Watchdog not installed. Falling back to polling mode (client-side only)."
237
287
  )
238
288
 
239
- # Create the custom HTTP handler
289
+ # 3. Create Server Handler
240
290
  HandlerClass = _create_handler(filename, path)
241
291
 
242
- # Print server information
292
+ # 4. User Feedback
243
293
  print(f"Serving {filename} at http://localhost:{port}")
244
294
  print("Press Ctrl+C to stop.")
245
295
 
246
- # Automatically open the default web browser to the preview URL
296
+ # 5. Open Browser
297
+ # We open the browser *before* the server loop blocks, but the request might fail if the server
298
+ # isn't ready instantly. However, `socketserver` setup is usually fast enough.
247
299
  webbrowser.open(f"http://localhost:{port}")
248
300
 
249
- # Start the TCP server
250
- # Using ThreadingTCPServer to handle multiple requests concurrently
251
- # This ensures browser polling doesn't block the initial page load
301
+ # 6. Start Server
302
+ # We use `ThreadingTCPServer` so that multiple requests (e.g., polling + main page load)
303
+ # can be handled concurrently. This prevents the polling loop from blocking the page load.
252
304
  with socketserver.ThreadingTCPServer(("", port), HandlerClass) as httpd:
253
305
  try:
254
- # Serve forever until interrupted
306
+ # Block and handle requests indefinitely
255
307
  httpd.serve_forever()
256
308
  except KeyboardInterrupt:
257
- # Handle Ctrl+C gracefully
309
+ # 7. Graceful Shutdown
310
+ # Catch Ctrl+C to clean up resources properly
258
311
  print("\nStopping server...")
259
- # Stop the watchdog observer if it was started
260
312
  if observer:
261
313
  observer.stop()
262
314
  observer.join()
263
- # Close the server
264
315
  httpd.server_close()
265
316
 
266
317
 
267
318
  def main() -> None:
268
319
  """
269
- Entry point for the CLI application.
320
+ Main entry point for the CLI application.
270
321
 
271
- Parses command-line arguments and dispatches to the appropriate command handler.
272
- Currently supports only the 'serve' command for previewing Mermaid diagrams.
322
+ Responsibilities:
323
+ 1. **Argument Parsing**: Uses `argparse` to define commands and options.
324
+ 2. **Command Dispatch**: Calls the appropriate function based on the user's command.
273
325
  """
274
- # Create argument parser
326
+ # Initialize the argument parser with a description of the tool
275
327
  parser = argparse.ArgumentParser(
276
328
  description="MermaidTrace CLI - Preview Mermaid diagrams in browser"
277
329
  )
278
330
 
279
- # Add subparsers for different commands
331
+ # Create sub-parsers to handle different commands (currently only 'serve')
280
332
  subparsers = parser.add_subparsers(
281
333
  dest="command", required=True, help="Available commands"
282
334
  )
283
335
 
284
- # Define 'serve' command for previewing diagrams
336
+ # --- 'serve' command ---
337
+ # Defines the 'serve' command which takes a file path and an optional port
285
338
  serve_parser = subparsers.add_parser(
286
339
  "serve", help="Serve a Mermaid file in the browser with live reload"
287
340
  )
@@ -290,14 +343,15 @@ def main() -> None:
290
343
  "--port", type=int, default=8000, help="Port to bind to (default: 8000)"
291
344
  )
292
345
 
293
- # Parse arguments and execute command
346
+ # Parse the arguments provided by the user
294
347
  args = parser.parse_args()
295
348
 
349
+ # Dispatch logic
296
350
  if args.command == "serve":
297
- # Execute the serve command
351
+ # Invoke the serve function with parsed arguments
298
352
  serve(args.file, args.port)
299
353
 
300
354
 
301
355
  if __name__ == "__main__":
302
- # Run the main function when script is executed directly
356
+ # Standard boilerplate to run the main function when the script is executed directly
303
357
  main()
@@ -0,0 +1,55 @@
1
+ """
2
+ Configuration Module for Mermaid Trace
3
+ ======================================
4
+
5
+ This module provides a centralized configuration system for the library.
6
+ It allows users to control behavior globally, such as argument capturing,
7
+ string truncation limits, and logging levels.
8
+ """
9
+
10
+ from dataclasses import dataclass
11
+ import os
12
+
13
+
14
+ @dataclass
15
+ class MermaidConfig:
16
+ """
17
+ Global configuration settings for Mermaid Trace.
18
+
19
+ Attributes:
20
+ capture_args (bool): Whether to capture function arguments and return values.
21
+ Defaults to True. Set to False for performance or privacy.
22
+ max_string_length (int): Maximum length for string representations of objects.
23
+ Defaults to 50. Prevents huge log files.
24
+ max_arg_depth (int): Maximum recursion depth for nested objects (lists/dicts).
25
+ Defaults to 1.
26
+ queue_size (int): Size of the async queue. Defaults to 1000.
27
+ """
28
+
29
+ capture_args: bool = True
30
+ max_string_length: int = 50
31
+ max_arg_depth: int = 1
32
+ queue_size: int = 1000
33
+
34
+ @classmethod
35
+ def from_env(cls) -> "MermaidConfig":
36
+ """
37
+ Loads configuration from environment variables.
38
+
39
+ Env Vars:
40
+ MERMAID_TRACE_CAPTURE_ARGS (bool): "true"/"false"
41
+ MERMAID_TRACE_MAX_STRING_LENGTH (int)
42
+ MERMAID_TRACE_MAX_ARG_DEPTH (int)
43
+ MERMAID_TRACE_QUEUE_SIZE (int)
44
+ """
45
+ return cls(
46
+ capture_args=os.getenv("MERMAID_TRACE_CAPTURE_ARGS", "true").lower()
47
+ == "true",
48
+ max_string_length=int(os.getenv("MERMAID_TRACE_MAX_STRING_LENGTH", "50")),
49
+ max_arg_depth=int(os.getenv("MERMAID_TRACE_MAX_ARG_DEPTH", "1")),
50
+ queue_size=int(os.getenv("MERMAID_TRACE_QUEUE_SIZE", "1000")),
51
+ )
52
+
53
+
54
+ # Global configuration instance
55
+ config = MermaidConfig()