-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve non-standard type encoding (#12)
This PR improves the JSON encoding of non-standard types by introducing and using the `.defaults` module. The `.defaults` module adds helper functions that can test and apply formatting for types not supported by a given encoder. Please note that in doing so, some outputs of the `JsonFormatter` have changed. That said these changes return more "reasonable" results rather the the original `str(o)` fallback. For more detailed list of changes to the encoders see the CHANGELOG. ## Test Plan Have added additional tests and now check for specific output.
- Loading branch information
Showing
8 changed files
with
327 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" | |
|
||
[project] | ||
name = "python-json-logger" | ||
version = "3.1.0.rc1" | ||
version = "3.1.0.rc2" | ||
description = "JSON Log Formatter for the Python Logging Package" | ||
authors = [ | ||
{name = "Zakaria Zajac", email = "[email protected]"}, | ||
|
@@ -55,6 +55,8 @@ dev = [ | |
## Test | ||
"pytest", | ||
"freezegun", | ||
"backports.zoneinfo;python_version<'3.9'", | ||
"tzdata", | ||
## Build | ||
"build", | ||
] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# pylint: disable=missing-function-docstring | ||
|
||
### IMPORTS | ||
### ============================================================================ | ||
## Future | ||
from __future__ import annotations | ||
|
||
## Standard Library | ||
import base64 | ||
import dataclasses | ||
import datetime | ||
import enum | ||
import sys | ||
from types import TracebackType | ||
from typing import Any | ||
import traceback | ||
import uuid | ||
|
||
if sys.version_info >= (3, 10): | ||
from typing import TypeGuard | ||
else: | ||
from typing_extensions import TypeGuard | ||
|
||
## Installed | ||
|
||
## Application | ||
|
||
|
||
### FUNCTIONS | ||
### ============================================================================ | ||
def unknown_default(obj: Any) -> str: | ||
try: | ||
return str(obj) | ||
except Exception: # pylint: disable=broad-exception-caught | ||
pass | ||
try: | ||
return repr(obj) | ||
except Exception: # pylint: disable=broad-exception-caught | ||
pass | ||
return "__could_not_encode__" | ||
|
||
|
||
## Types | ||
## ----------------------------------------------------------------------------- | ||
def use_type_default(obj: Any) -> TypeGuard[type]: | ||
return isinstance(obj, type) | ||
|
||
|
||
def type_default(obj: type) -> str: | ||
return obj.__name__ | ||
|
||
|
||
## Dataclasses | ||
## ----------------------------------------------------------------------------- | ||
def use_dataclass_default(obj: Any) -> bool: | ||
return dataclasses.is_dataclass(obj) and not isinstance(obj, type) | ||
|
||
|
||
def dataclass_default(obj) -> dict[str, Any]: | ||
return dataclasses.asdict(obj) | ||
|
||
|
||
## Dates and Times | ||
## ----------------------------------------------------------------------------- | ||
def use_time_default(obj: Any) -> TypeGuard[datetime.time]: | ||
return isinstance(obj, datetime.time) | ||
|
||
|
||
def time_default(obj: datetime.time) -> str: | ||
return obj.isoformat() | ||
|
||
|
||
def use_date_default(obj: Any) -> TypeGuard[datetime.date]: | ||
return isinstance(obj, datetime.date) | ||
|
||
|
||
def date_default(obj: datetime.date) -> str: | ||
return obj.isoformat() | ||
|
||
|
||
def use_datetime_default(obj: Any) -> TypeGuard[datetime.datetime]: | ||
return isinstance(obj, datetime.datetime) | ||
|
||
|
||
def datetime_default(obj: datetime.datetime) -> str: | ||
return obj.isoformat() | ||
|
||
|
||
def use_datetime_any(obj: Any) -> TypeGuard[datetime.time | datetime.date | datetime.datetime]: | ||
return isinstance(obj, (datetime.time, datetime.date, datetime.datetime)) | ||
|
||
|
||
def datetime_any(obj: datetime.time | datetime.date | datetime.date) -> str: | ||
return obj.isoformat() | ||
|
||
|
||
## Exception and Tracebacks | ||
## ----------------------------------------------------------------------------- | ||
def use_exception_default(obj: Any) -> TypeGuard[BaseException]: | ||
return isinstance(obj, BaseException) | ||
|
||
|
||
def exception_default(obj: BaseException) -> str: | ||
return f"{obj.__class__.__name__}: {obj}" | ||
|
||
|
||
def use_traceback_default(obj: Any) -> TypeGuard[TracebackType]: | ||
return isinstance(obj, TracebackType) | ||
|
||
|
||
def traceback_default(obj: TracebackType) -> str: | ||
return "".join(traceback.format_tb(obj)).strip() | ||
|
||
|
||
## Enums | ||
## ----------------------------------------------------------------------------- | ||
def use_enum_default(obj: Any) -> TypeGuard[enum.Enum | enum.EnumMeta]: | ||
return isinstance(obj, (enum.Enum, enum.EnumMeta)) | ||
|
||
|
||
def enum_default(obj: enum.Enum | enum.EnumMeta) -> Any | list[Any]: | ||
if isinstance(obj, enum.Enum): | ||
return obj.value | ||
return [e.value for e in obj] # type: ignore[var-annotated] | ||
|
||
|
||
## UUIDs | ||
## ----------------------------------------------------------------------------- | ||
def use_uuid_default(obj: Any) -> TypeGuard[uuid.UUID]: | ||
return isinstance(obj, uuid.UUID) | ||
|
||
|
||
def uuid_default(obj: uuid.UUID) -> str: | ||
return str(obj) | ||
|
||
|
||
## Bytes | ||
## ----------------------------------------------------------------------------- | ||
def use_bytes_default(obj: Any) -> TypeGuard[bytes | bytearray]: | ||
return isinstance(obj, (bytes, bytearray)) | ||
|
||
|
||
def bytes_default(obj: bytes | bytearray, url_safe: bool = True) -> str: | ||
if url_safe: | ||
return base64.urlsafe_b64encode(obj).decode("utf8") | ||
return base64.b64encode(obj).decode("utf8") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.