Skip to content

Commit 2ebe738

Browse files
committed
PYCBC-1745: Use C++ Templating System for SDK C-Extension
Motivation ---------- Moving the client's c-extension to utilize C++ templating will help prepare for future changes and continue to improve the maintainability of the Couchbase Python client. Changes ------- * Use C++ templates to reduce the execution paths so maintenance becomes more straight forward (e.g. 50+ paths to ~10) * Use C++ templates to better map between Python and C++ objects * Namespace the code in the extension to pcybc * Clean up transactions logic - New new template system makes for easier creation of config/options objects - Cleaned up how we add txns objects (PYCBC-1686) * Clean up exception handling * Help to set the stage for the SDK to fully support static typing * Improved handling of the couchbase API’s multi operations - Added tests to confirm durability options cannot be mixed * Move c-extension shared object to reside in couchbase/logic/pycbc_core to help with static typing and imports Change-Id: Id672f13f9273fa0c2ff1b3698f7950375a383445 Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/240534 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com>
1 parent c740dd4 commit 2ebe738

184 files changed

Lines changed: 25063 additions & 23615 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,6 @@ file(
249249
GLOB
250250
SOURCE_FILES
251251
"src/*.cxx"
252-
"src/management/*.cxx"
253252
"src/transactions/*.cxx")
254253
add_library(pycbc_core SHARED ${SOURCE_FILES})
255254

@@ -301,5 +300,5 @@ endif()
301300
set_target_properties(
302301
pycbc_core
303302
PROPERTIES PREFIX ""
304-
OUTPUT_NAME pycbc_core
303+
OUTPUT_NAME _core
305304
SUFFIX ${PYCBC_C_MOD_SUFFIX})

acouchbase/analytics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
CouchbaseException,
2323
ErrorMapper,
2424
ExceptionMap)
25-
from couchbase.exceptions import exception as CouchbaseBaseException
2625
from couchbase.logic.analytics import AnalyticsQuery # noqa: F401
2726
from couchbase.logic.analytics import AnalyticsRequestLogic
27+
from couchbase.logic.pycbc_core import pycbc_exception as PycbcCoreException
2828

2929

3030
class AsyncAnalyticsRequest(AnalyticsRequestLogic):
@@ -82,7 +82,7 @@ def _get_next_row(self):
8282
return
8383

8484
row = next(self._streaming_result)
85-
if isinstance(row, CouchbaseBaseException):
85+
if isinstance(row, PycbcCoreException):
8686
raise ErrorMapper.build_exception(row)
8787
# should only be None one query request is complete and _no_ errors found
8888
if row is None:

