mermaid-trace 0.4.0__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
- from .core.events import FlowEvent
37
+ from .core.events import Event, FlowEvent
33
38
  from .core.context import LogContext
34
- from .core.formatter import MermaidFormatter
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,16 +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
- "FlowEvent",
130
- "MermaidFormatter",
131
- ]
mermaid_trace/cli.py CHANGED
@@ -1,3 +1,22 @@
1
+ """
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`
18
+ """
19
+
1
20
  import argparse
2
21
  import http.server
3
22
  import socketserver
@@ -7,23 +26,28 @@ import os
7
26
  from pathlib import Path
8
27
  from typing import Type, Any
9
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.
10
32
  try:
11
- # Watchdog is an optional dependency that allows efficient file monitoring.
12
- # If installed, we use it to detect file changes instantly.
13
33
  from watchdog.observers import Observer
14
34
  from watchdog.events import FileSystemEventHandler
15
35
 
16
36
  HAS_WATCHDOG = True
17
37
  except ImportError:
18
- # Fallback for when watchdog is not installed (e.g., minimal install).
38
+ # If watchdog is missing, we fall back to a simpler polling mechanism in the browser
19
39
  HAS_WATCHDOG = False
20
40
 
21
- # HTML Template for the preview page
22
- # This template provides a self-contained environment to render Mermaid diagrams.
41
+ # HTML Template for the diagram preview page.
42
+ # This string contains the full HTML structure served to the browser.
43
+ #
23
44
  # It includes:
24
- # 1. Mermaid.js library from CDN.
25
- # 2. CSS for basic styling and layout.
26
- # 3. JavaScript logic for auto-refreshing when the source file changes.
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.
27
51
  HTML_TEMPLATE = """
28
52
  <!DOCTYPE html>
29
53
  <html lang="en">
@@ -31,20 +55,30 @@ HTML_TEMPLATE = """
31
55
  <meta charset="UTF-8">
32
56
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
57
  <title>MermaidTrace Flow Preview</title>
34
- <!-- 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 -->
35
60
  <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
36
61
  <style>
37
62
  /* Basic styling for readability and layout */
