autonomous-app 0.2.25__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 (59) hide show
  1. autonomous/__init__.py +5 -2
  2. autonomous/ai/audioagent.py +32 -0
  3. autonomous/ai/imageagent.py +31 -0
  4. autonomous/ai/jsonagent.py +40 -0
  5. autonomous/ai/models/__init__.py +0 -0
  6. autonomous/ai/models/openai.py +308 -0
  7. autonomous/ai/oaiagent.py +20 -194
  8. autonomous/ai/textagent.py +35 -0
  9. autonomous/auth/autoauth.py +11 -11
  10. autonomous/auth/user.py +24 -11
  11. autonomous/db/__init__.py +41 -0
  12. autonomous/db/base/__init__.py +33 -0
  13. autonomous/db/base/common.py +62 -0
  14. autonomous/db/base/datastructures.py +476 -0
  15. autonomous/db/base/document.py +1230 -0
  16. autonomous/db/base/fields.py +767 -0
  17. autonomous/db/base/metaclasses.py +468 -0
  18. autonomous/db/base/utils.py +22 -0
  19. autonomous/db/common.py +79 -0
  20. autonomous/db/connection.py +472 -0
  21. autonomous/db/context_managers.py +313 -0
  22. autonomous/db/dereference.py +291 -0
  23. autonomous/db/document.py +1141 -0
  24. autonomous/db/errors.py +165 -0
  25. autonomous/db/fields.py +2732 -0
  26. autonomous/db/mongodb_support.py +24 -0
  27. autonomous/db/pymongo_support.py +80 -0
  28. autonomous/db/queryset/__init__.py +28 -0
  29. autonomous/db/queryset/base.py +2033 -0
  30. autonomous/db/queryset/field_list.py +88 -0
  31. autonomous/db/queryset/manager.py +58 -0
  32. autonomous/db/queryset/queryset.py +189 -0
  33. autonomous/db/queryset/transform.py +527 -0
  34. autonomous/db/queryset/visitor.py +189 -0
  35. autonomous/db/signals.py +59 -0
  36. autonomous/logger.py +3 -0
  37. autonomous/model/autoattr.py +120 -0
  38. autonomous/model/automodel.py +121 -308
  39. autonomous/storage/imagestorage.py +9 -54
  40. autonomous/tasks/autotask.py +0 -25
  41. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/METADATA +7 -8
  42. autonomous_app-0.3.1.dist-info/RECORD +60 -0
  43. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/WHEEL +1 -1
  44. autonomous/db/autodb.py +0 -86
  45. autonomous/db/table.py +0 -156
  46. autonomous/errors/__init__.py +0 -1
  47. autonomous/errors/danglingreferenceerror.py +0 -8
  48. autonomous/model/autoattribute.py +0 -20
  49. autonomous/model/orm.py +0 -86
  50. autonomous/model/serializer.py +0 -110
  51. autonomous_app-0.2.25.dist-info/RECORD +0 -36
  52. /autonomous/{storage → apis}/version_control/GHCallbacks.py +0 -0
  53. /autonomous/{storage → apis}/version_control/GHOrganization.py +0 -0
  54. /autonomous/{storage → apis}/version_control/GHRepo.py +0 -0
  55. /autonomous/{storage → apis}/version_control/GHVersionControl.py +0 -0
  56. /autonomous/{storage → apis}/version_control/__init__.py +0 -0
  57. /autonomous/{storage → utils}/markdown.py +0 -0
  58. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/LICENSE +0 -0
  59. {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,476 @@
1
+ import weakref
2
+
3
+ from bson import DBRef
4
+
5
+ from autonomous import log
6
+ from autonomous.db.common import _import_class
7
+ from autonomous.db.errors import DoesNotExist, MultipleObjectsReturned
8
+
9
+ __all__ = (
10
+ "BaseDict",
11
+ "StrictDict",
12
+ "BaseList",
13
+ "EmbeddedDocumentList",
14
+ "LazyReference",
15
+ )
16
+
17
+
18
+ def mark_as_changed_wrapper(parent_method):
19
+ """Decorator that ensures _mark_as_changed method gets called."""
20
+
21
+ def wrapper(self, *args, **kwargs):
22
+ # Can't use super() in the decorator.
23
+ # log(args, kwargs)
24
+ result = parent_method(self, *args, **kwargs)
25
+ self._mark_as_changed()
26
+ return result
27
+
28
+ return wrapper
29
+
30
+
31
+ def mark_key_as_changed_wrapper(parent_method):
32
+ """Decorator that ensures _mark_as_changed method gets called with the key argument"""
33
+
34
+ def wrapper(self, key, *args, **kwargs):
35
+ # Can't use super() in the decorator.
36
+ if not args or key not in self or self[key] != args[0]:
37
+ self._mark_as_changed(key)
38
+ return parent_method(self, key, *args, **kwargs)
39
+
40
+ return wrapper
41
+
42
+
43
+ class BaseDict(dict):
44
+ """A special dict so we can watch any changes."""
45
+
46
+ _dereferenced = False
47
+ _instance = None
48
+ _name = None
49
+
50
+ def __init__(self, dict_items, instance, name):
51
+ BaseDocument = _import_class("BaseDocument")
52
+
53
+ if isinstance(instance, BaseDocument):
54
+ self._instance = weakref.proxy(instance)
55
+ self._name = name
56
+ super().__init__(dict_items)
57
+
58
+ def get(self, key, default=None):
59
+ # get does not use __getitem__ by default so we must override it as well
60
+ try:
61
+ return self.__getitem__(key)
62
+ except KeyError:
63
+ return default
64
+
65
+ def __getitem__(self, key):
66
+ value = super().__getitem__(key)
67
+
68
+ EmbeddedDocument = _import_class("EmbeddedDocument")
69
+ if isinstance(value, EmbeddedDocument) and value._instance is None:
70
+ value._instance = self._instance
71
+ elif isinstance(value, dict) and not isinstance(value, BaseDict):
72
+ value = BaseDict(value, None, f"{self._name}.{key}")
73
+ super().__setitem__(key, value)
74
+ value._instance = self._instance
75
+ elif isinstance(value, list) and not isinstance(value, BaseList):
76
+ value = BaseList(value, None, f"{self._name}.{key}")
77
+ super().__setitem__(key, value)
78
+ value._instance = self._instance
79
+ return value
80
+
81
+ def __getstate__(self):
82
+ self.instance = None
83
+ self._dereferenced = False
84
+ return self
85
+
86
+ def __setstate__(self, state):
87
+ self = state
88
+ return self
89
+
90
+ __setitem__ = mark_key_as_changed_wrapper(dict.__setitem__)
91
+ __delattr__ = mark_key_as_changed_wrapper(dict.__delattr__)
92
+ __delitem__ = mark_key_as_changed_wrapper(dict.__delitem__)
93
+ pop = mark_as_changed_wrapper(dict.pop)
94
+ clear = mark_as_changed_wrapper(dict.clear)
95
+ update = mark_as_changed_wrapper(dict.update)
96
+ popitem = mark_as_changed_wrapper(dict.popitem)
97
+ setdefault = mark_as_changed_wrapper(dict.setdefault)
98
+
99
+ def _mark_as_changed(self, key=None):
100
+ if hasattr(self._instance, "_mark_as_changed"):
101
+ if key:
102
+ self._instance._mark_as_changed(f"{self._name}.{key}")
103
+ else:
104
+ self._instance._mark_as_changed(self._name)
105
+
106
+
107
+ class BaseList(list):
108
+ """A special list so we can watch any changes."""
109
+
110
+ _dereferenced = False
111
+ _instance = None
112
+ _name = None
113
+
114
+ def __init__(self, list_items, instance, name):
115
+ BaseDocument = _import_class("BaseDocument")
116
+
117
+ if isinstance(instance, BaseDocument):
118
+ if isinstance(instance, weakref.ProxyTypes):
119
+ self._instance = instance
120
+ else:
121
+ self._instance = weakref.proxy(instance)
122
+
123
+ self._name = name
124
+ super().__init__(list_items)
125
+
126
+ def __getitem__(self, key):
127
+ # change index to positive value because MongoDB does not support negative one
128
+ if isinstance(key, int) and key < 0:
129
+ key = len(self) + key
130
+ value = super().__getitem__(key)
131
+
132
+ if isinstance(key, slice):
133
+ # When receiving a slice operator, we don't convert the structure and bind
134
+ # to parent's instance. This is buggy for now but would require more work to be handled properly
135
+ return value
136
+
137
+ EmbeddedDocument = _import_class("EmbeddedDocument")
138
+ if isinstance(value, EmbeddedDocument) and value._instance is None:
139
+ value._instance = self._instance
140
+ elif isinstance(value, dict) and not isinstance(value, BaseDict):
141
+ # Replace dict by BaseDict
142
+ value = BaseDict(value, None, f"{self._name}.{key}")
143
+ super().__setitem__(key, value)
144
+ value._instance = self._instance
145
+ elif isinstance(value, list) and not isinstance(value, BaseList):
146
+ # Replace list by BaseList
147
+ value = BaseList(value, None, f"{self._name}.{key}")
148
+ super().__setitem__(key, value)
149
+ value._instance = self._instance
150
+ return value
151
+
152
+ def __iter__(self):
153
+ yield from super().__iter__()
154
+
155
+ def __getstate__(self):
156
+ self.instance = None
157
+ self._dereferenced = False
158
+ return self
159
+
160
+ def __setstate__(self, state):
161
+ self = state
162
+ return self
163
+
164
+ def __setitem__(self, key, value):
165
+ # log(key, value)
166
+ changed_key = key
167
+ if isinstance(key, slice):
168
+ # In case of slice, we don't bother to identify the exact elements being updated
169
+ # instead, we simply marks the whole list as changed
170
+ changed_key = None
171
+
172
+ result = super().__setitem__(key, value)
173
+ self._mark_as_changed(changed_key)
174
+ return result
175
+
176
+ append = mark_as_changed_wrapper(list.append)
177
+ extend = mark_as_changed_wrapper(list.extend)
178
+ insert = mark_as_changed_wrapper(list.insert)
179
+ pop = mark_as_changed_wrapper(list.pop)
180
+ remove = mark_as_changed_wrapper(list.remove)
181
+ reverse = mark_as_changed_wrapper(list.reverse)
182
+ sort = mark_as_changed_wrapper(list.sort)
183
+ __delitem__ = mark_as_changed_wrapper(list.__delitem__)
184
+ __iadd__ = mark_as_changed_wrapper(list.__iadd__)
185
+ __imul__ = mark_as_changed_wrapper(list.__imul__)
186
+
187
+ def _mark_as_changed(self, key=None):
188
+ if hasattr(self._instance, "_mark_as_changed"):
189
+ if key is not None:
190
+ self._instance._mark_as_changed(f"{self._name}.{key % len(self)}")
191
+ else:
192
+ self._instance._mark_as_changed(self._name)
193
+
194
+
195
+ class EmbeddedDocumentList(BaseList):
196
+ @classmethod
197
+ def __match_all(cls, embedded_doc, kwargs):
198
+ """Return True if a given embedded doc matches all the filter
199
+ kwargs. If it doesn't return False.
200
+ """
201
+ for key, expected_value in kwargs.items():
202
+ doc_val = getattr(embedded_doc, key)
203
+ if doc_val != expected_value and str(doc_val) != expected_value:
204
+ return False
205
+ return True
206
+
207
+ @classmethod
208
+ def __only_matches(cls, embedded_docs, kwargs):
209
+ """Return embedded docs that match the filter kwargs."""
210
+ if not kwargs:
211
+ return embedded_docs
212
+ return [doc for doc in embedded_docs if cls.__match_all(doc, kwargs)]
213
+
214
+ def filter(self, **kwargs):
215
+ """
216
+ Filters the list by only including embedded documents with the
217
+ given keyword arguments.
218
+
219
+ This method only supports simple comparison (e.g. .filter(name='John Doe'))
220
+ and does not support operators like __gte, __lte, __icontains like queryset.filter does
221
+
222
+ :param kwargs: The keyword arguments corresponding to the fields to
223
+ filter on. *Multiple arguments are treated as if they are ANDed
224
+ together.*
225
+ :return: A new ``EmbeddedDocumentList`` containing the matching
226
+ embedded documents.
227
+
228
+ Raises ``AttributeError`` if a given keyword is not a valid field for
229
+ the embedded document class.
230
+ """
231
+ values = self.__only_matches(self, kwargs)
232
+ return EmbeddedDocumentList(values, self._instance, self._name)
233
+
234
+ def exclude(self, **kwargs):
235
+ """
236
+ Filters the list by excluding embedded documents with the given
237
+ keyword arguments.
238
+
239
+ :param kwargs: The keyword arguments corresponding to the fields to
240
+ exclude on. *Multiple arguments are treated as if they are ANDed
241
+ together.*
242
+ :return: A new ``EmbeddedDocumentList`` containing the non-matching
243
+ embedded documents.
244
+
245
+ Raises ``AttributeError`` if a given keyword is not a valid field for
246
+ the embedded document class.
247
+ """
248
+ exclude = self.__only_matches(self, kwargs)
249
+ values = [item for item in self if item not in exclude]
250
+ return EmbeddedDocumentList(values, self._instance, self._name)
251
+
252
+ def count(self):
253
+ """
254
+ The number of embedded documents in the list.
255
+
256
+ :return: The length of the list, equivalent to the result of ``len()``.
257
+ """
258
+ return len(self)
259
+
260
+ def get(self, **kwargs):
261
+ """
262
+ Retrieves an embedded document determined by the given keyword
263
+ arguments.
264
+
265
+ :param kwargs: The keyword arguments corresponding to the fields to
266
+ search on. *Multiple arguments are treated as if they are ANDed
267
+ together.*
268
+ :return: The embedded document matched by the given keyword arguments.
269
+
270
+ Raises ``DoesNotExist`` if the arguments used to query an embedded
271
+ document returns no results. ``MultipleObjectsReturned`` if more
272
+ than one result is returned.
273
+ """
274
+ values = self.__only_matches(self, kwargs)
275
+ if len(values) == 0:
276
+ raise DoesNotExist("%s matching query does not exist." % self._name)
277
+ elif len(values) > 1:
278
+ raise MultipleObjectsReturned(
279
+ "%d items returned, instead of 1" % len(values)
280
+ )
281
+
282
+ return values[0]
283
+
284
+ def first(self):
285
+ """Return the first embedded document in the list, or ``None``
286
+ if empty.
287
+ """
288
+ if len(self) > 0:
289
+ return self[0]
290
+
291
+ def create(self, **values):
292
+ """
293
+ Creates a new instance of the EmbeddedDocument and appends it to this EmbeddedDocumentList.
294
+
295
+ .. note::
296
+ the instance of the EmbeddedDocument is not automatically saved to the database.
297
+ You still need to call .save() on the parent Document.
298
+
299
+ :param values: A dictionary of values for the embedded document.
300
+ :return: The new embedded document instance.
301
+ """
302
+ name = self._name
303
+ EmbeddedClass = self._instance._fields[name].field.document_type_obj
304
+ self._instance[self._name].append(EmbeddedClass(**values))
305
+
306
+ return self._instance[self._name][-1]
307
+
308
+ def save(self, *args, **kwargs):
309
+ """
310
+ Saves the ancestor document.
311
+
312
+ :param args: Arguments passed up to the ancestor Document's save
313
+ method.
314
+ :param kwargs: Keyword arguments passed up to the ancestor Document's
315
+ save method.
316
+ """
317
+ self._instance.save(*args, **kwargs)
318
+
319
+ def delete(self):
320
+ """
321
+ Deletes the embedded documents from the database.
322
+
323
+ .. note::
324
+ The embedded document changes are not automatically saved
325
+ to the database after calling this method.
326
+
327
+ :return: The number of entries deleted.
328
+ """
329
+ values = list(self)
330
+ for item in values:
331
+ self._instance[self._name].remove(item)
332
+
333
+ return len(values)
334
+
335
+ def update(self, **update):
336
+ """
337
+ Updates the embedded documents with the given replacement values. This
338
+ function does not support mongoDB update operators such as ``inc__``.
339
+
340
+ .. note::
341
+ The embedded document changes are not automatically saved
342
+ to the database after calling this method.
343
+
344
+ :param update: A dictionary of update values to apply to each
345
+ embedded document.
346
+ :return: The number of entries updated.
347
+ """
348
+ if len(update) == 0:
349
+ return 0
350
+ values = list(self)
351
+ for item in values:
352
+ for k, v in update.items():
353
+ setattr(item, k, v)
354
+
355
+ return len(values)
356
+
357
+
358
+ class StrictDict:
359
+ __slots__ = ()
360
+ _special_fields = {"get", "pop", "iteritems", "items", "keys", "create"}
361
+ _classes = {}
362
+
363
+ def __init__(self, **kwargs):
364
+ for k, v in kwargs.items():
365
+ setattr(self, k, v)
366
+
367
+ def __getitem__(self, key):
368
+ key = "_reserved_" + key if key in self._special_fields else key
369
+ try:
370
+ return getattr(self, key)
371
+ except AttributeError:
372
+ raise KeyError(key)
373
+
374
+ def __setitem__(self, key, value):
375
+ key = "_reserved_" + key if key in self._special_fields else key
376
+ return setattr(self, key, value)
377
+
378
+ def __contains__(self, key):
379
+ return hasattr(self, key)
380
+
381
+ def get(self, key, default=None):
382
+ try:
383
+ return self[key]
384
+ except KeyError:
385
+ return default
386
+
387
+ def pop(self, key, default=None):
388
+ v = self.get(key, default)
389
+ try:
390
+ delattr(self, key)
391
+ except AttributeError:
392
+ pass
393
+ return v
394
+
395
+ def iteritems(self):
396
+ for key in self:
397
+ yield key, self[key]
398
+
399
+ def items(self):
400
+ return [(k, self[k]) for k in iter(self)]
401
+
402
+ def iterkeys(self):
403
+ return iter(self)
404
+
405
+ def keys(self):
406
+ return list(iter(self))
407
+
408
+ def __iter__(self):
409
+ return (key for key in self.__slots__ if hasattr(self, key))
410
+
411
+ def __len__(self):
412
+ return len(list(self.items()))
413
+
414
+ def __eq__(self, other):
415
+ return list(self.items()) == list(other.items())
416
+
417
+ def __ne__(self, other):
418
+ return not (self == other)
419
+
420
+ @classmethod
421
+ def create(cls, allowed_keys):
422
+ allowed_keys_tuple = tuple(
423
+ ("_reserved_" + k if k in cls._special_fields else k) for k in allowed_keys
424
+ )
425
+ allowed_keys = frozenset(allowed_keys_tuple)
426
+ if allowed_keys not in cls._classes:
427
+
428
+ class SpecificStrictDict(cls):
429
+ __slots__ = allowed_keys_tuple
430
+
431
+ def __repr__(self):
432
+ return "{%s}" % ", ".join(
433
+ f'"{k!s}": {v!r}' for k, v in self.items()
434
+ )
435
+
436
+ cls._classes[allowed_keys] = SpecificStrictDict
437
+ return cls._classes[allowed_keys]
438
+
439
+
440
+ class LazyReference(DBRef):
441
+ __slots__ = ("_cached_doc", "passthrough", "document_type")
442
+
443
+ def fetch(self, force=False):
444
+ if not self._cached_doc or force:
445
+ self._cached_doc = self.document_type.objects.get(pk=self.pk)
446
+ if not self._cached_doc:
447
+ raise DoesNotExist("Trying to dereference unknown document %s" % (self))
448
+ return self._cached_doc
449
+
450
+ @property
451
+ def pk(self):
452
+ return self.id
453
+
454
+ def __init__(self, document_type, pk, cached_doc=None, passthrough=False):
455
+ self.document_type = document_type
456
+ self._cached_doc = cached_doc
457
+ self.passthrough = passthrough
458
+ super().__init__(self.document_type._get_collection_name(), pk)
459
+
460
+ def __getitem__(self, name):
461
+ if not self.passthrough:
462
+ raise KeyError()
463
+ document = self.fetch()
464
+ return document[name]
465
+
466
+ def __getattr__(self, name):
467
+ if not object.__getattribute__(self, "passthrough"):
468
+ raise AttributeError()
469
+ document = self.fetch()
470
+ try:
471
+ return document[name]
472
+ except KeyError:
473
+ raise AttributeError()
474
+
475
+ def __repr__(self):
476
+ return f"<LazyReference({self.document_type}, {self.pk!r})>"