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/mongo.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"
@@ -46,14 +37,20 @@ from . import config
46
37
  from . import common
47
38
  from . import exceptions
48
39
 
49
- try: import pymongo
50
- except ImportError: pymongo = None
40
+ try:
41
+ import pymongo
42
+ except ImportError:
43
+ pymongo = None
51
44
 
52
- try: import bson.json_util
53
- except ImportError: bson = None
45
+ try:
46
+ import bson.json_util
47
+ except ImportError:
48
+ bson = None
54
49
 
55
- try: import motor.motor_asyncio
56
- except ImportError: motor = None
50
+ try:
51
+ import motor.motor_asyncio
52
+ except ImportError:
53
+ motor = None
57
54
 
58
55
  URL = "mongodb://localhost"
59
56
  """ The default URL to be used for the connection when
@@ -67,44 +64,52 @@ connection_a = None
67
64
  """ The global connection reference for the async version
68
65
  of the Mongo client """
69
66
 
70
- class Mongo(object):
71
67
 
72
- def __init__(self, url = None):
68
+ class Mongo(object):
69
+ def __init__(self, url=None):
73
70
  self.url = url
74
71
  self._connection = None
75
72
  self._db = None
76
73
 
77
- def get_connection(self, url = None, connect = False):
78
- if self._connection: return self._connection
74
+ def get_connection(self, url=None, connect=False):
75
+ if self._connection:
76
+ return self._connection
79
77
  url_c = config.conf("MONGOHQ_URL", None)
80
78
  url_c = config.conf("MONGOLAB_URI", url_c)
81
79
  url_c = config.conf("MONGO_URL", url_c)
82
80
  url = url or self.url or url_c or URL
83
- if is_new(): self._connection = _pymongo().MongoClient(url, connect = connect)
84
- else: self._connection = _pymongo().Connection(url)
81
+ if is_new():
82
+ self._connection = _pymongo().MongoClient(url, connect=connect)
83
+ else:
84
+ self._connection = _pymongo().Connection(url)
85
85
  return self._connection
86
86
 
87
87
  def reset_connection(self):
88
- if not self._connection: return
89
- if is_new(): self._connection.close()
90
- else: self._connection.disconnect()
88
+ if not self._connection:
89
+ return
90
+ if is_new():
91
+ self._connection.close()
92
+ else:
93
+ self._connection.disconnect()
91
94
  self._connection = None
92
95
 
93
96
  def get_db(self, name):
94
- if self._db: return self._db
97
+ if self._db:
98
+ return self._db
95
99
  connection = self.get_connection()
96
100
  self._db = connection[name]
97
101
  return self._db
98
102
 
99
- class MongoAsync(object):
100
103
 
101
- def __init__(self, url = None):
104
+ class MongoAsync(object):
105
+ def __init__(self, url=None):
102
106
  self.url = url
103
107
  self._connection = None
104
108
  self._db = None
105
109
 
106
- def get_connection(self, url = None, connect = False):
107
- if self._connection: return self._connection
110
+ def get_connection(self, url=None, connect=False):
111
+ if self._connection:
112
+ return self._connection
108
113
  url_c = config.conf("MONGOHQ_URL", None)
109
114
  url_c = config.conf("MONGOLAB_URI", url_c)
110
115
  url_c = config.conf("MONGO_URL", url_c)
@@ -113,57 +118,76 @@ class MongoAsync(object):
113
118
  return self._connection
114
119
 
115
120
  def reset_connection(self):
116
- if not self._connection: return
121
+ if not self._connection:
122
+ return
117
123
  self._connection.disconnect()
118
124
  self._connection = None
119
125
 
120
126
  def get_db(self, name):
121
- if self._db: return self._db
127
+ if self._db:
128
+ return self._db
122
129
  connection = self.get_connection()
123
130
  self._db = connection[name]
124
131
  return self._db
125
132
 
126
- class MongoEncoder(json.JSONEncoder):
127
133
 
134
+ class MongoEncoder(json.JSONEncoder):
128
135
  def default(self, obj, **kwargs):
129
- if not bson: return json.JSONEncoder.default(self, obj, **kwargs)
130
- if isinstance(obj, bson.objectid.ObjectId): return str(obj)
131
- if isinstance(obj, legacy.BYTES): return legacy.str(obj, encoding = "utf-8")
132
- else: return json.JSONEncoder.default(self, obj, **kwargs)
136
+ if not bson:
137
+ return json.JSONEncoder.default(self, obj, **kwargs)
138
+ if isinstance(obj, bson.objectid.ObjectId):
139
+ return str(obj)
140
+ if isinstance(obj, legacy.BYTES):
141
+ return legacy.str(obj, encoding="utf-8")
142
+ else:
143
+ return json.JSONEncoder.default(self, obj, **kwargs)
144
+
133
145
 
134
- def get_connection(url = URL, connect = False):
146
+ def get_connection(url=URL, connect=False):
135
147
  global connection
136
- if connection: return connection
148
+ if connection:
149
+ return connection
137
150
  url = config.conf("MONGOHQ_URL", url)
138
151
  url = config.conf("MONGOLAB_URI", url)
139
152
  url = config.conf("MONGO_URL", url)
140
- if is_new(): connection = _pymongo().MongoClient(url, connect = connect)
141
- else: connection = _pymongo().Connection(url)
153
+ if is_new():
154
+ connection = _pymongo().MongoClient(url, connect=connect)
155
+ else:
156
+ connection = _pymongo().Connection(url)
142
157
  return connection
143
158
 
144
- def get_connection_a(url = URL, connect = False):
159
+
160
+ def get_connection_a(url=URL, connect=False):
145
161
  global connection_a
146
- if connection_a: return connection_a
162
+ if connection_a:
163
+ return connection_a
147
164
  url = config.conf("MONGOHQ_URL", url)
148
165
  url = config.conf("MONGOLAB_URI", url)
149
166
  url = config.conf("MONGO_URL", url)
150
167
  connection_a = _motor().AsyncIOMotorClient(url)
151
168
  return connection_a
152
169
 
170
+
153
171
  def reset_connection():
154
172
  global connection
155
- if not connection: return
156
- if is_new(): connection.close()
157
- else: connection.disconnect()
173
+ if not connection:
174
+ return
175
+ if is_new():
176
+ connection.close()
177
+ else:
178
+ connection.disconnect()
158
179
  connection = None
159
180
 
181
+
160
182
  def reset_connection_a():
161
183
  global connection_a
162
- if not connection_a: return
184
+ if not connection_a:
185
+ return
163
186
  connection_a.close()
164
187
  connection_a = None
165
188
 
166
- def get_db(name = None, get_connection = get_connection):
189
+
190
+ def get_db(name=None, get_connection=get_connection):
167
191
  url = config.conf("MONGOHQ_URL", None)
168
192
  url = config.conf("MONGOLAB_URI", url)
169
193
  url = config.conf("MONGO_URL", url)
@@ -175,7 +199,8 @@ def get_db(name = None, get_connection = get_connection):
175
199
  db = connection[name]
176
200
  return db
177
201
 
178
- def get_db_a(name = None, get_connection = get_connection):
202
+
203
+ def get_db_a(name=None, get_connection=get_connection):
179
204
  url = config.conf("MONGOHQ_URL", None)
180
205
  url = config.conf("MONGOLAB_URI", url)
181
206
  url = config.conf("MONGO_URL", url)
@@ -187,125 +212,176 @@ def get_db_a(name = None, get_connection = get_connection):
187
212
  db = connection[name]
188
213
  return db
189
214
 
190
- def drop_db(name = None, get_connection = get_connection):
191
- db = get_db(name = name)
215
+
216
+ def drop_db(name=None, get_connection=get_connection):
217
+ db = get_db(name=name)
192
218
  names = _list_names(db)
193
219
  for name in names:
194
- if name.startswith("system."): continue
220
+ if name.startswith("system."):
221
+ continue
195
222
  db.drop_collection(name)
196
223
  connection = get_connection()
197
224
  connection.drop_database(db.name)
198
225
 
199
- def drop_db_a(name = None, get_connection = get_connection):
200
- db = get_db_a(name = name)
226
+
227
+ def drop_db_a(name=None, get_connection=get_connection):
228
+ db = get_db_a(name=name)
201
229
  names = _list_names(db)
202
230
  for name in names:
203
- if name.startswith("system."): continue
231
+ if name.startswith("system."):
232
+ continue
204
233
  db.drop_collection(name)
205
234
  connection = get_connection_a()
206
235
  connection.drop_database(db.name)
207
236
 
237
+
208
238
  def object_id(value):
209
239
  return bson.ObjectId(value)
210
240
 
241
+
211
242
  def dumps(*args):
212
- return json.dumps(default = serialize, *args)
243
+ return json.dumps(default=serialize, *args)
244
+
213
245
 
214
246
  def serialize(obj):
215
- if isinstance(obj, common.model().Model): return obj.model
216
- if isinstance(obj, typesf.AbstractType): return obj.json_v()
247
+ if isinstance(obj, common.model().Model):
248
+ return obj.model
249
+ if isinstance(obj, typesf.AbstractType):
250
+ return obj.json_v()
217
251
  return bson.json_util.default(obj)
218
252
 
219
- def directions(all = False):
253
+
254
+ def directions(all=False):
220
255
  return (
221
- _pymongo().ASCENDING,
222
- _pymongo().DESCENDING,
223
- _pymongo().HASHED
224
- ) if all else (
225
- _pymongo().ASCENDING,
226
- _pymongo().DESCENDING
256
+ (_pymongo().ASCENDING, _pymongo().DESCENDING, _pymongo().HASHED)
257
+ if all
258
+ else (_pymongo().ASCENDING, _pymongo().DESCENDING)
227
259
  )
228
260
 
261
+
229
262
  def is_mongo(obj):
230
- if bson and isinstance(obj, bson.ObjectId): return True
231
- if bson and isinstance(obj, bson.DBRef): return True
263
+ if bson and isinstance(obj, bson.ObjectId):
264
+ return True
265
+ if bson and isinstance(obj, bson.DBRef):
266
+ return True
232
267
  return False
233
268
 
234
- def is_new(major = 3, minor = 0, patch = 0):
269
+
270
+ def is_new(major=3, minor=0, patch=0):
235
271
  _major, _minor, _patch = _version_t()
236
- if _major > major: return True
237
- elif _major < major: return False
238
- if _minor > minor: return True
239
- elif _minor < minor: return False
240
- if _patch >= patch: return True
241
- else: return False
272
+ if _major > major:
273
+ return True
274
+ elif _major < major:
275
+ return False
276
+ if _minor > minor:
277
+ return True
278
+ elif _minor < minor:
279
+ return False
280
+ if _patch >= patch:
281
+ return True
282
+ else:
283
+ return False
284
+
242
285
 
243
286
  def _list_names(db, *args, **kwargs):
244
- if is_new(3, 7): return db.list_collection_names()
245
- else: return db.collection_names()
287
+ if is_new(3, 7):
288
+ return db.list_collection_names()
289
+ else:
290
+ return db.collection_names()
291
+
246
292
 
247
293
  def _count(store, *args, **kwargs):
248
- if len(args) == 0: args = [{}]
249
- if is_new(3, 7): return store.count_documents(*args, **kwargs)
294
+ if len(args) == 0:
295
+ args = [{}]
296
+ if is_new(3, 7):
297
+ return store.count_documents(*args, **kwargs)
250
298
  return store.count(*args, **kwargs)
251
299
 
300
+
252
301
  def _count_documents(store, *args, **kwargs):
253
- if len(args) == 0: args = [{}]
254
- if is_new(3, 7): return store.count_documents(*args, **kwargs)
302
+ if len(args) == 0:
303
+ args = [{}]
304
+ if is_new(3, 7):
305
+ return store.count_documents(*args, **kwargs)
255
306
  result = store.find(*args, **kwargs)
256
307
  return result.count()
257
308
 
309
+
258
310
  def _store_find_and_modify(store, *args, **kwargs):
259
- if is_new(): return store.find_one_and_update(*args, **kwargs)
260
- else: return store.find_and_modify(*args, **kwargs)
311
+ if is_new():
312
+ return store.find_one_and_update(*args, **kwargs)
313
+ else:
314
+ return store.find_and_modify(*args, **kwargs)
315
+
261
316
 
262
317
  def _store_insert(store, *args, **kwargs):
263
- if is_new(): return store.insert_one(*args, **kwargs)
264
- else: return store.insert(*args, **kwargs)
318
+ if is_new():
319
+ return store.insert_one(*args, **kwargs)
320
+ else:
321
+ return store.insert(*args, **kwargs)
322
+
265
323
 
266
324
  def _store_update(store, *args, **kwargs):
267
- if is_new(): return store.update_one(*args, **kwargs)
268
- else: return store.update(*args, **kwargs)
325
+ if is_new():
326
+ return store.update_one(*args, **kwargs)
327
+ else:
328
+ return store.update(*args, **kwargs)
329
+
269
330
 
270
331
  def _store_remove(store, *args, **kwargs):
271
- if is_new(): return store.delete_many(*args, **kwargs)
272
- else: return store.remove(*args, **kwargs)
332
+ if is_new():
333
+ return store.delete_many(*args, **kwargs)
334
+ else:
335
+ return store.remove(*args, **kwargs)
336
+
273
337
 
274
338
  def _store_ensure_index(store, *args, **kwargs):
275
339
  kwargs["background"] = kwargs.get("background", True)
276
- if is_new(): return store.create_index(*args, **kwargs)
277
- else: return store.ensure_index(*args, **kwargs)
340
+ if is_new():
341
+ return store.create_index(*args, **kwargs)
342
+ else:
343
+ return store.ensure_index(*args, **kwargs)
344
+
278
345
 
279
346
  def _store_ensure_index_many(store, *args, **kwargs):
280
347
  directions_l = kwargs.pop("directions", None)
281
- if directions_l == "all": directions_l = directions(all = True)
282
- elif directions_l == None: directions_l = directions()
348
+ if directions_l == "all":
349
+ directions_l = directions(all=True)
350
+ elif directions_l == None:
351
+ directions_l = directions()
283
352
  for direction in directions_l:
284
353
  _args = list(args)
285
354
  _args[0] = [(_args[0], direction)]
286
355
  _store_ensure_index(store, *_args, **kwargs)
287
356
 
357
+
288
358
  def _version_t():
289
359
  pymongo_l = _pymongo()
290
- if hasattr(pymongo_l, "_version_t"): return pymongo_l._version_t
360
+ if hasattr(pymongo_l, "_version_t"):
361
+ return pymongo_l._version_t
291
362
  version_l = pymongo.version.split(".", 2)
292
- if len(version_l) == 2: version_l.append("0")
363
+ if len(version_l) == 2:
364
+ version_l.append("0")
293
365
  major_s, minor_s, patch_s = version_l
294
366
  pymongo_l._version_t = (int(major_s), int(minor_s), int(patch_s))
295
367
  return pymongo_l._version_t
296
368
 
297
- def _pymongo(verify = True):
298
- if verify: util.verify(
299
- not pymongo == None,
300
- message = "PyMongo library not available",
301
- exception = exceptions.OperationalError
302
- )
369
+
370
+ def _pymongo(verify=True):
371
+ if verify:
372
+ util.verify(
373
+ not pymongo == None,
374
+ message="PyMongo library not available",
375
+ exception=exceptions.OperationalError,
376
+ )
303
377
  return pymongo
304
378
 
305
- def _motor(verify = True):
306
- if verify: util.verify(
307
- not motor == None,
308
- message = "Motor library not available",
309
- exception = exceptions.OperationalError
310
- )
379
+
380
+ def _motor(verify=True):
381
+ if verify:
382
+ util.verify(
383
+ not motor == None,
384
+ message="Motor library not available",
385
+ exception=exceptions.OperationalError,
386
+ )
311
387
  return motor.motor_asyncio
appier/observer.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"
@@ -39,6 +30,7 @@ __license__ = "Apache License, Version 2.0"
39
30
 
40
31
  from . import util
41
32
 
33
+
42
34
  class Observable(object):
43
35
  """
