- Refactor exec_with_args to use *args/**kwargs instead of tuple arguments - Add *args/**kwargs to all command execute functions for consistency - Support multiple key bindings per command in registration - Add character-based key binding support via get_char() in KeyMapper - Make execute_plugin async and use asyncio.run for plugin execution - Use MIME type from Gio content_type instead of language for ftype
5.9 KiB
5.9 KiB
AGENTS.md - Development Guidelines for Newton
Overview
Newton is a Python/Gtk3 desktop application (code editor) using PyGObject. The project follows specific conventions documented below.
Build, Lint, and Test Commands
Running the Application
# Activate virtual environment and run
source .venv/bin/activate
python -m src
Type Checking (Pyright)
The project uses pyright for static type checking. Configuration is in pyrightconfig.json:
# Run pyright (ensure venv is activated)
pyright src/
# Or via Python module
python -m pyright src/
pyrightconfig.json settings:
venvPath: "."venv: ".venv"- Reports: unused variables, unused imports, duplicate imports
No Test Framework
There are currently no tests in this codebase. If tests are added:
- Use pytest as the testing framework
- Test files should be in a
tests/directory - Run a single test:
pytest tests/test_file.py::test_function_name
Code Style Guidelines
Import Organization
Imports must be organized with blank lines between groups (in this exact order):
# Python imports
import os
import logging
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
# Application imports
from libs.dto.states import SourceViewStates
from .mixins.source_view_dnd_mixin import SourceViewDnDMixin
Type Hints
- Use type hints for all function parameters and return types
- Use Python's built-in types (
list[str],dict[str, int]) not typing module aliases - Example:
def message_to(self, name: str, event: BaseEvent):
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase | ControllerBase, SourceView |
| Methods/Variables | snake_case | set_controller_context, _cut_buffer |
| Private methods | Leading underscore | _setup_styles, _subscribe_to_events |
| Constants | UPPER_SNAKE_CASE | LOG_LEVEL, DEFAULT_ZOOM |
| Exception classes | PascalCase ending in Exception | ControllerBaseException |
Class Structure
class MyClass(Singleton, EmitDispatcher):
def __init__(self):
super(MyClass, self).__init__()
self.controller_context: ControllerContext = None
self._private_var = None
def public_method(self, param: str) -> None:
...
def _private_method(self) -> None:
...
Placeholder Methods
Use ellipsis ... for unimplemented methods or abstract-like methods:
def _subscribe_to_events(self):
...
Dataclasses for DTOs/Events
Use @dataclass for data transfer objects and events:
from dataclasses import dataclass, field
@dataclass
class TextInsertedEvent(CodeEvent):
location: Gtk.TextIter = None
text: str = ""
length: int = 0
Error Handling
- Custom exceptions should end with
Exception - Use
raise ValueError("message")for validation errors - Let exceptions propagate for unexpected errors
Singleton Pattern
Classes requiring singleton behavior should inherit from Singleton:
from libs.singleton import Singleton
class MySingleton(Singleton):
...
GTK/Widget Conventions
- Call
_setup_styles(),_setup_signals(),_subscribe_to_events(),_load_widgets()in__init__ - Use
self.connect("signal-name", self._handler)for GTK signals - Private GTK attributes use underscore prefix:
self._cut_temp_timeout_id
Code Formatting
- Use spaces around operators:
x = value, notx=value - Align assignments with spaces for related variables:
self.state = state self._cut_temp_timeout_id = None - Maximum line length: Let pyright/editor handle warnings (typically 80-120 chars)
No Comments
DO NOT add comments to code unless explicitly required. Write self-documenting code.
Project Structure
Newton/
├── src/
│ ├── app.py # Main application entry
│ ├── __main__.py # Module entry point
│ ├── __builtins__.py # Built-in definitions
│ ├── core/ # Core UI components
│ │ ├── containers/ # Layout containers
│ │ ├── controllers/ # Controllers
│ │ └── widgets/ # GTK widgets
│ ├── libs/ # Core libraries
│ │ ├── controllers/ # Controller infrastructure
│ │ ├── db/ # Database (SQLModel)
│ │ ├── dto/ # Data transfer objects
│ │ ├── mixins/ # Mixin classes
│ │ └── settings/ # Settings management
│ └── plugins/ # Plugin system
├── plugins/ # Plugin implementations
├── user_config/ # User configuration files
├── requirements.txt # Python dependencies
└── pyrightconfig.json # Pyright configuration
Key Libraries Used
- PyGObject (GTK3) - GUI framework
- sqlmodel - Database (SQLAlchemy + Pydantic)
- setproctitle - Process title management
- pyxdg - XDG desktop file parsing
Common Patterns
Creating a New Controller
from libs.controllers.controller_base import ControllerBase
from libs.controllers.controller_context import ControllerContext
class MyController(ControllerBase):
def __init__(self):
super().__init__()
self.controller_context = None
def _controller_message(self, event: BaseEvent):
# Handle events
pass
Creating a New Event/DTO
from dataclasses import dataclass
from libs.dto.code.code_event import CodeEvent
@dataclass
class MyEvent(CodeEvent):
field1: str = ""
field2: int = 0
Connecting to Events
from libs.event_system import EventSystem
event_system = EventSystem()
event_system.subscribe("event_name", self.my_handler)