[Python-checkins] cpython: Eric Snow's implementation of PEP 421.

barry.warsaw python-checkins at python.org
Mon Jun 4 15:52:00 CEST 2012


http://hg.python.org/cpython/rev/9c445f4695c1
changeset:   77339:9c445f4695c1
parent:      77328:0808cb8c60fd
user:        Barry Warsaw <barry at python.org>
date:        Sun Jun 03 16:18:47 2012 -0400
summary:
  Eric Snow's implementation of PEP 421.

Issue 14673: Add sys.implementation

files:
  Doc/library/sys.rst       |   38 ++++
  Doc/library/types.rst     |   24 ++
  Include/Python.h          |    1 +
  Include/namespaceobject.h |   17 +
  Lib/test/test_sys.py      |   18 ++
  Lib/test/test_types.py    |  143 ++++++++++++++++-
  Lib/types.py              |    1 +
  Makefile.pre.in           |    2 +
  Objects/namespaceobject.c |  225 ++++++++++++++++++++++++++
  Objects/object.c          |    3 +
  Python/sysmodule.c        |   72 ++++++++-
  11 files changed, 541 insertions(+), 3 deletions(-)


diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -616,6 +616,44 @@
 
    Thus ``2.1.0a3`` is hexversion ``0x020100a3``.
 
+
+.. data:: implementation
+
+   An object containing the information about the implementation of the
+   currently running Python interpreter.  Its attributes are the those
+   that all Python implementations must implement.  They are described
+   below.
+
+   *name* is the implementation's identifier, like ``'cpython'``.
+
+   *version* is a named tuple, in the same format as
+   :data:`sys.version_info`.  It represents the version of the Python
+   *implementation*.  This has a distinct meaning from the specific
+   version of the Python *language* to which the currently running
+   interpreter conforms, which ``sys.version_info`` represents.  For
+   example, for PyPy 1.8 ``sys.implementation.version`` might be
+   ``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info``
+   would be ``sys.version_info(1, 8, 0, 'final', 0)``.  For CPython they
+   are the same value, since it is the reference implementation.
+
+   *hexversion* is the implementation version in hexadecimal format, like
+   :data:`sys.hexversion`.
+
+   *cache_tag* is the tag used by the import machinery in the filenames of
+   cached modules.  By convention, it would be a composite of the
+   implementation's name and version, like ``'cpython-33'``.  However, a
+   Python implementation may use some other value if appropriate.  If
+   ``cache_tag`` is set to ``None``, it indicates that module caching should
+   be disabled.
+
+   Regardless of its contents, :data:`sys.implementation` will not
+   change during a run of the interpreter, nor between implementation
+   versions.  (It may change between Python language versions,
+   however.)  See `PEP 421` for more information.
+
+   .. versionadded:: 3.3
+
+
 .. data:: int_info
 
    A :term:`struct sequence` that holds information about Python's internal
diff --git a/Doc/library/types.rst b/Doc/library/types.rst
--- a/Doc/library/types.rst
+++ b/Doc/library/types.rst
@@ -194,3 +194,27 @@
       Return a new view of the underlying mapping's values.
 
 
+.. class:: SimpleNamespace
+
+   A simple :class:`object` subclass that provides attribute access to its
+   namespace, as well as a meaningful repr.
+
+   Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove
+   attributes.  If a ``SimpleNamespace`` object is initialized with keyword
+   arguments, those are directly added to the underlying namespace.
+
+   The type is roughly equivalent to the following code::
+
+       class SimpleNamespace:
+           def __init__(self, **kwargs):
+               self.__dict__.update(kwargs)
+           def __repr__(self):
+               keys = sorted(self.__dict__)
+               items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
+               return "{}({})".format(type(self).__name__, ", ".join(items))
+
+   ``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``.
+   However, for a structured record type use :func:`~collections.namedtuple`
+   instead.
+
+   .. versionadded:: 3.3
diff --git a/Include/Python.h b/Include/Python.h
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -101,6 +101,7 @@
 #include "warnings.h"
 #include "weakrefobject.h"
 #include "structseq.h"
