Skip to content

Commit

Permalink
Update minimal Python version to 3.11:
Browse files Browse the repository at this point in the history
- Switch from "toml" to "tomlib + tomli-w"
- Fixes an issue reading mixed types lists uiri/toml#270 (and empty sets writing `[false,]` but that has no open issues)
- Use StrEnum to simplify `CaptureMethodEnum`
  • Loading branch information
Avasam committed Sep 22, 2024
1 parent f890ae3 commit 5bb0c85
Show file tree
Hide file tree
Showing 12 changed files with 36 additions and 51 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint-and-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
# Ruff is version and platform sensible
matrix:
os: [windows-latest, ubuntu-22.04]
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12"]
steps:
- name: Checkout ${{ github.repository }}/${{ github.ref }}
uses: actions/checkout@v4
Expand All @@ -66,7 +66,7 @@ jobs:
# Pyright is version and platform sensible
matrix:
os: [windows-latest, ubuntu-22.04]
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.11", "3.12"]
steps:
- name: Checkout ${{ github.repository }}/${{ github.ref }}
uses: actions/checkout@v4
Expand Down
1 change: 0 additions & 1 deletion .sonarcloud.properties

This file was deleted.

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ To understand how to use AutoSplit and how it works in-depth, please read the [t
- Should work on Ubuntu 20.04+ (Only tested on Ubuntu 22.04)
- Wayland is not currently supported
- WSL2/WSLg requires an additional Desktop Environment, external X11 server, and/or systemd
- Python 3.10+ (Not required for normal use. Refer to the [build instructions](/docs/build%20instructions.md) if you'd like run the application directly in Python).
- Python 3.11+ (Not required for normal use. Refer to the [build instructions](/docs/build%20instructions.md) if you'd like run the application directly in Python).

## Timer Integration

Expand Down Expand Up @@ -102,6 +102,8 @@ Not a developer? You can still help through the following methods:
- <https://github.com/opencv/opencv/issues/23906>
- <https://github.com/pywinrt/python-winsdk/issues/11>
- <https://github.com/pyinstaller/pyinstaller-hooks-contrib/issues/807>
- <https://github.com/hukkin/tomli-w/pull/46>
- <https://github.com/uiri/toml/issues/270>
- <https://github.com/microsoft/vscode/issues/40239>
- <https://github.com/microsoft/vscode/issues/168411>
- <https://github.com/python/mypy/issues/6700>
Expand Down
2 changes: 1 addition & 1 deletion docs/build instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

### All platforms

- [Python](https://www.python.org/downloads/) 3.10+.
- [Python](https://www.python.org/downloads/) 3.11+.
- [Node](https://nodejs.org) is optional, but required for complete linting.
- Alternatively you can install the [pyright python wrapper](https://pypi.org/project/pyright/) which has a bit of an overhead delay.
- [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell) is used to run all the scripts
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file
[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.10"
pythonVersion = "3.11"
# Prefer `pyright: ignore`
enableTypeIgnoreComments = false

Expand Down
2 changes: 1 addition & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# These configs are incompatible with ruff<0.5.7

# https://docs.astral.sh/ruff/configuration/
target-version = "py310" # Change this to the oldest supported version by your application
target-version = "py311" # Change this to the oldest supported version by your application
line-length = 100
preview = true

Expand Down
12 changes: 6 additions & 6 deletions scripts/python_build_from_source_linux.bash
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ sudo apt update
sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev tk-dev

# Download Python binary package:
wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz
wget https://www.python.org/ftp/python/3.11.10/Python-3.11.10.tgz

# Unzip the package:
tar -xzf Python-3.10.13.tgz
tar -xzf Python-3.11.10.tgz

# Execute configure script
cd Python-3.10.13
cd Python-3.11.10
./configure --enable-optimizations --enable-shared

# Build Python 3.10
# Build Python 3.11
make -j 2

# Install Python 3.10
# Install Python 3.11
sudo make install

# Verify the installation
python3.10 -V
python3.11 -V

echo "If Python version did not print, you may need to stop active processes"
2 changes: 1 addition & 1 deletion scripts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ PyWinCtl>=0.0.42 # py.typed
# When needed, dev builds can be found at https://download.qt.io/snapshots/ci/pyside/dev?C=M;O=D
PySide6-Essentials>=6.6.0 # Python 3.12 support
scipy>=1.11.2 # Python 3.12 support
toml
tomli-w
typing-extensions>=4.4.0 # @override decorator support
#
# Build and compile resources
Expand Down
6 changes: 3 additions & 3 deletions src/AutoSplitImage.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os
import tomllib
from enum import IntEnum, auto
from math import sqrt
from typing import TYPE_CHECKING

import cv2
import numpy as np
import toml
from cv2.typing import MatLike

import error_messages
Expand Down Expand Up @@ -133,8 +133,8 @@ def __parse_text_file(self, path: str):
error_messages.tesseract_missing(path)
return

with open(path, encoding="utf-8") as f:
data = toml.load(f)
with open(path, mode="rb") as f:
data = tomllib.load(f)

self.texts = [text.lower().strip() for text in data["texts"]]
self.__rect = (data["left"], data["right"], data["top"], data["bottom"])
Expand Down
30 changes: 6 additions & 24 deletions src/capture_method/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import sys
from collections import OrderedDict
from dataclasses import dataclass
from enum import Enum, EnumMeta, auto, unique
from enum import EnumMeta, StrEnum, auto, unique
from itertools import starmap
from typing import TYPE_CHECKING, TypedDict, cast
from typing import TYPE_CHECKING, Never, TypedDict, cast

from typing_extensions import Never, override
from typing_extensions import override

from capture_method.CaptureMethodBase import CaptureMethodBase
from capture_method.VideoCaptureDeviceCaptureMethod import VideoCaptureDeviceCaptureMethod
Expand Down Expand Up @@ -49,7 +49,7 @@ class Region(TypedDict):
height: int


class CaptureMethodEnumMeta(EnumMeta):
class ContainerEnumMeta(EnumMeta):
# Allow checking if simple string is enum
@override
def __contains__(cls, other: object):
Expand All @@ -62,26 +62,8 @@ def __contains__(cls, other: object):

@unique
# TODO: Try StrEnum in Python 3.11
class CaptureMethodEnum(Enum, metaclass=CaptureMethodEnumMeta):
# Allow TOML to save as a simple string
@override
def __repr__(self):
return self.value

# Allow direct comparison with strings
@override
def __eq__(self, other: object):
if isinstance(other, str):
return self.value == other
if isinstance(other, Enum):
return self.value == other.value
return other == self

# Restore hashing functionality for use in Maps
@override
def __hash__(self):
return self.value.__hash__()

class CaptureMethodEnum(StrEnum, metaclass=ContainerEnumMeta):
# Capitalize the string value from auto()
@override
@staticmethod
def _generate_next_value_(
Expand Down
2 changes: 1 addition & 1 deletion src/menu_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def __setup_bindings(self):

def add_or_del(checked: Literal[0, 2], command: CommandStr = command):
if checked:
_screenshot_on_setting.add(command)
_screenshot_on_setting.append(command)
else:
_screenshot_on_setting.remove(command)

Expand Down
20 changes: 11 additions & 9 deletions src/user_profile.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import os
import tomllib
from copy import deepcopy
from typing import TYPE_CHECKING, TypedDict, cast

import toml
import tomli_w
from PySide6 import QtCore, QtWidgets
from typing_extensions import deprecated, override

Expand Down Expand Up @@ -40,7 +41,7 @@ class UserProfileDict(TypedDict):
split_image_directory: str
screenshot_directory: str
open_screenshot: bool
screenshot_on: set[CommandStr]
screenshot_on: list[CommandStr]
captured_window_title: str
capture_region: Region

Expand Down Expand Up @@ -73,7 +74,7 @@ def copy():
split_image_directory="",
screenshot_directory="",
open_screenshot=True,
screenshot_on=set(),
screenshot_on=[],
captured_window_title="",
capture_region=Region(x=0, y=0, width=1, height=1),
)
Expand Down Expand Up @@ -112,8 +113,9 @@ def save_settings_as(autosplit: "AutoSplit"):

def __save_settings_to_file(autosplit: "AutoSplit", save_settings_file_path: str):
# Save settings to a .toml file
with open(save_settings_file_path, "w", encoding="utf-8") as file:
toml.dump(autosplit.settings_dict, file)
with open(save_settings_file_path, "wb") as file:
# https://github.com/hukkin/tomli-w/pull/46
tomli_w.dump(autosplit.settings_dict, file) # pyright: ignore[reportArgumentType]
autosplit.last_saved_settings = deepcopy(autosplit.settings_dict)
autosplit.last_successfully_loaded_settings_file_path = save_settings_file_path
return save_settings_file_path
Expand All @@ -132,14 +134,14 @@ def __load_settings_from_file(autosplit: "AutoSplit", load_settings_file_path: s
settings_widget.close()

try:
with open(load_settings_file_path, encoding="utf-8") as file:
with open(load_settings_file_path, mode="rb") as file:
# Casting here just so we can build an actual UserProfileDict once we're done validating
# Fallback to default settings if some are missing from the file.
# This happens when new settings are added.
loaded_settings = DEFAULT_PROFILE | cast(UserProfileDict, toml.load(file))
loaded_settings = DEFAULT_PROFILE | cast(UserProfileDict, tomllib.load(file))

# TODO: Data Validation / fallbacks ?
loaded_settings["screenshot_on"] = set(loaded_settings["screenshot_on"])
loaded_settings["screenshot_on"] = list(set(loaded_settings["screenshot_on"]))
autosplit.settings_dict = UserProfileDict(**loaded_settings)
autosplit.last_saved_settings = deepcopy(autosplit.settings_dict)

Expand All @@ -148,7 +150,7 @@ def __load_settings_from_file(autosplit: "AutoSplit", load_settings_file_path: s
autosplit.width_spinbox.setValue(autosplit.settings_dict["capture_region"]["width"])
autosplit.height_spinbox.setValue(autosplit.settings_dict["capture_region"]["height"])
autosplit.split_image_folder_input.setText(autosplit.settings_dict["split_image_directory"])
except (FileNotFoundError, MemoryError, TypeError, toml.TomlDecodeError):
except (FileNotFoundError, MemoryError, TypeError, tomllib.TOMLDecodeError):
autosplit.show_error_signal.emit(error_messages.invalid_settings)
return False

Expand Down

0 comments on commit 5bb0c85

Please sign in to comment.