autonomous-app 0.3.0__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. autonomous/__init__.py +1 -1
  2. autonomous/ai/audioagent.py +1 -1
  3. autonomous/ai/imageagent.py +1 -1
  4. autonomous/ai/jsonagent.py +1 -1
  5. autonomous/ai/models/openai.py +81 -53
  6. autonomous/ai/oaiagent.py +1 -14
  7. autonomous/ai/textagent.py +1 -1
  8. autonomous/auth/autoauth.py +10 -10
  9. autonomous/auth/user.py +17 -2
  10. autonomous/db/__init__.py +42 -0
  11. autonomous/db/base/__init__.py +33 -0
  12. autonomous/db/base/common.py +62 -0
  13. autonomous/db/base/datastructures.py +476 -0
  14. autonomous/db/base/document.py +1230 -0
  15. autonomous/db/base/fields.py +767 -0
  16. autonomous/db/base/metaclasses.py +468 -0
  17. autonomous/db/base/utils.py +22 -0
  18. autonomous/db/common.py +79 -0
  19. autonomous/db/connection.py +472 -0
  20. autonomous/db/context_managers.py +313 -0
  21. autonomous/db/dereference.py +291 -0
  22. autonomous/db/document.py +1141 -0
  23. autonomous/db/errors.py +165 -0
  24. autonomous/db/fields.py +2732 -0
  25. autonomous/db/mongodb_support.py +24 -0
  26. autonomous/db/pymongo_support.py +80 -0
  27. autonomous/db/queryset/__init__.py +28 -0
  28. autonomous/db/queryset/base.py +2033 -0
  29. autonomous/db/queryset/field_list.py +88 -0
  30. autonomous/db/queryset/manager.py +58 -0
  31. autonomous/db/queryset/queryset.py +189 -0
  32. autonomous/db/queryset/transform.py +527 -0
  33. autonomous/db/queryset/visitor.py +189 -0
  34. autonomous/db/signals.py +59 -0
  35. autonomous/logger.py +3 -0
  36. autonomous/model/autoattr.py +56 -41
  37. autonomous/model/automodel.py +88 -34
  38. {autonomous_app-0.3.0.dist-info → autonomous_app-0.3.1.dist-info}/METADATA +2 -2
  39. autonomous_app-0.3.1.dist-info/RECORD +60 -0
  40. {autonomous_app-0.3.0.dist-info → autonomous_app-0.3.1.dist-info}/WHEEL +1 -1
  41. autonomous_app-0.3.0.dist-info/RECORD +0 -35
  42. {autonomous_app-0.3.0.dist-info → autonomous_app-0.3.1.dist-info}/LICENSE +0 -0
  43. {autonomous_app-0.3.0.dist-info → autonomous_app-0.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,472 @@
1
+ import warnings
2
+
3
+ from pymongo import MongoClient, ReadPreference, uri_parser
4
+ from pymongo.common import _UUID_REPRESENTATIONS
5
+ from pymongo.database import _check_name
6
+
7
+ # DriverInfo was added in PyMongo 3.7.
8
+ try:
9
+ from pymongo.driver_info import DriverInfo
10
+ except ImportError:
11
+ DriverInfo = None
12
+
13
+ import autonomous.db
14
+ from autonomous.db.pymongo_support import PYMONGO_VERSION
15
+
16
+ __all__ = [
17
+ "DEFAULT_CONNECTION_NAME",
18
+ "DEFAULT_DATABASE_NAME",
19
+ "ConnectionFailure",
20
+ "connect",
21
+ "disconnect",
22
+ "disconnect_all",
23
+ "get_connection",
24
+ "get_db",
25
+ "register_connection",
26
+ ]
27
+
28
+
29
+ DEFAULT_CONNECTION_NAME = "default"
30
+ DEFAULT_DATABASE_NAME = "test"
31
+ DEFAULT_HOST = "localhost"
32
+ DEFAULT_PORT = 27017
33
+
34
+ _connection_settings = {}
35
+ _connections = {}
36
+ _dbs = {}
37
+
38
+ READ_PREFERENCE = ReadPreference.PRIMARY
39
+
40
+
41
+ class ConnectionFailure(Exception):
42
+ """Error raised when the database connection can't be established or
43
+ when a connection with a requested alias can't be retrieved.
44
+ """
45
+
46
+ pass
47
+
48
+
49
+ def _check_db_name(name):
50
+ """Check if a database name is valid.
51
+ This functionality is copied from pymongo Database class constructor.
52
+ """
53
+ if not isinstance(name, str):
54
+ raise TypeError("name must be an instance of %s" % str)
55
+ elif name != "$external":
56
+ _check_name(name)
57
+
58
+
59
+ def _get_connection_settings(
60
+ db=None,
61
+ name=None,
62
+ host=None,
63
+ port=None,
64
+ read_preference=READ_PREFERENCE,
65
+ username=None,
66
+ password=None,
67
+ authentication_source=None,
68
+ authentication_mechanism=None,
69
+ authmechanismproperties=None,
70
+ **kwargs,
71
+ ):
72
+ """Get the connection settings as a dict
73
+
74
+ :param db: the name of the database to use, for compatibility with connect
75
+ :param name: the name of the specific database to use
76
+ :param host: the host name of the: program: `mongod` instance to connect to
77
+ :param port: the port that the: program: `mongod` instance is running on
78
+ :param read_preference: The read preference for the collection
79
+ :param username: username to authenticate with
80
+ :param password: password to authenticate with
81
+ :param authentication_source: database to authenticate against
82
+ :param authentication_mechanism: database authentication mechanisms.
83
+ By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
84
+ MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
85
+ :param mongo_client_class: using alternative connection client other than
86
+ pymongo.MongoClient, e.g. mongomock, montydb, that provides pymongo alike
87
+ interface but not necessarily for connecting to a real mongo instance.
88
+ :param kwargs: ad-hoc parameters to be passed into the pymongo driver,
89
+ for example maxpoolsize, tz_aware, etc. See the documentation
90
+ for pymongo's `MongoClient` for a full list.
91
+ """
92
+ conn_settings = {
93
+ "name": name or db or DEFAULT_DATABASE_NAME,
94
+ "host": host or DEFAULT_HOST,
95
+ "port": port or DEFAULT_PORT,
96
+ "read_preference": read_preference,
97
+ "username": username,
98
+ "password": password,
99
+ "authentication_source": authentication_source,
100
+ "authentication_mechanism": authentication_mechanism,
101
+ "authmechanismproperties": authmechanismproperties,
102
+ }
103
+
104
+ _check_db_name(conn_settings["name"])
105
+ conn_host = conn_settings["host"]
106
+
107
+ # Host can be a list or a string, so if string, force to a list.
108
+ if isinstance(conn_host, str):
109
+ conn_host = [conn_host]
110
+
111
+ resolved_hosts = []
112
+ for entity in conn_host:
113
+ # Reject old mongomock integration
114
+ # To be removed in a few versions after 0.27.0
115
+ if entity.startswith("mongomock://") or kwargs.get("is_mock"):
116
+ raise Exception(
117
+ "Use of mongomock:// URI or 'is_mock' were removed in favor of 'mongo_client_class=mongomock.MongoClient'. "
118
+ "Check the CHANGELOG for more info"
119
+ )
120
+
121
+ # Handle URI style connections, only updating connection params which
122
+ # were explicitly specified in the URI.
123
+ if "://" in entity:
124
+ uri_dict = uri_parser.parse_uri(entity)
125
+ resolved_hosts.append(entity)
126
+
127
+ database = uri_dict.get("database")
128
+ if database:
129
+ conn_settings["name"] = database
130
+
131
+ for param in ("read_preference", "username", "password"):
132
+ if uri_dict.get(param):
133
+ conn_settings[param] = uri_dict[param]
134
+
135
+ uri_options = uri_dict[
136
+ "options"
137
+ ] # uri_options is a _CaseInsensitiveDictionary
138
+ if "replicaset" in uri_options:
139
+ conn_settings["replicaSet"] = uri_options["replicaset"]
140
+ if "authsource" in uri_options:
141
+ conn_settings["authentication_source"] = uri_options["authsource"]
142
+ if "authmechanism" in uri_options:
143
+ conn_settings["authentication_mechanism"] = uri_options["authmechanism"]
144
+ if "readpreference" in uri_options:
145
+ read_preferences = (
146
+ ReadPreference.NEAREST,
147
+ ReadPreference.PRIMARY,
148
+ ReadPreference.PRIMARY_PREFERRED,
149
+ ReadPreference.SECONDARY,
150
+ ReadPreference.SECONDARY_PREFERRED,
151
+ )
152
+
153
+ # Starting with PyMongo v3.5, the "readpreference" option is
154
+ # returned as a string (e.g. "secondaryPreferred") and not an
155
+ # int (e.g. 3).
156
+ # TODO simplify the code below once we drop support for
157
+ # PyMongo v3.4.
158
+ read_pf_mode = uri_options["readpreference"]
159
+ if isinstance(read_pf_mode, str):
160
+ read_pf_mode = read_pf_mode.lower()
161
+ for preference in read_preferences:
162
+ if (
163
+ preference.name.lower() == read_pf_mode
164
+ or preference.mode == read_pf_mode
165
+ ):
166
+ ReadPrefClass = preference.__class__
167
+ break
168
+
169
+ if "readpreferencetags" in uri_options:
170
+ conn_settings["read_preference"] = ReadPrefClass(
171
+ tag_sets=uri_options["readpreferencetags"]
172
+ )
173
+ else:
174
+ conn_settings["read_preference"] = ReadPrefClass()
175
+
176
+ if "authmechanismproperties" in uri_options:
177
+ conn_settings["authmechanismproperties"] = uri_options[
178
+ "authmechanismproperties"
179
+ ]
180
+ if "uuidrepresentation" in uri_options:
181
+ REV_UUID_REPRESENTATIONS = {
182
+ v: k for k, v in _UUID_REPRESENTATIONS.items()
183
+ }
184
+ conn_settings["uuidrepresentation"] = REV_UUID_REPRESENTATIONS[
185
+ uri_options["uuidrepresentation"]
186
+ ]
187
+ else:
188
+ resolved_hosts.append(entity)
189
+ conn_settings["host"] = resolved_hosts
190
+
191
+ # Deprecated parameters that should not be passed on
192
+ kwargs.pop("slaves", None)
193
+ kwargs.pop("is_slave", None)
194
+
195
+ keys = {
196
+ key.lower() for key in kwargs.keys()
197
+ } # pymongo options are case insensitive
198
+ if "uuidrepresentation" not in keys and "uuidrepresentation" not in conn_settings:
199
+ warnings.warn(
200
+ "No uuidRepresentation is specified! Falling back to "
201
+ "'pythonLegacy' which is the default for pymongo 3.x. "
202
+ "For compatibility with other MongoDB drivers this should be "
203
+ "specified as 'standard' or '{java,csharp}Legacy' to work with "
204
+ "older drivers in those languages. This will be changed to "
205
+ "'unspecified' in a future release.",
206
+ DeprecationWarning,
207
+ )
208
+ kwargs["uuidRepresentation"] = "pythonLegacy"
209
+
210
+ conn_settings.update(kwargs)
211
+ return conn_settings
212
+
213
+
214
+ def register_connection(
215
+ alias,
216
+ db=None,
217
+ name=None,
218
+ host=None,
219
+ port=None,
220
+ read_preference=READ_PREFERENCE,
221
+ username=None,
222
+ password=None,
223
+ authentication_source=None,
224
+ authentication_mechanism=None,
225
+ authmechanismproperties=None,
226
+ **kwargs,
227
+ ):
228
+ """Register the connection settings.
229
+
230
+ :param alias: the name that will be used to refer to this connection throughout MongoEngine
231
+ :param db: the name of the database to use, for compatibility with connect
232
+ :param name: the name of the specific database to use
233
+ :param host: the host name of the: program: `mongod` instance to connect to
234
+ :param port: the port that the: program: `mongod` instance is running on
235
+ :param read_preference: The read preference for the collection
236
+ :param username: username to authenticate with
237
+ :param password: password to authenticate with
238
+ :param authentication_source: database to authenticate against
239
+ :param authentication_mechanism: database authentication mechanisms.
240
+ By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
241
+ MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
242
+ :param mongo_client_class: using alternative connection client other than
243
+ pymongo.MongoClient, e.g. mongomock, montydb, that provides pymongo alike
244
+ interface but not necessarily for connecting to a real mongo instance.
245
+ :param kwargs: ad-hoc parameters to be passed into the pymongo driver,
246
+ for example maxpoolsize, tz_aware, etc. See the documentation
247
+ for pymongo's `MongoClient` for a full list.
248
+ """
249
+ conn_settings = _get_connection_settings(
250
+ db=db,
251
+ name=name,
252
+ host=host,
253
+ port=port,
254
+ read_preference=read_preference,
255
+ username=username,
256
+ password=password,
257
+ authentication_source=authentication_source,
258
+ authentication_mechanism=authentication_mechanism,
259
+ authmechanismproperties=authmechanismproperties,
260
+ **kwargs,
261
+ )
262
+ _connection_settings[alias] = conn_settings
263
+
264
+
265
+ def disconnect(alias=DEFAULT_CONNECTION_NAME):
266
+ """Close the connection with a given alias."""
267
+ from autonomous.db import Document
268
+ from autonomous.db.base.common import _get_documents_by_db
269
+
270
+ connection = _connections.pop(alias, None)
271
+ if connection:
272
+ # MongoEngine may share the same MongoClient across multiple aliases
273
+ # if connection settings are the same so we only close
274
+ # the client if we're removing the final reference.
275
+ # Important to use 'is' instead of '==' because clients connected to the same cluster
276
+ # will compare equal even with different options
277
+ if all(connection is not c for c in _connections.values()):
278
+ connection.close()
279
+
280
+ if alias in _dbs:
281
+ # Detach all cached collections in Documents
282
+ for doc_cls in _get_documents_by_db(alias, DEFAULT_CONNECTION_NAME):
283
+ if issubclass(doc_cls, Document): # Skip EmbeddedDocument
284
+ doc_cls._disconnect()
285
+
286
+ del _dbs[alias]
287
+
288
+ if alias in _connection_settings:
289
+ del _connection_settings[alias]
290
+
291
+
292
+ def disconnect_all():
293
+ """Close all registered database."""
294
+ for alias in list(_connections.keys()):
295
+ disconnect(alias)
296
+
297
+
298
+ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
299
+ """Return a connection with a given alias."""
300
+
301
+ # Connect to the database if not already connected
302
+ if reconnect:
303
+ disconnect(alias)
304
+
305
+ # If the requested alias already exists in the _connections list, return
306
+ # it immediately.
307
+ if alias in _connections:
308
+ return _connections[alias]
309
+
310
+ # Validate that the requested alias exists in the _connection_settings.
311
+ # Raise ConnectionFailure if it doesn't.
312
+ if alias not in _connection_settings:
313
+ if alias == DEFAULT_CONNECTION_NAME:
314
+ msg = "You have not defined a default connection"
315
+ else:
316
+ msg = 'Connection with alias "%s" has not been defined' % alias
317
+ raise ConnectionFailure(msg)
318
+
319
+ def _clean_settings(settings_dict):
320
+ if PYMONGO_VERSION < (4,):
321
+ irrelevant_fields_set = {
322
+ "name",
323
+ "username",
324
+ "password",
325
+ "authentication_source",
326
+ "authentication_mechanism",
327
+ "authmechanismproperties",
328
+ }
329
+ rename_fields = {}
330
+ else:
331
+ irrelevant_fields_set = {"name"}
332
+ rename_fields = {
333
+ "authentication_source": "authSource",
334
+ "authentication_mechanism": "authMechanism",
335
+ }
336
+ return {
337
+ rename_fields.get(k, k): v
338
+ for k, v in settings_dict.items()
339
+ if k not in irrelevant_fields_set and v is not None
340
+ }
341
+
342
+ raw_conn_settings = _connection_settings[alias].copy()
343
+
344
+ # Retrieve a copy of the connection settings associated with the requested
345
+ # alias and remove the database name and authentication info (we don't
346
+ # care about them at this point).
347
+ conn_settings = _clean_settings(raw_conn_settings)
348
+ if DriverInfo is not None:
349
+ conn_settings.setdefault(
350
+ "driver", DriverInfo("MongoEngine", autonomous.db.__version__)
351
+ )
352
+
353
+ # Determine if we should use PyMongo's or mongomock's MongoClient.
354
+ if "mongo_client_class" in conn_settings:
355
+ mongo_client_class = conn_settings.pop("mongo_client_class")
356
+ else:
357
+ mongo_client_class = MongoClient
358
+
359
+ # Re-use existing connection if one is suitable.
360
+ existing_connection = _find_existing_connection(raw_conn_settings)
361
+ if existing_connection:
362
+ connection = existing_connection
363
+ else:
364
+ connection = _create_connection(
365
+ alias=alias, mongo_client_class=mongo_client_class, **conn_settings
366
+ )
367
+ _connections[alias] = connection
368
+ return _connections[alias]
369
+
370
+
371
+ def _create_connection(alias, mongo_client_class, **connection_settings):
372
+ """
373
+ Create the new connection for this alias. Raise
374
+ ConnectionFailure if it can't be established.
375
+ """
376
+ try:
377
+ return mongo_client_class(**connection_settings)
378
+ except Exception as e:
379
+ raise ConnectionFailure(f"Cannot connect to database {alias} :\n{e}")
380
+
381
+
382
+ def _find_existing_connection(connection_settings):
383
+ """
384
+ Check if an existing connection could be reused
385
+
386
+ Iterate over all of the connection settings and if an existing connection
387
+ with the same parameters is suitable, return it
388
+
389
+ :param connection_settings: the settings of the new connection
390
+ :return: An existing connection or None
391
+ """
392
+ connection_settings_bis = (
393
+ (db_alias, settings.copy())
394
+ for db_alias, settings in _connection_settings.items()
395
+ )
396
+
397
+ def _clean_settings(settings_dict):
398
+ # Only remove the name but it's important to
399
+ # keep the username/password/authentication_source/authentication_mechanism
400
+ # to identify if the connection could be shared (cfr https://github.com/MongoEngine/autonomous.db/issues/2047)
401
+ return {k: v for k, v in settings_dict.items() if k != "name"}
402
+
403
+ cleaned_conn_settings = _clean_settings(connection_settings)
404
+ for db_alias, connection_settings in connection_settings_bis:
405
+ db_conn_settings = _clean_settings(connection_settings)
406
+ if cleaned_conn_settings == db_conn_settings and _connections.get(db_alias):
407
+ return _connections[db_alias]
408
+
409
+
410
+ def get_db(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
411
+ if reconnect:
412
+ disconnect(alias)
413
+
414
+ if alias not in _dbs:
415
+ conn = get_connection(alias)
416
+ conn_settings = _connection_settings[alias]
417
+ db = conn[conn_settings["name"]]
418
+ # Authenticate if necessary
419
+ if (
420
+ PYMONGO_VERSION < (4,)
421
+ and conn_settings["username"]
422
+ and (
423
+ conn_settings["password"]
424
+ or conn_settings["authentication_mechanism"] == "MONGODB-X509"
425
+ )
426
+ and conn_settings["authmechanismproperties"] is None
427
+ ):
428
+ auth_kwargs = {"source": conn_settings["authentication_source"]}
429
+ if conn_settings["authentication_mechanism"] is not None:
430
+ auth_kwargs["mechanism"] = conn_settings["authentication_mechanism"]
431
+ db.authenticate(
432
+ conn_settings["username"], conn_settings["password"], **auth_kwargs
433
+ )
434
+ _dbs[alias] = db
435
+ return _dbs[alias]
436
+
437
+
438
+ def connect(db=None, alias=DEFAULT_CONNECTION_NAME, **kwargs):
439
+ """Connect to the database specified by the 'db' argument.
440
+
441
+ Connection settings may be provided here as well if the database is not
442
+ running on the default port on localhost. If authentication is needed,
443
+ provide username and password arguments as well.
444
+
445
+ Multiple databases are supported by using aliases. Provide a separate
446
+ `alias` to connect to a different instance of: program: `mongod`.
447
+
448
+ In order to replace a connection identified by a given alias, you'll
449
+ need to call ``disconnect`` first
450
+
451
+ See the docstring for `register_connection` for more details about all
452
+ supported kwargs.
453
+ """
454
+ if alias in _connections:
455
+ prev_conn_setting = _connection_settings[alias]
456
+ new_conn_settings = _get_connection_settings(db, **kwargs)
457
+
458
+ if new_conn_settings != prev_conn_setting:
459
+ err_msg = (
460
+ "A different connection with alias `{}` was already "
461
+ "registered. Use disconnect() first"
462
+ ).format(alias)
463
+ raise ConnectionFailure(err_msg)
464
+ else:
465
+ register_connection(alias, db, **kwargs)
466
+
467
+ return get_connection(alias)
468
+
469
+
470
+ # Support old naming convention
471
+ _get_connection = get_connection
472
+ _get_db = get_db