vnai 2.0.1__py3-none-any.whl → 2.0.3__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.
vnai/beam/quota.py CHANGED
@@ -1,5 +1,7 @@
1
- # vnai/beam/quota.py
2
- # Resource allocation and management (formerly rate_limiter)
1
+ ##
2
+
3
+ ##
4
+
3
5
 
4
6
  import time
5
7
  import functools
@@ -8,7 +10,7 @@ from collections import defaultdict
8
10
  from datetime import datetime
9
11
 
10
12
  class RateLimitExceeded(Exception):
11
- """Custom exception for rate limit violations."""
13
+ #--
12
14
  def __init__(self, resource_type, limit_type="min", current_usage=None, limit_value=None, retry_after=None):
13
15
  self.resource_type = resource_type
14
16
  self.limit_type = limit_type
@@ -16,7 +18,8 @@ class RateLimitExceeded(Exception):
16
18
  self.limit_value = limit_value
17
19
  self.retry_after = retry_after
18
20
 
19
- # Create a user-friendly message
21
+ ##
22
+
20
23
  message = f"Bạn đã gửi quá nhiều request tới {resource_type}. "
21
24
  if retry_after:
22
25
  message += f"Vui lòng thử lại sau {round(retry_after)} giây."
@@ -26,7 +29,7 @@ class RateLimitExceeded(Exception):
26
29
  super().__init__(message)
27
30
 
28
31
  class Guardian:
29
- """Ensures optimal resource allocation"""
32
+ #--
30
33
 
31
34
  _instance = None
32
35
  _lock = threading.Lock()
@@ -39,23 +42,32 @@ class Guardian:
39
42
  return cls._instance
40
43
 
41
44
  def _initialize(self):
42
- """Initialize guardian"""
45
+ #--
43
46
  self.resource_limits = defaultdict(lambda: defaultdict(int))
44
47
  self.usage_counters = defaultdict(lambda: defaultdict(list))
45
48
 
46
- # Define resource limits
49
+ ##
50
+
47
51
  self.resource_limits["default"] = {"min": 60, "hour": 3000}
48
52
  self.resource_limits["TCBS"] = {"min": 60, "hour": 3000}
49
53
  self.resource_limits["VCI"] = {"min": 60, "hour": 3000}
54
+ self.resource_limits["VCI.ext"] = {"min": 600, "hour": 36000}
55
+ self.resource_limits["VND.ext"] = {"min": 600, "hour": 36000}
56
+ self.resource_limits["CAF.ext"] = {"min": 600, "hour": 36000}
57
+ self.resource_limits["SPL.ext"] = {"min": 600, "hour": 36000}
58
+ self.resource_limits["VDS.ext"] = {"min": 600, "hour": 36000}
59
+ self.resource_limits["FAD.ext"] = {"min": 600, "hour": 36000}
50
60
 
51
61
  def verify(self, operation_id, resource_type="default"):
52
- """Verify resource availability before operation"""
62
+ #--
53
63
  current_time = time.time()
54
64
 
55
- # Get limits for this resource type (or use default)
65
+ ##
66
+
56
67
  limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
57
68
 
58
- # Check minute limit
69
+ ##
70
+
59
71
  minute_cutoff = current_time - 60
60
72
  self.usage_counters[resource_type]["min"] = [
61
73
  t for t in self.usage_counters[resource_type]["min"]
@@ -66,7 +78,8 @@ class Guardian:
66
78
  minute_exceeded = minute_usage >= limits["min"]
67
79
 
68
80
  if minute_exceeded:
69
- # Track limit check through metrics module
81
+ ##
82
+
70
83
  from vnai.beam.metrics import collector
71
84
  collector.record(
72
85
  "rate_limit",
@@ -79,16 +92,19 @@ class Guardian:
79
92
  },
80
93
  priority="high"
81
94
  )
82
- # Raise custom exception with retry information
95
+ ##
96
+
83
97
  raise RateLimitExceeded(
84
98
  resource_type=resource_type,
85
99
  limit_type="min",
86
100
  current_usage=minute_usage,
87
101
  limit_value=limits["min"],
88
- retry_after=60 - (current_time % 60) # Seconds until the minute rolls over
102
+ retry_after=60 - (current_time % 60) ##
103
+
89
104
  )
