Drop Python 3.9, remove unused docs and perf tests (#129)

pull/135/head
Josh Karpel 2 years ago committed by GitHub
parent af8717880f
commit 6e94c12006
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,7 +12,7 @@ jobs:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest]
python-version: ["3.9", "3.10"]
python-version: ["3.10"]
defaults:
run:
shell: bash

@ -12,6 +12,7 @@ repos:
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- id: check-json
- id: debug-statements
- id: end-of-file-fixer
- id: forbid-new-submodules
@ -24,13 +25,7 @@ repos:
- id: python-no-eval
- id: python-no-log-warn
- id: python-use-type-annotations
- id: rst-backticks
- id: rst-directive-colons
- id: rst-inline-touching-normal
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: black
- id: python-check-blanket-type-ignore
- repo: https://github.com/hadialqattan/pycln
rev: v2.0.1
hooks:
@ -40,7 +35,7 @@ repos:
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.3.0
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: remove-crlf
- id: black

@ -1,13 +0,0 @@
version: 2
sphinx:
configuration: docs/conf.py
build:
image: testing
python:
version: 3.9
install:
- method: pip
path: .

@ -0,0 +1,7 @@
# Changelog
## 0.3.0
### Removed
- [#129](https://github.com/JoshKarpel/spiel/pull/129) Dropped support for Python `<=3.9`.

@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.10
RUN : \
&& apt-get update \

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

@ -1,33 +0,0 @@
import sphinx_rtd_theme
import spiel
project = spiel.constants.PACKAGE_NAME.capitalize()
copyright = "2021, Josh Karpel"
author = "Josh Karpel"
release = spiel.constants.__version__
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx_rtd_theme",
]
templates_path = ["_templates"]
html_static_path = ["_static"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
html_theme = "sphinx_rtd_theme"
# sphinx-autodoc
autoclass_content = "both"
autodoc_member_order = "bysource"
autodoc_default_options = {
"members": True,
"undoc-members": True,
}
autodoc_typehints = "signature"
# sphinx-autodoc-typehints
set_type_checking_flag = True

@ -1,18 +0,0 @@
Spiel
=====
Spiel is a framework for building and presenting richly-styled presentations in your terminal using Python.
To see what Spiel can do, install it (``pip install spiel``), then run this command to view the demonstration deck:
.. code-block:: console
$ spiel demo present
.. toctree::
:maxdepth: 2
:hidden:
self
reference

@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

@ -1,28 +0,0 @@
Reference
=========
.. py:currentmodule:: spiel
Decks and Slides
----------------
.. autoclass:: Deck
.. autoclass:: Slide
.. autoclass:: Example
Widgets
-------
.. autoclass:: Image
.. autoclass:: Plot
Triggers
--------
.. autoclass:: Triggers

@ -1,29 +0,0 @@
#!/usr/bin/env python
import os
from rich.console import Console
from spiel.main import DEMO_SOURCE
from spiel.present import render_slide
from spiel.state import State
CYCLES_PER_SLIDE = 10
TRIGGERS_PER_SLIDE = 10
def render_demo_repeatedly() -> None:
with open(os.devnull, "w") as f:
state = State.from_file(DEMO_SOURCE, console=Console(file=f))
for _ in range(CYCLES_PER_SLIDE):
for slide in state.deck:
for _ in range(TRIGGERS_PER_SLIDE):
rendered = render_slide(state, slide)
state.console.print(rendered)
state.trigger()
state.reset_trigger()
if __name__ == "__main__":
render_demo_repeatedly()

@ -1,25 +0,0 @@
#!/usr/bin/env python
import os
from rich.console import Console
from spiel.main import DEMO_SOURCE
from spiel.present import render_slide
from spiel.state import State
CYCLES_PER_SLIDE = 100
def render_image_repeatedly() -> None:
with open(os.devnull, "w") as f:
state = State.from_file(DEMO_SOURCE, console=Console(file=f))
for _ in range(CYCLES_PER_SLIDE):
slide = [slide for slide in state.deck.slides if "Image" in slide.title][0]
rendered = render_slide(state, slide)
state.console.print(rendered)
if __name__ == "__main__":
render_image_repeatedly()

1367
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -2,15 +2,6 @@
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 100
target-version = ["py36", "py37", "py38"]
include = "\\.pyi?$"
[tool.isort]
profile = "black"
line_length = 100
[tool.poetry]
name = "spiel"
version = "0.3.0"
@ -24,7 +15,6 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Unix",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Multimedia :: Graphics :: Presentation",
"Typing :: Typed"]
@ -33,9 +23,9 @@ license = "MIT"
include = ["py.typed", "demo/*"]
[tool.poetry.dependencies]
python = ">=3.9,<4"
python = ">=3.10,<4"
rich = ">=12"
typer = ">=0.3"
typer = ">=0.6"
watchdog = ">=2"
pendulum = ">=2"
Pillow = ">=8"
@ -49,25 +39,34 @@ pytest = ">=6"
pytest-watch = ">=4"
pytest-cov = ">=3"
pytest-xdist = ">=2"
mypy = ">=0.900"
mypy = ">=0.961"
pytest-mypy = ">=0.8"
pytest-mock = ">=3"
hypothesis = ">=6"
Sphinx = ">=4"
sphinx-rtd-theme = ">=0.5"
sphinx-autobuild = ">=2021.3.14"
line-profiler = ">=3"
[tool.poetry.scripts]
spiel = 'spiel.main:app'
[tool.black]
line-length = 100
target-version = ["py39", "py310"]
include = "\\.pyi?$"
[tool.isort]
profile = "black"
line_length = 100
[tool.pycln]
all = true
[tool.pytest.ini_options]
addopts = ["--strict-markers", "--mypy"]
addopts = ["--strict-markers", "--mypy", "-n", "auto"]
testpaths = ["tests", "spiel"]
norecursedirs = ["demo"]
[tool.mypy]
pretty = false
show_error_codes = true
files = ["spiel/*.py", "tests/*.py"]

