bedger 0.0.1__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.

Potentially problematic release.


This version of bedger might be problematic. Click here for more details.

bedger/__init__.py ADDED
File without changes
File without changes
bedger/edge/config.py ADDED
@@ -0,0 +1,8 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class Config:
6
+ socket_path: str = "/tmp/bedger.sock"
7
+
8
+ max_payload_size: int = 1024 * 1024 # 1 MB
@@ -0,0 +1,123 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import socket
5
+ from typing import Optional
6
+
7
+ from bedger.edge import errors
8
+ from bedger.edge.config import Config
9
+ from bedger.edge.entities import Message, Severity
10
+
11
+ logger = logging.getLogger("bedger.edge.connection")
12
+
13
+
14
+ class ConnectionManager:
15
+ """Manages a connection to a UNIX socket for sending and receiving messages.
16
+
17
+ This class provides context manager support for establishing and tearing
18
+ down a connection to a UNIX socket.
19
+
20
+ Attributes:
21
+ _config (Config): The configuration containing the socket path.
22
+ _socket (Optional[socket.socket]): The current socket connection.
23
+ """
24
+
25
+ def __init__(self, config: Config = Config()):
26
+ """Initializes the ConnectionManager.
27
+
28
+ Args:
29
+ config (Config): Configuration for the connection. Defaults to a new `Config` instance.
30
+ """
31
+ self._config = config
32
+ self._socket: Optional[socket.socket] = None
33
+
34
+ def __enter__(self) -> ConnectionManager:
35
+ """Establishes the socket connection when entering the context.
36
+
37
+ Returns:
38
+ ConnectionManager: The instance of the ConnectionManager.
39
+ """
40
+ self._connect()
41
+ return self
42
+
43
+ def __exit__(self, exc_type, exc_value, traceback) -> None:
44
+ """Closes the socket connection when exiting the context."""
45
+ self._disconnect()
46
+
47
+ def _connect(self) -> None:
48
+ """Establish a connection to the UNIX socket.
49
+
50
+ Raises:
51
+ errors.SocketPermissionDeniedError: If the application does not have permission to connect to the socket.
52
+ errors.SocketFileNotFoundError: If the socket file is not found at the specified path.
53
+ errors.SocketConnectionError: If any other error occurs during the connection attempt.
54
+ """
55
+ logger.info(f"Attempting to connect to socket at {self._config.socket_path}")
56
+ try:
57
+ self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
58
+ self._socket.connect(self._config.socket_path)
59
+ logger.info("Successfully connected to the socket")
60
+ except PermissionError:
61
+ logger.error("Permission denied: Unable to connect to the socket. Ensure you have the correct permissions.")
62
+ raise errors.SocketPermissionDeniedError("Permission denied to connect to socket. Are you running as root?")
63
+ except FileNotFoundError:
64
+ logger.error("Socket file not found. Verify the server is running and the socket path is correct.")
65
+ raise errors.SocketFileNotFoundError("Socket file not found. Is the server running?")
66
+ except Exception as e:
67
+ logger.exception(f"Unexpected error while connecting to the socket: {e}")
68
+ raise errors.SocketConnectionError(f"An unexpected error occurred while connecting to the socket: {e}")
69
+
70
+ def _disconnect(self) -> None:
71
+ """Close the socket connection gracefully.
72
+
73
+ Ensures the socket is closed and cleaned up properly, even in case of errors.
74
+ """
75
+ if self._socket:
76
+ try:
77
+ logger.info("Closing the socket connection")
78
+ self._socket.close()
79
+ except Exception as e:
80
+ logger.warning(f"Error occurred while closing the socket: {e}")
81
+ finally:
82
+ self._socket = None
83
+
84
+ def send_event(self, event_type: str, severity: str | Severity, payload: dict) -> None:
85
+ """Send an event message through the socket.
86
+
87
+ Args:
88
+ event_type (str): The type of event being sent.
89
+ severity (str | Severity): The severity level of the event.
90
+ payload (dict): The event's payload details.
91
+
92
+ Raises:
93
+ errors.SocketNotConnectedError: If no active socket connection exists.
94
+ errors.SocketBrokenPipeError: If the socket connection is lost during message transmission.
95
+ errors.SocketCommunicationError: If any error occurs during socket communication.
96
+ """
97
+ if not self._socket:
98
+ logger.error("Attempted to send a message without an active socket connection.")
99
+ raise errors.SocketNotConnectedError("No active socket connection. Ensure the connection is established.")
100
+
101
+ try:
102
+ message = Message(
103
+ event_type=event_type,
104
+ severity=severity,
105
+ details=payload,
106
+ )
107
+ message_json = message.model_dump_json()
108
+ logger.debug(f"Prepared message: {message_json}")
109
+
110
+ logger.debug("Sending message")
111
+ self._socket.sendall(message_json.encode())
112
+
113
+ ack = self._socket.recv(1024)
114
+ logger.info(f"Received acknowledgment from server: {ack.decode()}")
115
+ except BrokenPipeError:
116
+ logger.error("Broken pipe error: The socket connection was lost during message transmission.")
117
+ raise errors.SocketBrokenPipeError("Socket connection lost. Unable to send message.")
118
+ except socket.error as e:
119
+ logger.error(f"Socket error occurred: {e}")
120
+ raise errors.SocketCommunicationError(f"Socket error during communication: {e}")
121
+ except Exception as e:
122
+ logger.exception(f"Unexpected error while sending the message: {e}")
123
+ raise errors.SocketCommunicationError(f"Unexpected error occurred: {e}")
@@ -0,0 +1,4 @@
1
+ from .message import Message
2
+ from .severity import Severity
3
+
4
+ __all__ = [Message, Severity]
@@ -0,0 +1,32 @@
1
+ import json
2
+ from typing import Dict
3
+
4
+ from pydantic import BaseModel, Field, field_serializer, field_validator
5
+ from typing_extensions import Annotated
6
+
7
+ from .severity import Severity
8
+
9
+
10
+ class Message(BaseModel):
11
+ event_type: Annotated[
12
+ str,
13
+ Field(
14
+ description="Event type in PascalCase format",
15
+ pattern=r"^[A-Z][a-zA-Z0-9]*$",
16
+ ),
17
+ ]
18
+ severity: Severity
19
+ details: Annotated[Dict, Field(description="Details of the message as a JSON serializable dictionary")]
20
+
21
+ @field_validator("details")
22
+ @classmethod
23
+ def validate_details(cls, value: Dict) -> Dict:
24
+ try:
25
+ json.dumps(value)
26
+ except TypeError:
27
+ raise ValueError("details must be a JSON serializable dictionary")
28
+ return value
29
+
30
+ @field_serializer("severity")
31
+ def serialize_severity(self, severity: Severity) -> str:
32
+ return severity.value
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Severity(str, Enum):
5
+ UNKNOWN = "UNKNOWN"
6
+ LOW = "LOW"
7
+ MEDIUM = "MEDIUM"
8
+ HIGH = "HIGH"
9
+ CRITICAL = "CRITICAL"
bedger/edge/errors.py ADDED
@@ -0,0 +1,18 @@
1
+ class SocketPermissionDeniedError(Exception):
2
+ pass
3
+
4
+
5
+ class SocketFileNotFoundError(Exception):
6
+ pass
7
+
8
+
9
+ class SocketConnectionError(Exception):
10
+ pass
11
+
12
+
13
+ class SocketNotConnectedError(Exception):
14
+ pass
15
+
16
+
17
+ class SocketCommunicationError(Exception):
18
+ pass
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Henk van den Brink
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.1
2
+ Name: bedger
3
+ Version: 0.0.1
4
+ Summary:
5
+ Author: Henk van den Brink
6
+ Requires-Python: >=3.8.1,<4.0.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
14
+ Requires-Dist: typing-extensions (>=4.12.2,<5.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+
@@ -0,0 +1,12 @@
1
+ bedger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ bedger/edge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ bedger/edge/config.py,sha256=0fUKThP0aJFk3WBF7JUtuKKhXlV07SX5VnqVOLNou-A,152
4
+ bedger/edge/connection.py,sha256=25h7PzRhSw8m0w679p23M6X_o-wuIOl7M1P837bpy9I,5400
5
+ bedger/edge/entities/__init__.py,sha256=_oKO_XdDerqOwVX8izhDPtGv4HqVDwQaT-otOdY8lYg,91
6
+ bedger/edge/entities/message.py,sha256=wLbEZik_FBboyEAvX5Uvlto1t4BsjaeLXUFGOIjIHRU,913
7
+ bedger/edge/entities/severity.py,sha256=tUN7eDSEN5cc5eAqhvKzVHD1pSEmrdwvNEGsmh4Pnfw,157
8
+ bedger/edge/errors.py,sha256=6jdmhlKoWlD-kZ8-xRiAlObRHEo9PwrKqhWQ5su5TLk,266
9
+ bedger-0.0.1.dist-info/LICENSE,sha256=lVrf6pfElYZ_o6ETq-XR91_7GHTzKGyeNWAKghvcNUE,1075
10
+ bedger-0.0.1.dist-info/METADATA,sha256=ccz115dKsGRG2FYE5-SeFGqnhhmu2EvlUOYMUnbGYVY,555
11
+ bedger-0.0.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
12
+ bedger-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any