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
87 changes: 49 additions & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,60 +18,75 @@ jobs:
check-manifest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- run: pipx run check-manifest

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.1.0
with:
enable-cache: true
python-version: "3.13"
- run: uv run prek run --all-files

pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.1.0
with:
enable-cache: true
python-version: "3.13"
- run: uv run --with pyqt6 pyright

test:
name: ${{ matrix.platform }} py${{ matrix.python-version }}
name: ${{ matrix.platform }} py${{ matrix.python-version }} ${{ matrix.resolution }}
runs-on: ${{ matrix.platform }}
env:
UV_PRERELEASE: ${{ github.event_name == 'schedule' && 'allow' || 'if-necessary-or-explicit' }}
UV_NO_SYNC: 1
UV_RESOLUTION: ${{ matrix.resolution }}
PYTEST_ADDOPTS: ${{ matrix.resolution == 'lowest-direct' && '-W ignore' || '' }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.12", "3.14"]
python-version: ["3.11", "3.13", "3.15"]
platform: [ubuntu-latest, macos-latest, windows-latest]
resolution: [highest]
include:
- python-version: "3.12"
platform: macos-latest
resolution: highest
- python-version: "3.14"
platform: macos-latest
resolution: highest
- python-version: "3.11"
platform: "macos-latest"
- python-version: "3.13"
platform: "macos-latest"
- python-version: "3.10"
platform: "ubuntu-latest"
resolution: "lowest-direct"
platform: ubuntu-latest
resolution: lowest-direct
- python-version: "3.15"
platform: ubuntu-latest
resolution: lowest-direct

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v10.1.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
cache-dependency-glob: "**/pyproject.toml"

- name: Install Dependencies
run: uv sync --no-dev --group test --resolution ${{ matrix.resolution || 'highest'}}

- name: 🧪 Run Tests
run: uv run coverage run -p -m pytest -v
env:
PYTEST_ADDOPTS: ${{ matrix.resolution == 'lowest-direct' && '-W ignore' || '' }}
run: uv run --no-dev --group test coverage run -p -m pytest -v

- name: Upload coverage
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: covreport-${{ matrix.platform }}-py${{ matrix.python-version }}
name: covreport-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ matrix.resolution }}
path: ./.coverage*
include-hidden-files: true

