bpo-32922: dbm.open() now encodes filename with the filesystem encoding. (GH-5832)

This commit is contained in:
Serhiy Storchaka 2018-02-26 16:02:22 +02:00 committed by GitHub
parent 973cae07d6
commit 6f600ff173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 19 deletions

View File

@ -281,6 +281,21 @@ class DumbDBMTestCase(unittest.TestCase):
self.assertEqual(sorted(f.keys()), sorted(self._dict)) self.assertEqual(sorted(f.keys()), sorted(self._dict))
f.close() # don't write f.close() # don't write
@unittest.skipUnless(support.TESTFN_NONASCII,
'requires OS support of non-ASCII encodings')
def test_nonascii_filename(self):
filename = support.TESTFN_NONASCII
for suffix in ['.dir', '.dat', '.bak']:
self.addCleanup(support.unlink, filename + suffix)
with dumbdbm.open(filename, 'c') as db:
db[b'key'] = b'value'
self.assertTrue(os.path.exists(filename + '.dat'))
self.assertTrue(os.path.exists(filename + '.dir'))
with dumbdbm.open(filename, 'r') as db:
self.assertEqual(list(db.keys()), [b'key'])
self.assertTrue(b'key' in db)
self.assertEqual(db[b'key'], b'value')
def tearDown(self): def tearDown(self):
_delete_files() _delete_files()

View File

@ -2,7 +2,7 @@ from test import support
gdbm = support.import_module("dbm.gnu") #skip if not supported gdbm = support.import_module("dbm.gnu") #skip if not supported
import unittest import unittest
import os import os
from test.support import TESTFN, unlink from test.support import TESTFN, TESTFN_NONASCII, unlink
filename = TESTFN filename = TESTFN
@ -93,5 +93,39 @@ class TestGdbm(unittest.TestCase):
self.assertEqual(str(cm.exception), self.assertEqual(str(cm.exception),
"GDBM object has already been closed") "GDBM object has already been closed")
def test_bytes(self):
with gdbm.open(filename, 'c') as db:
db[b'bytes key \xbd'] = b'bytes value \xbd'
with gdbm.open(filename, 'r') as db:
self.assertEqual(list(db.keys()), [b'bytes key \xbd'])
self.assertTrue(b'bytes key \xbd' in db)
self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd')
def test_unicode(self):
with gdbm.open(filename, 'c') as db:
db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d'
with gdbm.open(filename, 'r') as db:
self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()])
self.assertTrue('Unicode key \U0001f40d'.encode() in db)
self.assertTrue('Unicode key \U0001f40d' in db)
self.assertEqual(db['Unicode key \U0001f40d'.encode()],
'Unicode value \U0001f40d'.encode())
self.assertEqual(db['Unicode key \U0001f40d'],
'Unicode value \U0001f40d'.encode())
@unittest.skipUnless(TESTFN_NONASCII,
'requires OS support of non-ASCII encodings')
def test_nonascii_filename(self):
filename = TESTFN_NONASCII
self.addCleanup(unlink, filename)
with gdbm.open(filename, 'c') as db:
db[b'key'] = b'value'
self.assertTrue(os.path.exists(filename))
with gdbm.open(filename, 'r') as db:
self.assertEqual(list(db.keys()), [b'key'])
self.assertTrue(b'key' in db)
self.assertEqual(db[b'key'], b'value')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,5 +1,6 @@
from test import support from test import support
support.import_module("dbm.ndbm") #skip if not supported support.import_module("dbm.ndbm") #skip if not supported
import os
import unittest import unittest
import dbm.ndbm import dbm.ndbm
from dbm.ndbm import error from dbm.ndbm import error
@ -47,6 +48,42 @@ class DbmTestCase(unittest.TestCase):
self.assertEqual(str(cm.exception), self.assertEqual(str(cm.exception),
"DBM object has already been closed") "DBM object has already been closed")
def test_bytes(self):
with dbm.ndbm.open(self.filename, 'c') as db:
db[b'bytes key \xbd'] = b'bytes value \xbd'
with dbm.ndbm.open(self.filename, 'r') as db:
self.assertEqual(list(db.keys()), [b'bytes key \xbd'])
self.assertTrue(b'bytes key \xbd' in db)
self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd')
def test_unicode(self):
with dbm.ndbm.open(self.filename, 'c') as db:
db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d'
with dbm.ndbm.open(self.filename, 'r') as db:
self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()])
self.assertTrue('Unicode key \U0001f40d'.encode() in db)
self.assertTrue('Unicode key \U0001f40d' in db)
self.assertEqual(db['Unicode key \U0001f40d'.encode()],
'Unicode value \U0001f40d'.encode())
self.assertEqual(db['Unicode key \U0001f40d'],
'Unicode value \U0001f40d'.encode())
@unittest.skipUnless(support.TESTFN_NONASCII,
'requires OS support of non-ASCII encodings')
def test_nonascii_filename(self):
filename = support.TESTFN_NONASCII
for suffix in ['', '.pag', '.dir', '.db']:
self.addCleanup(support.unlink, filename + suffix)
with dbm.ndbm.open(filename, 'c') as db:
db[b'key'] = b'value'
self.assertTrue(any(os.path.exists(filename + suffix)
for suffix in ['', '.pag', '.dir', '.db']))
with dbm.ndbm.open(filename, 'r') as db:
self.assertEqual(list(db.keys()), [b'key'])
self.assertTrue(b'key' in db)
self.assertEqual(db[b'key'], b'value')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -0,0 +1,2 @@
dbm.open() now encodes filename with the filesystem encoding rather than
default encoding.

