1# Copyright 2020-present MongoDB, Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you 4# may not use this file except in compliance with the License. You 5# may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12# implied. See the License for the specific language governing 13# permissions and limitations under the License. 14 15"""Support for MongoDB Versioned API. 16 17.. _versioned-api-ref: 18 19MongoDB Versioned API 20===================== 21 22Starting in MongoDB 5.0, applications can specify the server API version 23to use when creating a :class:`~pymongo.mongo_client.MongoClient`. Doing so 24ensures that the driver behaves in a manner compatible with that server API 25version, regardless of the server's actual release version. 26 27Declaring an API Version 28```````````````````````` 29 30.. attention:: Versioned API requires MongoDB >=5.0. 31 32To configure MongoDB Versioned API, pass the ``server_api`` keyword option to 33:class:`~pymongo.mongo_client.MongoClient`:: 34 35 >>> from pymongo.mongo_client import MongoClient 36 >>> from pymongo.server_api import ServerApi 37 >>> 38 >>> # Declare API version "1" for MongoClient "client" 39 >>> server_api = ServerApi('1') 40 >>> client = MongoClient(server_api=server_api) 41 42The declared API version is applied to all commands run through ``client``, 43including those sent through the generic 44:meth:`~pymongo.database.Database.command` helper. 45 46.. note:: Declaring an API version on the 47 :class:`~pymongo.mongo_client.MongoClient` **and** specifying versioned 48 API options in :meth:`~pymongo.database.Database.command` command document 49 is not supported and will lead to undefined behaviour. 50 51To run any command without declaring a server API version or using a different 52API version, create a separate :class:`~pymongo.mongo_client.MongoClient` 53instance. 54 55Strict Mode 56``````````` 57 58Configuring ``strict`` mode will cause the MongoDB server to reject all 59commands that are not part of the declared :attr:`ServerApi.version`. This 60includes command options and aggregation pipeline stages. 61 62For example:: 63 64 >>> server_api = ServerApi('1', strict=True) 65 >>> client = MongoClient(server_api=server_api) 66 >>> client.test.command('count', 'test') 67 Traceback (most recent call last): 68 ... 69 pymongo.errors.OperationFailure: Provided apiStrict:true, but the command count is not in API Version 1, full error: {'ok': 0.0, 'errmsg': 'Provided apiStrict:true, but the command count is not in API Version 1', 'code': 323, 'codeName': 'APIStrictError' 70 71Detecting API Deprecations 72`````````````````````````` 73 74The ``deprecationErrors`` option can be used to enable command failures 75when using functionality that is deprecated from the configured 76:attr:`ServerApi.version`. For example:: 77 78 >>> server_api = ServerApi('1', deprecation_errors=True) 79 >>> client = MongoClient(server_api=server_api) 80 81Note that at the time of this writing, no deprecated APIs exist. 82 83Classes 84======= 85""" 86 87 88class ServerApiVersion: 89 """An enum that defines values for :attr:`ServerApi.version`. 90 91 .. versionadded:: 3.12 92 """ 93 94 V1 = "1" 95 """Server API version "1".""" 96 97 98class ServerApi(object): 99 """MongoDB Versioned API.""" 100 def __init__(self, version, strict=None, deprecation_errors=None): 101 """Options to configure MongoDB Versioned API. 102 103 :Parameters: 104 - `version`: The API version string. Must be one of the values in 105 :class:`ServerApiVersion`. 106 - `strict` (optional): Set to ``True`` to enable API strict mode. 107 Defaults to ``None`` which means "use the server's default". 108 - `deprecation_errors` (optional): Set to ``True`` to enable 109 deprecation errors. Defaults to ``None`` which means "use the 110 server's default". 111 112 .. versionadded:: 3.12 113 """ 114 if version != ServerApiVersion.V1: 115 raise ValueError("Unknown ServerApi version: %s" % (version,)) 116 if strict is not None and not isinstance(strict, bool): 117 raise TypeError( 118 "Wrong type for ServerApi strict, value must be an instance " 119 "of bool, not %s" % (type(strict),)) 120 if (deprecation_errors is not None and 121 not isinstance(deprecation_errors, bool)): 122 raise TypeError( 123 "Wrong type for ServerApi deprecation_errors, value must be " 124 "an instance of bool, not %s" % (type(deprecation_errors),)) 125 self._version = version 126 self._strict = strict 127 self._deprecation_errors = deprecation_errors 128 129 @property 130 def version(self): 131 """The API version setting. 132 133 This value is sent to the server in the "apiVersion" field. 134 """ 135 return self._version 136 137 @property 138 def strict(self): 139 """The API strict mode setting. 140 141 When set, this value is sent to the server in the "apiStrict" field. 142 """ 143 return self._strict 144 145 @property 146 def deprecation_errors(self): 147 """The API deprecation errors setting. 148 149 When set, this value is sent to the server in the 150 "apiDeprecationErrors" field. 151 """ 152 return self._deprecation_errors 153 154 155def _add_to_command(cmd, server_api): 156 """Internal helper which adds API versioning options to a command. 157 158 :Parameters: 159 - `cmd`: The command. 160 - `server_api` (optional): A :class:`ServerApi` or ``None``. 161 """ 162 if not server_api: 163 return 164 cmd['apiVersion'] = server_api.version 165 if server_api.strict is not None: 166 cmd['apiStrict'] = server_api.strict 167 if server_api.deprecation_errors is not None: 168 cmd['apiDeprecationErrors'] = server_api.deprecation_errors 169