Skip to content

Commit

Permalink
Revert "Initial orjson support (home-assistant#72754)" (home-assistan…
Browse files Browse the repository at this point in the history
…t#72789)

This was causing the wheels to fail to build. We need
to workout why when we don't have release pressure

This reverts commit d9d22a9.
  • Loading branch information
bdraco authored May 31, 2022
1 parent 9cea936 commit c365454
Show file tree
Hide file tree
Showing 18 changed files with 67 additions and 127 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/history/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
)
from homeassistant.components.recorder.util import session_scope
from homeassistant.components.websocket_api import messages
from homeassistant.components.websocket_api.const import JSON_DUMP
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA
from homeassistant.helpers.json import JSON_DUMP
from homeassistant.helpers.typing import ConfigType
import homeassistant.util.dt as dt_util

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/logbook/websocket_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from homeassistant.components.recorder import get_instance
from homeassistant.components.websocket_api import messages
from homeassistant.components.websocket_api.connection import ActiveConnection
from homeassistant.components.websocket_api.const import JSON_DUMP
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.json import JSON_DUMP
import homeassistant.util.dt as dt_util

from .helpers import (
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/recorder/const.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Recorder constants."""

from functools import partial
import json
from typing import Final

from homeassistant.backports.enum import StrEnum
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_RESTORED, ATTR_SUPPORTED_FEATURES
from homeassistant.helpers.json import ( # noqa: F401 pylint: disable=unused-import
JSON_DUMP,
)
from homeassistant.helpers.json import JSONEncoder

DATA_INSTANCE = "recorder_instance"
SQLITE_URL_PREFIX = "sqlite://"
Expand All @@ -26,6 +27,7 @@

DB_WORKER_PREFIX = "DbWorker"

JSON_DUMP: Final = partial(json.dumps, cls=JSONEncoder, separators=(",", ":"))

ALL_DOMAIN_EXCLUDE_ATTRS = {ATTR_ATTRIBUTION, ATTR_RESTORED, ATTR_SUPPORTED_FEATURES}

Expand Down
10 changes: 4 additions & 6 deletions homeassistant/components/recorder/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,20 +744,19 @@ def _process_non_state_changed_event_into_session(self, event: Event) -> None:
return

try:
shared_data_bytes = EventData.shared_data_bytes_from_event(event)
shared_data = EventData.shared_data_from_event(event)
except (TypeError, ValueError) as ex:
_LOGGER.warning("Event is not JSON serializable: %s: %s", event, ex)
return

shared_data = shared_data_bytes.decode("utf-8")
# Matching attributes found in the pending commit
if pending_event_data := self._pending_event_data.get(shared_data):
dbevent.event_data_rel = pending_event_data
# Matching attributes id found in the cache
elif data_id := self._event_data_ids.get(shared_data):
dbevent.data_id = data_id
else:
data_hash = EventData.hash_shared_data_bytes(shared_data_bytes)
data_hash = EventData.hash_shared_data(shared_data)
# Matching attributes found in the database
if data_id := self._find_shared_data_in_db(data_hash, shared_data):
self._event_data_ids[shared_data] = dbevent.data_id = data_id
Expand All @@ -776,7 +775,7 @@ def _process_state_changed_event_into_session(self, event: Event) -> None:
assert self.event_session is not None
try:
dbstate = States.from_event(event)
shared_attrs_bytes = StateAttributes.shared_attrs_bytes_from_event(
shared_attrs = StateAttributes.shared_attrs_from_event(
event, self._exclude_attributes_by_domain
)
except (TypeError, ValueError) as ex:
Expand All @@ -787,7 +786,6 @@ def _process_state_changed_event_into_session(self, event: Event) -> None:
)
return

shared_attrs = shared_attrs_bytes.decode("utf-8")
dbstate.attributes = None
# Matching attributes found in the pending commit
if pending_attributes := self._pending_state_attributes.get(shared_attrs):
Expand All @@ -796,7 +794,7 @@ def _process_state_changed_event_into_session(self, event: Event) -> None:
elif attributes_id := self._state_attributes_ids.get(shared_attrs):
dbstate.attributes_id = attributes_id
else:
attr_hash = StateAttributes.hash_shared_attrs_bytes(shared_attrs_bytes)
attr_hash = StateAttributes.hash_shared_attrs(shared_attrs)
# Matching attributes found in the database
if attributes_id := self._find_shared_attr_in_db(attr_hash, shared_attrs):
dbstate.attributes_id = attributes_id
Expand Down
59 changes: 29 additions & 30 deletions homeassistant/components/recorder/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

from collections.abc import Callable
from datetime import datetime, timedelta
import json
import logging
from typing import Any, TypedDict, cast, overload

import ciso8601
from fnvhash import fnv1a_32
import orjson
from sqlalchemy import (
JSON,
BigInteger,
Expand Down Expand Up @@ -46,10 +46,9 @@
MAX_LENGTH_STATE_STATE,
)
from homeassistant.core import Context, Event, EventOrigin, State, split_entity_id
from homeassistant.helpers.json import JSON_DUMP, json_bytes
import homeassistant.util.dt as dt_util

from .const import ALL_DOMAIN_EXCLUDE_ATTRS
from .const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP

# SQLAlchemy Schema
# pylint: disable=invalid-name
Expand Down Expand Up @@ -133,7 +132,7 @@ def literal_processor(self, dialect: str) -> Callable[[Any], str]:

def process(value: Any) -> str:
"""Dump json."""
return JSON_DUMP(value)
return json.dumps(value)

return process

Expand Down Expand Up @@ -200,15 +199,15 @@ def to_native(self, validate_entity_id: bool = True) -> Event | None:
try:
return Event(
self.event_type,
orjson.loads(self.event_data) if self.event_data else {},
json.loads(self.event_data) if self.event_data else {},
EventOrigin(self.origin)
if self.origin
else EVENT_ORIGIN_ORDER[self.origin_idx],
process_timestamp(self.time_fired),
context=context,
)
except ValueError:
# When orjson.loads fails
# When json.loads fails
_LOGGER.exception("Error converting to event: %s", self)
return None

Expand Down Expand Up @@ -236,26 +235,25 @@ def __repr__(self) -> str:
@staticmethod
def from_event(event: Event) -> EventData:
"""Create object from an event."""
shared_data = json_bytes(event.data)
shared_data = JSON_DUMP(event.data)
return EventData(
shared_data=shared_data.decode("utf-8"),
hash=EventData.hash_shared_data_bytes(shared_data),
shared_data=shared_data, hash=EventData.hash_shared_data(shared_data)
)

@staticmethod
def shared_data_bytes_from_event(event: Event) -> bytes:
"""Create shared_data from an event."""
return json_bytes(event.data)
def shared_data_from_event(event: Event) -> str:
"""Create shared_attrs from an event."""
return JSON_DUMP(event.data)

@staticmethod
def hash_shared_data_bytes(shared_data_bytes: bytes) -> int:
def hash_shared_data(shared_data: str) -> int:
"""Return the hash of json encoded shared data."""
return cast(int, fnv1a_32(shared_data_bytes))
return cast(int, fnv1a_32(shared_data.encode("utf-8")))

def to_native(self) -> dict[str, Any]:
"""Convert to an HA state object."""
try:
return cast(dict[str, Any], orjson.loads(self.shared_data))
return cast(dict[str, Any], json.loads(self.shared_data))
except ValueError:
_LOGGER.exception("Error converting row to event data: %s", self)
return {}
Expand Down Expand Up @@ -342,9 +340,9 @@ def to_native(self, validate_entity_id: bool = True) -> State | None:
parent_id=self.context_parent_id,
)
try:
attrs = orjson.loads(self.attributes) if self.attributes else {}
attrs = json.loads(self.attributes) if self.attributes else {}
except ValueError:
# When orjson.loads fails
# When json.loads fails
_LOGGER.exception("Error converting row to state: %s", self)
return None
if self.last_changed is None or self.last_changed == self.last_updated:
Expand Down Expand Up @@ -390,39 +388,40 @@ def from_event(event: Event) -> StateAttributes:
"""Create object from a state_changed event."""
state: State | None = event.data.get("new_state")
# None state means the state was removed from the state machine
attr_bytes = b"{}" if state is None else json_bytes(state.attributes)
dbstate = StateAttributes(shared_attrs=attr_bytes.decode("utf-8"))
dbstate.hash = StateAttributes.hash_shared_attrs_bytes(attr_bytes)
dbstate = StateAttributes(
shared_attrs="{}" if state is None else JSON_DUMP(state.attributes)
)
dbstate.hash = StateAttributes.hash_shared_attrs(dbstate.shared_attrs)
return dbstate

@staticmethod
def shared_attrs_bytes_from_event(
def shared_attrs_from_event(
event: Event, exclude_attrs_by_domain: dict[str, set[str]]
) -> bytes:
) -> str:
"""Create shared_attrs from a state_changed event."""
state: State | None = event.data.get("new_state")
# None state means the state was removed from the state machine
if state is None:
return b"{}"
return "{}"
domain = split_entity_id(state.entity_id)[0]
exclude_attrs = (
exclude_attrs_by_domain.get(domain, set()) | ALL_DOMAIN_EXCLUDE_ATTRS
)
return json_bytes(
return JSON_DUMP(
{k: v for k, v in state.attributes.items() if k not in exclude_attrs}
)

@staticmethod
def hash_shared_attrs_bytes(shared_attrs_bytes: bytes) -> int:
"""Return the hash of orjson encoded shared attributes."""
return cast(int, fnv1a_32(shared_attrs_bytes))
def hash_shared_attrs(shared_attrs: str) -> int:
"""Return the hash of json encoded shared attributes."""
return cast(int, fnv1a_32(shared_attrs.encode("utf-8")))

def to_native(self) -> dict[str, Any]:
"""Convert to an HA state object."""
try:
return cast(dict[str, Any], orjson.loads(self.shared_attrs))
return cast(dict[str, Any], json.loads(self.shared_attrs))
except ValueError:
# When orjson.loads fails
# When json.loads fails
_LOGGER.exception("Error converting row to state attributes: %s", self)
return {}

Expand Down Expand Up @@ -836,7 +835,7 @@ def decode_attributes_from_row(
if not source or source == EMPTY_JSON_OBJECT:
return {}
try:
attr_cache[source] = attributes = orjson.loads(source)
attr_cache[source] = attributes = json.loads(source)
except ValueError:
_LOGGER.exception("Error converting row to state attributes: %s", source)
attr_cache[source] = attributes = {}
Expand Down
18 changes: 9 additions & 9 deletions homeassistant/components/websocket_api/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
TrackTemplateResult,
async_track_template_result,
)
from homeassistant.helpers.json import JSON_DUMP, ExtendedJSONEncoder
from homeassistant.helpers.json import ExtendedJSONEncoder
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.loader import IntegrationNotFound, async_get_integration
from homeassistant.setup import DATA_SETUP_TIME, async_get_loaded_integrations
Expand Down Expand Up @@ -241,13 +241,13 @@ def handle_get_states(
# to succeed for the UI to show.
response = messages.result_message(msg["id"], states)
try:
connection.send_message(JSON_DUMP(response))
connection.send_message(const.JSON_DUMP(response))
return
except (ValueError, TypeError):
connection.logger.error(
"Unable to serialize to JSON. Bad data found at %s",
format_unserializable_data(
find_paths_unserializable_data(response, dump=JSON_DUMP)
find_paths_unserializable_data(response, dump=const.JSON_DUMP)
),
)
del response
Expand All @@ -256,13 +256,13 @@ def handle_get_states(
serialized = []
for state in states:
try:
serialized.append(JSON_DUMP(state))
serialized.append(const.JSON_DUMP(state))
except (ValueError, TypeError):
# Error is already logged above
pass

# We now have partially serialized states. Craft some JSON.
response2 = JSON_DUMP(messages.result_message(msg["id"], ["TO_REPLACE"]))
response2 = const.JSON_DUMP(messages.result_message(msg["id"], ["TO_REPLACE"]))
response2 = response2.replace('"TO_REPLACE"', ", ".join(serialized))
connection.send_message(response2)

Expand Down Expand Up @@ -315,13 +315,13 @@ def forward_entity_changes(event: Event) -> None:
# to succeed for the UI to show.
response = messages.event_message(msg["id"], data)
try:
connection.send_message(JSON_DUMP(response))
connection.send_message(const.JSON_DUMP(response))
return
except (ValueError, TypeError):
connection.logger.error(
"Unable to serialize to JSON. Bad data found at %s",
format_unserializable_data(
find_paths_unserializable_data(response, dump=JSON_DUMP)
find_paths_unserializable_data(response, dump=const.JSON_DUMP)
),
)
del response
Expand All @@ -330,14 +330,14 @@ def forward_entity_changes(event: Event) -> None:
cannot_serialize: list[str] = []
for entity_id, state_dict in add_entities.items():
try:
JSON_DUMP(state_dict)
const.JSON_DUMP(state_dict)
except (ValueError, TypeError):
cannot_serialize.append(entity_id)

for entity_id in cannot_serialize:
del add_entities[entity_id]

connection.send_message(JSON_DUMP(messages.event_message(msg["id"], data)))
connection.send_message(const.JSON_DUMP(messages.event_message(msg["id"], data)))


@decorators.websocket_command({vol.Required("type"): "get_services"})
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/websocket_api/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from homeassistant.auth.models import RefreshToken, User
from homeassistant.core import Context, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError, Unauthorized
from homeassistant.helpers.json import JSON_DUMP

from . import const, messages

Expand Down Expand Up @@ -57,7 +56,7 @@ def send_result(self, msg_id: int, result: Any | None = None) -> None:
async def send_big_result(self, msg_id: int, result: Any) -> None:
"""Send a result message that would be expensive to JSON serialize."""
content = await self.hass.async_add_executor_job(
JSON_DUMP, messages.result_message(msg_id, result)
const.JSON_DUMP, messages.result_message(msg_id, result)
)
self.send_message(content)

Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/websocket_api/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import asyncio
from collections.abc import Awaitable, Callable
from concurrent import futures
from functools import partial
import json
from typing import TYPE_CHECKING, Any, Final

from homeassistant.core import HomeAssistant
from homeassistant.helpers.json import JSONEncoder

if TYPE_CHECKING:
from .connection import ActiveConnection # noqa: F401
Expand Down Expand Up @@ -50,6 +53,10 @@
# Data used to store the current connection list
DATA_CONNECTIONS: Final = f"{DOMAIN}.connections"

JSON_DUMP: Final = partial(
json.dumps, cls=JSONEncoder, allow_nan=False, separators=(",", ":")
)

COMPRESSED_STATE_STATE = "s"
COMPRESSED_STATE_ATTRIBUTES = "a"
COMPRESSED_STATE_CONTEXT = "c"
Expand Down
7 changes: 3 additions & 4 deletions homeassistant/components/websocket_api/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from homeassistant.core import Event, State
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.json import JSON_DUMP
from homeassistant.util.json import (
find_paths_unserializable_data,
format_unserializable_data,
Expand Down Expand Up @@ -194,15 +193,15 @@ def compressed_state_dict_add(state: State) -> dict[str, Any]:
def message_to_json(message: dict[str, Any]) -> str:
"""Serialize a websocket message to json."""
try:
return JSON_DUMP(message)
return const.JSON_DUMP(message)
except (ValueError, TypeError):
_LOGGER.error(
"Unable to serialize to JSON. Bad data found at %s",
format_unserializable_data(
find_paths_unserializable_data(message, dump=JSON_DUMP)
find_paths_unserializable_data(message, dump=const.JSON_DUMP)
),
)
return JSON_DUMP(
return const.JSON_DUMP(
error_message(
message["id"], const.ERR_UNKNOWN_ERROR, "Invalid JSON in response"
)
Expand Down
Loading

0 comments on commit c365454

Please sign in to comment.