44
55import traceback
66import inspect
7- import json as pyjson
87from threading import Lock
98from functools import wraps
109from io import IOBase
10+ import warnings
1111
1212from pyrsistent import PClass , field
1313
14- from . import _bytesjson as bytesjson
1514from zope .interface import Interface , implementer
1615
1716from ._traceback import write_traceback , TRACEBACK_MESSAGE
1817from ._message import EXCEPTION_FIELD , MESSAGE_TYPE_FIELD , REASON_FIELD
1918from ._util import saferepr , safeunicode
20- from .json import EliotJSONEncoder
19+ from .json import (
20+ json_default ,
21+ _encoder_to_default_function ,
22+ _dumps_bytes ,
23+ _dumps_unicode ,
24+ )
2125from ._validation import ValidationError
2226
2327
@@ -260,12 +264,19 @@ class MemoryLogger(object):
260264 not mutate this list.
261265 """
262266
263- def __init__ (self , encoder = EliotJSONEncoder ):
267+ def __init__ (self , encoder = None , json_default = json_default ):
264268 """
265- @param encoder: A JSONEncoder subclass to use when encoding JSON.
269+ @param encoder: DEPRECATED. A JSONEncoder subclass to use when
270+ encoding JSON.
271+
272+ @param json_default: A callable that handles objects the default JSON
273+ serializer can't handle.
266274 """
275+ json_default = _json_default_from_encoder_and_json_default (
276+ encoder , json_default
277+ )
267278 self ._lock = Lock ()
268- self ._encoder = encoder
279+ self ._json_default = json_default
269280 self .reset ()
270281
271282 @exclusively
@@ -346,7 +357,7 @@ def _validate_message(self, dictionary, serializer):
346357 serializer .serialize (dictionary )
347358
348359 try :
349- pyjson . dumps (dictionary , cls = self ._encoder )
360+ _dumps_unicode (dictionary , default = self ._json_default )
350361 except Exception as e :
351362 raise TypeError ("Message %s doesn't encode to JSON: %s" % (dictionary , e ))
352363
@@ -409,13 +420,26 @@ def reset(self):
409420 self ._failed_validations = []
410421
411422
423+ def _json_default_from_encoder_and_json_default (encoder , json_default ):
424+ if encoder is not None :
425+ warnings .warn (
426+ "Using a JSON encoder subclass is no longer supported, please switch to using a default function" ,
427+ DeprecationWarning ,
428+ stacklevel = 3 ,
429+ )
430+ from .json import json_default as default_json_default
431+
432+ if json_default is not default_json_default :
433+ raise RuntimeError ("Can't pass in both encoder and default function" )
434+
435+ json_default = _encoder_to_default_function (encoder ())
436+ return json_default
437+
438+
412439class FileDestination (PClass ):
413440 """
414- Callable that writes JSON messages to a file.
415-
416- On Python 3 the file may support either C{bytes} or C{unicode}. On
417- Python 2 only C{bytes} are supported since that is what all files expect
418- in practice.
441+ Callable that writes JSON messages to a file that accepts either C{bytes}
442+ or C{str}.
419443
420444 @ivar file: The file to which messages will be written.
421445
@@ -425,48 +449,68 @@ class FileDestination(PClass):
425449 """
426450
427451 file = field (mandatory = True )
428- encoder = field (mandatory = True )
452+ _json_default = field (mandatory = True )
429453 _dumps = field (mandatory = True )
430454 _linebreak = field (mandatory = True )
431455
432- def __new__ (cls , file , encoder = EliotJSONEncoder ):
456+ def __new__ (cls , file , encoder = None , json_default = json_default ):
457+ """
458+ Use ``json_default`` to pass in a default function for JSON dumping.
459+
460+ The ``encoder`` parameter is deprecated.
461+ """
433462 if isinstance (file , IOBase ) and not file .writable ():
434463 raise RuntimeError ("Given file {} is not writeable." )
435464
465+ json_default = _json_default_from_encoder_and_json_default (
466+ encoder , json_default
467+ )
468+
436469 unicodeFile = False
437470 try :
438471 file .write (b"" )
439472 except TypeError :
440473 unicodeFile = True
441474
442475 if unicodeFile :
443- # On Python 3 native json module outputs unicode:
444- _dumps = pyjson .dumps
476+ _dumps = _dumps_unicode
445477 _linebreak = "\n "
446478 else :
447- _dumps = bytesjson . dumps
479+ _dumps = _dumps_bytes
448480 _linebreak = b"\n "
449481 return PClass .__new__ (
450- cls , file = file , _dumps = _dumps , _linebreak = _linebreak , encoder = encoder
482+ cls ,
483+ file = file ,
484+ _dumps = _dumps ,
485+ _linebreak = _linebreak ,
486+ _json_default = json_default ,
451487 )
452488
453489 def __call__ (self , message ):
454490 """
455491 @param message: A message dictionary.
456492 """
457- self .file .write (self ._dumps (message , cls = self .encoder ) + self ._linebreak )
493+ self .file .write (
494+ self ._dumps (message , default = self ._json_default ) + self ._linebreak
495+ )
458496 self .file .flush ()
459497
460498
461- def to_file (output_file , encoder = EliotJSONEncoder ):
499+ def to_file (output_file , encoder = None , json_default = json_default ):
462500 """
463501 Add a destination that writes a JSON message per line to the given file.
464502
465503 @param output_file: A file-like object.
466504
467- @param encoder: A JSONEncoder subclass to use when encoding JSON.
505+ @param encoder: DEPRECATED. A JSONEncoder subclass to use when encoding
506+ JSON.
507+
508+ @param json_default: A callable that handles objects the default JSON
509+ serializer can't handle.
468510 """
469- Logger ._destinations .add (FileDestination (file = output_file , encoder = encoder ))
511+ Logger ._destinations .add (
512+ FileDestination (file = output_file , encoder = encoder , json_default = json_default )
513+ )
470514
471515
472516# The default Logger, used when none is specified:
0 commit comments