mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Issue19995: %o, %x, %X now only accept ints
This commit is contained in:
		
							parent
							
								
									6d2ea21337
								
							
						
					
					
						commit
						df3ed242c0
					
				
					 6 changed files with 72 additions and 17 deletions
				
			
		| 
						 | 
				
			
			@ -2080,9 +2080,17 @@ left undefined.
 | 
			
		|||
 | 
			
		||||
.. method:: object.__index__(self)
 | 
			
		||||
 | 
			
		||||
   Called to implement :func:`operator.index`.  Also called whenever Python needs
 | 
			
		||||
   an integer object (such as in slicing, or in the built-in :func:`bin`,
 | 
			
		||||
   :func:`hex` and :func:`oct` functions). Must return an integer.
 | 
			
		||||
   Called to implement :func:`operator.index`, and whenever Python needs to
 | 
			
		||||
   losslessly convert the numeric object to an integer object (such as in
 | 
			
		||||
   slicing, or in the built-in :func:`bin`, :func:`hex` and :func:`oct`
 | 
			
		||||
   functions). Presence of this method indicates that the numeric object is
 | 
			
		||||
   an integer type.  Must return an integer.
 | 
			
		||||
 | 
			
		||||
   .. note::
 | 
			
		||||
 | 
			
		||||
      When :meth:`__index__` is defined, :meth:`__int__` should also be defined,
 | 
			
		||||
      and both shuld return the same value, in order to have a coherent integer
 | 
			
		||||
      type class.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _context-managers:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -196,7 +196,7 @@ def itn(n, digits=8, format=DEFAULT_FORMAT):
 | 
			
		|||
    # A 0o200 byte indicates a positive number, a 0o377 byte a negative
 | 
			
		||||
    # number.
 | 
			
		||||
    if 0 <= n < 8 ** (digits - 1):
 | 
			
		||||
        s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL
 | 
			
		||||
        s = bytes("%0*o" % (digits - 1, int(n)), "ascii") + NUL
 | 
			
		||||
    elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1):
 | 
			
		||||
        if n >= 0:
 | 
			
		||||
            s = bytearray([0o200])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,7 +142,6 @@ def test_format(self):
 | 
			
		|||
        testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345")
 | 
			
		||||
        # same, except no 0 flag
 | 
			
		||||
        testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345")
 | 
			
		||||
        testformat("%x", float(big), "123456_______________", 6)
 | 
			
		||||
        big = 0o12345670123456701234567012345670  # 32 octal digits
 | 
			
		||||
        testformat("%o", big, "12345670123456701234567012345670")
 | 
			
		||||
        testformat("%o", -big, "-12345670123456701234567012345670")
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +181,6 @@ def test_format(self):
 | 
			
		|||
        testformat("%034.33o", big, "0012345670123456701234567012345670")
 | 
			
		||||
        # base marker shouldn't change that
 | 
			
		||||
        testformat("%0#34.33o", big, "0o012345670123456701234567012345670")
 | 
			
		||||
        testformat("%o", float(big), "123456__________________________", 6)
 | 
			
		||||
        # Some small ints, in both Python int and flavors).
 | 
			
		||||
        testformat("%d", 42, "42")
 | 
			
		||||
        testformat("%d", -42, "-42")
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +191,6 @@ def test_format(self):
 | 
			
		|||
        testformat("%#x", 1, "0x1")
 | 
			
		||||
        testformat("%#X", 1, "0X1")
 | 
			
		||||
        testformat("%#X", 1, "0X1")
 | 
			
		||||
        testformat("%#x", 1.0, "0x1")
 | 
			
		||||
        testformat("%#o", 1, "0o1")
 | 
			
		||||
        testformat("%#o", 1, "0o1")
 | 
			
		||||
        testformat("%#o", 0, "0o0")
 | 
			
		||||
