4444
4545from pymongo .errors import (ConfigurationError ,
4646 EncryptionError ,
47+ InvalidOperation ,
4748 ServerSelectionTimeoutError )
4849from pymongo .mongo_client import MongoClient
4950from pymongo .pool import _configured_socket , PoolOptions
@@ -387,7 +388,6 @@ def __init__(self, kms_providers, key_vault_namespace, key_vault_client,
387388 self ._encryption = ExplicitEncrypter (
388389 self ._io_callbacks , MongoCryptOptions (kms_providers , None ))
389390
390- @_wrap_encryption_errors
391391 def create_data_key (self , kms_provider , master_key = None ,
392392 key_alt_names = None ):
393393 """Create and insert a new data key into the key vault collection.
@@ -419,8 +419,11 @@ def create_data_key(self, kms_provider, master_key=None,
419419 :Returns:
420420 The ``_id`` of the created data key document.
421421 """
422- return self ._encryption .create_data_key (
423- kms_provider , master_key = master_key , key_alt_names = key_alt_names )
422+ self ._check_closed ()
423+ with _wrap_encryption_errors_ctx ():
424+ return self ._encryption .create_data_key (
425+ kms_provider , master_key = master_key ,
426+ key_alt_names = key_alt_names )
424427
425428 def encrypt (self , value , algorithm , key_id = None , key_alt_name = None ):
426429 """Encrypt a BSON value with a given key and algorithm.
@@ -440,6 +443,7 @@ def encrypt(self, value, algorithm, key_id=None, key_alt_name=None):
440443 :Returns:
441444 The encrypted value, a :class:`~bson.binary.Binary` with subtype 6.
442445 """
446+ self ._check_closed ()
443447 if (key_id is not None and not (
444448 isinstance (key_id , Binary ) and
445449 key_id .subtype == UUID_SUBTYPE )):
@@ -462,6 +466,7 @@ def decrypt(self, value):
462466 :Returns:
463467 The decrypted BSON value.
464468 """
469+ self ._check_closed ()
465470 if not (isinstance (value , Binary ) and value .subtype == 6 ):
466471 raise TypeError (
467472 'value to decrypt must be a bson.binary.Binary with subtype 6' )
@@ -472,9 +477,29 @@ def decrypt(self, value):
472477 return decode (decrypted_doc ,
473478 codec_options = self ._codec_options )['v' ]
474479
480+ def __enter__ (self ):
481+ return self
482+
483+ def __exit__ (self , exc_type , exc_val , exc_tb ):
484+ self .close ()
485+
486+ def _check_closed (self ):
487+ if self ._encryption is None :
488+ raise InvalidOperation ("Cannot use closed ClientEncryption" )
489+
475490 def close (self ):
476- """Release resources."""
477- self ._io_callbacks .close ()
478- self ._encryption .close ()
479- self ._io_callbacks = None
480- self ._encryption = None
491+ """Release resources.
492+
493+ Note that using this class in a with-statement will automatically call
494+ :meth:`close`::
495+
496+ with ClientEncryption(...) as client_encryption:
497+ encrypted = client_encryption.encrypt(value, ...)
498+ decrypted = client_encryption.decrypt(encrypted)
499+
500+ """
501+ if self ._io_callbacks :
502+ self ._io_callbacks .close ()
503+ self ._encryption .close ()
504+ self ._io_callbacks = None
505+ self ._encryption = None
0 commit comments