Source code for gntplib.constants

#!/usr/bin/env python3

# File: gntplib/constants.py
# Author: Hadi Cahyadi <cumulus13@gmail.com>
# Date: 2025-12-25
# Description: Constants and utility functions for GNTP library.
# License: MIT

"""Constants and utility functions for GNTP library.

This module defines all constants used in the GNTP protocol and provides
utility functions for common operations.
"""

import re
import secrets
import struct
from typing import List, Pattern

# ============================================================================
# CONSTANTS
# ============================================================================

# Protocol versions
SUPPORTED_VERSIONS: List[str] = ['1.0']
PROTOCOL_VERSION: str = '1.0'

# Network defaults
DEFAULT_PORT: int = 23053
DEFAULT_TIMEOUT: float = 10.0
DEFAULT_TTL: int = 60

# Message size limits
MAX_MESSAGE_SIZE: int = 4096
MAX_LINE_SIZE: int = 1024

# Message delimiters
LINE_DELIMITER: bytes = b'\r\n'
SECTION_DELIMITER: bytes = b'\r\n'
SECTION_BODY_START: bytes = b'\r\n'
SECTION_BODY_END: bytes = b'\r\n'
MESSAGE_DELIMITER: bytes = b'\r\n\r\n'
MESSAGE_DELIMITER_SIZE: int = len(MESSAGE_DELIMITER)

# Header prefixes
CUSTOM_HEADER_PREFIX: str = 'X-'
APP_SPECIFIC_HEADER_PREFIX: str = 'Data-'

# Response types
RESPONSE_OK: str = '-OK'
RESPONSE_ERROR: str = '-ERROR'
RESPONSE_CALLBACK: str = '-CALLBACK'

# Notification priorities
PRIORITY_VERY_LOW: int = -2
PRIORITY_LOW: int = -1
PRIORITY_NORMAL: int = 0
PRIORITY_HIGH: int = 1
PRIORITY_EMERGENCY: int = 2

# Callback results
CALLBACK_CLICKED: str = 'CLICKED'
CALLBACK_CLOSED: str = 'CLOSED'
CALLBACK_TIMEDOUT: str = 'TIMEDOUT'
CALLBACK_CLICK: str = 'CLICK'  # Alternative form
CALLBACK_CLOSE: str = 'CLOSE'  # Alternative form
CALLBACK_TIMEOUT: str = 'TIMEOUT'  # Alternative form

# Regular expressions
RESPONSE_INFORMATION_LINE_RE: Pattern[bytes] = re.compile(
    rb'GNTP/([^ ]+) (-OK|-ERROR|-CALLBACK) NONE'
)

# Resource URL scheme
RESOURCE_URL_SCHEME: bytes = b'x-growl-resource://'


[docs] def random_bytes(num_bytes: int) -> bytes: """Generate cryptographically secure random bytes. This function uses secrets module for secure random generation, suitable for cryptographic operations. Args: num_bytes: Number of random bytes to generate Returns: Bytes object containing random data Raises: ValueError: If num_bytes is negative Example: >>> data = random_bytes(16) >>> len(data) 16 """ if num_bytes < 0: raise ValueError("num_bytes must be non-negative") return secrets.token_bytes(num_bytes)
[docs] def random_hex_string(num_bytes: int) -> str: """Generate a random hex string. Args: num_bytes: Number of bytes to generate (hex string will be 2x this length) Returns: Hex string representation of random bytes Example: >>> hex_str = random_hex_string(8) >>> len(hex_str) 16 """ return secrets.token_hex(num_bytes)
[docs] def encode_utf8(text: str) -> bytes: """Safely encode text to UTF-8 bytes. Args: text: String to encode Returns: UTF-8 encoded bytes Example: >>> encode_utf8('Hello') b'Hello' """ if isinstance(text, bytes): return text return text.encode('utf-8')
[docs] def decode_utf8(data: bytes) -> str: """Safely decode UTF-8 bytes to string. Args: data: Bytes to decode Returns: Decoded string Raises: UnicodeDecodeError: If data is not valid UTF-8 Example: >>> decode_utf8(b'Hello') 'Hello' """ if isinstance(data, str): return data return data.decode('utf-8')
[docs] def safe_int_conversion(value: any, default: int = 0) -> int: # type: ignore """Safely convert a value to integer with fallback. Args: value: Value to convert to int default: Default value if conversion fails Returns: Integer value or default Example: >>> safe_int_conversion('42') 42 >>> safe_int_conversion('invalid', default=-1) -1 """ try: return int(value) except (ValueError, TypeError): return default
[docs] def validate_priority(priority: int) -> int: """Validate and clamp priority value to valid range. Priority must be between -2 and 2 (inclusive). Args: priority: Priority value to validate Returns: Clamped priority value Example: >>> validate_priority(5) 2 >>> validate_priority(-5) -2 """ return max(PRIORITY_VERY_LOW, min(PRIORITY_EMERGENCY, priority))