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

Argument Clinic: make it possible to clone __init__ functions #107880

Closed
erlend-aasland opened this issue Aug 11, 2023 · 1 comment · Fixed by #107974
Closed

Argument Clinic: make it possible to clone __init__ functions #107880

erlend-aasland opened this issue Aug 11, 2023 · 1 comment · Fixed by #107974
Labels
topic-argument-clinic type-feature A feature request or enhancement

Comments

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Aug 11, 2023

See #93057 (comment)

In sqlite3, both sqlite3.connect and sqlite3.Connection.__init__ have the same param spec. This has led to the far-from-optimal status quo:

// NB: This needs to be in sync with the Connection.__init__ docstring.
PyDoc_STRVAR(module_connect_doc,
"connect($module, /, database, timeout=5.0, detect_types=0,\n"
" isolation_level='', check_same_thread=True,\n"
" factory=ConnectionType, cached_statements=128, uri=False, *,\n"
" autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL)\n"
"--\n"
"\n"
"Opens a connection to the SQLite database file database.\n"
"\n"
"You can use \":memory:\" to open a database connection to a database that resides\n"
"in RAM instead of on disk.");
#define PYSQLITE_CONNECT_METHODDEF \
{"connect", _PyCFunction_CAST(module_connect), METH_FASTCALL|METH_KEYWORDS, module_connect_doc},
static PyObject *
module_connect(PyObject *module, PyObject *const *args, Py_ssize_t nargsf,
PyObject *kwnames)

// NB: This needs to be in sync with the sqlite3.connect docstring
/*[clinic input]
_sqlite3.Connection.__init__ as pysqlite_connection_init
database: object
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=9b0ab6c12f674fa3]*/
{

Instead, we want to be able to do this in connection.c:

/*[clinic input]
sqlite3.Connection.__init__ as pysqlite_connection_init
    database: object
    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]*/

The methoddef and docstring for sqlite3.connect could then be included in module.c.

However, to achieve this, we need to teach Argument Clinic how to clone __init__ functions.

Linked PRs

@erlend-aasland
Copy link
Contributor Author

#107885 introduced a regression; it is now possible to clone from a normal method to either __new__ or __init__ bypassing the required checks. The following code should fail:

/*[clinic input]
class C "void *" ""
C.meth
  a: int
[clinic start generated code]*/
/*[clinic input]
# This should not be allowed!
C.__new__ = C.meth
[clinic start generated code]*/
/*[clinic input]
# Neither should this
@classmethod
C.__init__ = C.meth
[clinic start generated code]*/

erlend-aasland added a commit to erlend-aasland/cpython that referenced this issue Aug 15, 2023
pythongh-107885 taught Argument Clinic to clone to __init__ and __new__
methods, but it did not validate the requirements of those special
methods.
erlend-aasland added a commit that referenced this issue Aug 15, 2023
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-argument-clinic type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant