Bundle demo (#7)

pull/8/head
Josh Karpel 3 years ago committed by GitHub
parent 62f593f868
commit 1089502331
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,6 +6,9 @@ source =
spiel/
tests/
omit =
spiel/demo/*
[report]
skip_empty = True

@ -31,7 +31,6 @@ jobs:
uses: snok/install-poetry@v1.1.2
with:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached virtual environment
id: cached-poetry-dependencies
uses: actions/cache@v2
@ -43,6 +42,8 @@ jobs:
run: poetry install --no-interaction --no-root
- name: Install package
run: poetry install --no-interaction
- name: Make sure we can build the package
run: poetry build
- name: Run tests
run: poetry run pytest --cov --cov-report=xml
- name: Upload coverage

@ -1,14 +0,0 @@
import string
from random import sample
from rich.text import Text
from spiel import Deck, Slide
DECK = Deck(name="Many Slides")
DECK.add_slides(
*(
Slide(Text(f"This is slide {n + 1}"), title="".join(sample(string.ascii_letters, 30)))
for n in range(30)
)
)

@ -3,6 +3,8 @@ pretty = false
files = spiel/*.py, tests/*.py
exclude = demo/
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_defs = true

@ -17,6 +17,7 @@ version = "0.1.0"
description = "Present slides in your terminal."
authors = ["JoshKarpel <josh.karpel@gmail.com>"]
license = "MIT"
include = ["demo/*"]
[tool.poetry.dependencies]
python = "^3.9"
@ -42,3 +43,4 @@ spiel = 'spiel.main:app'
[tool.pytest.ini_options]
addopts = ['--strict-markers', '--mypy']
testpaths = ["tests", "spiel"]
norecursedirs = ["demo"]

@ -2,5 +2,6 @@ from importlib import metadata
PACKAGE_NAME = "spiel"
__version__ = metadata.version(PACKAGE_NAME)
__rich_version__ = metadata.version("rich")
DECK = "DECK"

@ -24,7 +24,7 @@ RICH = "[Rich](https://rich.readthedocs.io/)"
DECK = Deck(name=f"Spiel Demo Deck (v{__version__})")
EXAMPLES_DIR = Path(__file__).resolve().parent
THIS_DIR = Path(__file__).resolve().parent
@DECK.slide(title="What is Spiel?")
@ -38,6 +38,8 @@ def what():
Spiel uses {RICH} to render slide content.
Anything you can display with Rich, you can display with Spiel (plus some other things)!
Use your right and left arrows keys (or 'f' and 'b') to go forwards and backwards through the deck. Press ctrl-k to exit.
"""
)
@ -65,7 +67,7 @@ def what():
Brandon Rhodes' [PyCon 2017](https://youtu.be/66P5FMkWoVU) and [North Bay Python 2017](https://youtu.be/rrMnmLyYjU8) talks.
David Beazley's [Lambda Calculus from the Ground Up](https://youtu.be/pkCLMl0e_0k) tutorial at Pycon 2019
David Beazley's [Lambda Calculus from the Ground Up](https://youtu.be/pkCLMl0e_0k) tutorial at PyCon 2019.
LaTeX's [Beamer](https://ctan.org/pkg/beamer) document class.
"""
@ -201,7 +203,7 @@ def watch():
{SPIEL} can reload your deck as you edit it if you add the `--watch` option to `present`:
`$ spiel present examples/demo.py --watch`
`$ spiel present path/to/deck.py --watch`
If you're on a system without inotify support (e.g., Windows Subsystem for Linux), you may need to use the `--poll` option instead.
@ -225,13 +227,13 @@ def image():
`export COLORTERM=truecolor`
to your `.bashrc` file.
to your `.bashrc` file, then restart your shell.
"""
)
root = Layout()
root.split_row(
Layout(Padding(Markdown(markup, justify="center"), pad=(0, 2))),
Layout(Image(EXAMPLES_DIR / "img.jpg")),
Layout(Image(THIS_DIR / "img.jpg")),
)
return root

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

@ -12,7 +12,3 @@ class UnknownModeError(SpielException):
class NoDeckFound(SpielException):
pass
class Quit(SpielException):
pass

@ -18,7 +18,9 @@ from typing import (
Union,
)
from .exceptions import DuplicateInputHandler, Quit
from typer import Exit
from .exceptions import DuplicateInputHandler
from .modes import Mode
from .state import State
@ -116,7 +118,7 @@ def get_character(stream: TextIO) -> Union[str, SpecialCharacters]:
result = stream.read(1)
if result == "": # this happens when stdin gets closed; equivalent to a quit
raise Quit()
raise Exit(code=0)
if result[-1] == "\x1b":
result += stream.read(2)
@ -168,12 +170,12 @@ def input_handler(
return decorator
@input_handler(SpecialCharacters.Right)
@input_handler(SpecialCharacters.Right, "f")
def next_slide(state: State) -> None:
state.next_slide()
@input_handler(SpecialCharacters.Left)
@input_handler(SpecialCharacters.Left, "b")
def previous_slide(state: State) -> None:
state.previous_slide()
@ -200,4 +202,4 @@ def slide_mode(state: State) -> None:
@input_handler(SpecialCharacters.CtrlK)
def kill(state: State) -> None:
raise Quit()
raise Exit(code=0)

@ -1,22 +1,29 @@
import shutil
import sys
from contextlib import nullcontext
from pathlib import Path
from rich.console import Console
from rich.style import Style
from rich.syntax import Syntax
from rich.table import Column, Table
from rich.text import Text
from typer import Argument, Option, Typer
from typer import Argument, Exit, Option, Typer
from spiel.constants import PACKAGE_NAME, __version__
from spiel.constants import PACKAGE_NAME, __rich_version__, __version__
from spiel.load import DeckReloader, DeckWatcher, load_deck
from spiel.modes import Mode
from spiel.present import present_deck
from spiel.state import State
THIS_DIR = Path(__file__).resolve().parent
app = Typer()
@app.command()
def present(
path: Path = Argument(..., help="The path to the slide deck file."),
path: Path = Argument(..., dir_okay=False, help="The path to the slide deck file."),
mode: Mode = Option(default=Mode.SLIDE, help="The mode to start presenting in."),
watch: bool = Option(
default=False, help="If enabled, reload the deck when the slide deck file changes."
@ -26,6 +33,13 @@ def present(
help="If enabled, poll the filesystem for changes (implies --watch). Use this option on systems that don't support file modification notifications.",
),
) -> None:
"""
Present a deck.
"""
_present(path=path, mode=mode, watch=watch, poll=poll)
def _present(path: Path, mode: Mode, watch: bool, poll: bool) -> None:
state = State(
console=Console(),
deck=load_deck(path),
@ -38,10 +52,88 @@ def present(
else nullcontext()
)
with watcher:
present_deck(state)
try:
with watcher:
present_deck(state)
except KeyboardInterrupt:
raise Exit(code=0)
@app.command()
def version() -> None:
Console().print(Text(f"{PACKAGE_NAME} {__version__}"))
"""
Display version information for spiel and critical dependencies.
"""
console = Console()
grid = Table(
Column(justify="right"),
Column(justify="left"),
show_header=False,
box=None,
)
grid.add_row(PACKAGE_NAME, __version__)
grid.add_row("rich", __rich_version__)
grid.add_row("python", ".".join(map(str, sys.version_info)))
console.print(grid)
demo = Typer(
name="demo",
help="Use the demonstration deck (present it, display source, etc.)",
)
DEMO_DIR = THIS_DIR / "demo"
DEMO_SOURCE = THIS_DIR / "demo" / "demo.py"
@demo.command(name="present")
def present_demo() -> None:
"""
Present the demo deck.
"""
_present(path=DEMO_SOURCE, mode=Mode.SLIDE, watch=False, poll=False)
@demo.command()
def source() -> None:
"""
Display the source code for the demo deck in your PAGER.
"""
console = Console()
with console.pager(styles=True):
console.print(Syntax(DEMO_SOURCE.read_text(), lexer_name="python"))
@demo.command()
def copy(
path: Path = Argument(
default=...,
writable=True,
help="The path to copy the demo deck source code and assets to.",
)
) -> None:
"""
Copy the demo deck source code and assets to a new directory.
"""
console = Console()
if path.exists():
console.print(Text(f"Error: {path} already exists!", style=Style(color="red")))
raise Exit(code=2)
try:
shutil.copytree(DEMO_DIR, path)
except Exception as e:
console.print(Text(f"Failed to copy demo deck directory: {e}", style=Style(color="red")))
raise Exit(code=1)
console.print(
Text(f"Wrote demo deck source code and assets to {path}", style=Style(color="green"))
)
app.add_typer(demo)

@ -11,7 +11,7 @@ from rich.style import Style
from spiel import Slide
from .exceptions import Quit, UnknownModeError
from .exceptions import UnknownModeError
from .footer import Footer
from .input import handle_input, no_echo
from .modes import Mode
@ -88,9 +88,6 @@ def present_deck(state: State) -> None:
refresh_per_second=10,
vertical_overflow="visible",
) as live:
try:
while True:
handle_input(state, sys.stdin)
live.refresh()
except Quit:
return
while True:
handle_input(state, sys.stdin)
live.refresh()

@ -1,6 +1,5 @@
import subprocess
import sys
import traceback
from pathlib import Path
import pytest
@ -8,7 +7,6 @@ from typer.testing import CliRunner
from spiel.constants import PACKAGE_NAME, __version__
from spiel.main import app
from spiel.modes import Mode
@pytest.fixture
@ -36,10 +34,40 @@ def test_version(runner: CliRunner) -> None:
assert __version__ in result.stdout
@pytest.mark.parametrize("deck_path", (Path(__file__).parents[1] / "examples").glob("*.py"))
@pytest.mark.parametrize("mode", list(Mode))
@pytest.mark.parametrize("stdin", ["", "s", "d"])
def test_display_example_decks(runner: CliRunner, deck_path: Path, mode: Mode, stdin: str) -> None:
result = runner.invoke(app, ["present", str(deck_path), "--mode", mode], input=stdin)
def test_demo_display(runner: CliRunner) -> None:
result = runner.invoke(app, ["demo", "present"])
assert result.exit_code == 0
def test_demo_source(runner: CliRunner) -> None:
result = runner.invoke(app, ["demo", "source"])
assert result.exit_code == 0
def test_demo_copy_to_new_path(runner: CliRunner, tmp_path: Path) -> None:
target = tmp_path / "new"
result = runner.invoke(app, ["demo", "copy", str(target)])
print(result.stdout)
assert result.exit_code == 0
def test_demo_copy_to_existing_file(runner: CliRunner, tmp_path: Path) -> None:
target = tmp_path / "new"
target.touch()
result = runner.invoke(app, ["demo", "copy", str(target)])
assert result.exit_code == 2
def test_demo_copy_to_existing_dir(runner: CliRunner, tmp_path: Path) -> None:
target = tmp_path / "new"
target.mkdir(parents=True)
result = runner.invoke(app, ["demo", "copy", str(target)])
assert result.exit_code == 2

@ -8,9 +8,9 @@ from hypothesis import given, settings
from hypothesis import strategies as st
from rich.console import Console
from rich.text import Text
from typer import Exit
from spiel import Deck, Slide
from spiel.exceptions import Quit
from spiel.input import (
INPUT_HANDLERS,
InputHandler,
@ -51,7 +51,7 @@ def test_enter_slide_mode(three_slide_state: State) -> None:
def test_kill(three_slide_state: State) -> None:
with pytest.raises(Quit):
with pytest.raises(Exit):
kill(three_slide_state)

Loading…
Cancel
Save