A bunch of minor fixes

This commit is contained in:
Simon Willison 2025-11-24 09:44:34 -08:00
commit f35d4c9e2c
6 changed files with 19 additions and 17 deletions

View file

@ -111,7 +111,7 @@ This release introduces a new :ref:`plugin system <plugins>`. Read more about th
3.32 (2023-05-21) 3.32 (2023-05-21)
----------------- -----------------
- New experimental ``sqlite-utils tui`` interface for interactively building command-line invocations, powered by `Trogon <https://github.com/Textualize/trogon>`__. This requires an optional dependency, installed using ``sqlite-utils install trogon``. (:issue:`545`) - New experimental ``sqlite-utils tui`` interface for interactively building command-line invocations, powered by `Trogon <https://github.com/Textualize/trogon>`__. This requires an optional dependency, installed using ``sqlite-utils install trogon``. There is a screenshot :ref:`in the documentation <cli_tui>`. (:issue:`545`)
- ``sqlite-utils analyze-tables`` command (:ref:`documentation <cli_analyze_tables>`) now has a ``--common-limit 20`` option for changing the number of common/least-common values shown for each column. (:issue:`544`) - ``sqlite-utils analyze-tables`` command (:ref:`documentation <cli_analyze_tables>`) now has a ``--common-limit 20`` option for changing the number of common/least-common values shown for each column. (:issue:`544`)
- ``sqlite-utils analyze-tables --no-most`` and ``--no-least`` options for disabling calculation of most-common and least-common values. - ``sqlite-utils analyze-tables --no-most`` and ``--no-least`` options for disabling calculation of most-common and least-common values.
- If a column contains only ``null`` values, ``analyze-tables`` will no longer attempt to calculate the most common and least common values for that column. (:issue:`547`) - If a column contains only ``null`` values, ``analyze-tables`` will no longer attempt to calculate the most common and least common values for that column. (:issue:`547`)

View file

@ -65,6 +65,7 @@ This page lists the ``--help`` for every ``sqlite-utils`` CLI sub-command.
"create-spatial-index": "cli_spatialite_indexes", "create-spatial-index": "cli_spatialite_indexes",
"install": "cli_install", "install": "cli_install",
"uninstall": "cli_uninstall", "uninstall": "cli_uninstall",
"tui": "cli_tui",
} }
commands.sort(key = lambda command: go_first.index(command) if command in go_first else 999) commands.sort(key = lambda command: go_first.index(command) if command in go_first else 999)
cog.out("\n") cog.out("\n")

View file