44
36
  The base class that implements the observable
@@ -68,19 +60,22 @@ class Observable(object):
68
60
  return name_f
69
61
 
70
62
  @classmethod
71
- def bind_g(cls, name, method, oneshot = False):
72
- if oneshot: method.oneshot = oneshot
63
+ def bind_g(cls, name, method, oneshot=False):
64
+ if oneshot:
65
+ method.oneshot = oneshot
73
66
  name_f = cls.name_f(name)
74
67
  methods = cls._events_g.get(name_f, [])
75
68
  methods.append(method)
76
69
  cls._events_g[name_f] = methods
77
70
 
78
71
  @classmethod
79
- def unbind_g(cls, name, method = None):
72
+ def unbind_g(cls, name, method=None):
80
73
  name_f = cls.name_f(name)
81
74
  methods = cls._events_g.get(name_f, [])
82
- if method: methods.remove(method)
83
- else: del methods[:]
75
+ if method:
76
+ methods.remove(method)
77
+ else:
78
+ del methods[:]
84
79
 
85
80
  @classmethod
86
81
  def trigger_g(cls, name, *args, **kwargs):
@@ -89,12 +84,16 @@ class Observable(object):
89
84
  methods = cls._events_g.get(name_f, [])
90
85
  for method in methods:
91
86
  method(*args, **kwargs)
