Coverage for src/refcount/base.py: 100.00%
26 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-14 17:01 +1100
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-14 17:01 +1100
1"""Base classes for reference counting."""
3from typing import Any
6class ReferenceCounter:
7 """A base class for reference counters.
9 Attributes:
10 reference_count (int): property getter, reference count
11 """
13 def __init__(self, prior_ref_count: int = 0):
14 """Initialize this with an initial reference count.
16 Args:
17 prior_ref_count (int): the initial reference count. Default 0 if this object is sole responsible for the lifecycle of the resource.
18 """
19 self._ref_count: int = prior_ref_count + 1
21 @property
22 def reference_count(self) -> int:
23 """Get the current reference count."""
24 return self._ref_count
26 def add_ref(self) -> None:
27 """Manually increment the reference count.
29 Users usually have no need to call this method. They may have to if they
30 manage cases where one native handle wrapper uses another wrapper (and its underlying resource).
31 """
32 self._ref_count = self._ref_count + 1
34 def decrement_ref(self) -> None:
35 """Manually increment the reference count.
37 Users usually have no need to call this method. They may have to if they
38 manage cases where one native handle wrapper uses another wrapper (and its underlying resource).
39 """
40 self._ref_count = self._ref_count - 1
43class NativeHandle(ReferenceCounter):
44 """A base class for wrappers around otherwise "unmanaged" resources e.g. in a native library.
46 Attributes:
47 _handle (object): The handle (e.g. cffi pointer) to the native resource.
48 _finalizing (bool): a flag telling whether this object is in its deletion phase. This has a use in some advanced cases with reverse callback, possibly not relevant in Python.
49 """
51 def __init__(self, handle: Any = None, prior_ref_count: int = 0):
52 """Initialize a reference counter for a resource handle, with an initial reference count.
54 Args:
55 handle (object): The handle (e.g. cffi pointer) to the native resource.
56 prior_ref_count (int): the initial reference count. Default 0 if this NativeHandle is sole responsible for the lifecycle of the resource.
57 """
58 super().__init__(prior_ref_count)
59 # TODO checks
60 self._finalizing: bool = False
61 self._handle: Any = None
62 if handle is None:
63 return # defer setting handle to the inheritor.
64 self._set_handle(handle, prior_ref_count)
66 def _set_handle(self, handle: Any, prior_ref_count: int = 0) -> None:
67 """Sets a handle, after performing checks on its suitability as a handle for this object.
69 Args:
70 handle (object): The handle (e.g. cffi pointer) to the native resource.
71 prior_ref_count (int): the initial reference count. Default 0 if this NativeHandle is sole responsible for the lifecycle of the resource.
73 Raises:
74 error message when a handle is not a valid object.
75 """
76 if not self._is_valid_handle(handle):
77 raise RuntimeError("The specified handle argument is not a valid handle")
78 self._handle = handle
79 self._ref_count = prior_ref_count + 1
81 def _is_valid_handle(self, handle: Any) -> bool:
82 """Checks a handle on its suitability as a handle for this object.
84 This method must be overriden by the inheritors.
86 Args:
87 handle (object): The handle (e.g. cffi pointer) to the native resource.
88 """
89 # See also https://stackoverflow.com/questions/4714136/how-to-implement-virtual-methods-in-python
90 # May want to make this abstract using ABC - we'll see.
91 raise NotImplementedError("method _is_valid_handle must be overriden by child classes")