38
63
  body {{ font-family: sans-serif; padding: 20px; background: #f4f4f4; }}
64
+
65
+ /* Container for the diagram to give it a "card" look */
39
66
  .container {{ background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
67
+
40
68
  h1 {{ color: #333; }}
41
- #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 */
42
74
  .refresh-btn {{
43
75
  position: fixed; bottom: 20px; right: 20px;
44
76
  padding: 10px 20px; background: #007bff; color: white;
45
77
  border: none; border-radius: 5px; cursor: pointer; font-size: 16px;
46
78
  }}
47
79
  .refresh-btn:hover {{ background: #0056b3; }}
80
+
81
+ /* Status indicator to show the user that the live-reload is active */
48
82
  .status {{
49
83
  position: fixed; bottom: 20px; left: 20px;
50
84
  font-size: 12px; color: #666;
@@ -54,29 +88,38 @@ HTML_TEMPLATE = """
54
88
  <body>
55
89
  <div class="container">
56
90
  <h1>MermaidTrace Flow Preview: {filename}</h1>
57
- <!-- 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 -->
58
93
  <div class="mermaid" id="diagram">
59
94
  {content}
60
95
  </div>
61
96
  </div>
62
- <!-- Button to manually reload the page/diagram -->
97
+
98
+ <!-- Button to manually reload the page/diagram if auto-reload fails or is slow -->
63
99
  <button class="refresh-btn" onclick="location.reload()">Refresh Diagram</button>
64
100
  <div class="status" id="status">Monitoring for changes...</div>
65
101
 
66
102
  <script>
67
- // 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.
68
105
  mermaid.initialize({{ startOnLoad: true, theme: 'default' }});
69
106
 
70
- // Live Reload Logic
71
- // 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.
72
111
  const currentMtime = "{mtime}";
73
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
+ */
74
117
  function checkUpdate() {{
75
- // Poll the /_status endpoint to check if the file has changed on disk
76
118
  fetch('/_status')
77
119
  .then(response => response.text())
78
120
  .then(mtime => {{
79
- // 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.
80
123
  if (mtime && mtime !== currentMtime) {{
81
124
  console.log("File changed, reloading...");
82
125
  location.reload();
@@ -85,8 +128,9 @@ HTML_TEMPLATE = """
85
128
  .catch(err => console.error("Error checking status:", err));
86
129
  }}
87
130
 
88
- // Poll every 1 second
89
- // 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.
90
134
  setInterval(checkUpdate, 1000);
91
135
  </script>
92
136
  </body>
@@ -98,70 +142,91 @@ def _create_handler(
98
142
  filename: str, path: Path
99
143
  ) -> Type[http.server.SimpleHTTPRequestHandler]:
100
144
  """
101
- Factory function to create a custom request handler class.
145
+ Factory function to create a custom HTTP request handler class.
102
146
 
103
- This uses a closure to inject `filename` and `path` into the handler's scope,
104
- 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.
105
150
 
106
151
  Args:
107
- filename (str): Display name of the file.
108
- path (Path): Path object 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.
109
154
 
110
155
  Returns:
111
- Type[SimpleHTTPRequestHandler]: A custom handler class.
156
+ Type[SimpleHTTPRequestHandler]: A custom class inheriting from `SimpleHTTPRequestHandler`.
112
157
  """
113
158
 
114
159
  class Handler(http.server.SimpleHTTPRequestHandler):
115
160
  """
116
- Custom Request Handler to serve the generated HTML dynamically.
117
- It intercepts GET requests to serve the constructed HTML instead of static files.
161
+ Custom HTTP Request Handler for serving Mermaid diagram previews.
162
+
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).
118
166
  """
119
167
 
120
168
  def log_message(self, format: str, *args: Any) -> None:
121
- # Suppress default logging to keep console clean
122
- # We only want to see application logs, not every HTTP request
169
+ """
170
+ Override `log_message` to suppress default HTTP request logging.
171
+
172
+ By default, `SimpleHTTPRequestHandler` logs every request to stderr.
173
+ We override this to keep the console output clean, showing only important application logs.
174
+ """
123
175
  pass
124
176
 
125
177
  def do_GET(self) -> None:
126
178
  """
127
- Handle GET requests.
128
- Serves the HTML wrapper for the root path ('/').
179
+ Handle HTTP GET requests.
180
+
181
+ Routes:
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).
129
185
  """
130
186
  if self.path == "/":
187
+ # --- Root Endpoint: Serve the HTML Page ---
131
188
  self.send_response(200)
132
189
  self.send_header("Content-type", "text/html")
133
190
  self.end_headers()
134
191
 
135
192
  try:
136
- # Read the 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.
137
195
  content = path.read_text(encoding="utf-8")
196
+ # Get the modification time to embed in the page for the JS poller.
138
197
  mtime = str(path.stat().st_mtime)
139
198
  except Exception as e:
140
- # Fallback if reading fails (e.g., file locked)
141
- # Show the 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.
142
203
  content = f"sequenceDiagram\nNote right of Error: Failed to read file: {e}"
143
204
  mtime = "0"
144
205
 
145
- # Inject content into the HTML template
206
+ # Inject variables into the HTML template
146
207
  html = HTML_TEMPLATE.format(
147
208
  filename=filename, content=content, mtime=mtime
148
209
  )
149
210
  self.wfile.write(html.encode("utf-8"))
150
211
 
151
212
  elif self.path == "/_status":
152
- # API endpoint for client-side polling.
153
- # Returns the current modification time of the file.
213
+ # --- Status Endpoint: Live Reload Polling ---
214
+ # The client-side JavaScript calls this endpoint periodically.
154
215
  self.send_response(200)
155
216
  self.send_header("Content-type", "text/plain")
156
217
  self.end_headers()
157
218
  try:
219
+ # Return the current modification time as the response body.
158
220
  mtime = str(path.stat().st_mtime)
159
221
  except OSError:
222
+ # If the file cannot be accessed (e.g., deleted), return "0".
160
223
  mtime = "0"
161
224
  self.wfile.write(mtime.encode("utf-8"))
162
225
 
163
226
  else:
164
- # 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.
165
230
  super().do_GET()
166
231
 
167
232
  return Handler
@@ -169,35 +234,43 @@ def _create_handler(
169
234
 
170
235
  def serve(filename: str, port: int = 8000) -> None:
171
236
  """
172
- Starts a local HTTP server to preview the Mermaid diagram.
237
+ Starts the local HTTP server and file watcher to preview a Mermaid diagram.
173
238
 
174
- This function blocks the main thread and runs a TCP server.
175
- It automatically opens the default web browser to the preview URL.
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.
176
241
 
177
- Features:
178
- - Serves the .mmd file wrapped in an HTML viewer.
179
- - Uses Watchdog (if available) or client-side polling for live reloads.
180
- - Gracefully handles shutdown on Ctrl+C.
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.
181
248
 
182
249
  Args:
183
- filename (str): Path to the .mmd file to serve.
184
- port (int): Port to bind the server to. Default is 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).
185
252
  """
253
+ # Create a Path object for robust file path handling
186
254
  path = Path(filename)
255
+
256
+ # 1. Validation
187
257
  if not path.exists():
188
258
  print(f"Error: File '{filename}' not found.")
189
259
  sys.exit(1)
190
260
 
191
- # Setup Watchdog if available
192
- # Watchdog allows us to print console messages when the file changes.
193
- # The actual browser reload is triggered by the client polling the /_status endpoint,
194
- # but Watchdog gives immediate feedback in the terminal.
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.
195
265
  observer = None
196
266
  if HAS_WATCHDOG:
197
267
 
198
268
  class FileChangeHandler(FileSystemEventHandler):
269
+ """Internal handler class for Watchdog events."""
270
+
199
271
  def on_modified(self, event: Any) -> None:
200
- # Filter for the specific file we are watching
272
+ """Triggered when a file is modified in the watched directory."""
273
+ # We only care about modifications to the specific file we are serving.
201
274
  if not event.is_directory and os.path.abspath(event.src_path) == str(
202
275
  path.resolve()
203
276
  ):
@@ -205,6 +278,7 @@ def serve(filename: str, port: int = 8000) -> None:
205
278
 
206
279
  print("Initializing file watcher...")
207
280
  observer = Observer()
281
+ # Watch the directory containing the file, but filter events in the handler
208
282
  observer.schedule(FileChangeHandler(), path=str(path.parent), recursive=False)
209
283
  observer.start()
210
284
  else:
@@ -212,21 +286,28 @@ def serve(filename: str, port: int = 8000) -> None:
212
286
  "Watchdog not installed. Falling back to polling mode (client-side only)."
213
287
  )
214
288
 
289
+ # 3. Create Server Handler
215
290
  HandlerClass = _create_handler(filename, path)
216
291
 
292
+ # 4. User Feedback
217
293
  print(f"Serving {filename} at http://localhost:{port}")
218
294
  print("Press Ctrl+C to stop.")
219
295
 
220
- # Open browser automatically to the server 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.
221
299
  webbrowser.open(f"http://localhost:{port}")
222
300
 
223
- # Start the TCP server
224
- # ThreadingTCPServer is used to handle multiple requests concurrently if needed,
225
- # ensuring the browser polling doesn't block the initial 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.
226
304
  with socketserver.ThreadingTCPServer(("", port), HandlerClass) as httpd:
227
305
  try:
306
+ # Block and handle requests indefinitely
228
307
  httpd.serve_forever()
229
308
  except KeyboardInterrupt:
309
+ # 7. Graceful Shutdown
310
+ # Catch Ctrl+C to clean up resources properly
230
311
  print("\nStopping server...")
231
312
  if observer:
232
313
  observer.stop()
@@ -236,26 +317,41 @@ def serve(filename: str, port: int = 8000) -> None:
236
317
 
237
318
  def main() -> None:
238
319
  """
239
- Entry point for the CLI application.
240
- Parses arguments and dispatches to the appropriate command handler.
320
+ Main entry point for the CLI application.
321
+
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.
241
325
  """
242
- parser = argparse.ArgumentParser(description="MermaidTrace CLI")
243
- subparsers = parser.add_subparsers(dest="command", required=True)
326
+ # Initialize the argument parser with a description of the tool
327
+ parser = argparse.ArgumentParser(
328
+ description="MermaidTrace CLI - Preview Mermaid diagrams in browser"
329
+ )
330
+
331
+ # Create sub-parsers to handle different commands (currently only 'serve')
332
+ subparsers = parser.add_subparsers(
333
+ dest="command", required=True, help="Available commands"
334
+ )
244
335
 
245
- # 'serve' command definition
336
+ # --- 'serve' command ---
337
+ # Defines the 'serve' command which takes a file path and an optional port
246
338
  serve_parser = subparsers.add_parser(
247
- "serve", help="Serve a Mermaid file in the browser"
339
+ "serve", help="Serve a Mermaid file in the browser with live reload"
248
340
  )
249
- serve_parser.add_argument("file", help="Path to the .mmd file")
341
+ serve_parser.add_argument("file", help="Path to the .mmd file to serve")
250
342
  serve_parser.add_argument(
251
343
  "--port", type=int, default=8000, help="Port to bind to (default: 8000)"
252
344
  )
253
345
 
346
+ # Parse the arguments provided by the user
254
347
  args = parser.parse_args()
255
348
 
349
+ # Dispatch logic
256
350
  if args.command == "serve":
351
+ # Invoke the serve function with parsed arguments
257
352
  serve(args.file, args.port)
258
353
 
259
354
 
260
355
  if __name__ == "__main__":
356
+ # Standard boilerplate to run the main function when the script is executed directly
261
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()