Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ jobs:
codestyle:
name: ruff codestyle check/linting
runs-on: ubuntu-latest
continue-on-error: true

steps:
- name: checkout code
Expand All @@ -31,6 +30,9 @@ jobs:
run: ruff check hololinked

test:
name: unit-integration tests
needs: codestyle

strategy:
matrix:
include:
Expand Down
1 change: 0 additions & 1 deletion hololinked/client/abstractions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import typing
import builtins
import logging
from types import FunctionType, MethodType
from dataclasses import dataclass

from ..td import PropertyAffordance, ActionAffordance, EventAffordance
Expand Down
2 changes: 1 addition & 1 deletion hololinked/client/zmq/consumed_interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from ...constants import Operations
from ...serializers.payloads import SerializableData
from ...core import Thing, Action
from ...core import Thing, Action # noqa: F401
from ...td import PropertyAffordance, ActionAffordance, EventAffordance
from ...td.forms import Form
from ...core.zmq.message import ResponseMessage
Expand Down
11 changes: 10 additions & 1 deletion hololinked/core/zmq/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
from .brokers import *
from .brokers import ( # noqa: F401
AsyncZMQServer,
ZMQServerPool,
SyncZMQClient,
AsyncZMQClient,
MessageMappedZMQClientPool,
EventPublisher,
AsyncEventConsumer,
EventConsumer,
)
12 changes: 7 additions & 5 deletions hololinked/core/zmq/brokers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from ...serializers.serializers import Serializers
from ...exceptions import BreakLoop
from .message import (
EMPTY_BYTE,
ERROR,
EXIT,
HANDSHAKE,
Expand Down Expand Up @@ -895,7 +894,8 @@ def exit(self) -> None:
# print("poller exception did not occur 2")
self.poller.unregister(self._monitor_socket)
# print("poller exception did not occur 3")
except Exception as ex:
except Exception as ex: # noqa
# TODO log message and undo noqa
# raises a weird key error for some reason
# unable to deregister from poller - <zmq.asyncio.Socket(zmq.PAIR) at 0x1c9e5028830> - KeyError
# unable to deregister from poller - <zmq.asyncio.Socket(zmq.PAIR) at 0x1c9e502a350> - KeyError
Expand Down Expand Up @@ -1091,7 +1091,8 @@ def recv_response(self, message_id: bytes) -> ResponseMessage:
finally:
try:
self._poller_lock.release()
except Exception as ex:
except Exception as ex: # noqa
# TODO log exception message and undo noqa
# 1. no need to release an unacquired lock, which can happen if another thread polling
# put the expected message in response message cache
# 2. also release the lock in every iteration because a message may be added in response cache
Expand Down Expand Up @@ -1706,7 +1707,7 @@ async def _resolve_response(self, message_id: str, data: typing.Any) -> None:
def assert_client_ready(self, client: AsyncZMQClient):
if not client._handshake_event.is_set():
raise ConnectionAbortedError(f"{client.id} is currently not alive")
if not client.socket in self.poller._map:
if client.socket not in self.poller._map:
raise ConnectionError(
"handshake complete, server is alive but client socket not yet ready to be polled."
+ "Application using MessageMappedClientPool should register the socket manually for polling."
Expand Down Expand Up @@ -2236,7 +2237,8 @@ def exit(self):
BaseZMQ.exit(self)
self.poller.unregister(self.socket)
self.poller.unregister(self.interruptor)
except Exception as ex:
except Exception as ex: # noqa
# TODO - log message and undo noqa
# self.logger.warning("could not properly terminate socket or attempted to terminate an already terminated socket of event consuming socket at address '{}'. Exception message: {}".format(
# self.socket_address, str(E)))
# above line prints too many warnings
Expand Down
6 changes: 3 additions & 3 deletions hololinked/core/zmq/rpc_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from collections import deque


from ...exceptions import *
from ...exceptions import BreakLoop, BreakInnerLoop
from ...constants import ZMQ_TRANSPORTS, Operations
from ...utils import (
format_exception_as_json,
Expand All @@ -31,9 +31,9 @@
)
from .brokers import AsyncZMQServer, BaseZMQServer, EventPublisher
from ..thing import Thing
from ..property import Property
from ..property import Property # noqa: F401
from ..properties import TypedDict
from ..actions import BoundAction
from ..actions import BoundAction # noqa: F401
from ..logger import LogHistoryHandler


Expand Down
4 changes: 2 additions & 2 deletions hololinked/schema_validators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .validators import BaseSchemaValidator, JSONSchemaValidator, PydanticSchemaValidator
from .json_schema import JSONSchema
from .validators import BaseSchemaValidator, JSONSchemaValidator, PydanticSchemaValidator # noqa: F401
from .json_schema import JSONSchema # noqa: F401
2 changes: 1 addition & 1 deletion hololinked/schema_validators/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,5 @@ def __get_state__(self):
def __set_state__(self, schema):
return FastJsonSchemaValidator(schema)

except ImportError as ex:
except ImportError:
pass
10 changes: 9 additions & 1 deletion hololinked/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
from .serializers import *
from .serializers import ( # noqa: F401
JSONSerializer,
PickleSerializer,
MsgpackSerializer,
TextSerializer,
PythonBuiltinJSONSerializer,
BaseSerializer,
Serializers,
)
2 changes: 1 addition & 1 deletion hololinked/serializers/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def register_content_type_for_object_per_thing_instance(
content_type: str
the content type to be used
"""
if not content_type in cls.content_types:
if content_type not in cls.content_types:
raise ValueError("content type {} unsupported".format(content_type))
from ..core import Property, Action, Event

Expand Down
2 changes: 1 addition & 1 deletion hololinked/server/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ def _comply_http_method(http_methods: typing.Any):
if not isinstance(http_methods, tuple):
raise TypeError("http_method should be a tuple")
for method in http_methods:
if method not in HTTP_METHODS.__members__.values() and not method is None:
if method not in HTTP_METHODS.__members__.values() and method is not None:
raise ValueError(f"method {method} not supported")
return http_methods

Expand Down
6 changes: 3 additions & 3 deletions hololinked/server/http/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import uuid
from tornado.web import RequestHandler, StaticFileHandler
from tornado.iostream import StreamClosedError

from msgspec import DecodeError as MsgspecJSONDecodeError

from ...utils import format_exception_as_json, run_callable_somehow
from ...config import global_config
Expand Down Expand Up @@ -153,14 +153,14 @@ def get_execution_parameters(
if len(value) == 1:
try:
arguments[key] = Serializers.json.loads(value[0])
except:
except MsgspecJSONDecodeError:
arguments[key] = value[0].decode("utf-8")
else:
final_value = []
for val in value:
try:
final_value.append(Serializers.json.loads(val))
except:
except MsgspecJSONDecodeError:
final_value.append(val.decode("utf-8"))
arguments[key] = final_value
thing_execution_context = ThingExecutionContext(
Expand Down
51 changes: 32 additions & 19 deletions hololinked/storage/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, MappedAsDataclass
from sqlite3 import DatabaseError
from pymongo import MongoClient, errors as mongo_errors
from ..param import Parameterized
from ..core.property import Property
from dataclasses import dataclass

from ..param import Parameterized
from ..core.property import Property
from ..constants import JSONSerializable
from ..config import global_config
from ..utils import pep8_to_dashed_name
from ..serializers.serializers import PythonBuiltinJSONSerializer as JSONSerializer, BaseSerializer, Serializers
from ..core.property import Property


class ThingTableBase(DeclarativeBase):
Expand Down Expand Up @@ -372,7 +370,7 @@ def set_properties(self, properties: typing.Dict[typing.Union[str, Property], ty
for obj, value in properties.items():
name = obj if isinstance(obj, str) else obj.name
db_prop = list(filter(lambda db_prop: db_prop.name == name, db_props)) # type: typing.List[SerializedProperty]
if len(prop) > 1:
if len(db_prop) > 1:
raise DatabaseError("multiple properties with same name found") # Impossible actually
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, name)
if len(db_prop) == 1:
Expand Down Expand Up @@ -460,20 +458,24 @@ def __exit__(self, exc_type, exc_value, exc_tb) -> None:
try:
self.db_engine.set_property(name, value)
except Exception as ex:
pass
self.db_engine.thing_instance.logger.error(
f"failed to set property {name} to value {value} during batch commit due to exception {ex}"
)


class MongoThingDB:
"""
MongoDB-backed database engine for Thing properties and info.

This class provides persistence for Thing properties using MongoDB.
Properties are stored in the 'properties' collection, with fields:
- id: Thing instance identifier
- name: property name
- serialized_value: serialized property value

Methods mirror the interface of ThingDB for compatibility.
"""

def __init__(self, instance: Parameterized, config_file: typing.Union[str, None] = None) -> None:
"""
Initialize MongoThingDB for a Thing instance.
Expand Down Expand Up @@ -530,12 +532,12 @@ def set_property(self, property: typing.Union[str, Property], value: typing.Any)
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, name)
serialized_value = base64.b64encode(serializer.dumps(value)).decode("utf-8")
self.properties.update_one(
{"id": self.id, "name": name},
{"$set": {"serialized_value": serialized_value}},
upsert=True
{"id": self.id, "name": name}, {"$set": {"serialized_value": serialized_value}}, upsert=True
)

def get_properties(self, properties: typing.Dict[typing.Union[str, Property], typing.Any], deserialized: bool = True) -> typing.Dict[str, typing.Any]:
def get_properties(
self, properties: typing.Dict[typing.Union[str, Property], typing.Any], deserialized: bool = True
) -> typing.Dict[str, typing.Any]:
"""
Get multiple property values from MongoDB for this Thing.
Returns a dict of property names to values.
Expand All @@ -545,7 +547,11 @@ def get_properties(self, properties: typing.Dict[typing.Union[str, Property], ty
result = {}
for doc in cursor:
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, doc["name"])
result[doc["name"]] = doc["serialized_value"] if not deserialized else serializer.loads(base64.b64decode(doc["serialized_value"]))
result[doc["name"]] = (
doc["serialized_value"]
if not deserialized
else serializer.loads(base64.b64decode(doc["serialized_value"]))
)
return result

def set_properties(self, properties: typing.Dict[typing.Union[str, Property], typing.Any]) -> None:
Expand All @@ -557,29 +563,36 @@ def set_properties(self, properties: typing.Dict[typing.Union[str, Property], ty
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, name)
serialized_value = base64.b64encode(serializer.dumps(value)).decode("utf-8")
self.properties.update_one(
{"id": self.id, "name": name},
{"$set": {"serialized_value": serialized_value}},
upsert=True
{"id": self.id, "name": name}, {"$set": {"serialized_value": serialized_value}}, upsert=True
)

def get_all_properties(self, deserialized: bool = True) -> typing.Dict[str, typing.Any]:
cursor = self.properties.find({"id": self.id})
result = {}
for doc in cursor:
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, doc["name"])
result[doc["name"]] = doc["serialized_value"] if not deserialized else serializer.loads(base64.b64decode(doc["serialized_value"]))
result[doc["name"]] = (
doc["serialized_value"]
if not deserialized
else serializer.loads(base64.b64decode(doc["serialized_value"]))
)
return result

def create_missing_properties(self, properties: typing.Dict[str, Property], get_missing_property_names: bool = False) -> typing.Any:
def create_missing_properties(
self, properties: typing.Dict[str, Property], get_missing_property_names: bool = False
) -> typing.Any:
missing_props = []
existing_props = self.get_all_properties()
for name, new_prop in properties.items():
if name not in existing_props:
serializer = Serializers.for_object(self.id, self.thing_instance.__class__.__name__, new_prop.name)
serialized_value = base64.b64encode(serializer.dumps(getattr(self.thing_instance, new_prop.name))).decode("utf-8")
serialized_value = base64.b64encode(
serializer.dumps(getattr(self.thing_instance, new_prop.name))
).decode("utf-8")
self.properties.insert_one({"id": self.id, "name": new_prop.name, "serialized_value": serialized_value})
missing_props.append(name)
if get_missing_property_names:
return missing_props



__all__ = [BaseAsyncDB.__name__, BaseSyncDB.__name__, ThingDB.__name__, batch_db_commit.__name__]
4 changes: 2 additions & 2 deletions hololinked/td/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .interaction_affordance import (
from .interaction_affordance import ( # noqa: F401
InteractionAffordance,
PropertyAffordance,
ActionAffordance,
EventAffordance,
)
from .tm import ThingModel
from .tm import ThingModel # noqa: F401
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,10 @@ filterwarnings = [
"ignore::UserWarning",
"ignore::DeprecationWarning",
"ignore::pytest.PytestCollectionWarning"
]

[tool.ruff]
exclude = [
"hololinked/core/properties.py",
"hololinked/param"
]
Loading