emtest 0.0.4__tar.gz → 0.0.5__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.

Potentially problematic release.


This version of emtest might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emtest
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: Testing utilities which I find useful.
5
5
  Author-email: Emendir <dev@emendir.tech>
6
6
  License-Expression: MIT-0 OR CC0-1.0
@@ -41,17 +41,21 @@ pip install emtest
41
41
 
42
42
  ## Usage
43
43
 
44
- See the `examples/` directory for complete working examples showing:
44
+ See the [Usage docs](docs/Usage/PytestUtils.md) for explanations and a complete working example showing:
45
45
  - Basic test setup with `conftest.py`
46
+ - Showing and hiding logs
46
47
  - Dual execution pattern implementation
47
48
  - Source loading validation
48
49
  - Thread cleanup testing
49
-
50
+ - Options like minising output, python-debugger breakpoints and more
50
51
 
51
52
  ## Documentation
52
53
 
53
54
  - [Full Documentation](docs/README.md):
54
55
  - [API-Reference](docs/API-Reference/README.html)
56
+ - Usage:
57
+ - [PytestUtils](docs/Usage/PytestUtils.md)
58
+ - [LogRecording](docs/Usage/LogRecording.md)
55
59
 
56
60
  ## Roadmap
57
61
 
@@ -63,18 +67,17 @@ See the `examples/` directory for complete working examples showing:
63
67
  - GitHub Issues: if you find bugs, other issues, or would like to submit feature requests
64
68
  - GitHub Merge Requests: if you think you know what you're doing, you're very welcome!
65
69
 
66
- ### Donations
70
+ ### Donate
67
71
 
68
72
  To support me in my work on this and other projects, you can make donations with the following currencies:
69
73
 
70
74
  - **Bitcoin:** `BC1Q45QEE6YTNGRC5TSZ42ZL3MWV8798ZEF70H2DG0`
71
75
  - **Ethereum:** `0xA32C3bBC2106C986317f202B3aa8eBc3063323D4`
