Introduce an encoding helper and exercise it with some non-ascii paths in the rand module tests.
Does it make sense? I dunno.
diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py
index baeecc6..ed5ddce 100644
--- a/OpenSSL/_util.py
+++ b/OpenSSL/_util.py
@@ -1,3 +1,5 @@
+import sys
+
from six import PY3, binary_type, text_type
from cryptography.hazmat.bindings.openssl.binding import Binding
@@ -45,6 +47,23 @@
+def path_string(s):
+ """
+ Convert a Python string to a :py:class:`bytes` string identifying the same
+ path and which can be passed into an OpenSSL API accepting a filename.
+
+ :param s: An instance of :py:class:`bytes` or :py:class:`unicode`.
+
+ :return: An instance of :py:class:`bytes`.
+ """
+ if isinstance(s, binary_type):
+ return s
+ elif isinstance(s, text_type):
+ return s.encode(sys.getfilesystemencoding())
+ else:
+ raise TypeError("Path must be represented as bytes or unicode string")
+
+
if PY3:
def byte_string(s):
return s.encode("charmap")
diff --git a/OpenSSL/rand.py b/OpenSSL/rand.py
index 311c00b..7592fe0 100644
--- a/OpenSSL/rand.py
+++ b/OpenSSL/rand.py
@@ -11,7 +11,8 @@
from OpenSSL._util import (
ffi as _ffi,
lib as _lib,
- exception_from_error_queue as _exception_from_error_queue)
+ exception_from_error_queue as _exception_from_error_queue,
+ path_string as _path_string)
class Error(Exception):
@@ -136,8 +137,7 @@
to read the entire file
:return: The number of bytes read
"""
- if not isinstance(filename, (str, _builtin_bytes)):
- raise TypeError("filename must be a string")
+ filename = _path_string(filename)
if maxbytes is _unspecified:
maxbytes = -1
@@ -155,9 +155,7 @@
:param filename: The file to write data to
:return: The number of bytes written
"""
- if not isinstance(filename, (str, _builtin_bytes)):
- raise TypeError("filename must be a string")
-
+ filename = _path_string(filename)
return _lib.RAND_write_file(filename)
diff --git a/OpenSSL/test/test_rand.py b/OpenSSL/test/test_rand.py
index fbb452c..c6f985a 100644
--- a/OpenSSL/test/test_rand.py
+++ b/OpenSSL/test/test_rand.py
@@ -176,26 +176,52 @@
self.assertRaises(TypeError, rand.write_file, None)
self.assertRaises(TypeError, rand.write_file, "foo", None)
- def test_files(self):
+ def _read_write_test(self, path):
"""
- Test reading and writing of files via rand functions.
+ Verify that ``rand.write_file`` and ``rand.load_file`` can be used.
"""
- # Write random bytes to a file
- tmpfile = self.mktemp()
- # Make sure it exists (so cleanup definitely succeeds)
- fObj = open(tmpfile, 'w')
- fObj.close()
+ # Create the file so cleanup is more straightforward
+ with open(path, "w"):
+ pass
+
try:
- rand.write_file(tmpfile)
+ # Write random bytes to a file
+ rand.write_file(path)
+
# Verify length of written file
- size = os.stat(tmpfile)[stat.ST_SIZE]
+ size = os.stat(path)[stat.ST_SIZE]
self.assertEqual(1024, size)
+
# Read random bytes from file
- rand.load_file(tmpfile)
- rand.load_file(tmpfile, 4) # specify a length
+ rand.load_file(path)
+ rand.load_file(path, 4) # specify a length
finally:
# Cleanup
- os.unlink(tmpfile)
+ os.unlink(path)
+
+
+ def test_bytes_paths(self):
+ """
+ Random data can be saved and loaded to files with paths specified as
+ bytes.
+ """
+ path = self.mktemp()
+ # This is the UTF-8 encoding of the SNOWMAN unicode code point.
+ path += b("\xe2\x98\x83").decode("utf-8").encode(
+ sys.getfilesystemencoding()
+ )
+ self._read_write_test(path)
+
+
+ def test_unicode_paths(self):
+ """
+ Random data can be saved and loaded to files with paths specified as
+ unicode.
+ """
+ path = self.mktemp().decode('utf-8')
+ # This is the UTF-8 encoding of the SNOWMAN unicode code point.
+ path += b("\xe2\x98\x83").decode('utf-8')
+ self._read_write_test(path)
if __name__ == '__main__':