Commit c3a8b678 authored by Jason R. Coombs's avatar Jason R. Coombs Committed by GitHub

Merge pull request #1660 from pypa/bugfix/1653-downcast-config-strings

Prefer native strings on Python 2 when reading config files.
parents 78fd7302 32c2ec8b
On Python 2, when reading config files, downcast options from text to bytes to satisfy distutils expectations.
...@@ -603,7 +603,7 @@ class Distribution(_Distribution): ...@@ -603,7 +603,7 @@ class Distribution(_Distribution):
for opt in options: for opt in options:
if opt != '__name__' and opt not in ignore_options: if opt != '__name__' and opt not in ignore_options:
val = parser.get(section, opt) val = self._try_str(parser.get(section, opt))
opt = opt.replace('-', '_') opt = opt.replace('-', '_')
opt_dict[opt] = (filename, val) opt_dict[opt] = (filename, val)
...@@ -627,6 +627,26 @@ class Distribution(_Distribution): ...@@ -627,6 +627,26 @@ class Distribution(_Distribution):
except ValueError as msg: except ValueError as msg:
raise DistutilsOptionError(msg) raise DistutilsOptionError(msg)
@staticmethod
def _try_str(val):
"""
On Python 2, much of distutils relies on string values being of
type 'str' (bytes) and not unicode text. If the value can be safely
encoded to bytes using the default encoding, prefer that.
Why the default encoding? Because that value can be implicitly
decoded back to text if needed.
Ref #1653
"""
if six.PY3:
return val
try:
return val.encode()
except UnicodeEncodeError:
pass
return val
def _set_command_options(self, command_obj, option_dict=None): def _set_command_options(self, command_obj, option_dict=None):
""" """
Set the options for 'command_obj' from 'option_dict'. Basically Set the options for 'command_obj' from 'option_dict'. Basically
......
...@@ -46,6 +46,8 @@ def run_setup_py(cmd, pypath=None, path=None, ...@@ -46,6 +46,8 @@ def run_setup_py(cmd, pypath=None, path=None,
cmd, stdout=_PIPE, stderr=_PIPE, shell=shell, env=env, cmd, stdout=_PIPE, stderr=_PIPE, shell=shell, env=env,
) )
if isinstance(data_stream, tuple):
data_stream = slice(*data_stream)
data = proc.communicate()[data_stream] data = proc.communicate()[data_stream]
except OSError: except OSError:
return 1, '' return 1, ''
......
...@@ -8,6 +8,10 @@ from setuptools.command.build_ext import build_ext, get_abi3_suffix ...@@ -8,6 +8,10 @@ from setuptools.command.build_ext import build_ext, get_abi3_suffix
from setuptools.dist import Distribution from setuptools.dist import Distribution
from setuptools.extension import Extension from setuptools.extension import Extension
from . import environment
from .files import build_files
from .textwrap import DALS
class TestBuildExt: class TestBuildExt:
def test_get_ext_filename(self): def test_get_ext_filename(self):
...@@ -43,3 +47,69 @@ class TestBuildExt: ...@@ -43,3 +47,69 @@ class TestBuildExt:
assert res.endswith('eggs.pyd') assert res.endswith('eggs.pyd')
else: else:
assert 'abi3' in res assert 'abi3' in res
def test_build_ext_config_handling(tmpdir_cwd):
files = {
'setup.py': DALS(
"""
from setuptools import Extension, setup
setup(
name='foo',
version='0.0.0',
ext_modules=[Extension('foo', ['foo.c'])],
)
"""),
'foo.c': DALS(
"""
#include "Python.h"
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"foo",
NULL,
0,
NULL,
NULL,
NULL,
NULL,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC PyInit_foo(void)
#else
#define INITERROR return
void initfoo(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create(&moduledef);
#else
PyObject *module = Py_InitModule("extension", NULL);
#endif
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
"""),
'setup.cfg': DALS(
"""
[build]
build-base = foo_build
"""),
}
build_files(files)
code, output = environment.run_setup_py(
cmd=['build'], data_stream=(0, 2),
)
assert code == 0, '\nSTDOUT:\n%s\nSTDERR:\n%s' % output
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment