]> granicus.if.org Git - python/commitdiff
Implements issue #9951: Adds a hex() method to bytes, bytearray, & memoryview.
authorGregory P. Smith <greg@krypto.org>
Sat, 25 Apr 2015 23:22:26 +0000 (23:22 +0000)
committerGregory P. Smith <greg@krypto.org>
Sat, 25 Apr 2015 23:22:26 +0000 (23:22 +0000)
Also updates a few internal implementations of the same thing to use the
new built-in code.

Contributed by Arnon Yaari.

15 files changed:
Doc/library/stdtypes.rst
Doc/whatsnew/3.5.rst
Lib/test/test_bytes.py
Lib/test/test_doctest.py
Makefile.pre.in
Misc/ACKS
Misc/NEWS
Modules/sha1module.c
Modules/sha256module.c
Modules/sha512module.c
Objects/bytearrayobject.c
Objects/bytesobject.c
Objects/memoryobject.c
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters

index 26ff30988e7233f544f436f13a4274f79ae0532f..2c66fca31bf84a59ef5696e0bd01d126571e0abf 100644 (file)
@@ -2289,6 +2289,19 @@ the bytes type has an additional class method to read data in that format:
    >>> bytes.fromhex('2Ef0 F1f2  ')
    b'.\xf0\xf1\xf2'
 