@ -3,10 +3,9 @@ from __future__ import annotations
import dis
import inspect
import sys
from collections.abc import Collection
from collections.abc import Callable, Collection, Iterator, Sequence
from dataclasses import dataclass, field
from textwrap import dedent
from typing import Callable, Iterator, List, Sequence
from spiel.example import Example
from spiel.presentable import Presentable
@ -16,7 +15,7 @@ from spiel.slide import MakeRenderable, Slide
@dataclass
class Deck(Collection[Presentable]):
name: str
slides: List[Presentable] = field(default_factory=list)
slides: list[Presentable] = field(default_factory=list)
def __getitem__(self, idx: int) -> Presentable:
return self.slides[idx]
@ -69,12 +68,8 @@ class Deck(Collection[Presentable]):
return exampleify
def get_function_body(function: Callable[[], None]) -> str:
def get_function_body(function: Callable[..., object]) -> str:
lines, line_of_def_start = inspect.getsourcelines(function)
line_of_first_instruction = list(dis.Bytecode(function))[0].starts_line or line_of_def_start
offset = line_of_first_instruction - line_of_def_start
return dedent("".join(lines[offset:]))
def count_leading_whitespace(s: str) -> int:
return len(s) - len(s.lstrip())

@ -123,7 +123,7 @@ def code():
Here's the code for `Deck` and `Slide`!
The source code is pulled directly from the definitions via [`inspect.getsource`](https://docs.python.org/3/library/inspect.html#inspect.getsource).
The source code is pulled directly from the definitions via [inspect.getsource](https://docs.python.org/3/library/inspect.html#inspect.getsource).
({RICH} supports syntax highlighting, so {SPIEL} does too!)
"""

@ -3,10 +3,10 @@ from __future__ import annotations
import shlex
import sys
import tempfile
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from pathlib import Path
from subprocess import PIPE, STDOUT, run
from typing import Callable, Optional, Sequence
from rich.align import Align
from rich.console import ConsoleRenderable
@ -23,7 +23,7 @@ from .triggers import Triggers
class CachedExample:
trigger_number: int
input: str
output: Optional[str]
output: str | None
def example_panels(example: Example) -> ConsoleRenderable:
@ -65,7 +65,7 @@ class Example(Presentable):
name: str = "example.py"
language: str = "python"
_layout: ExampleLayout = example_panels
_cache: Optional[CachedExample] = None
_cache: CachedExample | None = None
def layout(self, function: ExampleLayout) -> ExampleLayout:
self._layout = function
@ -93,7 +93,7 @@ class Example(Presentable):
)
@property
def output(self) -> Optional[Text]:
def output(self) -> Text | None:
return (
Text(self._cache.output)
if (self._cache is not None and self._cache.output is not None)

@ -1,10 +1,11 @@
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
from functools import lru_cache
from math import floor
from pathlib import Path
from typing import Iterable, List, NamedTuple, Tuple, Union
from typing import NamedTuple
from PIL import Image as Img
from PIL.Image import Resampling
@ -21,11 +22,11 @@ class ImageSize(NamedTuple):
height: int
Pixels = Tuple[Union[Tuple[int, int, int], None], ...]
Pixels = tuple[tuple[int, int, int] | None, ...]
@lru_cache(maxsize=2**8)
def _pixels_to_segments(pixels: Pixels, size: ImageSize) -> List[Segment]:
def _pixels_to_segments(pixels: Pixels, size: ImageSize) -> list[Segment]:
line = Segment.line()
segments = []

@ -5,6 +5,7 @@ import inspect
import string
import sys
import termios
from collections.abc import Callable, Iterable, Iterator, MutableMapping
from contextlib import contextmanager
from copy import deepcopy
from dataclasses import dataclass
@ -13,19 +14,7 @@ from io import UnsupportedOperation
from itertools import product
from pathlib import Path
from textwrap import dedent
from typing import (
Any,
Callable,
Iterable,
Iterator,
List,
MutableMapping,
NoReturn,
Optional,
TextIO,
Tuple,
Union,
)
from typing import Any, NoReturn, TextIO
import typer
from rich.control import Control
@ -44,8 +33,9 @@ from spiel.state import State
LFLAG = 3
CC = 6
try:
ORIGINAL_TCGETATTR: Optional[List[Any]] = termios.tcgetattr(sys.stdin)
ORIGINAL_TCGETATTR: list[Any] | None = termios.tcgetattr(sys.stdin)
except (UnsupportedOperation, termios.error):
ORIGINAL_TCGETATTR = None
@ -103,10 +93,6 @@ class SpecialCharacters(Enum):
CtrlSpace = "ctrl-space"
Enter = "enter"
@classmethod
def from_character(cls, character: str) -> SpecialCharacters:
return SPECIAL_CHARACTERS[character]
SPECIAL_CHARACTERS = {
"\x1b[A": SpecialCharacters.Up,
@ -132,7 +118,7 @@ SPECIAL_CHARACTERS = {
}
def get_character(stream: TextIO) -> Union[str, SpecialCharacters]:
def get_character(stream: TextIO) -> str | SpecialCharacters:
result = stream.read(1)
if result == "": # this happens when stdin gets closed; equivalent to a quit
@ -144,37 +130,34 @@ def get_character(stream: TextIO) -> Union[str, SpecialCharacters]:
if len(result) != 1 and result[-1] == "1":
result += stream.read(3)
try:
return SpecialCharacters.from_character(result)
except KeyError:
return result
return SPECIAL_CHARACTERS.get(result, result)
Character = Union[str, SpecialCharacters]
InputHandler = Callable[[State], Optional[NoReturn]]
InputHandlerKey = Tuple[Character, Mode]
Character = str | SpecialCharacters
InputHandler = Callable[[State], NoReturn | None]
InputHandlerKey = tuple[Character, Mode]
InputHandlerDecorator = Callable[[InputHandler], InputHandler]
InputHandlers = MutableMapping[InputHandlerKey, InputHandler]
INPUT_HANDLERS: InputHandlers = {} # type: ignore
INPUT_HANDLERS: InputHandlers = {} # type: ignore[assignment]
@dataclass(frozen=True)
class InputHandlerHelpInfo:
name: str
help: str
characters: Tuple[Character, ...]
modes: List[Mode]
characters: tuple[Character, ...]
modes: list[Mode]
INPUT_HANDLER_HELP: List[InputHandlerHelpInfo] = []
INPUT_HANDLER_HELP: list[InputHandlerHelpInfo] = []
def handle_input(
state: State,
stream: TextIO,
handlers: InputHandlers = INPUT_HANDLERS,
) -> Optional[NoReturn]:
) -> NoReturn | None:
character = get_character(stream)
try:
@ -191,9 +174,9 @@ def normalize_help(help: str) -> str:
def input_handler(
*characters: Character,
modes: Optional[Iterable[Mode]] = None,
modes: Iterable[Mode] | None = None,
handlers: InputHandlers = INPUT_HANDLERS,
name: Optional[str] = None,
name: str | None = None,
help: str,
) -> InputHandlerDecorator:
target_modes = list(modes or list(Mode))

@ -5,7 +5,7 @@ import sys
from dataclasses import dataclass
from pathlib import Path
from types import TracebackType
from typing import ContextManager, Optional, Tuple, Type
from typing import ContextManager, Type
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
@ -17,7 +17,7 @@ from spiel.exceptions import NoDeckFound
from spiel.options import Options
def load_deck_and_options(path: Path) -> Tuple[Deck, Options]:
def load_deck_and_options(path: Path) -> tuple[Deck, Options]:
module_name = "__deck"
spec = importlib.util.spec_from_file_location(module_name, path)
@ -28,7 +28,10 @@ def load_deck_and_options(path: Path) -> Tuple[Deck, Options]:
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module) # type: ignore
loader = spec.loader
assert loader is not None
loader.exec_module(module)
try:
deck = getattr(module, DECK)
@ -65,10 +68,10 @@ class DeckWatcher(ContextManager["DeckWatcher"]):
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> Optional[bool]:
exc_type: Type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> bool | None:
self.observer.stop()
self.observer.join()

@ -20,18 +20,20 @@ from spiel.state import State
THIS_DIR = Path(__file__).resolve().parent
app = Typer(
help=dedent(
f"""\
Display richly-styled presentations using your terminal.
Display [italic yellow]Rich[/italic yellow]ly-styled presentations using your terminal.
To see what {PACKAGE_NAME.capitalize()} can do, take a look at the demo deck:
$ spiel demo present
A {PACKAGE_NAME.capitalize()} presentation (a "deck [of slides]") is defined programmatically using a Python script.
A {PACKAGE_NAME.capitalize()} presentation (a "[italic green]deck[/italic green] of slides") is defined programmatically using a Python script.
"""
),
rich_markup_mode="rich",
no_args_is_help=True,
)
@ -194,6 +196,8 @@ demo = Typer(
Use the demonstration deck (present it, display source, etc.).
"""
),
rich_markup_mode="rich",
no_args_is_help=True,
)
DEMO_DIR = THIS_DIR / "demo"