90
105
 
91
- # Check hour limit
106
+ ##
107
+
92
108
  hour_cutoff = current_time - 3600
93
109
  self.usage_counters[resource_type]["hour"] = [
94
110
  t for t in self.usage_counters[resource_type]["hour"]
@@ -98,7 +114,8 @@ class Guardian:
98
114
  hour_usage = len(self.usage_counters[resource_type]["hour"])
99
115
  hour_exceeded = hour_usage >= limits["hour"]
100
116
 
101
- # Track rate limit check
117
+ ##
118
+
102
119
  from vnai.beam.metrics import collector
103
120
  collector.record(
104
121
  "rate_limit",
@@ -112,26 +129,30 @@ class Guardian:
112
129
  )
113
130
 
114
131
  if hour_exceeded:
115
- # Raise custom exception with retry information
132
+ ##
133
+
116
134
  raise RateLimitExceeded(
117
135
  resource_type=resource_type,
118
136
  limit_type="hour",
119
137
  current_usage=hour_usage,
120
138
  limit_value=limits["hour"],
121
- retry_after=3600 - (current_time % 3600) # Seconds until the hour rolls over
139
+ retry_after=3600 - (current_time % 3600) ##
140
+
122
141
  )
123
142
 
124
- # Record this request
143
+ ##
144
+
125
145
  self.usage_counters[resource_type]["min"].append(current_time)
126
146
  self.usage_counters[resource_type]["hour"].append(current_time)
127
147
  return True
128
148
 
129
149
  def usage(self, resource_type="default"):
130
- """Get current usage percentage for resource limits"""
150
+ #--
131
151
  current_time = time.time()
132
152
  limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
133
153
 
134
- # Clean old timestamps
154
+ ##
155
+
135
156
  minute_cutoff = current_time - 60
136
157
  hour_cutoff = current_time - 3600
137
158
 
@@ -145,22 +166,25 @@ class Guardian:
145
166
  if t > hour_cutoff
146
167
  ]
147
168
 
148
- # Calculate percentages
169
+ ##
170
+
149
171
  minute_usage = len(self.usage_counters[resource_type]["min"])
150
172
  hour_usage = len(self.usage_counters[resource_type]["hour"])
151
173
 
152
174
  minute_percentage = (minute_usage / limits["min"]) * 100 if limits["min"] > 0 else 0
153
175
  hour_percentage = (hour_usage / limits["hour"]) * 100 if limits["hour"] > 0 else 0
154
176
 
155
- # Return the higher percentage
177
+ ##
178
+
156
179
  return max(minute_percentage, hour_percentage)
157
180
 
158
181
  def get_limit_status(self, resource_type="default"):
159
- """Get detailed information about current limit status"""
182
+ #--
160
183
  current_time = time.time()
161
184
  limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
162
185
 
163
- # Clean old timestamps
186
+ ##
187
+
164
188
  minute_cutoff = current_time - 60
165
189
  hour_cutoff = current_time - 3600
166
190
 
@@ -185,14 +209,17 @@ class Guardian:
185
209
  }
186
210
  }
187
211
 
188
- # Create singleton instance
212
+ ##
213
+
189
214
  guardian = Guardian()
190
215
 
191
216
  class CleanErrorContext:
192
- """Context manager to clean up tracebacks for rate limits"""
193
- # Class variable to track if a message has been displayed recently
217
+ #--
218
+ ##
219
+
194
220
  _last_message_time = 0
195
- _message_cooldown = 5 # Only show a message every 5 seconds
221
+ _message_cooldown = 5 ##
222
+
196
223
 
197
224
  def __enter__(self):
198
225
  return self
@@ -201,64 +228,37 @@ class CleanErrorContext:
201
228
  if exc_type is RateLimitExceeded:
202
229
  current_time = time.time()
203
230
 