+#include "namespaceobject.h"
 
 #include "codecs.h"
 #include "pyerrors.h"
diff --git a/Include/namespaceobject.h b/Include/namespaceobject.h
new file mode 100644
--- /dev/null
+++ b/Include/namespaceobject.h
@@ -0,0 +1,17 @@
+
+/* simple namespace object interface */
+
+#ifndef NAMESPACEOBJECT_H
+#define NAMESPACEOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+PyAPI_DATA(PyTypeObject) _PyNamespace_Type;
+
+PyAPI_FUNC(PyObject *) _PyNamespace_New(PyObject *kwds);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !NAMESPACEOBJECT_H */
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -581,6 +581,24 @@
             expected = None
         self.check_fsencoding(fs_encoding, expected)
 
+    def test_implementation(self):
+        # This test applies to all implementations equally.
+
+        levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'release': 0xF}
+
+        self.assertTrue(hasattr(sys.implementation, 'name'))
+        self.assertTrue(hasattr(sys.implementation, 'version'))
+        self.assertTrue(hasattr(sys.implementation, 'hexversion'))
+        self.assertTrue(hasattr(sys.implementation, 'cache_tag'))
+
+        version = sys.implementation.version
+        self.assertEqual(version[:2], (version.major, version.minor))
+
+        hexversion = (version.major << 24 | version.minor << 16 |
+                      version.micro << 8 | levels[version.releaselevel] << 4 |
+                      version.serial << 0)
+        self.assertEqual(sys.implementation.hexversion, hexversion)
+
 
 class SizeofTest(unittest.TestCase):
 
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -996,8 +996,149 @@
             X = types.new_class("X", (int(), C))
 
 