92
- if not hasattr(method, "oneshot"): continue
93
- if not method.oneshot: continue
87
+ if not hasattr(method, "oneshot"):
88
+ continue
89
+ if not method.oneshot:
90
+ continue
94
91
  oneshots = [] if oneshots == None else oneshots
95
92
  oneshots.append(method)
96
- if not oneshots: return
97
- for oneshot in oneshots: cls.unbind_g(name, oneshot)
93
+ if not oneshots:
94
+ return
95
+ for oneshot in oneshots:
96
+ cls.unbind_g(name, oneshot)
98
97
 
99
98
  def build(self):
100
99
  pass
@@ -102,20 +101,25 @@ class Observable(object):
102
101
  def destroy(self):
103
102
  self.unbind_all()
104
103
 
105
- def bind(self, name, method, oneshot = False):
106
- if oneshot: method.oneshot = oneshot
104
+ def bind(self, name, method, oneshot=False):
105
+ if oneshot:
106
+ method.oneshot = oneshot
107
107
  methods = self._events.get(name, [])
108
108
  methods.append(method)
109
109
  self._events[name] = methods
110
110
 
111
- def unbind(self, name, method = None):
111
+ def unbind(self, name, method=None):
112
112
  methods = self._events.get(name, [])
113
- if method: methods.remove(method)
114
- else: del methods[:]
113
+ if method:
114
+ methods.remove(method)
115
+ else:
116
+ del methods[:]
115
117
 
116
118
  def unbind_all(self):
117
- if not hasattr(self, "_events"): return
118
- for methods in self._events.values(): del methods[:]
119
+ if not hasattr(self, "_events"):
120
+ return
121
+ for methods in self._events.values():
122
+ del methods[:]
119
123
  self._events.clear()
120
124
 
121
125
  def trigger(self, name, *args, **kwargs):
@@ -127,10 +131,14 @@ class Observable(object):
127
131
  methods = self._events.get(name, [])
128
132
  for method in methods:
129
133
  method(*args, **kwargs)
130
- if not hasattr(method, "oneshot"): continue
131
- if not method.oneshot: continue
134
+ if not hasattr(method, "oneshot"):
135
+ continue
136
+ if not method.oneshot:
137
+ continue
132
138
  oneshots = [] if oneshots == None else oneshots
133
139
  oneshots.append(method)
134
140
  level.trigger_g(name, self, *args, **kwargs)
135
- if not oneshots: return
136
- for oneshot in oneshots: self.unbind(name, oneshot)
141
+ if not oneshots:
142
+ return
143
+ for oneshot in oneshots:
144
+ self.unbind(name, oneshot)