204
- # Only print the message if enough time has passed since the last one
231
+ ##
232
+
205
233
  if current_time - CleanErrorContext._last_message_time >= CleanErrorContext._message_cooldown:
206
234
  print(f"\n⚠️ {str(exc_val)}\n")
207
235
  CleanErrorContext._last_message_time = current_time
208
236
 
209
- # Re-raise the exception more forcefully to ensure it propagates
210
- # This will bypass any try/except blocks that might be catching RateLimitExceeded
237
+ ##
238
+
239
+ ##
240
+
211
241
  import sys
212
242
  sys.exit(f"Rate limit exceeded. {str(exc_val)} Process terminated.")
213
243
 
214
- # The line below won't be reached, but we keep it for clarity
244
+ ##
245
+
215
246
  return False
216
247
  return False
217
248
 
218
249
 
219
250
  def optimize(resource_type='default', loop_threshold=10, time_window=5, ad_cooldown=150, content_trigger_threshold=3,
220
251
  max_retries=2, backoff_factor=2, debug=False):
221
- """
222
- Decorator that optimizes function execution, tracks metrics, and detects loop patterns for ad opportunities.
223
-
224
- Features:
225
- - Resource verification
226
- - Performance metrics collection
227
- - Loop detection for ad/content opportunities
228
- - Automatic retry with exponential backoff for rate limit errors
229
-
230
- Args:
231
- resource_type: Type of resource used by function ("network", "database", "cpu", "memory", "io", "default")
232
- loop_threshold: Number of calls within time_window to consider as a loop (min: 2)
233
- time_window: Time period in seconds to consider for loop detection
234
- ad_cooldown: Minimum seconds between showing ads for the same function
235
- content_trigger_threshold: Number of consecutive loop detections before triggering content (min: 1)
236
- max_retries: Maximum number of times to retry when rate limits are hit
237
- backoff_factor: Base factor for exponential backoff (wait time = backoff_factor^retry_count)
238
- debug: When True, prints diagnostic information about loop detection
239
-
240
- Examples:
241
- @optimize
242
- def simple_function():
243
- return "result"
244
-
245
- @optimize("network")
246
- def fetch_stock_data(symbol):
247
- # Makes network calls
248
- return data
249
-
250
- @optimize("database", loop_threshold=4, time_window=10)
251
- def query_financial_data(params):
252
- # Database queries
253
- return results
254
- """
255
- # Handle case where decorator is used without arguments: @optimize
252
+ #--
253
+ ##
254
+
256
255
  if callable(resource_type):
257
256
  func = resource_type
258
257
  return _create_wrapper(func, 'default', loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
259
258
  max_retries, backoff_factor, debug)
260
259
 
261
- # Basic validation
260
+ ##
261
+
262
262
  if loop_threshold < 2:
263
263
  raise ValueError(f"loop_threshold must be at least 2, got {loop_threshold}")
264
264
  if time_window <= 0:
@@ -270,7 +270,8 @@ def optimize(resource_type='default', loop_threshold=10, time_window=5, ad_coold
270
270
  if backoff_factor <= 0:
271
271
  raise ValueError(f"backoff_factor must be positive, got {backoff_factor}")
272
272
 
273
- # Return the actual decorator
273
+ ##
274
+
274
275
  def decorator(func):
275
276
  return _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
276
277
  max_retries, backoff_factor, debug)
@@ -278,14 +279,17 @@ def optimize(resource_type='default', loop_threshold=10, time_window=5, ad_coold
278
279
 
279
280
  def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
280
281
  max_retries, backoff_factor, debug):
281
- """Creates the function wrapper with call tracking for loop detection"""
282
- # Static storage for each decorated function instance
282
+ #--
283
+ ##
284
+
283
285
  call_history = []
284
286
  last_ad_time = 0
285
287
  consecutive_loop_detections = 0
286
- session_displayed = False # Track if we've displayed an ad in this session
288
+ session_displayed = False ##
289
+
287
290
  session_start_time = time.time()
288
- session_timeout = 1800 # 30 minutes for session expiration
291
+ session_timeout = 1800 ##
292
+
289
293
 