+class SimpleNamespaceTests(unittest.TestCase):
+
+    def test_constructor(self):
+        ns1 = types.SimpleNamespace()
+        ns2 = types.SimpleNamespace(x=1, y=2)
+        ns3 = types.SimpleNamespace(**dict(x=1, y=2))
+
+        with self.assertRaises(TypeError):
+            types.SimpleNamespace(1, 2, 3)
+
+        self.assertEqual(len(ns1.__dict__), 0)
+        self.assertEqual(vars(ns1), {})
+        self.assertEqual(len(ns2.__dict__), 2)
+        self.assertEqual(vars(ns2), {'y': 2, 'x': 1})
+        self.assertEqual(len(ns3.__dict__), 2)
+        self.assertEqual(vars(ns3), {'y': 2, 'x': 1})
+
+    def test_unbound(self):
+        ns1 = vars(types.SimpleNamespace())
+        ns2 = vars(types.SimpleNamespace(x=1, y=2))
+
+        self.assertEqual(ns1, {})
+        self.assertEqual(ns2, {'y': 2, 'x': 1})
+
+    def test_underlying_dict(self):
+        ns1 = types.SimpleNamespace()
+        ns2 = types.SimpleNamespace(x=1, y=2)
+        ns3 = types.SimpleNamespace(a=True, b=False)
+        mapping = ns3.__dict__
+        del ns3
+
+        self.assertEqual(ns1.__dict__, {})
+        self.assertEqual(ns2.__dict__, {'y': 2, 'x': 1})
+        self.assertEqual(mapping, dict(a=True, b=False))
+
+    def test_attrget(self):
+        ns = types.SimpleNamespace(x=1, y=2, w=3)
+
+        self.assertEqual(ns.x, 1)
+        self.assertEqual(ns.y, 2)
+        self.assertEqual(ns.w, 3)
+        with self.assertRaises(AttributeError):
+            ns.z
+
+    def test_attrset(self):
+        ns1 = types.SimpleNamespace()
+        ns2 = types.SimpleNamespace(x=1, y=2, w=3)
+        ns1.a = 'spam'
+        ns1.b = 'ham'
+        ns2.z = 4
+        ns2.theta = None
+
+        self.assertEqual(ns1.__dict__, dict(a='spam', b='ham'))
+        self.assertEqual(ns2.__dict__, dict(x=1, y=2, w=3, z=4, theta=None))
+
+    def test_attrdel(self):
+        ns1 = types.SimpleNamespace()
+        ns2 = types.SimpleNamespace(x=1, y=2, w=3)
+
+        with self.assertRaises(AttributeError):
+            del ns1.spam
+        with self.assertRaises(AttributeError):
+            del ns2.spam
+
+        del ns2.y
+        self.assertEqual(vars(ns2), dict(w=3, x=1))
+        ns2.y = 'spam'
+        self.assertEqual(vars(ns2), dict(w=3, x=1, y='spam'))
+        del ns2.y
+        self.assertEqual(vars(ns2), dict(w=3, x=1))
+
+        ns1.spam = 5
+        self.assertEqual(vars(ns1), dict(spam=5))
+        del ns1.spam
+        self.assertEqual(vars(ns1), {})
+
+    def test_repr(self):
+        ns1 = types.SimpleNamespace(x=1, y=2, w=3)
+        ns2 = types.SimpleNamespace()
+        ns2.x = "spam"
+        ns2._y = 5
+
+        self.assertEqual(repr(ns1), "namespace(w=3, x=1, y=2)")
+        self.assertEqual(repr(ns2), "namespace(_y=5, x='spam')")
+
+    def test_nested(self):
+        ns1 = types.SimpleNamespace(a=1, b=2)
+        ns2 = types.SimpleNamespace()
+        ns3 = types.SimpleNamespace(x=ns1)
+        ns2.spam = ns1
+        ns2.ham = '?'
+        ns2.spam = ns3
+
+        self.assertEqual(vars(ns1), dict(a=1, b=2))
+        self.assertEqual(vars(ns2), dict(spam=ns3, ham='?'))
+        self.assertEqual(ns2.spam, ns3)
+        self.assertEqual(vars(ns3), dict(x=ns1))
+        self.assertEqual(ns3.x.a, 1)
+
+    def test_recursive(self):
+        ns1 = types.SimpleNamespace(c='cookie')
+        ns2 = types.SimpleNamespace()
+        ns3 = types.SimpleNamespace(x=1)
+        ns1.spam = ns1
+        ns2.spam = ns3
+        ns3.spam = ns2
+
+        self.assertEqual(ns1.spam, ns1)
+        self.assertEqual(ns1.spam.spam, ns1)
+        self.assertEqual(ns1.spam.spam, ns1.spam)
+        self.assertEqual(ns2.spam, ns3)
+        self.assertEqual(ns3.spam, ns2)
+        self.assertEqual(ns2.spam.spam, ns2)
+
+    def test_recursive_repr(self):
+        ns1 = types.SimpleNamespace(c='cookie')
+        ns2 = types.SimpleNamespace()
+        ns3 = types.SimpleNamespace(x=1)
+        ns1.spam = ns1
+        ns2.spam = ns3
+        ns3.spam = ns2
+
+        self.assertEqual(repr(ns1),
+                         "namespace(c='cookie', spam=namespace(...))")
+        self.assertEqual(repr(ns2),
+                         "namespace(spam=namespace(spam=namespace(...), x=1))")
+
+    def test_as_dict(self):
+        ns = types.SimpleNamespace(spam='spamspamspam')
+
+        with self.assertRaises(TypeError):
+            len(ns)
+        with self.assertRaises(TypeError):
+            iter(ns)
+        with self.assertRaises(TypeError):
+            'spam' in ns
+        with self.assertRaises(TypeError):
+            ns['spam']
+
+
 def test_main():