+A reverse conversion function exists to transform a bytes object into its
+hexadecimal representation.
+
+.. method:: bytes.hex()
+
+   Return a string object containing two hexadecimal digits for each
+   byte in the instance.
+
+   >>> b'\xf0\xf1\xf2'.hex()
+   'f0f1f2'
+
+   .. versionadded:: 3.5
+
 Since bytes objects are sequences of integers (akin to a tuple), for a bytes
 object *b*, ``b[0]`` will be an integer, while ``b[0:1]`` will be a bytes
 object of length 1.  (This contrasts with text strings, where both indexing
@@ -2344,6 +2357,19 @@ the bytearray type has an additional class method to read data in that format:
    >>> bytearray.fromhex('2Ef0 F1f2  ')
    bytearray(b'.\xf0\xf1\xf2')
 
+A reverse conversion function exists to transform a bytearray object into its
+hexadecimal representation.
+
+.. method:: bytearray.hex()
+
+   Return a string object containing two hexadecimal digits for each
+   byte in the instance.
+
+   >>> bytearray(b'\xf0\xf1\xf2').hex()
+   'f0f1f2'
+
+   .. versionadded:: 3.5
+
 Since bytearray objects are sequences of integers (akin to a list), for a
 bytearray object *b*, ``b[0]`` will be an integer, while ``b[0:1]`` will be
 a bytearray object of length 1.  (This contrasts with text strings, where
@@ -3458,6 +3484,17 @@ copying.
       supports all format strings, including those that are not in
       :mod:`struct` module syntax.
 
+   .. method:: hex()
+
+      Return a string object containing two hexadecimal digits for each
+      byte in the buffer. ::
+
+         >>> m = memoryview(b"abc")
+         >>> m.hex()
+         '616263'
+
+      .. versionadded:: 3.5
+
    .. method:: tolist()
 
       Return the data in the buffer as a list of elements. ::
index f364317ba716621abe97dcbcee7ceb81c3aacbbb..da7c5bba915924684597ff0bc3490e5f33eedfce 100644 (file)
@@ -80,6 +80,9 @@ New built-in features:
 
 * ``bytes % args``, ``bytearray % args``: :pep:`461` - Adding ``%`` formatting
   to bytes and bytearray
+* ``b'\xf0\x9f\x90\x8d'.hex()``, ``bytearray(b'\xf0\x9f\x90\x8d').hex()``,
+  ``memoryview(b'\xf0\x9f\x90\x8d').hex()``: :issue:`9951` - A ``hex`` method
+  has been added to bytes, bytearray, and memoryview.
 
 Implementation improvements:
 
index ad283002da88bef492266fdacb8901aa237bab3d..1c832aa9e692f203fbf7969fb25467a8421d54fd 100644 (file)
@@ -301,6 +301,14 @@ class BaseBytesTest:
         self.assertRaises(ValueError, self.type2test.fromhex, '\x00')
         self.assertRaises(ValueError, self.type2test.fromhex, '12   \x00   34')
 
+    def test_hex(self):
+        self.assertRaises(TypeError, self.type2test.hex)
+        self.assertRaises(TypeError, self.type2test.hex, 1)
+        self.assertEquals(self.type2test(b"").hex(), "")
+        self.assertEquals(bytearray([0x1a, 0x2b, 0x30]).hex(), '1a2b30')
+        self.assertEquals(self.type2test(b"\x1a\x2b\x30").hex(), '1a2b30')
+        self.assertEquals(memoryview(b"\x1a\x2b\x30").hex(), '1a2b30')
+
     def test_join(self):
         self.assertEqual(self.type2test(b"").join([]), b"")
         self.assertEqual(self.type2test(b"").join([b""]), b"")
index bbe5a13628fbd1748cc7064c8ed83c6259c61ac9..b92c4b53f02931e35ea397a83e2f0143d7ea552e 100644 (file)
@@ -659,7 +659,7 @@ plain ol' Python and is guaranteed to be available.
 
     >>> import builtins
     >>> tests = doctest.DocTestFinder().find(builtins)
-    >>> 790 < len(tests) < 800 # approximate number of objects with docstrings
+    >>> 790 < len(tests) < 810 # approximate number of objects with docstrings
     True
     >>> real_tests = [t for t in tests if len(t.examples) > 0]
     >>> len(real_tests) # objects that actually have doctests
index b78bcf189ba4fbd20c06a2fab5f090745b08f7e2..34b413f7078f5b6decf61c95ccf0c2511fa9fc8a 100644 (file)
@@ -402,6 +402,7 @@ PYTHON_OBJS=        \
                Python/getopt.o \
                Python/pystrcmp.o \
                Python/pystrtod.o \
+               Python/pystrhex.o \
                Python/dtoa.o \
                Python/formatter_unicode.o \
                Python/fileutils.o \
@@ -919,6 +920,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/pystate.h \
                $(srcdir)/Include/pystrcmp.h \
                $(srcdir)/Include/pystrtod.h \
+               $(srcdir)/Include/pystrhex.h \
                $(srcdir)/Include/pythonrun.h \
                $(srcdir)/Include/pythread.h \
                $(srcdir)/Include/pytime.h \
index 0a04a07e2f921d41dd85a5edd25b5e4ca6bd9697..ca0fcab1802f6a7fb1aeae024a3f23ed13879e9a 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1547,6 +1547,7 @@ Doug Wyatt
 Robert Xiao
 Florent Xicluna
 Hirokazu Yamamoto
+Arnon Yaari
 Ka-Ping Yee
 Jason Yeo
 EungJun Yi
index c559ea528ad911eb60ace05daf89573537028045..1d54c5f221f39d2169a70a2aac57695a0af98ce8 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -11,6 +11,7 @@ Core and Builtins
 -----------------
 
 - Issue #24022: Fix tokenizer crash when processing undecodable source code.
+- Issue #9951: Added a hex() method to bytes, bytearray, and memoryview.
 
 Library
 -------
index d1f89364d7b0734723f0c4e9038a4279cd7d2b48..74b94ba270a75488159708cd3b8845ab4ad886e5 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "Python.h"
 #include "hashlib.h"
+#include "pystrhex.h"
 
 /*[clinic input]
 module _sha1
@@ -364,32 +365,12 @@ SHA1Type_hexdigest_impl(SHA1object *self)
 {
     unsigned char digest[SHA1_DIGESTSIZE];
     struct sha1_state temp;
-    PyObject *retval;
-    Py_UCS1 *hex_digest;
-    int i, j;
 
     /* Get the raw (binary) digest value */
     temp = self->hash_state;
     sha1_done(&temp, digest);
 
-    /* Create a new string */
-    retval = PyUnicode_New(SHA1_DIGESTSIZE * 2, 127);
-    if (!retval)
-            return NULL;
-    hex_digest = PyUnicode_1BYTE_DATA(retval);
-
-    /* Make hex version of the digest */
-    for(i=j=0; i<SHA1_DIGESTSIZE; i++) {
-        unsigned char c;
-        c = (digest[i] >> 4) & 0xf;
-        hex_digest[j++] = Py_hexdigits[c];
-        c = (digest[i] & 0xf);
-        hex_digest[j++] = Py_hexdigits[c];
-    }
-#ifdef Py_DEBUG
-    assert(_PyUnicode_CheckConsistency(retval, 1));
-#endif
-    return retval;
+    return _Py_strhex((const char *)digest, SHA1_DIGESTSIZE);
 }
 
 /*[clinic input]
index 957fd2b8f9b35e0726470c95ad297c1708a99ac8..8c4def05726013717af5a0af11013cbc6f486b8e 100644 (file)
@@ -19,6 +19,7 @@
 #include "Python.h"
 #include "structmember.h"
 #include "hashlib.h"
+#include "pystrhex.h"
 
 /*[clinic input]
 module _sha256
@@ -454,32 +455,12 @@ SHA256Type_hexdigest_impl(SHAobject *self)
 {
     unsigned char digest[SHA_DIGESTSIZE];
     SHAobject temp;
-    PyObject *retval;
-    Py_UCS1 *hex_digest;
-    int i, j;
 
     /* Get the raw (binary) digest value */
     SHAcopy(self, &temp);
     sha_final(digest, &temp);
 
-    /* Create a new string */
-    retval = PyUnicode_New(self->digestsize * 2, 127);
-    if (!retval)
-            return NULL;
-    hex_digest = PyUnicode_1BYTE_DATA(retval);
-
-    /* Make hex version of the digest */
-    for(i=j=0; i<self->digestsize; i++) {
-        unsigned char c;
-        c = (digest[i] >> 4) & 0xf;
-        hex_digest[j++] = Py_hexdigits[c];
-        c = (digest[i] & 0xf);
-        hex_digest[j++] = Py_hexdigits[c];
-    }
-#ifdef Py_DEBUG
-    assert(_PyUnicode_CheckConsistency(retval, 1));
-#endif
-    return retval;
+    return _Py_strhex((const char *)digest, self->digestsize);
 }
 
 /*[clinic input]
index 4533c003d599d95806b204a783525f1f35e43cd8..8237d867f4aa28b15ef23c106e0f922b49666b40 100644 (file)
@@ -19,6 +19,7 @@
 #include "Python.h"
 #include "structmember.h"
 #include "hashlib.h"
+#include "pystrhex.h"
 
 /*[clinic input]
 module _sha512
@@ -521,32 +522,12 @@ SHA512Type_hexdigest_impl(SHAobject *self)
 {
     unsigned char digest[SHA_DIGESTSIZE];
     SHAobject temp;
-    PyObject *retval;
-    Py_UCS1 *hex_digest;
-    int i, j;
 
     /* Get the raw (binary) digest value */
     SHAcopy(self, &temp);
     sha512_final(digest, &temp);
 
