add introspection to range objects (closes #9896)
authorBenjamin Peterson <benjamin@python.org>
Sat, 5 Nov 2011 19:17:52 +0000 (15:17 -0400)
committerBenjamin Peterson <benjamin@python.org>
Sat, 5 Nov 2011 19:17:52 +0000 (15:17 -0400)
Patch by Daniel Urban.

Doc/library/functions.rst
Lib/test/test_range.py
Objects/rangeobject.c

index f9af3d811520a200d02736fb73ca845620f9950f..3cf5335763929d78dc9747bef7f3e12d8d58f392 100644 (file)
@@ -1042,7 +1042,9 @@ are always available.  They are listed here in alphabetical order.
    ...]``.  If *step* is positive, the last element is the largest ``start + i *
    step`` less than *stop*; if *step* is negative, the last element is the
    smallest ``start + i * step`` greater than *stop*.  *step* must not be zero
-   (or else :exc:`ValueError` is raised).  Example:
+   (or else :exc:`ValueError` is raised).  Range objects have read-only data
+   attributes :attr:`start`, :attr:`stop` and :attr:`step` which return the
+   argument values (or their default).  Example:
 
       >>> list(range(10))
       [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
@@ -1100,6 +1102,9 @@ are always available.  They are listed here in alphabetical order.
       sequence of values they define (instead of comparing based on
       object identity).
 
+   .. versionadded:: 3.3
+      The :attr:`start`, :attr:`stop` and :attr:`step` attributes.
+
 
 .. function:: repr(object)
 
index 6035e76481c77b8a6044c7ada2ca91f2894c1d86..2e335cc70d19da6e8e921020fe7a3f1fb146e916 100644 (file)
@@ -560,6 +560,35 @@ class RangeTest(unittest.TestCase):
             range(0) >= range(0)
 
 
+    def test_attributes(self):
+        # test the start, stop and step attributes of range objects
+        self.assert_attrs(range(0), 0, 0, 1)
+        self.assert_attrs(range(10), 0, 10, 1)
+        self.assert_attrs(range(-10), 0, -10, 1)
+        self.assert_attrs(range(0, 10, 1), 0, 10, 1)
+        self.assert_attrs(range(0, 10, 3), 0, 10, 3)
+        self.assert_attrs(range(10, 0, -1), 10, 0, -1)
+        self.assert_attrs(range(10, 0, -3), 10, 0, -3)
+
+    def assert_attrs(self, rangeobj, start, stop, step):
+        self.assertEqual(rangeobj.start, start)
+        self.assertEqual(rangeobj.stop, stop)
+        self.assertEqual(rangeobj.step, step)
+
+        with self.assertRaises(AttributeError):
+            rangeobj.start = 0
+        with self.assertRaises(AttributeError):
+            rangeobj.stop = 10
+        with self.assertRaises(AttributeError):
+            rangeobj.step = 1
+
+        with self.assertRaises(AttributeError):
+            del rangeobj.start
+        with self.assertRaises(AttributeError):
+            del rangeobj.stop
+        with self.assertRaises(AttributeError):
+            del rangeobj.step
+
 def test_main():
     test.support.run_unittest(RangeTest)
 
index c1e9e5430303b6bdb136af4802a7f9f2e7162364..fb6a5fe02d12d6617ac24a119fe5bf4262048398 100644 (file)
@@ -1,6 +1,7 @@
 /* Range object implementation */
 
 #include "Python.h"
+#include "structmember.h"
 
 /* Support objects whose length is > PY_SSIZE_T_MAX.
 
@@ -880,6 +881,13 @@ static PyMethodDef range_methods[] = {
     {NULL,              NULL}           /* sentinel */
 };
 
+static PyMemberDef range_members[] = {
+    {"start",   T_OBJECT_EX,    offsetof(rangeobject, start),   READONLY},
+    {"stop",    T_OBJECT_EX,    offsetof(rangeobject, stop),    READONLY},
+    {"step",    T_OBJECT_EX,    offsetof(rangeobject, step),    READONLY},
+    {0}
+};
+
 PyTypeObject PyRange_Type = {
         PyVarObject_HEAD_INIT(&PyType_Type, 0)
         "range",                /* Name of this type */
@@ -909,7 +917,7 @@ PyTypeObject PyRange_Type = {
         range_iter,             /* tp_iter */
         0,                      /* tp_iternext */
         range_methods,          /* tp_methods */
-        0,                      /* tp_members */
+        range_members,          /* tp_members */
         0,                      /* tp_getset */
         0,                      /* tp_base */
         0,                      /* tp_dict */