Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make optional parameters of sqlite3.connect() keyword-only #93057

Closed
serhiy-storchaka opened this issue May 21, 2022 · 9 comments
Closed

Make optional parameters of sqlite3.connect() keyword-only #93057

serhiy-storchaka opened this issue May 21, 2022 · 9 comments
Labels
topic-sqlite3 triaged The issue has been accepted as valid by a triager. type-feature A feature request or enhancement

Comments

@serhiy-storchaka
Copy link
Member

serhiy-storchaka commented May 21, 2022

sqlite3.connect() has a large number of parameters: 1 required and 7 optional. It is much more convenient to pass optional arguments by keyword, because you can pass only values which differs from default. And if you pass positional arguments, it is easy to make an error and pass value as wrong argument.

It is recommended to make optional rarely used arguments keyword-only. I do not think it will break much code, but we need a deprecation period for this.

The problem is that sqlite3.connect() was converted to Argument Clinic, and it was much easier to add deprecation warning in the old code.

Linked PRs

@serhiy-storchaka serhiy-storchaka added type-feature A feature request or enhancement topic-sqlite3 labels May 21, 2022
@erlend-aasland
Copy link
Contributor

The problem is that sqlite3.connect() was converted to Argument Clinic, and it was much easier to add deprecation warning in the old code.

Can we enhance AC with such functionality first?

@serhiy-storchaka
Copy link
Member Author

Can we enhance AC with such functionality first?

It would be not easy. 😫

@erlend-aasland
Copy link
Contributor

It would be not easy. 😫

That I assumed 🙂 Do you have a coarse outline in mind of how such a change could be implemented?

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Jun 24, 2022

Can we enhance AC with such functionality first?

It would be not easy. 😫

Continuing the AC digression :)
I made a proof-of-concept of such AC functionality here: erlend-aasland#37
It is a very crude implementation; there's a lot of corner cases missing checks. See details for more info. Feel free to play around with it, and/or improve it (or even finish it). We make an issue to follow up this now.

I introduced a new special symbol, x, that means "all optional parameters from now on are deprecated as positional parameters". We can use any other symbol or string. For example d/ (deprecated positional), -/, deprecated/. I liked the brevity of x, and how it visually kinda resembles *.

Example AC code:

/*[clinic input]
_sqlite3.connect as pysqlite_connect

    database: object
    timeout: double = 5.0
    detect_types: int = 0
    isolation_level: object = NULL
    check_same_thread: bool(accept={int}) = True
    factory: object(c_default='(PyObject*)clinic_state()->ConnectionType') = ConnectionType
    x
    cached_statements: int = 128
    uri: bool = False

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.
[clinic start generated code]*/

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Jun 26, 2022

It is recommended to make optional rarely used arguments keyword-only. I do not think it will break much code, but we need a deprecation period for this.

I suggest we make all but the database argument keyword-only. If my AC hack is a possible way forward1, we can do this in 3.12.

Also, the database argument should be positional-only.

Footnotes

  1. It is a hack; I didn't think through all possible scenarios, or even if it is the best path forward.

@erlend-aasland
Copy link
Contributor

Argument Clinic now has support for deprecating positional use of positional-or-keyword parameters. However, there are still some prerequisites that must be in place before we can continue with this issue:

  1. Currently, the clinic code in Modules/_sqlite uses module _sqlite3. This means that deprecation warnings will mention _sqlite3.connect(), not sqlite3.connect(). This can be confusing for users. I suggest we change the clinic code to use module sqlite3 instead. This change should not result in any behaviour change.
  2. Next, we have some technical dept when it comes to sqlite3.connect and sqlite3.Connection.__init__. Currently, the latter uses clinic, and the former does not use clinic. However, it is sqlite3.connect that needs the docstring, not sqlite3.Connection.__init__. We should make it so that the docstring from sqlite3.Connection.__init__ is used for sqlite3.connect and output to a separate file, for inclusion by Module/_sqlite/module.c.
  3. In order to achieve 2. properly, we must make it possible for clinic.py to clone sqlite3.Connection.__init__ to sqlite3.connect.