290
294
  @functools.wraps(func)
291
295
  def wrapper(*args, **kwargs):
@@ -293,29 +297,36 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
293
297
  current_time = time.time()
294
298
  content_triggered = False
295
299
 
296
- # Reset session if it has expired
300
+ ##
301
+
297
302
  if current_time - session_start_time > session_timeout:
298
303
  session_displayed = False
299
304
  session_start_time = current_time
300
305
 
301
- # For automatic retries with rate limits
306
+ ##
307
+
302
308
  retries = 0
303
309
  while True:
304
- # ===== LOOP DETECTION LOGIC =====
305
- # Add current call to history
310
+ ##
311
+
312
+ ##
313
+
306
314
  call_history.append(current_time)
307
315
 
308
- # Prune old calls outside the time window
316
+ ##
317
+
309
318
  while call_history and current_time - call_history[0] > time_window:
310
319
  call_history.pop(0)
311
320
 
312
- # Check if we're in a loop pattern
321
+ ##
322
+
313
323
  loop_detected = len(call_history) >= loop_threshold
314
324
 
315
325
  if debug and loop_detected:
316
326
  print(f"[OPTIMIZE] Đã phát hiện vòng lặp cho {func.__name__}: {len(call_history)} lần gọi trong {time_window}s")
317
327
 
318
- # Handle loop detection
328
+ ##
329
+
319
330
  if loop_detected:
320
331
  consecutive_loop_detections += 1
321
332
  if debug:
@@ -323,26 +334,29 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
323
334
  else:
324
335
  consecutive_loop_detections = 0
325
336
 
326
- # Determine if we should show content - add session_displayed check
327
- should_show_content = (consecutive_loop_detections >= content_trigger_threshold) and \
328
- (current_time - last_ad_time >= ad_cooldown) and \
329
- not session_displayed
337
+ ##
338
+
339
+ should_show_content = (consecutive_loop_detections >= content_trigger_threshold) and (current_time - last_ad_time >= ad_cooldown) and not session_displayed
330
340
 
331
- # Handle content opportunity
341
+ ##
342
+
332
343
  if should_show_content:
333
344
  last_ad_time = current_time
334
345
  consecutive_loop_detections = 0
335
346
  content_triggered = True
336
- session_displayed = True # Mark that we've displayed in this session
347
+ session_displayed = True ##
348
+
337
349
 
338
350
  if debug:
339
351
  print(f"[OPTIMIZE] Đã kích hoạt nội dung cho {func.__name__}")
340
352
 
341
- # Trigger content display using promo manager with "loop" context
353
+ ##
354
+
342
355
  try:
343
356
  from vnai.scope.promo import manager
344
357
 
345
- # Get environment if available
358
+ ##
359
+
346
360
  try:
347
361
  from vnai.scope.profile import inspector
348
362
  environment = inspector.examine().get("environment", None)
@@ -351,21 +365,26 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
351
365
  manager.present_content(context="loop")
352
366
 
353
367
  except ImportError:
354
- # Fallback if content manager is not available
368
+ ##
369
+
355
370
  print(f"Phát hiện vòng lặp: Hàm '{func.__name__}' đang được gọi trong một vòng lặp")
356
371
  except Exception as e:
357
- # Don't let content errors affect the main function
372
+ ##
373
+
358
374
  if debug:
359
375
  print(f"[OPTIMIZE] Lỗi khi hiển thị nội dung: {str(e)}")
360
376
 
361
- # ===== RESOURCE VERIFICATION =====
377
+ ##
378
+
362
379
  try:
363
- # Use a context manager to clean up the traceback
380
+ ##
381
+
364
382
  with CleanErrorContext():
365
383
  guardian.verify(func.__name__, resource_type)
366
384
 
367
385
  except RateLimitExceeded as e:
368
- # Record the rate limit error
386
+ ##
387
+
369
388
  from vnai.beam.metrics import collector
370
389
  collector.record(
371
390
  "error",
@@ -379,7 +398,8 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
379
398
  priority="high"
380
399
  )
381
400
 
