A handler can only be registered once

This commit is contained in:
2026-01-05 19:53:52 +01:00
parent 618e21e012
commit 662d47ac21
4 changed files with 37 additions and 13 deletions

View File

@@ -349,4 +349,5 @@ See LICENSE file for details.
## Version History ## Version History
* 0.1.0 - Initial release * 0.1.0 - Initial release
* 0.2.0 - Added custom reference handlers * 0.2.0 - Added custom reference handlers
* 0.2.1 - A handler can only be registered once

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "mydbengine" name = "mydbengine"
version = "0.2.0" version = "0.2.1"
description = "A lightweight, git-inspired database engine that maintains complete history of all modifications" description = "A lightweight, git-inspired database engine that maintains complete history of all modifications"
readme = "README.md" readme = "README.md"
requires-python = ">=3.8" requires-python = ">=3.8"

View File

@@ -9,11 +9,11 @@ TAG_SPECIAL = "__special__"
class BaseHandler: class BaseHandler:
"""Base class for all handlers. Subclasses must implement is_eligible_for() and tag().""" """Base class for all handlers. Subclasses must implement is_eligible_for() and tag()."""
def is_eligible_for(self, obj): def is_eligible_for(self, obj):
"""Check if this handler can process the given object.""" """Check if this handler can process the given object."""
raise NotImplementedError raise NotImplementedError
def tag(self): def tag(self):
"""Return a unique tag identifying this handler type.""" """Return a unique tag identifying this handler type."""
raise NotImplementedError raise NotImplementedError
@@ -21,11 +21,11 @@ class BaseHandler:
class BaseInlineHandler(BaseHandler): class BaseInlineHandler(BaseHandler):
"""Handler that serializes data directly into the JSON snapshot.""" """Handler that serializes data directly into the JSON snapshot."""
def serialize(self, obj) -> dict: def serialize(self, obj) -> dict:
"""Serialize object to dict for inline storage in JSON snapshot.""" """Serialize object to dict for inline storage in JSON snapshot."""
raise NotImplementedError raise NotImplementedError
def deserialize(self, data: dict) -> object: def deserialize(self, data: dict) -> object:
"""Deserialize object from dict stored in JSON snapshot.""" """Deserialize object from dict stored in JSON snapshot."""
raise NotImplementedError raise NotImplementedError
@@ -33,15 +33,15 @@ class BaseInlineHandler(BaseHandler):
class BaseRefHandler(BaseHandler): class BaseRefHandler(BaseHandler):
"""Handler that serializes data to refs/ directory using content-addressable storage.""" """Handler that serializes data to refs/ directory using content-addressable storage."""
def serialize_to_bytes(self, obj) -> bytes: def serialize_to_bytes(self, obj) -> bytes:
"""Serialize object to bytes for storage in refs/. Must be implemented by subclass.""" """Serialize object to bytes for storage in refs/. Must be implemented by subclass."""
raise NotImplementedError raise NotImplementedError
def deserialize_from_bytes(self, data: bytes) -> object: def deserialize_from_bytes(self, data: bytes) -> object:
"""Deserialize object from bytes loaded from refs/. Must be implemented by subclass.""" """Deserialize object from bytes loaded from refs/. Must be implemented by subclass."""
raise NotImplementedError raise NotImplementedError
def serialize(self, obj, ref_helper) -> dict: def serialize(self, obj, ref_helper) -> dict:
""" """
Default implementation: converts object to bytes, saves to refs/, returns dict with tag and digest. Default implementation: converts object to bytes, saves to refs/, returns dict with tag and digest.
@@ -52,10 +52,10 @@ class BaseRefHandler(BaseHandler):
from dbengine.serializer import TAG_DIGEST from dbengine.serializer import TAG_DIGEST
TAG_SPECIAL = "__special__" TAG_SPECIAL = "__special__"
return { return {
TAG_SPECIAL: self.tag(), TAG_SPECIAL: self.tag(),
TAG_DIGEST: digest TAG_DIGEST: digest
} }
def deserialize(self, data: dict, ref_helper) -> object: def deserialize(self, data: dict, ref_helper) -> object:
""" """
Default implementation: loads bytes from refs/ using digest, deserializes from bytes. Default implementation: loads bytes from refs/ using digest, deserializes from bytes.
@@ -89,7 +89,7 @@ class DateHandler(BaseInlineHandler):
class Handlers: class Handlers:
def __init__(self, handlers_): def __init__(self, handlers_):
self.handlers = handlers_ self.handlers = handlers_ or []
def get_handler(self, obj): def get_handler(self, obj):
if has_tag(obj, TAG_SPECIAL): if has_tag(obj, TAG_SPECIAL):
@@ -102,7 +102,14 @@ class Handlers:
return None return None
def register_handler(self, handler): def register_handler(self, handler):
for h in self.handlers:
if type(h) == type(handler):
return # won't register twice the same handler
self.handlers.append(handler) self.handlers.append(handler)
def unregister_handler(self, handler):
self.handlers = [h for h in self.handlers if type(h) != type(handler)]
handlers = Handlers([DateHandler()]) handlers = Handlers([DateHandler()])

16
tests/test_handlers.py Normal file
View File

@@ -0,0 +1,16 @@
from dbengine.handlers import BaseHandler, handlers
class DummyHandler(BaseHandler):
pass
def test_i_can_register_only_once():
handlers.register_handler(DummyHandler())
count = len(handlers.handlers)
handlers.register_handler(DummyHandler()) # second time is skipped as the class is already registered
assert count == len(handlers.handlers)
handlers.unregister_handler(DummyHandler()) # clean the handlers list
assert len(handlers.handlers) == count - 1