diff --git a/eliot/_output.py b/eliot/_output.py index ddba4b1..6c08e72 100644 --- a/eliot/_output.py +++ b/eliot/_output.py @@ -7,9 +7,10 @@ import sys import json as pyjson -from characteristic import attributes from six import text_type as unicode, PY3 +from pyrsistent import PClass, field + from . import _bytesjson as slow_json if PY3: fast_json = slow_json @@ -340,8 +341,7 @@ def reset(self): -@attributes(["file"]) -class FileDestination(object): +class FileDestination(PClass): """ Callable that writes JSON messages to a file. @@ -355,23 +355,27 @@ class FileDestination(object): @ivar _linebreak: C{"\n"} as either bytes or unicode. """ + file = field(mandatory=True) + _dumps = field(mandatory=True) + _linebreak = field(mandatory=True) - def __init__(self): + def __new__(cls, file): unicodeFile = False if PY3: try: - self.file.write(b"") + file.write(b"") except TypeError: unicodeFile = True if unicodeFile: # On Python 3 native json module outputs unicode: - self._dumps = pyjson.dumps - self._linebreak = u"\n" + _dumps = pyjson.dumps + _linebreak = u"\n" else: - self._dumps = fast_json.dumps - self._linebreak = b"\n" - + _dumps = fast_json.dumps + _linebreak = b"\n" + return PClass.__new__( + cls, file=file, _dumps=_dumps, _linebreak=_linebreak) def __call__(self, message): """ diff --git a/eliot/_validation.py b/eliot/_validation.py index fd7af2a..07060fe 100644 --- a/eliot/_validation.py +++ b/eliot/_validation.py @@ -7,11 +7,11 @@ from __future__ import unicode_literals -from collections import namedtuple - import six unicode = six.text_type +from pyrsistent import PClass, field as pyrsistent_field + from ._message import ( Message, REASON_FIELD, @@ -333,12 +333,13 @@ def __call__(self, **fields): -class _ActionSerializers(namedtuple("_ActionSerializers", - ["start", "success", "failure"])): +class _ActionSerializers(PClass): """ Serializers for the three action messages: start, success and failure. """ - + start = pyrsistent_field(mandatory=True) + success = pyrsistent_field(mandatory=True) + failure = pyrsistent_field(mandatory=True) class ActionType(object): @@ -404,12 +405,12 @@ def makeActionStatusField(value): EXCEPTION] self._serializers = _ActionSerializers( - _MessageSerializer(startFields), - _MessageSerializer(successFields), + start=_MessageSerializer(startFields), + success=_MessageSerializer(successFields), # Failed action messages can have extra fields from exception # extraction: - _MessageSerializer(failureFields, - allow_additional_fields=True)) + failure=_MessageSerializer(failureFields, + allow_additional_fields=True)) def __call__(self, logger=None, **fields): diff --git a/eliot/testing.py b/eliot/testing.py index 1321043..09831b7 100644 --- a/eliot/testing.py +++ b/eliot/testing.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals from unittest import SkipTest -from collections import namedtuple from functools import wraps +from pyrsistent import PClass, field + from ._action import ( ACTION_STATUS_FIELD, ACTION_TYPE_FIELD, @@ -56,8 +57,7 @@ def assertContainsFields(test, message, fields): -class LoggedAction(namedtuple( - "LoggedAction", "startMessage endMessage children")): +class LoggedAction(PClass): """ An action whose start and finish messages have been logged. @@ -70,6 +70,14 @@ class LoggedAction(namedtuple( @ivar children: A C{list} of direct child L{LoggedMessage} and L{LoggedAction} instances. """ + startMessage = field(mandatory=True) + endMessage = field(mandatory=True) + children = field(mandatory=True) + + def __new__(cls, startMessage, endMessage, children): + return PClass.__new__(cls, startMessage=startMessage, + endMessage=endMessage, children=children) + @property def start_message(self): return self.startMessage @@ -193,12 +201,17 @@ def succeeded(self): -class LoggedMessage(namedtuple("LoggedMessage", "message")): +class LoggedMessage(PClass): """ A message that has been logged. @ivar message: A C{dict}, the message contents. """ + message = field(mandatory=True) + + def __new__(cls, message): + return PClass.__new__(cls, message=message) + @classmethod def ofType(klass, messages, messageType): """ diff --git a/eliot/tests/test_action.py b/eliot/tests/test_action.py index 2223a14..4586ada 100644 --- a/eliot/tests/test_action.py +++ b/eliot/tests/test_action.py @@ -597,7 +597,9 @@ def test_startTaskSerializers(self): resulting L{Action}. """ logger = MemoryLogger() - serializers = _ActionSerializers(None, None, None) + serializers = _ActionSerializers(start=None, + success=None, + failure=None) action = startTask(logger, "sys:do", serializers) self.assertIs(action._serializers, serializers) @@ -608,7 +610,9 @@ def test_startActionSerializers(self): resulting L{Action}. """ logger = MemoryLogger() - serializers = _ActionSerializers(None, None, None) + serializers = _ActionSerializers(start=None, + success=None, + failure=None) action = startAction(logger, "sys:do", serializers) self.assertIs(action._serializers, serializers) diff --git a/setup.py b/setup.py index 781258b..5532167 100644 --- a/setup.py +++ b/setup.py @@ -41,8 +41,6 @@ def read(path): "six", # Internal code documentation: "zope.interface", - # Struct-like objects: - "characteristic", # Persistent objects for Python: "pyrsistent >= 0.11.8", # version with multi-type pvector/pmap_field ],