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.
Files changed (72) hide show
  1. logxpy/__init__.py +126 -0
  2. logxpy/_action.py +958 -0
  3. logxpy/_async.py +186 -0
  4. logxpy/_base.py +80 -0
  5. logxpy/_compat.py +71 -0
  6. logxpy/_config.py +45 -0
  7. logxpy/_dest.py +88 -0
  8. logxpy/_errors.py +58 -0
  9. logxpy/_fmt.py +68 -0
  10. logxpy/_generators.py +136 -0
  11. logxpy/_mask.py +23 -0
  12. logxpy/_message.py +195 -0
  13. logxpy/_output.py +517 -0
  14. logxpy/_pool.py +93 -0
  15. logxpy/_traceback.py +126 -0
  16. logxpy/_types.py +71 -0
  17. logxpy/_util.py +56 -0
  18. logxpy/_validation.py +486 -0
  19. logxpy/_version.py +21 -0
  20. logxpy/cli.py +61 -0
  21. logxpy/dask.py +172 -0
  22. logxpy/decorators.py +268 -0
  23. logxpy/filter.py +124 -0
  24. logxpy/journald.py +88 -0
  25. logxpy/json.py +149 -0
  26. logxpy/loggerx.py +253 -0
  27. logxpy/logwriter.py +84 -0
  28. logxpy/parse.py +191 -0
  29. logxpy/prettyprint.py +173 -0
  30. logxpy/serializers.py +36 -0
  31. logxpy/stdlib.py +23 -0
  32. logxpy/tai64n.py +45 -0
  33. logxpy/testing.py +472 -0
  34. logxpy/tests/__init__.py +9 -0
  35. logxpy/tests/common.py +36 -0
  36. logxpy/tests/strategies.py +231 -0
  37. logxpy/tests/test_action.py +1751 -0
  38. logxpy/tests/test_api.py +86 -0
  39. logxpy/tests/test_async.py +67 -0
  40. logxpy/tests/test_compat.py +13 -0
  41. logxpy/tests/test_config.py +21 -0
  42. logxpy/tests/test_coroutines.py +105 -0
  43. logxpy/tests/test_dask.py +211 -0
  44. logxpy/tests/test_decorators.py +54 -0
  45. logxpy/tests/test_filter.py +122 -0
  46. logxpy/tests/test_fmt.py +42 -0
  47. logxpy/tests/test_generators.py +292 -0
  48. logxpy/tests/test_journald.py +246 -0
  49. logxpy/tests/test_json.py +208 -0
  50. logxpy/tests/test_loggerx.py +44 -0
  51. logxpy/tests/test_logwriter.py +262 -0
  52. logxpy/tests/test_message.py +334 -0
  53. logxpy/tests/test_output.py +921 -0
  54. logxpy/tests/test_parse.py +309 -0
  55. logxpy/tests/test_pool.py +55 -0
  56. logxpy/tests/test_prettyprint.py +303 -0
  57. logxpy/tests/test_pyinstaller.py +35 -0
  58. logxpy/tests/test_serializers.py +36 -0
  59. logxpy/tests/test_stdlib.py +73 -0
  60. logxpy/tests/test_tai64n.py +66 -0
  61. logxpy/tests/test_testing.py +1051 -0
  62. logxpy/tests/test_traceback.py +251 -0
  63. logxpy/tests/test_twisted.py +814 -0
  64. logxpy/tests/test_util.py +45 -0
  65. logxpy/tests/test_validation.py +989 -0
  66. logxpy/twisted.py +265 -0
  67. logxpy-0.1.0.dist-info/METADATA +100 -0
  68. logxpy-0.1.0.dist-info/RECORD +72 -0
  69. logxpy-0.1.0.dist-info/WHEEL +5 -0
  70. logxpy-0.1.0.dist-info/entry_points.txt +2 -0
  71. logxpy-0.1.0.dist-info/licenses/LICENSE +201 -0
  72. 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
+ )