appier 1.33.4__py2.py3-none-any.whl → 1.34.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.
appier/__init__.py CHANGED
@@ -291,7 +291,15 @@ from .smtp import (
291
291
  header,
292
292
  )
293
293
  from .storage import StorageEngine, BaseEngine, FsEngine
294
- from .structures import OrderedDict, LazyDict, LazyValue, GeneratorFile, lazy_dict, lazy
294
+ from .structures import (
295
+ OrderedDict,
296
+ LazyDict,
297
+ LazyValue,
298
+ GeneratorFile,
299
+ LimitedSizeDict,
300
+ lazy_dict,
301
+ lazy,
302
+ )
295
303
  from .typesf import (
296
304
  AbstractType,
297
305
  Type,
appier/base.py CHANGED
@@ -94,7 +94,7 @@ NAME = "appier"
94
94
  """ The name to be used to describe the framework while working
95
95
  on its own environment, this is just a descriptive value """
96
96
 
97
- VERSION = "1.33.4"
97
+ VERSION = "1.34.0"
98
98
  """ The version of the framework that is currently installed
99
99
  this value may be used for debugging/diagnostic purposes """
100
100
 
@@ -4207,6 +4207,35 @@ class App(
4207
4207
  )
4208
4208
  line = line.decode(encoding, "ignore") if legacy.is_bytes(line) else line
4209
4209
 
4210
+ # runs the template for the path, so that it's possible to better
4211
+ # understand the origin of the path execution
4212
+ path_f = template % (path, lineno, context)
4213
+
4214
+ # in case the path to the file does not exists, then there's nothing
4215
+ # remaining to be done in terms of specific stack format
4216
+ if not path or not os.path.exists(path):
4217
+ # creates the "file-less" item dictionary to be used to a certain
4218
+ # degree of debuggability
4219
+ item_d = dict(
4220
+ id=id,
4221
+ path=path,
4222
+ path_f=path_f,
4223
+ line=line,
4224
+ lineno=lineno,
4225
+ context=context,
4226
+ lines=[],
4227
+ lines_b=[],
4228
+ )
4229
+ cls._extended_handle(item_d)
4230
+
4231
+ # adds the newly created formatted item to the list of formatted
4232
+ # items to be returned at the end of the method execution
4233
+ formatted.append(item_d)
4234
+
4235
+ # continues the loop as this is not a valid file and no
4236
+ # further information can be retrieved
4237
+ continue
4238
+
4210
4239
  # opens the current file in stack trace and reads the complete
4211
4240
  # contents from it so that the target lines may be read
4212
4241
  file = open(path, "rb")
@@ -4220,7 +4249,7 @@ class App(
4220
4249
  contents_d = contents.decode(encoding, "ignore")
4221
4250
 
4222
4251
  # generates a new random identifier for the current stack item
4223
- # this is going to be used to identify it univocally
4252
+ # this is going to be used to identify it uniquely
4224
4253
  id = str(uuid.uuid4())
4225
4254
 
4226
4255
  # normalizes the currently extracted path by ensuring that it's
@@ -4259,10 +4288,6 @@ class App(
4259
4288
  # directly print the complete set of lines in the structure
4260
4289
  lines_b = legacy.u("\n").join((line["line"] for line in lines))
4261
4290
 
4262
- # runs the template for the path, so that it's possible to better
4263
- # understand the origin of the path execution
4264
- path_f = template % (path, lineno, context)
4265
-
4266
4291
  # creates the dictionary that contains the complete set of information
4267
4292
  # about the current line in the stack and then runs the pipeline of
4268
4293
  # operation in it to properly process it
appier/http.py CHANGED
@@ -1120,7 +1120,7 @@ def _quote(values, plus=False, safe="/"):
1120
1120
 
1121
1121
  final = dict()
1122
1122
 
1123
- for key, value in values.items():
1123
+ for key, value in values:
1124
1124
  key = method(key, safe=safe)
1125
1125
  value = method(value[0], safe=safe)
1126
1126
  final[key] = value
appier/part.pyi CHANGED
@@ -1 +1,3 @@
1
- class Part: ...
1
+ class Part:
2
+ def load(self): ...
3
+ def unload(self): ...
appier/structures.py CHANGED
@@ -29,6 +29,7 @@ __license__ = "Apache License, Version 2.0"
29
29
  """ The license for the module """
30
30
 
31
31
  import os
32
+ import collections
32
33
 
33
34
 
34
35
  class OrderedDict(dict):
@@ -280,5 +281,30 @@ class GeneratorFile(object):
280
281
  self._generator.close()
281
282
 
282
283
 
284
+ class LimitedSizeDict(dict):
285
+ """
286
+ Size limited dictionary that removes the oldest item
287
+ once the maximum size is reached.
288
+
289
+ Useful for caching purposes where the cache should have
290
+ a limited size and the oldest items should be removed
291
+ once the maximum size is reached, constrains memory usage.
292
+ """
293
+
294
+ def __init__(self, max_size=128):
295
+ dict.__init__(self)
296
+ self.max_size = max_size
297
+ self._order = collections.deque()
298
+
299
+ def __setitem__(self, key, value):
300
+ if key in self:
301
+ self._order.remove(key)
302
+ elif len(self) >= self.max_size:
303
+ oldest_key = self._order.popleft()
304
+ del self[oldest_key]
305
+ dict.__setitem__(self, key, value)
306
+ self._order.append(key)
307
+
308
+
283
309
  lazy_dict = LazyDict
284
310
  lazy = LazyValue
appier/test/scheduler.py CHANGED
@@ -134,6 +134,33 @@ class CronSchedulerTest(unittest.TestCase):
134
134
  scheduler.next_run(), datetime.datetime(2013, 4, 10, hour=0, minute=11)
135
135
  )
136
136
 
137
+ def test_duplicate(self):
138
+ state = dict(value=0)
139
+
140
+ def increment():
141
+ state["value"] += 1
142
+
143
+ scheduler = appier.CronScheduler(None)
144
+ task1 = scheduler.schedule(
145
+ lambda: increment(),
146
+ appier.SchedulerDate(minutes=11, days_of_month=10, days_of_week=2),
147
+ now=datetime.datetime(2013, 1, 1, hour=1, minute=1),
148
+ )
149
+ task2 = scheduler.schedule(
150
+ lambda: increment(),
151
+ appier.SchedulerDate(minutes=11, days_of_month=10, days_of_week=2),
152
+ now=datetime.datetime(2013, 1, 1, hour=1, minute=1),
153
+ )
154
+ self.assertNotEqual(task1, None)
155
+ self.assertNotEqual(task2, None)
156
+ self.assertEqual(isinstance(task1, appier.SchedulerTask), True)
157
+ self.assertEqual(isinstance(task2, appier.SchedulerTask), True)
158
+ self.assertEqual(task1.enabled, True)
159
+ self.assertEqual(task2.enabled, True)
160
+ self.assertEqual(
161
+ scheduler.next_run(), datetime.datetime(2013, 4, 10, hour=0, minute=11)
162
+ )
163
+
137
164
 
138
165
  class SchedulerDateTest(unittest.TestCase):
139
166
 
appier/test/structures.py CHANGED
@@ -220,3 +220,41 @@ class LazyDictTest(unittest.TestCase):
220
220
  self.assertEqual(struct["first"], 1)
221
221
  self.assertEqual(isinstance(struct.__getitem__("second", True), int), True)
222
222
  self.assertEqual(struct["second"], 2)
223
+
224
+
225
+ class LimitedSizeDictTest(unittest.TestCase):
226
+ def setUp(self):
227
+ self.dict_size = 1024
228
+ self.limited_dict = appier.LimitedSizeDict(self.dict_size)
229
+
230
+ def test_add_single_item(self):
231
+ self.limited_dict["first"] = "first_value"
232
+ self.assertIn("first", self.limited_dict)
233
+ self.assertEqual(self.limited_dict["first"], "first_value")
234
+
235
+ def test_exceeding_size_limit(self):
236
+ for index in range(self.dict_size + 1):
237
+ self.limited_dict["key_%d" % index] = "value_%d" % index
238
+ self.assertNotIn("key_0", self.limited_dict)
239
+ self.assertIn("key_%d" % self.dict_size, self.limited_dict)
240
+
241
+ def test_maintaining_order(self):
242
+ for index in range(self.dict_size):
243
+ self.limited_dict["key_%d" % index] = "value_%d" % index
244
+ self.limited_dict["new_key"] = "new_value"
245
+ self.assertNotIn("key_0", self.limited_dict)
246
+ self.assertIn("new_key", self.limited_dict)
247
+ self.assertEqual(self.limited_dict["new_key"], "new_value")
248
+
249
+ def test_update_existing_key(self):
250
+ self.limited_dict["first"] = "first_value"
251
+ self.limited_dict["first"] = "second_value"
252
+ self.assertEqual(self.limited_dict["first"], "second_value")
253
+ self.assertEqual(len(self.limited_dict), 1)
254
+
255
+ def test_repr(self):
256
+ self.limited_dict["first"] = "first_value"
257
+ self.limited_dict["second"] = "second_value"
258
+ repr_str = repr(self.limited_dict)
259
+ self.assertIn("'first': 'first_value'", repr_str)
260
+ self.assertIn("'second': 'second_value'", repr_str)
appier/typesf.py CHANGED
@@ -28,9 +28,11 @@ __copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
28
28
  __license__ = "Apache License, Version 2.0"
29
29
  """ The license for the module """
30
30
 
31
+ import os
31
32
  import uuid
32
33
  import base64
33
34
  import hashlib
35
+ import mimetypes
34
36
 
35
37
  from . import util
36
38
  from . import crypt
@@ -85,6 +87,17 @@ class File(AbstractType):
85
87
  def __len__(self):
86
88
  return self.size
87
89
 
90
+ @classmethod
91
+ def open(cls, path):
92
+ name = os.path.basename(path)
93
+ content_type, _encoding = mimetypes.guess_type(name)
94
+ file = open(path, "rb")
95
+ try:
96
+ data = file.read()
97
+ finally:
98
+ file.close()
99
+ return cls((name, content_type, data))
100
+
88
101
  def build_d(self, file_d, name="default"):
89
102
  self.build_t((name, None, file_d))
90
103
 
@@ -192,6 +205,15 @@ class File(AbstractType):
192
205
  engine = self._engine()
193
206
  return engine.cleanup(self)
194
207
 
208
+ def save(self, path=None):
209
+ path = path or self.file_name
210
+ data = self.read()
211
+ file = open(path, "wb")
212
+ try:
213
+ file.write(data)
214
+ finally:
215
+ file.close()
216
+
195
217
  def json_v(self, *args, **kwargs):
196
218
  if not self.is_valid():
197
219
  return None
appier/util.py CHANGED
@@ -503,7 +503,7 @@ def install_pip(package, delayed=False, isolated=True, user=None):
503
503
  result = pip_main(args)
504
504
  if result == 0:
505
505
  return
506
- raise exceptions.OperationalError(message="pip error")
506
+ raise exceptions.OperationalError(message="pip error, exit code (%d)" % result)
507
507
 
508
508
 
509
509
  def install_pip_s(package, delayed=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: appier
3
- Version: 1.33.4
3
+ Version: 1.34.0
4
4
  Summary: Appier Framework
5
5
  Home-page: http://appier.hive.pt
6
6
  Author: Hive Solutions Lda.
@@ -1,4 +1,4 @@
1
- appier/__init__.py,sha256=2UqTnGyy5JGVUsLXxRzfr7br1oKmY6wpwgf0bH89dgM,9355
1
+ appier/__init__.py,sha256=-Xd1j5egjwXV6yoKAkcPVWIZEnpPK_SJTTmQjDrW93E,9413
2
2
  appier/amqp.py,sha256=etYxUlfaK27Og_9FJ6qCgNLSYhnz9XgVhIhSmD2ITW4,3852
3
3
  appier/api.py,sha256=s5ZJs0tgR3wktAhLZ7Vuhbd_4lWPsGJSEx0ptkqLxvM,14369
4
4
  appier/asgi.py,sha256=sKQH9K_QEDPbBSk3AWcvHo7Lfl5yawIo6NcM0z1qQCw,12100
@@ -6,7 +6,7 @@ appier/async_neo.py,sha256=gQpyT1-nZE6Uynh5FEmFl5kfXkxChdUsgiKgwlaNw5E,5446
6
6
  appier/async_old.py,sha256=m3BFqHVPCRuIZ9L2sGYhCQPEwuCNKBO2y7SKM0dbtj8,9194
7
7
  appier/asynchronous.py,sha256=a1LQa3wbGMaXELhF7W71dRr1klPOj6x-ST9EInvPhtU,1757
8
8
  appier/asynchronous.pyi,sha256=5CpLkpKcUq09woMOVYwpl24Pli0A8Xu6nXoT20xDQ-o,104
9
- appier/base.py,sha256=Oi05Ho4qbmFSNUxrc2tkWXeEHydYSYsbTZxtMB_-7wQ,269068
9
+ appier/base.py,sha256=3acfd3DK2alileixYuQuM3YkgVbroyBYlXHJdEz5Flg,270121
10
10
  appier/base.pyi,sha256=Vx8idYnMvP3nLwGdd2LJKGc0RFpT6iav77YMcBpcr4o,2515
11
11
  appier/bus.py,sha256=AMC2UXlaf6rzhzlIyTHkzbfb6tdfPBiOii4lpl9AAwg,7670
12
12
  appier/bus.pyi,sha256=W6_MIBpvDq468wfT5XM2WpPuR3O49QiZMOSCdMsJWos,182
@@ -31,7 +31,7 @@ appier/extra_old.py,sha256=5mwfHIrz6Um7-QpXiqnEm_lXXpWhNvnMgPWIFVegItU,1991
31
31
  appier/geo.py,sha256=EEPh8oX7Uk7iert-XKt7HCw0swicc8lFAv7XFeQSbZ8,4877
32
32
  appier/git.py,sha256=-neM2mxqlyiCNu17CpLyiz3RN7Ti6LtAx5L1tipxAGA,9785
33
33
  appier/graph.py,sha256=jSgu-ookILujLm4ivW14SymVmuI1nbjbVZMgRS2ehOg,4054
34
- appier/http.py,sha256=Xk871N8yCN39OGkm5WqQ5M9lnI9teG9t9_-A5zdVwy4,38635
34
+ appier/http.py,sha256=qmQ3FP54oWtunPvQHePtkOS2I9ymLjkqzqoICYhhPVQ,38627
35
35
  appier/legacy.py,sha256=o_oJ_2lqZELZKwkvfGt0aDam6ZSZiZiL7FYygNjrDbY,15881
36
36
  appier/log.py,sha256=jhV7ub5nZwrLzY7x-tZDJfb8tcsXu-ndWxxrCspBUdU,12825
37
37
  appier/meta.py,sha256=rgBLOjD6QU9CGYsbCQS3Fy4iY14uk1-Kd8ljkfmxxzc,7168
@@ -42,7 +42,7 @@ appier/model_a.py,sha256=c6XpG4oIelXNDK0uicsZ69-f6isUgmh5-29F61PZt9c,16176
42
42
  appier/mongo.py,sha256=t-257ReqFWnEw0bQkDvS4McsPsc6AuKoQXIFHxlrOMw,10891
43
43
  appier/observer.py,sha256=T0QpkxdkcNuEOZJdmJT5nFSJ2e-0VPbGveX5YKtt7mA,4519
44
44
  appier/part.py,sha256=LW3ejynHMyxH-iP3dUqmPPPkFYVRPHjwNF4kR1W-_jY,4380
45
- appier/part.pyi,sha256=BHClxTBbnefBTqp0npxgDYNKevFSJlKxhHDg14OLSSs,16
45
+ appier/part.pyi,sha256=1HHFwLw4DGT_neDyJjQ0yojNgx42G06A65uXLgWkUJk,62
46
46
  appier/preferences.py,sha256=3JrH2f8Drzw0zNTDA4MtOYMzhUaBm3Rr4d6E95mL49o,8287
47
47
  appier/preferences.pyi,sha256=uYVgvDq2f4KWlLRY-C8bi6xry_utFKDeToGHH5aptxc,197
48
48
  appier/queuing.py,sha256=6AJlRVdd5JunzChu8Q1U-9Biq4vQUHTuJFC8qs2wzCs,8097
@@ -56,9 +56,9 @@ appier/session.pyi,sha256=JBYcT1wZ9mKBBk9iTw2GDJENB8PWFzxXjIr9oXKe85Q,239
56
56
  appier/settings.py,sha256=28tTRJIZ47iwXqUs6YtUoW7ntEVsp_0-QfONVDGA1sg,1136
57
57
  appier/smtp.py,sha256=5tW0dOEm03kejLoQyhRQFjgjYRd-jhb-VlhEKDb4Zn0,3434
58
58
  appier/storage.py,sha256=Qp_CMCDg85q35n9lggNVBZRWwlGrQ-nLYNj0W7Mnbec,7470
59
- appier/structures.py,sha256=YSv0YwZk8tRjPn9N6YEtYoPQ72PZQkfLEtTI_H6jXV8,8076
60
- appier/typesf.py,sha256=iby3f-3XayIOu9FgjHS6I9L3UoaJntHtG73eKXgIgFE,36882
61
- appier/util.py,sha256=L9ea0WTmvcelkgKSztHXgQNRhlireKcrOlXpkgrrkRo,86776
59
+ appier/structures.py,sha256=lwAEiz4k1O4bywGcfHvOvZJusN-lUwcviihR9aZLMtY,8883
60
+ appier/typesf.py,sha256=XIYRQ2M7ErjvjB0SAu4Rkv3aYUOmWhqfZy3sUYyHtpM,37451
61
+ appier/util.py,sha256=e2682yvqkx8ck6p1RsgoBcCwPHw-QgVfzzTZRYXEBU8,86801
62
62
  appier/validation.py,sha256=vxb289skd7RfGEQVmpkQxYb71JdAfLu17QaKOuAAEEQ,22388
63
63
  appier/res/static/css/base.css,sha256=J9zLozd57KoslAsrsj2a42glGTObxbkrrckR-W-_f2A,6127
64
64
  appier/res/static/images/favicon.ico,sha256=fAL8DLx_0Yl6jwpRG_K6S7g3yXxr9Hyjn3JmqyU8t7U,1150
@@ -84,16 +84,16 @@ appier/test/part.py,sha256=yHQxg2hdfojJMO2Fs0NLvYTX3uCJ6xwgsD4VaKV04ak,3352
84
84
  appier/test/preferences.py,sha256=QyqoBddOCAmhkcgUYAJ5RgypqrkGL6x6s6a0jsIwE9w,2890
85
85
  appier/test/queuing.py,sha256=Lq7gI5QkZfXPf_MUzmRd6LHHknf9EryF-MauWjDLJn0,5710
86
86
  appier/test/request.py,sha256=h3DdvhEMARpYTp7eRcJ3-qLQyG-a8PHqSnWY52BiOeU,8176
87
- appier/test/scheduler.py,sha256=Y2r91OQ-bt5bzkT1x5otdK2k4RO77c9oLgA5oZ-8Hbg,6360
87
+ appier/test/scheduler.py,sha256=VlbwpoYYI5hgqlpjcyVcMnSHnLGCCCeMi6kyPD7YxV4,7440
88
88
  appier/test/serialize.py,sha256=roX01n86AQfnPxzPVdIjdIUQWC_x0C_HE3hNgF37ci8,2120
89
89
  appier/test/session.py,sha256=KdiYLLB5autIEu1sHwOuYJXVd0y6RMPgg0ITBuRTMfA,4419
90
90
  appier/test/smtp.py,sha256=XJNa0ZTmabdrX8MfVMgcqBxabvBfHgIimvozdziP4_E,1826
91
- appier/test/structures.py,sha256=Uylzx5vLNORXKG5wsPMdIreW1SlHWGnmSQouqQNG6E8,6745
91
+ appier/test/structures.py,sha256=MRjUFRlnJi-i7YGWW5y792JbJwicNvOIzVAS220tgeQ,8367
92
92
  appier/test/typesf.py,sha256=KHumQFzx7wPZSCb8_mpIwobhIy2Fh_0XYviwPjXMbKI,9680
93
93
  appier/test/util.py,sha256=SSHuBAZQ0TcXLDYI0xUvmwId5nndDZb88s_5Y-Hqibw,46884
94
94
  appier/test/validation.py,sha256=riOCsGKob1P5jnbcB5qGZ45ApimNAVS0byg9v_uUdrk,4952
95
- appier-1.33.4.dist-info/LICENSE,sha256=Pd-b5cKP4n2tFDpdx27qJSIq0d1ok0oEcGTlbtL6QMU,11560
96
- appier-1.33.4.dist-info/METADATA,sha256=h_0pk6WPiXQ-jKGM8M1ZytS8VowhKAvStdUmEyMfBec,1920
97
- appier-1.33.4.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
98
- appier-1.33.4.dist-info/top_level.txt,sha256=Z2e_Y1ya06a554WwQZkfNRiaaQxqsdaPtBzrck384Lo,7
99
- appier-1.33.4.dist-info/RECORD,,
95
+ appier-1.34.0.dist-info/LICENSE,sha256=Pd-b5cKP4n2tFDpdx27qJSIq0d1ok0oEcGTlbtL6QMU,11560
96
+ appier-1.34.0.dist-info/METADATA,sha256=DYxSVNTiozLHgTC4dR7-UFrJzV9Lq6XrsTUdvyAmZ_s,1920
97
+ appier-1.34.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
98
+ appier-1.34.0.dist-info/top_level.txt,sha256=Z2e_Y1ya06a554WwQZkfNRiaaQxqsdaPtBzrck384Lo,7
99
+ appier-1.34.0.dist-info/RECORD,,