anzu 0.1.4__py3-none-any.whl → 0.1.5__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.
anzu/__init__.py CHANGED
@@ -1 +1,5 @@
1
- from .logger import (logger, redis_logger)
1
+ import anzu.logger
2
+ import anzu.websocket
3
+ import anzu.worker
4
+
5
+ __version__ = "2.3.0"
anzu/__main__.py ADDED
File without changes
@@ -0,0 +1 @@
1
+ from .logger import redis_logger, logger, AnzuLogger
anzu/logger/logger.py ADDED
@@ -0,0 +1,45 @@
1
+ import logging
2
+
3
+
4
+ class AnzuLogger:
5
+ _instance = None
6
+
7
+ def __new__(cls, level: str = logging.INFO, name: str = __name__):
8
+ # Ensures that only one instance of the logger is created
9
+ if cls._instance is None:
10
+ cls._instance = super(AnzuLogger, cls).__new__(cls)
11
+ cls._instance._logger = logging.getLogger(name)
12
+ cls._instance._logger.setLevel(logging.INFO) # Default log level
13
+ handler = logging.StreamHandler() # You can also use FileHandler
14
+ formatter = logging.Formatter(
15
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
16
+ )
17
+ handler.setFormatter(formatter)
18
+ cls._instance._logger.addHandler(handler)
19
+
20
+ return cls._instance._logger
21
+
22
+ def get_logger(self):
23
+ """Returns the logger instance."""
24
+ return self._instance._logger
25
+
26
+ def set_name(self, name: str):
27
+ """Sets the name of the logger."""
28
+ self._instance._logger = logging.getLogger(name)
29
+
30
+ def add_file_handler(self, file_name: str):
31
+ """Adds a file handler to the logger."""
32
+ handler = logging.FileHandler(file_name)
33
+ formatter = logging.Formatter(
34
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
35
+ )
36
+ handler.setFormatter(formatter)
37
+ self._instance._logger.addHandler(handler)
38
+
39
+ def remove_handler(self, handler):
40
+ """Removes a handler from the logger."""
41
+ self._instance._logger.removeHandler(handler)
42
+
43
+
44
+ logger = AnzuLogger()
45
+ redis_logger = AnzuLogger(name="redis_logger")
@@ -0,0 +1 @@
1
+ from .sockets import send_socket_response, emit_socket_event
@@ -0,0 +1,14 @@
1
+ import socketio
2
+
3
+ # Create client
4
+ sio = socketio.Client(ssl_verify=False)
5
+ sio_server = 'https://django:9500'
6
+
7
+ def emit_socket_event(event, data):
8
+ if sio.connected is False:
9
+ sio.connect(sio_server)
10
+
11
+ sio.emit(event, data)
12
+
13
+ def send_socket_response(event, data):
14
+ emit_socket_event(event, data)
@@ -0,0 +1,2 @@
1
+ from .client import update_queue_item
2
+ from .constants import WAITING, PROCESSING, FAILURE, SUCCESS
anzu/worker/client.py ADDED
@@ -0,0 +1,112 @@
1
+ import json
2
+ import requests
3
+ from requests.auth import HTTPBasicAuth
4
+ from .exceptions import QueueUpdateError
5
+ from .utils import get_env_var, setup_logger
6
+
7
+ logger = setup_logger()
8
+
9
+
10
+ class QueueClient:
11
+ """Client for updating queue items in a Django service."""
12
+
13
+ def __init__(self, django_url=None, username=None, password=None, service_endpoint=None, verify_ssl=None):
14
+ """
15
+ Initialize the queue client with connection parameters.
16
+
17
+ Args:
18
+ django_url (str, optional): URL of the Django service. Defaults to DJANGO_URL env var.
19
+ username (str, optional): Django superuser username. Defaults to DJANGO_SUPERUSER_USERNAME env var.
20
+ password (str, optional): Django superuser password. Defaults to DJANGO_SUPERUSER_PASSWORD env var.
21
+ service_endpoint (str, optional): Service endpoint. Defaults to SERVICE_ENDPOINT env var or '/'.
22
+ verify_ssl (bool, optional): Whether to verify SSL certificates. Defaults to True unless URL contains 'https'.
23
+ """
24
+ self.django_url = django_url or get_env_var('DJANGO_URL', required=True)
25
+ self.username = username or get_env_var('DJANGO_SUPERUSER_USERNAME', required=True)
26
+ self.password = password or get_env_var('DJANGO_SUPERUSER_PASSWORD', required=True)
27
+ self.service_endpoint = service_endpoint or get_env_var('SERVICE_ENDPOINT', '/')
28
+
29
+ # Handle the verify_ssl parameter
30
+ if verify_ssl is None:
31
+ self.verify_ssl = False if 'https' in self.django_url else True
32
+ else:
33
+ self.verify_ssl = verify_ssl
34
+
35
+ self.headers = {
36
+ 'Content-Type': 'application/json'
37
+ }
38
+
39
+ def update_queue_item(self, qi_hash, status, data=None, error=None, timeout=10):
40
+ """
41
+ Update a queue item with the given status and optional data or error.
42
+
43
+ Args:
44
+ qi_hash (str): Hash of the queue item to update.
45
+ status (str): New status for the queue item.
46
+ data (dict, optional): Additional data to update. Defaults to None.
47
+ error (str, optional): Error message if applicable. Defaults to None.
48
+ timeout (int, optional): Request timeout in seconds. Defaults to 10.
49
+
50
+ Returns:
51
+ dict: Response data from the API
52
+
53
+ Raises:
54
+ TypeError: If data is not a dictionary.
55
+ QueueUpdateError: If the update request fails.
56
+ """
57
+ if not qi_hash:
58
+ logger.warning("No queue item to update")
59
+ return
60
+
61
+ payload = {
62
+ "status": status
63
+ }
64
+
65
+ if data is not None:
66
+ if not isinstance(data, dict):
67
+ raise TypeError(f"Expected dict, got {type(data)} with value: {data}")
68
+ payload.update(data)
69
+
70
+ if error is not None:
71
+ payload['error'] = error
72
+
73
+ url = f'{self.django_url}{self.service_endpoint}{qi_hash}/'
74
+
75
+ response = requests.request(
76
+ "PATCH",
77
+ url,
78
+ headers=self.headers,
79
+ data=json.dumps(payload),
80
+ auth=HTTPBasicAuth(self.username, self.password),
81
+ verify=self.verify_ssl,
82
+ timeout=timeout
83
+ )
84
+
85
+ if response.status_code not in [200, 204]:
86
+ response_text = None
87
+ if 'text/html' in response.headers.get('Content-Type', ''):
88
+ logger.error(f"Failed to update queue item. Status Code: {response.status_code}")
89
+ else:
90
+ response_text = response.text
91
+ logger.error(
92
+ f"Failed to update queue item. Status Code: {response.status_code}, Response: {response_text}")
93
+
94
+ raise QueueUpdateError(response.status_code, response_text)
95
+
96
+ try:
97
+ return response.json()
98
+ except (ValueError, json.JSONDecodeError):
99
+ # Return empty dict if no JSON in response
100
+ return {}
101
+
102
+
103
+ # For backwards compatibility
104
+ def update_queue_item(qi_hash, status, data=None, error=None):
105
+ """
106
+ Legacy function to update a queue item. Uses environment variables for connection details.
107
+
108
+ This function exists for backwards compatibility with the original code.
109
+ New code should use the QueueClient class instead.
110
+ """
111
+ client = QueueClient()
112
+ return client.update_queue_item(qi_hash, status, data, error)
@@ -0,0 +1,4 @@
1
+ WAITING = "0"
2
+ PROCESSING = "1"
3
+ SUCCESS = "2"
4
+ FAILURE = "3"
@@ -0,0 +1,14 @@
1
+ class QueueUpdaterError(Exception):
2
+ """Base exception for queue updater."""
3
+ pass
4
+
5
+
6
+ class QueueUpdateError(QueueUpdaterError):
7
+ """Exception raised when updating a queue item fails."""
8
+ def __init__(self, status_code, response_text=None):
9
+ self.status_code = status_code
10
+ self.response_text = response_text
11
+ message = f"Failed to update queue item. Status Code: {status_code}"
12
+ if response_text:
13
+ message += f", Response: {response_text}"
14
+ super().__init__(message)
anzu/worker/utils.py ADDED
@@ -0,0 +1,25 @@
1
+ import logging
2
+ import os
3
+
4
+
5
+ def get_env_var(name, default=None, required=False):
6
+ """Get environment variable with proper error handling."""
7
+ value = os.environ.get(name, default)
8
+ if required and value is None:
9
+ raise ValueError(f"Required environment variable '{name}' is not set")
10
+ return value
11
+
12
+
13
+ def setup_logger(name='queue_updater', level=logging.INFO):
14
+ """Setup and return a logger with the given name and level."""
15
+ logger = logging.getLogger(name)
16
+
17
+ # Only add handler if not already added to avoid duplicate logs
18
+ if not logger.handlers:
19
+ handler = logging.StreamHandler()
20
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
21
+ handler.setFormatter(formatter)
22
+ logger.addHandler(handler)
23
+
24
+ logger.setLevel(level)
25
+ return logger
@@ -1,35 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anzu
3
- Version: 0.1.4
4
- Summary: A package for updating queue items in a Django service
5
- Home-page: https://github.com/yourusername/queue_updater
6
- Author: Your Name
3
+ Version: 0.1.5
4
+ Summary: Anzu package for workers
7
5
  Author-email: Your Name <your.email@example.com>