View File

@ -412,7 +412,7 @@ static PyTypeObject Dbmtype = {
_dbm.open as dbmopen _dbm.open as dbmopen
filename: str filename: unicode
The filename to open. The filename to open.
flags: str="r" flags: str="r"
@ -429,9 +429,9 @@ Return a database object.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
dbmopen_impl(PyObject *module, const char *filename, const char *flags, dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
int mode) int mode)
/*[clinic end generated code: output=5fade8cf16e0755f input=226334bade5764e6]*/ /*[clinic end generated code: output=9527750f5df90764 input=376a9d903a50df59]*/
{ {
int iflags; int iflags;
@ -450,7 +450,20 @@ dbmopen_impl(PyObject *module, const char *filename, const char *flags,
"arg 2 to open should be 'r', 'w', 'c', or 'n'"); "arg 2 to open should be 'r', 'w', 'c', or 'n'");
return NULL; return NULL;
} }
return newdbmobject(filename, iflags, mode);
PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename);
if (filenamebytes == NULL) {
return NULL;
}
const char *name = PyBytes_AS_STRING(filenamebytes);
if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
Py_DECREF(filenamebytes);
PyErr_SetString(PyExc_ValueError, "embedded null character");
return NULL;
}
PyObject *self = newdbmobject(name, iflags, mode);
Py_DECREF(filenamebytes);
return self;
} }
static PyMethodDef dbmmodule_methods[] = { static PyMethodDef dbmmodule_methods[] = {

View File

@ -527,7 +527,7 @@ static PyTypeObject Dbmtype = {
/*[clinic input] /*[clinic input]
_gdbm.open as dbmopen _gdbm.open as dbmopen
filename as name: str filename: unicode
flags: str="r" flags: str="r"
mode: int(py_default="0o666") = 0o666 mode: int(py_default="0o666") = 0o666
/ /
@ -557,8 +557,9 @@ when the database has to be created. It defaults to octal 0o666.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
dbmopen_impl(PyObject *module, const char *name, const char *flags, int mode) dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
/*[clinic end generated code: output=31aa1bafdf5da688 input=55563cd60e51984a]*/ int mode)
/*[clinic end generated code: output=9527750f5df90764 input=3be0b0875974b928]*/
{ {
int iflags; int iflags;
@ -606,7 +607,19 @@ dbmopen_impl(PyObject *module, const char *name, const char *flags, int mode)
} }
} }
return newdbmobject(name, iflags, mode); PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename);
if (filenamebytes == NULL) {
return NULL;
}
const char *name = PyBytes_AS_STRING(filenamebytes);
if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
Py_DECREF(filenamebytes);
PyErr_SetString(PyExc_ValueError, "embedded null character");
return NULL;
}
PyObject *self = newdbmobject(name, iflags, mode);
Py_DECREF(filenamebytes);
return self;
} }
static const char dbmmodule_open_flags[] = "rwcn" static const char dbmmodule_open_flags[] = "rwcn"

View File

@ -121,18 +121,18 @@ PyDoc_STRVAR(dbmopen__doc__,
{"open", (PyCFunction)dbmopen, METH_FASTCALL, dbmopen__doc__}, {"open", (PyCFunction)dbmopen, METH_FASTCALL, dbmopen__doc__},
static PyObject * static PyObject *
dbmopen_impl(PyObject *module, const char *filename, const char *flags, dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
int mode); int mode);
static PyObject * static PyObject *
dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
const char *filename; PyObject *filename;
const char *flags = "r"; const char *flags = "r";
int mode = 438; int mode = 438;
if (!_PyArg_ParseStack(args, nargs, "s|si:open", if (!_PyArg_ParseStack(args, nargs, "U|si:open",
&filename, &flags, &mode)) { &filename, &flags, &mode)) {
goto exit; goto exit;
} }
@ -141,4 +141,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=8ce71abac849155f input=a9049054013a1b77]*/ /*[clinic end generated code: output=5c858b4080a011a4 input=a9049054013a1b77]*/

View File

@ -234,23 +234,24 @@ PyDoc_STRVAR(dbmopen__doc__,
{"open", (PyCFunction)dbmopen, METH_FASTCALL, dbmopen__doc__}, {"open", (PyCFunction)dbmopen, METH_FASTCALL, dbmopen__doc__},
static PyObject * static PyObject *
dbmopen_impl(PyObject *module, const char *name, const char *flags, int mode); dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
int mode);
static PyObject * static PyObject *
dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
const char *name; PyObject *filename;
const char *flags = "r"; const char *flags = "r";
int mode = 438; int mode = 438;
if (!_PyArg_ParseStack(args, nargs, "s|si:open", if (!_PyArg_ParseStack(args, nargs, "U|si:open",
&name, &flags, &mode)) { &filename, &flags, &mode)) {
goto exit; goto exit;
} }
return_value = dbmopen_impl(module, name, flags, mode); return_value = dbmopen_impl(module, filename, flags, mode);
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=dc0aca8c00055d02 input=a9049054013a1b77]*/ /*[clinic end generated code: output=dec05ff9c5aeaeae input=a9049054013a1b77]*/