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/.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 df28583..ac22d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,31 @@ # 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 + 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 + 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 +109,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 +119,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..8444a1e 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( @@ -35,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, @@ -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): @@ -79,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. @@ -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..1c3fb75 100644 --- a/headless_kivy_pi/__init__.py +++ b/headless_kivy_pi/__init__.py @@ -14,70 +14,41 @@ """ 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 typing_extensions import Any - -from headless_kivy_pi.constants import ( - BAUDRATE, - BITS_PER_BYTE, - BYTES_PER_PIXEL, - CLEAR_AT_EXIT, - IS_RPI, -) +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 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,30 +59,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 HeadlessWidget._is_setup_headless_called: - msg = ( - 'You need to run `setup_headless` before instantiating `HeadlessWidget`' - ) - 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: + if not IS_TEST_ENVIRONMENT: + config.check_initialized() + + self.should_ignore_hash = False + + __import__('kivy.core.window') + + 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: @@ -127,22 +92,24 @@ 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(), + self.render_on_display, 1 / self.fps, interval=True, ) self.render_trigger() - App.get_running_app().bind( - on_stop=lambda _: self.render_trigger.cancel(), - ) + app = App.get_running_app() + + def clear(*_: object) -> None: + self.render_trigger.cancel() + + if app: + app.bind(on_stop=clear) 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 @@ -152,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 @@ -187,62 +154,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()) @@ -259,22 +208,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() @@ -282,14 +229,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() @@ -297,11 +244,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() @@ -317,81 +264,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, - ) - - 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 kivy.core.window import Window - 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..a9536ce --- /dev/null +++ b/headless_kivy_pi/config.py @@ -0,0 +1,267 @@ +# pyright: reportMissingImports=false +"""Implement `setup_headless_kivy`, it configures headless-kivy-pi.""" +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/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/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/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 6472243..d7265d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,24 +1,18 @@ [tool.poetry] name = "headless-kivy-pi" -version = "0.5.10" +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,24 +22,35 @@ 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 = [] +lint.select = ['ALL'] +lint.ignore = ['INP001', 'PLR0911', 'D203', 'D213'] +lint.fixable = ['ALL'] +lint.unfixable = [] -fixable = ['ALL'] -unfixable = [] - -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] docstring-quotes = "double" inline-quotes = "single" multiline-quotes = "double" [tool.ruff.format] quote-style = 'single' + +[tool.isort] +profile = "black" + +[tool.pyright] +exclude = ['typings']