@ -1,6 +1,7 @@
from collections.abc import Mapping
from dataclasses import asdict, dataclass, fields
from pathlib import Path
from typing import Any, Mapping
from typing import Any
import tomli
import tomli_w

@ -1,6 +1,7 @@
import inspect
from collections.abc import Callable, Mapping
from dataclasses import dataclass
from typing import Any, Callable, Dict, Mapping
from typing import Any
from rich.console import ConsoleRenderable
@ -19,7 +20,7 @@ class Presentable: # Why not an ABC? https://github.com/python/mypy/issues/5374
) -> Mapping[str, Any]:
signature = inspect.signature(function)
kwargs: Dict[str, Any] = {}
kwargs: dict[str, Any] = {}
if "triggers" in signature.parameters:
kwargs["triggers"] = triggers

@ -1,5 +1,5 @@
import code
from typing import Callable, MutableMapping
from collections.abc import Callable, MutableMapping
import IPython
from traitlets.config import Config

@ -1,12 +1,14 @@
from __future__ import annotations
from collections import deque
from time import monotonic
from typing import Deque, Optional
from typing import Deque
from spiel.constants import TARGET_RPS
class RPSCounter:
def __init__(self, render_history_length: Optional[int] = None) -> None:
def __init__(self, render_history_length: int | None = None) -> None:
if render_history_length is None:
render_history_length = 3 * TARGET_RPS

