Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 409da15

Browse files
committed
Eric Snow's implementation of PEP 421.
Issue 14673: Add sys.implementation
1 parent 82ffabd commit 409da15

11 files changed

Lines changed: 541 additions & 3 deletions

File tree

Doc/library/sys.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,44 @@ always available.
616616

617617
Thus ``2.1.0a3`` is hexversion ``0x020100a3``.
618618

619+
620+
.. data:: implementation
621+
622+
An object containing the information about the implementation of the
623+
currently running Python interpreter. Its attributes are the those
624+
that all Python implementations must implement. They are described
625+
below.
626+
627+
*name* is the implementation's identifier, like ``'cpython'``.
628+
629+
*version* is a named tuple, in the same format as
630+
:data:`sys.version_info`. It represents the version of the Python
631+
*implementation*. This has a distinct meaning from the specific
632+
version of the Python *language* to which the currently running
633+
interpreter conforms, which ``sys.version_info`` represents. For
634+
example, for PyPy 1.8 ``sys.implementation.version`` might be
635+
``sys.version_info(1, 8, 0, 'final', 0)``, whereas ``sys.version_info``
636+
would be ``sys.version_info(1, 8, 0, 'final', 0)``. For CPython they
637+
are the same value, since it is the reference implementation.
638+
639+
*hexversion* is the implementation version in hexadecimal format, like
640+
:data:`sys.hexversion`.
641+
642+
*cache_tag* is the tag used by the import machinery in the filenames of
643+
cached modules. By convention, it would be a composite of the
644+
implementation's name and version, like ``'cpython-33'``. However, a
645+
Python implementation may use some other value if appropriate. If
646+
``cache_tag`` is set to ``None``, it indicates that module caching should
647+
be disabled.
648+
649+
Regardless of its contents, :data:`sys.implementation` will not
650+
change during a run of the interpreter, nor between implementation
651+
versions. (It may change between Python language versions,
652+
however.) See `PEP 421` for more information.
653+
654+
.. versionadded:: 3.3
655+
656+
619657
.. data:: int_info
620658

621659
A :term:`struct sequence` that holds information about Python's internal

Doc/library/types.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,27 @@ Standard names are defined for the following types:
194194
Return a new view of the underlying mapping's values.
195195

196196

197+
.. class:: SimpleNamespace
198+
199+
A simple :class:`object` subclass that provides attribute access to its
200+
namespace, as well as a meaningful repr.
201+
202+
Unlike :class:`object`, with ``SimpleNamespace`` you can add and remove
203+
attributes. If a ``SimpleNamespace`` object is initialized with keyword
204+
arguments, those are directly added to the underlying namespace.
205+
206+
The type is roughly equivalent to the following code::
207+
208+
class SimpleNamespace:
209+
def __init__(self, **kwargs):
210+
self.__dict__.update(kwargs)
211+
def __repr__(self):
212+
keys = sorted(self.__dict__)
213+
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
214+
return "{}({})".format(type(self).__name__, ", ".join(items))
215+
216+
``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``.
217+
However, for a structured record type use :func:`~collections.namedtuple`
218+
instead.
219+
220+
.. versionadded:: 3.3

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
#include "warnings.h"
102102
#include "weakrefobject.h"
103103
#include "structseq.h"
104+
#include "namespaceobject.h"
104105

105106
#include "codecs.h"
106107
#include "pyerrors.h"

Include/namespaceobject.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
/* simple namespace object interface */
3+
4+
#ifndef NAMESPACEOBJECT_H
5+
#define NAMESPACEOBJECT_H
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
PyAPI_DATA(PyTypeObject) _PyNamespace_Type;
11+
12+
PyAPI_FUNC(PyObject *) _PyNamespace_New(PyObject *kwds);
13+
14+
#ifdef __cplusplus
15+
}
16+
#endif
17+
#endif /* !NAMESPACEOBJECT_H */

Lib/test/test_sys.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,24 @@ def test_getfilesystemencoding(self):
581581
expected = None
582582
self.check_fsencoding(fs_encoding, expected)
583583