72
- - [**Fiat** (via Credit or Debit Card, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
76
+ - [Credit Card, Debit Card, Bank Transfer, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
73
77
 
74
78
  Donations help me:
75
79
  - dedicate more time to developing and maintaining open-source projects
76
- - cover costs for IT infrastructure
77
- - finance projects requiring additional hardware & compute
80
+ - cover costs for IT resources
78
81
 
79
82
  ## About the Developer
80
83
 
@@ -26,17 +26,21 @@ pip install emtest
26
26
 
27
27
  ## Usage
28
28
 
29
- See the `examples/` directory for complete working examples showing:
29
+ See the [Usage docs](docs/Usage/PytestUtils.md) for explanations and a complete working example showing:
30
30
  - Basic test setup with `conftest.py`
31
+ - Showing and hiding logs
31
32
  - Dual execution pattern implementation
32
33
  - Source loading validation
33
34
  - Thread cleanup testing
34
-
35
+ - Options like minising output, python-debugger breakpoints and more
35
36
 
36
37
  ## Documentation
37
38
 
38
39
  - [Full Documentation](docs/README.md):
39
40
  - [API-Reference](docs/API-Reference/README.html)
41
+ - Usage:
42
+ - [PytestUtils](docs/Usage/PytestUtils.md)
43
+ - [LogRecording](docs/Usage/LogRecording.md)
40
44
 
41
45
  ## Roadmap
42
46
 
@@ -48,18 +52,17 @@ See the `examples/` directory for complete working examples showing:
48
52
  - GitHub Issues: if you find bugs, other issues, or would like to submit feature requests
49
53
  - GitHub Merge Requests: if you think you know what you're doing, you're very welcome!
50
54
 
51
- ### Donations
55
+ ### Donate
52
56
 
53
57
  To support me in my work on this and other projects, you can make donations with the following currencies:
54
58
 
55
59
  - **Bitcoin:** `BC1Q45QEE6YTNGRC5TSZ42ZL3MWV8798ZEF70H2DG0`
56
60
  - **Ethereum:** `0xA32C3bBC2106C986317f202B3aa8eBc3063323D4`
57
- - [**Fiat** (via Credit or Debit Card, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
61
+ - [Credit Card, Debit Card, Bank Transfer, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
58
62
 
59
63
  Donations help me:
60
64
  - dedicate more time to developing and maintaining open-source projects
61
- - cover costs for IT infrastructure
62
- - finance projects requiring additional hardware & compute
65
+ - cover costs for IT resources
63
66
 
64
67
  ## About the Developer
65
68
 
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "emtest"
8
- version = "0.0.4"
8
+ version = "0.0.5"
9
9
  description = "Testing utilities which I find useful."
10
10
  readme = {file = "README.md", content-type = "text/markdown"}
11
11
  authors = [{ name = "Emendir" , email = "dev@emendir.tech"}]
@@ -0,0 +1,154 @@
1
+ """In-memory log recording utilities for Python's logging framework.
2
+
3
+ This module provides a `RecordingHandler` and helper methods to
4
+ dynamically attach and detach in-memory log recorders to standard
5
+ `logging.Logger` instances. It allows recording log output programmatically
6
+ for testing, debugging, or analysis without relying on files or streams.
7
+
8
+ Example:
9
+ >>> import logging
10
+ >>> import emtest.log_recording
11
+ >>> logger = logging.getLogger("demo")
12
+ >>> logger.setLevel(logging.INFO)
13
+ >>> logger.start_recording() # Start recording logs
14
+ >>> logger.info("Hello world")
15
+ >>> logs = logger.get_recording()
16
+ >>> print(logs)
17
+ ['2025-10-18 10:00:00,000 [INFO] Hello world']
18
+ >>> logger.stop_recording() # Stop and remove recorder
19
+ """
20
+
21
+ import logging
22
+
23
+
24
+ class RecordingHandler(logging.Handler):
25
+ """A logging handler that buffers formatted log records in memory.
26
+
27
+ This handler captures each emitted log record, formats it,
28
+ and stores the resulting string in an internal list.
29
+ It is useful for unit tests or scenarios where log output
30
+ needs to be inspected programmatically.
31
+
32
+ Attributes:
33
+ formatter (logging.Formatter): The formatter used to format log messages.
34
+ _records (list[str]): Internal buffer of recorded log messages.
35
+ """
36
+
37
+ def __init__(self, formatter: logging.Formatter):
38
+ """Initialize the handler with a specific formatter.
39
+
40
+ Args:
41
+ formatter (logging.Formatter): Formatter to apply to each log record.
42
+ """
43
+ super().__init__()
44
+ self.formatter = formatter
45
+ self._records: list[str] = []
46
+
47
+ def emit(self, record: logging.LogRecord):
48
+ """Store a formatted log record in the internal buffer.
49
+
50
+ Args:
51
+ record (logging.LogRecord): The log record to handle.
52
+ """
53
+ self._records.append(self.format(record))
54
+
55
+ def get_records(self) -> list[str]:
56
+ """Retrieve the recorded log messages.
57
+
58
+ Returns:
59
+ list[str]: A copy of the list of formatted log messages.
60
+ """
61
+ return list(self._records)
62
+
63
+ def clear(self):
64
+ """Clear all recorded log messages from the buffer."""
65
+ self._records.clear()
66
+
67
+
68
+ def start_recording(self: logging.Logger, name: str | None = None):
69
+ """Start recording logs to a named recorder.
70
+
71
+ This function dynamically attaches a `RecordingHandler` to the logger.
72
+ If a recorder with the given name already exists, it does nothing.
73
+
74
+ The recorder uses the formatter from the first existing handler
75
+ if available, or falls back to a default formatter.
76
+
77
+ Args:
78
+ self (logging.Logger): The logger instance.
79
+ name (str | None): Optional name of the recorder. Defaults to `"__default__"`.
80
+
81
+ Example:
82
+ >>> logger.start_recording("test")
83
+ >>> logger.info("Message")
84
+ """
85
+ if name is None:
86
+ name = "__default__"
87
+
88
+ # Ensure the logger has a dict of recording handlers attached
89
+ if not hasattr(self, "_recording_handlers"):
90
+ self._recording_handlers = {}
91
+ if name in self._recording_handlers:
92
+ return # already recording under this name
93
+
94
+ # Use same formatter as first handler, or fallback
95
+ if self.handlers:
96
+ formatter = self.handlers[0].formatter
97
+ else:
98
+ formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
99
+
100
+ handler = RecordingHandler(formatter)
101
+ self.addHandler(handler)
102
+ self._recording_handlers.update({name: handler})
103
+
104
+
105
+ def stop_recording(self: logging.Logger, name: str | None = None):
106
+ """Stop and remove a named recorder from the logger.
107
+
108
+ If no recorder exists for the given name, the function does nothing.
109
+
110
+ Args:
111
+ self (logging.Logger): The logger instance.
112
+ name (str | None): Optional name of the recorder to stop.
113
+ Defaults to `"__default__"`.
114
+
115
+ Example:
116
+ >>> logger.stop_recording("test")
117
+ """
118
+ if name is None:
119
+ name = "__default__"
120
+
121
+ handler = self._recording_handlers.get(name, None)
122
+ if handler:
123
+ self.removeHandler(handler)
124
+
125
+
126
+ def get_recording(self: logging.Logger, name: str | None = None) -> list[str]:
127
+ """Retrieve the recorded log messages for a named recorder.
128
+
129
+ Args:
130
+ self (logging.Logger): The logger instance.
131
+ name (str | None): Optional name of the recorder. Defaults to `"__default__"`.
132
+
133
+ Returns:
134
+ list[str]: A list of formatted log messages recorded so far.
135
+ Returns an empty list if no handler is found for the name.
136
+
137
+ Raises:
138
+ KeyError: If the recorder with the specified name does not exist.
139
+
140
+ Example:
141
+ >>> messages = logger.get_recording("test")
142
+ >>> print(messages)
143
+ """
144
+ if name is None:
145
+ name = "__default__"
146
+
147
+ handler = self._recording_handlers[name]
148
+ return handler.get_records() if handler else []
149
+
150
+
151
+ # Monkey-patch Logger with recording methods
152
+ logging.Logger.start_recording = start_recording
153
+ logging.Logger.stop_recording = stop_recording
154
+ logging.Logger.get_recording = get_recording
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emtest
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: Testing utilities which I find useful.
5
5
  Author-email: Emendir <dev@emendir.tech>
6
6
  License-Expression: MIT-0 OR CC0-1.0
@@ -41,17 +41,21 @@ pip install emtest
41
41
 
42
42
  ## Usage
43
43
 
44
- See the `examples/` directory for complete working examples showing:
44
+ See the [Usage docs](docs/Usage/PytestUtils.md) for explanations and a complete working example showing:
45
45
  - Basic test setup with `conftest.py`
46
+ - Showing and hiding logs
46
47
  - Dual execution pattern implementation
47
48
  - Source loading validation
48
49
  - Thread cleanup testing
49
-
50
+ - Options like minising output, python-debugger breakpoints and more
50
51
 
51
52
  ## Documentation
52
53
 
53
54
  - [Full Documentation](docs/README.md):
54
55
  - [API-Reference](docs/API-Reference/README.html)
56
+ - Usage:
57
+ - [PytestUtils](docs/Usage/PytestUtils.md)
58
+ - [LogRecording](docs/Usage/LogRecording.md)
55
59
 
56
60
  ## Roadmap
57
61
 
@@ -63,18 +67,17 @@ See the `examples/` directory for complete working examples showing:
63
67
  - GitHub Issues: if you find bugs, other issues, or would like to submit feature requests
64
68
  - GitHub Merge Requests: if you think you know what you're doing, you're very welcome!
65
69
 
66
- ### Donations
70
+ ### Donate
67
71
 
68
72
  To support me in my work on this and other projects, you can make donations with the following currencies:
69
73
 
70
74
  - **Bitcoin:** `BC1Q45QEE6YTNGRC5TSZ42ZL3MWV8798ZEF70H2DG0`
71
75
  - **Ethereum:** `0xA32C3bBC2106C986317f202B3aa8eBc3063323D4`
72
- - [**Fiat** (via Credit or Debit Card, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
76
+ - [Credit Card, Debit Card, Bank Transfer, Apple Pay, Google Pay, Revolut Pay)](https://checkout.revolut.com/pay/4e4d24de-26cf-4e7d-9e84-ede89ec67f32)
73
77
 
74
78
  Donations help me:
75
79
  - dedicate more time to developing and maintaining open-source projects
76
- - cover costs for IT infrastructure
77
- - finance projects requiring additional hardware & compute
80
+ - cover costs for IT resources
78
81
 
79
82
  ## About the Developer
80
83
 
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  requirements.txt
6
6
  src/_auto_run_with_pytest/__init__.py
7
7
  src/emtest/__init__.py
8
+ src/emtest/log_recording.py
8
9
  src/emtest/pytest_utils.py
9
10
  src/emtest/testing_utils.py
10
11
  src/emtest.egg-info/PKG-INFO
File without changes
File without changes
File without changes
File without changes
File without changes