@ -1,7 +1,7 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import Callable, Union
from rich.console import ConsoleRenderable
from rich.text import Text
@ -10,7 +10,7 @@ from spiel.presentable import Presentable
from spiel.triggers import Triggers
MakeRenderable = Callable[..., ConsoleRenderable]
RenderableLike = Union[MakeRenderable, ConsoleRenderable]
RenderableLike = MakeRenderable | ConsoleRenderable
@dataclass

@ -1,12 +1,13 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
from tempfile import TemporaryDirectory
from time import monotonic
from types import TracebackType
from typing import Callable, ContextManager, List, Optional, Type, Union
from typing import ContextManager, Type
from rich.console import Console
from rich.style import Style
@ -19,7 +20,7 @@ from spiel.modes import Mode
from spiel.options import Options
from spiel.presentable import Presentable
TextLike = Union[Text, Callable[[], Text]]
TextLike = Text | Callable[[], Text]
@dataclass
@ -30,10 +31,10 @@ class State(ContextManager["State"]):
_current_slide_idx: int = 0
_mode: Mode = Mode.SLIDE
_message: TextLike = Text("")
trigger_times: List[float] = field(default_factory=list)
trigger_times: list[float] = field(default_factory=list)
@classmethod
def from_file(cls, path: Path, console: Optional[Console] = None) -> State:
def from_file(cls, path: Path, console: Console | None = None) -> State:
deck, options = load_deck_and_options(path)
return cls(console=console or Console(), deck=deck, options=options)
@ -115,8 +116,8 @@ class State(ContextManager["State"]):
def __exit__(
self,
exctype: Optional[Type[BaseException]],
excinst: Optional[BaseException],
exctb: Optional[TracebackType],
exctype: Type[BaseException] | None,
excinst: BaseException | None,
exctb: TracebackType | None,
) -> None:
self._tmp_dir.cleanup()