584+
def test_implementation(self):
585+
# This test applies to all implementations equally.
586+
587+
levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'release': 0xF}
588+
589+
self.assertTrue(hasattr(sys.implementation, 'name'))
590+
self.assertTrue(hasattr(sys.implementation, 'version'))
591+
self.assertTrue(hasattr(sys.implementation, 'hexversion'))
592+
self.assertTrue(hasattr(sys.implementation, 'cache_tag'))
593+
594+
version = sys.implementation.version
595+
self.assertEqual(version[:2], (version.major, version.minor))
596+
597+
hexversion = (version.major << 24 | version.minor << 16 |
598+
version.micro << 8 | levels[version.releaselevel] << 4 |
599+
version.serial << 0)
600+
self.assertEqual(sys.implementation.hexversion, hexversion)
601+
584602

585603
class SizeofTest(unittest.TestCase):
586604

Lib/test/test_types.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,8 +996,149 @@ def __prepare__(mcls, name, bases):
996996
X = types.new_class("X", (int(), C))
997997

998998

999+
class SimpleNamespaceTests(unittest.TestCase):
1000+
1001+
def test_constructor(self):
1002+
ns1 = types.SimpleNamespace()
1003+
ns2 = types.SimpleNamespace(x=1, y=2)
1004+
ns3 = types.SimpleNamespace(**dict(x=1, y=2))
1005+
1006+
with self.assertRaises(TypeError):
1007+
types.SimpleNamespace(1, 2, 3)
1008+
1009+
self.assertEqual(len(ns1.__dict__), 0)
1010+
self.assertEqual(vars(ns1), {})
1011+
self.assertEqual(len(ns2.__dict__), 2)
1012+
self.assertEqual(vars(ns2), {'y': 2, 'x': 1})
1013+
self.assertEqual(len(ns3.__dict__), 2)
1014+
self.assertEqual(vars(ns3), {'y': 2, 'x': 1})
1015+
1016+
def test_unbound(self):
1017+
ns1 = vars(types.SimpleNamespace())
1018+
ns2 = vars(types.SimpleNamespace(x=1, y=2))
1019+
1020+
self.assertEqual(ns1, {})
1021+
self.assertEqual(ns2, {'y': 2, 'x': 1})
1022+
1023+
def test_underlying_dict(self):
1024+
ns1 = types.SimpleNamespace()
1025+
ns2 = types.SimpleNamespace(x=1, y=2)
1026+
ns3 = types.SimpleNamespace(a=True, b=False)
1027+
mapping = ns3.__dict__
1028+
del ns3
1029+
1030+
self.assertEqual(ns1.__dict__, {})
1031+
self.assertEqual(ns2.__dict__, {'y': 2, 'x': 1})
1032+
self.assertEqual(mapping, dict(a=True, b=False))
1033+
1034+
def test_attrget(self):
1035+
ns = types.SimpleNamespace(x=1, y=2, w=3)
1036+
1037+
self.assertEqual(ns.x, 1)
1038+
self.assertEqual(ns.y, 2)
1039+
self.assertEqual(ns.w, 3)
1040+
with self.assertRaises(AttributeError):
1041+
ns.z
1042+
1043+
def test_attrset(self):
1044+
ns1 = types.SimpleNamespace()
1045+
ns2 = types.SimpleNamespace(x=1, y=2, w=3)
1046+
ns1.a = 'spam'
1047+
ns1.b = 'ham'
1048+
ns2.z = 4
1049+
ns2.theta = None
1050+
1051+
self.assertEqual(ns1.__dict__, dict(a='spam', b='ham'))
1052+
self.assertEqual(ns2.__dict__, dict(x=1, y=2, w=3, z=4, theta=None))
1053+
1054+
def test_attrdel(self):
1055+
ns1 = types.SimpleNamespace()
1056+
ns2 = types.SimpleNamespace(x=1, y=2, w=3)
1057+
1058+
with self.assertRaises(AttributeError):
1059+
del ns1.spam
1060+
with self.assertRaises(AttributeError):
1061+
del ns2.spam
1062+
1063+
del ns2.y
1064+
self.assertEqual(vars(ns2), dict(w=3, x=1))
1065+
ns2.y = 'spam'
1066+
self.assertEqual(vars(ns2), dict(w=3, x=1, y='spam'))
1067+
del ns2.y
1068+
self.assertEqual(vars(ns2), dict(w=3, x=1))
1069+
1070+
ns1.spam = 5
1071+
self.assertEqual(vars(ns1), dict(spam=5))
1072+
del ns1.spam
1073+
self.assertEqual(vars(ns1), {})
1074+
1075+
def test_repr(self):
1076+
ns1 = types.SimpleNamespace(x=1, y=2, w=3)
1077+
ns2 = types.SimpleNamespace()
1078+
ns2.x = "spam"
1079+
ns2._y = 5
1080+
1081+
self.assertEqual(repr(ns1), "namespace(w=3, x=1, y=2)")
1082+
self.assertEqual(repr(ns2), "namespace(_y=5, x='spam')")
1083+
1084+
def test_nested(self):
1085+
ns1 = types.SimpleNamespace(a=1, b=2)
1086+
ns2 = types.SimpleNamespace()
1087+
ns3 = types.SimpleNamespace(x=ns1)
1088+
ns2.spam = ns1
1089+
ns2.ham = '?'
1090+
ns2.spam = ns3
1091+
1092+
self.assertEqual(vars(ns1), dict(a=1, b=2))
1093+
self.assertEqual(vars(ns2), dict(spam=ns3, ham='?'))
1094+
self.assertEqual(ns2.spam, ns3)
1095+
self.assertEqual(vars(ns3), dict(x=ns1))
1096+
self.assertEqual(ns3.x.a, 1)
1097+
1098+
def test_recursive(self):
1099+
ns1 = types.SimpleNamespace(c='cookie')
1100+
ns2 = types.SimpleNamespace()
1101+
ns3 = types.SimpleNamespace(x=1)
1102+
ns1.spam = ns1
1103+
ns2.spam = ns3
1104+
ns3.spam = ns2
1105+
1106+
self.assertEqual(ns1.spam, ns1)
1107+
self.assertEqual(ns1.spam.spam, ns1)
1108+
self.assertEqual(ns1.spam.spam, ns1.spam)
1109+
self.assertEqual(ns2.spam, ns3)
1110+
self.assertEqual(ns3.spam, ns2)
1111+
self.assertEqual(ns2.spam.spam, ns2)
1112+
1113+
def test_recursive_repr(self):
1114+
ns1 = types.SimpleNamespace(c='cookie')
1115+
ns2 = types.SimpleNamespace()
1116+
ns3 = types.SimpleNamespace(x=1)
1117+
ns1.spam = ns1
1118+
ns2.spam = ns3
1119+
ns3.spam = ns2
1120+
1121+
self.assertEqual(repr(ns1),
1122+
"namespace(c='cookie', spam=namespace(...))")
1123+
self.assertEqual(repr(ns2),
1124+
"namespace(spam=namespace(spam=namespace(...), x=1))")
1125+
1126+
def test_as_dict(self):
1127+
ns = types.SimpleNamespace(spam='spamspamspam')
1128+
1129+
with self.assertRaises(TypeError):
1130+
len(ns)
1131+
with self.assertRaises(TypeError):
1132+
iter(ns)
1133+
with self.assertRaises(TypeError):
1134+
'spam' in ns
1135+
with self.assertRaises(TypeError):
1136+
ns['spam']
1137+
1138+
9991139
def test_main():
1000-
run_unittest(TypesTests, MappingProxyTests, ClassCreationTests)
1140+
run_unittest(TypesTests, MappingProxyTests, ClassCreationTests,
1141+
SimpleNamespaceTests)
10011142

10021143
if __name__ == '__main__':
10031144
test_main()

Lib/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def _f(): pass
1313
LambdaType = type(lambda: None) # Same as FunctionType
1414
CodeType = type(_f.__code__)
1515
MappingProxyType = type(type.__dict__)
16+
SimpleNamespace = type(sys.implementation)
1617

1718
def _g():
1819
yield 1

Makefile.pre.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ OBJECT_OBJS= \
392392
Objects/memoryobject.o \
393393
Objects/methodobject.o \
394394
Objects/moduleobject.o \
395+
Objects/namespaceobject.o \
395396
Objects/object.o \
396397
Objects/obmalloc.o \
397398
Objects/capsule.o \
@@ -766,6 +767,7 @@ PYTHON_HEADERS= \
766767
$(srcdir)/Include/methodobject.h \
767768
$(srcdir)/Include/modsupport.h \
768769
$(srcdir)/Include/moduleobject.h \
770+
$(srcdir)/Include/namespaceobject.h \
769771
$(srcdir)/Include/node.h \
770772
$(srcdir)/Include/object.h \
771773
$(srcdir)/Include/objimpl.h \

0 commit comments

Comments
 (0)