Expand All @@ -80,38 +95,34 @@ jobs:
runs-on: ${{ matrix.platform }}
env:
UV_PRERELEASE: ${{ github.event_name == 'schedule' && 'allow' || 'if-necessary-or-explicit' }}
UV_NO_SYNC: 1
UV_RESOLUTION: ${{ matrix.resolution }}
PYTEST_ADDOPTS: ${{ matrix.resolution == 'lowest-direct' && '-W ignore' || '' }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.14"]
python-version: ["3.11", "3.15"]
platform: [macos-latest, windows-latest]
extra: [pyside6, pyqt6]
resolution: [highest, lowest-direct]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v10.1.0
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
cache-dependency-glob: "**/pyproject.toml"

- uses: pyvista/setup-headless-display-action@v4
- uses: pyvista/setup-headless-display-action@v5
with:
qt: true

- name: Install Dependencies
run: uv sync --no-dev --group test-qt --extra ${{ matrix.extra }} --resolution ${{ matrix.resolution }}

- name: 🧪 Run Tests
run: uv run coverage run -p -m pytest -v
env:
PYTEST_ADDOPTS: ${{ matrix.resolution == 'lowest-direct' && '-W ignore' || '' }}
run: uv run --no-dev --group test-qt --extra ${{ matrix.extra }} coverage run -p -m pytest -v

# If something goes wrong with --pre tests, we can open an issue in the repo
- name: 📝 Report --pre Failures
Expand All @@ -128,7 +139,7 @@ jobs:
update_existing: true

- name: Upload coverage
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: covreport-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ matrix.extra }}-${{ matrix.resolution }}
path: ./.coverage*
Expand Down Expand Up @@ -158,10 +169,10 @@ jobs:
needs: [check-manifest, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: hynek/build-and-inspect-python-package@v2
- uses: hynek/build-and-inspect-python-package@v3.0.1

upload-to-pypi:
name: Upload package to PyPI
Expand All @@ -174,13 +185,13 @@ jobs:

steps:
- name: Download built artifact to dist/
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: Packages
path: dist
- name: 🚢 Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- uses: softprops/action-gh-release@v2
- uses: softprops/action-gh-release@v3
with:
generate_release_notes: true
files: "./dist/*"
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@v10.1.0
with:
python-version: "3.13"
enable-cache: true
Expand All @@ -40,6 +40,6 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/configure-pages@v5
- uses: actions/configure-pages@v6
- uses: actions/deploy-pages@v5
id: deployment
16 changes: 11 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,31 @@ ci:

repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.25
rev: "0.26"
hooks:
- id: validate-pyproject

- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
hooks:
- id: actionlint
files: "^\\.github/workflows/.*\\.ya?ml$"

- repo: https://github.com/adhtruong/mirrors-typos
rev: v1.46.0
rev: v1.50.2
hooks:
- id: typos
args: [--force-exclude]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
rev: v0.16.8
hooks:
- id: ruff-check
args: ["--fix", "--unsafe-fixes"]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.20.2
rev: v2.3.1
hooks:
- id: mypy
files: "^src/"
Expand All @@ -40,4 +46,4 @@ repos:
exclude: "^tests/.*|^demo/.*|^docs/.*"
types_or: [python, pyi]
require_serial: true
entry: uv run pyright
entry: uv run --with pyqt6 pyright
6 changes: 3 additions & 3 deletions demo/multi_file/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from enum import Enum
from enum import StrEnum


class CommandId(str, Enum):
class CommandId(StrEnum):
OPEN = "myapp.open"
CLOSE = "myapp.close"
SAVE = "myapp.save"
Expand All @@ -17,7 +17,7 @@ def __str__(self) -> str:
return self.value


class MenuId(str, Enum):
class MenuId(StrEnum):
FILE = "myapp/file"
EDIT = "myapp/edit"

Expand Down
35 changes: 20 additions & 15 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ object.
```python
from app_model import Application

my_app = Application('my-app')
my_app = Application("my-app")
```

## Registering Actions
Expand All @@ -27,27 +27,30 @@ and additional metadata like title, icons, tooltips, etc...
```python
from app_model.types import Action, KeyBindingRule, KeyCode, KeyMod, MenuRule


def open_file():
print('open file!')
print("open file!")


def close_window():
print('close window!')
print("close window!")


ACTIONS: list[Action] = [
Action(
id='open',
id="open",
title="Open",
icon="fa6-solid:folder-open",
callback=open_file,
menus=['File'],
menus=["File"],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyO)],
),
Action(
id='close',
id="close",
title="Close",
icon="fa-solid:window-close",
callback=close_window,
menus=['File'],
menus=["File"],
keybindings=[KeyBindingRule(primary=KeyMod.CtrlCmd | KeyCode.KeyW)],
),
# ...
Expand Down Expand Up @@ -105,9 +108,10 @@ You can connect callbacks to these events to handle them as needed.
```python
@my_app.commands.registered.connect
def on_command_registered(command_id: str):
print(f'Command {command_id!r} registered!')
print(f"Command {command_id!r} registered!")

my_app.commands.register_command('new-id', lambda: None, title='No-op')

my_app.commands.register_command("new-id", lambda: None, title="No-op")
# Command 'new-id' registered!
```

Expand All @@ -116,7 +120,7 @@ my_app.commands.register_command('new-id', lambda: None, title='No-op')
Registered commands may be executed on-demand using [`execute_command`][app_model.registries.CommandsRegistry.execute_command] method on the command registry:

```python
my_app.commands.execute_command('open')
my_app.commands.execute_command("open")
# prints "open file!" from the `open_file` function registered above.
```

Expand Down Expand Up @@ -150,7 +154,7 @@ method:
```python
class User:
def name(self):
return 'John Doe'
return "John Doe"
```

Assume the application has some way of retrieving the current user:
Expand All @@ -174,14 +178,15 @@ for callbacks in actions registered with the application.
def print_user_name(user: User) -> None:
print(f"Hi {user.name()}!")


action = Action(
id='greet',
id="greet",
title="Greet Current User",
callback=print_user_name,
)

my_app.register_action(action)
my_app.commands.execute_command('greet')
my_app.commands.execute_command("greet")
# prints "Hi John Doe!"
```

Expand Down Expand Up @@ -215,12 +220,12 @@ main = QModelMainWindow(my_app)

# pick menus for main menu bar,
# using menu ids from the application's MenusRegistry
main.setModelMenuBar(['File'])
main.setModelMenuBar(["File"])

# add toolbars using menu ids from the application's MenusRegistry
# here we re-use the File menu ... but you can have menus
# dedicated for toolbars, or just exclude items from the menu
main.addModelToolBar('File')
main.addModelToolBar("File")
main.show()

app.exec()
Expand Down
Loading
Loading