Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contributor/code/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Automation and system configuration:
**[Development Environment Setup](development-setup)** – **Start here!** Set up your development machine, install tools, and configure your environment for GhostBSD development.

### Development Guides
- [Python Development Guide](python-guide) – Contributing to our GTK3 applications
- [Python Development Guide](python-guide) – Python coding standards, typing, and GTK3 development
- [C Development Guide](c-guide) – Working with system components
- [Shell Scripting Guide](shell-guide) – Build tools and configuration scripts
- [Testing and QA](testing) – How to test your changes
Expand Down
360 changes: 360 additions & 0 deletions contributor/code/python-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
# Python Development Guide

This guide outlines the Python coding standards for GhostBSD projects. Following these standards ensures code consistency, maintainability, and compatibility with our development tools.

## Code Style Standards

### PEP 8 Compliance
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:
1. Standard library imports
2. Related third-party imports
3. Local application/library imports

### Naming Conventions
- **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`

### Code Formatting
- **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

## Pylint Configuration

Configure Pylint to match our standards by creating a `.pylintrc` file in your project root:

```ini
[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=15
```

## Internationalization with Gettext

All user-visible text must be internationalized using gettext. This ensures GhostBSD applications can be translated into multiple languages.

### Setting up Gettext

Import and configure gettext at the top of your Python files following the GhostBSD pattern:

```python
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.gettext
```

### Marking Translatable Strings

Wrap all user-visible strings with the `_()` function:

```python
# 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")
```

### String Formatting with Gettext

Use format strings properly with translations:

```python
# 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}"
```

### Pluralization

Handle plural forms correctly:

```python
import ngettext

# Proper pluralization
count_message = ngettext(
"Found {count} network",
"Found {count} networks",
network_count
).format(count=network_count)
```

## Documentation Standards

### Docstrings
Use Google-style docstrings for all public functions, classes, and modules:

```python
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
"""
pass
```

### Type Hints
Use type hints for function parameters and return values:

```python
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."""
pass
```

## Type Annotations

Type annotations are required for all new code. Python 3.11+ provides modern syntax without importing from `typing`.

### Basic Types
Use built-in types directly:

```python
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.5
```

### Collection Types
Use built-in collection types directly:

```python
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"}
```

### Optional and Union Types
Use modern union syntax with `|`:

```python
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."""
pass
```

### Class Annotations
Annotate class attributes and methods:

```python
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 True
```

### Complex Collections
Handle nested collections properly:

```python
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)
```

## Error Handling

### Exception Handling
Use specific exception types and provide translatable error messages:

```python
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."))
```

### Logging
Use the standard logging module with appropriate levels:

```python
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))
raise
```

## GTK3 Integration

### Widget Creation
Follow consistent patterns when creating GTK widgets:

```python
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."""
pass
```

### Signal Connections
Use descriptive callback names and handle errors appropriately:

```python
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."))
```

## Tools Integration

### IDE Recommendation
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:
```bash
sudo pkg install pycharm-ce
```

PyCharm's default settings align well with these standards, providing automatic code formatting, linting, and error detection that matches our requirements.

### Pre-commit Hooks
Consider setting up pre-commit hooks to automatically check code style:

```bash
# Install pre-commit
pip install pre-commit

# Create .pre-commit-config.yaml
pre-commit install
```

Following these standards helps maintain high code quality and ensures your contributions integrate smoothly with the existing GhostBSD codebase.