diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 81597f04..e107b485 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,23 +1,23 @@ repos: - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.274 + rev: v0.0.282 hooks: - id: ruff args: [ --fix, --exit-non-zero-on-fix ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.4.0 + rev: v1.4.1 hooks: - id: mypy additional_dependencies: - click - duckdb>=0.8.0 - shandy-sqlfmt[jinjafmt] - - textual - - textual-textarea>=0.1.2 + - textual>=0.31.0 + - textual-textarea>=0.3.3 - pytest args: - "--disallow-untyped-calls" diff --git a/poetry.lock b/poetry.lock index 5617f41c..4dbd05d0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -112,6 +113,7 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -126,6 +128,7 @@ frozenlist = ">=1.1.0" name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -137,6 +140,7 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -155,6 +159,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "black" version = "23.7.0" description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -201,6 +206,7 @@ uvloop = ["uvloop (>=0.15.2)"] name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -212,6 +218,7 @@ files = [ name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -296,6 +303,7 @@ files = [ name = "click" version = "8.1.6" description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -310,6 +318,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -321,6 +330,7 @@ files = [ name = "distlib" version = "0.3.7" description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" files = [ @@ -332,6 +342,7 @@ files = [ name = "duckdb" version = "0.8.1" description = "DuckDB embedded database" +category = "main" optional = false python-versions = "*" files = [ @@ -393,6 +404,7 @@ files = [ name = "exceptiongroup" version = "1.1.2" description = "Backport of PEP 654 (exception groups)" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -407,6 +419,7 @@ test = ["pytest (>=6)"] name = "filelock" version = "3.12.2" description = "A platform independent file lock." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -422,6 +435,7 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "p name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -492,6 +506,7 @@ files = [ name = "identify" version = "2.5.26" description = "File identification library for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -506,6 +521,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -517,6 +533,7 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -536,6 +553,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -547,6 +565,7 @@ files = [ name = "linkify-it-py" version = "2.0.2" description = "Links recognition library with FULL unicode support." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -567,6 +586,7 @@ test = ["coverage", "pytest", "pytest-cov"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -593,6 +613,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdit-py-plugins" version = "0.4.0" description = "Collection of plugins for markdown-it-py" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -612,6 +633,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -623,6 +645,7 @@ files = [ name = "msgpack" version = "1.0.5" description = "MessagePack serializer" +category = "dev" optional = false python-versions = "*" files = [ @@ -695,6 +718,7 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -778,6 +802,7 @@ files = [ name = "mypy" version = "1.4.1" description = "Optional static typing for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -824,6 +849,7 @@ reports = ["lxml"] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -835,6 +861,7 @@ files = [ name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -849,6 +876,7 @@ setuptools = "*" name = "packaging" version = "23.1" description = "Core utilities for Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -858,34 +886,37 @@ files = [ [[package]] name = "pathspec" -version = "0.11.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "platformdirs" -version = "3.9.1" +version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.9.1-py3-none-any.whl", hash = "sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f"}, - {file = "platformdirs-3.9.1.tar.gz", hash = "sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421"}, + {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, + {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -901,6 +932,7 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "3.3.3" description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -919,6 +951,7 @@ virtualenv = ">=20.10.0" name = "pygments" version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -933,6 +966,7 @@ plugins = ["importlib-metadata"] name = "pyperclip" version = "1.8.2" description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +category = "main" optional = false python-versions = "*" files = [ @@ -943,6 +977,7 @@ files = [ name = "pytest" version = "7.4.0" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -965,6 +1000,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -983,6 +1019,7 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1030,13 +1067,14 @@ files = [ [[package]] name = "rich" -version = "13.4.2" +version = "13.5.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"}, - {file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"}, + {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, + {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, ] [package.dependencies] @@ -1051,6 +1089,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "ruff" version = "0.0.282" description = "An extremely fast Python linter, written in Rust." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1077,6 +1116,7 @@ files = [ name = "setuptools" version = "68.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1091,13 +1131,14 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( [[package]] name = "shandy-sqlfmt" -version = "0.19.1" +version = "0.19.2" description = "sqlfmt is an opinionated CLI tool that formats your sql files" +category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "shandy_sqlfmt-0.19.1-py3-none-any.whl", hash = "sha256:45d34ce310a7a456e751e1f2cfbd7be483b0a129a64f89e9dc260ab91f8c1d37"}, - {file = "shandy_sqlfmt-0.19.1.tar.gz", hash = "sha256:11888e6e1f10e015c3d77a8c242f524532a148dbfa3a3cf5524df425476cf145"}, + {file = "shandy_sqlfmt-0.19.2-py3-none-any.whl", hash = "sha256:c23d49eaad546d92026fe41fbd4bc59705ca250a955a058f18990806e6169f23"}, + {file = "shandy_sqlfmt-0.19.2.tar.gz", hash = "sha256:5f0e0904c4d2d93c4f7b557403f5d1edb5d89baef1fd69d4b9a790a97f32af60"}, ] [package.dependencies] @@ -1112,13 +1153,14 @@ sqlfmt-primer = ["gitpython (>=3.1.24,<4.0.0)"] [[package]] name = "textual" -version = "0.30.0" +version = "0.31.0" description = "Modern Text User Interface framework" +category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "textual-0.30.0-py3-none-any.whl", hash = "sha256:e87d587e4569236f3809d41955ed9556287dbedaca64724e1d6ad5adbb69c9c5"}, - {file = "textual-0.30.0.tar.gz", hash = "sha256:bf7045a7e9b7dc3ac589c38ce86ac31aecf0e76e8c8ce09aee474316bc2e2c03"}, + {file = "textual-0.31.0-py3-none-any.whl", hash = "sha256:1243bccadb28e1ff46bdfe676ee25a6ce52756842bc9dca4d824e0bc4d7d9a42"}, + {file = "textual-0.31.0.tar.gz", hash = "sha256:e2b43f1c26b21731ee83f558f8d6cb4f7163e3a713854c36cd7785139a0e4e51"}, ] [package.dependencies] @@ -1131,6 +1173,7 @@ typing-extensions = ">=4.4.0,<5.0.0" name = "textual-dev" version = "1.0.1" description = "Development tools for working with Textual" +category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -1149,6 +1192,7 @@ typing-extensions = ">=4.4.0,<5.0.0" name = "textual-textarea" version = "0.3.3" description = "A text area (multi-line input) with syntax highlighting for Textual" +category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1164,6 +1208,7 @@ textual = ">=0.21.0,<1.0.0" name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1175,6 +1220,7 @@ files = [ name = "tqdm" version = "4.65.0" description = "Fast, Extensible Progress Meter" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1195,6 +1241,7 @@ telegram = ["requests"] name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1206,6 +1253,7 @@ files = [ name = "uc-micro-py" version = "1.0.2" description = "Micro subset of unicode data files for linkify-it-py projects." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1220,6 +1268,7 @@ test = ["coverage", "pytest", "pytest-cov"] name = "virtualenv" version = "20.24.2" description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1240,6 +1289,7 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "yarl" version = "1.9.2" description = "Yet another URL library" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1327,6 +1377,7 @@ multidict = ">=4.0" name = "zipp" version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1341,4 +1392,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8fc5208aa5476d5376dc75016ea9e1e2ae1cd89ba1f94c42caae2a911a0a742b" +content-hash = "e8bd7b1f03532088f77d0176902981ed77a35458e9d7b6fcb542c62ff519026b" diff --git a/pyproject.toml b/pyproject.toml index 407a0ae4..27872a6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.dependencies] python = "^3.8" -textual = "==0.30.0" +textual = "==0.31.0" textual-textarea = "==0.3.3" click = "^8.1.3" duckdb = ">=0.8.0" diff --git a/src/harlequin/tui/app.py b/src/harlequin/tui/app.py index f822bdcd..8030155d 100644 --- a/src/harlequin/tui/app.py +++ b/src/harlequin/tui/app.py @@ -254,10 +254,6 @@ def action_toggle_full_screen(self) -> None: def action_show_help_screen(self) -> None: self.push_screen(HelpScreen(id="help_screen")) - def set_data(self, data: List[Tuple]) -> None: - log(f"set_data {len(data)}") - self.data = data - def watch_full_screen(self, full_screen: bool) -> None: full_screen_widgets = [self.editor, self.results_viewer] other_widgets = [self.run_query_bar, self.footer] @@ -385,36 +381,38 @@ def chunk( log(f"yielding chunk {i}") yield i, data[i * chunksize : (i + 1) * chunksize] - @work(exclusive=True, exit_on_error=False) # type: ignore - def _build_relation(self, query_text: str) -> Union[duckdb.DuckDBPyRelation, None]: + @work(exclusive=True, exit_on_error=False) + async def _build_relation( + self, query_text: str + ) -> Union[duckdb.DuckDBPyRelation, None]: relation = self.connection.sql(query_text) if relation and self.run_query_bar.checkbox.value: relation = relation.limit(self.limit) return relation - @work(exclusive=True, exit_on_error=False) # type: ignore - def fetch_relation_data(self, relation: duckdb.DuckDBPyRelation) -> None: + @work(exclusive=True, exit_on_error=False) + async def fetch_relation_data(self, relation: duckdb.DuckDBPyRelation) -> None: log(f"fetch_relation_data {hash(relation)}") data = relation.fetchall() log(f"fetch_relation_data FINISHED {hash(relation)}") worker = get_current_worker() if not worker.is_cancelled: - self.call_from_thread(self.set_data, data) + self.data = data @work(exclusive=False) - def add_data_to_table(self, table: ResultsTable, data: List[Tuple]) -> Worker: + async def add_data_to_table(self, table: ResultsTable, data: List[Tuple]) -> Worker: log(f"add_data_to_table {len(data)}") worker = get_current_worker() if not worker.is_cancelled: - self.call_from_thread(table.add_rows, data) + table.add_rows(data) return worker @work(exclusive=True) - def update_schema_data(self) -> None: + async def update_schema_data(self) -> None: catalog = get_catalog(self.connection) worker = get_current_worker() if not worker.is_cancelled: - self.call_from_thread(self.schema_viewer.update_tree, catalog) + self.schema_viewer.update_tree(catalog) if __name__ == "__main__":