-    /* Create a new string */
-    retval = PyUnicode_New(self->digestsize * 2, 127);
-    if (!retval)
-            return NULL;
-    hex_digest = PyUnicode_1BYTE_DATA(retval);
-
-    /* Make hex version of the digest */
-    for (i=j=0; i<self->digestsize; i++) {
-        unsigned char c;
-        c = (digest[i] >> 4) & 0xf;
-        hex_digest[j++] = Py_hexdigits[c];
-        c = (digest[i] & 0xf);
-        hex_digest[j++] = Py_hexdigits[c];
-    }
-#ifdef Py_DEBUG
-    assert(_PyUnicode_CheckConsistency(retval, 1));
-#endif
-    return retval;
+    return _Py_strhex((const char *)digest, self->digestsize);
 }
 
 /*[clinic input]
index d76f15f3acbd7c861442553947a9658cf35fd49c..14444a2e8e2e3319944b3c4a51beeb310e280ec7 100644 (file)
@@ -5,6 +5,7 @@
 #include "structmember.h"
 #include "bytes_methods.h"
 #include "bytesobject.h"
+#include "pystrhex.h"
 
 /*[clinic input]
 class bytearray "PyByteArrayObject *" "&PyByteArray_Type"
@@ -2872,6 +2873,19 @@ bytearray_fromhex_impl(PyObject*cls, PyObject *string)
     return NULL;
 }
 
+PyDoc_STRVAR(hex__doc__,
+"B.hex() -> string\n\
+\n\
+Create a string of hexadecimal numbers from a bytearray object.\n\
+Example: bytearray([0xb9, 0x01, 0xef]).hex() -> 'b901ef'.");
+
+static PyObject *
+bytearray_hex(PyBytesObject *self)
+{
+    char* argbuf = PyByteArray_AS_STRING(self);
+    Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
+    return _Py_strhex(argbuf, arglen);
+}
 
 static PyObject *
 _common_reduce(PyByteArrayObject *self, int proto)
@@ -3002,6 +3016,7 @@ bytearray_methods[] = {
     BYTEARRAY_EXTEND_METHODDEF
     {"find", (PyCFunction)bytearray_find, METH_VARARGS, find__doc__},
     BYTEARRAY_FROMHEX_METHODDEF
+    {"hex", (PyCFunction)bytearray_hex, METH_NOARGS, hex__doc__},
     {"index", (PyCFunction)bytearray_index, METH_VARARGS, index__doc__},
     BYTEARRAY_INSERT_METHODDEF
     {"isalnum", (PyCFunction)stringlib_isalnum, METH_NOARGS,
index d981e0ee69859fbbcf6db5178cf15c6d0ef4d2c4..d2b52c77b3329b71852799672a24ad963234a589 100644 (file)
@@ -5,6 +5,7 @@
 #include "Python.h"
 
 #include "bytes_methods.h"
+#include "pystrhex.h"
 #include <stddef.h>
 
 /*[clinic input]
@@ -3036,6 +3037,20 @@ bytes_fromhex_impl(PyTypeObject *type, PyObject *string)
     return NULL;
 }
 
+PyDoc_STRVAR(hex__doc__,
+"B.hex() -> string\n\
+\n\
+Create a string of hexadecimal numbers from a bytes object.\n\
+Example: b'\\xb9\\x01\\xef'.hex() -> 'b901ef'.");
+
+static PyObject *
+bytes_hex(PyBytesObject *self)
+{
+    char* argbuf = PyBytes_AS_STRING(self);
+    Py_ssize_t arglen = PyBytes_GET_SIZE(self);
+    return _Py_strhex(argbuf, arglen);
+}
+
 static PyObject *
 bytes_getnewargs(PyBytesObject *v)
 {
@@ -3057,6 +3072,7 @@ bytes_methods[] = {
      expandtabs__doc__},
     {"find", (PyCFunction)bytes_find, METH_VARARGS, find__doc__},
     BYTES_FROMHEX_METHODDEF
+    {"hex", (PyCFunction)bytes_hex, METH_NOARGS, hex__doc__},
     {"index", (PyCFunction)bytes_index, METH_VARARGS, index__doc__},
     {"isalnum", (PyCFunction)stringlib_isalnum, METH_NOARGS,
      _Py_isalnum__doc__},
index b6951ab88e0e031753bd86d69cf5f88731c35f04..54fd05f6f61051df7b29bcb444f67bffaaa15f2e 100644 (file)
@@ -1,6 +1,7 @@
 /* Memoryview object implementation */
 
 #include "Python.h"
