[Python-checkins] gh-97955: Migrate `zoneinfo` to Argument Clinic (#97958)

ambv webhook-mailer at python.org
Fri Oct 7 14:06:32 EDT 2022


https://github.com/python/cpython/commit/24a664589448d99a62386d5afa180cfb52733a7e
commit: 24a664589448d99a62386d5afa180cfb52733a7e
branch: main
author: Nikita Sobolev <mail at sobolevn.me>
committer: ambv <lukasz at langa.pl>
date: 2022-10-07T11:06:23-07:00
summary:

gh-97955: Migrate `zoneinfo` to Argument Clinic (#97958)

files:
A Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst
A Modules/clinic/_zoneinfo.c.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Lib/test/test_zoneinfo/test_zoneinfo.py
M Modules/_zoneinfo.c

diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index 94f56fadc463..2966b60e0cd8 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -494,6 +494,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(offset_src)
         STRUCT_FOR_ID(on_type_read)
         STRUCT_FOR_ID(onceregistry)
+        STRUCT_FOR_ID(only_keys)
         STRUCT_FOR_ID(oparg)
         STRUCT_FOR_ID(opcode)
         STRUCT_FOR_ID(open)
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index ea01fc01bbef..617582f96e33 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -1003,6 +1003,7 @@ extern "C" {
                 INIT_ID(offset_src), \
                 INIT_ID(on_type_read), \
                 INIT_ID(onceregistry), \
+                INIT_ID(only_keys), \
                 INIT_ID(oparg), \
                 INIT_ID(opcode), \
                 INIT_ID(open), \
@@ -2313,6 +2314,8 @@ _PyUnicode_InitStaticStrings(void) {
     PyUnicode_InternInPlace(&string);
     string = &_Py_ID(onceregistry);
     PyUnicode_InternInPlace(&string);
+    string = &_Py_ID(only_keys);
+    PyUnicode_InternInPlace(&string);
     string = &_Py_ID(oparg);
     PyUnicode_InternInPlace(&string);
     string = &_Py_ID(opcode);
@@ -6554,6 +6557,10 @@ _PyStaticObjects_CheckRefcnt(void) {
         _PyObject_Dump((PyObject *)&_Py_ID(onceregistry));
         Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT");
     };
+    if (Py_REFCNT((PyObject *)&_Py_ID(only_keys)) < _PyObject_IMMORTAL_REFCNT) {
+        _PyObject_Dump((PyObject *)&_Py_ID(only_keys));
+        Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT");
+    };
     if (Py_REFCNT((PyObject *)&_Py_ID(oparg)) < _PyObject_IMMORTAL_REFCNT) {
         _PyObject_Dump((PyObject *)&_Py_ID(oparg));
         Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT");
diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py
index a2172f3ac21d..fd0e3bc032ec 100644
--- a/Lib/test/test_zoneinfo/test_zoneinfo.py
+++ b/Lib/test/test_zoneinfo/test_zoneinfo.py
@@ -404,6 +404,19 @@ def test_time_fixed_offset(self):
 class CZoneInfoTest(ZoneInfoTest):
     module = c_zoneinfo
 
+    def test_signatures(self):
+        """Ensure that C module has valid method signatures."""
+        import inspect
+
+        must_have_signatures = (
+            self.klass.clear_cache,
+            self.klass.no_cache,
+            self.klass.from_file,
+        )
+        for method in must_have_signatures:
+            with self.subTest(method=method):
+                inspect.Signature.from_callable(method)
+
     def test_fold_mutate(self):
         """Test that fold isn't mutated when no change is necessary.
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst
new file mode 100644
index 000000000000..e21794df4f18
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-06-14-14-28.gh-issue-97955.Nq5VXD.rst	
@@ -0,0 +1 @@
+Migrate :mod:`zoneinfo` to Argument Clinic.
diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c
index 207340adec15..36b25bf3c054 100644
--- a/Modules/_zoneinfo.c
+++ b/Modules/_zoneinfo.c
@@ -12,6 +12,13 @@
 
 #include "datetime.h"
 
+#include "clinic/_zoneinfo.c.h"
+/*[clinic input]
+module zoneinfo
+class zoneinfo.ZoneInfo "PyObject *" "PyTypeObject *"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d12c73c0eef36df8]*/
+
 // Imports
 static PyObject *io_open = NULL;
 static PyObject *_tzpath_find_tzfile = NULL;
@@ -338,20 +345,25 @@ zoneinfo_dealloc(PyObject *obj_self)
     Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
+/*[clinic input]
+ at classmethod
+zoneinfo.ZoneInfo.from_file
+
+    file_obj: object
+    /
+    key: object = None
+
+Create a ZoneInfo file from a file object.
+[clinic start generated code]*/
+
 static PyObject *
-zoneinfo_from_file(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj,
+                                 PyObject *key)
+/*[clinic end generated code: output=68ed2022404ae5be input=ccfe73708133d2e4]*/
 {
-    PyObject *file_obj = NULL;
     PyObject *file_repr = NULL;
-    PyObject *key = Py_None;
     PyZoneInfo_ZoneInfo *self = NULL;
 
-    static char *kwlist[] = {"", "key", NULL};
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &file_obj,
-                                     &key)) {
-        return NULL;
-    }
-
     PyObject *obj_self = (PyObject *)(type->tp_alloc(type, 0));
     self = (PyZoneInfo_ZoneInfo *)obj_self;
     if (self == NULL) {
@@ -379,16 +391,20 @@ zoneinfo_from_file(PyTypeObject *type, PyObject *args, PyObject *kwargs)
     return NULL;
 }
 
+/*[clinic input]
+ at classmethod
+zoneinfo.ZoneInfo.no_cache
+
+    key: object
+
+Get a new instance of ZoneInfo, bypassing the cache.
+[clinic start generated code]*/
+
 static PyObject *
-zoneinfo_no_cache(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key)
+/*[clinic end generated code: output=751c6894ad66f91b input=bb24afd84a80ba46]*/
 {
-    static char *kwlist[] = {"key", NULL};
-    PyObject *key = NULL;
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &key)) {
-        return NULL;
-    }
-
-    PyObject *out = zoneinfo_new_instance(cls, key);
+    PyObject *out = zoneinfo_new_instance(type, key);
     if (out != NULL) {
         ((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE;
     }
@@ -396,18 +412,20 @@ zoneinfo_no_cache(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
     return out;
 }
 
-static PyObject *
-zoneinfo_clear_cache(PyObject *cls, PyObject *args, PyObject *kwargs)
-{
-    PyObject *only_keys = NULL;
-    static char *kwlist[] = {"only_keys", NULL};
+/*[clinic input]
+ at classmethod
+zoneinfo.ZoneInfo.clear_cache
 
-    if (!(PyArg_ParseTupleAndKeywords(args, kwargs, "|$O", kwlist,
-                                      &only_keys))) {
-        return NULL;
-    }
+    *
+    only_keys: object = None
 
-    PyTypeObject *type = (PyTypeObject *)cls;
+Clear the ZoneInfo cache.
+[clinic start generated code]*/
+
+static PyObject *
+zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys)
+/*[clinic end generated code: output=eec0a3276f07bd90 input=8cff0182a95f295b]*/
+{
     PyObject *weak_cache = get_weak_cache(type);
 
     if (only_keys == NULL || only_keys == Py_None) {
@@ -2545,15 +2563,9 @@ zoneinfo_init_subclass(PyTypeObject *cls, PyObject *args, PyObject **kwargs)
 /////
 // Specify the ZoneInfo type
 static PyMethodDef zoneinfo_methods[] = {
-    {"clear_cache", (PyCFunction)(void (*)(void))zoneinfo_clear_cache,
-     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
-     PyDoc_STR("Clear the ZoneInfo cache.")},
-    {"no_cache", (PyCFunction)(void (*)(void))zoneinfo_no_cache,
-     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
-     PyDoc_STR("Get a new instance of ZoneInfo, bypassing the cache.")},
-    {"from_file", (PyCFunction)(void (*)(void))zoneinfo_from_file,
-     METH_VARARGS | METH_KEYWORDS | METH_CLASS,
-     PyDoc_STR("Create a ZoneInfo file from a file object.")},
+    ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF
+    ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF
+    ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF
     {"utcoffset", (PyCFunction)zoneinfo_utcoffset, METH_O,
      PyDoc_STR("Retrieve a timedelta representing the UTC offset in a zone at "
                "the given datetime.")},
diff --git a/Modules/clinic/_zoneinfo.c.h b/Modules/clinic/_zoneinfo.c.h
new file mode 100644
index 000000000000..78fcbfa9411b
--- /dev/null
+++ b/Modules/clinic/_zoneinfo.c.h
@@ -0,0 +1,188 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+#  include "pycore_gc.h"            // PyGC_Head
+#  include "pycore_runtime.h"       // _Py_ID()
+#endif
+
+
+PyDoc_STRVAR(zoneinfo_ZoneInfo_from_file__doc__,
+"from_file($type, file_obj, /, key=None)\n"
+"--\n"
+"\n"
+"Create a ZoneInfo file from a file object.");
+
+#define ZONEINFO_ZONEINFO_FROM_FILE_METHODDEF    \
+    {"from_file", _PyCFunction_CAST(zoneinfo_ZoneInfo_from_file), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_from_file__doc__},
+
+static PyObject *
+zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyObject *file_obj,
+                                 PyObject *key);
+
+static PyObject *
+zoneinfo_ZoneInfo_from_file(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 1
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_item = { &_Py_ID(key), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"", "key", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "from_file",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
+    PyObject *file_obj;
+    PyObject *key = Py_None;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    file_obj = args[0];
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    key = args[1];
+skip_optional_pos:
+    return_value = zoneinfo_ZoneInfo_from_file_impl(type, file_obj, key);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(zoneinfo_ZoneInfo_no_cache__doc__,
+"no_cache($type, /, key)\n"
+"--\n"
+"\n"
+"Get a new instance of ZoneInfo, bypassing the cache.");
+
+#define ZONEINFO_ZONEINFO_NO_CACHE_METHODDEF    \
+    {"no_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_no_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_no_cache__doc__},
+
+static PyObject *
+zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyObject *key);
+
+static PyObject *
+zoneinfo_ZoneInfo_no_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 1
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_item = { &_Py_ID(key), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"key", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "no_cache",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[1];
+    PyObject *key;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    key = args[0];
+    return_value = zoneinfo_ZoneInfo_no_cache_impl(type, key);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(zoneinfo_ZoneInfo_clear_cache__doc__,
+"clear_cache($type, /, *, only_keys=None)\n"
+"--\n"
+"\n"
+"Clear the ZoneInfo cache.");
+
+#define ZONEINFO_ZONEINFO_CLEAR_CACHE_METHODDEF    \
+    {"clear_cache", _PyCFunction_CAST(zoneinfo_ZoneInfo_clear_cache), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, zoneinfo_ZoneInfo_clear_cache__doc__},
+
+static PyObject *
+zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyObject *only_keys);
+
+static PyObject *
+zoneinfo_ZoneInfo_clear_cache(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 1
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_item = { &_Py_ID(only_keys), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"only_keys", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "clear_cache",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[1];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+    PyObject *only_keys = Py_None;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    if (!noptargs) {
+        goto skip_optional_kwonly;
+    }
+    only_keys = args[0];
+skip_optional_kwonly:
+    return_value = zoneinfo_ZoneInfo_clear_cache_impl(type, only_keys);
+
+exit:
+    return return_value;
+}
+/*[clinic end generated code: output=d2da73ef66146b83 input=a9049054013a1b77]*/



More information about the Python-checkins mailing list