-    run_unittest(TypesTests, MappingProxyTests, ClassCreationTests)
+    run_unittest(TypesTests, MappingProxyTests, ClassCreationTests,
+                 SimpleNamespaceTests)
 
 if __name__ == '__main__':
     test_main()
diff --git a/Lib/types.py b/Lib/types.py
--- a/Lib/types.py
+++ b/Lib/types.py
@@ -13,6 +13,7 @@
 LambdaType = type(lambda: None)         # Same as FunctionType
 CodeType = type(_f.__code__)
 MappingProxyType = type(type.__dict__)
+SimpleNamespace = type(sys.implementation)
 
 def _g():
     yield 1
diff --git a/Makefile.pre.in b/Makefile.pre.in
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -392,6 +392,7 @@
 		Objects/memoryobject.o \
 		Objects/methodobject.o \
 		Objects/moduleobject.o \
+		Objects/namespaceobject.o \
 		Objects/object.o \
 		Objects/obmalloc.o \
 		Objects/capsule.o \
@@ -766,6 +767,7 @@
 		$(srcdir)/Include/methodobject.h \
 		$(srcdir)/Include/modsupport.h \
 		$(srcdir)/Include/moduleobject.h \
+		$(srcdir)/Include/namespaceobject.h \
 		$(srcdir)/Include/node.h \
 		$(srcdir)/Include/object.h \
 		$(srcdir)/Include/objimpl.h \
diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c
new file mode 100644
--- /dev/null
+++ b/Objects/namespaceobject.c
@@ -0,0 +1,225 @@
+/* namespace object implementation */
+
+#include "Python.h"
+#include "structmember.h"
+
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *ns_dict;
+} _PyNamespaceObject;
+
+
+static PyMemberDef namespace_members[] = {
+    {"__dict__", T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), READONLY},
+    {NULL}
+};
+
+
+/* Methods */
+
+static PyObject *
+namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    _PyNamespaceObject *ns;
+    ns = PyObject_GC_New(_PyNamespaceObject, &_PyNamespace_Type);
+    if (ns == NULL)
+        return NULL;
+
+    ns->ns_dict = PyDict_New();
+    if (ns->ns_dict == NULL) {
+        Py_DECREF(ns);
+        return NULL;
+    }
+
+    PyObject_GC_Track(ns);
+    return (PyObject *)ns;
+}
+
+
+static int
+namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds)
+{
+    /* ignore args if it's NULL or empty */
+    if (args != NULL) {
+        Py_ssize_t argcount = PyObject_Size(args);
+        if (argcount < 0)
+            return argcount;
+        else if (argcount > 0) {
+            PyErr_Format(PyExc_TypeError, "no positional arguments expected");
+            return -1;
+        }
+    }
+    if (kwds == NULL)
+        return 0;
+    return PyDict_Update(ns->ns_dict, kwds);
+}
+
+
+static void
+namespace_dealloc(_PyNamespaceObject *ns)
+{
+    PyObject_GC_UnTrack(ns);
+    Py_CLEAR(ns->ns_dict);
+    Py_TYPE(ns)->tp_free((PyObject *)ns);
+}
+
+
+static PyObject *
+namespace_repr(_PyNamespaceObject *ns)
+{
+    int i, loop_error = 0;
+    PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL;
+    PyObject *key;
+    PyObject *separator, *pairsrepr, *repr = NULL;
+
+    i = Py_ReprEnter((PyObject *)ns);
+    if (i != 0) {
+        return i > 0 ? PyUnicode_FromString("namespace(...)") : NULL;
+    }
+
+    pairs = PyList_New(0);
+    if (pairs == NULL)
+        goto error;
+
+    d = ((_PyNamespaceObject *)ns)->ns_dict;
+    assert(d != NULL);
+    Py_INCREF(d);
+
+    keys = PyDict_Keys(d);
+    if (keys == NULL)
+        goto error;
+    if (PyList_Sort(keys) != 0)
+        goto error;
+
+    keys_iter = PyObject_GetIter(keys);
+    if (keys_iter == NULL)
+        goto error;
+
+    while ((key = PyIter_Next(keys_iter)) != NULL) {
+        if (PyUnicode_Check(key) && PyUnicode_GET_SIZE(key) > 0) {
+            PyObject *value, *item;
+
+            value = PyDict_GetItem(d, key);
+            assert(value != NULL);
+
+            item = PyUnicode_FromFormat("%S=%R", key, value);
+            if (item == NULL) {
+                loop_error = 1;
+            }
+            else {
+                loop_error = PyList_Append(pairs, item);
+                Py_DECREF(item);
+            }
+        }
+
+        Py_DECREF(key);
+        if (loop_error)
+            goto error;
+    }
+
+    separator = PyUnicode_FromString(", ");
+    if (separator == NULL)
+        goto error;
+
+    pairsrepr = PyUnicode_Join(separator, pairs);
+    Py_DECREF(separator);
+    if (pairsrepr == NULL)
+        goto error;
+
+    repr = PyUnicode_FromFormat("%s(%S)",
+                                ((PyObject *)ns)->ob_type->tp_name, pairsrepr);
+    Py_DECREF(pairsrepr);
+
+error:
+    Py_XDECREF(pairs);
+    Py_XDECREF(d);
+    Py_XDECREF(keys);
+    Py_XDECREF(keys_iter);
+    Py_ReprLeave((PyObject *)ns);
+
+    return repr;
+}
+
+
+static int
+namespace_traverse(_PyNamespaceObject *ns, visitproc visit, void *arg)
+{
+    Py_VISIT(ns->ns_dict);
+    return 0;
+}
+
+
+static int
+namespace_clear(_PyNamespaceObject *ns)
+{
+    Py_CLEAR(ns->ns_dict);
+    return 0;
+}
+
+
+PyDoc_STRVAR(namespace_doc,
+"A simple attribute-based namespace.\n\
+\n\
+namespace(**kwargs)");
+
+PyTypeObject _PyNamespace_Type = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    "namespace",                                /* tp_name */
+    sizeof(_PyNamespaceObject),                 /* tp_size */
+    0,                                          /* tp_itemsize */
+    (destructor)namespace_dealloc,              /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_reserved */
+    (reprfunc)namespace_repr,                   /* tp_repr */
+    0,                                          /* tp_as_number */
+    0,                                          /* tp_as_sequence */
+    0,                                          /* tp_as_mapping */
+    0,                                          /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    PyObject_GenericSetAttr,                    /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+        Py_TPFLAGS_BASETYPE,                    /* tp_flags */
+    namespace_doc,                              /* tp_doc */
+    (traverseproc)namespace_traverse,           /* tp_traverse */
+    (inquiry)namespace_clear,                   /* tp_clear */
+    0,                                          /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    0,                                          /* tp_methods */
+    namespace_members,                          /* tp_members */
+    0,                                          /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    offsetof(_PyNamespaceObject, ns_dict),      /* tp_dictoffset */
+    (initproc)namespace_init,                   /* tp_init */
+    PyType_GenericAlloc,                        /* tp_alloc */
+    (newfunc)namespace_new,                     /* tp_new */
+    PyObject_GC_Del,                            /* tp_free */
+};
+
+
+PyObject *
+_PyNamespace_New(PyObject *kwds)
+{
+    PyObject *ns = namespace_new(&_PyNamespace_Type, NULL, NULL);
+    if (ns == NULL)
+        return NULL;
+
+    if (kwds == NULL)
+        return ns;
+    if (PyDict_Update(((_PyNamespaceObject *)ns)->ns_dict, kwds) != 0) {
+        Py_DECREF(ns);
+        return NULL;
+    }
+
+    return (PyObject *)ns;
+}
diff --git a/Objects/object.c b/Objects/object.c
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1707,6 +1707,9 @@
 
     if (PyType_Ready(&PyZip_Type) < 0)
         Py_FatalError("Can't initialize zip type");