+#include "pystrhex.h"
 #include <stddef.h>
 
 
@@ -2157,6 +2158,14 @@ memory_tobytes(PyMemoryViewObject *self, PyObject *dummy)
     return bytes;
 }
 
+static PyObject *
+memory_hex(PyMemoryViewObject *self, PyObject *dummy)
+{
+    Py_buffer *src = VIEW_ADDR(self);
+    CHECK_RELEASED(self);
+    return _Py_strhex(src->buf, src->len);
+}
+
 static PyObject *
 memory_repr(PyMemoryViewObject *self)
 {
@@ -3061,6 +3070,10 @@ PyDoc_STRVAR(memory_tobytes_doc,
 "tobytes($self, /)\n--\n\
 \n\
 Return the data in the buffer as a byte string.");
+PyDoc_STRVAR(memory_hex_doc,
+"hex($self, /)\n--\n\
+\n\
+Return the data in the buffer as a string of hexadecimal numbers.");
 PyDoc_STRVAR(memory_tolist_doc,
 "tolist($self, /)\n--\n\
 \n\
@@ -3073,6 +3086,7 @@ Cast a memoryview to a new format or shape.");
 static PyMethodDef memory_methods[] = {
     {"release",     (PyCFunction)memory_release, METH_NOARGS, memory_release_doc},
     {"tobytes",     (PyCFunction)memory_tobytes, METH_NOARGS, memory_tobytes_doc},
+    {"hex",         (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc},
     {"tolist",      (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc},
     {"cast",        (PyCFunction)memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_doc},
     {"__enter__",   memory_enter, METH_NOARGS, NULL},
index a5690f6b06e3f8a6a1c697774a4dd7950e729fdd..bcefe94c979940b3e57499c1a992345ad5993321 100644 (file)
     <ClInclude Include="..\Include\pystate.h" />
     <ClInclude Include="..\Include\pystrcmp.h" />
     <ClInclude Include="..\Include\pystrtod.h" />
+    <ClInclude Include="..\Include\pystrhex.h" />
     <ClInclude Include="..\Include\dtoa.h" />
     <ClInclude Include="..\Include\Python-ast.h" />
     <ClInclude Include="..\Include\Python.h" />
     <ClCompile Include="..\Python\pytime.c" />
     <ClCompile Include="..\Python\pystate.c" />
     <ClCompile Include="..\Python\pystrcmp.c" />
+    <ClCompile Include="..\Python\pystrhex.c" />
     <ClCompile Include="..\Python\pystrtod.c" />
     <ClCompile Include="..\Python\dtoa.c" />
     <ClCompile Include="..\Python\Python-ast.c" />
index b4154054068ab526e2b7220c7d0eb48e34ed200a..3b275bb0de543ef17194a980dc7cbc40aebcc754 100644 (file)
     <ClInclude Include="..\Include\pystrtod.h">
       <Filter>Include</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\pystrhex.h">
+      <Filter>Include</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\dtoa.h">
       <Filter>Include</Filter>
     </ClInclude>
     <ClCompile Include="..\Python\pystrcmp.c">
       <Filter>Python</Filter>
     </ClCompile>
+    <ClCompile Include="..\Python\pystrhex.c">
+      <Filter>Python</Filter>
+    </ClCompile>
     <ClCompile Include="..\Python\pystrtod.c">
       <Filter>Python</Filter>
     </ClCompile>