From a22a127458d75b9b7e65e058f5db5ff705df5696 Mon Sep 17 00:00:00 2001 From: Anders Lorentsen Date: Tue, 7 Nov 2017 01:47:43 +0100 Subject: [PATCH] bpo-31843: sqlite3.connect() now accepts PathLike objects as database name (#4299) --- Doc/library/sqlite3.rst | 13 ++++++++++--- Lib/sqlite3/test/dbapi.py | 11 +++++++++++ .../2017-11-07-00-37-50.bpo-31843.lM2gkR.rst | 2 ++ Modules/_sqlite/connection.c | 9 +++++++-- Modules/_sqlite/module.c | 4 ++-- 5 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-11-07-00-37-50.bpo-31843.lM2gkR.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index ef0c0bf64cd..c7b9af4037f 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -172,9 +172,13 @@ Module functions and constants .. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri]) - Opens a connection to the SQLite database file *database*. You can use - ``":memory:"`` to open a database connection to a database that resides in RAM - instead of on disk. + Opens a connection to the SQLite database file *database*. By default returns a + :class:`Connection` object, unless a custom *factory* is given. + + *database* is a :term:`path-like object` giving the pathname (absolute or + relative to the current working directory) of the database file to be opened. + You can use ``":memory:"`` to open a database connection to a database that + resides in RAM instead of on disk. When a database is accessed by multiple connections, and one of the processes modifies the database, the SQLite database is locked until that transaction is @@ -223,6 +227,9 @@ Module functions and constants .. versionchanged:: 3.4 Added the *uri* parameter. + .. versionchanged:: 3.7 + *database* can now also be a :term:`path-like object`, not only a string. + .. function:: register_converter(typename, callable) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 12c9fb460fd..5332975e0a6 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -160,6 +160,17 @@ class ConnectionTests(unittest.TestCase): with self.assertRaises(AttributeError): self.cx.in_transaction = True + def CheckOpenWithPathLikeObject(self): + """ Checks that we can succesfully connect to a database using an object that + is PathLike, i.e. has __fspath__(). """ + self.addCleanup(unlink, TESTFN) + class Path: + def __fspath__(self): + return TESTFN + path = Path() + with sqlite.connect(path) as cx: + cx.execute('create table test(id integer)') + def CheckOpenUri(self): if sqlite.sqlite_version_info < (3, 7, 7): with self.assertRaises(sqlite.NotSupportedError): diff --git a/Misc/NEWS.d/next/Library/2017-11-07-00-37-50.bpo-31843.lM2gkR.rst b/Misc/NEWS.d/next/Library/2017-11-07-00-37-50.bpo-31843.lM2gkR.rst new file mode 100644 index 00000000000..c3142f05358 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-11-07-00-37-50.bpo-31843.lM2gkR.rst @@ -0,0 +1,2 @@ +*database* argument of sqlite3.connect() now accepts a +:term:`path-like object`, instead of just a string. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 70e56aad520..57eee2d32df 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -76,6 +76,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject }; char* database; + PyObject* database_obj; int detect_types = 0; PyObject* isolation_level = NULL; PyObject* factory = NULL; @@ -85,14 +86,16 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject double timeout = 5.0; int rc; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOip", kwlist, - &database, &timeout, &detect_types, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|diOiOip", kwlist, + PyUnicode_FSConverter, &database_obj, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements, &uri)) { return -1; } + database = PyBytes_AsString(database_obj); + self->initialized = 1; self->begin_statement = NULL; @@ -124,6 +127,8 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject #endif Py_END_ALLOW_THREADS + Py_DECREF(database_obj); + if (rc != SQLITE_OK) { _pysqlite_seterror(self->db, NULL); return -1; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index ffb711830bc..9066c32db55 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -55,7 +55,7 @@ static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* "check_same_thread", "factory", "cached_statements", "uri", NULL }; - char* database; + PyObject* database; int detect_types = 0; PyObject* isolation_level; PyObject* factory = NULL; @@ -66,7 +66,7 @@ static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* PyObject* result; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|diOiOip", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|diOiOip", kwlist, &database, &timeout, &detect_types, &isolation_level, &check_same_thread, &factory, &cached_statements, &uri))