382
- # Display rate limit content ONLY if we haven't shown any content this session
401
+ ##
402
+
383
403
  if not session_displayed:
384
404
  try:
385
405
  from vnai.scope.promo import manager
@@ -387,21 +407,25 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
387
407
  from vnai.scope.profile import inspector
388
408
  environment = inspector.examine().get("environment", None)
389
409
  manager.present_content(environment=environment, context="loop")
390
- session_displayed = True # Mark that we've displayed
410
+ session_displayed = True ##
411
+
391
412
  last_ad_time = current_time
392
413
  except ImportError:
393
414
  manager.present_content(context="loop")
394
415
  session_displayed = True
395
416
  last_ad_time = current_time
396
417
  except Exception:
397
- pass # Don't let content errors affect the retry logic
418
+ pass ##
419
+
398
420
 
399
- # Continue with retry logic
421
+ ##
422
+
400
423
  if retries < max_retries:
401
424
  wait_time = backoff_factor ** retries
402
425
  retries += 1
403
426
 
404
- # If the exception has a retry_after value, use that instead
427
+ ##
428
+
405
429
  if hasattr(e, "retry_after") and e.retry_after:
406
430
  wait_time = min(wait_time, e.retry_after)
407
431
 
@@ -409,18 +433,22 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
409
433
  print(f"[OPTIMIZE] Đã đạt giới hạn tốc độ cho {func.__name__}, thử lại sau {wait_time} giây (lần thử {retries}/{max_retries})")
410
434
 
411
435
  time.sleep(wait_time)
412
- continue # Retry the call
436
+ continue ##
437
+
413
438
  else:
414
- # No more retries, re-raise the exception
439
+ ##
440
+
415
441
  raise
416
442
 
417
- # ===== FUNCTION EXECUTION & METRICS =====
443
+ ##
444
+
418
445
  start_time = time.time()
419
446
  success = False
420
447
  error = None
421
448
 
422
449
  try:
423
- # Execute the original function
450
+ ##
451
+
424
452
  result = func(*args, **kwargs)
425
453
  success = True
426
454
  return result
@@ -428,10 +456,12 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
428
456
  error = str(e)
429
457
  raise
430
458
  finally:
431
- # Calculate execution metrics
459
+ ##
460
+
432
461
  execution_time = time.time() - start_time
433
462
 
434
- # Record metrics
463
+ ##
464
+
435
465
  try:
436
466
  from vnai.beam.metrics import collector
437
467
  collector.record(
@@ -450,7 +480,8 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
450
480
  }
451
481
  )
452
482
 
453
- # Record content opportunity metrics if detected
483
+ ##
484
+
454
485
  if content_triggered:
455
486
  collector.record(
456
487
  "ad_opportunity",
@@ -463,16 +494,19 @@ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldow
463
494
  }
464
495
  )
465
496
  except ImportError:
466
- # Metrics module not available, just continue
497
+ ##
498
+
467
499
  pass
468
500
 
469
- # If we got here, the function executed successfully, so break the retry loop
501
+ ##
502
+
470
503
  break
471
504
 
472
505
  return wrapper
473
506
 
474
507
 
475
- # Helper function for getting the current rate limit status
508
+ ##
509
+
476
510
  def rate_limit_status(resource_type="default"):
477
- """Get the current rate limit status for a resource type"""
511
+ #--
478
512
  return guardian.get_limit_status(resource_type)
vnai/flow/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
- # vnai/flow/__init__.py
2
- # Data flow and transmission management
1
+ ##
2
+
3
+ ##
4
+
3
5
 
4
6
  from vnai.flow.relay import conduit, configure
5
7
  from vnai.flow.queue import buffer
vnai/flow/queue.py CHANGED
@@ -1,5 +1,7 @@
1
- # vnai/flow/queue.py
2
- # Data buffering system
1
+ ##
2
+
3
+ ##
4
+
3
5
 
4
6
  import time
5
7
  import threading
@@ -8,7 +10,7 @@ from datetime import datetime
8
10
  from pathlib import Path
9
11
 
10
12
  class Buffer:
11
- """Manages data buffering with persistence"""
13
+ #--
12
14
 