| 
						 | 
				
			
			@ -210,12 +207,10 @@ def test_format(self):
 | 
			
		|||
        testformat("%x", -0x42, "-42")
 | 
			
		||||
        testformat("%x", 0x42, "42")
 | 
			
		||||
        testformat("%x", -0x42, "-42")
 | 
			
		||||
        testformat("%x", float(0x42), "42")
 | 
			
		||||
        testformat("%o", 0o42, "42")
 | 
			
		||||
        testformat("%o", -0o42, "-42")
 | 
			
		||||
        testformat("%o", 0o42, "42")
 | 
			
		||||
        testformat("%o", -0o42, "-42")
 | 
			
		||||
        testformat("%o", float(0o42), "42")
 | 
			
		||||
        testformat("%r", "\u0378", "'\\u0378'")  # non printable
 | 
			
		||||
        testformat("%a", "\u0378", "'\\u0378'")  # non printable
 | 
			
		||||
        testformat("%r", "\u0374", "'\u0374'")   # printable
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1126,6 +1126,35 @@ def __str__(self):
 | 
			
		|||
        self.assertEqual('%.1s' % "a\xe9\u20ac", 'a')
 | 
			
		||||
        self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9')
 | 
			
		||||
 | 
			
		||||
        #issue 19995
 | 
			
		||||
        class PsuedoInt:
 | 
			
		||||
            def __init__(self, value):
 | 
			
		||||
                self.value = int(value)
 | 
			
		||||
            def __int__(self):
 | 
			
		||||
                return self.value
 | 
			
		||||
            def __index__(self):
 | 
			
		||||
                return self.value
 | 
			
		||||
        class PsuedoFloat:
 | 
			
		||||
            def __init__(self, value):
 | 
			
		||||
                self.value = float(value)
 | 
			
		||||
            def __int__(self):
 | 
			
		||||
                return int(self.value)
 | 
			
		||||
        pi = PsuedoFloat(3.1415)
 | 
			
		||||
        letter_m = PsuedoInt(109)
 | 
			
		||||
        self.assertEquals('%x' % 42, '2a')
 | 
			
		||||
        self.assertEquals('%X' % 15, 'F')
 | 
			
		||||
        self.assertEquals('%o' % 9, '11')
 | 
			
		||||
        self.assertEquals('%c' % 109, 'm')
 | 
			
		||||
        self.assertEquals('%x' % letter_m, '6d')
 | 
			
		||||
        self.assertEquals('%X' % letter_m, '6D')
 | 
			
		||||
        self.assertEquals('%o' % letter_m, '155')
 | 
			
		||||
        self.assertEquals('%c' % letter_m, 'm')
 | 
			
		||||
        self.assertRaises(TypeError, '%x'.__mod__, pi)
 | 
			
		||||
        self.assertRaises(TypeError, '%x'.__mod__, 3.14)
 | 
			
		||||
        self.assertRaises(TypeError, '%X'.__mod__, 2.11)
 | 
			
		||||
        self.assertRaises(TypeError, '%o'.__mod__, 1.79)
 | 
			
		||||
        self.assertRaises(TypeError, '%c'.__mod__, pi)
 | 
			
		||||
 | 
			
		||||
    def test_formatting_with_enum(self):
 | 
			
		||||
        # issue18780
 | 
			
		||||
        import enum
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,10 @@ Core and Builtins
 | 
			
		|||
- Issue #19969: PyBytes_FromFormatV() now raises an OverflowError if "%c"
 | 
			
		||||
  argument is not in range [0; 255].
 | 
			
		||||
 | 
			
		||||
- Issue #19995: %c, %o, %x, and %X now raise TypeError on non-integer input;
 | 
			
		||||
  reworded docs to clarify that an integer type should define both __int__
 | 
			
		||||
  and __index__.
 | 
			
		||||
 | 
			
		||||
- Issue #19787: PyThread_set_key_value() now always set the value. In Python
 | 
			
		||||
  3.3, the function did nothing if the key already exists (if the current value
 | 
			
		||||
  is a non-NULL pointer).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13988,7 +13988,7 @@ formatlong(PyObject *val, struct unicode_format_arg_t *arg)
 | 
			
		|||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Format an integer.
 | 
			
		||||
/* Format an integer or a float as an integer.
 | 
			
		||||
 * Return 1 if the number has been formatted into the writer,
 | 
			
		||||
 *        0 if the number has been formatted into *p_output
 | 
			
		||||
 *       -1 and raise an exception on error */
 | 
			
		||||
| 
						 | 
				
			
			@ -14005,12 +14005,20 @@ mainformatlong(PyObject *v,
 | 
			
		|||
        goto wrongtype;
 | 
			
		||||
 | 
			
		||||
    if (!PyLong_Check(v)) {
 | 
			
		||||
        if (type == 'o' || type == 'x' || type == 'X') {
 | 
			
		||||
            iobj = PyNumber_Index(v);
 | 
			
		||||
            if (iobj == NULL) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            iobj = PyNumber_Long(v);
 | 
			
		||||
            if (iobj == NULL ) {
 | 
			
		||||
                if (PyErr_ExceptionMatches(PyExc_TypeError))
 | 
			
		||||
                    goto wrongtype;
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        assert(PyLong_Check(iobj));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
| 
						 | 
				
			
			@ -14079,8 +14087,18 @@ formatchar(PyObject *v)
 | 
			
		|||
        goto onError;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        /* Integer input truncated to a character */
 | 
			
		||||
        PyObject *iobj;
 | 
			
		||||
        long x;
 | 
			
		||||
        /* make sure number is a type of integer */
 | 
			
		||||
        if (!PyLong_Check(v)) {
 | 
			
		||||
            iobj = PyNumber_Index(v);
 | 
			
		||||
            if (iobj == NULL) {
 | 
			
		||||
                goto onError;
 | 
			
		||||
            }
 | 
			
		||||
            v = iobj;
 | 
			
		||||
            Py_DECREF(iobj);
 | 
			
		||||
        }
 | 
			
		||||
        /* Integer input truncated to a character */
 | 
			
		||||
        x = PyLong_AsLong(v);
 | 
			
		||||
        if (x == -1 && PyErr_Occurred())
 | 
			
		||||
            goto onError;
 | 
			
		||||
| 
						 | 
				
			
			@ -14282,7 +14300,8 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
 | 
			
		|||
/* Format one argument. Supported conversion specifiers:
 | 
			
		||||
 | 
			
		||||
   - "s", "r", "a": any type
 | 
			
		||||
   - "i", "d", "u", "o", "x", "X": int
 | 
			
		||||
   - "i", "d", "u": int or float
 | 
			
		||||
   - "o", "x", "X": int
 | 
			
		||||
   - "e", "E", "f", "F", "g", "G": float
 | 
			
		||||
   - "c": int or str (1 character)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue