This guide outlines the Python coding standards for GhostBSD projects. Following these standards ensures code consistency, maintainability, and compatibility with our development tools.
All Python code must follow PEP 8 standards with these specific configurations:
- Line length: Maximum 120 characters
- Indentation: 4 spaces (no tabs)
- Imports: Organize in three groups separated by blank lines:
- Standard library imports
- Related third-party imports
- Local application/library imports
- Variables:
snake_case(e.g.,network_name,retry_count) - Functions:
snake_case(e.g.,connect_to_network(),get_available_networks()) - Methods:
snake_case(e.g.,self.setup_ui(),self._validate_input()) - Classes:
PascalCase(e.g.,NetworkManager,ConfigDialog) - Constants:
UPPER_SNAKE_CASE(e.g.,MAX_RETRY_ATTEMPTS,DEFAULT_TIMEOUT) - Private attributes: Single leading underscore
_private_var - Protected attributes: Double leading underscore
__protected_var
- String quotes: Use double quotes
"for user-facing strings, single quotes'for internal strings - Trailing commas: Always use trailing commas in multi-line lists, tuples, and dictionaries
- Blank lines: Two blank lines between top-level functions and classes, one blank line between methods
Configure Pylint to match our standards by creating a .pylintrc file in your project root:
[MASTER]
extension-pkg-whitelist=gi
[FORMAT]
max-line-length=120
indent-string=' '
[MESSAGES CONTROL]
disable=missing-docstring,
too-few-public-methods,
import-error,
no-member
[DESIGN]
max-args=8
max-locals=20
max-branches=15All user-visible text must be internationalized using gettext. This ensures GhostBSD applications can be translated into multiple languages.
Import and configure gettext at the top of your Python files following the GhostBSD pattern:
import gettext
# Set up translation (replace 'your-app-name' with your application name)
gettext.bindtextdomain('your-app-name', '/usr/local/share/locale')
gettext.textdomain('your-app-name')
_ = gettext.gettextWrap all user-visible strings with the _() function:
# Good - translatable
error_message = _("Unable to connect to network")
dialog.set_title(_("Network Settings"))
button.set_label(_("Apply Changes"))
# Bad - not translatable
error_message = "Unable to connect to network"
dialog.set_title("Network Settings")Use format strings properly with translations:
# Good - proper formatting
message = _("Connected to {network_name}").format(network_name=ssid)
count_text = _("Found {count} networks").format(count=len(networks))
# Bad - formatting outside translation
message = _("Connected to") + f" {ssid}"Handle plural forms correctly:
import ngettext
# Proper pluralization
count_message = ngettext(
"Found {count} network",
"Found {count} networks",
network_count
).format(count=network_count)Use Google-style docstrings for all public functions, classes, and modules:
def connect_to_network(ssid, password=None):
"""Connect to a wireless network.
Args:
ssid (str): The network SSID to connect to
password (str, optional): Network password. Defaults to None.
Returns:
bool: True if connection successful, False otherwise
Raises:
NetworkError: If the network interface is not available
"""
passUse type hints for function parameters and return values:
def get_available_networks() -> list[dict[str, str]]:
"""Get list of available wireless networks.
Returns:
List of dictionaries containing network information
"""
pass
def connect_to_network(ssid: str, password: str | None = None) -> bool:
"""Connect to specified network."""
passType annotations are required for all new code. Python 3.11+ provides modern syntax without importing from typing.
Use built-in types directly:
def process_config(name: str, port: int, enabled: bool) -> None:
"""Process configuration with basic types."""
pass
def get_timeout() -> float:
"""Return timeout value in seconds."""
return 30.5Use built-in collection types directly:
def get_network_list() -> list[str]:
"""Return list of network names."""
return ["WiFi-1", "WiFi-2"]
def get_network_config() -> dict[str, str]:
"""Return network configuration mapping."""
return {"ssid": "MyNetwork", "security": "WPA2"}
def get_coordinates() -> tuple[int, int]:
"""Return x, y coordinates."""
return (100, 200)
def get_unique_names() -> set[str]:
"""Return set of unique network names."""
return {"WiFi-1", "WiFi-2"}Use modern union syntax with |:
def connect_with_password(password: str | None = None) -> bool:
"""Connect to network with optional password."""
if password is None:
return connect_without_auth()
return connect_with_auth(password)
def parse_value(data: str | int) -> str:
"""Parse value that can be string or integer."""
return str(data)
def get_config_value(key: str) -> str | int | bool:
"""Get configuration value of various types."""
passAnnotate class attributes and methods:
class NetworkManager:
"""Network management class with type annotations."""
MAX_RETRIES: int = 3 # Class variable
def __init__(self, interface: str) -> None:
# Instance variables
self.interface: str = interface
self.connected: bool = False
self.current_network: str | None = None
self._retry_count: int = 0
def get_networks(self) -> list[dict[str, str]]:
"""Get available networks."""
return []
def connect(self, ssid: str, password: str | None = None) -> bool:
"""Connect to specified network."""
self.current_network = ssid
return TrueHandle nested collections properly:
def get_network_details() -> dict[str, list[str]]:
"""Get network names mapped to their properties."""
return {
"WiFi-1": ["WPA2", "5GHz", "Strong"],
"WiFi-2": ["WPA3", "2.4GHz", "Weak"]
}
def process_connections(
connections: list[tuple[str, bool]]
) -> dict[str, bool]:
"""Process list of connection tuples."""
return dict(connections)Use specific exception types and provide translatable error messages:
try:
result = network_operation()
except NetworkNotFoundError:
error_dialog(_("Network not found. Please check the network name."))
except PermissionError:
error_dialog(_("Permission denied. Please run as administrator."))
except Exception as e:
logger.error("Unexpected error: %s", str(e))
error_dialog(_("An unexpected error occurred. Please try again."))Use the standard logging module with appropriate levels:
import logging
logger = logging.getLogger(__name__)
def process_network_config():
logger.info(_("Starting network configuration"))
try:
# Process configuration
logger.debug("Configuration data: %s", config_data)
except ConfigError as e:
logger.error(_("Configuration error: %s"), str(e))
raiseFollow consistent patterns when creating GTK widgets:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class NetworkDialog(Gtk.Dialog):
def __init__(self, parent=None):
super().__init__(
title=_("Network Settings"),
parent=parent,
modal=True
)
# Add buttons with translatable labels
self.add_button(_("Cancel"), Gtk.ResponseType.CANCEL)
self.add_button(_("Apply"), Gtk.ResponseType.OK)
self._setup_ui()
def _setup_ui(self):
"""Set up the dialog user interface."""
passUse descriptive callback names and handle errors appropriately:
def _on_connect_button_clicked(self, button):
"""Handle connect button click event."""
try:
ssid = self.ssid_entry.get_text()
if not ssid:
self._show_error(_("Please enter a network name"))
return
self._connect_to_network(ssid)
except Exception as e:
logger.error("Connection failed: %s", str(e))
self._show_error(_("Connection failed. Please try again."))PyCharm Community Edition is recommended for GhostBSD Python development. It provides excellent built-in code analysis, debugging capabilities, and seamless integration with the coding standards outlined above.
Install PyCharm on GhostBSD:
sudo pkg install pycharm-cePyCharm's default settings align well with these standards, providing automatic code formatting, linting, and error detection that matches our requirements.
Consider setting up pre-commit hooks to automatically check code style:
# Install pre-commit
pip install pre-commit
# Create .pre-commit-config.yaml
pre-commit installFollowing these standards helps maintain high code quality and ensures your contributions integrate smoothly with the existing GhostBSD codebase.