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.
- autonomous/__init__.py +5 -2
- autonomous/ai/audioagent.py +32 -0
- autonomous/ai/imageagent.py +31 -0
- autonomous/ai/jsonagent.py +40 -0
- autonomous/ai/models/__init__.py +0 -0
- autonomous/ai/models/openai.py +308 -0
- autonomous/ai/oaiagent.py +20 -194
- autonomous/ai/textagent.py +35 -0
- autonomous/auth/autoauth.py +11 -11
- autonomous/auth/user.py +24 -11
- autonomous/db/__init__.py +41 -0
- autonomous/db/base/__init__.py +33 -0
- autonomous/db/base/common.py +62 -0
- autonomous/db/base/datastructures.py +476 -0
- autonomous/db/base/document.py +1230 -0
- autonomous/db/base/fields.py +767 -0
- autonomous/db/base/metaclasses.py +468 -0
- autonomous/db/base/utils.py +22 -0
- autonomous/db/common.py +79 -0
- autonomous/db/connection.py +472 -0
- autonomous/db/context_managers.py +313 -0
- autonomous/db/dereference.py +291 -0
- autonomous/db/document.py +1141 -0
- autonomous/db/errors.py +165 -0
- autonomous/db/fields.py +2732 -0
- autonomous/db/mongodb_support.py +24 -0
- autonomous/db/pymongo_support.py +80 -0
- autonomous/db/queryset/__init__.py +28 -0
- autonomous/db/queryset/base.py +2033 -0
- autonomous/db/queryset/field_list.py +88 -0
- autonomous/db/queryset/manager.py +58 -0
- autonomous/db/queryset/queryset.py +189 -0
- autonomous/db/queryset/transform.py +527 -0
- autonomous/db/queryset/visitor.py +189 -0
- autonomous/db/signals.py +59 -0
- autonomous/logger.py +3 -0
- autonomous/model/autoattr.py +120 -0
- autonomous/model/automodel.py +121 -308
- autonomous/storage/imagestorage.py +9 -54
- autonomous/tasks/autotask.py +0 -25
- {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/METADATA +7 -8
- autonomous_app-0.3.1.dist-info/RECORD +60 -0
- {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/WHEEL +1 -1
- autonomous/db/autodb.py +0 -86
- autonomous/db/table.py +0 -156
- autonomous/errors/__init__.py +0 -1
- autonomous/errors/danglingreferenceerror.py +0 -8
- autonomous/model/autoattribute.py +0 -20
- autonomous/model/orm.py +0 -86
- autonomous/model/serializer.py +0 -110
- autonomous_app-0.2.25.dist-info/RECORD +0 -36
- /autonomous/{storage → apis}/version_control/GHCallbacks.py +0 -0
- /autonomous/{storage → apis}/version_control/GHOrganization.py +0 -0
- /autonomous/{storage → apis}/version_control/GHRepo.py +0 -0
- /autonomous/{storage → apis}/version_control/GHVersionControl.py +0 -0
- /autonomous/{storage → apis}/version_control/__init__.py +0 -0
- /autonomous/{storage → utils}/markdown.py +0 -0
- {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/LICENSE +0 -0
- {autonomous_app-0.2.25.dist-info → autonomous_app-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
from autonomous.db.errors import InvalidQueryError
|
|
5
|
+
from autonomous.db.queryset import transform
|
|
6
|
+
|
|
7
|
+
__all__ = ("Q", "QNode")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def warn_empty_is_deprecated():
|
|
11
|
+
msg = "'empty' property is deprecated in favour of using 'not bool(filter)'"
|
|
12
|
+
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class QNodeVisitor:
|
|
16
|
+
"""Base visitor class for visiting Q-object nodes in a query tree."""
|
|
17
|
+
|
|
18
|
+
def visit_combination(self, combination):
|
|
19
|
+
"""Called by QCombination objects."""
|
|
20
|
+
return combination
|
|
21
|
+
|
|
22
|
+
def visit_query(self, query):
|
|
23
|
+
"""Called by (New)Q objects."""
|
|
24
|
+
return query
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DuplicateQueryConditionsError(InvalidQueryError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SimplificationVisitor(QNodeVisitor):
|
|
32
|
+
"""Simplifies query trees by combining unnecessary 'and' connection nodes
|
|
33
|
+
into a single Q-object.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def visit_combination(self, combination):
|
|
37
|
+
if combination.operation == combination.AND:
|
|
38
|
+
# The simplification only applies to 'simple' queries
|
|
39
|
+
if all(isinstance(node, Q) for node in combination.children):
|
|
40
|
+
queries = [n.query for n in combination.children]
|
|
41
|
+
try:
|
|
42
|
+
return Q(**self._query_conjunction(queries))
|
|
43
|
+
except DuplicateQueryConditionsError:
|
|
44
|
+
# Cannot be simplified
|
|
45
|
+
pass
|
|
46
|
+
return combination
|
|
47
|
+
|
|
48
|
+
def _query_conjunction(self, queries):
|
|
49
|
+
"""Merges query dicts - effectively &ing them together."""
|
|
50
|
+
query_ops = set()
|
|
51
|
+
combined_query = {}
|
|
52
|
+
for query in queries:
|
|
53
|
+
ops = set(query.keys())
|
|
54
|
+
# Make sure that the same operation isn't applied more than once
|
|
55
|
+
# to a single field
|
|
56
|
+
intersection = ops.intersection(query_ops)
|
|
57
|
+
if intersection:
|
|
58
|
+
raise DuplicateQueryConditionsError()
|
|
59
|
+
|
|
60
|
+
query_ops.update(ops)
|
|
61
|
+
combined_query.update(copy.deepcopy(query))
|
|
62
|
+
return combined_query
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class QueryCompilerVisitor(QNodeVisitor):
|
|
66
|
+
"""Compiles the nodes in a query tree to a PyMongo-compatible query
|
|
67
|
+
dictionary.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, document):
|
|
71
|
+
self.document = document
|
|
72
|
+
|
|
73
|
+
def visit_combination(self, combination):
|
|
74
|
+
operator = "$and"
|
|
75
|
+
if combination.operation == combination.OR:
|
|
76
|
+
operator = "$or"
|
|
77
|
+
return {operator: combination.children}
|
|
78
|
+
|
|
79
|
+
def visit_query(self, query):
|
|
80
|
+
return transform.query(self.document, **query.query)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class QNode:
|
|
84
|
+
"""Base class for nodes in query trees."""
|
|
85
|
+
|
|
86
|
+
AND = 0
|
|
87
|
+
OR = 1
|
|
88
|
+
|
|
89
|
+
def to_query(self, document):
|
|
90
|
+
query = self.accept(SimplificationVisitor())
|
|
91
|
+
query = query.accept(QueryCompilerVisitor(document))
|
|
92
|
+
return query
|
|
93
|
+
|
|
94
|
+
def accept(self, visitor):
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
def _combine(self, other, operation):
|
|
98
|
+
"""Combine this node with another node into a QCombination
|
|
99
|
+
object.
|
|
100
|
+
"""
|
|
101
|
+
# If the other Q() is empty, ignore it and just use `self`.
|
|
102
|
+
if not bool(other):
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
# Or if this Q is empty, ignore it and just use `other`.
|
|
106
|
+
if not bool(self):
|
|
107
|
+
return other
|
|
108
|
+
|
|
109
|
+
return QCombination(operation, [self, other])
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def empty(self):
|
|
113
|
+
warn_empty_is_deprecated()
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
def __or__(self, other):
|
|
117
|
+
return self._combine(other, self.OR)
|
|
118
|
+
|
|
119
|
+
def __and__(self, other):
|
|
120
|
+
return self._combine(other, self.AND)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class QCombination(QNode):
|
|
124
|
+
"""Represents the combination of several conditions by a given
|
|
125
|
+
logical operator.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, operation, children):
|
|
129
|
+
self.operation = operation
|
|
130
|
+
self.children = []
|
|
131
|
+
for node in children:
|
|
132
|
+
# If the child is a combination of the same type, we can merge its
|
|
133
|
+
# children directly into this combinations children
|
|
134
|
+
if isinstance(node, QCombination) and node.operation == operation:
|
|
135
|
+
self.children += node.children
|
|
136
|
+
else:
|
|
137
|
+
self.children.append(node)
|
|
138
|
+
|
|
139
|
+
def __repr__(self):
|
|
140
|
+
op = " & " if self.operation is self.AND else " | "
|
|
141
|
+
return "(%s)" % op.join([repr(node) for node in self.children])
|
|
142
|
+
|
|
143
|
+
def __bool__(self):
|
|
144
|
+
return bool(self.children)
|
|
145
|
+
|
|
146
|
+
def accept(self, visitor):
|
|
147
|
+
for i in range(len(self.children)):
|
|
148
|
+
if isinstance(self.children[i], QNode):
|
|
149
|
+
self.children[i] = self.children[i].accept(visitor)
|
|
150
|
+
|
|
151
|
+
return visitor.visit_combination(self)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def empty(self):
|
|
155
|
+
warn_empty_is_deprecated()
|
|
156
|
+
return not bool(self.children)
|
|
157
|
+
|
|
158
|
+
def __eq__(self, other):
|
|
159
|
+
return (
|
|
160
|
+
self.__class__ == other.__class__
|
|
161
|
+
and self.operation == other.operation
|
|
162
|
+
and self.children == other.children
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Q(QNode):
|
|
167
|
+
"""A simple query object, used in a query tree to build up more complex
|
|
168
|
+
query structures.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self, **query):
|
|
172
|
+
self.query = query
|
|
173
|
+
|
|
174
|
+
def __repr__(self):
|
|
175
|
+
return "Q(**%s)" % repr(self.query)
|
|
176
|
+
|
|
177
|
+
def __bool__(self):
|
|
178
|
+
return bool(self.query)
|
|
179
|
+
|
|
180
|
+
def __eq__(self, other):
|
|
181
|
+
return self.__class__ == other.__class__ and self.query == other.query
|
|
182
|
+
|
|
183
|
+
def accept(self, visitor):
|
|
184
|
+
return visitor.visit_query(self)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def empty(self):
|
|
188
|
+
warn_empty_is_deprecated()
|
|
189
|
+
return not bool(self.query)
|
autonomous/db/signals.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
__all__ = (
|
|
2
|
+
"pre_init",
|
|
3
|
+
"post_init",
|
|
4
|
+
"pre_save",
|
|
5
|
+
"pre_save_post_validation",
|
|
6
|
+
"post_save",
|
|
7
|
+
"pre_delete",
|
|
8
|
+
"post_delete",
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
signals_available = False
|
|
12
|
+
try:
|
|
13
|
+
from blinker import Namespace
|
|
14
|
+
|
|
15
|
+
signals_available = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
|
|
18
|
+
class Namespace:
|
|
19
|
+
def signal(self, name, doc=None):
|
|
20
|
+
return _FakeSignal(name, doc)
|
|
21
|
+
|
|
22
|
+
class _FakeSignal:
|
|
23
|
+
"""If blinker is unavailable, create a fake class with the same
|
|
24
|
+
interface that allows sending of signals but will fail with an
|
|
25
|
+
error on anything else. Instead of doing anything on send, it
|
|
26
|
+
will just ignore the arguments and do nothing instead.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, name, doc=None):
|
|
30
|
+
self.name = name
|
|
31
|
+
self.__doc__ = doc
|
|
32
|
+
|
|
33
|
+
def _fail(self, *args, **kwargs):
|
|
34
|
+
raise RuntimeError(
|
|
35
|
+
"signalling support is unavailable "
|
|
36
|
+
"because the blinker library is "
|
|
37
|
+
"not installed."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
send = lambda *a, **kw: None # noqa
|
|
41
|
+
connect = disconnect = has_receivers_for = receivers_for = (
|
|
42
|
+
temporarily_connected_to
|
|
43
|
+
) = _fail
|
|
44
|
+
del _fail
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# the namespace for code signals. If you are not autonomous.db code, do
|
|
48
|
+
# not put signals in here. Create your own namespace instead.
|
|
49
|
+
_signals = Namespace()
|
|
50
|
+
|
|
51
|
+
pre_init = _signals.signal("pre_init")
|
|
52
|
+
post_init = _signals.signal("post_init")
|
|
53
|
+
pre_save = _signals.signal("pre_save")
|
|
54
|
+
pre_save_post_validation = _signals.signal("pre_save_post_validation")
|
|
55
|
+
post_save = _signals.signal("post_save")
|
|
56
|
+
pre_delete = _signals.signal("pre_delete")
|
|
57
|
+
post_delete = _signals.signal("post_delete")
|
|
58
|
+
pre_bulk_insert = _signals.signal("pre_bulk_insert")
|
|
59
|
+
post_bulk_insert = _signals.signal("post_bulk_insert")
|
autonomous/logger.py
CHANGED
|
@@ -43,6 +43,7 @@ class Logger:
|
|
|
43
43
|
|
|
44
44
|
def __call__(self, *args, **kwargs):
|
|
45
45
|
if self.enabled:
|
|
46
|
+
is_printed = kwargs.pop("_print", False)
|
|
46
47
|
caller = inspect.stack()[1]
|
|
47
48
|
fn = caller.filename.split("/")[-1]
|
|
48
49
|
msg = f"\n\n{'='*20}\t{fn}:{caller.function}()::{caller.lineno}\t{'='*20}\n"
|
|
@@ -57,6 +58,8 @@ class Logger:
|
|
|
57
58
|
current.write(f"{msg}\n")
|
|
58
59
|
with open(self.logarchive, "a") as archive:
|
|
59
60
|
archive.write(f"{msg}\n")
|
|
61
|
+
if is_printed:
|
|
62
|
+
print(msg)
|
|
60
63
|
|
|
61
64
|
|
|
62
65
|
log = Logger()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from autonomous import log
|
|
2
|
+
from autonomous.db.fields import (
|
|
3
|
+
BooleanField,
|
|
4
|
+
DateTimeField,
|
|
5
|
+
DictField,
|
|
6
|
+
DoesNotExist,
|
|
7
|
+
EmailField,
|
|
8
|
+
EnumField,
|
|
9
|
+
FileField,
|
|
10
|
+
FloatField,
|
|
11
|
+
GenericLazyReferenceField,
|
|
12
|
+
GenericReferenceField,
|
|
13
|
+
ImageField,
|
|
14
|
+
IntField,
|
|
15
|
+
ListField,
|
|
16
|
+
StringField,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StringAttr(StringField):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class IntAttr(IntField):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FloatAttr(FloatField):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BoolAttr(BooleanField):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DateTimeAttr(DateTimeField):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EmailAttr(EmailField):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class FileAttr(FileField):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ImageAttr(ImageField):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ReferenceAttr(GenericReferenceField):
|
|
53
|
+
def __get__(self, instance, owner):
|
|
54
|
+
try:
|
|
55
|
+
result = super().__get__(instance, owner)
|
|
56
|
+
except DoesNotExist as e:
|
|
57
|
+
log(f"ReferenceAttr Error: {e}")
|
|
58
|
+
return None
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# class ReferenceAttr(GenericLazyReferenceField):
|
|
63
|
+
# def __get__(self, instance, owner):
|
|
64
|
+
# try:
|
|
65
|
+
# result = super().__get__(instance, owner)
|
|
66
|
+
# except DoesNotExist as e:
|
|
67
|
+
# log(f"ReferenceAttr Error: {e}")
|
|
68
|
+
# return None
|
|
69
|
+
# return result.fetch() if result and result.pk else result
|
|
70
|
+
|
|
71
|
+
# except DoesNotExist:
|
|
72
|
+
# If the document doesn't exist, return None
|
|
73
|
+
# return None
|
|
74
|
+
|
|
75
|
+
# def validate(self, value):
|
|
76
|
+
# if value is not None and not self.required:
|
|
77
|
+
# super().validate(value)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ListAttr(ListField):
|
|
81
|
+
# pass
|
|
82
|
+
def __get__(self, instance, owner):
|
|
83
|
+
# log(instance, owner)
|
|
84
|
+
results = super().__get__(instance, owner) or []
|
|
85
|
+
# print(self.name, self.field, owner, results)
|
|
86
|
+
if isinstance(self.field, ReferenceAttr):
|
|
87
|
+
i = 0
|
|
88
|
+
while i < len(results):
|
|
89
|
+
try:
|
|
90
|
+
if not results[i]:
|
|
91
|
+
log(f"Removing Object: {results[i]}")
|
|
92
|
+
results.pop(i)
|
|
93
|
+
else:
|
|
94
|
+
i += 1
|
|
95
|
+
except DoesNotExist:
|
|
96
|
+
results.pop(i)
|
|
97
|
+
log(f"Object Not Found: {results[i]}")
|
|
98
|
+
# log(results)
|
|
99
|
+
return results
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class DictAttr(DictField):
|
|
103
|
+
def __get__(self, instance, owner):
|
|
104
|
+
# log(instance, owner)
|
|
105
|
+
results = super().__get__(instance, owner) or {}
|
|
106
|
+
log(self.name, self.field, owner, results)
|
|
107
|
+
for key, lazy_obj in results.items():
|
|
108
|
+
try:
|
|
109
|
+
if hasattr(lazy_obj, "fetch"):
|
|
110
|
+
lazy_obj = (
|
|
111
|
+
lazy_obj.fetch() if lazy_obj and lazy_obj.pk else lazy_obj
|
|
112
|
+
)
|
|
113
|
+
except DoesNotExist:
|
|
114
|
+
log(f"Object Not Found: {lazy_obj}")
|
|
115
|
+
results[key] = lazy_obj
|
|
116
|
+
return results
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class EnumAttr(EnumField):
|
|
120
|
+
pass
|