@ -1301,7 +1301,7 @@ You can set the ``SQLITE_UTILS_DETECT_TYPES`` environment variable if you want `
If a CSV or TSV file includes empty cells, like this one: If a CSV or TSV file includes empty cells, like this one:
:: .. code-block:: csv
name,age,weight name,age,weight
Cleo,6, Cleo,6,

View file

@ -962,7 +962,7 @@ def insert_upsert_implementation(
db = sqlite_utils.Database(path) db = sqlite_utils.Database(path)
_load_extensions(db, load_extension) _load_extensions(db, load_extension)
if functions: if functions:
_maybe_register_functions(db, functions) _register_functions_from_multiple(db, functions)
if (delimiter or quotechar or sniff or no_headers) and not tsv: if (delimiter or quotechar or sniff or no_headers) and not tsv:
csv = True csv = True
if (nl + csv + tsv) >= 2: if (nl + csv + tsv) >= 2:
@ -1800,7 +1800,7 @@ def query(
db.register_fts4_bm25() db.register_fts4_bm25()
if functions: if functions:
_maybe_register_functions(db, functions) _register_functions_from_multiple(db, functions)
_execute_query( _execute_query(
db, db,
@ -2002,7 +2002,7 @@ def memory(
db.register_fts4_bm25() db.register_fts4_bm25()
if functions: if functions:
_maybe_register_functions(db, functions) _register_functions_from_multiple(db, functions)
if return_db: if return_db:
return db return db
@ -3306,16 +3306,10 @@ def _register_functions(db, functions):
db.register_function(value, name=name) db.register_function(value, name=name)
def _value_or_none(value): def _register_functions_from_multiple(db, functions_list):
if getattr(value, "__class__", None).__name__ == "Sentinel": """Register functions from multiple --functions arguments."""
return None
return value
def _maybe_register_functions(db, functions_list):
if not functions_list: if not functions_list:
return return
for functions in functions_list: for functions in functions_list:
functions = _value_or_none(functions)
if isinstance(functions, str) and functions.strip(): if isinstance(functions, str) and functions.strip():
_register_functions(db, functions) _register_functions(db, functions)

View file

@ -237,36 +237,43 @@ if pd:
class AlterError(Exception): class AlterError(Exception):
"Error altering table" "Error altering table"
pass pass
class NoObviousTable(Exception): class NoObviousTable(Exception):
"Could not tell which table this operation refers to" "Could not tell which table this operation refers to"
pass pass
class NoTable(Exception): class NoTable(Exception):
"Specified table does not exist" "Specified table does not exist"
pass pass
class BadPrimaryKey(Exception): class BadPrimaryKey(Exception):
"Table does not have a single obvious primary key" "Table does not have a single obvious primary key"
pass pass
class NotFoundError(Exception): class NotFoundError(Exception):
"Record not found" "Record not found"
pass pass
class PrimaryKeyRequired(Exception): class PrimaryKeyRequired(Exception):
"Primary key needs to be specified" "Primary key needs to be specified"
pass pass
class InvalidColumns(Exception): class InvalidColumns(Exception):
"Specified columns do not exist" "Specified columns do not exist"
pass pass
@ -3203,7 +3210,7 @@ class Table(Queryable):
:param not_null: Set of strings specifying columns that should be ``NOT NULL``. :param not_null: Set of strings specifying columns that should be ``NOT NULL``.
:param defaults: Dictionary specifying default values for specific columns. :param defaults: Dictionary specifying default values for specific columns.
:param hash_id: Name of a column to create and use as a primary key, where the :param hash_id: Name of a column to create and use as a primary key, where the
value of thet primary key will be derived as a SHA1 hash of the other column values value of that primary key will be derived as a SHA1 hash of the other column values
in the record. ``hash_id="id"`` is a common column name used for this. in the record. ``hash_id="id"`` is a common column name used for this.
:param alter: Boolean, should any missing columns be added automatically? :param alter: Boolean, should any missing columns be added automatically?
:param ignore: Boolean, if a record already exists with this primary key, ignore this insert. :param ignore: Boolean, if a record already exists with this primary key, ignore this insert.
@ -3852,7 +3859,7 @@ def jsonify_if_needed(value):
def resolve_extracts( def resolve_extracts(
extracts: Optional[Union[Dict[str, str], List[str], Tuple[str]]] extracts: Optional[Union[Dict[str, str], List[str], Tuple[str]]],
) -> dict: ) -> dict:
if extracts is None: if extracts is None:
extracts = {} extracts = {}

View file

@ -987,7 +987,7 @@ def test_query_json_with_json_cols(db_path):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"content,is_binary", "content,is_binary",
[(b"\x00\x0Fbinary", True), ("this is text", False), (1, False), (1.5, False)], [(b"\x00\x0fbinary", True), ("this is text", False), (1, False), (1.5, False)],
) )
def test_query_raw(db_path, content, is_binary): def test_query_raw(db_path, content, is_binary):
Database(db_path)["files"].insert({"content": content}) Database(db_path)["files"].insert({"content": content})
@ -1002,7 +1002,7 @@ def test_query_raw(db_path, content, is_binary):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"content,is_binary", "content,is_binary",
[(b"\x00\x0Fbinary", True), ("this is text", False), (1, False), (1.5, False)], [(b"\x00\x0fbinary", True), ("this is text", False), (1, False), (1.5, False)],
) )
def test_query_raw_lines(db_path, content, is_binary): def test_query_raw_lines(db_path, content, is_binary):
Database(db_path)["files"].insert_all({"content": content} for _ in range(3)) Database(db_path)["files"].insert_all({"content": content} for _ in range(3))