general: python3.9 reached EOL, switch min version
also enable 3.13 on CI
This commit is contained in:
parent
a8f86e32b9
commit
25a6bcd966
8 changed files with 36 additions and 53 deletions
11
.github/workflows/main.yml
vendored
11
.github/workflows/main.yml
vendored
|
@ -21,19 +21,20 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
|
||||
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
||||
exclude: [
|
||||
# windows runners are pretty scarce, so let's only run lowest and highest python version
|
||||
{platform: windows-latest, python-version: '3.9' },
|
||||
{platform: windows-latest, python-version: '3.10'},
|
||||
{platform: windows-latest, python-version: '3.11'},
|
||||
{platform: windows-latest, python-version: '3.12'},
|
||||
|
||||
# same, macos is a bit too slow and ubuntu covers python quirks well
|
||||
{platform: macos-latest , python-version: '3.9' },
|
||||
{platform: macos-latest , python-version: '3.10' },
|
||||
{platform: macos-latest , python-version: '3.11' },
|
||||
{platform: macos-latest , python-version: '3.12' },
|
||||
]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
@ -63,11 +64,13 @@ jobs:
|
|||
- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
include-hidden-files: true
|
||||
name: .coverage.mypy-misc_${{ matrix.platform }}_${{ matrix.python-version }}
|
||||
path: .coverage.mypy-misc/
|
||||
- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
include-hidden-files: true
|
||||
name: .coverage.mypy-core_${{ matrix.platform }}_${{ matrix.python-version }}
|
||||
path: .coverage.mypy-core/
|
||||
|
||||
|
@ -81,7 +84,7 @@ jobs:
|
|||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.8'
|
||||
python-version: '3.10'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
|
@ -171,8 +171,6 @@ See https://github.com/karlicoss/HPI/blob/master/doc/SETUP.org#setting-up-module
|
|||
# use a temporary directory, useful because
|
||||
# - compileall ignores -B, so always craps with .pyc files (annoyng on RO filesystems)
|
||||
# - compileall isn't following symlinks, just silently ignores them
|
||||
# note: ugh, annoying that copytree requires a non-existing dir before 3.8.
|
||||
# once we have min version 3.8, can use dirs_exist_ok=True param
|
||||
tdir = Path(td) / 'cfg'
|
||||
# NOTE: compileall still returns code 0 if the path doesn't exist..
|
||||
# but in our case hopefully it's not an issue
|
||||
|
@ -181,7 +179,7 @@ See https://github.com/karlicoss/HPI/blob/master/doc/SETUP.org#setting-up-module
|
|||
try:
|
||||
# this will resolve symlinks when copying
|
||||
# should be under try/catch since might fail if some symlinks are missing
|
||||
shutil.copytree(cfg_path, tdir)
|
||||
shutil.copytree(cfg_path, tdir, dirs_exist_ok=True)
|
||||
check_call(cmd)
|
||||
info('syntax check: ' + ' '.join(cmd))
|
||||
except Exception as e:
|
||||
|
|
|
@ -210,7 +210,7 @@ class ZipPath(zipfile_Path):
|
|||
|
||||
def iterdir(self) -> Iterator[ZipPath]:
|
||||
for s in self._as_dir().iterdir():
|
||||
yield ZipPath(s.root, s.at) # type: ignore[attr-defined]
|
||||
yield ZipPath(s.root, s.at)
|
||||
|
||||
@property
|
||||
def stem(self) -> str:
|
||||
|
|
|
@ -21,25 +21,19 @@ if not TYPE_CHECKING:
|
|||
# TODO warn here?
|
||||
source.backup(dest, **kwargs)
|
||||
|
||||
# keeping for runtime backwards compatibility (added in 3.9)
|
||||
@deprecated('use .removeprefix method on string directly instead')
|
||||
def removeprefix(text: str, prefix: str) -> str:
|
||||
return text.removeprefix(prefix)
|
||||
|
||||
## can remove after python3.9 (although need to keep the method itself for bwd compat)
|
||||
def removeprefix(text: str, prefix: str) -> str:
|
||||
if text.startswith(prefix):
|
||||
return text[len(prefix) :]
|
||||
return text
|
||||
@deprecated('use .removesuffix method on string directly instead')
|
||||
def removesuffix(text: str, suffix: str) -> str:
|
||||
return text.removesuffix(suffix)
|
||||
##
|
||||
|
||||
def removesuffix(text: str, suffix: str) -> str:
|
||||
if text.endswith(suffix):
|
||||
return text[:-len(suffix)]
|
||||
return text
|
||||
##
|
||||
|
||||
## used to have compat function before 3.8 for these, keeping for runtime back compatibility
|
||||
if not TYPE_CHECKING:
|
||||
## used to have compat function before 3.8 for these, keeping for runtime back compatibility
|
||||
from functools import cached_property
|
||||
from typing import Literal, Protocol, TypedDict
|
||||
else:
|
||||
from typing_extensions import Literal, Protocol, TypedDict
|
||||
##
|
||||
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ Schema = Any
|
|||
def _as_columns(s: Schema) -> Dict[str, Type]:
|
||||
# todo would be nice to extract properties; add tests for this as well
|
||||
if dataclasses.is_dataclass(s):
|
||||
return {f.name: f.type for f in dataclasses.fields(s)}
|
||||
return {f.name: f.type for f in dataclasses.fields(s)} # type: ignore[misc] # ugh, why mypy thinks f.type can return str??
|
||||
# else must be NamedTuple??
|
||||
# todo assert my.core.common.is_namedtuple?
|
||||
return getattr(s, '_field_types')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
from concurrent.futures import Executor, Future
|
||||
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar
|
||||
from typing import Any, Callable, Optional, TypeVar
|
||||
|
||||
from ..compat import ParamSpec
|
||||
|
||||
|
@ -19,33 +19,21 @@ class DummyExecutor(Executor):
|
|||
self._shutdown = False
|
||||
self._max_workers = max_workers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if sys.version_info[:2] <= (3, 8):
|
||||
# 3.8 doesn't support ParamSpec as Callable arg :(
|
||||
# and any attempt to type results in incompatible supertype.. so whatever
|
||||
def submit(self, fn, *args, **kwargs): ...
|
||||
def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]:
|
||||
if self._shutdown:
|
||||
raise RuntimeError('cannot schedule new futures after shutdown')
|
||||
|
||||
f: Future[Any] = Future()
|
||||
try:
|
||||
result = fn(*args, **kwargs)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except BaseException as e:
|
||||
f.set_exception(e)
|
||||
else:
|
||||
f.set_result(result)
|
||||
|
||||
def submit(self, fn: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs) -> Future[_T]: ...
|
||||
|
||||
else:
|
||||
|
||||
def submit(self, fn, *args, **kwargs):
|
||||
if self._shutdown:
|
||||
raise RuntimeError('cannot schedule new futures after shutdown')
|
||||
|
||||
f: Future[Any] = Future()
|
||||
try:
|
||||
result = fn(*args, **kwargs)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except BaseException as e:
|
||||
f.set_exception(e)
|
||||
else:
|
||||
f.set_result(result)
|
||||
|
||||
return f
|
||||
return f
|
||||
|
||||
def shutdown(self, wait: bool = True, **kwargs) -> None: # noqa: FBT001,FBT002,ARG002
|
||||
self._shutdown = True
|
||||
|
|
|
@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|||
from typing import TYPE_CHECKING, Any, Iterable, Iterator
|
||||
|
||||
from my.core import Res, Stats, datetime_aware, make_logger, stat, warnings
|
||||
from my.core.compat import deprecated, removeprefix, removesuffix
|
||||
from my.core.compat import deprecated
|
||||
|
||||
logger = make_logger(__name__)
|
||||
|
||||
|
@ -117,10 +117,10 @@ def _watched() -> Iterator[Res[Watched]]:
|
|||
|
||||
# all titles contain it, so pointless to include 'Watched '
|
||||
# also compatible with legacy titles
|
||||
title = removeprefix(title, 'Watched ')
|
||||
title = title.removeprefix('Watched ')
|
||||
|
||||
# watches originating from some activity end with this, remove it for consistency
|
||||
title = removesuffix(title, ' - YouTube')
|
||||
title = title.removesuffix(' - YouTube')
|
||||
|
||||
if YOUTUBE_VIDEO_LINK not in url:
|
||||
if 'youtube.com/post/' in url:
|
||||
|
|
2
setup.py
2
setup.py
|
@ -44,7 +44,7 @@ def main() -> None:
|
|||
author_email='karlicoss@gmail.com',
|
||||
description='A Python interface to my life',
|
||||
|
||||
python_requires='>=3.8',
|
||||
python_requires='>=3.9',
|
||||
install_requires=INSTALL_REQUIRES,
|
||||
extras_require={
|
||||
'testing': [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue