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(
         [

Reply via email to