Codebase: 7 documents (stack, architecture, structure, conventions, testing, integrations, concerns) Research: 5 documents (stack, features, architecture, pitfalls, summary)
9.7 KiB
Coding Conventions
Analysis Date: 2026-02-04
Naming Patterns
Files:
- Python files: lowercase with underscores (e.g.,
bot.py,credentials) - Bash scripts: lowercase with hyphens (e.g.,
npm-api,uptime-kuma) - Helper scripts in
~/bin/: all lowercase, no extension (e.g.,pve,pbs,dns)
Functions:
- Python: snake_case (e.g.,
cmd_status(),get_authorized_users(),run_command()) - Bash: snake_case with
cmd_prefix for command handlers (e.g.,cmd_status(),cmd_tasks()) - Bash: auxiliary functions also use snake_case (e.g.,
ssh_pbs(),get_token())
Variables:
- Python: snake_case for local/module vars (e.g.,
authorized_users,output_lines) - Python: UPPERCASE for constants (e.g.,
TOKEN,INBOX_FILE,AUTHORIZED_FILE,NODE,PBS_HOST) - Bash: UPPERCASE for environment variables and constants (e.g.,
PBS_HOST,TOKEN,BASE,DEFAULT_ZONE) - Bash: lowercase for local variables (e.g.,
hours,cutoff,status_icon)
Types/Classes:
- Python: PascalCase for imported classes (e.g.,
ProxmoxAPI,Update,Application) - Dictionary/config keys: lowercase with hyphens or underscores (e.g.,
token_name,max-mem)
Code Style
Formatting:
- No automated formatter detected in codebase
- Python: PEP 8 conventions followed informally
- 4-space indentation
- Max line length ~90-100 characters (observed in practice)
- Blank lines: 2 lines before module-level functions, 1 line before methods
- Bash: 4-space indentation (observed)
Linting:
- No linting configuration detected (no .pylintrc, .flake8, .eslintrc)
- Code style is manually maintained
Docstrings:
- Python: Triple-quoted strings at module level describing purpose
- Example from
telegram/bot.py:
""" Homelab Telegram Bot Two-way interactive bot for homelab management and notifications. """ - Example from
- Python: Function docstrings used for major functions
- Single-line format for simple functions
- Example:
"""Handle /start command - first contact with bot.""" - Example:
"""Load authorized user IDs."""
Import Organization
Order:
- Standard library imports (e.g.,
sys,os,json,subprocess) - Third-party imports (e.g.,
ProxmoxAPI,telegram,pocketbase) - Local imports (rarely used in this codebase)
Path Aliases:
- No aliases detected
- Absolute imports used throughout
Credential Loading Pattern: All scripts that need credentials follow the same pattern:
# Load credentials
creds_path = Path.home() / ".config" / <service> / "credentials"
creds = {}
with open(creds_path) as f:
for line in f:
if '=' in line:
key, value = line.strip().split('=', 1)
creds[key] = value
Or in Bash:
source ~/.config/dns/credentials
Error Handling
Patterns:
-
Python: Try-except with broad exception catching (bare
except:used inpvescript lines 70, 82, 95, 101)- Not ideal but pragmatic for CLI tools that need to try multiple approaches
- Example from
pve:
try: status = pve.nodes(NODE).lxc(vmid).status.current.get() # ... return except: pass -
Python: Explicit exception handling in telegram bot
- Catches
subprocess.TimeoutExpiredspecifically inrun_command()function - Example from
telegram/bot.py:
try: result = subprocess.run(...) output = result.stdout or result.stderr or "No output" if len(output) > 4000: output = output[:4000] + "\n... (truncated)" return output except subprocess.TimeoutExpired: return "Command timed out" except Exception as e: return f"Error: {e}" - Catches
-
Bash: Set strict mode with
set -ein some scripts (dnsscript line 12)- Causes script to exit on first error
-
Bash: No error handling in most scripts (
pbs,beszel,kuma)- Relies on exit codes implicitly
Return Value Handling:
-
Python: Functions return data directly or None on failure
- Example from
pbshelper: Returns JSON-parsed data or string output - Example from
pve: Returns nothing (prints output), but uses exceptions for flow control
- Example from
-
Python: Command runner returns error strings:
"Command timed out","Error: {e}"
Logging
Framework:
- Python: Standard
loggingmodule- Configured in
telegram/bot.pylines 18-22:
logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__)- Log level: INFO
- Format includes timestamp, logger name, level, message
- Configured in
Patterns:
-
logger.info()for general informational messages- Example:
logger.info("Starting Homelab Bot...") - Example:
logger.info(f"Inbox message from {user.first_name}: {message[:50]}...") - Example:
logger.info(f"Photo saved from {user.first_name}: {filepath}")
- Example:
-
Bash: Uses
echofor output, no structured logging- Informational messages for user feedback
- Error messages sent to stdout (not stderr)
Comments
When to Comment:
- Module-level docstrings at top of file (required for all scripts)
- Usage examples in module docstrings (e.g.,
pve,pbs,kuma) - Inline comments for complex logic (e.g., in
pbsscript parsing hex timestamps) - Comments on tricky regex patterns (e.g.,
pbstasks parsing)
Bash Comments:
- Header comment with script name, purpose, and usage (lines 1-10)
- Inline comments before major sections (e.g.,
# Datastore info,# Storage stats) - No comments in simple expressions
Python Comments:
- Header comment with purpose (module docstring)
- Sparse inline comments except for complex sections
- Example from
telegram/bot.pyline 71:# Telegram has 4096 char limit per message - Example from
pveline 70:# Try as container first
Function Design
Size:
-
Python: Functions are generally 10-50 lines
- Smaller functions for simple operations (e.g.,
is_authorized()is 2 lines) - Larger functions for command handlers that do setup + API calls (e.g.,
status()is 40 lines)
- Smaller functions for simple operations (e.g.,
-
Bash: Functions are typically 20-80 lines
- Longer functions acceptable for self-contained operations like
cmd_status()inpbs
- Longer functions acceptable for self-contained operations like
Parameters:
-
Python: Explicit parameters, typically 1-5 parameters per function
- Optional parameters with defaults (e.g.,
timeout: int = 30,port=45876) - Type hints not used consistently (some functions have them, many don't)
- Optional parameters with defaults (e.g.,
-
Bash: Parameters passed as positional arguments
- Some functions take zero parameters and rely on global variables
- Example:
ssh_pbs()inpbsuses global$PBS_HOST
Return Values:
-
Python: Functions return data (strings, dicts, lists) or None
- Command handlers often return nothing (implicitly None)
- Helper functions return computed values (e.g.,
is_authorized()returns bool)
-
Bash: Functions print output directly, return exit codes
- No explicit return values beyond exit codes
- Output captured by caller with
$()
Module Design
Exports:
-
Python: All functions are module-level, no explicit exports
if __name__ == "__main__":pattern used in all scripts to guard main execution- Example from
beszellines 101-152
-
Bash: All functions are script-level, called via case statement
- Main dispatch logic at bottom of script
- Example from
dnslines 29-106:case "$1" in ... esac
Async/Await (Telegram Bot Only):
- Python telegram bot uses
asyncioandasync deffor all handlers - All command handlers are async (e.g.,
async def start()) - Use
awaitfor async operations (e.g.,await update.message.reply_text()) - Example from
telegram/bot.pylines 81-94:
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle /start command - first contact with bot."""
user = update.effective_user
chat_id = update.effective_chat.id
# ... async operations with await
File Structure:
- Single-file modules: Most helpers are single files
telegram/bot.py: Main bot implementation with all handlers/bin/scripts: Each script is self-contained with helper functions + main dispatch
Data Structures
JSON/Config Files:
- Credentials files: Simple
KEY=valueformat (no JSON) - PBS task logging: Uses hex-encoded UPID format, parsed with regex
- Telegram bot: Saves messages to text files with timestamp prefix
- JSON output: Parsed with
python3 -c "import sys, json; ..."in Bash scripts
Error Response Patterns:
- API calls: Check for
.get('status') == 'ok'or similar - Command execution: Check
returncode == 0, capture stdout/stderr - API clients: Let exceptions bubble up, caught at command handler level
Conditionals and Flow Control
Python:
- if/elif/else chains for command dispatch
- Simple truthiness checks:
if not user_id:,if not alerts: - Example from
telegram/bot.pyline 86-100: Authorization check pattern
Bash:
- case/esac for command dispatch (preferred)
- if with regex matching for parsing
- Example from
pbslines 122-143: Complex regex with BASH_REMATCH array
Security Patterns
Credential Management:
- Credentials stored in
~/.config/<service>/credentialswith restricted permissions (not enforced in code) - Telegram token loaded from file, not environment
- Credentials never logged or printed
Input Validation:
- Bash: Basic validation with isalnum() check in
ping_host()function- Example:
if not host.replace('.', '').replace('-', '').isalnum():
- Example:
- Bash: Whitelist command names from case statements
- No SQL injection risk (no databases used directly)
Shell Injection:
- Bash scripts use quoted variables appropriately
- Some inline Python in Bash uses string interpolation (potential risk)
- Example from
dnslines 31-37:curl ... | python3 -c "..."with variable interpolation
- Example from
Convention analysis: 2026-02-04