logxpy 0.1.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.
- logxpy/__init__.py +126 -0
- logxpy/_action.py +958 -0
- logxpy/_async.py +186 -0
- logxpy/_base.py +80 -0
- logxpy/_compat.py +71 -0
- logxpy/_config.py +45 -0
- logxpy/_dest.py +88 -0
- logxpy/_errors.py +58 -0
- logxpy/_fmt.py +68 -0
- logxpy/_generators.py +136 -0
- logxpy/_mask.py +23 -0
- logxpy/_message.py +195 -0
- logxpy/_output.py +517 -0
- logxpy/_pool.py +93 -0
- logxpy/_traceback.py +126 -0
- logxpy/_types.py +71 -0
- logxpy/_util.py +56 -0
- logxpy/_validation.py +486 -0
- logxpy/_version.py +21 -0
- logxpy/cli.py +61 -0
- logxpy/dask.py +172 -0
- logxpy/decorators.py +268 -0
- logxpy/filter.py +124 -0
- logxpy/journald.py +88 -0
- logxpy/json.py +149 -0
- logxpy/loggerx.py +253 -0
- logxpy/logwriter.py +84 -0
- logxpy/parse.py +191 -0
- logxpy/prettyprint.py +173 -0
- logxpy/serializers.py +36 -0
- logxpy/stdlib.py +23 -0
- logxpy/tai64n.py +45 -0
- logxpy/testing.py +472 -0
- logxpy/tests/__init__.py +9 -0
- logxpy/tests/common.py +36 -0
- logxpy/tests/strategies.py +231 -0
- logxpy/tests/test_action.py +1751 -0
- logxpy/tests/test_api.py +86 -0
- logxpy/tests/test_async.py +67 -0
- logxpy/tests/test_compat.py +13 -0
- logxpy/tests/test_config.py +21 -0
- logxpy/tests/test_coroutines.py +105 -0
- logxpy/tests/test_dask.py +211 -0
- logxpy/tests/test_decorators.py +54 -0
- logxpy/tests/test_filter.py +122 -0
- logxpy/tests/test_fmt.py +42 -0
- logxpy/tests/test_generators.py +292 -0
- logxpy/tests/test_journald.py +246 -0
- logxpy/tests/test_json.py +208 -0
- logxpy/tests/test_loggerx.py +44 -0
- logxpy/tests/test_logwriter.py +262 -0
- logxpy/tests/test_message.py +334 -0
- logxpy/tests/test_output.py +921 -0
- logxpy/tests/test_parse.py +309 -0
- logxpy/tests/test_pool.py +55 -0
- logxpy/tests/test_prettyprint.py +303 -0
- logxpy/tests/test_pyinstaller.py +35 -0
- logxpy/tests/test_serializers.py +36 -0
- logxpy/tests/test_stdlib.py +73 -0
- logxpy/tests/test_tai64n.py +66 -0
- logxpy/tests/test_testing.py +1051 -0
- logxpy/tests/test_traceback.py +251 -0
- logxpy/tests/test_twisted.py +814 -0
- logxpy/tests/test_util.py +45 -0
- logxpy/tests/test_validation.py +989 -0
- logxpy/twisted.py +265 -0
- logxpy-0.1.0.dist-info/METADATA +100 -0
- logxpy-0.1.0.dist-info/RECORD +72 -0
- logxpy-0.1.0.dist-info/WHEEL +5 -0
- logxpy-0.1.0.dist-info/entry_points.txt +2 -0
- logxpy-0.1.0.dist-info/licenses/LICENSE +201 -0
- logxpy-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for L{eliot._message}.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from unittest import TestCase
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from pyrsistent import pmap
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from twisted.python.failure import Failure
|
|
13
|
+
except ImportError:
|
|
14
|
+
Failure = None
|
|
15
|
+
|
|
16
|
+
from .._message import WrittenMessage, Message, log_message
|
|
17
|
+
from .._output import MemoryLogger
|
|
18
|
+
from .._action import Action, start_action, TaskLevel
|
|
19
|
+
from .. import add_destinations, remove_destination
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DeprecatedMessageTests(TestCase):
|
|
23
|
+
"""
|
|
24
|
+
Test for L{Message}.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def test_new(self):
|
|
28
|
+
"""
|
|
29
|
+
L{Message.new} returns a new L{Message} that is initialized with the
|
|
30
|
+
given keyword arguments as its contents, and a default message type.
|
|
31
|
+
"""
|
|
32
|
+
msg = Message.new(key="value", another=2)
|
|
33
|
+
self.assertEqual(msg.contents(), {"key": "value", "another": 2})
|
|
34
|
+
|
|
35
|
+
def test_contentsCopies(self):
|
|
36
|
+
"""
|
|
37
|
+
L{Message.contents} returns a copy of the L{Message} contents.
|
|
38
|
+
"""
|
|
39
|
+
msg = Message.new(key="value")
|
|
40
|
+
del msg.contents()["key"]
|
|
41
|
+
self.assertEqual(msg.contents(), {"key": "value"})
|
|
42
|
+
|
|
43
|
+
def test_bindOverwrites(self):
|
|
44
|
+
"""
|
|
45
|
+
L{Message.bind} returns a new L{Message} whose contents include the
|
|
46
|
+
additional given fields.
|
|
47
|
+
"""
|
|
48
|
+
msg = Message.new(key="value", another=2)
|
|
49
|
+
another = msg.bind(another=3, more=4)
|
|
50
|
+
self.assertIsInstance(another, Message)
|
|
51
|
+
self.assertEqual(another.contents(), {"key": "value", "another": 3, "more": 4})
|
|
52
|
+
|
|
53
|
+
def test_bindPreservesOriginal(self):
|
|
54
|
+
"""
|
|
55
|
+
L{Message.bind} does not mutate the instance it is called on.
|
|
56
|
+
"""
|
|
57
|
+
msg = Message.new(key=4)
|
|
58
|
+
msg.bind(key=6)
|
|
59
|
+
self.assertEqual(msg.contents(), {"key": 4})
|
|
60
|
+
|
|
61
|
+
def test_writeCallsLoggerWrite(self):
|
|
62
|
+
"""
|
|
63
|
+
L{Message.write} calls the given logger's C{write} method with a
|
|
64
|
+
dictionary that is superset of the L{Message} contents.
|
|
65
|
+
"""
|
|
66
|
+
logger = MemoryLogger()
|
|
67
|
+
msg = Message.new(key=4)
|
|
68
|
+
msg.write(logger)
|
|
69
|
+
self.assertEqual(len(logger.messages), 1)
|
|
70
|
+
self.assertEqual(logger.messages[0]["key"], 4)
|
|
71
|
+
|
|
72
|
+
def test_writeDefaultLogger(self):
|
|
73
|
+
"""
|
|
74
|
+
L{Message.write} writes to the default logger if none is given.
|
|
75
|
+
"""
|
|
76
|
+
messages = []
|
|
77
|
+
add_destinations(messages.append)
|
|
78
|
+
self.addCleanup(remove_destination, messages.append)
|
|
79
|
+
Message.new(some_key=1234).write()
|
|
80
|
+
self.assertEqual(messages[0]["some_key"], 1234)
|
|
81
|
+
|
|
82
|
+
def test_writeCreatesNewDictionary(self):
|
|
83
|
+
"""
|
|
84
|
+
L{Message.write} creates a new dictionary on each call.
|
|
85
|
+
|
|
86
|
+
This is important because we mutate the dictionary in
|
|
87
|
+
``Logger.write``, so we want to make sure the ``Message`` is unchanged
|
|
88
|
+
in that case. In general we want ``Message`` objects to be effectively
|
|
89
|
+
immutable.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
class Logger(list):
|
|
93
|
+
def write(self, d, serializer):
|
|
94
|
+
self.append(d)
|
|
95
|
+
|
|
96
|
+
logger = Logger()
|
|
97
|
+
msg = Message.new(key=4)
|
|
98
|
+
msg.write(logger)
|
|
99
|
+
logger[0]["key"] = 5
|
|
100
|
+
msg.write(logger)
|
|
101
|
+
self.assertEqual(logger[1]["key"], 4)
|
|
102
|
+
|
|
103
|
+
def test_logCallsDefaultLoggerWrite(self):
|
|
104
|
+
"""
|
|
105
|
+
L{Message.log} calls the default logger's C{write} method with a
|
|
106
|
+
dictionary that is superset of the L{Message} contents.
|
|
107
|
+
"""
|
|
108
|
+
messages = []
|
|
109
|
+
add_destinations(messages.append)
|
|
110
|
+
self.addCleanup(remove_destination, messages.append)
|
|
111
|
+
Message.log(some_key=1234)
|
|
112
|
+
self.assertEqual(messages[0]["some_key"], 1234)
|
|
113
|
+
|
|
114
|
+
def test_defaultTime(self):
|
|
115
|
+
"""
|
|
116
|
+
L{Message._time} returns L{time.time} by default.
|
|
117
|
+
"""
|
|
118
|
+
msg = Message({})
|
|
119
|
+
self.assertIs(msg._time, time.time)
|
|
120
|
+
|
|
121
|
+
def test_writeAddsTimestamp(self):
|
|
122
|
+
"""
|
|
123
|
+
L{Message.write} adds a C{"timestamp"} field to the dictionary written
|
|
124
|
+
to the logger, with the current time in seconds since the epoch.
|
|
125
|
+
"""
|
|
126
|
+
logger = MemoryLogger()
|
|
127
|
+
msg = Message.new(key=4)
|
|
128
|
+
msg.write(logger)
|
|
129
|
+
self.assertTrue(time.time() - logger.messages[0]["timestamp"] < 0.1)
|
|
130
|
+
|
|
131
|
+
def test_write_preserves_message_type(self):
|
|
132
|
+
"""
|
|
133
|
+
L{Message.write} doesn't add a C{message_type} if one is already set.
|
|
134
|
+
"""
|
|
135
|
+
logger = MemoryLogger()
|
|
136
|
+
msg = Message.new(key=4, message_type="isetit")
|
|
137
|
+
msg.write(logger)
|
|
138
|
+
self.assertEqual(logger.messages[0]["message_type"], "isetit")
|
|
139
|
+
self.assertNotIn("action_type", logger.messages[0])
|
|
140
|
+
|
|
141
|
+
def test_explicitAction(self):
|
|
142
|
+
"""
|
|
143
|
+
L{Message.write} adds the identification fields from the given
|
|
144
|
+
L{Action} to the dictionary written to the logger, as well as a
|
|
145
|
+
message_type if none is set.
|
|
146
|
+
"""
|
|
147
|
+
logger = MemoryLogger()
|
|
148
|
+
action = Action(logger, "unique", TaskLevel(level=[]), "sys:thename")
|
|
149
|
+
msg = Message.new(key=2)
|
|
150
|
+
msg.write(logger, action)
|
|
151
|
+
written = logger.messages[0]
|
|
152
|
+
del written["timestamp"]
|
|
153
|
+
self.assertEqual(
|
|
154
|
+
written,
|
|
155
|
+
{"task_uuid": "unique", "task_level": [1], "key": 2, "message_type": ""},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def test_implicitAction(self):
|
|
159
|
+
"""
|
|
160
|
+
If no L{Action} is specified, L{Message.write} adds the identification
|
|
161
|
+
fields from the current execution context's L{Action} to the
|
|
162
|
+
dictionary written to the logger.
|
|
163
|
+
"""
|
|
164
|
+
logger = MemoryLogger()
|
|
165
|
+
action = Action(logger, "unique", TaskLevel(level=[]), "sys:thename")
|
|
166
|
+
msg = Message.new(key=2)
|
|
167
|
+
with action:
|
|
168
|
+
msg.write(logger)
|
|
169
|
+
written = logger.messages[0]
|
|
170
|
+
del written["timestamp"]
|
|
171
|
+
self.assertEqual(
|
|
172
|
+
written,
|
|
173
|
+
{"task_uuid": "unique", "task_level": [1], "key": 2, "message_type": ""},
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def test_missingAction(self):
|
|
177
|
+
"""
|
|
178
|
+
If no L{Action} is specified, and the current execution context has no
|
|
179
|
+
L{Action}, a new task_uuid is generated.
|
|
180
|
+
|
|
181
|
+
This ensures all messages have a unique identity, as specified by
|
|
182
|
+
task_uuid/task_level.
|
|
183
|
+
"""
|
|
184
|
+
logger = MemoryLogger()
|
|
185
|
+
Message.new(key=2).write(logger)
|
|
186
|
+
Message.new(key=3).write(logger)
|
|
187
|
+
|
|
188
|
+
message1, message2 = logger.messages
|
|
189
|
+
self.assertEqual(
|
|
190
|
+
(
|
|
191
|
+
UUID(message1["task_uuid"]) != UUID(message2["task_uuid"]),
|
|
192
|
+
message1["task_level"],
|
|
193
|
+
message2["task_level"],
|
|
194
|
+
),
|
|
195
|
+
(True, [1], [1]),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def test_actionCounter(self):
|
|
199
|
+
"""
|
|
200
|
+
Each message written within the context of an L{Action} gets its
|
|
201
|
+
C{task_level} field incremented.
|
|
202
|
+
"""
|
|
203
|
+
logger = MemoryLogger()
|
|
204
|
+
msg = Message.new(key=2)
|
|
205
|
+
with start_action(logger, "sys:thename"):
|
|
206
|
+
for i in range(4):
|
|
207
|
+
msg.write(logger)
|
|
208
|
+
# We expect 6 messages: start action, 4 standalone messages, finish
|
|
209
|
+
# action:
|
|
210
|
+
self.assertEqual(
|
|
211
|
+
[m["task_level"] for m in logger.messages], [[1], [2], [3], [4], [5], [6]]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def test_writePassesSerializer(self):
|
|
215
|
+
"""
|
|
216
|
+
If a L{Message} is created with a serializer, it is passed as a second
|
|
217
|
+
argument to the logger when C{write} is called.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
class ListLogger(list):
|
|
221
|
+
def write(self, dictionary, serializer):
|
|
222
|
+
self.append(serializer)
|
|
223
|
+
|
|
224
|
+
logger = ListLogger()
|
|
225
|
+
serializer = object()
|
|
226
|
+
msg = Message({}, serializer)
|
|
227
|
+
msg.write(logger)
|
|
228
|
+
self.assertIs(logger[0], serializer)
|
|
229
|
+
|
|
230
|
+
def test_serializerPassedInBind(self):
|
|
231
|
+
"""
|
|
232
|
+
The L{Message} returned by L{Message.bind} includes the serializer
|
|
233
|
+
passed to the parent.
|
|
234
|
+
"""
|
|
235
|
+
serializer = object()
|
|
236
|
+
msg = Message({}, serializer)
|
|
237
|
+
msg2 = msg.bind(x=1)
|
|
238
|
+
self.assertIs(msg2._serializer, serializer)
|
|
239
|
+
|
|
240
|
+
def test_newWithSerializer(self):
|
|
241
|
+
"""
|
|
242
|
+
L{Message.new} can accept a serializer.
|
|
243
|
+
"""
|
|
244
|
+
serializer = object()
|
|
245
|
+
msg = Message.new(serializer, x=1)
|
|
246
|
+
self.assertIs(msg._serializer, serializer)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class WrittenMessageTests(TestCase):
|
|
250
|
+
"""
|
|
251
|
+
Tests for L{WrittenMessage}.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
def test_as_dict(self):
|
|
255
|
+
"""
|
|
256
|
+
L{WrittenMessage.as_dict} returns the dictionary that will be serialized
|
|
257
|
+
to the log.
|
|
258
|
+
"""
|
|
259
|
+
log_entry = pmap(
|
|
260
|
+
{"timestamp": 1, "task_uuid": "unique", "task_level": [1], "foo": "bar"}
|
|
261
|
+
)
|
|
262
|
+
self.assertEqual(WrittenMessage.from_dict(log_entry).as_dict(), log_entry)
|
|
263
|
+
|
|
264
|
+
def test_from_dict(self):
|
|
265
|
+
"""
|
|
266
|
+
L{WrittenMessage.from_dict} converts a dictionary that has been
|
|
267
|
+
deserialized from a log into a L{WrittenMessage} object.
|
|
268
|
+
"""
|
|
269
|
+
log_entry = pmap(
|
|
270
|
+
{"timestamp": 1, "task_uuid": "unique", "task_level": [1], "foo": "bar"}
|
|
271
|
+
)
|
|
272
|
+
parsed = WrittenMessage.from_dict(log_entry)
|
|
273
|
+
self.assertEqual(parsed.timestamp, 1)
|
|
274
|
+
self.assertEqual(parsed.task_uuid, "unique")
|
|
275
|
+
self.assertEqual(parsed.task_level, TaskLevel(level=[1]))
|
|
276
|
+
self.assertEqual(parsed.contents, pmap({"foo": "bar"}))
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class LogMessageTests(TestCase):
|
|
280
|
+
"""Tests for L{log_message}."""
|
|
281
|
+
|
|
282
|
+
def test_writes_message(self):
|
|
283
|
+
"""
|
|
284
|
+
L{log_message} writes to the default logger.
|
|
285
|
+
"""
|
|
286
|
+
messages = []
|
|
287
|
+
add_destinations(messages.append)
|
|
288
|
+
self.addCleanup(remove_destination, messages.append)
|
|
289
|
+
log_message(message_type="hello", some_key=1234)
|
|
290
|
+
self.assertEqual(messages[0]["some_key"], 1234)
|
|
291
|
+
self.assertEqual(messages[0]["message_type"], "hello")
|
|
292
|
+
self.assertTrue(time.time() - messages[0]["timestamp"] < 0.1)
|
|
293
|
+
|
|
294
|
+
def test_implicitAction(self):
|
|
295
|
+
"""
|
|
296
|
+
If no L{Action} is specified, L{log_message} adds the identification
|
|
297
|
+
fields from the current execution context's L{Action} to the
|
|
298
|
+
dictionary written to the logger.
|
|
299
|
+
"""
|
|
300
|
+
logger = MemoryLogger()
|
|
301
|
+
action = Action(logger, "unique", TaskLevel(level=[]), "sys:thename")
|
|
302
|
+
with action:
|
|
303
|
+
log_message(key=2, message_type="a")
|
|
304
|
+
written = logger.messages[0]
|
|
305
|
+
del written["timestamp"]
|
|
306
|
+
self.assertEqual(
|
|
307
|
+
written,
|
|
308
|
+
{"task_uuid": "unique", "task_level": [1], "key": 2, "message_type": "a"},
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def test_missingAction(self):
|
|
312
|
+
"""
|
|
313
|
+
If no L{Action} is specified, and the current execution context has no
|
|
314
|
+
L{Action}, a new task_uuid is generated.
|
|
315
|
+
|
|
316
|
+
This ensures all messages have a unique identity, as specified by
|
|
317
|
+
task_uuid/task_level.
|
|
318
|
+
"""
|
|
319
|
+
messages = []
|
|
320
|
+
add_destinations(messages.append)
|
|
321
|
+
self.addCleanup(remove_destination, messages.append)
|
|
322
|
+
|
|
323
|
+
log_message(key=2, message_type="")
|
|
324
|
+
log_message(key=3, message_type="")
|
|
325
|
+
|
|
326
|
+
message1, message2 = messages
|
|
327
|
+
self.assertEqual(
|
|
328
|
+
(
|
|
329
|
+
UUID(message1["task_uuid"]) != UUID(message2["task_uuid"]),
|
|
330
|
+
message1["task_level"],
|
|
331
|
+
message2["task_level"],
|
|
332
|
+
),
|
|
333
|
+
(True, [1], [1]),
|
|
334
|
+
)
|