From 662d47ac217e71f7a7f2313f56632efeab4035d6 Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Mon, 5 Jan 2026 19:53:52 +0100 Subject: [PATCH] A handler can only be registered once --- README.md | 3 ++- pyproject.toml | 2 +- src/dbengine/handlers.py | 29 ++++++++++++++++++----------- tests/test_handlers.py | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 tests/test_handlers.py diff --git a/README.md b/README.md index 89ae91e..83b296f 100644 --- a/README.md +++ b/README.md @@ -349,4 +349,5 @@ See LICENSE file for details. ## Version History * 0.1.0 - Initial release -* 0.2.0 - Added custom reference handlers \ No newline at end of file +* 0.2.0 - Added custom reference handlers +* 0.2.1 - A handler can only be registered once \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 23d1e3c..542b10f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mydbengine" -version = "0.2.0" +version = "0.2.1" description = "A lightweight, git-inspired database engine that maintains complete history of all modifications" readme = "README.md" requires-python = ">=3.8" diff --git a/src/dbengine/handlers.py b/src/dbengine/handlers.py index 22d82c5..072cc1a 100644 --- a/src/dbengine/handlers.py +++ b/src/dbengine/handlers.py @@ -9,11 +9,11 @@ TAG_SPECIAL = "__special__" class BaseHandler: """Base class for all handlers. Subclasses must implement is_eligible_for() and tag().""" - + def is_eligible_for(self, obj): """Check if this handler can process the given object.""" raise NotImplementedError - + def tag(self): """Return a unique tag identifying this handler type.""" raise NotImplementedError @@ -21,11 +21,11 @@ class BaseHandler: class BaseInlineHandler(BaseHandler): """Handler that serializes data directly into the JSON snapshot.""" - + def serialize(self, obj) -> dict: """Serialize object to dict for inline storage in JSON snapshot.""" raise NotImplementedError - + def deserialize(self, data: dict) -> object: """Deserialize object from dict stored in JSON snapshot.""" raise NotImplementedError @@ -33,15 +33,15 @@ class BaseInlineHandler(BaseHandler): class BaseRefHandler(BaseHandler): """Handler that serializes data to refs/ directory using content-addressable storage.""" - + def serialize_to_bytes(self, obj) -> bytes: """Serialize object to bytes for storage in refs/. Must be implemented by subclass.""" raise NotImplementedError - + def deserialize_from_bytes(self, data: bytes) -> object: """Deserialize object from bytes loaded from refs/. Must be implemented by subclass.""" raise NotImplementedError - + def serialize(self, obj, ref_helper) -> dict: """ 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 TAG_SPECIAL = "__special__" return { - TAG_SPECIAL: self.tag(), - TAG_DIGEST: digest + TAG_SPECIAL: self.tag(), + TAG_DIGEST: digest } - + def deserialize(self, data: dict, ref_helper) -> object: """ Default implementation: loads bytes from refs/ using digest, deserializes from bytes. @@ -89,7 +89,7 @@ class DateHandler(BaseInlineHandler): class Handlers: def __init__(self, handlers_): - self.handlers = handlers_ + self.handlers = handlers_ or [] def get_handler(self, obj): if has_tag(obj, TAG_SPECIAL): @@ -102,7 +102,14 @@ class Handlers: return None 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) + + def unregister_handler(self, handler): + self.handlers = [h for h in self.handlers if type(h) != type(handler)] handlers = Handlers([DateHandler()]) diff --git a/tests/test_handlers.py b/tests/test_handlers.py new file mode 100644 index 0000000..3455536 --- /dev/null +++ b/tests/test_handlers.py @@ -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