|
4 | 4 | import time |
5 | 5 | import warnings |
6 | 6 | from asyncio import iscoroutinefunction |
| 7 | +from contextvars import ContextVar |
7 | 8 | from functools import wraps |
8 | 9 | from itertools import combinations |
9 | | -from threading import local |
10 | 10 | from typing import Any, Callable, Optional, TextIO, Union |
11 | 11 | from urllib.parse import quote, unquote, urlparse |
12 | 12 |
|
@@ -83,25 +83,123 @@ async def wrapper(self: Any, *args: Any, **kwargs: Any) -> Callable: |
83 | 83 | return wrapper |
84 | 84 |
|
85 | 85 |
|
86 | | -class AsyncDatabase(local): |
| 86 | +class AsyncDatabase: |
87 | 87 | """ |
88 | 88 | A singleton object via which all operations from neomodel to the Neo4j backend are handled with. |
89 | 89 | """ |
90 | 90 |
|
| 91 | + # Shared global registries |
91 | 92 | _NODE_CLASS_REGISTRY: dict[frozenset, Any] = {} |
92 | 93 | _DB_SPECIFIC_CLASS_REGISTRY: dict[str, dict[frozenset, Any]] = {} |
93 | 94 |
|
| 95 | + @property |
| 96 | + def _active_transaction(self) -> Optional[AsyncTransaction]: |
| 97 | + return self.__active_transaction.get() |
| 98 | + |
| 99 | + @_active_transaction.setter |
| 100 | + def _active_transaction(self, value: AsyncTransaction) -> None: |
| 101 | + self.__active_transaction.set(value) |
| 102 | + |
| 103 | + @property |
| 104 | + def url(self) -> Optional[str]: |
| 105 | + return self.__url.get() |
| 106 | + |
| 107 | + @url.setter |
| 108 | + def url(self, value: str) -> None: |
| 109 | + self.__url.set(value) |
| 110 | + |
| 111 | + @property |
| 112 | + def driver(self) -> Optional[AsyncDriver]: |
| 113 | + return self.__driver.get() |
| 114 | + |
| 115 | + @driver.setter |
| 116 | + def driver(self, value: AsyncDriver) -> None: |
| 117 | + self.__driver.set(value) |
| 118 | + |
| 119 | + @property |
| 120 | + def _session(self) -> Optional[AsyncSession]: |
| 121 | + return self.__session.get() |
| 122 | + |
| 123 | + @_session.setter |
| 124 | + def _session(self, value: AsyncSession) -> None: |
| 125 | + self.__session.set(value) |
| 126 | + |
| 127 | + @property |
| 128 | + def _pid(self) -> Optional[int]: |
| 129 | + return self.__pid.get() |
| 130 | + |
| 131 | + @_pid.setter |
| 132 | + def _pid(self, value: int) -> None: |
| 133 | + self.__pid.set(value) |
| 134 | + |
| 135 | + @property |
| 136 | + def _database_name(self) -> Optional[str]: |
| 137 | + return self.__database_name.get() |
| 138 | + |
| 139 | + @_database_name.setter |
| 140 | + def _database_name(self, value: str) -> None: |
| 141 | + self.__database_name.set(value) |
| 142 | + |
| 143 | + @property |
| 144 | + def _database_version(self) -> Optional[str]: |
| 145 | + return self.__database_version.get() |
| 146 | + |
| 147 | + @_database_version.setter |
| 148 | + def _database_version(self, value: str) -> None: |
| 149 | + self.__database_version.set(value) |
| 150 | + |
| 151 | + @property |
| 152 | + def _database_edition(self) -> Optional[str]: |
| 153 | + return self.__database_edition.get() |
| 154 | + |
| 155 | + @_database_edition.setter |
| 156 | + def _database_edition(self, value: str) -> None: |
| 157 | + self.__database_edition.set(value) |
| 158 | + |
| 159 | + @property |
| 160 | + def impersonated_user(self) -> Optional[str]: |
| 161 | + return self.__impersonated_user.get() |
| 162 | + |
| 163 | + @impersonated_user.setter |
| 164 | + def impersonated_user(self, value: str) -> None: |
| 165 | + self.__impersonated_user.set(value) |
| 166 | + |
| 167 | + @property |
| 168 | + def _parallel_runtime(self) -> Optional[bool]: |
| 169 | + return self.__parallel_runtime.get() |
| 170 | + |
| 171 | + @_parallel_runtime.setter |
| 172 | + def _parallel_runtime(self, value: bool) -> None: |
| 173 | + self.__parallel_runtime.set(value) |
| 174 | + |
94 | 175 | def __init__(self) -> None: |
95 | | - self._active_transaction: Optional[AsyncTransaction] = None |
96 | | - self.url: Optional[str] = None |
97 | | - self.driver: Optional[AsyncDriver] = None |
98 | | - self._session: Optional[AsyncSession] = None |
99 | | - self._pid: Optional[int] = None |
100 | | - self._database_name: Optional[str] = DEFAULT_DATABASE |
101 | | - self._database_version: Optional[str] = None |
102 | | - self._database_edition: Optional[str] = None |
103 | | - self.impersonated_user: Optional[str] = None |
104 | | - self._parallel_runtime: Optional[bool] = False |
| 176 | + # Private to instances and contexts |
| 177 | + self.__active_transaction: ContextVar[Optional[AsyncTransaction]] = ContextVar( |
| 178 | + "_active_transaction", default=None |
| 179 | + ) |
| 180 | + self.__url: ContextVar[Optional[str]] = ContextVar("url", default=None) |
| 181 | + self.__driver: ContextVar[Optional[AsyncDriver]] = ContextVar( |
| 182 | + "driver", default=None |
| 183 | + ) |
| 184 | + self.__session: ContextVar[Optional[AsyncSession]] = ContextVar( |
| 185 | + "_session", default=None |
| 186 | + ) |
| 187 | + self.__pid: ContextVar[Optional[int]] = ContextVar("_pid", default=None) |
| 188 | + self.__database_name: ContextVar[Optional[str]] = ContextVar( |
| 189 | + "_database_name", default=DEFAULT_DATABASE |
| 190 | + ) |
| 191 | + self.__database_version: ContextVar[Optional[str]] = ContextVar( |
| 192 | + "_database_version", default=None |
| 193 | + ) |
| 194 | + self.__database_edition: ContextVar[Optional[str]] = ContextVar( |
| 195 | + "_database_edition", default=None |
| 196 | + ) |
| 197 | + self.__impersonated_user: ContextVar[Optional[str]] = ContextVar( |
| 198 | + "impersonated_user", default=None |
| 199 | + ) |
| 200 | + self.__parallel_runtime: ContextVar[Optional[bool]] = ContextVar( |
| 201 | + "_parallel_runtime", default=False |
| 202 | + ) |
105 | 203 |
|
106 | 204 | async def set_connection( |
107 | 205 | self, url: Optional[str] = None, driver: Optional[AsyncDriver] = None |
|
0 commit comments