Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-sqlite-utils for
openSUSE:Factory checked in at 2026-03-24 18:49:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sqlite-utils (Old)
and /work/SRC/openSUSE:Factory/.python-sqlite-utils.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sqlite-utils"
Tue Mar 24 18:49:39 2026 rev:9 rq:1342171 version:3.39
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-sqlite-utils/python-sqlite-utils.changes
2025-10-08 18:19:21.574589526 +0200
+++
/work/SRC/openSUSE:Factory/.python-sqlite-utils.new.8177/python-sqlite-utils.changes
2026-03-24 18:50:35.166707278 +0100
@@ -1,0 +2,18 @@
+Tue Mar 24 07:17:57 UTC 2026 - Matej Cepl <[email protected]>
+
+- Remove the forgotten tarball.
+
+-------------------------------------------------------------------
+Mon Mar 23 23:07:25 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 3.39:
+ * Fixed a bug with sqlite-utils install when the tool had been
+ installed using uv. (:issue:`687`)
+ * The `--functions` argument now optionally accepts a path to a
+ Python file as an alternative to a string full of code, and
+ can be specified multiple times - see
+ :ref:`cli_query_functions`. (:issue:`659`)
+ * sqlite-utils now requires on Python 3.10 or higher.
+- drop support-click-8.3.0.patch (upstream)
+
+-------------------------------------------------------------------
Old:
----
sqlite_utils-3.38.tar.gz
support-click-8.3.0.patch
New:
----
sqlite_utils-3.39.tar.gz
----------(Old B)----------
Old: * sqlite-utils now requires on Python 3.10 or higher.
- drop support-click-8.3.0.patch (upstream)
----------(Old E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-sqlite-utils.spec ++++++
--- /var/tmp/diff_new_pack.Y4pD5z/_old 2026-03-24 18:50:37.154789308 +0100
+++ /var/tmp/diff_new_pack.Y4pD5z/_new 2026-03-24 18:50:37.178790298 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-sqlite-utils
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,16 +18,14 @@
%{?sle15_python_module_pythons}
Name: python-sqlite-utils
-Version: 3.38
+Version: 3.39
Release: 0
Summary: Python CLI tool and library for manipulating SQLite databases
License: Apache-2.0
URL: https://github.com/simonw/sqlite-utils
Source:
https://files.pythonhosted.org/packages/source/s/sqlite_utils/sqlite_utils-%{version}.tar.gz
-# PATCH-FIX-UPSTREAM
gh#simonw/sqlite-utils#665/commits/211831966ed389954f44cb8aa2b842481c374557
-Patch0: support-click-8.3.0.patch
-BuildRequires: %{python_module click-default-group}
-BuildRequires: %{python_module click}
+BuildRequires: %{python_module click >= 8.3.1}
+BuildRequires: %{python_module click-default-group >= 1.2.3}
BuildRequires: %{python_module hypothesis}
BuildRequires: %{python_module pandas}
BuildRequires: %{python_module pip}
@@ -39,8 +37,8 @@
BuildRequires: %{pythons}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-click
-Requires: python-click-default-group
+Requires: python-click >= 8.3.1
+Requires: python-click-default-group >= 1.2.3
Requires: python-pluggy
Requires: python-python-dateutil
Requires: python-sqlite-fts4
++++++ sqlite_utils-3.38.tar.gz -> sqlite_utils-3.39.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/PKG-INFO
new/sqlite_utils-3.39/PKG-INFO
--- old/sqlite_utils-3.38/PKG-INFO 2024-11-23 23:49:37.456869600 +0100
+++ new/sqlite_utils-3.39/PKG-INFO 2025-11-24 19:45:47.662463000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: sqlite-utils
-Version: 3.38
+Version: 3.39
Summary: CLI tool and Python library for manipulating SQLite databases
Home-page: https://github.com/simonw/sqlite-utils
Author: Simon Willison
@@ -15,22 +15,21 @@
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: End Users/Desktop
Classifier: Topic :: Database
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
-Requires-Python: >=3.8
+Classifier: Programming Language :: Python :: 3.14
+Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlite-fts4
-Requires-Dist: click
+Requires-Dist: click>=8.3.1
Requires-Dist: click-default-group>=1.2.3
Requires-Dist: tabulate
Requires-Dist: python-dateutil
Requires-Dist: pluggy
+Requires-Dist: pip
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: black>=24.1.1; extra == "test"
@@ -54,6 +53,18 @@
Requires-Dist: flake8; extra == "flake8"
Provides-Extra: tui
Requires-Dist: trogon; extra == "tui"
+Dynamic: author
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
# sqlite-utils
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/docs/changelog.rst
new/sqlite_utils-3.39/docs/changelog.rst
--- old/sqlite_utils-3.38/docs/changelog.rst 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/docs/changelog.rst 2025-11-24 19:45:38.000000000
+0100
@@ -4,6 +4,17 @@
Changelog
===========
+.. _v3_39:
+
+3.39 (2025-11-24)
+-----------------
+
+- Fixed a bug with ``sqlite-utils install`` when the tool had been installed
using ``uv``. (:issue:`687`)
+- The ```--functions``` argument now optionally accepts a path to a Python
file as an alternative to a string full of code, and can be specified multiple
times - see :ref:`cli_query_functions`. (:issue:`659`)
+- ``sqlite-utils`` now requires on Python 3.10 or higher.
+
+`sqlite-utils 4.0a1
<https://sqlite-utils.datasette.io/en/latest/changelog.html#a1-2025-11-23>`__
is now available as an alpha with some `minor breaking changes
<https://simonwillison.net/2025/Nov/24/sqlite-utils-40a1/>`__.
+
.. _v3_38:
3.38 (2024-11-23)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/docs/cli-reference.rst
new/sqlite_utils-3.39/docs/cli-reference.rst
--- old/sqlite_utils-3.38/docs/cli-reference.rst 2024-11-23
23:49:29.000000000 +0100
+++ new/sqlite_utils-3.39/docs/cli-reference.rst 2025-11-24
19:45:38.000000000 +0100
@@ -132,7 +132,7 @@
-r, --raw Raw output, first column of first row
--raw-lines Raw output, first column of each row
-p, --param <TEXT TEXT>... Named :parameters for SQL query
- --functions TEXT Python code defining one or more custom SQL
+ --functions TEXT Python code or file path defining custom SQL
functions
--load-extension TEXT Path to SQLite extension, with optional
:entrypoint
@@ -175,7 +175,7 @@
sqlite-utils memory animals.csv --schema
Options:
- --functions TEXT Python code defining one or more custom SQL
+ --functions TEXT Python code or file path defining custom SQL
functions
--attach <TEXT FILE>... Additional databases to attach - specify
alias and
filepath
@@ -375,7 +375,7 @@
Options:
--batch-size INTEGER Commit every X records
- --functions TEXT Python code defining one or more custom SQL
functions
+ --functions TEXT Python code or file path defining custom SQL
functions
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
becomes
{"a_b": 1}
--nl Expect newline-delimited JSON
@@ -1497,7 +1497,7 @@
paths. To load it from a specific path, use --load-extension.
Options:
- -t, --type
[POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION|GEOMETRY]
+ -t, --type
[point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection|geometry]
Specify a geometry type for this column.
[default: GEOMETRY]
--srid INTEGER Spatial Reference ID. See
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/docs/cli.rst
new/sqlite_utils-3.39/docs/cli.rst
--- old/sqlite_utils-3.38/docs/cli.rst 2024-11-23 23:49:29.000000000 +0100
+++ new/sqlite_utils-3.39/docs/cli.rst 2025-11-24 19:45:38.000000000 +0100
@@ -368,6 +368,22 @@
Every callable object defined in the block will be registered as a SQL
function with the same name, with the exception of functions with names that
begin with an underscore.
+You can also pass the path to a Python file containing function definitions:
+
+.. code-block:: bash
+
+ sqlite-utils query sites.db "select url, domain(url) from urls"
--functions functions.py
+
+The ``--functions`` option can be used multiple times to load functions from
multiple sources:
+
+.. code-block:: bash
+
+ sqlite-utils query sites.db "select url, domain(url), extract_path(url)
from urls" \
+ --functions domain_funcs.py \
+ --functions 'def extract_path(url):
+ from urllib.parse import urlparse
+ return urlparse(url).path'
+
.. _cli_query_extensions:
SQLite extensions
@@ -1128,7 +1144,7 @@
Inserting newline-delimited JSON
--------------------------------
-You can also import `newline-delimited JSON <http://ndjson.org/>`__ using the
``--nl`` option:
+You can also import newline-delimited JSON (see `JSON Lines
<https://jsonlines.org/>`__) using the ``--nl`` option:
.. code-block:: bash
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/docs/python-api.rst
new/sqlite_utils-3.39/docs/python-api.rst
--- old/sqlite_utils-3.38/docs/python-api.rst 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/docs/python-api.rst 2025-11-24 19:45:38.000000000
+0100
@@ -2711,7 +2711,7 @@
print(db.execute('select rev("hello")').fetchone()[0])
-Python 3.8 added the ability to register `deterministic SQLite functions
<https://sqlite.org/deterministic.html>`__, allowing you to indicate that a
function will return the exact same result for any given inputs and hence
allowing SQLite to apply some performance optimizations. You can mark a
function as deterministic using ``deterministic=True``, like this:
+If a function will return the exact same result for any given inputs you can
register it as a `deterministic SQLite function
<https://sqlite.org/deterministic.html>`__ allowing SQLite to apply some
performance optimizations:
.. code-block:: python
@@ -2719,8 +2719,6 @@
def reverse_string(s):
return "".join(reversed(list(s)))
-If you run this on a version of Python prior to 3.8 your code will still work,
but the ``deterministic=True`` parameter will be ignored.
-
By default registering a function with the same name and number of arguments
will have no effect - the ``Database`` instance keeps track of functions that
have already been registered and skips registering them if
``@db.register_function`` is called a second time.
If you want to deliberately replace the registered function with a new
implementation, use the ``replace=True`` argument:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/setup.py
new/sqlite_utils-3.39/setup.py
--- old/sqlite_utils-3.38/setup.py 2024-11-23 23:49:29.000000000 +0100
+++ new/sqlite_utils-3.39/setup.py 2025-11-24 19:45:38.000000000 +0100
@@ -2,7 +2,7 @@
import io
import os
-VERSION = "3.38"
+VERSION = "3.39"
def get_long_description():
@@ -25,11 +25,12 @@
package_data={"sqlite_utils": ["py.typed"]},
install_requires=[
"sqlite-fts4",
- "click",
+ "click>=8.3.1",
"click-default-group>=1.2.3",
"tabulate",
"python-dateutil",
"pluggy",
+ "pip",
],
extras_require={
"test": ["pytest", "black>=24.1.1", "hypothesis", "cogapp"],
@@ -64,20 +65,18 @@
"Issues": "https://github.com/simonw/sqlite-utils/issues",
"CI": "https://github.com/simonw/sqlite-utils/actions",
},
- python_requires=">=3.8",
+ python_requires=">=3.10",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Intended Audience :: End Users/Desktop",
"Topic :: Database",
- "License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
],
# Needed to bundle py.typed so mypy can see it:
zip_safe=False,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/sqlite_utils/cli.py
new/sqlite_utils-3.39/sqlite_utils/cli.py
--- old/sqlite_utils-3.38/sqlite_utils/cli.py 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/sqlite_utils/cli.py 2025-11-24 19:45:38.000000000
+0100
@@ -1,7 +1,7 @@
import base64
import click
from click_default_group import DefaultGroup # type: ignore
-from datetime import datetime
+from datetime import datetime, timezone
import hashlib
import pathlib
from runpy import run_module
@@ -962,7 +962,7 @@
db = sqlite_utils.Database(path)
_load_extensions(db, load_extension)
if functions:
- _register_functions(db, functions)
+ _register_functions_from_multiple(db, functions)
if (delimiter or quotechar or sniff or no_headers) and not tsv:
csv = True
if (nl + csv + tsv) >= 2:
@@ -1370,7 +1370,9 @@
@click.argument("file", type=click.File("rb"), required=True)
@click.option("--batch-size", type=int, default=100, help="Commit every X
records")
@click.option(
- "--functions", help="Python code defining one or more custom SQL functions"
+ "--functions",
+ help="Python code or file path defining custom SQL functions",
+ multiple=True,
)
@import_options
@load_extension_option
@@ -1759,7 +1761,9 @@
help="Named :parameters for SQL query",
)
@click.option(
- "--functions", help="Python code defining one or more custom SQL functions"
+ "--functions",
+ help="Python code or file path defining custom SQL functions",
+ multiple=True,
)
@load_extension_option
def query(
@@ -1796,7 +1800,7 @@
db.register_fts4_bm25()
if functions:
- _register_functions(db, functions)
+ _register_functions_from_multiple(db, functions)
_execute_query(
db,
@@ -1824,7 +1828,9 @@
)
@click.argument("sql")
@click.option(
- "--functions", help="Python code defining one or more custom SQL functions"
+ "--functions",
+ help="Python code or file path defining custom SQL functions",
+ multiple=True,
)
@click.option(
"--attach",
@@ -1996,7 +2002,7 @@
db.register_fts4_bm25()
if functions:
- _register_functions(db, functions)
+ _register_functions_from_multiple(db, functions)
if return_db:
return db
@@ -3203,8 +3209,12 @@
"ctime": lambda p: p.stat().st_ctime,
"mtime_int": lambda p: int(p.stat().st_mtime),
"ctime_int": lambda p: int(p.stat().st_ctime),
- "mtime_iso": lambda p:
datetime.utcfromtimestamp(p.stat().st_mtime).isoformat(),
- "ctime_iso": lambda p:
datetime.utcfromtimestamp(p.stat().st_ctime).isoformat(),
+ "mtime_iso": lambda p: datetime.fromtimestamp(p.stat().st_mtime,
timezone.utc)
+ .replace(tzinfo=None)
+ .isoformat(),
+ "ctime_iso": lambda p: datetime.fromtimestamp(p.stat().st_ctime,
timezone.utc)
+ .replace(tzinfo=None)
+ .isoformat(),
"size": lambda p: p.stat().st_size,
"stem": lambda p: p.stem,
"suffix": lambda p: p.suffix,
@@ -3281,6 +3291,13 @@
def _register_functions(db, functions):
# Register any Python functions as SQL functions:
+ # Check if this is a file path
+ if "\n" not in functions and functions.endswith(".py"):
+ try:
+ functions = pathlib.Path(functions).read_text()
+ except FileNotFoundError:
+ raise click.ClickException("File not found: {}".format(functions))
+
sqlite3.enable_callback_tracebacks(True)
globals = {}
try:
@@ -3291,3 +3308,12 @@
for name, value in globals.items():
if callable(value) and not name.startswith("_"):
db.register_function(value, name=name)
+
+
+def _register_functions_from_multiple(db, functions_list):
+ """Register functions from multiple --functions arguments."""
+ if not functions_list:
+ return
+ for functions in functions_list:
+ if isinstance(functions, str) and functions.strip():
+ _register_functions(db, functions)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/sqlite_utils/db.py
new/sqlite_utils-3.39/sqlite_utils/db.py
--- old/sqlite_utils-3.38/sqlite_utils/db.py 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/sqlite_utils/db.py 2025-11-24 19:45:38.000000000
+0100
@@ -237,36 +237,43 @@
class AlterError(Exception):
"Error altering table"
+
pass
class NoObviousTable(Exception):
"Could not tell which table this operation refers to"
+
pass
class NoTable(Exception):
"Specified table does not exist"
+
pass
class BadPrimaryKey(Exception):
"Table does not have a single obvious primary key"
+
pass
class NotFoundError(Exception):
"Record not found"
+
pass
class PrimaryKeyRequired(Exception):
"Primary key needs to be specified"
+
pass
class InvalidColumns(Exception):
"Specified columns do not exist"
+
pass
@@ -368,7 +375,7 @@
pm.hook.prepare_connection(conn=self.conn)
self.strict = strict
- def close(self):
+ def close(self) -> None:
"Close the SQLite connection, and the underlying database file"
self.conn.close()
@@ -3203,7 +3210,7 @@
:param not_null: Set of strings specifying columns that should be
``NOT NULL``.
: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
- 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.
: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.
@@ -3852,7 +3859,7 @@
def resolve_extracts(
- extracts: Optional[Union[Dict[str, str], List[str], Tuple[str]]]
+ extracts: Optional[Union[Dict[str, str], List[str], Tuple[str]]],
) -> dict:
if extracts is None:
extracts = {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/sqlite_utils.egg-info/PKG-INFO
new/sqlite_utils-3.39/sqlite_utils.egg-info/PKG-INFO
--- old/sqlite_utils-3.38/sqlite_utils.egg-info/PKG-INFO 2024-11-23
23:49:37.000000000 +0100
+++ new/sqlite_utils-3.39/sqlite_utils.egg-info/PKG-INFO 2025-11-24
19:45:47.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: sqlite-utils
-Version: 3.38
+Version: 3.39
Summary: CLI tool and Python library for manipulating SQLite databases
Home-page: https://github.com/simonw/sqlite-utils
Author: Simon Willison
@@ -15,22 +15,21 @@
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: End Users/Desktop
Classifier: Topic :: Database
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
-Requires-Python: >=3.8
+Classifier: Programming Language :: Python :: 3.14
+Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlite-fts4
-Requires-Dist: click
+Requires-Dist: click>=8.3.1
Requires-Dist: click-default-group>=1.2.3
Requires-Dist: tabulate
Requires-Dist: python-dateutil
Requires-Dist: pluggy
+Requires-Dist: pip
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: black>=24.1.1; extra == "test"
@@ -54,6 +53,18 @@
Requires-Dist: flake8; extra == "flake8"
Provides-Extra: tui
Requires-Dist: trogon; extra == "tui"
+Dynamic: author
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
# sqlite-utils
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/sqlite_utils.egg-info/requires.txt
new/sqlite_utils-3.39/sqlite_utils.egg-info/requires.txt
--- old/sqlite_utils-3.38/sqlite_utils.egg-info/requires.txt 2024-11-23
23:49:37.000000000 +0100
+++ new/sqlite_utils-3.39/sqlite_utils.egg-info/requires.txt 2025-11-24
19:45:47.000000000 +0100
@@ -1,9 +1,10 @@
sqlite-fts4
-click
+click>=8.3.1
click-default-group>=1.2.3
tabulate
python-dateutil
pluggy
+pip
[docs]
furo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/tests/test_cli.py
new/sqlite_utils-3.39/tests/test_cli.py
--- old/sqlite_utils-3.38/tests/test_cli.py 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/tests/test_cli.py 2025-11-24 19:45:38.000000000
+0100
@@ -806,6 +806,77 @@
assert "_two" not in functions
+def test_query_functions_from_file(db_path, tmp_path):
+ # Create a temporary file with function definitions
+ functions_file = tmp_path / "my_functions.py"
+ functions_file.write_text(TEST_FUNCTIONS)
+
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ db_path,
+ "select zero(), one(1), two(1, 2)",
+ "--functions",
+ str(functions_file),
+ ],
+ )
+ assert result.exit_code == 0
+ assert json.loads(result.output.strip()) == [
+ {"zero()": 0, "one(1)": 1, "two(1, 2)": 3}
+ ]
+
+
+def test_query_functions_file_not_found(db_path):
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ db_path,
+ "select zero()",
+ "--functions",
+ "nonexistent.py",
+ ],
+ )
+ assert result.exit_code == 1
+ assert "File not found: nonexistent.py" in result.output
+
+
+def test_query_functions_multiple_invocations(db_path):
+ # Test using --functions multiple times
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ db_path,
+ "select triple(2), quadruple(2)",
+ "--functions",
+ "def triple(x):\n return x * 3",
+ "--functions",
+ "def quadruple(x):\n return x * 4",
+ ],
+ )
+ assert result.exit_code == 0
+ assert json.loads(result.output.strip()) == [{"triple(2)": 6,
"quadruple(2)": 8}]
+
+
+def test_query_functions_file_and_inline(db_path, tmp_path):
+ # Test combining file and inline code
+ functions_file = tmp_path / "file_funcs.py"
+ functions_file.write_text("def triple(x):\n return x * 3")
+
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ db_path,
+ "select triple(2), quadruple(2)",
+ "--functions",
+ str(functions_file),
+ "--functions",
+ "def quadruple(x):\n return x * 4",
+ ],
+ )
+ assert result.exit_code == 0
+ assert json.loads(result.output.strip()) == [{"triple(2)": 6,
"quadruple(2)": 8}]
+
+
LOREM_IPSUM_COMPRESSED = (
b"x\x9c\xed\xd1\xcdq\x03!\x0c\x05\xe0\xbb\xabP\x01\x1eW\x91\xdc|M\x01\n\xc8\x8e"
b"f\xf83H\x1e\x97\x1f\x91M\x8e\xe9\xe0\xdd\x96\x05\x84\xf4\xbek\x9fRI\xc7\xf2J"
@@ -916,7 +987,7 @@
@pytest.mark.parametrize(
"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):
Database(db_path)["files"].insert({"content": content})
@@ -931,7 +1002,7 @@
@pytest.mark.parametrize(
"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):
Database(db_path)["files"].insert_all({"content": content} for _ in
range(3))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/tests/test_cli_bulk.py
new/sqlite_utils-3.39/tests/test_cli_bulk.py
--- old/sqlite_utils-3.38/tests/test_cli_bulk.py 2024-11-23
23:49:29.000000000 +0100
+++ new/sqlite_utils-3.39/tests/test_cli_bulk.py 2025-11-24
19:45:38.000000000 +0100
@@ -45,6 +45,32 @@
] == list(db["example"].rows)
+def test_cli_bulk_multiple_functions(test_db_and_path):
+ db, db_path = test_db_and_path
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ "bulk",
+ db_path,
+ "insert into example (id, name) values (:id,
myupper(mylower(:name)))",
+ "-",
+ "--nl",
+ "--functions",
+ "myupper = lambda s: s.upper()",
+ "--functions",
+ "mylower = lambda s: s.lower()",
+ ],
+ input='{"id": 3, "name": "ThReE"}\n{"id": 4, "name": "FoUr"}\n',
+ )
+ assert result.exit_code == 0, result.output
+ assert [
+ {"id": 1, "name": "One"},
+ {"id": 2, "name": "Two"},
+ {"id": 3, "name": "THREE"},
+ {"id": 4, "name": "FOUR"},
+ ] == list(db["example"].rows)
+
+
def test_cli_bulk_batch_size(test_db_and_path):
db, db_path = test_db_and_path
proc = subprocess.Popen(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/tests/test_cli_memory.py
new/sqlite_utils-3.39/tests/test_cli_memory.py
--- old/sqlite_utils-3.38/tests/test_cli_memory.py 2024-11-23
23:49:29.000000000 +0100
+++ new/sqlite_utils-3.39/tests/test_cli_memory.py 2025-11-24
19:45:38.000000000 +0100
@@ -307,6 +307,22 @@
assert result.output.strip() == '[{"hello()": "Hello"}]'
+def test_memory_functions_multiple():
+ result = CliRunner().invoke(
+ cli.cli,
+ [
+ "memory",
+ "select triple(2), quadruple(2)",
+ "--functions",
+ "def triple(x):\n return x * 3",
+ "--functions",
+ "def quadruple(x):\n return x * 4",
+ ],
+ )
+ assert result.exit_code == 0
+ assert result.output.strip() == '[{"triple(2)": 6, "quadruple(2)": 8}]'
+
+
def test_memory_return_db(tmpdir):
# https://github.com/simonw/sqlite-utils/issues/643
from sqlite_utils.cli import cli
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/sqlite_utils-3.38/tests/test_recipes.py
new/sqlite_utils-3.39/tests/test_recipes.py
--- old/sqlite_utils-3.38/tests/test_recipes.py 2024-11-23 23:49:29.000000000
+0100
+++ new/sqlite_utils-3.39/tests/test_recipes.py 2025-11-24 19:45:38.000000000
+0100
@@ -64,6 +64,7 @@
@pytest.mark.parametrize("fn", ("parsedate", "parsedatetime"))
@pytest.mark.parametrize("errors", (None, recipes.SET_NULL, recipes.IGNORE))
[email protected]("ignore::pytest.PytestUnraisableExceptionWarning")
def test_dateparse_errors(fresh_db, fn, errors):
fresh_db["example"].insert_all(
[