orionis 0.643.0__py3-none-any.whl → 0.645.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,627 +0,0 @@
1
- import inspect
2
- import json
3
- import os
4
- from dataclasses import is_dataclass
5
- from datetime import datetime
6
- from typing import Any
7
- from rich.console import Console
8
- from rich.panel import Panel
9
- from rich.syntax import Syntax
10
- from rich.table import Table
11
- from rich.traceback import install
12
- from orionis.console.contracts.debug import IDebug
13
-
14
- class Debug(IDebug):
15
-
16
- def __init__(self, line:str = None) -> None:
17
- """
18
- Initializes the dump class.
19
-
20
- Parameters
21
- ----------
22
- line : str, optional
23
- The line text callback to use for output, default is None
24
- Returns
25
- -------
26
- None
27
- Notes
28
- -----
29
- This constructor performs the following setup:
30
- - Installs required dependencies via `install()`
31
- - Initializes a console instance for output handling
32
- - Sets the default indentation size to 4 spaces
33
- - Creates a set to prevent recursion during operations
34
- - Stores the provided line callback if specified
35
- """
36
- install()
37
- self.console = Console()
38
- self.indent_size = 4
39
- self._recursion_guard = set()
40
- self.line_tcbk = line
41
-
42
- def dd(self, *args: Any) -> None:
43
- """
44
- Dumps the provided arguments to the output and exits the program.
45
-
46
- Parameters
47
- ----------
48
- *args : Any
49
- Variable length argument list to be processed and output.
50
- Returns
51
- -------
52
- Notes
53
- -----
54
- This method will terminate the program execution after dumping the arguments.
55
- """
56
- self.__processOutput(*args, exit_after=True)
57
-
58
- def dump(self, *args: Any) -> None:
59
- """
60
- Dumps the provided arguments to the output without exiting the program.
61
-
62
- Parameters
63
- ----------
64
- *args : Any
65
- Variable length argument list to be processed and output.
66
-
67
- Returns
68
- -------
69
- None
70
-
71
- Notes
72
- -----
73
- This method displays the arguments in an appropriate format based on their type
74
- but allows the program execution to continue.
75
- """
76
- self.__processOutput(*args, exit_after=False)
77
-
78
- def __processOutput(self, *args: Any, exit_after: bool) -> None:
79
- """
80
- Processes the output based on the provided arguments and determines the appropriate
81
- format for displaying the data.
82
-
83
- Parameters
84
- ----------
85
- *args : Any
86
- Variable-length arguments representing the data to be processed.
87
- exit_after : bool
88
- If True, the program will exit after processing the output.
89
-
90
- Raises
91
- ------
92
- Exception
93
- Catches and logs any exception that occurs during processing.
94
-
95
- Notes
96
- -----
97
- If `exit_after` is True, the program will terminate with an exit code of 1.
98
- Determines whether to display data as a table, JSON, or raw dump based on its structure.
99
- """
100
-
101
- # Try to process the output and handle any exceptions that may occur
102
- try:
103
-
104
- # Check if any arguments were provided
105
- if not args:
106
- raise ValueError("No arguments were provided, or the arguments are null or invalid")
107
-
108
- # If only one argument is provided, determine its type and print accordingly
109
- elif len(args) == 1:
110
- arg = args[0]
111
- if self.__isJsonSerializable(arg) and self.__isTabular(arg) and isinstance(arg, (list)):
112
- self.__printTable(arg)
113
- elif self.__isJsonSerializable(arg):
114
- self.__printJson(arg)
115
- else:
116
- self.__printDump(args)
117
-
118
- # If multiple arguments are provided, determine the type of each and print accordingly
119
- else:
120
- self.__printDump(args)
121
-
122
- except Exception as e:
123
-
124
- # If an error occurs, print the error message in a standard panel format
125
- self.__printStandardPanel(
126
- f"[bold red]An error occurred while processing the debug output: {str(e)}[/]",
127
- border_style="red",
128
- )
129
- finally:
130
-
131
- # If exit_after is True, exit the program with a non-zero status code
132
- if exit_after:
133
- os._exit(1)
134
-
135
- def __printDump(self, args: tuple) -> None:
136
- """
137
- Prints a formatted dump of the provided arguments to the console.
138
-
139
- Parameters
140
- ----------
141
- args : tuple
142
- A tuple containing the objects to be dumped and displayed.
143
-
144
- Returns
145
- -------
146
- This method doesn't return anything.
147
-
148
- Notes
149
- -----
150
- This method processes each argument in the tuple, clears the recursion guard
151
- for each iteration, renders the argument using the __render method, and then
152
- prints all rendered content in a formatted panel with syntax highlighting.
153
- """
154
-
155
- # Clear the recursion guard before processing the arguments
156
- content = []
157
- for arg in args:
158
- self._recursion_guard.clear()
159
- content.append(self.__render(arg))
160
-
161
- # Print the rendered content in a standard panel with syntax highlighting
162
- self.__printStandardPanel(
163
- Syntax(
164
- "\n".join(content),
165
- "python",
166
- line_numbers=False,
167
- background_color="default",
168
- word_wrap=True
169
- ),
170
- border_style="cyan bold",
171
- )
172
-
173
- def __printJson(self, data: Any) -> None:
174
- """
175
- Print a JSON representation of the given data to the console using a styled panel.
176
-
177
- Parameters
178
- ----------
179
- data : Any
180
- The data to be serialized and displayed as JSON.
181
- Must be a dictionary or list for proper JSON serialization.
182
-
183
- Returns
184
- -------
185
- None
186
-
187
- Raises
188
- ------
189
- TypeError
190
- If the data cannot be serialized to JSON, falls back to a generic dump method.
191
-
192
- Notes
193
- -----
194
- Uses the `rich` library to format and display the JSON output with syntax highlighting.
195
- Retrieves and displays the caller's line information for context.
196
- Handles non-serializable objects using a custom JSON serializer.
197
- If serialization fails, falls back to the `__printDump` method.
198
- """
199
- try:
200
-
201
- # Check if the data is JSON serializable
202
- if not isinstance(data, (dict, list)):
203
- raise TypeError("Data must be a dictionary or a list for JSON serialization.")
204
-
205
- # Serialize the data to JSON format with custom serializer
206
- json_str = json.dumps(
207
- data,
208
- ensure_ascii=False,
209
- indent=2,
210
- default=self.__jsonSerializer
211
- )
212
-
213
- # Print the JSON string in a standard panel with syntax highlighting
214
- self.__printStandardPanel(
215
- Syntax(
216
- json_str,
217
- "json",
218
- line_numbers=True,
219
- background_color="default",
220
- word_wrap=True
221
- ),
222
- border_style="green",
223
- )
224
-
225
- except TypeError:
226
-
227
- # If serialization fails, print a dump of the data instead
228
- self.__printDump((data,))
229
-
230
- def __printTable(self, data: Any) -> None:
231
- """
232
- Prints a formatted table representation of the given data.
233
-
234
- Parameters
235
- ----------
236
- data : Any
237
- The data to be displayed in a tabular format.
238
- Can be a list of dictionaries, list of objects, or a dictionary.
239
-
240
- Returns
241
- -------
242
- None
243
- This method doesn't return anything, it prints output to the console.
244
-
245
- Notes
246
- -----
247
- - For lists of dictionaries: Uses dictionary keys as column headers
248
- - For lists of objects: Uses object attribute names as column headers
249
- - For simple lists: Shows index and value columns
250
- - For dictionaries: Shows key-value pairs as two columns
251
- - Falls back to __printDump if table rendering fails
252
- """
253
- try:
254
-
255
- # Create a table with specified styles and minimum width
256
- table = Table(
257
- show_header=True,
258
- header_style="bold white on blue",
259
- min_width=(self.console.width // 4) * 3
260
- )
261
-
262
- # Check if the data is in a tabular format (list or dict)
263
- if isinstance(data, list):
264
-
265
- # If the list is empty, print a message and return
266
- if not data:
267
- self.console.print("[yellow]Empty list[/]")
268
- return
269
-
270
- # Determine the columns based on the first item in the list
271
- first = data[0]
272
- if isinstance(first, dict):
273
- columns = list(first.keys())
274
- elif hasattr(first, '__dict__'):
275
- columns = list(vars(first).keys())
276
- else:
277
- columns = ["Index", "Value"]
278
-
279
- # Add columns to the table
280
- for col in columns:
281
- table.add_column(str(col))
282
-
283
- # Populate the table with data
284
- for i, item in enumerate(data):
285
- if isinstance(item, dict):
286
- table.add_row(*[str(item.get(col, '')) for col in columns])
287
- elif hasattr(item, '__dict__'):
288
- item_dict = vars(item)
289
- table.add_row(*[str(item_dict.get(col, '')) for col in columns])
290
- else:
291
- table.add_row(str(i), str(item))
292
-
293
- # If the data is a dictionary, create a key-value table
294
- elif isinstance(data, dict):
295
- table.add_column("Key", style="magenta")
296
- table.add_column("Value")
297
-
298
- for k, v in data.items():
299
- table.add_row(str(k), str(v))
300
-
301
- # If the data is not in a recognized format, print a dump
302
- self.__printStandardPanel(
303
- table,
304
- border_style="blue",
305
- )
306
-
307
- except Exception:
308
-
309
- # If an error occurs while creating the table, print a dump of the data
310
- self.__printDump((data,))
311
-
312
- def __printStandardPanel(self, renderable, border_style: str, padding=(0, 1)) -> None:
313
- """
314
- Renders a standard panel with the given content and styling options.
315
-
316
- Parameters
317
- ----------
318
- renderable : Any
319
- The content to be displayed inside the panel. This can be any renderable object
320
- supported by the Rich library.
321
- border_style : str
322
- The style of the border for the panel (e.g., "green", "red bold").
323
- padding : tuple, optional
324
- A tuple specifying the padding inside the panel as (vertical, horizontal).
325
- Default is (0, 1).
326
-
327
- Returns
328
- -------
329
- None
330
- This method prints to the console but does not return a value.
331
-
332
- Notes
333
- -----
334
- This method uses the Rich library to create and display a panel with the specified
335
- content and styling. It includes line information from the call stack and a timestamp.
336
- """
337
-
338
- # Get the line information from the call stack or use the provided callback
339
- if self.line_tcbk is None:
340
- frame = inspect.currentframe()
341
- caller_frame = frame.f_back.f_back.f_back.f_back if frame else None
342
- line_info = f"[blue underline]{self.__getLineInfo(caller_frame) if caller_frame else 'Unknown location'}[/]"
343
-
344
- # If a line callback is provided, use it to get the line information
345
- else:
346
- line_info = f"[blue underline]{self.line_tcbk}[/]"
347
-
348
- # Get the current timestamp for the subtitle
349
- subtitle = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
350
-
351
- # Print a blank line before the panel for better readability
352
- self.console.print()
353
- self.console.print(Panel(
354
- renderable,
355
- title=f"Debugger - {line_info}",
356
- title_align='left',
357
- subtitle=subtitle,
358
- subtitle_align='right',
359
- border_style=border_style,
360
- highlight=True,
361
- padding=padding,
362
- width=(self.console.width // 4) * 3,
363
- ))
364
- self.console.print()
365
-
366
- def __isTabular(self, data: Any) -> bool:
367
- """
368
- Determines if the given data is in a tabular format.
369
-
370
- Parameters
371
- ----------
372
- data : Any
373
- The data to be checked for tabular structure.
374
-
375
- Returns
376
- -------
377
- bool
378
- True if the data is in a tabular format, False otherwise.
379
-
380
- Notes
381
- -----
382
- A data structure is considered tabular if it is:
383
- - A list of dictionaries with identical keys
384
- - A list of objects with attributes
385
- - A dictionary
386
- """
387
-
388
- # Check if the data is a list or a dictionary
389
- if isinstance(data, list):
390
-
391
- # If the list is empty, it is not considered tabular
392
- if all(isinstance(item, dict) for item in data):
393
- keys = set(data[0].keys())
394
- return all(set(item.keys()) == keys for item in data)
395
-
396
- # If the list contains objects, check if they have a __dict__ attribute
397
- if len(data) > 0 and hasattr(data[0], '__dict__'):
398
- return True
399
-
400
- # If the data is a tuple, set, or any other iterable, it is not considered tabular
401
- elif isinstance(data, dict):
402
- return True
403
-
404
- # If the data is not a list or dictionary, it is not considered tabular
405
- return False
406
-
407
- def __isJsonSerializable(self, data: Any) -> bool:
408
- """
409
- Determines if the given data is JSON serializable.
410
-
411
- Parameters
412
- ----------
413
- data : Any
414
- The data to check for JSON serializability.
415
-
416
- Returns
417
- -------
418
- bool
419
- True if the data is JSON serializable, False otherwise.
420
-
421
- Notes
422
- -----
423
- This method attempts to serialize the provided data into a JSON string
424
- using a custom serializer. If the serialization succeeds, the data is
425
- considered JSON serializable.
426
- """
427
- try:
428
-
429
- # Attempt to serialize the data to JSON format
430
- json.dumps(
431
- data,
432
- default=self.__jsonSerializer
433
- )
434
-
435
- # If serialization succeeds, return True
436
- return True
437
-
438
- except (TypeError, OverflowError):
439
-
440
- # If serialization fails due to unsupported types or overflow,
441
- # return False indicating the data is not JSON serializable
442
- return False
443
-
444
- def __render(self, value: Any, indent: int = 0, key: Any = None, depth: int = 0) -> str:
445
- """
446
- Recursively renders a string representation of a given value.
447
-
448
- Parameters
449
- ----------
450
- value : Any
451
- The value to render. Can be of any type, including dict, list, tuple, set,
452
- dataclass, or objects with a `__dict__` attribute.
453
- indent : int, optional
454
- The current indentation level. Default is 0.
455
- key : Any, optional
456
- The key or index associated with the value, if applicable. Default is None.
457
- depth : int, optional
458
- The current recursion depth. Default is 0.
459
-
460
- Returns
461
- -------
462
- str
463
- A string representation of the value, formatted with indentation and type information.
464
-
465
- Notes
466
- -----
467
- - Limits recursion depth to 10 to prevent infinite loops.
468
- - Detects and handles recursive references to avoid infinite recursion.
469
- - Supports rendering of common Python data structures, dataclasses, and objects with attributes.
470
- - Formats datetime objects and callable objects with additional details.
471
- """
472
-
473
- # Check for maximum recursion depth to prevent infinite loops
474
- if depth > 10:
475
- return "... (max depth)"
476
-
477
- # Check for recursion guard to prevent infinite recursion
478
- obj_id = id(value)
479
-
480
- # If the object ID is already in the recursion guard, return a placeholder
481
- if obj_id in self._recursion_guard:
482
- return "... (recursive)"
483
-
484
- # Add the object ID to the recursion guard to track it
485
- self._recursion_guard.add(obj_id)
486
-
487
- # Prepare the prefix for the rendered output
488
- space = ' ' * indent
489
- prefix = f"{space}"
490
-
491
- # If a key is provided, format it and add it to the prefix
492
- if key is not None:
493
- prefix += f"{self.__formatKey(key)} => "
494
-
495
- # If the value is None, return a formatted string indicating None
496
- if value is None:
497
- result = f"{prefix}None"
498
-
499
- # Handle different types of values and format them accordingly
500
- elif isinstance(value, dict):
501
- result = f"{prefix}dict({len(value)})"
502
- for k, v in value.items():
503
- result += "\n" + self.__render(v, indent + self.indent_size, k, depth + 1)
504
-
505
- # If the value is a list, tuple, or set, format it with its type and length
506
- elif isinstance(value, (list, tuple, set)):
507
- type_name = type(value).__name__
508
- result = f"{prefix}{type_name}({len(value)})"
509
- for i, item in enumerate(value):
510
- result += "\n" + self.__render(
511
- item,
512
- indent + self.indent_size,
513
- i if isinstance(value, (list, tuple)) else None,
514
- depth + 1
515
- )
516
-
517
- # If the value is a string, format it with its type and length
518
- elif is_dataclass(value):
519
- result = f"{prefix}{value.__class__.__name__}"
520
- for k, v in vars(value).items():
521
- result += "\n" + self.__render(v, indent + self.indent_size, k, depth + 1)
522
-
523
- # If the value is an object with a __dict__ attribute, format it with its class name
524
- elif hasattr(value, "__dict__"):
525
- result = f"{prefix}{value.__class__.__name__}"
526
- for k, v in vars(value).items():
527
- result += "\n" + self.__render(v, indent + self.indent_size, k, depth + 1)
528
-
529
- # If the value is a datetime object, format it with its ISO 8601 string representation
530
- elif isinstance(value, datetime):
531
- result = f"{prefix}datetime({value.isoformat()})"
532
-
533
- # If the value is a callable (function or method), format it with its name
534
- elif callable(value):
535
- result = f"{prefix}callable({value.__name__ if hasattr(value, '__name__') else repr(value)})"
536
-
537
- # If the value is a simple type (int, float, str, etc.), format it with its type and value
538
- else:
539
- result = f"{prefix}{type(value).__name__}({repr(value)})"
540
-
541
- # Remove the object ID from the recursion guard after processing
542
- self._recursion_guard.discard(obj_id)
543
-
544
- # Return the formatted result
545
- return result
546
-
547
- @staticmethod
548
- def __jsonSerializer(obj):
549
- """
550
- Serialize an object into a JSON-compatible format.
551
-
552
- Parameters
553
- ----------
554
- obj : object
555
- The object to serialize. Supported types include:
556
-
557
- Returns
558
- -------
559
- object
560
-
561
- Raises
562
- ------
563
- TypeError
564
- If the object type is not supported for JSON serialization.
565
- """
566
-
567
- # Check if the object is a datetime instance and return its ISO format
568
- if isinstance(obj, datetime):
569
- return obj.isoformat()
570
-
571
- # Check if the object is a dataclass and return its dictionary representation
572
- elif hasattr(obj, '__dict__'):
573
- return vars(obj)
574
-
575
- # Check if the object is a dataclass instance and return its dictionary representation
576
- elif isinstance(obj, (set, tuple)):
577
- return list(obj)
578
-
579
- # If the object is a list, convert it to a list of JSON-serializable items
580
- raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
581
-
582
- @staticmethod
583
- def __formatKey(key: Any) -> str:
584
- """
585
- Formats a given key into a string representation.
586
-
587
- Parameters
588
- ----------
589
- key : Any
590
- The key to be formatted. It can be of any type.
591
- Returns
592
- -------
593
- str
594
- A string representation of the key. If the key is a string, it is
595
- """
596
-
597
- # If the key is a string, return it wrapped in quotes; otherwise, convert it to a string
598
- if isinstance(key, str):
599
- return f'"{key}"'
600
-
601
- # If the key is not a string, convert it to a string representation
602
- return str(key)
603
-
604
- @staticmethod
605
- def __getLineInfo(frame: inspect.FrameInfo) -> str:
606
- """
607
- Extracts and formats line information from a given frame.
608
-
609
- Parameters
610
- ----------
611
- frame : inspect.FrameInfo
612
- The frame object containing code context.
613
-
614
- Returns
615
- -------
616
- str
617
- A string in the format "filename:line_no", where `filename` is the
618
- name of the file (excluding the path) and `line_no` is the line number
619
- in the file where the frame is located.
620
- """
621
-
622
- # Extract the filename and line number from the frame
623
- filename = frame.f_code.co_filename.split('/')[-1]
624
- line_no = frame.f_lineno
625
-
626
- # Return the formatted string with filename and line number
627
- return f"{filename}:{line_no}"
File without changes