-
Notifications
You must be signed in to change notification settings - Fork 89
Migrate typechecking to ty #242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3c4b677
9c6b498
455a2a2
7dcbf49
d1f4b79
3090920
96c86dd
2489a14
30eb4ba
7c6daf3
05022e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,10 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import threading | ||
| from typing import Callable | ||
|
|
||
| from .packages.special.main import COMMANDS | ||
| from collections import OrderedDict | ||
| from typing import Callable, cast | ||
|
|
||
| from .packages.special.main import COMMANDS | ||
| from .sqlcompleter import SQLCompleter | ||
| from .sqlexecute import SQLExecute | ||
|
|
||
|
|
@@ -77,7 +76,9 @@ def _bg_refresh( | |
|
|
||
| # If callbacks is a single function then push it into a list. | ||
| if callable(callbacks): | ||
| callbacks = [callbacks] | ||
| callbacks_list: list[Callable] = [callbacks] | ||
| else: | ||
| callbacks_list = list(cast(list[Callable], callbacks)) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ty was complaining about this, hence modified it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is weird. Wouldn't this work?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately no: https://play.ty.dev/0c4ac49a-58d7-4032-8d4b-453fc395b34a :-( |
||
|
|
||
| while 1: | ||
| for refresher in self.refreshers.values(): | ||
|
|
@@ -94,7 +95,7 @@ def _bg_refresh( | |
| # break statement. | ||
| continue | ||
|
|
||
| for callback in callbacks: | ||
| for callback in callbacks_list: | ||
| callback(completer) | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,10 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import errno | ||
| import shutil | ||
| import os | ||
| import platform | ||
| from os.path import expanduser, exists, dirname | ||
|
|
||
| import shutil | ||
| from os.path import dirname, exists, expanduser | ||
|
|
||
| from configobj import ConfigObj | ||
|
|
||
|
|
@@ -55,7 +54,7 @@ def upgrade_config(config: str, def_config: str) -> None: | |
| def get_config(liteclirc_file: str | None = None) -> ConfigObj: | ||
| from litecli import __file__ as package_root | ||
|
|
||
| package_root = os.path.dirname(package_root) | ||
| package_root = os.path.dirname(str(package_root)) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ty doesn't like package_root as it comes from import reference, hence converting to string. |
||
|
|
||
| liteclirc_file = liteclirc_file or f"{config_location()}config" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,17 +13,17 @@ | |
| from io import open | ||
|
|
||
| try: | ||
| from sqlean import OperationalError, sqlite_version | ||
| from sqlean import OperationalError, sqlite_version # type: ignore[import-untyped] | ||
| except ImportError: | ||
| from sqlite3 import OperationalError, sqlite_version | ||
| from time import time | ||
| from typing import Any, Iterable | ||
| from typing import Any, Generator, Iterable, cast | ||
|
|
||
| import click | ||
| import sqlparse | ||
| from cli_helpers.tabular_output import TabularOutputFormatter, preprocessors | ||
| from prompt_toolkit.auto_suggest import AutoSuggestFromHistory | ||
| from prompt_toolkit.completion import DynamicCompleter | ||
| from prompt_toolkit.completion import Completion, DynamicCompleter | ||
| from prompt_toolkit.document import Document | ||
| from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode | ||
| from prompt_toolkit.filters import HasFocus, IsDone | ||
|
|
@@ -35,8 +35,6 @@ | |
| ) | ||
| from prompt_toolkit.lexers import PygmentsLexer | ||
| from prompt_toolkit.shortcuts import CompleteStyle, PromptSession | ||
| from typing import cast | ||
| from prompt_toolkit.completion import Completion | ||
|
|
||
| from .__init__ import __version__ | ||
| from .clibuffer import cli_is_multiline | ||
|
|
@@ -53,8 +51,6 @@ | |
| from .sqlcompleter import SQLCompleter | ||
| from .sqlexecute import SQLExecute | ||
|
|
||
| click.disable_unicode_literals_warning = True | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is no longer needed in Python 3.9 |
||
|
|
||
| # Query tuples are used for maintaining history | ||
| Query = namedtuple("Query", ["query", "successful", "mutating"]) | ||
|
|
||
|
|
@@ -84,7 +80,8 @@ def __init__( | |
| self.key_bindings = c["main"]["key_bindings"] | ||
| special.set_favorite_queries(self.config) | ||
| self.formatter = TabularOutputFormatter(format_name=c["main"]["table_format"]) | ||
| self.formatter.litecli = self | ||
| # self.formatter.litecli = self, ty raises unresolved-attribute, hence use dynamic assignment | ||
| setattr(self.formatter, "litecli", self) | ||
| self.syntax_style = c["main"]["syntax_style"] | ||
| self.less_chatty = c["main"].as_bool("less_chatty") | ||
| self.show_bottom_toolbar = c["main"].as_bool("show_bottom_toolbar") | ||
|
|
@@ -181,7 +178,7 @@ def register_special_commands(self) -> None: | |
| case_sensitive=True, | ||
| ) | ||
|
|
||
| def change_table_format(self, arg: str, **_: Any) -> Iterable[tuple]: | ||
| def change_table_format(self, arg: str, **_: Any) -> Generator[tuple[None, None, None, str], None, None]: | ||
| try: | ||
| self.formatter.format_name = arg | ||
| yield (None, None, None, "Changed table format to {}".format(arg)) | ||
|
|
@@ -200,11 +197,14 @@ def change_db(self, arg: str | None, **_: Any) -> Iterable[tuple]: | |
| self.sqlexecute.connect(database=arg) | ||
|
|
||
| self.refresh_completions() | ||
| # guard so that ty doesn't complain | ||
| dbname = self.sqlexecute.dbname if self.sqlexecute is not None else "" | ||
|
|
||
| yield ( | ||
| None, | ||
| None, | ||
| None, | ||
| 'You are now connected to database "%s"' % (self.sqlexecute.dbname), | ||
| 'You are now connected to database "%s"' % (dbname), | ||
| ) | ||
|
|
||
| def execute_from_file(self, arg: str | None, **_: Any) -> Iterable[tuple[Any, ...]]: | ||
|
|
@@ -303,7 +303,7 @@ def get(key: str) -> str | None: | |
|
|
||
| return {x: get(x) for x in keys} | ||
|
|
||
| def connect(self, database: str = "") -> None: | ||
| def connect(self, database: str | None = "") -> None: | ||
| cnf: dict[str, str | None] = {"database": None} | ||
|
|
||
| cnf = self.read_my_cnf_files(cnf.keys()) | ||
|
|
@@ -510,7 +510,8 @@ def one_iteration(text: str | None = None) -> None: | |
| successful = False | ||
| start = time() | ||
| res = sqlexecute.run(text) | ||
| self.formatter.query = text | ||
| # Set query attribute dynamically on formatter | ||
| setattr(self.formatter, "query", text) | ||
| successful = True | ||
| special.unset_once_if_written() | ||
| # Keep track of whether or not the query is mutating. In case | ||
|
|
@@ -522,7 +523,8 @@ def one_iteration(text: str | None = None) -> None: | |
| raise e | ||
| except KeyboardInterrupt: | ||
| try: | ||
| sqlexecute.conn.interrupt() | ||
| # since connection can be sqlite3 or sqlean, it's hard to annotate the type for interrupt. so ignore the type hint warning. | ||
| sqlexecute.conn.interrupt() # type: ignore[attr-defined] | ||
| except Exception as e: | ||
| self.echo( | ||
| "Encountered error while cancelling query: {}".format(e), | ||
|
|
@@ -755,6 +757,7 @@ def refresh_completions(self, reset: bool = False) -> list[tuple]: | |
| if reset: | ||
| with self._completer_lock: | ||
| self.completer.reset_completions() | ||
| assert self.sqlexecute is not None | ||
| self.completion_refresher.refresh( | ||
| self.sqlexecute, | ||
| self._on_completions_refreshed, | ||
|
|
@@ -815,7 +818,7 @@ def run_query(self, query: str, new_line: bool = True) -> None: | |
| results = self.sqlexecute.run(query) | ||
| for result in results: | ||
| title, cur, headers, status = result | ||
| self.formatter.query = query | ||
| setattr(self.formatter, "query", query) | ||
| output = self.format_output(title, cur, headers) | ||
| for line in output: | ||
| click.echo(line, nl=new_line) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,8 +4,8 @@ | |
| from typing import Generator, Iterable, Literal | ||
|
|
||
| import sqlparse | ||
| from sqlparse.sql import IdentifierList, Identifier, Function, Token, TokenList | ||
| from sqlparse.tokens import Keyword, DML, Punctuation | ||
| from sqlparse.sql import Function, Identifier, IdentifierList, Token, TokenList | ||
| from sqlparse.tokens import DML, Keyword, Punctuation | ||
|
|
||
| cleanup_regex: dict[str, re.Pattern[str]] = { | ||
| # This matches only alphanumerics and underscores. | ||
|
|
@@ -18,10 +18,10 @@ | |
| "all_punctuations": re.compile(r"([^\s]+)$"), | ||
| } | ||
|
|
||
| LAST_WORD_INCLUDE_TYPE = Literal["alphanum_underscore", "many_punctuations", "most_punctuations", "all_punctuations"] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ty later complains the value passed is str and so declare and import later. |
||
|
|
||
| def last_word( | ||
| text: str, include: Literal["alphanum_underscore", "many_punctuations", "most_punctuations", "all_punctuations"] = "alphanum_underscore" | ||
| ) -> str: | ||
|
|
||
| def last_word(text: str, include: LAST_WORD_INCLUDE_TYPE = "alphanum_underscore") -> str: | ||
| R""" | ||
| Find the last word in a sentence. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| # ruff: noqa | ||
|
|
||
| from __future__ import annotations | ||
| from types import FunctionType | ||
|
|
||
| from typing import Callable, Any | ||
|
|
||
|
|
@@ -9,11 +10,34 @@ | |
|
|
||
| def export(defn: Callable[..., Any]) -> Callable[..., Any]: | ||
| """Decorator to explicitly mark functions that are exposed in a lib.""" | ||
| globals()[defn.__name__] = defn | ||
| __all__.append(defn.__name__) | ||
| # ty, requires explict check for callable of tyep | function type to access __name__ | ||
| if isinstance(defn, (type, FunctionType)): | ||
| globals()[defn.__name__] = defn | ||
| __all__.append(defn.__name__) | ||
| return defn | ||
|
|
||
|
|
||
| from . import dbcommands | ||
| from . import iocommands | ||
| from . import llm | ||
| from . import utils | ||
| from .main import CommandNotFound, register_special_command, execute | ||
| from .iocommands import ( | ||
| set_favorite_queries, | ||
| editor_command, | ||
| get_filename, | ||
| get_editor_query, | ||
| open_external_editor, | ||
| is_expanded_output, | ||
| set_expanded_output, | ||
| write_tee, | ||
| unset_once_if_written, | ||
| unset_pipe_once_if_written, | ||
| disable_pager, | ||
| set_pager, | ||
| is_pager_enabled, | ||
| write_once, | ||
| write_pipe_once, | ||
| close_tee, | ||
| ) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ty doesn't like when these methods are accessed like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There may be scope to improve over all import systems. |
||
| from .llm import is_llm_command, handle_llm, FinishIteration | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| # -*- coding: utf-8 -*- | ||
| from __future__ import annotations | ||
|
|
||
| import builtins | ||
| from typing import Any, cast | ||
|
|
||
|
|
||
|
|
@@ -39,7 +40,7 @@ class FavoriteQueries(object): | |
| def __init__(self, config: Any) -> None: | ||
| self.config = config | ||
|
|
||
| def list(self) -> list[str]: | ||
| def list(self) -> builtins.list[str]: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ty is opinionated about this. astral-sh/ty#2035 |
||
| section = cast(dict[str, str], self.config.get(self.section_name, {})) | ||
| return list(section.keys()) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapping to accurate type so ty doesn't complain.