From 1862c7367bc588ce8d1723bd4122e1c3222ff5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Thu, 2 May 2024 12:14:03 +0100 Subject: [PATCH] Add ability to debug queries --- beets/dbcore/db.py | 18 +++++++ poetry.lock | 115 ++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 1ce26561c..1356a104b 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -20,6 +20,7 @@ import contextlib import os import re import sqlite3 +import sys import threading import time from abc import ABC @@ -48,6 +49,8 @@ from typing import ( cast, ) +from rich import print +from rich_tables.generic import flexitable from unidecode import unidecode import beets @@ -64,6 +67,18 @@ from .query import ( TrueQuery, ) +DEBUG = bool(os.getenv("BEETS_DEBUG", False)) + + +def print_query(sql, subvals=None): + """If debugging, replace placeholders and print the query.""" + if not DEBUG: + return + topr = sql + for val in subvals or []: + topr = topr.replace("?", str(val), 1) + print(flexitable({"sql": topr}), file=sys.stderr) + class DBAccessError(Exception): """The SQLite database became inaccessible. @@ -962,6 +977,7 @@ class Transaction: """Execute an SQL statement with substitution values and return a list of rows from the database. """ + print_query(statement, subvals) cursor = self.db._connection().execute(statement, subvals) return cursor.fetchall() @@ -970,6 +986,7 @@ class Transaction: the row ID of the last affected row. """ try: + print_query(statement, subvals) cursor = self.db._connection().execute(statement, subvals) except sqlite3.OperationalError as e: # In two specific cases, SQLite reports an error while accessing @@ -990,6 +1007,7 @@ class Transaction: """Execute a string containing multiple SQL statements.""" # We don't know whether this mutates, but quite likely it does. self._mutated = True + print_query(statements) self.db._connection().executescript(statements) diff --git a/poetry.lock b/poetry.lock index 1e3b4cd1d..94c13b0f3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -685,6 +685,17 @@ files = [ [package.dependencies] Flask = ">=0.9" +[[package]] +name = "funcy" +version = "2.0" +description = "A fancy and practical functional tools" +optional = false +python-versions = "*" +files = [ + {file = "funcy-2.0-py2.py3-none-any.whl", hash = "sha256:53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0"}, + {file = "funcy-2.0.tar.gz", hash = "sha256:3963315d59d41c6f30c04bc910e10ab50a3ac4a225868bfa96feed133df075cb"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -1170,6 +1181,30 @@ html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] source = ["Cython (>=3.0.10)"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "2.1.5" @@ -1250,6 +1285,17 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mediafile" version = "0.12.0" @@ -1284,6 +1330,17 @@ build = ["blurb", "twine", "wheel"] docs = ["sphinx"] test = ["pytest", "pytest-cov"] +[[package]] +name = "multimethod" +version = "1.10" +description = "Multiple argument dispatching." +optional = false +python-versions = ">=3.8" +files = [ + {file = "multimethod-1.10-py3-none-any.whl", hash = "sha256:afd84da9c3d0445c84f827e4d63ad42d17c6d29b122427c6dee9032ac2d2a0d4"}, + {file = "multimethod-1.10.tar.gz", hash = "sha256:daa45af3fe257f73abb69673fd54ddeaf31df0eb7363ad6e1251b7c9b192d8c5"}, +] + [[package]] name = "multivolumefile" version = "0.2.3" @@ -2312,6 +2369,47 @@ urllib3 = ">=1.25.10,<3.0" [package.extras] tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rich-tables" +version = "0.4.0" +description = "Ready-made rich tables for various purposes" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "rich_tables-0.4.0-py3-none-any.whl", hash = "sha256:f7e7431d1c3cbe74084848f7393926560bd2f05aaae56e673371fc1380c7b4be"}, + {file = "rich_tables-0.4.0.tar.gz", hash = "sha256:ca6474e35d6bc6a592d3cdfb6c03ed6dd26e5bef2ad09f5733685b972fde19dc"}, +] + +[package.dependencies] +funcy = ">=2.0" +multimethod = "*" +platformdirs = ">=4.2.0" +rich = ">=12.3.0" +sqlparse = ">=0.4.4" +typing-extensions = ">=4.7.1" + +[package.extras] +hue = ["rgbxy (>=0.5)"] + [[package]] name = "six" version = "1.16.0" @@ -2502,6 +2600,21 @@ files = [ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] +[[package]] +name = "sqlparse" +version = "0.5.0" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sqlparse-0.5.0-py3-none-any.whl", hash = "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"}, + {file = "sqlparse-0.5.0.tar.gz", hash = "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"}, +] + +[package.extras] +dev = ["build", "hatch"] +doc = ["sphinx"] + [[package]] name = "texttable" version = "1.7.0" @@ -2720,4 +2833,4 @@ web = ["flask", "flask-cors"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4" -content-hash = "740281ee3ddba4c6015eab9cfc24bb947e8816e3b7f5a6bebeb39ff2413d7ac3" +content-hash = "ee74bed6f552311e7d4da47904e5507ca6d1d7cafefc8493719fcc3b18abb151" diff --git a/pyproject.toml b/pyproject.toml index 3ef11ac14..a73fdbca7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,7 @@ mediafile = ">=0.12.0" munkres = ">=1.0.0" musicbrainzngs = ">=0.4" pyyaml = "*" +rich-tables = ">=0.4.0" typing_extensions = "*" unidecode = ">=1.3.6" beautifulsoup4 = { version = "*", optional = true }