@ -1,12 +1,12 @@
from collections.abc import Iterator
from dataclasses import dataclass, field
from functools import cached_property
from time import monotonic
from typing import Iterator, Tuple
@dataclass(frozen=True)
class Triggers:
times: Tuple[float, ...]
times: tuple[float, ...]
now: float = field(default_factory=monotonic)
def __len__(self) -> int:

@ -1,14 +1,17 @@
from __future__ import annotations
from collections.abc import Iterable, Iterator
from itertools import zip_longest
from typing import Any, Iterable, Iterator, Optional, TypeVar
from typing import TypeVar
T = TypeVar("T")
def filter_join(separator: str, items: Iterable[Optional[Any]]) -> str:
def filter_join(separator: str, items: Iterable[object | None]) -> str:
return separator.join(map(str, filter(None, items)))
def drop_nones(*items: Optional[T]) -> Iterator[T]:
def drop_nones(*items: T | None) -> Iterator[T]:
yield from (item for item in items if item is not None)
@ -20,6 +23,6 @@ def clamp(value: int, lower: int, upper: int) -> int:
return max(min(value, upper), lower)
def chunks(iterable: Iterable[T], n: int, fill_value: Optional[T] = None) -> Iterable[Iterable[T]]:
def chunks(iterable: Iterable[T], n: int, fill_value: T | None = None) -> Iterable[Iterable[T]]:
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fill_value)

