aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/pythonjsonlogger
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/pythonjsonlogger')
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/__init__.py17
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/core.py394
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/defaults.py241
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/exception.py27
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/json.py119
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/jsonlogger.py18
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/msgspec.py63
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/orjson.py71
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/py.typed1
-rw-r--r--.venv/lib/python3.12/site-packages/pythonjsonlogger/utils.py40
10 files changed, 991 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/__init__.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/__init__.py
new file mode 100644
index 00000000..298a3feb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/__init__.py
@@ -0,0 +1,17 @@
+### IMPORTS
+### ============================================================================
+## Future
+
+## Standard Library
+import warnings
+
+## Installed
+
+## Application
+from . import json
+from . import utils
+
+### CONSTANTS
+### ============================================================================
+ORJSON_AVAILABLE = utils.package_is_available("orjson")
+MSGSPEC_AVAILABLE = utils.package_is_available("msgspec")
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/core.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/core.py
new file mode 100644
index 00000000..1a4dee38
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/core.py
@@ -0,0 +1,394 @@
+"""Core functionality shared by all JSON loggers"""
+
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+from datetime import datetime, timezone
+import importlib
+import logging
+import re
+import sys
+from typing import Optional, Union, Callable, List, Dict, Container, Any, Sequence
+
+if sys.version_info >= (3, 10):
+ from typing import TypeAlias
+else:
+ from typing_extensions import TypeAlias
+
+## Installed
+
+## Application
+
+
+### CONSTANTS
+### ============================================================================
+RESERVED_ATTRS: List[str] = [
+ "args",
+ "asctime",
+ "created",
+ "exc_info",
+ "exc_text",
+ "filename",
+ "funcName",
+ "levelname",
+ "levelno",
+ "lineno",
+ "module",
+ "msecs",
+ "message",
+ "msg",
+ "name",
+ "pathname",
+ "process",
+ "processName",
+ "relativeCreated",
+ "stack_info",
+ "thread",
+ "threadName",
+]
+"""Default reserved attributes.
+
+These come from the [default attributes of `LogRecord` objects](http://docs.python.org/library/logging.html#logrecord-attributes).
+
+Note:
+ Although considered a constant, this list is dependent on the Python version due to
+ different `LogRecord` objects having different attributes in different Python versions.
+
+*Changed in 3.0*: `RESERVED_ATTRS` is now `list[str]` instead of `tuple[str, ...]`.
+"""
+
+if sys.version_info >= (3, 12):
+ # taskName added in python 3.12
+ RESERVED_ATTRS.append("taskName")
+ RESERVED_ATTRS.sort()
+
+
+STYLE_STRING_TEMPLATE_REGEX = re.compile(r"\$\{(.+?)\}", re.IGNORECASE) # $ style
+STYLE_STRING_FORMAT_REGEX = re.compile(r"\{(.+?)\}", re.IGNORECASE) # { style
+STYLE_PERCENT_REGEX = re.compile(r"%\((.+?)\)", re.IGNORECASE) # % style
+
+## Type Aliases
+## -----------------------------------------------------------------------------
+OptionalCallableOrStr: TypeAlias = Optional[Union[Callable, str]]
+"""Type alias"""
+
+LogRecord: TypeAlias = Dict[str, Any]
+"""Type alias"""
+
+
+### FUNCTIONS
+### ============================================================================
+def str_to_object(obj: Any) -> Any:
+ """Import strings to an object, leaving non-strings as-is.
+
+ Args:
+ obj: the object or string to process
+
+ *New in 3.1*
+ """
+
+ if not isinstance(obj, str):
+ return obj
+
+ module_name, attribute_name = obj.rsplit(".", 1)
+ return getattr(importlib.import_module(module_name), attribute_name)
+
+
+def merge_record_extra(
+ record: logging.LogRecord,
+ target: Dict,
+ reserved: Container[str],
+ rename_fields: Optional[Dict[str, str]] = None,
+) -> Dict:
+ """
+ Merges extra attributes from LogRecord object into target dictionary
+
+ Args:
+ record: logging.LogRecord
+ target: dict to update
+ reserved: dict or list with reserved keys to skip
+ rename_fields: an optional dict, used to rename field names in the output.
+ e.g. Rename `levelname` to `log.level`: `{'levelname': 'log.level'}`
+
+ *Changed in 3.1*: `reserved` is now `Container[str]`.
+ """
+ if rename_fields is None:
+ rename_fields = {}
+ for key, value in record.__dict__.items():
+ # this allows to have numeric keys
+ if key not in reserved and not (hasattr(key, "startswith") and key.startswith("_")):
+ target[rename_fields.get(key, key)] = value
+ return target
+
+
+### CLASSES
+### ============================================================================
+class BaseJsonFormatter(logging.Formatter):
+ """Base class for all formatters
+
+ Must not be used directly.
+
+ *New in 3.1*
+
+ *Changed in 3.2*: `defaults` argument is no longer ignored.
+
+ *Added in UNRELEASED*: `exc_info_as_array` and `stack_info_as_array` options are added.
+ """
+
+ _style: Union[logging.PercentStyle, str] # type: ignore[assignment]
+
+ ## Parent Methods
+ ## -------------------------------------------------------------------------
+ # pylint: disable=too-many-arguments,super-init-not-called
+ def __init__(
+ self,
+ fmt: Optional[str] = None,
+ datefmt: Optional[str] = None,
+ style: str = "%",
+ validate: bool = True,
+ *,
+ prefix: str = "",
+ rename_fields: Optional[Dict[str, str]] = None,
+ rename_fields_keep_missing: bool = False,
+ static_fields: Optional[Dict[str, Any]] = None,
+ reserved_attrs: Optional[Sequence[str]] = None,
+ timestamp: Union[bool, str] = False,
+ defaults: Optional[Dict[str, Any]] = None,
+ exc_info_as_array: bool = False,
+ stack_info_as_array: bool = False,
+ ) -> None:
+ """
+ Args:
+ fmt: string representing fields to log
+ datefmt: format to use when formatting `asctime` field
+ style: how to extract log fields from `fmt`
+ validate: validate `fmt` against style, if implementing a custom `style` you
+ must set this to `False`.
+ defaults: a dictionary containing default fields that are added before all other fields and
+ may be overridden. The supplied fields are still subject to `rename_fields`.
+ prefix: an optional string prefix added at the beginning of
+ the formatted string
+ rename_fields: an optional dict, used to rename field names in the output.
+ Rename `message` to `@message`: `{'message': '@message'}`
+ rename_fields_keep_missing: When renaming fields, include missing fields in the output.
+ static_fields: an optional dict, used to add fields with static values to all logs
+ reserved_attrs: an optional list of fields that will be skipped when
+ outputting json log record. Defaults to [all log record attributes][pythonjsonlogger.core.RESERVED_ATTRS].
+ timestamp: an optional string/boolean field to add a timestamp when
+ outputting the json log record. If string is passed, timestamp will be added
+ to log record using string as key. If True boolean is passed, timestamp key
+ will be "timestamp". Defaults to False/off.
+ exc_info_as_array: break the exc_info into a list of lines based on line breaks.
+ stack_info_as_array: break the stack_info into a list of lines based on line breaks.
+
+ *Changed in 3.1*:
+
+ - you can now use custom values for style by setting validate to `False`.
+ The value is stored in `self._style` as a string. The `parse` method will need to be
+ overridden in order to support the new style.
+ - Renaming fields now preserves the order that fields were added in and avoids adding
+ missing fields. The original behaviour, missing fields have a value of `None`, is still
+ available by setting `rename_fields_keep_missing` to `True`.
+ """
+ ## logging.Formatter compatibility
+ ## ---------------------------------------------------------------------
+ # Note: validate added in 3.8, defaults added in 3.10
+ if style in logging._STYLES:
+ _style = logging._STYLES[style][0](fmt) # type: ignore[operator]
+ if validate:
+ _style.validate()
+ self._style = _style
+ self._fmt = _style._fmt
+
+ elif not validate:
+ self._style = style
+ self._fmt = fmt
+
+ else:
+ raise ValueError(f"Style must be one of: {','.join(logging._STYLES.keys())}")
+
+ self.datefmt = datefmt
+
+ ## JSON Logging specific
+ ## ---------------------------------------------------------------------
+ self.prefix = prefix
+ self.rename_fields = rename_fields if rename_fields is not None else {}
+ self.rename_fields_keep_missing = rename_fields_keep_missing
+ self.static_fields = static_fields if static_fields is not None else {}
+ self.reserved_attrs = set(reserved_attrs if reserved_attrs is not None else RESERVED_ATTRS)
+ self.timestamp = timestamp
+
+ self._required_fields = self.parse()
+ self._skip_fields = set(self._required_fields)
+ self._skip_fields.update(self.reserved_attrs)
+ self.defaults = defaults if defaults is not None else {}
+ self.exc_info_as_array = exc_info_as_array
+ self.stack_info_as_array = stack_info_as_array
+ return
+
+ def format(self, record: logging.LogRecord) -> str:
+ """Formats a log record and serializes to json
+
+ Args:
+ record: the record to format
+ """
+ message_dict: Dict[str, Any] = {}
+ # TODO: logging.LogRecord.msg and logging.LogRecord.message in typeshed
+ # are always type of str. We shouldn't need to override that.
+ if isinstance(record.msg, dict):
+ message_dict = record.msg
+ record.message = ""
+ else:
+ record.message = record.getMessage()
+
+ # only format time if needed
+ if "asctime" in self._required_fields:
+ record.asctime = self.formatTime(record, self.datefmt)
+
+ # Display formatted exception, but allow overriding it in the
+ # user-supplied dict.
+ if record.exc_info and not message_dict.get("exc_info"):
+ message_dict["exc_info"] = self.formatException(record.exc_info)
+ if not message_dict.get("exc_info") and record.exc_text:
+ message_dict["exc_info"] = record.exc_text
+
+ # Display formatted record of stack frames
+ # default format is a string returned from :func:`traceback.print_stack`
+ if record.stack_info and not message_dict.get("stack_info"):
+ message_dict["stack_info"] = self.formatStack(record.stack_info)
+
+ log_record: LogRecord = {}
+ self.add_fields(log_record, record, message_dict)
+ log_record = self.process_log_record(log_record)
+
+ return self.serialize_log_record(log_record)
+
+ ## JSON Formatter Specific Methods
+ ## -------------------------------------------------------------------------
+ def parse(self) -> List[str]:
+ """Parses format string looking for substitutions
+
+ This method is responsible for returning a list of fields (as strings)
+ to include in all log messages.
+
+ You can support custom styles by overriding this method.
+
+ Returns:
+ list of fields to be extracted and serialized
+ """
+ if isinstance(self._style, logging.StringTemplateStyle):
+ formatter_style_pattern = STYLE_STRING_TEMPLATE_REGEX
+
+ elif isinstance(self._style, logging.StrFormatStyle):
+ formatter_style_pattern = STYLE_STRING_FORMAT_REGEX
+
+ elif isinstance(self._style, logging.PercentStyle):
+ # PercentStyle is parent class of StringTemplateStyle and StrFormatStyle
+ # so it must be checked last.
+ formatter_style_pattern = STYLE_PERCENT_REGEX
+
+ else:
+ raise ValueError(f"Style {self._style!r} is not supported")
+
+ if self._fmt:
+ return formatter_style_pattern.findall(self._fmt)
+
+ return []
+
+ def serialize_log_record(self, log_record: LogRecord) -> str:
+ """Returns the final representation of the log record.
+
+ Args:
+ log_record: the log record
+ """
+ return self.prefix + self.jsonify_log_record(log_record)
+
+ def add_fields(
+ self,
+ log_record: Dict[str, Any],
+ record: logging.LogRecord,
+ message_dict: Dict[str, Any],
+ ) -> None:
+ """Extract fields from a LogRecord for logging
+
+ This method can be overridden to implement custom logic for adding fields.
+
+ Args:
+ log_record: data that will be logged
+ record: the record to extract data from
+ message_dict: dictionary that was logged instead of a message. e.g
+ `logger.info({"is_this_message_dict": True})`
+ """
+ for field in self.defaults:
+ log_record[self._get_rename(field)] = self.defaults[field]
+
+ for field in self._required_fields:
+ log_record[self._get_rename(field)] = record.__dict__.get(field)
+
+ for data_dict in [self.static_fields, message_dict]:
+ for key, value in data_dict.items():
+ log_record[self._get_rename(key)] = value
+
+ merge_record_extra(
+ record,
+ log_record,
+ reserved=self._skip_fields,
+ rename_fields=self.rename_fields,
+ )
+
+ if self.timestamp:
+ key = self.timestamp if isinstance(self.timestamp, str) else "timestamp"
+ log_record[self._get_rename(key)] = datetime.fromtimestamp(
+ record.created, tz=timezone.utc
+ )
+
+ if self.rename_fields_keep_missing:
+ for field in self.rename_fields.values():
+ if field not in log_record:
+ log_record[field] = None
+ return
+
+ def _get_rename(self, key: str) -> str:
+ return self.rename_fields.get(key, key)
+
+ # Child Methods
+ # ..........................................................................
+ def jsonify_log_record(self, log_record: LogRecord) -> str:
+ """Convert this log record into a JSON string.
+
+ Child classes MUST override this method.
+
+ Args:
+ log_record: the data to serialize
+ """
+ raise NotImplementedError()
+
+ def process_log_record(self, log_record: LogRecord) -> LogRecord:
+ """Custom processing of the log record.
+
+ Child classes can override this method to alter the log record before it
+ is serialized.
+
+ Args:
+ log_record: incoming data
+ """
+ return log_record
+
+ def formatException(self, ei) -> Union[str, list[str]]: # type: ignore
+ """Format and return the specified exception information.
+
+ If exc_info_as_array is set to True, This method returns an array of strings.
+ """
+ exception_info_str = super().formatException(ei)
+ return exception_info_str.splitlines() if self.exc_info_as_array else exception_info_str
+
+ def formatStack(self, stack_info) -> Union[str, list[str]]: # type: ignore
+ """Format and return the specified stack information.
+
+ If stack_info_as_array is set to True, This method returns an array of strings.
+ """
+ stack_info_str = super().formatStack(stack_info)
+ return stack_info_str.splitlines() if self.stack_info_as_array else stack_info_str
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/defaults.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/defaults.py
new file mode 100644
index 00000000..0a002a90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/defaults.py
@@ -0,0 +1,241 @@
+"""Collection of functions for building custom `json_default` functions.
+
+In general functions come in pairs of `use_x_default` and `x_default`, where the former is used
+to determine if you should call the latter.
+
+Most `use_x_default` functions also act as a [`TypeGuard`](https://mypy.readthedocs.io/en/stable/type_narrowing.html#user-defined-type-guards).
+"""
+
+### 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:
+ """Backup default function for any object type.
+
+ Will attempt to use `str` or `repr`. If both functions error will return
+ the string `"__could_not_encode__"`.
+
+ Args:
+ obj: object to handle
+ """
+ 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]:
+ """Default check function for `type` objects (aka classes)."""
+ return isinstance(obj, type)
+
+
+def type_default(obj: type) -> str:
+ """Default function for `type` objects.
+
+ Args:
+ obj: object to handle
+ """
+ return obj.__name__
+
+
+## Dataclasses
+## -----------------------------------------------------------------------------
+def use_dataclass_default(obj: Any) -> bool:
+ """Default check function for dataclass instances"""
+ return dataclasses.is_dataclass(obj) and not isinstance(obj, type)
+
+
+def dataclass_default(obj) -> dict[str, Any]:
+ """Default function for dataclass instances
+
+ Args:
+ obj: object to handle
+ """
+ return dataclasses.asdict(obj)
+
+
+## Dates and Times
+## -----------------------------------------------------------------------------
+def use_time_default(obj: Any) -> TypeGuard[datetime.time]:
+ """Default check function for `datetime.time` instances"""
+ return isinstance(obj, datetime.time)
+
+
+def time_default(obj: datetime.time) -> str:
+ """Default function for `datetime.time` instances
+
+ Args:
+ obj: object to handle
+ """
+ return obj.isoformat()
+
+
+def use_date_default(obj: Any) -> TypeGuard[datetime.date]:
+ """Default check function for `datetime.date` instances"""
+ return isinstance(obj, datetime.date)
+
+
+def date_default(obj: datetime.date) -> str:
+ """Default function for `datetime.date` instances
+
+ Args:
+ obj: object to handle
+ """
+ return obj.isoformat()
+
+
+def use_datetime_default(obj: Any) -> TypeGuard[datetime.datetime]:
+ """Default check function for `datetime.datetime` instances"""
+ return isinstance(obj, datetime.datetime)
+
+
+def datetime_default(obj: datetime.datetime) -> str:
+ """Default function for `datetime.datetime` instances
+
+ Args:
+ obj: object to handle
+ """
+ return obj.isoformat()
+
+
+def use_datetime_any(obj: Any) -> TypeGuard[datetime.time | datetime.date | datetime.datetime]:
+ """Default check function for `datetime` related instances"""
+ return isinstance(obj, (datetime.time, datetime.date, datetime.datetime))
+
+
+def datetime_any(obj: datetime.time | datetime.date | datetime.date) -> str:
+ """Default function for `datetime` related instances
+
+ Args:
+ obj: object to handle
+ """
+ return obj.isoformat()
+
+
+## Exception and Tracebacks
+## -----------------------------------------------------------------------------
+def use_exception_default(obj: Any) -> TypeGuard[BaseException]:
+ """Default check function for exception instances.
+
+ Exception classes are not treated specially and should be handled by the
+ `[use_]type_default` functions.
+ """
+ return isinstance(obj, BaseException)
+
+
+def exception_default(obj: BaseException) -> str:
+ """Default function for exception instances
+
+ Args:
+ obj: object to handle
+ """
+ return f"{obj.__class__.__name__}: {obj}"
+
+
+def use_traceback_default(obj: Any) -> TypeGuard[TracebackType]:
+ """Default check function for tracebacks"""
+ return isinstance(obj, TracebackType)
+
+
+def traceback_default(obj: TracebackType) -> str:
+ """Default function for tracebacks
+
+ Args:
+ obj: object to handle
+ """
+ return "".join(traceback.format_tb(obj)).strip()
+
+
+## Enums
+## -----------------------------------------------------------------------------
+def use_enum_default(obj: Any) -> TypeGuard[enum.Enum | enum.EnumMeta]:
+ """Default check function for enums.
+
+ Supports both enum classes and enum values.
+ """
+ return isinstance(obj, (enum.Enum, enum.EnumMeta))
+
+
+def enum_default(obj: enum.Enum | enum.EnumMeta) -> Any | list[Any]:
+ """Default function for enums.
+
+ Supports both enum classes and enum values.
+
+ Args:
+ obj: object to handle
+ """
+ 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]:
+ """Default check function for `uuid.UUID` instances"""
+ return isinstance(obj, uuid.UUID)
+
+
+def uuid_default(obj: uuid.UUID) -> str:
+ """Default function for `uuid.UUID` instances
+
+ Formats the UUID using "hyphen" format.
+
+ Args:
+ obj: object to handle
+ """
+ return str(obj)
+
+
+## Bytes
+## -----------------------------------------------------------------------------
+def use_bytes_default(obj: Any) -> TypeGuard[bytes | bytearray]:
+ """Default check function for bytes"""
+ return isinstance(obj, (bytes, bytearray))
+
+
+def bytes_default(obj: bytes | bytearray, url_safe: bool = True) -> str:
+ """Default function for bytes
+
+ Args:
+ obj: object to handle
+ url_safe: use URL safe base 64 character set.
+
+ Returns:
+ The byte data as a base 64 string.
+ """
+ if url_safe:
+ return base64.urlsafe_b64encode(obj).decode("utf8")
+ return base64.b64encode(obj).decode("utf8")
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/exception.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/exception.py
new file mode 100644
index 00000000..1233f1ab
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/exception.py
@@ -0,0 +1,27 @@
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+
+## Installed
+
+## Application
+
+
+### CLASSES
+### ============================================================================
+class PythonJsonLoggerError(Exception):
+ "Generic base clas for all Python JSON Logger exceptions"
+
+
+class MissingPackageError(ImportError, PythonJsonLoggerError):
+ "A required package is missing"
+
+ def __init__(self, name: str, extras_name: str | None = None) -> None:
+ msg = f"The {name!r} package is required but could not be found."
+ if extras_name is not None:
+ msg += f" It can be installed using 'python-json-logger[{extras_name}]'."
+ super().__init__(msg)
+ return
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/json.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/json.py
new file mode 100644
index 00000000..21e78d0f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/json.py
@@ -0,0 +1,119 @@
+"""JSON formatter using the standard library's `json` for encoding.
+
+Module contains the `JsonFormatter` and a custom `JsonEncoder` which supports a greater
+variety of types.
+"""
+
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+import datetime
+import json
+from typing import Any, Callable, Optional, Union
+import warnings
+
+## Application
+from . import core
+from . import defaults as d
+
+
+### CLASSES
+### ============================================================================
+class JsonEncoder(json.JSONEncoder):
+ """A custom encoder extending [json.JSONEncoder](https://docs.python.org/3/library/json.html#json.JSONEncoder)"""
+
+ def default(self, o: Any) -> Any:
+ if d.use_datetime_any(o):
+ return self.format_datetime_obj(o)
+
+ if d.use_exception_default(o):
+ return d.exception_default(o)
+
+ if d.use_traceback_default(o):
+ return d.traceback_default(o)
+
+ if d.use_enum_default(o):
+ return d.enum_default(o)
+
+ if d.use_bytes_default(o):
+ return d.bytes_default(o)
+
+ if d.use_dataclass_default(o):
+ return d.dataclass_default(o)
+
+ if d.use_type_default(o):
+ return d.type_default(o)
+
+ try:
+ return super().default(o)
+ except TypeError:
+ return d.unknown_default(o)
+
+ def format_datetime_obj(self, o: datetime.time | datetime.date | datetime.datetime) -> str:
+ """Format datetime objects found in `self.default`
+
+ This allows subclasses to change the datetime format without understanding the
+ internals of the default method.
+ """
+ return d.datetime_any(o)
+
+
+class JsonFormatter(core.BaseJsonFormatter):
+ """JSON formatter using the standard library's [`json`](https://docs.python.org/3/library/json.html) for encoding"""
+
+ def __init__(
+ self,
+ *args,
+ json_default: core.OptionalCallableOrStr = None,
+ json_encoder: core.OptionalCallableOrStr = None,
+ json_serializer: Union[Callable, str] = json.dumps,
+ json_indent: Optional[Union[int, str]] = None,
+ json_ensure_ascii: bool = True,
+ **kwargs,
+ ) -> None:
+ """
+ Args:
+ args: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ json_default: a function for encoding non-standard objects
+ json_encoder: custom JSON encoder
+ json_serializer: a [`json.dumps`](https://docs.python.org/3/library/json.html#json.dumps)-compatible callable
+ that will be used to serialize the log record.
+ json_indent: indent parameter for the `json_serializer`
+ json_ensure_ascii: `ensure_ascii` parameter for the `json_serializer`
+ kwargs: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ """
+ super().__init__(*args, **kwargs)
+
+ self.json_default = core.str_to_object(json_default)
+ self.json_encoder = core.str_to_object(json_encoder)
+ self.json_serializer = core.str_to_object(json_serializer)
+ self.json_indent = json_indent
+ self.json_ensure_ascii = json_ensure_ascii
+ if not self.json_encoder and not self.json_default:
+ self.json_encoder = JsonEncoder
+ return
+
+ def jsonify_log_record(self, log_record: core.LogRecord) -> str:
+ """Returns a json string of the log record."""
+ return self.json_serializer(
+ log_record,
+ default=self.json_default,
+ cls=self.json_encoder,
+ indent=self.json_indent,
+ ensure_ascii=self.json_ensure_ascii,
+ )
+
+
+### DEPRECATED COMPATIBILITY
+### ============================================================================
+def __getattr__(name: str):
+ if name == "RESERVED_ATTRS":
+ warnings.warn(
+ "RESERVED_ATTRS has been moved to pythonjsonlogger.core",
+ DeprecationWarning,
+ )
+ return core.RESERVED_ATTRS
+ raise AttributeError(f"module {__name__} has no attribute {name}")
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/jsonlogger.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/jsonlogger.py
new file mode 100644
index 00000000..0b283b2d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/jsonlogger.py
@@ -0,0 +1,18 @@
+"""Stub module retained for compatibility.
+
+It retains access to old names whilst sending deprecation warnings.
+"""
+
+# pylint: disable=wrong-import-position,unused-import
+
+import warnings
+
+## Throw warning
+warnings.warn(
+ "pythonjsonlogger.jsonlogger has been moved to pythonjsonlogger.json",
+ DeprecationWarning,
+)
+
+## Import names
+from .json import JsonFormatter, JsonEncoder
+from .core import RESERVED_ATTRS
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/msgspec.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/msgspec.py
new file mode 100644
index 00000000..8646f85d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/msgspec.py
@@ -0,0 +1,63 @@
+"""JSON Formatter using [`msgspec`](https://github.com/jcrist/msgspec)"""
+
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+from typing import Any
+
+## Installed
+
+## Application
+from . import core
+from . import defaults as d
+from .utils import package_is_available
+
+# We import msgspec after checking it is available
+package_is_available("msgspec", throw_error=True)
+import msgspec.json # pylint: disable=wrong-import-position,wrong-import-order
+
+
+### FUNCTIONS
+### ============================================================================
+def msgspec_default(obj: Any) -> Any:
+ """msgspec default encoder function for non-standard types"""
+ if d.use_exception_default(obj):
+ return d.exception_default(obj)
+ if d.use_traceback_default(obj):
+ return d.traceback_default(obj)
+ if d.use_enum_default(obj):
+ return d.enum_default(obj)
+ if d.use_type_default(obj):
+ return d.type_default(obj)
+ return d.unknown_default(obj)
+
+
+### CLASSES
+### ============================================================================
+class MsgspecFormatter(core.BaseJsonFormatter):
+ """JSON formatter using [`msgspec.json.Encoder`](https://jcristharif.com/msgspec/api.html#msgspec.json.Encoder) for encoding."""
+
+ def __init__(
+ self,
+ *args,
+ json_default: core.OptionalCallableOrStr = msgspec_default,
+ **kwargs,
+ ) -> None:
+ """
+ Args:
+ args: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ json_default: a function for encoding non-standard objects
+ kwargs: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ """
+ super().__init__(*args, **kwargs)
+
+ self.json_default = core.str_to_object(json_default)
+ self._encoder = msgspec.json.Encoder(enc_hook=self.json_default)
+ return
+
+ def jsonify_log_record(self, log_record: core.LogRecord) -> str:
+ """Returns a json string of the log record."""
+ return self._encoder.encode(log_record).decode("utf8")
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/orjson.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/orjson.py
new file mode 100644
index 00000000..16db8426
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/orjson.py
@@ -0,0 +1,71 @@
+"""JSON Formatter using [orjson](https://github.com/ijl/orjson)"""
+
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+from typing import Any
+
+## Installed
+
+## Application
+from . import core
+from . import defaults as d
+from .utils import package_is_available
+
+# We import msgspec after checking it is available
+package_is_available("orjson", throw_error=True)
+import orjson # pylint: disable=wrong-import-position,wrong-import-order
+
+
+### FUNCTIONS
+### ============================================================================
+def orjson_default(obj: Any) -> Any:
+ """orjson default encoder function for non-standard types"""
+ if d.use_exception_default(obj):
+ return d.exception_default(obj)
+ if d.use_traceback_default(obj):
+ return d.traceback_default(obj)
+ if d.use_bytes_default(obj):
+ return d.bytes_default(obj)
+ if d.use_enum_default(obj):
+ return d.enum_default(obj)
+ if d.use_type_default(obj):
+ return d.type_default(obj)
+ return d.unknown_default(obj)
+
+
+### CLASSES
+### ============================================================================
+class OrjsonFormatter(core.BaseJsonFormatter):
+ """JSON formatter using [orjson](https://github.com/ijl/orjson) for encoding."""
+
+ def __init__(
+ self,
+ *args,
+ json_default: core.OptionalCallableOrStr = orjson_default,
+ json_indent: bool = False,
+ **kwargs,
+ ) -> None:
+ """
+ Args:
+ args: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ json_default: a function for encoding non-standard objects
+ json_indent: indent output with 2 spaces.
+ kwargs: see [BaseJsonFormatter][pythonjsonlogger.core.BaseJsonFormatter]
+ """
+ super().__init__(*args, **kwargs)
+
+ self.json_default = core.str_to_object(json_default)
+ self.json_indent = json_indent
+ return
+
+ def jsonify_log_record(self, log_record: core.LogRecord) -> str:
+ """Returns a json string of the log record."""
+ opt = orjson.OPT_NON_STR_KEYS
+ if self.json_indent:
+ opt |= orjson.OPT_INDENT_2
+
+ return orjson.dumps(log_record, default=self.json_default, option=opt).decode("utf8")
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/py.typed b/.venv/lib/python3.12/site-packages/pythonjsonlogger/py.typed
new file mode 100644
index 00000000..89afa56d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/py.typed
@@ -0,0 +1 @@
+# PEP-561 marker. https://mypy.readthedocs.io/en/latest/installed_packages.html
diff --git a/.venv/lib/python3.12/site-packages/pythonjsonlogger/utils.py b/.venv/lib/python3.12/site-packages/pythonjsonlogger/utils.py
new file mode 100644
index 00000000..d810a130
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pythonjsonlogger/utils.py
@@ -0,0 +1,40 @@
+"""Utilities for Python JSON Logger"""
+
+### IMPORTS
+### ============================================================================
+## Future
+from __future__ import annotations
+
+## Standard Library
+import importlib.util
+
+## Installed
+
+## Application
+from .exception import MissingPackageError
+
+
+### FUNCTIONS
+### ============================================================================
+def package_is_available(
+ name: str, *, throw_error: bool = False, extras_name: str | None = None
+) -> bool:
+ """Determine if the given package is available for import.
+
+ Args:
+ name: Import name of the package to check.
+ throw_error: Throw an error if the package is unavailable.
+ extras_name: Extra dependency name to use in `throw_error`'s message.
+
+ Raises:
+ MissingPackageError: When `throw_error` is `True` and the return value would be `False`
+
+ Returns:
+ If the package is available for import.
+ """
+ available = importlib.util.find_spec(name) is not None
+
+ if not available and throw_error:
+ raise MissingPackageError(name, extras_name)
+
+ return available