acouchbase/collection.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,8 @@ def scan(self,
10301030
10311031
10321032
""" # noqa: E501
1033-
req = self._impl.request_builder.build_range_scan_async_request(scan_type, *opts, **kwargs)
1033+
req = self._impl.request_builder.build_range_scan_async_request(
1034+
self._impl.connection, scan_type, *opts, **kwargs)
10341035
return self._impl.range_scan(req)
10351036

10361037
def binary(self) -> BinaryCollection:

acouchbase/kv_range_scan.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15-
#
15+
16+
from __future__ import annotations
1617

1718
import asyncio
1819
from concurrent.futures import ThreadPoolExecutor
20+
from typing import TYPE_CHECKING, Any
1921

2022
from couchbase.exceptions import (PYCBC_ERROR_MAP,
2123
AlreadyQueriedException,
@@ -24,11 +26,14 @@
2426
RangeScanCompletedException)
2527
from couchbase.logic.kv_range_scan import RangeScanRequestLogic
2628

29+
if TYPE_CHECKING:
30+
from couchbase.logic.pycbc_core import pycbc_connection
31+
2732

2833
class AsyncRangeScanRequest(RangeScanRequestLogic):
29-
def __init__(self, loop: asyncio.AbstractEventLoop, **kwargs: object) -> None:
34+
def __init__(self, connection: pycbc_connection, loop: asyncio.AbstractEventLoop, **kwargs: Any) -> None:
3035
num_workers = kwargs.pop('num_workers', 2)
31-
super().__init__(**kwargs)
36+
super().__init__(connection, **kwargs)
3237
self._loop = loop
3338
self._result_ftr = None
3439
self._tp_executor = ThreadPoolExecutor(num_workers)

acouchbase/logic/bucket_impl.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616
from __future__ import annotations
1717

1818
from asyncio import AbstractEventLoop, Future
19-
from typing import (TYPE_CHECKING,
20-
Optional,
21-
Union)
19+
from typing import TYPE_CHECKING, Union
2220

2321
from acouchbase.views import AsyncViewRequest
2422
from couchbase.logic.bucket_req_builder import BucketRequestBuilder
25-
from couchbase.logic.client_adapter import PyCapsuleType
2623
from couchbase.logic.cluster_settings import ClusterSettings, StreamingTimeouts
24+
from couchbase.logic.pycbc_core import pycbc_connection
2725
from couchbase.result import PingResult, ViewResult
2826

2927
if TYPE_CHECKING:
@@ -68,7 +66,7 @@ def connected(self) -> bool:
6866
return self._client_adapter.connected
6967

7068
@property
71-
def connection(self) -> Optional[PyCapsuleType]:
69+
def connection(self) -> pycbc_connection:
7270
"""**INTERNAL**"""
7371
return self._client_adapter.connection
7472

acouchbase/logic/client_adapter.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
ErrorMapper,
3030
ExceptionMap,
3131
InternalSDKException)
32-
from couchbase.exceptions import exception as BaseCouchbaseException
3332
from couchbase.logic.binding_map import BindingMap
3433
from couchbase.logic.bucket_types import CloseBucketRequest, OpenBucketRequest
3534
from couchbase.logic.cluster_types import CloseConnectionRequest
36-
from couchbase.logic.top_level_types import OpenOrCloseBucket, PyCapsuleType
35+
from couchbase.logic.pycbc_core import pycbc_connection
36+
from couchbase.logic.pycbc_core import pycbc_exception as PycbcCoreException
3737

3838
if TYPE_CHECKING:
3939
from asyncio import AbstractEventLoop
@@ -51,24 +51,25 @@ def __init__(self,
5151
connect_req: CreateConnectionRequest,
5252
loop_validator: Optional[Callable[[Optional[AbstractEventLoop]], AbstractEventLoop]] = None
5353
) -> None:
54-
self._connection: Optional[PyCapsuleType] = None
54+
num_io_threads = connect_req.options.get('num_io_threads', None)
55+
self._connection = pycbc_connection(num_io_threads) if num_io_threads is not None else pycbc_connection()
5556
if loop_validator:
5657
self._loop = loop_validator(loop)
5758
else:
5859
self._loop = self._get_loop(loop)
5960
self._connect_req = connect_req
60-
self._binding_map = BindingMap()
61+
self._binding_map = BindingMap(self._connection)
6162
self._close_ft: Optional[Future[None]] = None
6263
self._connect_ft: Optional[Future[None]] = None
6364
self._closed = False
6465
self._create_connection()
6566

6667
@property
6768
def connected(self) -> bool:
68-
return self._connection is not None
69+
return self._connection is not None and self._connection.connected
6970

7071
@property
71-
def connection(self) -> Optional[PyCapsuleType]:
72+
def connection(self) -> pycbc_connection:
7273
return self._connection
7374

7475
@property
@@ -109,12 +110,12 @@ def _errback(exc) -> None:
109110
excptn = ErrorMapper.build_exception(exc)
110111
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
111112

112-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
113+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
113114
self._execute_req(ft, req.op_name, req_dict)
114115
return ft
115116

116117
def execute_close_bucket_request(self, bucket_name: str) -> Future[None]:
117-
req = CloseBucketRequest(bucket_name, OpenOrCloseBucket.CLOSE)
118+
req = CloseBucketRequest(bucket_name)
118119
return self.execute_bucket_request(req)
119120

120121
def execute_cluster_request(self, req: ClusterRequest) -> Future[Any]:
@@ -129,7 +130,7 @@ def _errback(exc) -> None:
129130
excptn = ErrorMapper.build_exception(exc)
130131
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
131132

132-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
133+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
133134
if not self.connected:
134135
chained_ft = self._execute_connect_request() if self._connect_ft is None else self._connect_ft
135136
chained_ft.add_done_callback(partial(self._execute_chained_req, ft, req.op_name, req_dict))
@@ -139,9 +140,9 @@ def _errback(exc) -> None:
139140

140141
def execute_cluster_request_sync(self, req: ClusterRequest) -> Any:
141142
self._ensure_not_closed()
142-
req_dict = req.req_to_dict(self._connection)
143+
req_dict = req.req_to_dict()
143144
ret = self._execute_req_sync(req.op_name, req_dict)
144-
if isinstance(ret, BaseCouchbaseException):
145+
if isinstance(ret, PycbcCoreException):
145146
raise ErrorMapper.build_exception(ret)
146147
return ret
147148

@@ -158,14 +159,14 @@ def _errback(exc) -> None:
158159
excptn = ErrorMapper.build_exception(exc)
159160
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
160161

161-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
162+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
162163
self._execute_req(ft, req.op_name, req_dict)
163164
return ft
164165

165166
def execute_connect_bucket_request(self, bucket_name: str) -> Future[None]:
166167
self._ensure_not_closed()
167168

168-
req = OpenBucketRequest(bucket_name, OpenOrCloseBucket.OPEN)
169+
req = OpenBucketRequest(bucket_name)
169170
ft = self._loop.create_future()
170171

171172
def _callback(_) -> None:
@@ -177,7 +178,7 @@ def _errback(ret: Any) -> None:
177178
if not ft.done():
178179
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
179180

180-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
181+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
181182
if not self.connected:
182183
chained_ft = self._execute_connect_request() if self._connect_ft is None else self._connect_ft
183184
chained_ft.add_done_callback(partial(self._execute_chained_req, ft, req.op_name, req_dict))
@@ -199,7 +200,7 @@ def _errback(ret: Any) -> None:
199200
if not ft.done():
200201
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
201202

202-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
203+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
203204
if not self.connected:
204205
chained_ft = self._execute_connect_request() if self._connect_ft is None else self._connect_ft
205206
chained_ft.add_done_callback(partial(self._execute_chained_req, ft, req.op_name, req_dict))
@@ -239,7 +240,6 @@ def _execute_chained_req(self,
239240
ft.set_exception(exc)
240241
return
241242

242-
req_dict['conn'] = self._connection
243243
self._execute_req(ft, op_name, req_dict)
244244

245245
def _execute_close_connection_request(self) -> Future[None]:
@@ -256,7 +256,7 @@ def _errback(ret: Any) -> None:
256256
if not ft.done():
257257
self._loop.call_soon_threadsafe(ft.set_exception, excptn)
258258

259-
req_dict = req.req_to_dict(self._connection, callback=_callback, errback=_errback)
259+
req_dict = req.req_to_dict(callback=_callback, errback=_errback)
260260
if not self.connected:
261261
# If we're closed, don't try to reconnect just to close again
262262
if self._closed:
@@ -272,9 +272,8 @@ def _errback(ret: Any) -> None:
272272
def _execute_connect_request(self) -> Future[None]:
273273
ft = self._loop.create_future()
274274

275-
def _callback(ret: PyCapsuleType) -> None:
275+
def _callback(_) -> None:
276276
if not ft.done():
277-
self._connection = ret
278277
self._loop.call_soon_threadsafe(ft.set_result, None)
279278

280279
def _errback(ret: Any) -> None:

acouchbase/logic/cluster_impl.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from couchbase.logic.cluster_impl import ClusterSettings
3232
from couchbase.logic.cluster_req_builder import ClusterRequestBuilder
3333
from couchbase.logic.cluster_types import CreateConnectionRequest, GetConnectionInfoRequest
34-
from couchbase.logic.top_level_types import PyCapsuleType
34+
from couchbase.logic.pycbc_core import pycbc_connection
3535
from couchbase.result import (AnalyticsResult,
3636
ClusterInfoResult,
3737
DiagnosticsResult,
@@ -88,7 +88,7 @@ def connected(self) -> bool:
8888
return self._client_adapter.connected
8989

9090
@property
91-
def connection(self) -> Optional[PyCapsuleType]:
91+
def connection(self) -> pycbc_connection:
9292
"""**INTERNAL**"""
9393
return self._client_adapter.connection
9494

0 commit comments

Comments
 (0)