@ -1,4 +1,6 @@
from typing import List, Optional, TypeVar
from __future__ import annotations
from typing import TypeVar
import pytest
@ -16,5 +18,5 @@ T = TypeVar("T")
("", 2, None, []),
],
)
def test_chunks(items: List[T], n: int, fill: Optional[T], expected: List[List[T]]) -> None:
def test_chunks(items: list[T], n: int, fill: T | None, expected: list[list[T]]) -> None:
assert [list(chunk) for chunk in chunks(items, n, fill_value=fill)] == expected

@ -1,5 +1,3 @@
from typing import Tuple
import pytest
from hypothesis import given
from hypothesis import strategies as st
@ -8,7 +6,7 @@ from spiel.utils import clamp
@given(st.tuples(st.integers(), st.integers(), st.integers()).filter(lambda x: x[1] <= x[2]))
def test_clamp(value_lower_upper: Tuple[int, int, int]) -> None:
def test_clamp(value_lower_upper: tuple[int, int, int]) -> None:
value, lower, upper = value_lower_upper
clamped = clamp(value, lower, upper)
assert clamped <= upper
@ -16,7 +14,7 @@ def test_clamp(value_lower_upper: Tuple[int, int, int]) -> None:
@given(st.tuples(st.integers(), st.integers(), st.integers()).filter(lambda x: x[1] > x[2]))
def test_clamp_raises_for_bad_bounds(value_lower_upper: Tuple[int, int, int]) -> None:
def test_clamp_raises_for_bad_bounds(value_lower_upper: tuple[int, int, int]) -> None:
value, lower, upper = value_lower_upper
with pytest.raises(ValueError):
clamp(value, lower, upper)

@ -33,4 +33,4 @@ def test_get_function_body() -> None:
def test_get_function_body_raises_on_function_with_no_source() -> None:
with pytest.raises(TypeError):
get_function_body(str) # type: ignore
get_function_body(sorted)

@ -1,5 +1,5 @@
from collections.abc import Callable
from io import StringIO
from typing import Callable
import pytest
from rich.console import Console

@ -1,7 +1,6 @@
import os
import string
from random import sample
from typing import List
import pytest
from hypothesis import given, settings
@ -74,7 +73,7 @@ TESTABLE_INPUT_HANDLERS = list(
@given(input_handlers=st.lists(st.sampled_from(TESTABLE_INPUT_HANDLERS)))
@settings(max_examples=2_000 if os.getenv("CI") else 200)
def test_input_sequences_dont_crash(input_handlers: List[InputHandler]) -> None:
def test_input_sequences_dont_crash(input_handlers: list[InputHandler]) -> None:
state = State(
console=Console(),
deck=Deck(

@ -1,4 +1,6 @@
from typing import Any, Iterable, Optional
from __future__ import annotations
from collections.abc import Iterable
import pytest
@ -15,5 +17,5 @@ from spiel.utils import filter_join
(".", iter(["a", None, "b"]), "a.b"),
],
)
def test_filter_join(joiner: str, items: Iterable[Optional[Any]], expected: str) -> None:
def test_filter_join(joiner: str, items: Iterable[str | None], expected: str) -> None:
assert filter_join(joiner, items) == expected

@ -25,10 +25,10 @@ def test_reloader_triggers_when_file_modified(
file_with_empty_deck.write_text(
dedent(
f"""\
from spiel import Deck
from spiel import Deck
{DECK} = Deck(name="modified")
"""
{DECK} = Deck(name="modified")
"""
)
)

Loading…
Cancel
Save