From 05e5f234c5b95bcce6630f08af428212c7c215a3 Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Sun, 31 Dec 2023 15:41:16 +0400 Subject: [PATCH 1/3] fix: make sure `kivy.core.window` is loaded in `setup_headless` to avoid segmentation fault --- CHANGELOG.md | 27 +++++++++++++---- README.md | 59 +++++++++++++++++++----------------- headless_kivy_pi/__init__.py | 12 +++----- pyproject.toml | 2 +- 4 files changed, 59 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df28583..c3ad049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,19 @@ # Changelog +## Version 0.5.12 + +- fix: make sure `kivy.core.window` is loaded in `setup_headless` to avoid + segmentation fault + +## Version 0.5.11 + +- docs: remove instructions to deal with legacy Raspbian from `README.md` + ## Version 0.5.10 - feat: ensure `setup_headless` is called before instantiating `HeadlessWidget` -- feat: periodically write snapshots of the screen to filesystem in debug mode for remote debugging +- feat: periodically write snapshots of the screen to filesystem in debug mode for + remote debugging - refactor: fix pyright and ruff errors and warnings by faking modules - refactor: split `__init__.py` into several files @@ -87,7 +97,8 @@ ## Version 0.2.1 -- fix: make adafruit-circuitpython-rgb-display conditionally install only on raspberry pi +- fix: make adafruit-circuitpython-rgb-display conditionally install only on raspberry + pi ## Version 0.2.0 @@ -96,17 +107,21 @@ - feat: add typings and docs - docs: add pagination demo - chore: update poetry packages and config -- feat: add activate_low_fps_mode and activate_high_fps_mode to manually set fps when needed +- feat: add activate_low_fps_mode and activate_high_fps_mode to manually set fps + when needed - feat: add logging and improve fps handler - docs: add sdl installation instructions and a reference to demo.py in README.md - feat: drop Kivy FPS to min_fps when the user interface is idle - feat: Add default values for `setup_headless()` based on environment variables - docs: add demo.py to show all the features in action - docs: add README.md -- feat: avoid sending data to the lcd when the current frame is the same as the last one +- feat: avoid sending data to the lcd when the current frame is the same as the last + one - feat: add double buffering -- feat: add synchronous mode to save CPU time by avoiding Kivy from rendering frames when last frame is not yet rendered on the lcd +- feat: add synchronous mode to save CPU time by avoiding Kivy from rendering frames + when last frame is not yet rendered on the lcd ## Version 0.1.0 -- feat: implement Headless widget to render content on SPI display without needing a display server/window manager +- feat: implement Headless widget to render content on SPI display without needing + a display server/window manager diff --git a/README.md b/README.md index 69f4bbb..df13853 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ # Kivy Headless Renderer for Raspberry Pi -This project demonstrates the use of the Kivy framework to create a headless renderer for a Raspberry Pi. The renderer is specifically designed for and tested with the ST7789 SPI display, but it should work with other SPI displays as well. The code utilizes the Adafruit RGB Display library to communicate with the display. The renderer is optimized to not update the LCD if nothing has changed in the frame. +This project uses the Kivy framework to create a headless renderer +for a Raspberry Pi. The renderer is specifically designed for and tested with the +ST7789 SPI display, but it should work with other SPI displays as well. The code +utilizes the Adafruit RGB Display library to communicate with the display. The +renderer is optimized to not update the LCD if nothing has changed in the frame. -## โšก๏ธ Requirements +## ๐Ÿ“‹ Requirements - Raspberry Pi 4 or 5 - SPI Display (tested with ST7789 module) @@ -24,9 +28,12 @@ pip install headless-kivy-pi[dev] poetry --group dev headless-kivy-pi ``` -## ๐Ÿš€ Usage +## ๐Ÿ›  Usage -1. Call setup_headless() before inheriting the `HeadlessWidget` class for the root widget of your application, and provide the optional parameters as needed. For example (these are all default values, you only need to provide the ones you want to change): +1. Call setup_headless() before inheriting the `HeadlessWidget` class for the root + widget of your application, and provide the optional parameters as needed. For + example (these are all default values, you only need to provide the ones you want + to change): ```python setup_headless( @@ -44,7 +51,8 @@ poetry --group dev headless-kivy-pi ) ``` -1. Inherit the `HeadlessWidget` class for the root widget of your Kivy application. For example: +1. Inherit the `HeadlessWidget` class for the root widget of your Kivy application. + For example: ```python class FboFloatLayout(FloatLayout, HeadlessWidget): @@ -89,48 +97,45 @@ The display class to use (default is ST7789). #### `double_buffering` -Is set to `True`, it will let Kivy generate the next frame while sending the last frame to the display. +Is set to `True`, it will let Kivy generate the next frame while sending the last +frame to the display. #### `synchronous_clock` -If set to `True`, Kivy will wait for the LCD before rendering next frames. This will cause Headless to skip frames if they are rendered before the LCD has finished displaying the previous frames. If set to False, frames will be rendered asynchronously, letting Kivy render frames regardless of display being able to catch up or not at the expense of possible frame skipping. +If set to `True`, Kivy will wait for the LCD before rendering next frames. This will +cause Headless to skip frames if they are rendered before the LCD has finished displaying +the previous frames. If set to False, frames will be rendered asynchronously, letting +Kivy render frames regardless of display being able to catch up or not at the expense +of possible frame skipping. #### `automatic_fps` -If set to `True`, it will monitor the hash of the screen data, if this hash changes, it will increase the fps to the maximum and if the hash doesn't change for a while, it will drop the fps to the minimum. +If set to `True`, it will monitor the hash of the screen data, if this hash changes, +it will increase the fps to the maximum and if the hash doesn't change for a while, +it will drop the fps to the minimum. #### `clear_at_exit` If set to `True`, it will clear the screen before exiting. -## โš’๏ธ Contribution +## ๐Ÿค Contributing You need to have [Poetry](https://python-poetry.org/) installed on your machine. -To install poetry in Raspbian you need to follow these instructions to install rust compiler, this is temporary until [this issue](https://github.com/python-poetry/poetry/issues/7645) is resolved: - -```sh -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -sudo apt-get install pkg-config libssl-dev -curl -sSL https://install.python-poetry.org | python3 - -``` - -After having poetry, to install the required dependencies, run the following command: +After having poetry, to install the required dependencies, run the following command +in the root directory of the project: ```sh poetry install ``` -Also be aware of [this issue](https://github.com/python-poetry/poetry/issues/1917) and until it is resolved you can manually disable keyring by prefixing your poetry commands like this: - -```sh -PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring poetry install -``` - ## โš ๏ธ Important Note -This project has only been tested with the ST7789 SPI display module. Other display modules might not be compatible or may require changing the parameters or even modifications to the code. +This project has only been tested with the ST7789 SPI display module. Other display +modules might not be compatible or may require changing the parameters or even modifications +to the code. -## ๐Ÿ“œ License +## ๐Ÿ”’ License -This project is released under the Apache-2.0 License. +This project is released under the Apache-2.0 License. See the [LICENSE](./LICENSE) +file for more details. diff --git a/headless_kivy_pi/__init__.py b/headless_kivy_pi/__init__.py index c915672..79dd002 100644 --- a/headless_kivy_pi/__init__.py +++ b/headless_kivy_pi/__init__.py @@ -100,9 +100,6 @@ def __init__(self: HeadlessWidget, **kwargs: Any) -> None: # noqa: ANN401 ) raise RuntimeError(msg) - if HeadlessWidget.width is None or HeadlessWidget.height is None: - msg = '"setup_headless" should be called before instantiating "Headless"' - raise RuntimeError(msg) if self.debug_mode: self.last_second = int(time.time()) self.rendered_frames = 0 @@ -135,9 +132,9 @@ def __init__(self: HeadlessWidget, **kwargs: Any) -> None: # noqa: ANN401 interval=True, ) self.render_trigger() - App.get_running_app().bind( - on_stop=lambda _: self.render_trigger.cancel(), - ) + app = App.get_running_app() + if app: + app.bind(on_stop=lambda _: self.render_trigger.cancel()) def add_widget( self: HeadlessWidget, @@ -356,6 +353,8 @@ def setup_headless( msg, ) + from kivy.core.window import Window + if IS_RPI: Config.set('graphics', 'window_state', 'hidden') spi = board.SPI() @@ -386,7 +385,6 @@ def setup_headless( ), ) else: - from kivy.core.window import Window from screeninfo import get_monitors monitor = get_monitors()[0] diff --git a/pyproject.toml b/pyproject.toml index 6472243..dfe9f7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "headless-kivy-pi" -version = "0.5.10" +version = "0.5.12" description = "Headless renderer for Kivy framework on Raspberry Pi" authors = ["Sassan Haradji "] license = "Apache-2.0" From 2ac530594d39261ca7461b52f5526220297327cd Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Tue, 20 Feb 2024 02:09:27 +0400 Subject: [PATCH 2/3] refactor: `HeadlessWidget` is not a singleton anymore, config migrated to config module refactor: initializing display and config is now done in `setup_headless_kivy` feat: class method `get_instance` to get the closest parent instance of type `HeadlessWidget` --- .gitignore | 106 +------------- CHANGELOG.md | 7 + README.md | 4 +- headless_kivy_pi/__init__.py | 245 +++++++++---------------------- headless_kivy_pi/config.py | 267 ++++++++++++++++++++++++++++++++++ headless_kivy_pi/constants.py | 9 +- headless_kivy_pi/display.py | 17 +-- headless_kivy_pi/setup.py | 120 --------------- pyproject.toml | 8 +- 9 files changed, 371 insertions(+), 412 deletions(-) create mode 100644 headless_kivy_pi/config.py delete mode 100644 headless_kivy_pi/setup.py diff --git a/.gitignore b/.gitignore index 120fc87..0f9f385 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# Gitignore for the following technologies: Python +.DS_Store # Byte-compiled / optimized / DLL files __pycache__/ @@ -57,69 +57,8 @@ cover/ *.mo *.pot -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - # pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py +.python-version # Environments .env @@ -130,41 +69,8 @@ ENV/ env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ +# pyright +/typings/ -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - -# ruff -.ruff_cache/ - -.DS_Store +# logs +*.log diff --git a/CHANGELOG.md b/CHANGELOG.md index c3ad049..4b2642e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Version 0.6.0 + +- refactor: `HeadlessWidget` is not a singleton anymore, config migrated to config + module +- refactor: initializing display and config is now done in `setup_headless_kivy` +- feat: class method `get_instance` to get the closest parent instance of type `HeadlessWidget` + ## Version 0.5.12 - fix: make sure `kivy.core.window` is loaded in `setup_headless` to avoid diff --git a/README.md b/README.md index df13853..8444a1e 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ poetry --group dev headless-kivy-pi width=240, height=240, baudrate=60000000, - debug_mode=False, + is_debug_mode=False, display_class=ST7789, double_buffering=True, synchronous_clock=True, @@ -87,7 +87,7 @@ The height of the display in pixels. The baud rate for the display connection. -#### `debug_mode` +#### `is_debug_mode` If set to True, the application will print debug information, including FPS. diff --git a/headless_kivy_pi/__init__.py b/headless_kivy_pi/__init__.py index 79dd002..61de737 100644 --- a/headless_kivy_pi/__init__.py +++ b/headless_kivy_pi/__init__.py @@ -14,70 +14,42 @@ """ from __future__ import annotations -import atexit import time from pathlib import Path from queue import Empty, Queue from threading import Thread -from typing import TYPE_CHECKING, ClassVar +from typing import TYPE_CHECKING -import kivy import numpy as np -from kivy.app import App, ObjectProperty, Widget +from kivy.app import App from kivy.clock import Clock -from kivy.config import Config -from kivy.graphics import ( - Canvas, - ClearBuffers, - ClearColor, - Color, - Fbo, - Rectangle, -) +from kivy.graphics.context_instructions import Color +from kivy.graphics.fbo import Fbo +from kivy.graphics.gl_instructions import ClearBuffers, ClearColor +from kivy.graphics.instructions import Canvas +from kivy.graphics.vertex_instructions import Rectangle +from kivy.properties import ObjectProperty +from kivy.uix.widget import Widget from typing_extensions import Any -from headless_kivy_pi.constants import ( - BAUDRATE, - BITS_PER_BYTE, - BYTES_PER_PIXEL, - CLEAR_AT_EXIT, - IS_RPI, -) +from headless_kivy_pi import config +from headless_kivy_pi.constants import IS_TEST_ENVIRONMENT from headless_kivy_pi.display import transfer_to_display from headless_kivy_pi.logger import logger -from headless_kivy_pi.setup import SetupHeadlessConfig, setup_kivy if TYPE_CHECKING: from adafruit_rgb_display.rgb import DisplaySPI from kivy.graphics.texture import Texture -import board -import digitalio -from adafruit_rgb_display.st7789 import ST7789 - -kivy.require('2.2.1') - class HeadlessWidget(Widget): """Headless Kivy widget class rendering on SPI connected display.""" - instance: ClassVar[HeadlessWidget | None] = None - - _is_setup_headless_called: ClassVar = False - should_ignore_hash: ClassVar = False + _is_setup_headless_called: bool = False + should_ignore_hash: bool = False texture = ObjectProperty(None, allownone=True) _display: DisplaySPI - min_fps: int - max_fps: int - is_paused: bool - width: int - height: int - debug_mode: bool - double_buffering: bool - synchronous_clock: bool - automatic_fps_control: bool - last_second: int rendered_frames: int skipped_frames: int @@ -88,27 +60,24 @@ class HeadlessWidget(Widget): fbo: Fbo fbo_rect: Rectangle - def __init__(self: HeadlessWidget, **kwargs: Any) -> None: # noqa: ANN401 + def __init__(self: HeadlessWidget, **kwargs: dict[str, object]) -> None: """Initialize a `HeadlessWidget`.""" - if HeadlessWidget.instance: - msg = 'Only one instantiation of `HeadlessWidget` is possible' - raise RuntimeError(msg) + if not IS_TEST_ENVIRONMENT: + config.check_initialized() + + self.should_ignore_hash = False - if not HeadlessWidget._is_setup_headless_called: - msg = ( - 'You need to run `setup_headless` before instantiating `HeadlessWidget`' - ) - raise RuntimeError(msg) + __import__('kivy.core.window') - if self.debug_mode: + if config.is_debug_mode(): self.last_second = int(time.time()) self.rendered_frames = 0 self.skipped_frames = 0 - self.pending_render_threads = Queue(2 if HeadlessWidget.double_buffering else 1) + self.pending_render_threads = Queue(2 if config.double_buffering() else 1) self.last_hash = 0 self.last_change = time.time() - self.fps = self.max_fps + self.fps = config.max_fps() self.canvas = Canvas() with self.canvas: @@ -124,8 +93,6 @@ def __init__(self: HeadlessWidget, **kwargs: Any) -> None: # noqa: ANN401 super().__init__(**kwargs) - HeadlessWidget.instance = self - self.render_trigger = Clock.create_trigger( lambda _: self.render_on_display(), 1 / self.fps, @@ -133,8 +100,12 @@ def __init__(self: HeadlessWidget, **kwargs: Any) -> None: # noqa: ANN401 ) self.render_trigger() app = App.get_running_app() + + def clear(*_: object) -> None: + self.render_trigger.cancel() + if app: - app.bind(on_stop=lambda _: self.render_trigger.cancel()) + app.bind(on_stop=clear) def add_widget( self: HeadlessWidget, @@ -184,62 +155,44 @@ def on_alpha(self: HeadlessWidget, _: HeadlessWidget, value: float) -> None: """Update alpha value of `fbo_rect` when widget's alpha value changes.""" self.fbo_color.rgba = (1, 1, 1, value) - @classmethod - def pause(cls: type[HeadlessWidget]) -> None: - """Pause writing to the display.""" - HeadlessWidget.is_paused = True - - @classmethod - def resume(cls: type[HeadlessWidget]) -> None: - """Resume writing to the display.""" - cls.is_paused = False - cls.should_ignore_hash = True - - @classmethod - def render(cls: type[HeadlessWidget]) -> None: + def render(self: HeadlessWidget) -> None: """Schedule a force render.""" - if not cls.instance: + if not self: return - Clock.schedule_once(cls.instance.render_on_display, 0) + Clock.schedule_once(self.render_on_display, 0) - @classmethod - def _activate_high_fps_mode(cls: type[HeadlessWidget]) -> None: + def _activate_high_fps_mode(self: HeadlessWidget) -> None: """Increase fps to `max_fps`.""" - if not cls.instance: + if not self: return logger.info('Activating high fps mode, setting FPS to `max_fps`') - cls.instance.fps = cls.max_fps - cls.instance.render_trigger.timeout = 1.0 / cls.instance.fps - cls.instance.last_hash = 0 + self.fps = config.max_fps() + self.render_trigger.timeout = 1.0 / self.fps + self.last_hash = 0 - @classmethod - def activate_high_fps_mode(cls: type[HeadlessWidget]) -> None: + def activate_high_fps_mode(self: HeadlessWidget) -> None: """Schedule increasing fps to `max_fps`.""" - cls.render() - Clock.schedule_once(lambda _: cls._activate_high_fps_mode(), 0) + self.render() + Clock.schedule_once(lambda _: self._activate_high_fps_mode(), 0) - @classmethod - def _activate_low_fps_mode(cls: type[HeadlessWidget]) -> None: + def _activate_low_fps_mode(self: HeadlessWidget) -> None: """Drop fps to `min_fps`.""" - if not cls.instance: - return logger.info('Activating low fps mode, dropping FPS to `min_fps`') - cls.instance.fps = cls.min_fps - cls.instance.render_trigger.timeout = 1.0 / cls.instance.fps + self.fps = config.min_fps() + self.render_trigger.timeout = 1.0 / self.fps - @classmethod - def activate_low_fps_mode(cls: type[HeadlessWidget]) -> None: + def activate_low_fps_mode(self: HeadlessWidget) -> None: """Schedule dropping fps to `min_fps`.""" - cls.render() - Clock.schedule_once(lambda _: cls._activate_low_fps_mode(), 0) + self.render() + Clock.schedule_once(lambda _: self._activate_low_fps_mode(), 0) - def render_on_display(self: HeadlessWidget, *_: Any) -> None: # noqa: ANN401 + def render_on_display(self: HeadlessWidget, *_: object) -> None: """Render the widget on display connected to the SPI controller.""" - if HeadlessWidget.is_paused: + if config.is_paused(): return # Log the number of skipped and rendered frames in the last second - if self.debug_mode: + if config.is_debug_mode(): # Increment rendered_frames/skipped_frames count every frame and reset their # values to zero every second. current_second = int(time.time()) @@ -256,22 +209,20 @@ def render_on_display(self: HeadlessWidget, *_: Any) -> None: # noqa: ANN401 # If `synchronous_clock` is False, skip frames if there are more than one # pending render in case `double_buffering` is enabled, or if there are ANY # pending render in case `double_buffering` is disabled. - if ( - not HeadlessWidget.synchronous_clock - and self.pending_render_threads.qsize() - > (1 if HeadlessWidget.double_buffering else 0) + if not config.synchronous_clock() and self.pending_render_threads.qsize() > ( + 1 if config.double_buffering() else 0 ): self.skipped_frames += 1 return data = np.frombuffer(self.texture.pixels, dtype=np.uint8) data_hash = hash(data.data.tobytes()) - if data_hash == self.last_hash and not HeadlessWidget.should_ignore_hash: + if data_hash == self.last_hash and not self.should_ignore_hash: # Only drop FPS when the screen has not changed for at least one second if ( - self.automatic_fps_control + config.automatic_fps() and time.time() - self.last_change > 1 - and self.fps != self.min_fps + and self.fps != config.min_fps() ): logger.debug('Frame content has not changed for 1 second') self.activate_low_fps_mode() @@ -279,14 +230,14 @@ def render_on_display(self: HeadlessWidget, *_: Any) -> None: # noqa: ANN401 # Considering the content has not changed, this frame can safely be ignored return - if self.debug_mode: + if config.is_debug_mode(): self.rendered_frames += 1 with Path('headless_kivy_pi_buffer.raw').open('wb') as snapshot_file: snapshot_file.write( bytes( data.reshape( - HeadlessWidget.width, - HeadlessWidget.height, + config.width(), + config.height(), -1, )[::-1, :, :3] .flatten() @@ -294,11 +245,11 @@ def render_on_display(self: HeadlessWidget, *_: Any) -> None: # noqa: ANN401 ), ) - HeadlessWidget.should_ignore_hash = False + self.should_ignore_hash = False self.last_change = time.time() self.last_hash = data_hash - if self.automatic_fps_control and self.fps != self.max_fps: + if config.automatic_fps and self.fps != config.max_fps(): logger.debug('Frame content has changed') self.activate_high_fps_mode() @@ -314,82 +265,22 @@ def render_on_display(self: HeadlessWidget, *_: Any) -> None: # noqa: ANN401 data_hash, last_thread, ), + daemon=True, ) self.pending_render_threads.put(thread) thread.start() @classmethod - def setup_headless( + def get_instance( cls: type[HeadlessWidget], - config: SetupHeadlessConfig | None = None, - ) -> None: - """Set up `HeadlessWidget`.""" - if config is None: - config = {} - - setup_kivy(config) - baudrate = config.get('baudrate', BAUDRATE) - display_class: DisplaySPI = config.get('st7789', ST7789) - clear_at_exit = config.get('clear_at_exit', CLEAR_AT_EXIT) - - if HeadlessWidget.min_fps > HeadlessWidget.max_fps: - msg = f"""Invalid value "{HeadlessWidget.min_fps}" for "min_fps", it can't \ -be higher than 'max_fps' which is set to '{HeadlessWidget.max_fps}'.""" - raise ValueError(msg) - - fps_cap = baudrate / ( - HeadlessWidget.width - * HeadlessWidget.height - * BYTES_PER_PIXEL - * BITS_PER_BYTE - ) - - if HeadlessWidget.max_fps > fps_cap: - msg = f"""Invalid value "{HeadlessWidget.max_fps}" for "max_fps", it can't \ -be higher than "{fps_cap:.1f}" (baudrate={baudrate} รท (width={HeadlessWidget.width} x \ -height={HeadlessWidget.height} x bytes per pixel={BYTES_PER_PIXEL} x bits per byte=\ -{BITS_PER_BYTE}))""" - raise ValueError( - msg, - ) - - from kivy.core.window import Window - - if IS_RPI: - Config.set('graphics', 'window_state', 'hidden') - spi = board.SPI() - # Configuration for CS and DC pins (these are PiTFT defaults): - cs_pin = digitalio.DigitalInOut(board.CE0) - dc_pin = digitalio.DigitalInOut(board.D25) - reset_pin = digitalio.DigitalInOut(board.D24) - display = display_class( - spi, - height=HeadlessWidget.height, - width=HeadlessWidget.width, - y_offset=80, - x_offset=0, - cs=cs_pin, - dc=dc_pin, - rst=reset_pin, - baudrate=baudrate, - ) - HeadlessWidget._display = display - if clear_at_exit: - atexit.register( - lambda: display._block( # noqa: SLF001 - 0, - 0, - HeadlessWidget.width - 1, - HeadlessWidget.height - 1, - bytes(HeadlessWidget.width * HeadlessWidget.height * 2), - ), - ) - else: - from screeninfo import get_monitors + widget: Widget, + ) -> HeadlessWidget | None: + """Get the nearest instance of `HeadlessWidget`.""" + if isinstance(widget, HeadlessWidget): + return widget + if widget.parent: + return cls.get_instance(widget.parent) + return None - monitor = get_monitors()[0] - Window._win.set_always_on_top(True) # noqa: SLF001, FBT003 - Window._set_top(200) # noqa: SLF001 - Window._set_left(monitor.width - Window._size[0]) # noqa: SLF001 - HeadlessWidget._is_setup_headless_called = True +__all__ = ['HeadlessWidget'] diff --git a/headless_kivy_pi/config.py b/headless_kivy_pi/config.py new file mode 100644 index 0000000..12e966b --- /dev/null +++ b/headless_kivy_pi/config.py @@ -0,0 +1,267 @@ +# pyright: reportMissingImports=false +"""Implement `setup_kivy`, it configures Kivy.""" +from __future__ import annotations + +import atexit +import sys +from functools import cache +from typing import TYPE_CHECKING, NoReturn, NotRequired, TypedDict + +import kivy +from kivy.config import Config + +from headless_kivy_pi.constants import ( + AUTOMATIC_FPS, + BAUDRATE, + BITS_PER_BYTE, + BYTES_PER_PIXEL, + CLEAR_AT_EXIT, + DOUBLE_BUFFERING, + HEIGHT, + IS_DEBUG_MODE, + IS_RPI, + IS_TEST_ENVIRONMENT, + MAX_FPS, + MIN_FPS, + SYNCHRONOUS_CLOCK, + WIDTH, +) +from headless_kivy_pi.fake import Fake +from headless_kivy_pi.logger import add_file_handler, add_stdout_handler + +if not IS_RPI: + sys.modules['board'] = Fake() + sys.modules['digitalio'] = Fake() + sys.modules['adafruit_rgb_display.st7789'] = Fake() + +import board +import digitalio +from adafruit_rgb_display.st7789 import ST7789 + +kivy.require('2.3.0') + +if TYPE_CHECKING: + from adafruit_rgb_display.rgb import DisplaySPI + + +class SetupHeadlessConfig(TypedDict): + """Arguments of `setup_headless_kivy` function.""" + + """Minimum frames per second for when the Kivy application is idle.""" + min_fps: NotRequired[int] + """Maximum frames per second for the Kivy application.""" + max_fps: NotRequired[int] + """The width of the display in pixels.""" + width: NotRequired[int] + """The height of the display in pixels.""" + height: NotRequired[int] + """The baud rate for the display connection.""" + baudrate: NotRequired[int] + """If set to True, the application will consume computational resources to log + additional debug information.""" + is_debug_mode: NotRequired[bool] + """The display class to use (default is ST7789).""" + display_class: NotRequired[type[DisplaySPI]] + """Is set to `True`, it will let Kivy generate the next frame while sending the + last frame to the display.""" + double_buffering: NotRequired[bool] + """If set to `True`, Kivy will wait for the LCD before rendering next frames. This + will cause Headless to skip frames if they are rendered before the LCD has finished + displaying the previous frames. If set to False, frames will be rendered + asynchronously, letting Kivy render frames regardless of display being able to catch + up or not at the expense of possible frame skipping.""" + synchronous_clock: NotRequired[bool] + """If set to `True`, it will monitor the hash of the screen data, if this hash + changes, it will increase the fps to the maximum and if the hash doesn't change for + a while, it will drop the fps to the minimum.""" + automatic_fps: NotRequired[bool] + """If set to `True`, it will clear the screen before exiting.""" + clear_at_eixt: NotRequired[bool] + + +_config: SetupHeadlessConfig | None = None +_display: DisplaySPI | None = None + + +def report_uninitialized() -> NoReturn: + """Report that the module has not been initialized.""" + msg = """You need to run `setup_headless_kivy` before importing \ +`kivy.core.window` module. \ +Note that it might have been imported by another module unintentionally.""" + raise RuntimeError(msg) + + +def setup_headless_kivy(config: SetupHeadlessConfig) -> None: + """Configure the headless mode for the Kivy application. + + Arguments: + --------- + config: `SetupHeadlessConfig` + + """ + global _config, _display # noqa: PLW0603 + _config = config + + if is_debug_mode(): + add_stdout_handler() + add_file_handler() + + Config.set('kivy', 'kivy_clock', 'default') + Config.set('graphics', 'fbo', 'force-hardware') + Config.set('graphics', 'fullscreen', '0') + Config.set('graphics', 'maxfps', f'{max_fps()}') + Config.set('graphics', 'multisamples', '1') + Config.set('graphics', 'resizable', '0') + Config.set('graphics', 'vsync', '0') + Config.set('graphics', 'width', f'{width()}') + Config.set('graphics', 'height', f'{height()}') + + baudrate = config.get('baudrate', BAUDRATE) + display_class: DisplaySPI = config.get('st7789', ST7789) + clear_at_exit = config.get('clear_at_exit', CLEAR_AT_EXIT) + + if min_fps() > max_fps(): + msg = f"""Invalid value "{min_fps()}" for "min_fps", it can't \ +be higher than 'max_fps' which is set to '{max_fps()}'.""" + raise ValueError(msg) + + fps_cap = baudrate / (width() * height() * BYTES_PER_PIXEL * BITS_PER_BYTE) + + if max_fps() > fps_cap: + msg = f"""Invalid value "{max_fps()}" for "max_fps", it can't \ +be higher than "{fps_cap:.1f}" (baudrate={baudrate} รท (width={width()} x \ +height={height()} x bytes per pixel={BYTES_PER_PIXEL} x bits per byte=\ +{BITS_PER_BYTE}))""" + raise ValueError(msg) + + if IS_TEST_ENVIRONMENT: + Config.set('graphics', 'window_state', 'hidden') + from kivy.core.window import Window + elif IS_RPI: + Config.set('graphics', 'window_state', 'hidden') + spi = board.SPI() + # Configuration for CS and DC pins (these are PiTFT defaults): + cs_pin = digitalio.DigitalInOut(board.CE0) + dc_pin = digitalio.DigitalInOut(board.D25) + reset_pin = digitalio.DigitalInOut(board.D24) + _display = display_class( + spi, + height=height(), + width=width(), + y_offset=80, + x_offset=0, + cs=cs_pin, + dc=dc_pin, + rst=reset_pin, + baudrate=baudrate, + ) + if clear_at_exit: + atexit.register( + lambda: _display + and _display._block( # noqa: SLF001 + 0, + 0, + width() - 1, + height() - 1, + bytes(width() * height() * 2), + ), + ) + else: + from kivy.core.window import Window + from screeninfo import get_monitors + + monitor = get_monitors()[0] + + Window._win.set_always_on_top(True) # noqa: SLF001 + Window._set_top(200) # noqa: SLF001 + Window._set_left(monitor.width - Window._size[0]) # noqa: SLF001 + + +def check_initialized() -> None: + """Check if the module has been initialized.""" + if not _config: + report_uninitialized() + + +@cache +def min_fps() -> int: + """Return the minimum frames per second for when the Kivy application is idle.""" + if _config: + return _config.get('min_fps', MIN_FPS) + report_uninitialized() + + +@cache +def max_fps() -> int: + """Return the maximum frames per second for the Kivy application.""" + if _config: + return _config.get('max_fps', MAX_FPS) + report_uninitialized() + + +@cache +def width() -> int: + """Return the width of the display in pixels.""" + if _config: + return _config.get('width', WIDTH) + report_uninitialized() + + +@cache +def height() -> int: + """Return the height of the display in pixels.""" + if _config: + return _config.get('height', HEIGHT) + report_uninitialized() + + +@cache +def is_debug_mode() -> bool: + """Return `True` if the application will consume computational resources to log.""" + if _config: + return _config.get('is_debug_mode', IS_DEBUG_MODE) + report_uninitialized() + + +@cache +def double_buffering() -> bool: + """Generate the next frame while sending the last frame to the display.""" + if _config: + return _config.get('double_buffering', DOUBLE_BUFFERING) + report_uninitialized() + + +@cache +def synchronous_clock() -> bool: + """headless-kivy-pi will wait for the LCD before rendering next frames.""" + if _config: + return _config.get('synchronous_clock', SYNCHRONOUS_CLOCK) + report_uninitialized() + + +@cache +def automatic_fps() -> bool: + """headless-kivy-pi adjusts the FPS automatically.""" + if _config: + return _config.get('automatic_fps', AUTOMATIC_FPS) + report_uninitialized() + + +_is_paused: bool = False + + +def is_paused() -> bool: + """Return `True` if rendering the application is paused.""" + return _is_paused + + +def pause() -> None: + """Pause rendering the application.""" + global _is_paused # noqa: PLW0603 + _is_paused = True + + +def resume() -> None: + """Resume rendering the application.""" + global _is_paused # noqa: PLW0603 + _is_paused = False diff --git a/headless_kivy_pi/constants.py b/headless_kivy_pi/constants.py index f20a44e..5da51b4 100644 --- a/headless_kivy_pi/constants.py +++ b/headless_kivy_pi/constants.py @@ -12,12 +12,15 @@ # Configure the headless mode for the Kivy application and initialize the display +IS_TEST_ENVIRONMENT = ( + strtobool(os.environ.get('HEADLESS_KIVY_PI_TEST_ENVIRONMENT', 'False')) == 1 +) MIN_FPS = int(os.environ.get('HEADLESS_KIVY_PI_MIN_FPS', '1')) MAX_FPS = int(os.environ.get('HEADLESS_KIVY_PI_MAX_FPS', '32')) WIDTH = int(os.environ.get('HEADLESS_KIVY_PI_WIDTH', '240')) HEIGHT = int(os.environ.get('HEADLESS_KIVY_PI_HEIGHT', '240')) BAUDRATE = int(os.environ.get('HEADLESS_KIVY_PI_BAUDRATE', '60000000')) -DEBUG_MODE = ( +IS_DEBUG_MODE = ( strtobool( os.environ.get('HEADLESS_KIVY_PI_DEBUG', 'False' if IS_RPI else 'True'), ) @@ -35,9 +38,9 @@ ) == 1 ) -AUTOMATIC_FPS_CONTROL = ( +AUTOMATIC_FPS = ( strtobool( - os.environ.get('HEADLESS_KIVY_PI_AUTOMATIC_FPS_CONTROL', 'True'), + os.environ.get('HEADLESS_KIVY_PI_AUTOMATIC_FPS', 'True'), ) == 1 ) diff --git a/headless_kivy_pi/display.py b/headless_kivy_pi/display.py index aaa3224..78e810a 100644 --- a/headless_kivy_pi/display.py +++ b/headless_kivy_pi/display.py @@ -5,7 +5,7 @@ import numpy as np -from headless_kivy_pi.constants import IS_RPI +from headless_kivy_pi import config from headless_kivy_pi.logger import logger if TYPE_CHECKING: @@ -20,14 +20,12 @@ def transfer_to_display( last_render_thread: Thread, ) -> None: """Transfer data to the display via SPI controller.""" - from headless_kivy_pi import HeadlessWidget - logger.debug(f'Rendering frame with hash "{data_hash}"') # Flip the image vertically data = data.reshape( - HeadlessWidget.width, - HeadlessWidget.height, + config.width(), + config.height(), -1, )[::-1, :, :3].astype(np.uint16) @@ -45,11 +43,12 @@ def transfer_to_display( last_render_thread.join() # Only render when running on a Raspberry Pi - if IS_RPI: - HeadlessWidget._display._block( # noqa: SLF001 + display = config._display # noqa: SLF001 + if display: + display._block( # noqa: SLF001 0, 0, - HeadlessWidget.width - 1, - HeadlessWidget.height - 1, + config.width() - 1, + config.height() - 1, data_bytes, ) diff --git a/headless_kivy_pi/setup.py b/headless_kivy_pi/setup.py deleted file mode 100644 index d74b727..0000000 --- a/headless_kivy_pi/setup.py +++ /dev/null @@ -1,120 +0,0 @@ -# pyright: reportMissingImports=false -"""Implement `setup_kivy`, it configures Kivy.""" -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, NotRequired, TypedDict - -from kivy import Config - -from headless_kivy_pi.constants import ( - AUTOMATIC_FPS_CONTROL, - DEBUG_MODE, - DOUBLE_BUFFERING, - HEIGHT, - IS_RPI, - MAX_FPS, - MIN_FPS, - SYNCHRONOUS_CLOCK, - WIDTH, -) -from headless_kivy_pi.fake import Fake -from headless_kivy_pi.logger import add_file_handler, add_stdout_handler - -if TYPE_CHECKING: - from adafruit_rgb_display.rgb import DisplaySPI - -if not IS_RPI: - sys.modules['board'] = Fake() - sys.modules['digitalio'] = Fake() - sys.modules['adafruit_rgb_display.st7789'] = Fake() - - -class SetupHeadlessConfig(TypedDict): - """Arguments of `setup_headless` function.""" - - """Minimum frames per second for when the Kivy application is idle.""" - min_fps: NotRequired[int] - """Maximum frames per second for the Kivy application.""" - max_fps: NotRequired[int] - """The width of the display in pixels.""" - width: NotRequired[int] - """The height of the display in pixels.""" - height: NotRequired[int] - """The baud rate for the display connection.""" - baudrate: NotRequired[int] - """If set to True, the application will consume computational resources to log - additional debug information.""" - debug_mode: NotRequired[bool] - """The display class to use (default is ST7789).""" - display_class: NotRequired[type[DisplaySPI]] - """Is set to `True`, it will let Kivy generate the next frame while sending the - last frame to the display.""" - double_buffering: NotRequired[bool] - """If set to `True`, Kivy will wait for the LCD before rendering next frames. This - will cause Headless to skip frames if they are rendered before the LCD has finished - displaying the previous frames. If set to False, frames will be rendered - asynchronously, letting Kivy render frames regardless of display being able to catch - up or not at the expense of possible frame skipping.""" - synchronous_clock: NotRequired[bool] - """If set to `True`, it will monitor the hash of the screen data, if this hash - changes, it will increase the fps to the maximum and if the hash doesn't change for - a while, it will drop the fps to the minimum.""" - automatic_fps: NotRequired[bool] - """If set to `True`, it will clear the screen before exiting.""" - clear_at_eixt: NotRequired[bool] - - -def setup_kivy(config: SetupHeadlessConfig) -> None: - """Configure the headless mode for the Kivy application. - - Arguments: - --------- - config: `SetupHeadlessConfig`, optional - """ - from headless_kivy_pi import HeadlessWidget - - if 'kivy.core.window' in sys.modules: - msg = """You need to run `setup_headless` before importing \ -`kivy.core.window` module. \ -Note that it might have been imported by another module unintentionally.""" - raise RuntimeError(msg) - - min_fps = config.get('min_fps', MIN_FPS) - max_fps = config.get('max_fps', MAX_FPS) - width = config.get('width', WIDTH) - height = config.get('height', HEIGHT) - HeadlessWidget.debug_mode = config.get('debug_mode', DEBUG_MODE) - HeadlessWidget.double_buffering = config.get( - 'double_buffering', - DOUBLE_BUFFERING, - ) - HeadlessWidget.synchronous_clock = config.get( - 'synchronous_clock', - SYNCHRONOUS_CLOCK, - ) - HeadlessWidget.automatic_fps_control = config.get( - 'automatic_fps', - AUTOMATIC_FPS_CONTROL, - ) - - if HeadlessWidget.debug_mode: - add_stdout_handler() - add_file_handler() - - HeadlessWidget.min_fps = min_fps - HeadlessWidget.max_fps = max_fps - HeadlessWidget.width = width - HeadlessWidget.height = height - - HeadlessWidget.is_paused = False - - Config.set('kivy', 'kivy_clock', 'default') - Config.set('graphics', 'fbo', 'force-hardware') - Config.set('graphics', 'fullscreen', '0') - Config.set('graphics', 'maxfps', f'{max_fps}') - Config.set('graphics', 'multisamples', '1') - Config.set('graphics', 'resizable', '0') - Config.set('graphics', 'vsync', '0') - Config.set('graphics', 'width', f'{width}') - Config.set('graphics', 'height', f'{height}') diff --git a/pyproject.toml b/pyproject.toml index dfe9f7f..9f27852 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "headless-kivy-pi" -version = "0.5.12" +version = "0.6.0" description = "Headless renderer for Kivy framework on Raspberry Pi" authors = ["Sassan Haradji "] license = "Apache-2.0" @@ -49,3 +49,9 @@ multiline-quotes = "double" [tool.ruff.format] quote-style = 'single' + +[tool.isort] +profile = "black" + +[tool.pyright] +exclude = ['typings'] From 8cbabe0b5735549a417e1cf43fb3f194202dcb6c Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Fri, 8 Mar 2024 17:13:42 +0400 Subject: [PATCH 3/3] chore: GitHub workflow to publish pushes on `main` branch to PyPI --- .github/workflows/integration_delivery.yml | 216 ++++++++++ .github/workflows/wheels.yml | 47 --- CHANGELOG.md | 5 + headless_kivy_pi/__init__.py | 11 +- headless_kivy_pi/config.py | 2 +- headless_kivy_pi/fake.py | 8 +- poetry.lock | 447 ++++++++++++++------- pyproject.toml | 31 +- 8 files changed, 549 insertions(+), 218 deletions(-) create mode 100644 .github/workflows/integration_delivery.yml delete mode 100644 .github/workflows/wheels.yml diff --git a/.github/workflows/integration_delivery.yml b/.github/workflows/integration_delivery.yml new file mode 100644 index 0000000..7690771 --- /dev/null +++ b/.github/workflows/integration_delivery.yml @@ -0,0 +1,216 @@ +name: CI/CD + +on: + push: + pull_request: + workflow_dispatch: + +env: + PYTHON_VERSION: '3.11' + +jobs: + dependencies: + name: Install Dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout + + - name: Save Cached Poetry + id: cached-poetry + uses: actions/cache@v4 + with: + path: | + ~/.cache + ~/.local + key: poetry-${{ hashFiles('poetry.lock') }} + + - uses: actions/setup-python@v5 + name: Setup Python + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: x64 + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + + - name: Install dependencies + run: poetry install --with dev --extras=dev + + type-check: + name: Type Check + needs: + - dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout + + - uses: actions/setup-python@v5 + name: Setup Python + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: x64 + + - name: Load Cached Poetry + id: cached-poetry + uses: actions/cache/restore@v4 + with: + path: | + ~/.cache + ~/.local + key: poetry-${{ hashFiles('poetry.lock') }} + + - name: Create stub files + run: poetry run pyright --createstub kivy + + - name: Type Check + run: poetry run poe typecheck + + lint: + name: Lint + needs: + - dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout + + - uses: actions/setup-python@v5 + name: Setup Python + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: x64 + + - name: Load Cached Poetry + id: cached-poetry + uses: actions/cache/restore@v4 + with: + path: | + ~/.cache + ~/.local + key: poetry-${{ hashFiles('poetry.lock') }} + + - name: Lint + run: poetry run poe lint + + build: + name: Build + needs: + - dependencies + runs-on: ubuntu-latest + outputs: + version: ${{ steps.extract_version.outputs.version }} + name: ${{ steps.extract_version.outputs.name }} + steps: + - uses: actions/checkout@v4 + name: Checkout + + - uses: actions/setup-python@v5 + name: Setup Python + with: + python-version: ${{ env.PYTHON_VERSION }} + architecture: x64 + + - name: Load Cached Poetry + id: cached-poetry + uses: actions/cache/restore@v4 + with: + path: | + ~/.cache + ~/.local + key: poetry-${{ hashFiles('poetry.lock') }} + + - name: Build + run: poetry build + + - name: Extract Version + id: extract_version + run: | + echo "version=$(poetry version --short)" >> "$GITHUB_OUTPUT" + echo "name=$(poetry version | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + + - name: Upload wheel + uses: actions/upload-artifact@v4 + with: + name: wheel + path: dist/*.whl + if-no-files-found: error + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: binary + path: dist/*.tar.gz + if-no-files-found: error + + pypi-publish: + name: Publish to PyPI + if: >- + github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + needs: + - type-check + - lint + - build + runs-on: ubuntu-latest + environment: + name: release + url: https://pypi.org/p/${{ needs.build.outputs.name }} + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: wheel + path: dist + + - uses: actions/download-artifact@v4 + with: + name: binary + path: dist + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist + verbose: true + + release: + name: Release + needs: + - type-check + - lint + - build + - pypi-publish + environment: + name: release + url: https://pypi.org/p/${{ needs.build.outputs.name }} + runs-on: ubuntu-latest + permissions: + contents: write + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - name: Procure Wheel + uses: actions/download-artifact@v4 + with: + name: wheel + path: artifacts + + - name: Procure Binary + uses: actions/download-artifact@v4 + with: + name: binary + path: artifacts + + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: artifacts/* + tag_name: v${{ needs.build.outputs.version }} + body: | + Release of version ${{ needs.build.outputs.version }} + PyPI package: https://pypi.org/project/${{ needs.build.outputs.name }}/${{ needs.build.outputs.version }} + prerelease: false + draft: false diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml deleted file mode 100644 index da26bee..0000000 --- a/.github/workflows/wheels.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: Build - -on: [push, pull_request] - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - build_wheels: - name: Build wheels on ${{ matrix.os }} - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - virtualenvs-create: true - virtualenvs-in-project: true - installer-parallel: true - - - name: Load cached venv - id: cached-poetry-dependencies - uses: actions/cache@v3 - with: - path: .venv - key: - venv-${{ runner.os }}-${{ - steps.setup-python.outputs.python-version}}-${{ - hashFiles('**/poetry.lock') }} - - - name: Build - run: poetry build - - - name: Upload - uses: actions/upload-artifact@v3 - with: - path: ./dist/*.whl diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b2642e..ac22d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 0.6.1 + +- chore: GitHub workflow to publish pushes on `main` branch to PyPI +- chore: create GitHub release for main branch in GitHub workflows + ## Version 0.6.0 - refactor: `HeadlessWidget` is not a singleton anymore, config migrated to config diff --git a/headless_kivy_pi/__init__.py b/headless_kivy_pi/__init__.py index 61de737..1c3fb75 100644 --- a/headless_kivy_pi/__init__.py +++ b/headless_kivy_pi/__init__.py @@ -30,7 +30,6 @@ from kivy.graphics.vertex_instructions import Rectangle from kivy.properties import ObjectProperty from kivy.uix.widget import Widget -from typing_extensions import Any from headless_kivy_pi import config from headless_kivy_pi.constants import IS_TEST_ENVIRONMENT @@ -94,7 +93,7 @@ def __init__(self: HeadlessWidget, **kwargs: dict[str, object]) -> None: super().__init__(**kwargs) self.render_trigger = Clock.create_trigger( - lambda _: self.render_on_display(), + self.render_on_display, 1 / self.fps, interval=True, ) @@ -109,8 +108,8 @@ def clear(*_: object) -> None: def add_widget( self: HeadlessWidget, - *args: Any, # noqa: ANN401 - **kwargs: Any, # noqa: ANN401 + *args: object, + **kwargs: object, ) -> None: """Extend `Widget.add_widget` and handle `canvas`.""" canvas = self.canvas @@ -120,8 +119,8 @@ def add_widget( def remove_widget( self: HeadlessWidget, - *args: Any, # noqa: ANN401 - **kwargs: Any, # noqa: ANN401 + *args: object, + **kwargs: object, ) -> None: """Extend `Widget.remove_widget` and handle `canvas`.""" canvas = self.canvas diff --git a/headless_kivy_pi/config.py b/headless_kivy_pi/config.py index 12e966b..a9536ce 100644 --- a/headless_kivy_pi/config.py +++ b/headless_kivy_pi/config.py @@ -1,5 +1,5 @@ # pyright: reportMissingImports=false -"""Implement `setup_kivy`, it configures Kivy.""" +"""Implement `setup_headless_kivy`, it configures headless-kivy-pi.""" from __future__ import annotations import atexit diff --git a/headless_kivy_pi/fake.py b/headless_kivy_pi/fake.py index 1a52bb4..92caa1b 100644 --- a/headless_kivy_pi/fake.py +++ b/headless_kivy_pi/fake.py @@ -16,13 +16,15 @@ def __init__(self: Fake) -> None: """Fake constructor.""" super().__init__('') - def __getattr__(self: Fake, attr: str) -> Fake: + def __getattr__(self: Fake, attr: str) -> Fake | str: """Fake all attrs.""" logger.debug( - 'Accessing fake attribute of a `Fake` instance', + 'Accessing fake attribute of a `Fake` insta', extra={'attr': attr}, ) - return Fake() + if attr == '__file__': + return 'fake' + return self def __call__(self: Fake, *args: object, **kwargs: object) -> Fake: """Fake call.""" diff --git a/poetry.lock b/poetry.lock index 87a0ae9..26587fd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "adafruit-blinka" -version = "8.25.0" +version = "8.34.0" description = "CircuitPython APIs for non-CircuitPython versions of Python such as CPython on Linux and MicroPython." optional = false python-versions = ">=3.7.0" files = [ - {file = "Adafruit-Blinka-8.25.0.tar.gz", hash = "sha256:4cae1655a60c341d97e59108c0fa4f3963e8373b6195381e3b7f76ce1f4c2a5b"}, - {file = "Adafruit_Blinka-8.25.0-py3-none-any.whl", hash = "sha256:6ccb124143bfc1073af7225445c75781e5415ecb97484bd24a9e0d9dbfb90816"}, + {file = "Adafruit-Blinka-8.34.0.tar.gz", hash = "sha256:6629bc67d7a025d4c6b6e9d9851c12a86c8cedd4a8ac27f67fd63576010e9dee"}, + {file = "Adafruit_Blinka-8.34.0-py3-none-any.whl", hash = "sha256:153d4bd49abd8341888f96ccbdcc3855e742c215bb3955d08862d066c0a5069e"}, ] [package.dependencies] @@ -19,13 +19,13 @@ pyftdi = ">=0.40.0" [[package]] name = "adafruit-circuitpython-aw9523" -version = "1.1.8" +version = "1.1.9" description = "Python library for AW9523 GPIO expander and LED driver" optional = false python-versions = "*" files = [ - {file = "adafruit-circuitpython-aw9523-1.1.8.tar.gz", hash = "sha256:178c071bc6bb0eb974e18b35e9842230383ef789da47814eb89ba8d207258f10"}, - {file = "adafruit_circuitpython_aw9523-1.1.8-py3-none-any.whl", hash = "sha256:b1153a3aa5306dab90aafc04d97670fff4e6da1c65909df63aaa6654adf66339"}, + {file = "adafruit-circuitpython-aw9523-1.1.9.tar.gz", hash = "sha256:bdc5ad40a851a47ae0a5660f09e67a70583bf6182984c3b99c8c57aa56f279b5"}, + {file = "adafruit_circuitpython_aw9523-1.1.9-py3-none-any.whl", hash = "sha256:73562793f13aa5d58af1ffee2bc0465e1e445e2582a696608d9b866be2e4bfb2"}, ] [package.dependencies] @@ -48,15 +48,29 @@ files = [ Adafruit-Blinka = ">=7.0.0" adafruit-circuitpython-typing = "*" +[[package]] +name = "adafruit-circuitpython-connectionmanager" +version = "1.0.1" +description = "A urllib3.poolmanager/urllib3.connectionpool-like library for managing sockets and connections" +optional = false +python-versions = "*" +files = [ + {file = "adafruit-circuitpython-connectionmanager-1.0.1.tar.gz", hash = "sha256:913b325488860524edcb6a69f13575addc27c0127764cd74074476e149ba7a73"}, + {file = "adafruit_circuitpython_connectionmanager-1.0.1-py3-none-any.whl", hash = "sha256:9b7800960b61896f499086a2dbf192c270a3eb1f69bf88268a1bd5422f33ad68"}, +] + +[package.dependencies] +Adafruit-Blinka = "*" + [[package]] name = "adafruit-circuitpython-register" -version = "1.9.17" +version = "1.9.18" description = "CircuitPython data descriptor classes to represent hardware registers on I2C and SPI devices." optional = false python-versions = "*" files = [ - {file = "adafruit-circuitpython-register-1.9.17.tar.gz", hash = "sha256:74982b07a8009b2de9cec138c545cddac7370732333d1aa1b50583a54bdc392d"}, - {file = "adafruit_circuitpython_register-1.9.17-py3-none-any.whl", hash = "sha256:038731e1da8dfa8eeaa083c430364c82931856d2edee0109d22473e85822255c"}, + {file = "adafruit-circuitpython-register-1.9.18.tar.gz", hash = "sha256:146fdec0ca787663962e1a894a3028056f1adbd0322a1d187bdeda739e4e228d"}, + {file = "adafruit_circuitpython_register-1.9.18-py3-none-any.whl", hash = "sha256:fc84e55129c32e65e740c37f821efb1a6ab2b9439d2b27459c85a6017b0d9302"}, ] [package.dependencies] @@ -67,27 +81,28 @@ typing-extensions = ">=4.0,<5.0" [[package]] name = "adafruit-circuitpython-requests" -version = "2.0.2" +version = "3.0.1" description = "A requests-like library for web interfacing" optional = false python-versions = "*" files = [ - {file = "adafruit-circuitpython-requests-2.0.2.tar.gz", hash = "sha256:fec34d21be9d721a44bd1471e3651630eb7d394ce806de2fb39536dac73408aa"}, - {file = "adafruit_circuitpython_requests-2.0.2-py3-none-any.whl", hash = "sha256:91e7634cd223ee3adf22ca3cf6e8f34713a56a3809031f37ec31b90a3a9f503f"}, + {file = "adafruit-circuitpython-requests-3.0.1.tar.gz", hash = "sha256:0998cbc81429754f348a67ff7245655be4da2646e682215e4d80812feaa1f1d5"}, + {file = "adafruit_circuitpython_requests-3.0.1-py3-none-any.whl", hash = "sha256:9403b7877b473aa9fe59b1e5785fa8253cc5b95de98c1e3f913add3d65dda6bb"}, ] [package.dependencies] Adafruit-Blinka = "*" +Adafruit-Circuitpython-ConnectionManager = "*" [[package]] name = "adafruit-circuitpython-rgb-display" -version = "3.12.2" +version = "3.12.4" description = "CircuitPython library for RGB displays." optional = false python-versions = "*" files = [ - {file = "adafruit-circuitpython-rgb-display-3.12.2.tar.gz", hash = "sha256:a41afdf15e18734e27e138ef14c72ae03b0307b4b3799c4b17507dac8c2793f8"}, - {file = "adafruit_circuitpython_rgb_display-3.12.2-py3-none-any.whl", hash = "sha256:9eba697fbe86a8b32bdb6b62d907cafc48be01d1d35766f48721b7a5ef6c4a1a"}, + {file = "adafruit-circuitpython-rgb-display-3.12.4.tar.gz", hash = "sha256:b1fa959df17e4b180bb257fb5d713cf4f8513b07dac590e07ca0e6e1a8b2c2bb"}, + {file = "adafruit_circuitpython_rgb_display-3.12.4-py3-none-any.whl", hash = "sha256:79e67706764d94bbdf4af0c50cbacaa987c4cbf8f6d7d5ba0feaba6f8f272c82"}, ] [package.dependencies] @@ -96,13 +111,13 @@ adafruit-circuitpython-busdevice = "*" [[package]] name = "adafruit-circuitpython-typing" -version = "1.9.5" +version = "1.10.2" description = "Types needed for type annotation that are not in `typing`" optional = false python-versions = "*" files = [ - {file = "adafruit-circuitpython-typing-1.9.5.tar.gz", hash = "sha256:6a2a7a4f60d54348f3c4aad8f4dd0d0f0aaf9c854ddd1761b20d5146440faa1d"}, - {file = "adafruit_circuitpython_typing-1.9.5-py3-none-any.whl", hash = "sha256:02625de1f8fdeb42db5a88999d2dc7a905339b30c673f7a2d8608b840e8520cc"}, + {file = "adafruit-circuitpython-typing-1.10.2.tar.gz", hash = "sha256:ecfbaae7ac0f41b202aa3ed98cbd0f696b17e83b7e7f3fac6dc6a228cbc1c6c6"}, + {file = "adafruit_circuitpython_typing-1.10.2-py3-none-any.whl", hash = "sha256:e64bbf2f7d4a4bdf0f9be8e039cfe2ed199aa7d51aa44a090d50be4f0917d24f"}, ] [package.dependencies] @@ -113,13 +128,13 @@ typing-extensions = ">=4.0,<5.0" [[package]] name = "adafruit-platformdetect" -version = "3.54.0" +version = "3.62.0" description = "Platform detection for use by libraries like Adafruit-Blinka." optional = false python-versions = "*" files = [ - {file = "Adafruit-PlatformDetect-3.54.0.tar.gz", hash = "sha256:3fe7fb781a83d3f28812bcbff34edd4100afb68901d9c62ee22d07e824d82168"}, - {file = "Adafruit_PlatformDetect-3.54.0-py3-none-any.whl", hash = "sha256:fecae7a6612bbcd32d1f40c2d2f1aa263c554be5e52cc6fa4eebaf8b80c33519"}, + {file = "Adafruit-PlatformDetect-3.62.0.tar.gz", hash = "sha256:2f609ba96ab23a8e26abe2ac3bc1580321c29512855cc2d65ad7d8120d120f7e"}, + {file = "Adafruit_PlatformDetect-3.62.0-py3-none-any.whl", hash = "sha256:49f4099a0f2b9ab449b7d6ab89f98ebf18ff84ebab243383aa294f9a53c82430"}, ] [[package]] @@ -135,13 +150,13 @@ files = [ [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -245,13 +260,69 @@ files = [ [[package]] name = "cython" -version = "3.0.6" +version = "3.0.9" description = "The Cython compiler for writing C extensions in the Python language." optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "Cython-3.0.6-py2.py3-none-any.whl", hash = "sha256:5921a175ea20779d4443ef99276cfa9a1a47de0e32d593be7679be741c9ed93b"}, - {file = "Cython-3.0.6.tar.gz", hash = "sha256:399d185672c667b26eabbdca420c98564583798af3bc47670a8a09e9f19dd660"}, + {file = "Cython-3.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:296bd30d4445ac61b66c9d766567f6e81a6e262835d261e903c60c891a6729d3"}, + {file = "Cython-3.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f496b52845cb45568a69d6359a2c335135233003e708ea02155c10ce3548aa89"}, + {file = "Cython-3.0.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:858c3766b9aa3ab8a413392c72bbab1c144a9766b7c7bfdef64e2e414363fa0c"}, + {file = "Cython-3.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0eb1e6ef036028a52525fd9a012a556f6dd4788a0e8755fe864ba0e70cde2ff"}, + {file = "Cython-3.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c8191941073ea5896321de3c8c958fd66e5f304b0cd1f22c59edd0b86c4dd90d"}, + {file = "Cython-3.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e32b016030bc72a8a22a1f21f470a2f57573761a4f00fbfe8347263f4fbdb9f1"}, + {file = "Cython-3.0.9-cp310-cp310-win32.whl", hash = "sha256:d6f3ff1cd6123973fe03e0fb8ee936622f976c0c41138969975824d08886572b"}, + {file = "Cython-3.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:56f3b643dbe14449248bbeb9a63fe3878a24256664bc8c8ef6efd45d102596d8"}, + {file = "Cython-3.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:35e6665a20d6b8a152d72b7fd87dbb2af6bb6b18a235b71add68122d594dbd41"}, + {file = "Cython-3.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92f4960c40ad027bd8c364c50db11104eadc59ffeb9e5b7f605ca2f05946e20"}, + {file = "Cython-3.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38df37d0e732fbd9a2fef898788492e82b770c33d1e4ed12444bbc8a3b3f89c0"}, + {file = "Cython-3.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad7fd88ebaeaf2e76fd729a8919fae80dab3d6ac0005e28494261d52ff347a8f"}, + {file = "Cython-3.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1365d5f76bf4d19df3d19ce932584c9bb76e9fb096185168918ef9b36e06bfa4"}, + {file = "Cython-3.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c232e7f279388ac9625c3e5a5a9f0078a9334959c5d6458052c65bbbba895e1e"}, + {file = "Cython-3.0.9-cp311-cp311-win32.whl", hash = "sha256:357e2fad46a25030b0c0496487e01a9dc0fdd0c09df0897f554d8ba3c1bc4872"}, + {file = "Cython-3.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:1315aee506506e8d69cf6631d8769e6b10131fdcc0eb66df2698f2a3ddaeeff2"}, + {file = "Cython-3.0.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:157973807c2796addbed5fbc4d9c882ab34bbc60dc297ca729504901479d5df7"}, + {file = "Cython-3.0.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00b105b5d050645dd59e6767bc0f18b48a4aa11c85f42ec7dd8181606f4059e3"}, + {file = "Cython-3.0.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac5536d09bef240cae0416d5a703d298b74c7bbc397da803ac9d344e732d4369"}, + {file = "Cython-3.0.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c44501d476d16aaa4cbc29c87f8c0f54fc20e69b650d59cbfa4863426fc70c"}, + {file = "Cython-3.0.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:cc9c3b9f20d8e298618e5ccd32083ca386e785b08f9893fbec4c50b6b85be772"}, + {file = "Cython-3.0.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a30d96938c633e3ec37000ac3796525da71254ef109e66bdfd78f29891af6454"}, + {file = "Cython-3.0.9-cp312-cp312-win32.whl", hash = "sha256:757ca93bdd80702546df4d610d2494ef2e74249cac4d5ba9464589fb464bd8a3"}, + {file = "Cython-3.0.9-cp312-cp312-win_amd64.whl", hash = "sha256:1dc320a9905ab95414013f6de805efbff9e17bb5fb3b90bbac533f017bec8136"}, + {file = "Cython-3.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4ae349960ebe0da0d33724eaa7f1eb866688fe5434cc67ce4dbc06d6a719fbfc"}, + {file = "Cython-3.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63d2537bf688247f76ded6dee28ebd26274f019309aef1eb4f2f9c5c482fde2d"}, + {file = "Cython-3.0.9-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f5a2dfc724bea1f710b649f02d802d80fc18320c8e6396684ba4a48412445a"}, + {file = "Cython-3.0.9-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:deaf4197d4b0bcd5714a497158ea96a2bd6d0f9636095437448f7e06453cc83d"}, + {file = "Cython-3.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:000af6deb7412eb7ac0c635ff5e637fb8725dd0a7b88cc58dfc2b3de14e701c4"}, + {file = "Cython-3.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:15c7f5c2d35bed9aa5f2a51eaac0df23ae72f2dbacf62fc672dd6bfaa75d2d6f"}, + {file = "Cython-3.0.9-cp36-cp36m-win32.whl", hash = "sha256:f49aa4970cd3bec66ac22e701def16dca2a49c59cceba519898dd7526e0be2c0"}, + {file = "Cython-3.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:4558814fa025b193058d42eeee498a53d6b04b2980d01339fc2444b23fd98e58"}, + {file = "Cython-3.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:539cd1d74fd61f6cfc310fa6bbbad5adc144627f2b7486a07075d4e002fd6aad"}, + {file = "Cython-3.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3232926cd406ee02eabb732206f6e882c3aed9d58f0fea764013d9240405bcf"}, + {file = "Cython-3.0.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33b6ac376538a7fc8c567b85d3c71504308a9318702ec0485dd66c059f3165cb"}, + {file = "Cython-3.0.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2cc92504b5d22ac66031ffb827bd3a967fc75a5f0f76ab48bce62df19be6fdfd"}, + {file = "Cython-3.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:22b8fae756c5c0d8968691bed520876de452f216c28ec896a00739a12dba3bd9"}, + {file = "Cython-3.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9cda0d92a09f3520f29bd91009f1194ba9600777c02c30c6d2d4ac65fb63e40d"}, + {file = "Cython-3.0.9-cp37-cp37m-win32.whl", hash = "sha256:ec612418490941ed16c50c8d3784c7bdc4c4b2a10c361259871790b02ec8c1db"}, + {file = "Cython-3.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:976c8d2bedc91ff6493fc973d38b2dc01020324039e2af0e049704a8e1b22936"}, + {file = "Cython-3.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5055988b007c92256b6e9896441c3055556038c3497fcbf8c921a6c1fce90719"}, + {file = "Cython-3.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9360606d964c2d0492a866464efcf9d0a92715644eede3f6a2aa696de54a137"}, + {file = "Cython-3.0.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c6e809f060bed073dc7cba1648077fe3b68208863d517c8b39f3920eecf9dd"}, + {file = "Cython-3.0.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95ed792c966f969cea7489c32ff90150b415c1f3567db8d5a9d489c7c1602dac"}, + {file = "Cython-3.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8edd59d22950b400b03ca78d27dc694d2836a92ef0cac4f64cb4b2ff902f7e25"}, + {file = "Cython-3.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4cf0ed273bf60e97922fcbbdd380c39693922a597760160b4b4355e6078ca188"}, + {file = "Cython-3.0.9-cp38-cp38-win32.whl", hash = "sha256:5eb9bd4ae12ebb2bc79a193d95aacf090fbd8d7013e11ed5412711650cb34934"}, + {file = "Cython-3.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:44457279da56e0f829bb1fc5a5dc0836e5d498dbcf9b2324f32f7cc9d2ec6569"}, + {file = "Cython-3.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4b419a1adc2af43f4660e2f6eaf1e4fac2dbac59490771eb8ac3d6063f22356"}, + {file = "Cython-3.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f836192140f033b2319a0128936367c295c2b32e23df05b03b672a6015757ea"}, + {file = "Cython-3.0.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd198c1a7f8e9382904d622cc0efa3c184605881fd5262c64cbb7168c4c1ec5"}, + {file = "Cython-3.0.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a274fe9ca5c53fafbcf5c8f262f8ad6896206a466f0eeb40aaf36a7951e957c0"}, + {file = "Cython-3.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:158c38360bbc5063341b1e78d3737f1251050f89f58a3df0d10fb171c44262be"}, + {file = "Cython-3.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bf30b045f7deda0014b042c1b41c1d272facc762ab657529e3b05505888e878"}, + {file = "Cython-3.0.9-cp39-cp39-win32.whl", hash = "sha256:9a001fd95c140c94d934078544ff60a3c46aca2dc86e75a76e4121d3cd1f4b33"}, + {file = "Cython-3.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:530c01c4aebba709c0ec9c7ecefe07177d0b9fd7ffee29450a118d92192ccbdf"}, + {file = "Cython-3.0.9-py2.py3-none-any.whl", hash = "sha256:bf96417714353c5454c2e3238fca9338599330cf51625cdc1ca698684465646f"}, + {file = "Cython-3.0.9.tar.gz", hash = "sha256:a2d354f059d1f055d34cfaa62c5b68bc78ac2ceab6407148d47fb508cf3ba4f3"}, ] [[package]] @@ -278,76 +349,83 @@ files = [ [[package]] name = "kivy" -version = "2.2.1" -description = "A software library for rapid development of hardware-accelerated multitouch applications." +version = "2.3.0" +description = "An open-source Python framework for developing GUI apps that work cross-platform, including desktop, mobile and embedded platforms." optional = false python-versions = ">=3.7" files = [ - {file = "Kivy-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5aa8a0ca0708dd6edb0127c91712db7b8aad3aea3a79d498df145b8a7ee0aee6"}, - {file = "Kivy-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af45f433208c16a4d0e755fafac5f2238560880a475a8a041ca9d99695a6b166"}, - {file = "Kivy-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:564705e8fbd788dce22dae8dfe0488be705e14381fa59d6bdfa3df5255446429"}, - {file = "Kivy-2.2.1-cp310-cp310-win32.whl", hash = "sha256:19cd0b0b40046d6233f0a30c8003e1ffe5a135b96ae50a5442bd62ef9d9c6744"}, - {file = "Kivy-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:4915e931cb26efa016956b16a0d04bcc8eb89abcb2734ed718786c30241795bb"}, - {file = "Kivy-2.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e3df4f6230f4b125c78b1dd923f714e93341afda5b55ae9795df2d6b2c5dbc4d"}, - {file = "Kivy-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eb5de5ffea53a341790ff7ba4d3cea60d37523f0766a4783692e2aa1d50670e"}, - {file = "Kivy-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42a5e292f232a67bc67dfa91a205aed472dc4df7329ce059c74a88031c652efa"}, - {file = "Kivy-2.2.1-cp311-cp311-win32.whl", hash = "sha256:d78a8d92565c6309bb1edc9a0c99883630adc0503dba25f3eb8b667ceb736395"}, - {file = "Kivy-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:e317a710e0be3083901d29457fa8a203c14a71b7cef8008e3cdc7d1eb8e4d7c5"}, - {file = "Kivy-2.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a7b8c8725cd1b3575b95f8f8b5d6f804e096af8958156450bb876821fbb2ce37"}, - {file = "Kivy-2.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b89bc5add63bf83fd1b026ec4d492ea9c90067b30652c14afe97cd4c30a2b5a"}, - {file = "Kivy-2.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f19ed8efaea7c880ab6342269c41ac47989812cbd036a524abc49ba9923487f"}, - {file = "Kivy-2.2.1-cp37-cp37m-win32.whl", hash = "sha256:c932fa9a8ade002ec4468c416bedda4188cda53721be84e4086b278c3b00c654"}, - {file = "Kivy-2.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7eeda699fe764c5efa2cc43e0ef28275be2a56ccd8af17933b6e2457d66d5b0d"}, - {file = "Kivy-2.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0456359afe01a94d39bb29ce8a8dd9d9dcd86cec6532f17bffb0725647a8d63a"}, - {file = "Kivy-2.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37874a7a2a9983ae494910578e2e6a4aba1568b35f915b014d85f779b5add8b9"}, - {file = "Kivy-2.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7bfbc97f66d50d71bcbc5c254e74386d94d7a1fb051448fbc1ba6742c1d1d21"}, - {file = "Kivy-2.2.1-cp38-cp38-win32.whl", hash = "sha256:0454ad509b83f5ee04dd7bb46682f077adcde99d86c206127f2dd57aedd28f5b"}, - {file = "Kivy-2.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:a6b47a8b5b47d091ac4604635edef7522ddfb9cf47b3f258cccd8ffbf094b429"}, - {file = "Kivy-2.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e8370d529548d360803424f803231ed8ad19c7f4c351aa893fe9d39bcc940fc"}, - {file = "Kivy-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1e9cf59dc3875c943c89d3668a73235c96f4c03680c4691416ccc5d1c7f50f0"}, - {file = "Kivy-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97442522fb58ebf4038f3c57a795046e00a23f7c4dc0775b282af9aff845732a"}, - {file = "Kivy-2.2.1-cp39-cp39-win32.whl", hash = "sha256:f3dcfdce8403dc3df7a8b1a7d424c0939d41bc48a0b627403b685931a609a7ec"}, - {file = "Kivy-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:35eb3025a77894380196d5d64852da6c539e72aa2ff4970011385eb7a70c46e0"}, - {file = "Kivy-2.2.1.tar.gz", hash = "sha256:047a434e8efd32d425321e065fcccf725f165adb0c81ed69dc33c360d4796f7e"}, + {file = "Kivy-2.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcd8fdc742ae10d27e578df2052b4c3e99a754e91baad77d1f2e4f4d1238917f"}, + {file = "Kivy-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7492593a5d5d916c48b14a06fbe177341b1efb5753c9984be2fb84e3b3313c89"}, + {file = "Kivy-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:199c30e8daeace61392329766eeb68daa49631cd9793bec9440dda5cf30d68d5"}, + {file = "Kivy-2.3.0-cp310-cp310-win32.whl", hash = "sha256:03fc4b26c7d6a5ecee2c97ffa8d622e97ac8a8c4e0a00d333c156d64e09e4e19"}, + {file = "Kivy-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa5d57494cab405d395d65570d8481ab87869ba6daf4efb6c985bd16b32e7abf"}, + {file = "Kivy-2.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ec36ab3b74a525fa463b61895d3a2d76e9e4d206641233defae0d604e75df7ad"}, + {file = "Kivy-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3e923397779776ac97ad87a1b9dd603b7f1c911a6ae04f1d1658712eaaf7cb"}, + {file = "Kivy-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7766baac2509d699df84b284579fa25ee31383d48893660cd8dba62081453a29"}, + {file = "Kivy-2.3.0-cp311-cp311-win32.whl", hash = "sha256:d654aaec6ddf9ca0edf73abd79e6aea423299c825a7ac432df17b031adaa7900"}, + {file = "Kivy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:33dca85a520fe958e7134b96025b0625eb769adfb8829359959c8b314b7bc8d4"}, + {file = "Kivy-2.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7b1307521843d316265481d963344e85870ae5fa0c7d0881129749acfe61da7b"}, + {file = "Kivy-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:521105a4ca1db3e1203c3cdba4abe737533874d9c29bbfb1e1ae941238507440"}, + {file = "Kivy-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6672959894f652856d1dfcbcdcc09263de5f1cbed768b997dc8dcecab4385a4f"}, + {file = "Kivy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:cf0bccc95b1344b79fbfdf54155d40438490f9801fd77279f068a4f66db72e4e"}, + {file = "Kivy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:710648c987a63e37c723e6622853efe0278767596631a38728a54474b2cb77f2"}, + {file = "Kivy-2.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d2c6a411e2d837684d91b46231dd12db74fb1db6a2628e9f27581ce1583e5c8a"}, + {file = "Kivy-2.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8757f189d8a41a4b164150144037359405906a46b07572e8e1c602a782cacebf"}, + {file = "Kivy-2.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06c9b0a4bff825793e150e2cdbc823b59f635ce51e575d470d0fc3a06159596c"}, + {file = "Kivy-2.3.0-cp37-cp37m-win32.whl", hash = "sha256:d72599b80c8a7c2698769b4129ff52f2c4e28b6a75f9401180052c7d80763f19"}, + {file = "Kivy-2.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:0c42cf3c33e1aa3dee9c8acb6f91f8a4ad6c9de76064dcb8fdb1c60809643788"}, + {file = "Kivy-2.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:da8dd7ade7b7859642f53c3f32e10513877ce650367b68591b3aaacb46dcf012"}, + {file = "Kivy-2.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb6191bb51983f9e8257356aa53a71ccff5b6cf92f0bdcd5756973a6ac4b4446"}, + {file = "Kivy-2.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa3e7ce4fbd22284b303939676c5ae5448bb1e4d405f066dfc76c7cf56595cd"}, + {file = "Kivy-2.3.0-cp38-cp38-win32.whl", hash = "sha256:221f809220e518ae8b88a9b31310f9fef73727569e5cb13436572674fce4507b"}, + {file = "Kivy-2.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:8d2c3e5927fcf021d32124f915d56ae29e3a126c4f53db098436ea3959758a4c"}, + {file = "Kivy-2.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e8b91dfa2ad83739cc12d0f7bbe6410a3af2c2b3afd7b1d08919d9ec92826d61"}, + {file = "Kivy-2.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d79cb4a8649c476db18a079c447e57f8dbd4ad41459dc2162133a45cbb8cae96"}, + {file = "Kivy-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3be8db1eecc2d18859a7324b5cea79afb44095ccd73671987840afa26c68b0c9"}, + {file = "Kivy-2.3.0-cp39-cp39-win32.whl", hash = "sha256:5e6c431088584132d685696592e281fac217a5fd662f92cc6c6b48316e30b9c2"}, + {file = "Kivy-2.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:c332ff319db7648004d486c40fc4e700972f8e79a882d698e18eb238b2009e98"}, + {file = "Kivy-2.3.0.tar.gz", hash = "sha256:e8b8610c7f8ef6db908a139d369b247378f18105c96981e492eab2b4706c79d5"}, ] [package.dependencies] docutils = "*" -"kivy-deps.angle" = {version = ">=0.3.3,<0.4.0", markers = "sys_platform == \"win32\""} +"kivy-deps.angle" = {version = ">=0.4.0,<0.5.0", markers = "sys_platform == \"win32\""} "kivy-deps.glew" = {version = ">=0.3.1,<0.4.0", markers = "sys_platform == \"win32\""} -"kivy-deps.sdl2" = {version = ">=0.6.0,<0.7.0", markers = "sys_platform == \"win32\""} +"kivy-deps.sdl2" = {version = ">=0.7.0,<0.8.0", markers = "sys_platform == \"win32\""} Kivy-Garden = ">=0.1.4" pygments = "*" pypiwin32 = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -angle = ["kivy-deps.angle (>=0.3.3,<0.4.0)"] -base = ["docutils", "kivy-deps.angle (>=0.3.3,<0.4.0)", "kivy-deps.glew (>=0.3.1,<0.4.0)", "kivy-deps.sdl2 (>=0.6.0,<0.7.0)", "pillow", "pygments", "pypiwin32", "requests"] -dev = ["flake8", "funcparserlib (==1.0.0a0)", "kivy-deps.glew-dev (>=0.3.1,<0.4.0)", "kivy-deps.gstreamer-dev (>=0.3.3,<0.4.0)", "kivy-deps.sdl2-dev (>=0.6.0,<0.7.0)", "pre-commit", "pyinstaller", "pytest (>=3.6)", "pytest-asyncio (!=0.11.0)", "pytest-benchmark", "pytest-cov", "pytest-timeout", "responses", "sphinx (<=6.2.1)", "sphinxcontrib-actdiag", "sphinxcontrib-blockdiag", "sphinxcontrib-jquery", "sphinxcontrib-nwdiag", "sphinxcontrib-seqdiag"] -full = ["docutils", "ffpyplayer", "kivy-deps.angle (>=0.3.3,<0.4.0)", "kivy-deps.glew (>=0.3.1,<0.4.0)", "kivy-deps.gstreamer (>=0.3.3,<0.4.0)", "kivy-deps.sdl2 (>=0.6.0,<0.7.0)", "pillow", "pygments", "pypiwin32"] +angle = ["kivy-deps.angle (>=0.4.0,<0.5.0)"] +base = ["docutils", "kivy-deps.angle (>=0.4.0,<0.5.0)", "kivy-deps.glew (>=0.3.1,<0.4.0)", "kivy-deps.sdl2 (>=0.7.0,<0.8.0)", "pillow (>=9.5.0,<11)", "pygments", "pypiwin32", "requests"] +dev = ["flake8", "funcparserlib (==1.0.0a0)", "kivy-deps.glew-dev (>=0.3.1,<0.4.0)", "kivy-deps.gstreamer-dev (>=0.3.3,<0.4.0)", "kivy-deps.sdl2-dev (>=0.7.0,<0.8.0)", "pre-commit", "pyinstaller", "pytest (>=3.6)", "pytest-asyncio (!=0.11.0)", "pytest-benchmark", "pytest-cov", "pytest-timeout", "responses", "sphinx (<=6.2.1)", "sphinxcontrib-actdiag", "sphinxcontrib-blockdiag", "sphinxcontrib-jquery", "sphinxcontrib-nwdiag", "sphinxcontrib-seqdiag"] +full = ["docutils", "ffpyplayer", "kivy-deps.angle (>=0.4.0,<0.5.0)", "kivy-deps.glew (>=0.3.1,<0.4.0)", "kivy-deps.gstreamer (>=0.3.3,<0.4.0)", "kivy-deps.sdl2 (>=0.7.0,<0.8.0)", "pillow (>=9.5.0,<11)", "pygments", "pypiwin32"] glew = ["kivy-deps.glew (>=0.3.1,<0.4.0)"] gstreamer = ["kivy-deps.gstreamer (>=0.3.3,<0.4.0)"] media = ["ffpyplayer", "kivy-deps.gstreamer (>=0.3.3,<0.4.0)"] -sdl2 = ["kivy-deps.sdl2 (>=0.6.0,<0.7.0)"] +sdl2 = ["kivy-deps.sdl2 (>=0.7.0,<0.8.0)"] tuio = ["oscpy"] [[package]] name = "kivy-deps-angle" -version = "0.3.3" +version = "0.4.0" description = "Repackaged binary dependency of Kivy." optional = false python-versions = "*" files = [ - {file = "kivy_deps.angle-0.3.3-cp310-cp310-win32.whl", hash = "sha256:8e571b2c662480a265420d6ded386df208d83272896daf616256672c5648e538"}, - {file = "kivy_deps.angle-0.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:e48457f6830fb53aaaa6bf3bd63d50120c1bc1c94f207d988cc8071c122456e9"}, - {file = "kivy_deps.angle-0.3.3-cp311-cp311-win32.whl", hash = "sha256:fdc3de5ead670a8ee0bb411b603cabeaecf7fe277cf99cd70c9585cd964f19a2"}, - {file = "kivy_deps.angle-0.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:c380fb602782e525deba1200cdc0fd3c03b6b3855edb283d76f68485a185061a"}, - {file = "kivy_deps.angle-0.3.3-cp37-cp37m-win32.whl", hash = "sha256:0fa32cc718b51b21745b6b7a055e6e171517f567b5900250c49539bae3d69806"}, - {file = "kivy_deps.angle-0.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6336455e59f5b8dd4dd57caca2adf397a48726af8afc48384f4539dccb7b889c"}, - {file = "kivy_deps.angle-0.3.3-cp38-cp38-win32.whl", hash = "sha256:e3fd1168dd4090cc2ccf650abe209483c0794c5a454360c41b01633888afbba1"}, - {file = "kivy_deps.angle-0.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:f044d7e6a9f839222e44f9ae0441ae25ff42e35a5c1b7d1ea9a4d0845dd57172"}, - {file = "kivy_deps.angle-0.3.3-cp39-cp39-win32.whl", hash = "sha256:00a4922a50a55f532722a4419eb394a7a8837d0f78620e2b47f33c6db45a4dc0"}, - {file = "kivy_deps.angle-0.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:de6dbe3273e95a85f6f96c9f52cc0d29a1f92aa05fd10de886ba8c483021b532"}, + {file = "kivy_deps.angle-0.4.0-cp310-cp310-win32.whl", hash = "sha256:7873a551e488afa5044c4949a4aa42c4a4c4290469f0a6dd861e6b95283c9638"}, + {file = "kivy_deps.angle-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71f2f01a3a7bbe1d4790e2a64e64a0ea8ae154418462ea407799ed66898b2c1f"}, + {file = "kivy_deps.angle-0.4.0-cp311-cp311-win32.whl", hash = "sha256:c3899ff1f3886b80b155955bad07bfa33bbebd97718cdf46dfd788dc467124bc"}, + {file = "kivy_deps.angle-0.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:574381d4e66f3198bc48aa10f238e7a3816ad56b80ec939f5d56fb33a378d0b1"}, + {file = "kivy_deps.angle-0.4.0-cp312-cp312-win32.whl", hash = "sha256:4fa7a6366899fba13f7624baf4645787165f45731db08d14557da29c12ee48f0"}, + {file = "kivy_deps.angle-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:668e670d4afd2551af0af2c627ceb0feac884bd799fb6a3dff78fdbfa2ea0451"}, + {file = "kivy_deps.angle-0.4.0-cp37-cp37m-win32.whl", hash = "sha256:24cfc0076d558080a00c443c7117311b4a977c1916fe297232eff1fd6f62651e"}, + {file = "kivy_deps.angle-0.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:48592ac6f7c183c5cd10d9ebe43d4148d0b2b9e400a2b0bcb5d21014cc929ce2"}, + {file = "kivy_deps.angle-0.4.0-cp38-cp38-win32.whl", hash = "sha256:1bbacf20bf6bd6ee965388f95d937c8fba2c54916fb44faa166c2ba58276753c"}, + {file = "kivy_deps.angle-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:e2ba4e390b02ad5bcb57b43a9227fa27ff55e69cd715a87217b324195eb267c3"}, + {file = "kivy_deps.angle-0.4.0-cp39-cp39-win32.whl", hash = "sha256:6546a62aba2b7e18a800b3df79daa757af3a980c297646c986896522395794e2"}, + {file = "kivy_deps.angle-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:bfaf9b37f2ecc3e4e7736657eed507716477af35cdd3118903e999d9d567ae8c"}, ] [[package]] @@ -361,6 +439,8 @@ files = [ {file = "kivy_deps.glew-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef2d2a93f129d8425c75234e7f6cc0a34b59a4aee67f6d2cd7a5fdfa9915b53"}, {file = "kivy_deps.glew-0.3.1-cp311-cp311-win32.whl", hash = "sha256:ee2f80ef7ac70f4b61c50da8101b024308a8c59a57f7f25a6e09762b6c48f942"}, {file = "kivy_deps.glew-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:22e155ec59ce717387f5d8804811206d200a023ba3d0bc9bbf1393ee28d0053e"}, + {file = "kivy_deps.glew-0.3.1-cp312-cp312-win32.whl", hash = "sha256:b64ee4e445a04bc7c848c0261a6045fc2f0944cc05d7f953e3860b49f2703424"}, + {file = "kivy_deps.glew-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:3acbbd30da05fc10c185b5d4bb75fbbc882a6ef2192963050c1c94d60a6e795a"}, {file = "kivy_deps.glew-0.3.1-cp37-cp37m-win32.whl", hash = "sha256:5bf6a63fe9cc4fe7bbf280ec267ec8c47914020a1175fb22152525ff1837b436"}, {file = "kivy_deps.glew-0.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d64a8625799fab7a7efeb3661ef8779a7f9c6d80da53eed87a956320f55530fa"}, {file = "kivy_deps.glew-0.3.1-cp38-cp38-win32.whl", hash = "sha256:00f4ae0a4682d951266458ddb639451edb24baa54a35215dce889209daf19a06"}, @@ -371,21 +451,23 @@ files = [ [[package]] name = "kivy-deps-sdl2" -version = "0.6.0" +version = "0.7.0" description = "Repackaged binary dependency of Kivy." optional = false python-versions = "*" files = [ - {file = "kivy_deps.sdl2-0.6.0-cp310-cp310-win32.whl", hash = "sha256:12706ce376f258233386cff2e1d31163d2d97e34f23730d7a3b1f3931af4c78d"}, - {file = "kivy_deps.sdl2-0.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:55e951f89b3e0ad9e703a0f1d3f68092f8beb71e3beebd91cffc0d13f9a06015"}, - {file = "kivy_deps.sdl2-0.6.0-cp311-cp311-win32.whl", hash = "sha256:f9c932badc868b948b65626884e4b6617373b7ce8260f4c1ac9de5979cdf30b3"}, - {file = "kivy_deps.sdl2-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:155c2b2ae33550dee39db2343b4128036aa7e039483996763a208f82a4cb20a8"}, - {file = "kivy_deps.sdl2-0.6.0-cp37-cp37m-win32.whl", hash = "sha256:d3b66da88cc256369be9df19874e55752473f41bffbbbc1a0d146bb09618c5a1"}, - {file = "kivy_deps.sdl2-0.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4d461e4eaa30d19b8e921b40e640efb991ec9b2047cab53c85d5c1d1a30cef4a"}, - {file = "kivy_deps.sdl2-0.6.0-cp38-cp38-win32.whl", hash = "sha256:f091c84144f612969e5474bdd2d6e46c6dd94834b621e79770e89b52572bd836"}, - {file = "kivy_deps.sdl2-0.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:23e6aed2d1570d2a2ba4f93956d8a9fb8610d908391fe816568ceb3096a3808a"}, - {file = "kivy_deps.sdl2-0.6.0-cp39-cp39-win32.whl", hash = "sha256:78b151065b89bf8fd35694e9415e24f9bd8b4706cf0f439f2371183a6950d857"}, - {file = "kivy_deps.sdl2-0.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc1f78d86021a2bf1d8e6107ad611cc8c0610f6faf76062ab39ffdf1de31baef"}, + {file = "kivy_deps.sdl2-0.7.0-cp310-cp310-win32.whl", hash = "sha256:3c4b2bf1e473e6124563e1ff58cf3475c4f19fe9248940872c9e3c248bac3cb4"}, + {file = "kivy_deps.sdl2-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac0f4a6fe989899a60bbdb39516f45e4d90e2499864ab5d63e3706001cde48e8"}, + {file = "kivy_deps.sdl2-0.7.0-cp311-cp311-win32.whl", hash = "sha256:b727123d059c0c00c7d13cc1db8c8cfd0e48388cf24c11ec71cc6783811063c8"}, + {file = "kivy_deps.sdl2-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd946ca4e36a403bcafbe202033948c17f54bd5d28a343d98efd61f976822855"}, + {file = "kivy_deps.sdl2-0.7.0-cp312-cp312-win32.whl", hash = "sha256:2a8f23fe201dea368b47adfecf8fb9133315788d314ad32f33000254aa2388e4"}, + {file = "kivy_deps.sdl2-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:e56d5d651f81545c24f920f6f6e5d67b4100802152521022ccde53e822c507a2"}, + {file = "kivy_deps.sdl2-0.7.0-cp37-cp37m-win32.whl", hash = "sha256:c75626f6a3f8979b1c6a59e5070c7a547bb7c379a8e03f249af6b4c399305fc1"}, + {file = "kivy_deps.sdl2-0.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:95005fb3ae5b9e1d5edd32a6c0cfae9019efa2aeb3d909738dd73c5b9eea9dc1"}, + {file = "kivy_deps.sdl2-0.7.0-cp38-cp38-win32.whl", hash = "sha256:9728eaf70af514e0df163b062944fec008a5ceb73e53897ac89e62fcd2b0bac2"}, + {file = "kivy_deps.sdl2-0.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:a23811df7359e62acf4002fe5240d968a25e7aeaf7989b78b59cd6437f34f7b9"}, + {file = "kivy_deps.sdl2-0.7.0-cp39-cp39-win32.whl", hash = "sha256:ecbbcbd562a14a4a3870c8b6a0b1612eda24e9435df74fbb8e5f670560f0a9d6"}, + {file = "kivy_deps.sdl2-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:a5ef494d2f57224b93649df5f7a20c4f4cbc22416167732bf9f62d1cb263fef4"}, ] [[package]] @@ -402,49 +484,63 @@ files = [ [package.dependencies] requests = "*" +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "numpy" -version = "1.26.2" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] @@ -508,38 +604,38 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyobjc-core" -version = "10.0" +version = "10.1" description = "Python<->ObjC Interoperability Module" optional = true python-versions = ">=3.8" files = [ - {file = "pyobjc-core-10.0.tar.gz", hash = "sha256:3dd0a7b3acd7e0b8ffd3f5331b29a3aaebe79a03323e61efeece38627a6020b3"}, - {file = "pyobjc_core-10.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:61ea5112a672d21b5b0ed945778707c655b17c400672aef144705674c4b95499"}, - {file = "pyobjc_core-10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99b72cda4593e0c66037b25a178f2bcc6efffb6d5d9dcd477ecca859a1f9ae8e"}, - {file = "pyobjc_core-10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2843ca32e86a01ccee67d7ad82a325ddd72d754929d1f2c0d96bc8741dc9af09"}, - {file = "pyobjc_core-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a886b9d2a93210cab4ae72601ab005ca6f627fa2f0cc62c43c03ef1405067a11"}, - {file = "pyobjc_core-10.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:166666b5c380a49e8aa1ad1dda978c581e29a00703d82203216f3c65a3f397a4"}, - {file = "pyobjc_core-10.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:198a0360f64e4c0148eed07b42d1de0545f56c498c356d1d5524422bb3352907"}, + {file = "pyobjc-core-10.1.tar.gz", hash = "sha256:1844f1c8e282839e6fdcb9a9722396c1c12fb1e9331eb68828a26f28a3b2b2b1"}, + {file = "pyobjc_core-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2a72a88222539ad07b5c8be411edc52ff9147d7cef311a2c849869d7bb9603fd"}, + {file = "pyobjc_core-10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fe1b9987b7b0437685fb529832876c2a8463500114960d4e76bb8ae96b6bf208"}, + {file = "pyobjc_core-10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9f628779c345d3abd0e20048fb0e256d894c22254577a81a6dcfdb92c3647682"}, + {file = "pyobjc_core-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25a9e5a2de19238787d24cfa7def6b7fbb94bbe89c0e3109f71c1cb108e8ab44"}, + {file = "pyobjc_core-10.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2d43205d3a784aa87055b84c0ec0dfa76498e5f18d1ad16bdc58a3dcf5a7d5d0"}, + {file = "pyobjc_core-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0aa9799b5996a893944999a2f1afcf1de119cab3551c169ad9f54d12e1d38c99"}, ] [[package]] name = "pyobjc-framework-cocoa" -version = "10.0" +version = "10.1" description = "Wrappers for the Cocoa frameworks on macOS" optional = true python-versions = ">=3.8" files = [ - {file = "pyobjc-framework-Cocoa-10.0.tar.gz", hash = "sha256:723421eff4f59e4ca9a9bb8ec6dafbc0f778141236fa85a49fdd86732d58a74c"}, - {file = "pyobjc_framework_Cocoa-10.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:80c22a8fc7f085746d9cd222adeca8fe6790e3e6ad7eed5fc70b32aa87c10adb"}, - {file = "pyobjc_framework_Cocoa-10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0187cba228976a45f41116c74aab079b64bacb3ffc3c886a4bd8e472bf9be581"}, - {file = "pyobjc_framework_Cocoa-10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a81dabdc40268591e3196087388e680c6570fed1b521df9b04733cb3ece0414e"}, - {file = "pyobjc_framework_Cocoa-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a23db9ab99e338e1d8a268d873cc15408f78cec9946308393ca2241820c18b8"}, - {file = "pyobjc_framework_Cocoa-10.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:a3c66fe56a5156a818fbf056c589f8140a5fdb1dcb1f1075cb34d3755474d900"}, - {file = "pyobjc_framework_Cocoa-10.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bf9020e85ead569021b15272dcd90207aab6c754093f520b11d4210a2efbdd06"}, + {file = "pyobjc-framework-Cocoa-10.1.tar.gz", hash = "sha256:8faaf1292a112e488b777d0c19862d993f3f384f3927dc6eca0d8d2221906a14"}, + {file = "pyobjc_framework_Cocoa-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e82c2e20b89811d92a7e6e487b6980f360b7c142e2576e90f0e7569caf8202b"}, + {file = "pyobjc_framework_Cocoa-10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0860a9beb7e5c72a1f575679a6d1428a398fa19ad710fb116df899972912e304"}, + {file = "pyobjc_framework_Cocoa-10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:34b791ea740e1afce211f19334e45469fea9a48d8fce5072e146199fd19ff49f"}, + {file = "pyobjc_framework_Cocoa-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1398c1a9bebad1a0f2549980e20f4aade00c341b9bac56b4493095a65917d34a"}, + {file = "pyobjc_framework_Cocoa-10.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:22be21226e223d26c9e77645564225787f2b12a750dd17c7ad99c36f428eda14"}, + {file = "pyobjc_framework_Cocoa-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0280561f4fb98a864bd23f2c480d907b0edbffe1048654f5dfab160cea8198e6"}, ] [package.dependencies] -pyobjc-core = ">=10.0" +pyobjc-core = ">=10.1" [[package]] name = "pypiwin32" @@ -555,6 +651,24 @@ files = [ [package.dependencies] pywin32 = ">=223" +[[package]] +name = "pyright" +version = "1.1.353" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.353-py3-none-any.whl", hash = "sha256:8d7e6719d0be4fd9f4a37f010237c6a74d91ec1e7c81de634c2f3f9965f8ab43"}, + {file = "pyright-1.1.353.tar.gz", hash = "sha256:24343bbc2a4f997563f966b6244a2e863473f1d85af6d24abcb366fcbb4abca9"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" + +[package.extras] +all = ["twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] + [[package]] name = "pyserial" version = "3.5" @@ -624,6 +738,32 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "ruff" +version = "0.3.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b82e3937d0d76554cd5796bc3342a7d40de44494d29ff490022d7a52c501744"}, + {file = "ruff-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ae7954c8f692b70e6a206087ae3988acc9295d84c550f8d90b66c62424c16771"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b730f56ccf91225da0f06cfe421e83b8cc27b2a79393db9c3df02ed7e2bbc01"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c78bfa85637668f47bd82aa2ae17de2b34221ac23fea30926f6409f9e37fc927"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6abaad602d6e6daaec444cbf4d9364df0a783e49604c21499f75bb92237d4af"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0c21b6914c3c9a25a59497cbb1e5b6c2d8d9beecc9b8e03ee986e24eee072e"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434c3fc72e6311c85cd143c4c448b0e60e025a9ac1781e63ba222579a8c29200"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78a7025e6312cbba496341da5062e7cdd47d95f45c1b903e635cdeb1ba5ec2b9"}, + {file = "ruff-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b02bb46f1a79b0c1fa93f6495bc7e77e4ef76e6c28995b4974a20ed09c0833"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11b5699c42f7d0b771c633d620f2cb22e727fb226273aba775a91784a9ed856c"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:54e5dca3e411772b51194b3102b5f23b36961e8ede463776b289b78180df71a0"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:951efb610c5844e668bbec4f71cf704f8645cf3106e13f283413969527ebfded"}, + {file = "ruff-0.3.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:09c7333b25e983aabcf6e38445252cff0b4745420fc3bda45b8fce791cc7e9ce"}, + {file = "ruff-0.3.1-py3-none-win32.whl", hash = "sha256:d937f9b99ebf346e0606c3faf43c1e297a62ad221d87ef682b5bdebe199e01f6"}, + {file = "ruff-0.3.1-py3-none-win_amd64.whl", hash = "sha256:c0318a512edc9f4e010bbaab588b5294e78c5cdc9b02c3d8ab2d77c7ae1903e3"}, + {file = "ruff-0.3.1-py3-none-win_arm64.whl", hash = "sha256:d3b60e44240f7e903e6dbae3139a65032ea4c6f2ad99b6265534ff1b83c20afa"}, + {file = "ruff-0.3.1.tar.gz", hash = "sha256:d30db97141fc2134299e6e983a6727922c9e03c031ae4883a6d69461de722ae7"}, +] + [[package]] name = "screeninfo" version = "0.8.1" @@ -639,6 +779,22 @@ files = [ Cython = {version = "*", markers = "sys_platform == \"darwin\""} pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""} +[[package]] +name = "setuptools" +version = "69.1.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "tomli" version = "2.0.1" @@ -652,28 +808,29 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -683,4 +840,4 @@ dev = ["poethepoet", "screeninfo"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "2423540103d5de72324a794121c6c003c24609982bc9c931aa7368d9a7e0f89d" +content-hash = "30803827fd5b16620a5d0f114b6b37bc56a07f7546d8a8e70eeeca2b78e82ae6" diff --git a/pyproject.toml b/pyproject.toml index 9f27852..d7265d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,24 +1,18 @@ [tool.poetry] name = "headless-kivy-pi" -version = "0.6.0" +version = "0.6.1" description = "Headless renderer for Kivy framework on Raspberry Pi" authors = ["Sassan Haradji "] license = "Apache-2.0" readme = "README.md" packages = [{ include = "headless_kivy_pi" }] - -[[tool.poetry.source]] -name = "PyPI" -priority = "primary" - - [tool.poetry.dependencies] python = "^3.11" typing-extensions = "^4.8.0" -numpy = { version = "^1.24.2", source = 'PyPI' } -kivy = { version = "^2.2.1", source = 'PyPI' } +numpy = { version = "^1.24.2" } +kivy = { version = "^2.2.1" } adafruit-circuitpython-rgb-display = { version = "^3.11.0", markers = "platform_machine=='aarch64'" } adafruit-circuitpython-aw9523 = { version = "^1.1.7", markers = "platform_machine=='aarch64'" } @@ -28,21 +22,26 @@ screeninfo = { version = "^0.8.1", optional = true } [tool.poetry.extras] dev = ['poethepoet', 'screeninfo'] +[tool.poetry.group.dev.dependencies] +ruff = "^0.3.1" +pyright = "^1.1.353" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poe.tasks] -lint = "pyright -p pyproject.toml ." +lint = "ruff check . --unsafe-fixes" +typecheck = "pyright -p pyproject.toml ." +sanity = ["typecheck", "lint"] [tool.ruff] -select = ['ALL'] -ignore = [] - -fixable = ['ALL'] -unfixable = [] +lint.select = ['ALL'] +lint.ignore = ['INP001', 'PLR0911', 'D203', 'D213'] +lint.fixable = ['ALL'] +lint.unfixable = [] -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] docstring-quotes = "double" inline-quotes = "single" multiline-quotes = "double"