8
6
  License: MIT
9
- Project-URL: Homepage, https://github.com/yourusername/queue_updater
10
- Project-URL: Bug Tracker, https://github.com/yourusername/queue_updater/issues
7
+ License-File: LICENSE
11
8
  Keywords: django,queue,update
12
- Classifier: Programming Language :: Python :: 3
13
9
  Classifier: License :: OSI Approved :: MIT License
14
10
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.6
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: requests>=2.25.0
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.8
19
13
  Requires-Dist: aiohttp>=3.7.3
20
14
  Requires-Dist: python-socketio>=5.12.1
21
- Provides-Extra: langfuse
22
- Requires-Dist: langfuse>=2.0.0; extra == "langfuse"
23
- Provides-Extra: dev
24
- Requires-Dist: pytest>=6.0.0; extra == "dev"
25
- Requires-Dist: black>=22.0.0; extra == "dev"
26
- Requires-Dist: isort>=5.0.0; extra == "dev"
27
- Requires-Dist: mypy>=0.900; extra == "dev"
28
- Requires-Dist: flake8>=4.0.0; extra == "dev"
29
- Dynamic: author
30
- Dynamic: home-page
31
- Dynamic: license-file
32
- Dynamic: requires-python
15
+ Requires-Dist: requests>=2.25.0
16
+ Description-Content-Type: text/markdown
33
17
 
