appier 1.31.4__py2.py3-none-any.whl → 1.32.0__py2.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 (81) hide show
  1. appier/__init__.py +333 -52
  2. appier/amqp.py +29 -30
  3. appier/api.py +214 -212
  4. appier/asgi.py +54 -55
  5. appier/async_neo.py +46 -35
  6. appier/async_old.py +55 -42
  7. appier/asynchronous.py +7 -13
  8. appier/base.py +1762 -1429
  9. appier/bus.py +51 -52
  10. appier/cache.py +99 -84
  11. appier/common.py +9 -11
  12. appier/component.py +17 -19
  13. appier/compress.py +25 -28
  14. appier/config.py +96 -73
  15. appier/controller.py +9 -15
  16. appier/crypt.py +25 -21
  17. appier/data.py +73 -57
  18. appier/defines.py +191 -226
  19. appier/exceptions.py +103 -63
  20. appier/execution.py +94 -88
  21. appier/export.py +90 -88
  22. appier/extra.py +6 -13
  23. appier/extra_neo.py +8 -11
  24. appier/extra_old.py +18 -16
  25. appier/geo.py +57 -47
  26. appier/git.py +101 -90
  27. appier/graph.py +23 -24
  28. appier/http.py +520 -398
  29. appier/legacy.py +373 -180
  30. appier/log.py +90 -97
  31. appier/meta.py +42 -42
  32. appier/mock.py +32 -34
  33. appier/model.py +793 -681
  34. appier/model_a.py +208 -183
  35. appier/mongo.py +183 -107
  36. appier/observer.py +39 -31
  37. appier/part.py +23 -24
  38. appier/preferences.py +44 -47
  39. appier/queuing.py +78 -96
  40. appier/redisdb.py +40 -35
  41. appier/request.py +227 -175
  42. appier/scheduler.py +13 -18
  43. appier/serialize.py +37 -31
  44. appier/session.py +161 -147
  45. appier/settings.py +2 -11
  46. appier/smtp.py +53 -49
  47. appier/storage.py +39 -33
  48. appier/structures.py +50 -45
  49. appier/test/__init__.py +2 -11
  50. appier/test/base.py +111 -108
  51. appier/test/cache.py +28 -35
  52. appier/test/config.py +10 -19
  53. appier/test/crypt.py +3 -12
  54. appier/test/data.py +3 -12
  55. appier/test/exceptions.py +8 -17
  56. appier/test/export.py +16 -33
  57. appier/test/graph.py +27 -60
  58. appier/test/http.py +42 -54
  59. appier/test/legacy.py +20 -30
  60. appier/test/log.py +14 -35
  61. appier/test/mock.py +27 -123
  62. appier/test/model.py +79 -91
  63. appier/test/part.py +5 -14
  64. appier/test/preferences.py +5 -13
  65. appier/test/queuing.py +29 -37
  66. appier/test/request.py +61 -73
  67. appier/test/serialize.py +12 -23
  68. appier/test/session.py +10 -19
  69. appier/test/smtp.py +8 -14
  70. appier/test/structures.py +20 -24
  71. appier/test/typesf.py +14 -28
  72. appier/test/util.py +480 -438
  73. appier/typesf.py +251 -171
  74. appier/util.py +578 -407
  75. appier/validation.py +280 -143
  76. {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/METADATA +6 -1
  77. appier-1.32.0.dist-info/RECORD +86 -0
  78. appier-1.31.4.dist-info/RECORD +0 -86
  79. {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/LICENSE +0 -0
  80. {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/WHEEL +0 -0
  81. {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/top_level.txt +0 -0
appier/model_a.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  # Hive Appier Framework
5
- # Copyright (c) 2008-2022 Hive Solutions Lda.
5
+ # Copyright (c) 2008-2024 Hive Solutions Lda.
6
6
  #
7
7
  # This file is part of Hive Appier Framework.
8
8
  #
@@ -22,16 +22,7 @@
22
22
  __author__ = "João Magalhães <joamag@hive.pt>"
23
23
  """ The author(s) of the module """
24
24
 
25
- __version__ = "1.0.0"
26
- """ The version of the module """
27
-
28
- __revision__ = "$LastChangedRevision$"
29
- """ The revision number of the module """
30
-
31
- __date__ = "$LastChangedDate$"
32
- """ The last change date of the module """
33
-
34
- __copyright__ = "Copyright (c) 2008-2022 Hive Solutions Lda."
25
+ __copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
35
26
  """ The copyright for the module """
36
27
 
37
28
  __license__ = "Apache License, Version 2.0"
@@ -43,201 +34,218 @@ from . import common
43
34
  from . import legacy
44
35
  from . import exceptions
45
36
 
46
- class ModelAsync(object):
47
37
 
38
+ class ModelAsync(object):
48
39
  @classmethod
49
40
  async def get_a(cls, *args, **kwargs):
50
- fields,\
51
- eager,\
52
- eager_l,\
53
- map,\
54
- rules,\
55
- meta,\
56
- build,\
57
- fill,\
58
- resolve_a,\
59
- skip,\
60
- limit,\
61
- sort,\
62
- raise_e = cls._get_attrs(kwargs, (
63
- ("fields", None),
64
- ("eager", None),
65
- ("eager_l", None),
66
- ("map", False),
67
- ("rules", True),
68
- ("meta", False),
69
- ("build", True),
70
- ("fill", True),
71
- ("resolve_a", None),
72
- ("skip", 0),
73
- ("limit", 0),
74
- ("sort", None),
75
- ("raise_e", True)
76
- ))
41
+ (
42
+ fields,
43
+ eager,
44
+ eager_l,
45
+ map,
46
+ rules,
47
+ meta,
48
+ build,
49
+ fill,
50
+ resolve_a,
51
+ skip,
52
+ limit,
53
+ sort,
54
+ raise_e,
55
+ ) = cls._get_attrs(
56
+ kwargs,
57
+ (
58
+ ("fields", None),
59
+ ("eager", None),
60
+ ("eager_l", None),
61
+ ("map", False),
62
+ ("rules", True),
63
+ ("meta", False),
64
+ ("build", True),
65
+ ("fill", True),
66
+ ("resolve_a", None),
67
+ ("skip", 0),
68
+ ("limit", 0),
69
+ ("sort", None),
70
+ ("raise_e", True),
71
+ ),
72
+ )
77
73
 
78
74
  # in case there's a sort field and the safe search mode is enabled
79
75
  # we must add sorting by the `_id` field so that the retrieval is
80
76
  # considered to be deterministic, otherwise some DB implementations
81
77
  # will not respect the same sorting sequence across different calls
82
78
  if sort and (skip or limit):
83
- if not isinstance(sort, list): sort = list(sort)
79
+ if not isinstance(sort, list):
80
+ sort = list(sort)
84
81
  sort.append(["_id", 1])
85
82
 
86
- if eager_l == None: eager_l = map
87
- if resolve_a == None: resolve_a = map
88
- if eager_l: eager = cls._eager_b(eager)
89
- fields = cls._sniff(fields, rules = rules)
83
+ if eager_l == None:
84
+ eager_l = map
85
+ if resolve_a == None:
86
+ resolve_a = map
87
+ if eager_l:
88
+ eager = cls._eager_b(eager)
89
+ fields = cls._sniff(fields, rules=rules)
90
90
  collection = cls._collection_a()
91
91
  model = await collection.find_one(
92
- kwargs,
93
- fields,
94
- skip = skip,
95
- limit = limit,
96
- sort = sort
92
+ kwargs, fields, skip=skip, limit=limit, sort=sort
97
93
  )
98
94
  if not model and raise_e:
99
95
  is_devel = common.is_devel()
100
- if is_devel: message = "%s not found for %s" % (cls.__name__, str(kwargs))
101
- else: message = "%s not found" % cls.__name__
102
- raise exceptions.NotFoundError(message = message)
103
- if not model and not raise_e: return model
96
+ if is_devel:
97
+ message = "%s not found for %s" % (cls.__name__, str(kwargs))
98
+ else:
99
+ message = "%s not found" % cls.__name__
100
+ raise exceptions.NotFoundError(message=message)
101
+ if not model and not raise_e:
102
+ return model
104
103
  cls.types(model)
105
- if fill: cls.fill(model, safe = True)
106
- if build: cls.build(model, map = map, rules = rules, meta = meta)
107
- if eager: model = cls._eager(model, eager, map = map)
108
- if resolve_a: model = cls._resolve_all(model, resolve = False)
109
- return model if map else cls.old(model = model, safe = False)
104
+ if fill:
105
+ cls.fill(model, safe=True)
106
+ if build:
107
+ cls.build(model, map=map, rules=rules, meta=meta)
108
+ if eager:
109
+ model = cls._eager(model, eager, map=map)
110
+ if resolve_a:
111
+ model = cls._resolve_all(model, resolve=False)
112
+ return model if map else cls.old(model=model, safe=False)
110
113
 
111
114
  @classmethod
112
115
  async def find_a(cls, *args, **kwargs):
113
- fields,\
114
- eager,\
115
- eager_l,\
116
- map,\
117
- rules,\
118
- meta,\
119
- build,\
120
- fill,\
121
- resolve_a,\
122
- skip,\
123
- limit,\
124
- sort,\
125
- raise_e = cls._get_attrs(kwargs, (
126
- ("fields", None),
127
- ("eager", None),
128
- ("eager_l", False),
129
- ("map", False),
130
- ("rules", True),
131
- ("meta", False),
132
- ("build", True),
133
- ("fill", True),
134
- ("resolve_a", None),
135
- ("skip", 0),
136
- ("limit", 0),
137
- ("sort", None),
138
- ("raise_e", False)
139
- ))
116
+ (
117
+ fields,
118
+ eager,
119
+ eager_l,
120
+ map,
121
+ rules,
122
+ meta,
123
+ build,
124
+ fill,
125
+ resolve_a,
126
+ skip,
127
+ limit,
128
+ sort,
129
+ raise_e,
130
+ ) = cls._get_attrs(
131
+ kwargs,
132
+ (
133
+ ("fields", None),
134
+ ("eager", None),
135
+ ("eager_l", False),
136
+ ("map", False),
137
+ ("rules", True),
138
+ ("meta", False),
139
+ ("build", True),
140
+ ("fill", True),
141
+ ("resolve_a", None),
142
+ ("skip", 0),
143
+ ("limit", 0),
144
+ ("sort", None),
145
+ ("raise_e", False),
146
+ ),
147
+ )
140
148
 
141
149
  # in case there's a sort field and the safe search mode is enabled
142
150
  # we must add sorting by the `_id` field so that the search is
143
151
  # considered to be deterministic, otherwise some DB implementations
144
152
  # will not respect the same sorting sequence across different calls
145
153
  if sort and (skip or limit):
146
- if not isinstance(sort, list): sort = list(sort)
154
+ if not isinstance(sort, list):
155
+ sort = list(sort)
147
156
  sort.append(["_id", 1])
148
157
 
149
- if resolve_a == None: resolve_a = map
150
- if eager_l: eager = cls._eager_b(eager)
158
+ if resolve_a == None:
159
+ resolve_a = map
160
+ if eager_l:
161
+ eager = cls._eager_b(eager)
151
162
 
152
163
  cls._find_s(kwargs)
153
164
  cls._find_d(kwargs)
154
165
 
155
- fields = cls._sniff(fields, rules = rules)
166
+ fields = cls._sniff(fields, rules=rules)
156
167
  collection = cls._collection_a()
157
- models = collection.find(
158
- kwargs,
159
- fields,
160
- skip = skip,
161
- limit = limit,
162
- sort = sort
163
- )
168
+ models = collection.find(kwargs, fields, skip=skip, limit=limit, sort=sort)
164
169
  if not models and raise_e:
165
170
  is_devel = common.is_devel()
166
- if is_devel: message = "%s not found for %s" % (cls.__name__, str(kwargs))
167
- else: message = "%s not found" % cls.__name__
168
- raise exceptions.NotFoundError(message = message)
171
+ if is_devel:
172
+ message = "%s not found for %s" % (cls.__name__, str(kwargs))
173
+ else:
174
+ message = "%s not found" % cls.__name__
175
+ raise exceptions.NotFoundError(message=message)
169
176
  models = [cls.types(model) async for model in models]
170
- if fill: models = [cls.fill(model, safe = True) for model in models]
171
- if build: [cls.build(model, map = map, rules = rules, meta = meta) for model in models]
172
- if eager: models = cls._eager(models, eager, map = map)
173
- if resolve_a: models = [cls._resolve_all(model, resolve = False) for model in models]
174
- models = models if map else [cls.old(model = model, safe = False) for model in models]
177
+ if fill:
178
+ models = [cls.fill(model, safe=True) for model in models]
179
+ if build:
180
+ [cls.build(model, map=map, rules=rules, meta=meta) for model in models]
181
+ if eager:
182
+ models = cls._eager(models, eager, map=map)
183
+ if resolve_a:
184
+ models = [cls._resolve_all(model, resolve=False) for model in models]
185
+ models = (
186
+ models if map else [cls.old(model=model, safe=False) for model in models]
187
+ )
175
188
  return models
176
189
 
177
190
  @classmethod
178
191
  async def _increment_a(cls, name):
179
192
  _name = cls._name() + ":" + name
180
- store = cls._collection_a(name = "counters")
193
+ store = cls._collection_a(name="counters")
181
194
  value = await store.find_and_modify(
182
- {
183
- "_id" : _name
184
- },
185
- {
186
- "$inc" : {
187
- "seq" : 1
188
- }
189
- },
190
- new = True,
191
- upsert = True
195
+ {"_id": _name}, {"$inc": {"seq": 1}}, new=True, upsert=True
192
196
  )
193
- value = value or await store.find_one({
194
- "_id" : _name
195
- })
197
+ value = value or await store.find_one({"_id": _name})
196
198
  return value["seq"]
197
199
 
198
200
  async def save_a(
199
201
  self,
200
- validate = True,
201
- verify = True,
202
- is_new = None,
203
- increment_a = None,
204
- immutables_a = None,
205
- pre_validate = True,
206
- pre_save = True,
207
- pre_create = True,
208
- pre_update = True,
209
- post_validate = True,
210
- post_save = True,
211
- post_create = True,
212
- post_update = True,
213
- before_callbacks = [],
214
- after_callbacks = []
202
+ validate=True,
203
+ verify=True,
204
+ is_new=None,
205
+ increment_a=None,
206
+ immutables_a=None,
207
+ pre_validate=True,
208
+ pre_save=True,
209
+ pre_create=True,
210
+ pre_update=True,
211
+ post_validate=True,
212
+ post_save=True,
213
+ post_create=True,
214
+ post_update=True,
215
+ before_callbacks=[],
216
+ after_callbacks=[],
215
217
  ):
216
218
  # ensures that the current instance is associated with
217
219
  # a concrete model, ready to be persisted in database
218
- if verify: self.assert_is_concrete()
220
+ if verify:
221
+ self.assert_is_concrete()
219
222
 
220
223
  # checks if the instance to be saved is a new instance
221
224
  # or if this is an update operation and then determines
222
225
  # series of default values taking that into account
223
- if is_new == None: is_new = self.is_new()
224
- if increment_a == None: increment_a = is_new
225
- if immutables_a == None: immutables_a = not is_new
226
+ if is_new == None:
227
+ is_new = self.is_new()
228
+ if increment_a == None:
229
+ increment_a = is_new
230
+ if immutables_a == None:
231
+ immutables_a = not is_new
226
232
 
227
233
  # runs the validation process in the current model, this
228
234
  # should ensure that the model is ready to be saved in the
229
235
  # data source, without corruption of it, only run this process
230
236
  # in case the validate flag is correctly set
231
237
  validate and self._validate(
232
- pre_validate = pre_validate,
233
- post_validate = post_validate
238
+ pre_validate=pre_validate, post_validate=post_validate
234
239
  )
235
240
 
236
241
  # calls the complete set of event handlers for the current
237
242
  # save operation, this should trigger changes in the model
238
- if pre_save: self.pre_save()
239
- if pre_create and is_new: self.pre_create()
240
- if pre_update and not is_new: self.pre_update()
243
+ if pre_save:
244
+ self.pre_save()
245
+ if pre_create and is_new:
246
+ self.pre_create()
247
+ if pre_update and not is_new:
248
+ self.pre_update()
241
249
 
242
250
  # filters the values that are present in the current model
243
251
  # so that only the valid ones are stored in, invalid values
@@ -247,37 +255,42 @@ class ModelAsync(object):
247
255
  # any relation is loaded the reference value is returned instead
248
256
  # of the loaded relation values (required for persistence)
249
257
  model = await self._filter_a(
250
- increment_a = increment_a,
251
- immutables_a = immutables_a,
252
- normalize = True
258
+ increment_a=increment_a, immutables_a=immutables_a, normalize=True
253
259
  )
254
260
 
255
261
  # in case the current model is not new must create a new
256
262
  # model instance and remove the main identifier from it
257
- if not is_new: _model = copy.copy(model); del _model["_id"]
263
+ if not is_new:
264
+ _model = copy.copy(model)
265
+ del _model["_id"]
258
266
 
259
267
  # calls the complete set of callbacks that should be called
260
268
  # before the concrete data store save operation
261
- for callback in before_callbacks: callback(self, model)
269
+ for callback in before_callbacks:
270
+ callback(self, model)
262
271
 
263
272
  # retrieves the reference to the store object to be used and
264
273
  # uses it to store the current model data
265
274
  store = self._get_store_a()
266
275
  if is_new:
267
276
  await store.insert(model)
268
- self.apply(model, safe_a = False)
277
+ self.apply(model, safe_a=False)
269
278
  else:
270
- await store.update({"_id" : model["_id"]}, {"$set" : _model})
279
+ await store.update({"_id": model["_id"]}, {"$set": _model})
271
280
 
272
281
  # calls the complete set of callbacks that should be called
273
282
  # after the concrete data store save operation
274
- for callback in after_callbacks: callback(self, model)
283
+ for callback in after_callbacks:
284
+ callback(self, model)
275
285
 
276
286
  # calls the post save event handlers in order to be able to
277
287
  # execute appropriate post operations
278
- if post_save: self.post_save()
279
- if post_create and is_new: self.post_create()
280
- if post_update and not is_new: self.post_update()
288
+ if post_save:
289
+ self.post_save()
290
+ if post_create and is_new:
291
+ self.post_create()
292
+ if post_update and not is_new:
293
+ self.post_update()
281
294
 
282
295
  # returns the instance that has just been used for the save
283
296
  # operation, this may be used for chaining operations
@@ -285,28 +298,31 @@ class ModelAsync(object):
285
298
 
286
299
  async def delete_a(
287
300
  self,
288
- verify = True,
289
- pre_delete = True,
290
- post_delete = True,
291
- before_callbacks = [],
292
- after_callbacks = []
301
+ verify=True,
302
+ pre_delete=True,
303
+ post_delete=True,
304
+ before_callbacks=[],
305
+ after_callbacks=[],
293
306
  ):
294
307
  # ensures that the current instance is associated with
295
308
  # a concrete model, ready to be persisted in database
296
- if verify: self.assert_is_concrete()
309
+ if verify:
310
+ self.assert_is_concrete()
297
311
 
298
312
  # calls the complete set of event handlers for the current
299
313
  # delete operation, this should trigger changes in the model
300
- if pre_delete: self.pre_delete()
314
+ if pre_delete:
315
+ self.pre_delete()
301
316
 
302
317
  # calls the complete set of callbacks that should be called
303
318
  # before the concrete data store delete operation
304
- for callback in before_callbacks: callback(self)
319
+ for callback in before_callbacks:
320
+ callback(self)
305
321
 
306
322
  # retrieves the reference to the store object to be able to
307
323
  # execute the removal command for the current model
308
324
  store = self._get_store_a()
309
- await store.remove({"_id" : self._id})
325
+ await store.remove({"_id": self._id})
310
326
 
311
327
  # calls the underlying delete handler that may be used to extend
312
328
  # the default delete functionality
@@ -314,29 +330,31 @@ class ModelAsync(object):
314
330
 
315
331
  # calls the complete set of callbacks that should be called
316
332
  # after the concrete data store delete operation
317
- for callback in after_callbacks: callback(self)
333
+ for callback in after_callbacks:
334
+ callback(self)
318
335
 
319
336
  # calls the complete set of event handlers for the current
320
337
  # delete operation, this should trigger changes in the model
321
- if post_delete: self.post_delete()
338
+ if post_delete:
339
+ self.post_delete()
322
340
 
323
341
  async def reload_a(self, *args, **kwargs):
324
342
  is_new = self.is_new()
325
- if is_new: raise exceptions.OperationalError(
326
- message = "Can't reload a new model entity",
327
- code = 412
328
- )
343
+ if is_new:
344
+ raise exceptions.OperationalError(
345
+ message="Can't reload a new model entity", code=412
346
+ )
329
347
  cls = self.__class__
330
- return await cls.get_a(_id = self._id, *args, **kwargs)
348
+ return await cls.get_a(_id=self._id, *args, **kwargs)
331
349
 
332
350
  async def _filter_a(
333
351
  self,
334
- increment_a = True,
335
- immutables_a = False,
336
- normalize = False,
337
- resolve = False,
338
- all = False,
339
- evaluator = "json_v"
352
+ increment_a=True,
353
+ immutables_a=False,
354
+ normalize=False,
355
+ resolve=False,
356
+ all=False,
357
+ evaluator="json_v",
340
358
  ):
341
359
  # creates the model that will hold the "filtered" model
342
360
  # with all the items that conform with the class specification
@@ -364,7 +382,8 @@ class ModelAsync(object):
364
382
  # fields so that a new value is set on the model, note that if
365
383
  # the increment apply is unset the increment operation is ignored
366
384
  for name in increments:
367
- if not increment_a: continue
385
+ if not increment_a:
386
+ continue
368
387
  if name in self.model:
369
388
  model[name] = cls._ensure_min(name, self.model[name])
370
389
  else:
@@ -373,9 +392,11 @@ class ModelAsync(object):
373
392
  # iterates over all the model items to filter the ones
374
393
  # that are not valid for the current class context
375
394
  for name, value in legacy.eager(self.model.items()):
376
- if not name in definition: continue
377
- if immutables_a and name in immutables: continue
378
- value = self._evaluate(name, value, evaluator = evaluator)
395
+ if not name in definition:
396
+ continue
397
+ if immutables_a and name in immutables:
398
+ continue
399
+ value = self._evaluate(name, value, evaluator=evaluator)
379
400
  model[name] = value
380
401
 
381
402
  # in case the normalize flag is set must iterate over all
@@ -384,8 +405,10 @@ class ModelAsync(object):
384
405
  # the normal value that would prevent normalization
385
406
  if normalize:
386
407
  for name, value in legacy.eager(self.model.items()):
387
- if not name in definition: continue
388
- if not hasattr(value, "ref_v"): continue
408
+ if not name in definition:
409
+ continue
410
+ if not hasattr(value, "ref_v"):
411
+ continue
389
412
  model[name] = value.ref_v()
390
413
 
391
414
  # in case the resolution flag is set, it means that a recursive
@@ -395,7 +418,8 @@ class ModelAsync(object):
395
418
  # may imply access to the base data source
396
419
  if resolve:
397
420
  for name, value in legacy.eager(self.model.items()):
398
- if not name in definition: continue
421
+ if not name in definition:
422
+ continue
399
423
  model[name] = cls._resolve(name, value)
400
424
 
401
425
  # in case the all flag is set the extra fields (not present
@@ -404,7 +428,8 @@ class ModelAsync(object):
404
428
  # present in the base map of the current instance
405
429
  if all:
406
430
  for name, value in legacy.eager(self.model.items()):
407
- if name in model: continue
431
+ if name in model:
432
+ continue
408
433
  model[name] = value
409
434
 
410
435
  # returns the model containing the "filtered" items resulting