This is roughly what the relevant parts of Modules/_sqlite/connection.c will look like:

/*[clinic input]
sqlite3.Connection.__init__ as pysqlite_connection_init
    database: object
    * [from 3.15]
    timeout: double = 5.0
    detect_types: int = 0
    isolation_level: IsolationLevel = ""
    check_same_thread: bool = True
    factory: object(c_default='(PyObject*)clinic_state()->ConnectionType') = ConnectionType
    cached_statements as cache_size: int = 128
    uri: bool = False
    *
    autocommit: Autocommit(c_default='LEGACY_TRANSACTION_CONTROL') = sqlite3.LEGACY_TRANSACTION_CONTROL
[clinic start generated code]*/

static int
pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
                              double timeout, int detect_types,
                              const char *isolation_level,
                              int check_same_thread, PyObject *factory,
                              int cache_size, int uri,
                              enum autocommit_mode autocommit)
/*[clinic end generated code: output=cba057313ea7712f input=a0949fb85339104d]*/
{
    // __init__ function is here; fast forward ...
}

/*[clinic input]
# Save the clinic output config.
output push

# Create a new destination 'connect' for the docstring and methoddef only.
destination connect new file '{dirname}/clinic/_sqlite3.connect.c.h'
output everything suppress
output docstring_definition connect
output methoddef_define connect

# Now, define the connect function.
sqlite3.connect as pysqlite_connect = sqlite3.Connection.__init__

[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7913cd0b3bfc1b4a]*/

/*[clinic input]
# Restore the clinic output config.
output pop
[clinic start generated code]*/

Then, in Modules/_sqlite/module.c:

#include "clinic/_sqlite3.connect.c.h"

static PyObject *
pysqlite_connect(PyObject *module, PyObject *const *args, Py_ssize_t nargsf,
               PyObject *kwnames)
{
    // ...
}

// ... and then:

static PyMethodDef module_methods[] = {
    PYSQLITE_ADAPT_METHODDEF
    PYSQLITE_COMPLETE_STATEMENT_METHODDEF
    PYSQLITE_CONNECT_METHODDEF    // <= this now comes from clinic/_sqlite3.connect.c.h
    PYSQLITE_ENABLE_CALLBACK_TRACE_METHODDEF
    PYSQLITE_REGISTER_ADAPTER_METHODDEF
    PYSQLITE_REGISTER_CONVERTER_METHODDEF
    {NULL, NULL}
};

erlend-aasland added a commit to erlend-aasland/cpython that referenced this issue Aug 11, 2023
…les/_sqlite/connection.c

This change has no impact on behaviour. It is needed only for Argument
Clinic to produce nicer deprecation messages when we get to deprecate
positional use of optional parameters to sqlite3.connect().
erlend-aasland added a commit to erlend-aasland/cpython that referenced this issue Aug 14, 2023
@erlend-aasland
Copy link
Contributor

Thanks for suggesting this feature and helping land it (and all its implications in Argument Clinic), @serhiy-storchaka. Very helpful, and a good learning experience.

@erlend-aasland
Copy link
Contributor

For the record, it was the Argument Clinic implications of this feature that spawned the desire to add typing to clinic.py (#104050), which in turn got both @AlexWaygood and me highly involved with Argument Clinic. We've now ended up in a position where more core devs have better knowledge of the inner workings of clinic.py; this is a good thing. Thanks, again!

@erlend-aasland
Copy link
Contributor

Also, the database argument should be positional-only.

If this is desired, we should create a new issue for it. cc. @serhiy-storchaka

iritkatriel pushed a commit to iritkatriel/cpython that referenced this issue Aug 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-sqlite3 triaged The issue has been accepted as valid by a triager. type-feature A feature request or enhancement
Projects
Status: Done
Development

No branches or pull requests

2 participants