13
15
  _instance = None
14
16
  _lock = threading.Lock()
@@ -21,13 +23,15 @@ class Buffer:
21
23
  return cls._instance
22
24
 
23
25
  def _initialize(self):
24
- """Initialize buffer"""
26
+ #--
25
27
  self.data = []
26
28
  self.lock = threading.Lock()
27
29
  self.max_size = 1000
28
- self.backup_interval = 300 # 5 minutes
30
+ self.backup_interval = 300 ##
31
+
29
32
 
30
- # Setup data directory
33
+ ##
34
+
31
35
  self.home_dir = Path.home()
32
36
  self.project_dir = self.home_dir / ".vnstock"
33
37
  self.project_dir.mkdir(exist_ok=True)
@@ -35,14 +39,16 @@ class Buffer:
35
39
  self.data_dir.mkdir(exist_ok=True)
36
40
  self.backup_path = self.data_dir / "buffer_backup.json"
37
41
 
38
- # Load from backup if exists
42
+ ##
43
+
39
44
  self._load_from_backup()
40
45
 
41
- # Start backup thread
46
+ ##
47
+
42
48
  self._start_backup_thread()
43
49
 
44
50
  def _load_from_backup(self):
45
- """Load data from backup file"""
51
+ #--
46
52
  if self.backup_path.exists():
47
53
  try:
48
54
  with open(self.backup_path, 'r') as f:
@@ -54,7 +60,7 @@ class Buffer:
54
60
  pass
55
61
 
56
62
  def _save_to_backup(self):
57
- """Save data to backup file"""
63
+ #--
58
64
  with self.lock:
59
65
  if not self.data:
60
66
  return
@@ -66,7 +72,7 @@ class Buffer:
66
72
  pass
67
73
 
68
74
  def _start_backup_thread(self):
69
- """Start background backup thread"""
75
+ #--
70
76
  def backup_task():
71
77
  while True:
72
78
  time.sleep(self.backup_interval)
@@ -76,30 +82,34 @@ class Buffer:
76
82
  backup_thread.start()
77
83
 
78
84
  def add(self, item, category=None):
79
- """Add item to buffer"""
85
+ #--
80
86
  with self.lock:
81
- # Add metadata
87
+ ##
88
+
82
89
  if isinstance(item, dict):
83
90
  if "timestamp" not in item:
84
91
  item["timestamp"] = datetime.now().isoformat()
85
92
  if category:
86
93
  item["category"] = category
87
94
 
88
- # Add to buffer
95
+ ##
96
+
89
97
  self.data.append(item)
90
98
 
91
- # Trim if exceeds max size
99
+ ##
100
+
92
101
  if len(self.data) > self.max_size:
93
102
  self.data = self.data[-self.max_size:]
94
103
 
95
- # Save to backup if buffer gets large
104
+ ##
105
+
96
106
  if len(self.data) % 100 == 0:
97
107
  self._save_to_backup()
98
108
 
99
109
  return len(self.data)
100
110
 
101
111
  def get(self, count=None, category=None):
102
- """Get items from buffer with optional filtering"""
112
+ #--
103
113
  with self.lock:
104
114
  if category:
105
115
  filtered_data = [item for item in self.data if item.get("category") == category]
@@ -112,7 +122,7 @@ class Buffer:
112
122
  return filtered_data
113
123
 
114
124
  def clear(self, category=None):
115
- """Clear buffer, optionally by category"""
125
+ #--
116
126
  with self.lock:
117
127
  if category:
118
128
  self.data = [item for item in self.data if item.get("category") != category]
@@ -123,12 +133,13 @@ class Buffer:
123
133
  return len(self.data)
124
134
 
125
135
  def size(self, category=None):
126
- """Get buffer size, optionally by category"""
136
+ #--
127
137
  with self.lock:
128
138
  if category:
129
139
  return len([item for item in self.data if item.get("category") == category])
130
140
  else:
131
141
  return len(self.data)
132
142
 
133
- # Create singleton instance
143
+ ##
144
+
134
145
  buffer = Buffer()