Role: You are an expert Python Desktop Application Engineer and Technical Lead specializing in accessible, cross-platform wxPython applications. You are responsible for understanding requirements, implementing carefully, testing, and preserving accessibility.
# Development
uv run accessiweather # Run the app from source
pytest -v # Run all tests serially
pytest -n auto # Run all tests in parallel
pytest tests/test_file.py::test_func # Run one test
pytest -k "test_name" -v # Run tests matching a pattern
pytest --lf --ff -m "unit" # Run last-failed/first-failed unit tests
# Linting & Formatting
ruff check --fix . && ruff format . # Lint and format code
pyright # Type checking; may be scoped for noisy legacy areas
# Build & Package
python installer/build_nuitka.py # Build packaged app artifacts
python installer/build.py --dev # Run installer build helper in development mode
# Git on Windows
git --no-pager log --oneline -5
git --no-pager diff
git --no-pager show HEADAccessiWeather is a cross-platform accessible desktop weather application built with Python and wxPython. It prioritizes screen reader accessibility, multi-source weather data, weather alerts, and straightforward JSON-based configuration.
| Category | Technology |
|---|---|
| Language | Python 3.11+ |
| GUI Framework | wxPython |
| HTTP Client | httpx |
| Build Tool | Nuitka packaging scripts |
| Testing | pytest, pytest-asyncio, pytest-xdist, hypothesis |
| Linting | Ruff |
| Type Checking | Pyright |
| Package Format | pyproject.toml |
Main Package: src/accessiweather/
| Module | Purpose |
|---|---|
app.py |
Main wxPython application orchestration |
main.py |
Console/GUI entry point |
weather_client.py |
Multi-source weather data orchestration |
alert_manager.py |
Weather alert management with rate limiting |
alert_notification_system.py |
Desktop notifications for weather alerts |
background_tasks.py |
Async periodic weather updates |
cache.py |
Weather API response caching |
ui/ |
wxPython windows, dialogs, and UI helpers |
config/ |
Settings, saved locations, secure storage, and source priority |
api/ |
NWS, Open-Meteo, Visual Crossing, and related API clients |
- Config is JSON-based, stored in the user config directory or a portable directory.
- API keys are stored through keyring-backed secure storage.
- There is no database.
- Use Ruff formatting: 100-character line length, double quotes, sorted imports.
- Prefer modern type hints:
dict[str, Any],Location | None, andfrom __future__ import annotations. - Keep files under 1000 lines where practical; split large modules before growing them further.
- Preserve unrelated user or agent changes in the working tree.
- Add focused comments only when they explain non-obvious behavior.
# DO: await async functions
data = await weather_client.fetch_weather()
# DO: schedule background work through the app's existing scheduler or asyncio task helpers
asyncio.create_task(background_refresh())
# DON'T: call asyncio.run() from GUI code- Keep UI work on the wx main thread; use
wx.CallAfterwhen crossing thread boundaries. - Prefer existing dialog/window helpers and local UI conventions over introducing new frameworks.
- All new interactive controls need accessible labels or names, useful descriptions where supported, keyboard access, and sensible focus behavior.
- Tests normally use the in-repo wx stub from
tests/conftest.py; setACCESSIWEATHER_ALLOW_REAL_WX_IN_TESTS=1only when a real wxPython smoke test is required.
- Unit tests live under
tests/test_*.pyand should mock external services. - GUI-focused tests may use the wx stub or live wx under xvfb/Windows only when needed.
- Integration tests live under
tests/integration/and should avoid live API dependence unless explicitly intended. - Use Hypothesis for broad input-space edge cases.
- Use the Hypothesis testing framework to good effect for bugs involving many combinations of provider data, missing fields, units, priorities, or fallback behavior.
- Prefer behavior and user-contract tests over coverage-padding tests.
- Name regression tests after the behavior or failure mode, not a PR number, coverage gap, or CI failure. If a temporary coverage/CI test is unavoidable, give it a clear follow-up path.
- Avoid
inspect.getsource()and source-string assertions unless there is no practical runtime behavior to assert. - If coverage pressure makes a test awkward, first consider whether the code should be refactored, excluded for a reasoned defensive branch, or tested through a public/domain behavior.
- Large parameterized or matrix tests are fine when they protect real parsing, mapping, provider, accessibility, playback, notification, or user-visible behavior. Avoid duplicating the same contract at multiple layers only for line coverage.
pytest -n auto -v --tb=short
pytest tests/ -m "not integration" -n auto
pytest tests/test_weather_client.py::test_fetch -v -s
pytest --hypothesis-profile=thorough| Workflow | Purpose |
|---|---|
ci.yml |
Linting, tests, changelog check, coverage gate |
build.yml |
Nuitka desktop build artifacts |
integration-tests.yml |
Scheduled integration cassette checks |
update-pages.yml |
GitHub Pages/download updates |
Important CI environment defaults:
FORCE_COLOR: "0"
PYTHONUTF8: "1"
ACCESSIWEATHER_TEST_MODE: "1"
HYPOTHESIS_PROFILE: ci- Use conventional commits and conventional PR titles.
- Branch from
devfor normal feature/fix work. - PRs should normally target
dev. - Use
gh apiRESTPATCHcalls for PR metadata updates instead ofgh pr edit. - Use
git --no-pagerfor Git inspection commands on Windows.
- Never commit
.envfiles or API keys. - Store API keys via
SecureStorage. - Validate coordinates: latitude
-90..90, longitude-180..180. - Sanitize user-provided strings before logging.
- Keep HTTP timeouts on external requests.
- Every interactive UI element must be reachable by keyboard.
- Controls need clear accessible names and useful descriptions/status text.
- Dialogs should set initial focus intentionally and preserve normal keyboard escape/cancel behavior.
- Dynamic updates should be announced or reflected in status text when useful to screen reader users.
AccessiWeather runs a CI gate (scripts/changelog_tools.py check) that fails
when a user-facing change reaches dev/main without a curated CHANGELOG.md
bullet under ## [Unreleased].
- When an entry is required: changes under
src/,installer/, orsoundpacks/(plusaccessiweather.spec,pyproject.tomldependency changes, andscripts/generate_build_meta.py). Write the bullet as a plain-language, user-facing note; describe the benefit, not the implementation. - Already exempt, with no entry or marker needed:
.github/,tests/,docs/, most ofscripts/, and the generatedsrc/accessiweather/weather_gov_api_client/client. - Exempting an internal change that still touches gated paths such as
refactors, CI, tooling, or release plumbing inside
src/:- On a PR: add the
skip-changeloglabel. - On a direct push to
dev/main: includeChangelog: noneor[skip changelog]in the commit message. The gate only passes when every non-merge commit in the range carries the marker, so do not mix a marked internal commit with an unmarked user-facing one in the same push.
- On a PR: add the
- Do not pad the changelog with technical or internal notes just to satisfy the gate. Use the exemptions above instead.
Write entries like a human:
- Detected current locations outside the US now get an editable place name when reverse geocoding can identify the coordinates.