zulip/zerver/lib/notes.py

55 lines
1.8 KiB
Python

import weakref
from abc import ABC, abstractmethod
from collections.abc import MutableMapping
from typing import Any, ClassVar, Generic, TypeVar
from typing_extensions import override
_KeyT = TypeVar("_KeyT")
_DataT = TypeVar("_DataT")
class BaseNotes(Generic[_KeyT, _DataT], ABC):
"""This class defines a generic type-safe mechanism for associating
additional data with an object (without modifying the original
object via subclassing or monkey-patching).
It was originally designed to avoid monkey-patching the Django
HttpRequest object, to which we want to associate computed state
(e.g. parsed state computed from the User-Agent) so that it's
available in code paths that receive the HttpRequest object.
The implementation uses a WeakKeyDictionary, so that the notes
object will be garbage-collected when the original object no
longer has other references (avoiding memory leaks).
We still need to be careful to avoid any of the attributes of
_DataT having points to the original object, as that can create a
cyclic reference cycle that the Python garbage collect may not
handle correctly.
"""
__notes_map: ClassVar[MutableMapping[Any, Any]]
@override
def __init_subclass__(cls, **kwargs: object) -> None:
super().__init_subclass__(**kwargs)
if not hasattr(cls, "__notes_map"):
cls.__notes_map = weakref.WeakKeyDictionary()
@classmethod
def get_notes(cls, key: _KeyT) -> _DataT:
try:
return cls.__notes_map[key]
except KeyError:
cls.__notes_map[key] = cls.init_notes()
return cls.__notes_map[key]
@classmethod
def set_notes(cls, key: _KeyT, notes: _DataT) -> None:
cls.__notes_map[key] = notes
@classmethod
@abstractmethod
def init_notes(cls) -> _DataT: ...