[issue39461] [RFE] os.environ should support Path-like values, like subprocess(..., env=...)

Eryk Sun report at bugs.python.org
Tue Jan 28 16:19:03 EST 2020


Eryk Sun <eryksun at gmail.com> added the comment:

> as long as the behavior is *consistent* with the env kwarg to 
> subprocess.run() 

subprocess isn't consistent with itself across platforms. The env parameter in Windows is strictly an str->str mapping, like os.environ. This is coded in getenvironment in Modules/_winapi.c:

    if (! PyUnicode_Check(key) || ! PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
            "environment can only contain strings");
        goto error;
    }

At a lower level, should the env parameter of os.spawnve and os.execve  allow path-like objects? Should (Unix) os.putenv allow the name and value to be path-like?

Currently these cases use PyUnicode_FSConverter and PyUnicode_FSDecoder, which support __fspath__ via PyOS_FSPath. PyUnicode_FSDecoder also supports the buffer protocol, with a warning. 

Since we have cases like this where the filesystem encoding is used for string data that's not actually a file path, should alternate ParseTuple converters be added that are limited to just str and bytes? Maybe name them PyUnicode_FSStringEncoder and PyUnicode_FSStringDecoder, where "String" emphasizes that fspath and buffer objects are not allowed. For example:

    int
    PyUnicode_FSStringEncoder(PyObject *path, void *addr)
    {
        PyObject *output = NULL;

        if (path == NULL) {
            Py_DECREF(*(PyObject **)addr);
            *(PyObject **)addr = NULL;
            return 1;
        }

        if (PyBytes_Check(path)) {
            output = path;
            Py_INCREF(output);
        }
        else if (PyUnicode_Check(path)) {
            output = PyUnicode_EncodeFSDefault(path);
            if (!output)
                return 0;
        }
        else {
            PyErr_Format(PyExc_TypeError, "path should be str or bytes, not "
                "%.200s", _PyType_Name(Py_TYPE(path)));
            return 0;
        }

        if ((size_t)PyBytes_GET_SIZE(output) !=
                strlen(PyBytes_AS_STRING(output)))
        {
            PyErr_SetString(PyExc_ValueError, "embedded null byte");
            Py_DECREF(output);
            return 0;
        }
        
        *(PyObject **)addr = output;
        return Py_CLEANUP_SUPPORTED;
    }


    int
    PyUnicode_FSStringDecoder(PyObject *path, void *addr)
    {
        PyObject *output = NULL;

        if (arg == NULL) {
            Py_DECREF(*(PyObject **)addr);
            *(PyObject **)addr = NULL;
            return 1;
        }

        if (PyUnicode_Check(path)) {
            output = path;
            Py_INCREF(output);
        }
        else if (PyBytes_Check(path)) {
            output = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AS_STRING(path),
                        PyBytes_GET_SIZE(path));
            if (!output)
                return 0;
        }
        else {
            PyErr_Format(PyExc_TypeError, "path should be str or bytes, not "
                "%.200s", _PyType_Name(Py_TYPE(path)));
            return 0;
        }
        
        if (PyUnicode_READY(output) == -1) {
            Py_DECREF(output);
            return 0;
        }
        
        if (findchar(PyUnicode_DATA(output), PyUnicode_KIND(output),
                     PyUnicode_GET_LENGTH(output), 0, 1) >= 0) {
            PyErr_SetString(PyExc_ValueError, "embedded null character");
            Py_DECREF(output);
            return 0;
        }
        
        *(PyObject **)addr = output;
        return Py_CLEANUP_SUPPORTED;
    }

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue39461>
_______________________________________


More information about the Python-bugs-list mailing list