34
18
  # Queue Updater
35
19
 
@@ -104,4 +88,4 @@ update_queue_item(
104
88
 
105
89
  ## License
106
90
 
107
- MIT
91
+ MIT
@@ -0,0 +1,16 @@
1
+ anzu/__init__.py,sha256=rNEjTqQzvpIw6-SKRv1Vy68XnX99GUoSYSKqCCFEHXM,83
2
+ anzu/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ anzu/logger/__init__.py,sha256=kb2kDG1QFvRU48_Seod3RHVCrpq1udgzACOdmy7-Rg8,52
4
+ anzu/logger/logger.py,sha256=M6oVFVUFD053BXWKEkj0Mz--d3FbvFGcKWFexsYsbco,1584
5
+ anzu/websocket/__init__.py,sha256=vJVQgpYZZeJtIt9KrlhP049l_Xx_eFpSQrZlLxyluuQ,60
6
+ anzu/websocket/sockets.py,sha256=VOGePsUkBMvP-54gabY6nPW5zAWMOujPoBenbIwiPPE,309
7
+ anzu/worker/__init__.py,sha256=6SGGl1IOaSfJP0lOHEYI-8ENv_Ed8FvpNzCc0UMYkGw,98
8
+ anzu/worker/client.py,sha256=SBBPAxJu9n1vfoqTSLu-SvjsARx0JvB4NhHEJ4bTLks,4347
9
+ anzu/worker/constants.py,sha256=Z_w0Hr-PtZ9mLDJwEkrfMKebCdm7GJidNXtjnJwQzkc,59
10
+ anzu/worker/exceptions.py,sha256=At2boea4ABjyph9b59pAyUIaSF5C0zl8Scqjxmjr-R8,524
11
+ anzu/worker/utils.py,sha256=mcUMb3J4MhsLrJnL57_ULRd4END2_MYXZ-6lIwZJjzE,835
12
+ anzu-0.1.5.dist-info/METADATA,sha256=Z8C3LKs5mwtVDjFLRiryghRzEFIbXXnB1lW_acjl-kU,2150
13
+ anzu-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ anzu-0.1.5.dist-info/entry_points.txt,sha256=ZzQxwCwDcp-JpCDaU6LiacEGNk7MhbeQgE1xhQqBUsQ,35
15
+ anzu-0.1.5.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ anzu-0.1.5.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ anzu = anzu:main
@@ -1,6 +0,0 @@
1
- anzu/__init__.py,sha256=yapPQwS18DmiLOYcWJc2tK5KUTVs5lrsW_zmwUcb9zo,43
2
- anzu-0.1.4.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- anzu-0.1.4.dist-info/METADATA,sha256=nRyxc6NCUk0g-LWouq22y_KfRK2vj9NlBiVujheN0DI,2805
4
- anzu-0.1.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
5
- anzu-0.1.4.dist-info/top_level.txt,sha256=ScsUMREYhumuqwuPGmSlaT4aifa-g8khMiFr7-h-xQ0,5
6
- anzu-0.1.4.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- anzu