diff --git a/gridfs/asynchronous/grid_file.py b/gridfs/asynchronous/grid_file.py index 4d6140750..afc1a0f75 100644 --- a/gridfs/asynchronous/grid_file.py +++ b/gridfs/asynchronous/grid_file.py @@ -1176,20 +1176,22 @@ class AsyncGridIn: raise AttributeError("GridIn object has no attribute '%s'" % name) def __setattr__(self, name: str, value: Any) -> None: - if _IS_SYNC: - # For properties of this instance like _buffer, or descriptors set on - # the class like filename, use regular __setattr__ - if name in self.__dict__ or name in self.__class__.__dict__: - object.__setattr__(self, name, value) - else: + # For properties of this instance like _buffer, or descriptors set on + # the class like filename, use regular __setattr__ + if name in self.__dict__ or name in self.__class__.__dict__: + object.__setattr__(self, name, value) + else: + if _IS_SYNC: # All other attributes are part of the document in db.fs.files. # Store them to be sent to server on close() or if closed, send # them now. self._file[name] = value if self._closed: self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) - else: - object.__setattr__(self, name, value) + else: + raise AttributeError( + "AsyncGridIn does not support __setattr__. Use AsyncGridIn.set() instead" + ) async def set(self, name: str, value: Any) -> None: # For properties of this instance like _buffer, or descriptors set on @@ -1484,6 +1486,17 @@ class AsyncGridOut(io.IOBase): _file: Any _chunk_iter: Any + async def __anext__(self) -> bytes: + return super().__next__() + + def __next__(self) -> bytes: # noqa: F811, RUF100 + if _IS_SYNC: + return super().__next__() + else: + raise TypeError( + "AsyncGridOut does not support synchronous iteration. Use `async for` instead" + ) + async def open(self) -> None: if not self._file: _disallow_transactions(self._session) @@ -1511,6 +1524,7 @@ class AsyncGridOut(io.IOBase): """Reads a chunk at a time. If the current position is within a chunk the remainder of the chunk is returned. """ + await self.open() received = len(self._buffer) - self._buffer_pos chunk_data = EMPTY chunk_size = int(self.chunk_size) diff --git a/gridfs/synchronous/grid_file.py b/gridfs/synchronous/grid_file.py index bc2e29a61..80015f96e 100644 --- a/gridfs/synchronous/grid_file.py +++ b/gridfs/synchronous/grid_file.py @@ -1166,20 +1166,22 @@ class GridIn: raise AttributeError("GridIn object has no attribute '%s'" % name) def __setattr__(self, name: str, value: Any) -> None: - if _IS_SYNC: - # For properties of this instance like _buffer, or descriptors set on - # the class like filename, use regular __setattr__ - if name in self.__dict__ or name in self.__class__.__dict__: - object.__setattr__(self, name, value) - else: + # For properties of this instance like _buffer, or descriptors set on + # the class like filename, use regular __setattr__ + if name in self.__dict__ or name in self.__class__.__dict__: + object.__setattr__(self, name, value) + else: + if _IS_SYNC: # All other attributes are part of the document in db.fs.files. # Store them to be sent to server on close() or if closed, send # them now. self._file[name] = value if self._closed: self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) - else: - object.__setattr__(self, name, value) + else: + raise AttributeError( + "GridIn does not support __setattr__. Use GridIn.set() instead" + ) def set(self, name: str, value: Any) -> None: # For properties of this instance like _buffer, or descriptors set on @@ -1472,6 +1474,15 @@ class GridOut(io.IOBase): _file: Any _chunk_iter: Any + def __next__(self) -> bytes: + return super().__next__() + + def __next__(self) -> bytes: # noqa: F811, RUF100 + if _IS_SYNC: + return super().__next__() + else: + raise TypeError("GridOut does not support synchronous iteration. Use `for` instead") + def open(self) -> None: if not self._file: _disallow_transactions(self._session) @@ -1499,6 +1510,7 @@ class GridOut(io.IOBase): """Reads a chunk at a time. If the current position is within a chunk the remainder of the chunk is returned. """ + self.open() received = len(self._buffer) - self._buffer_pos chunk_data = EMPTY chunk_size = int(self.chunk_size) diff --git a/pymongo/asynchronous/encryption.py b/pymongo/asynchronous/encryption.py index 8b63525f2..93484541e 100644 --- a/pymongo/asynchronous/encryption.py +++ b/pymongo/asynchronous/encryption.py @@ -304,7 +304,7 @@ class _EncryptionIO(AsyncMongoCryptCallback): # type: ignore[misc] class RewrapManyDataKeyResult: - """Result object returned by a :meth:`~ClientEncryption.rewrap_many_data_key` operation. + """Result object returned by a :meth:`~AsyncClientEncryption.rewrap_many_data_key` operation. .. versionadded:: 4.2 """ @@ -316,7 +316,7 @@ class RewrapManyDataKeyResult: def bulk_write_result(self) -> Optional[BulkWriteResult]: """The result of the bulk write operation used to update the key vault collection with one or more rewrapped data keys. If - :meth:`~ClientEncryption.rewrap_many_data_key` does not find any matching keys to rewrap, + :meth:`~AsyncClientEncryption.rewrap_many_data_key` does not find any matching keys to rewrap, no bulk write operation will be executed and this field will be ``None``. """ @@ -506,7 +506,7 @@ def _create_mongocrypt_options(**kwargs: Any) -> MongoCryptOptions: return opts -class ClientEncryption(Generic[_DocumentType]): +class AsyncClientEncryption(Generic[_DocumentType]): """Explicit client-side field level encryption.""" def __init__( @@ -519,7 +519,7 @@ class ClientEncryption(Generic[_DocumentType]): ) -> None: """Explicit client-side field level encryption. - The ClientEncryption class encapsulates explicit operations on a key + The AsyncClientEncryption class encapsulates explicit operations on a key vault collection that cannot be done directly on an AsyncMongoClient. Similar to configuring auto encryption on an AsyncMongoClient, it is constructed with an AsyncMongoClient (to a MongoDB cluster containing the key vault @@ -1126,7 +1126,7 @@ class ClientEncryption(Generic[_DocumentType]): result = await self._key_vault_coll.bulk_write(replacements) return RewrapManyDataKeyResult(result) - async def __aenter__(self) -> ClientEncryption[_DocumentType]: + async def __aenter__(self) -> AsyncClientEncryption[_DocumentType]: return self async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: @@ -1134,7 +1134,7 @@ class ClientEncryption(Generic[_DocumentType]): def _check_closed(self) -> None: if self._encryption is None: - raise InvalidOperation("Cannot use closed ClientEncryption") + raise InvalidOperation("Cannot use closed AsyncClientEncryption") async def close(self) -> None: """Release resources. @@ -1142,7 +1142,7 @@ class ClientEncryption(Generic[_DocumentType]): Note that using this class in a with-statement will automatically call :meth:`close`:: - with ClientEncryption(...) as client_encryption: + with AsyncClientEncryption(...) as client_encryption: encrypted = client_encryption.encrypt(value, ...) decrypted = client_encryption.decrypt(encrypted) diff --git a/pymongo/encryption_options.py b/pymongo/encryption_options.py index 3b0d32a4b..df1302650 100644 --- a/pymongo/encryption_options.py +++ b/pymongo/encryption_options.py @@ -70,7 +70,7 @@ class AutoEncryptionOpts: users. To configure automatic *decryption* without automatic *encryption* set ``bypass_auto_encryption=True``. Explicit encryption and explicit decryption is also supported for all users - with the :class:`~pymongo.encryption.ClientEncryption` class. + with the :class:`~pymongo.asynchronous.encryption.AsyncClientEncryption` and :class:`~pymongo.encryption.ClientEncryption` classes. See :ref:`automatic-client-side-encryption` for an example. diff --git a/pyproject.toml b/pyproject.toml index 4380b57e8..8452bfe95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,7 +126,7 @@ module = ["service_identity.*"] ignore_missing_imports = true [[tool.mypy.overrides]] -module = ["pymongo.synchronous.*", "gridfs.synchronous.*"] +module = ["pymongo.synchronous.*"] warn_unused_ignores = false disable_error_code = ["unused-coroutine"] @@ -134,6 +134,10 @@ disable_error_code = ["unused-coroutine"] module = ["pymongo.asynchronous.*"] warn_unused_ignores = false +[[tool.mypy.overrides]] +module = ["gridfs.synchronous.*"] +warn_unused_ignores = false +disable_error_code = ["unused-coroutine", "no-redef"] [tool.ruff] target-version = "py37" diff --git a/tools/synchro.py b/tools/synchro.py index 0c2aff130..65ff3bfe0 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -58,6 +58,7 @@ replacements = { "_AsyncGridOutChunkIterator": "GridOutChunkIterator", "_a_grid_in_property": "_grid_in_property", "_a_grid_out_property": "_grid_out_property", + "AsyncClientEncryption": "ClientEncryption", "AsyncMongoCryptCallback": "MongoCryptCallback", "AsyncExplicitEncrypter": "ExplicitEncrypter", "AsyncAutoEncrypter": "AutoEncrypter",