+
+    if (PyType_Ready(&_PyNamespace_Type) < 0)
+        Py_FatalError("Can't initialize namespace type");
 }
 
 
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1261,6 +1261,7 @@
 float_info -- a struct sequence with information about the float implementation.\n\
 float_repr_style -- string indicating the style of repr() output for floats\n\
 hexversion -- version information encoded as a single integer\n\
+implementation -- Python implementation information.\n\
 int_info -- a struct sequence with information about the int implementation.\n\
 maxsize -- the largest supported length of containers.\n\
 maxunicode -- the value of the largest Unicode codepoint\n\
@@ -1454,6 +1455,69 @@
     return version_info;
 }
 
+static PyObject *
+make_impl_info(PyObject *version_info)
+{
+    int res;
+    PyObject *impl_info, *value, *ns;
+
+    impl_info = PyDict_New();
+    if (impl_info == NULL)
+        return NULL;
+
+    /* populate the dict */
+
+#define NAME "cpython"
+#define QUOTE(arg) #arg
+#define STRIFY(name) QUOTE(name)
+#define MAJOR STRIFY(PY_MAJOR_VERSION)
+#define MINOR STRIFY(PY_MINOR_VERSION)
+#define TAG NAME "-" MAJOR MINOR
+    value = PyUnicode_FromString(NAME);
+    if (value == NULL)
+        goto error;
+    res = PyDict_SetItemString(impl_info, "name", value);
+    Py_DECREF(value);
+    if (res < 0)
+        goto error;
+
+    value = PyUnicode_FromString(TAG);
+    if (value == NULL)
+        goto error;
+    res = PyDict_SetItemString(impl_info, "cache_tag", value);
+    Py_DECREF(value);
+    if (res < 0)
+        goto error;
+#undef NAME
+#undef QUOTE
+#undef STRIFY
+#undef MAJOR
+#undef MINOR
+#undef TAG
+
+    res = PyDict_SetItemString(impl_info, "version", version_info);
+    if (res < 0)
+        goto error;
+
+    value = PyLong_FromLong(PY_VERSION_HEX);
+    if (value == NULL)
+        goto error;
+    res = PyDict_SetItemString(impl_info, "hexversion", value);
+    Py_DECREF(value);
+    if (res < 0)
+        goto error;
+
+    /* dict ready */
+
+    ns = _PyNamespace_New(impl_info);
+    Py_DECREF(impl_info);
+    return ns;
+
+error:
+    Py_CLEAR(impl_info);
+    return NULL;
+}
+
 static struct PyModuleDef sysmodule = {
     PyModuleDef_HEAD_INIT,
     "sys",
@@ -1469,7 +1533,7 @@
 PyObject *
 _PySys_Init(void)
 {
-    PyObject *m, *v, *sysdict;
+    PyObject *m, *v, *sysdict, *version_info;
     char *s;
 
     m = PyModule_Create(&sysmodule);
@@ -1589,11 +1653,15 @@
     /* version_info */
     if (VersionInfoType.tp_name == 0)
         PyStructSequence_InitType(&VersionInfoType, &version_info_desc);
-    SET_SYS_FROM_STRING("version_info", make_version_info());
+    version_info = make_version_info();
+    SET_SYS_FROM_STRING("version_info", version_info);
     /* prevent user from creating new instances */
     VersionInfoType.tp_init = NULL;
     VersionInfoType.tp_new = NULL;
 
+    /* implementation */
+    SET_SYS_FROM_STRING("implementation", make_impl_info(version_info));
+
     /* flags */
     if (FlagsType.tp_name == 0)
         PyStructSequence_InitType(&FlagsType, &flags_desc);

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list