From 5daf7e7d2a9336c7d845a0378554431d0dcba793 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sun, 26 Nov 2023 17:12:46 -0600 Subject: [PATCH] Make it work on 3.9 again turns out I have some systems where I care about that :) --- pyproject.toml | 3 ++- src/chap/commands/ask.py | 4 ++-- src/chap/commands/tui.py | 4 ++-- src/chap/core.py | 24 +++++++++++++----------- src/chap/session.py | 6 +++--- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5744bca..20f2923 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,13 +20,14 @@ name="chap" authors = [{name = "Jeff Epler", email = "jepler@gmail.com"}] description = "Interact with the OpenAI ChatGPT API (and other text generators)" dynamic = ["readme","version","dependencies"] -requires-python = ">=3.10" +requires-python = ">=3.9" keywords = ["llm", "tui", "chatgpt"] classifiers = [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/src/chap/commands/ask.py b/src/chap/commands/ask.py index e76eb1b..f308372 100644 --- a/src/chap/commands/ask.py +++ b/src/chap/commands/ask.py @@ -4,7 +4,7 @@ import asyncio import sys -from typing import Iterable, Protocol +from typing import Iterable, Optional, Protocol import click import rich @@ -40,7 +40,7 @@ class DumbPrinter: class WrappingPrinter: - def __init__(self, width: int | None = None) -> None: + def __init__(self, width: Optional[int] = None) -> None: self._width = width or rich.get_console().width self._column = 0 self._line = "" diff --git a/src/chap/commands/tui.py b/src/chap/commands/tui.py index 374b18d..6d218bd 100644 --- a/src/chap/commands/tui.py +++ b/src/chap/commands/tui.py @@ -5,7 +5,7 @@ import asyncio import subprocess import sys -from typing import Any, cast +from typing import Any, Optional, cast from markdown_it import MarkdownIt from textual import work @@ -63,7 +63,7 @@ class Tui(App[None]): ] def __init__( - self, api: Backend | None = None, session: Session | None = None + self, api: Optional[Backend] = None, session: Optional[Session] = None ) -> None: super().__init__() self.api = api or get_api("lorem") diff --git a/src/chap/core.py b/src/chap/core.py index 357071e..bfc4b53 100644 --- a/src/chap/core.py +++ b/src/chap/core.py @@ -11,8 +11,7 @@ import pathlib import pkgutil import subprocess from dataclasses import MISSING, dataclass, fields -from types import UnionType -from typing import Any, AsyncGenerator, Callable, cast +from typing import Any, AsyncGenerator, Callable, Optional, Union, cast import click import platformdirs @@ -22,6 +21,9 @@ from typing_extensions import Protocol from . import backends, commands # pylint: disable=no-name-in-module from .session import Message, Session, System, session_from_file +# 3.9 compatible version of `from types import UnionType` +UnionType = type(Union[int, list]) + conversations_path = platformdirs.user_state_path("chap") / "conversations" conversations_path.mkdir(parents=True, exist_ok=True) @@ -54,14 +56,14 @@ class AutoAskMixin: # pylint: disable=too-few-public-methods return "".join(tokens) -def last_session_path() -> pathlib.Path | None: +def last_session_path() -> Optional[pathlib.Path]: result = max( conversations_path.glob("*.json"), key=lambda p: p.stat().st_mtime, default=None ) return result -def new_session_path(opt_path: pathlib.Path | None = None) -> pathlib.Path: +def new_session_path(opt_path: Optional[pathlib.Path] = None) -> pathlib.Path: return opt_path or conversations_path / ( datetime.datetime.now().isoformat().replace(":", "_") + ".json" ) @@ -95,7 +97,7 @@ def get_api(name: str = "openai_chatgpt") -> Backend: def do_session_continue( - ctx: click.Context, param: click.Parameter, value: pathlib.Path | None + ctx: click.Context, param: click.Parameter, value: Optional[pathlib.Path] ) -> None: if value is None: return @@ -293,18 +295,18 @@ def version_callback( # pylint: disable=unused-argument @dataclass class Obj: - api: Backend | None = None - system_message: str | None = None - session: list[Message] | None = None - session_filename: pathlib.Path | None = None + api: Optional[Backend] = None + system_message: Optional[str] = None + session: Optional[list[Message]] = None + session_filename: Optional[pathlib.Path] = None class MyCLI(click.MultiCommand): def make_context( self, - info_name: str | None, + info_name: Optional[str], args: list[str], - parent: click.Context | None = None, + parent: Optional[click.Context] = None, **extra: Any, ) -> click.Context: result = super().make_context(info_name, args, parent, obj=Obj(), **extra) diff --git a/src/chap/session.py b/src/chap/session.py index 3982325..f885abc 100644 --- a/src/chap/session.py +++ b/src/chap/session.py @@ -7,7 +7,7 @@ from __future__ import annotations import json import pathlib from dataclasses import asdict, dataclass -from typing import cast +from typing import Union, cast from typing_extensions import TypedDict @@ -65,11 +65,11 @@ def session_from_json(data: str) -> Session: return [Message(**mapping) for mapping in j] -def session_from_file(path: pathlib.Path | str) -> Session: +def session_from_file(path: Union[pathlib.Path, str]) -> Session: with open(path, "r", encoding="utf-8") as f: return session_from_json(f.read()) -def session_to_file(session: Session, path: pathlib.Path | str) -> None: +def session_to_file(session: Session, path: Union[pathlib.Path, str]) -> None: with open(path, "w", encoding="utf-8") as f: f.write(session_to_json(session))