# Buggy code
def _parse_config_files(self, filenames=None):
    """
    Adapted from distutils.dist.Distribution.parse_config_files,
    this method provides the same functionality in subtly-improved
    ways.
    """
    from setuptools.extern.six.moves.configparser import ConfigParser

    # Ignore install directory options if we have a venv
    if six.PY3 and sys.prefix != sys.base_prefix:
        ignore_options = [
            "install-base",
            "install-platbase",
            "install-lib",
            "install-platlib",
            "install-purelib",
            "install-headers",
            "install-scripts",
            "install-data",
            "prefix",
            "exec-prefix",
            "home",
            "user",
            "root",
        ]
    else:
        ignore_options = []

    ignore_options = frozenset(ignore_options)

    if filenames is None:
        filenames = self.find_config_files()

    if DEBUG:
        self.announce("Distribution.parse_config_files():")

    parser = ConfigParser()
    for filename in filenames:
        with io.open(filename, "rb") as fp:
            encoding = detect_encoding(fp)
            if DEBUG:
                self.announce("  reading %s [%s]" % (filename, encoding or "locale"))
            reader = io.TextIOWrapper(fp, encoding=encoding)
            (parser.read_file if six.PY3 else parser.readfp)(reader)
        for section in parser.sections():
            options = parser.options(section)
            opt_dict = self.get_option_dict(section)

            for opt in options:
                if opt != "__name__" and opt not in ignore_options:
                    val = self._try_str(parser.get(section, opt))
                    opt = opt.replace("-", "_")
                    opt_dict[opt] = (filename, val)

        # Make the ConfigParser forget everything (so we retain
        # the original filenames that options come from)
        parser.__init__()

    # If there was a "global" section in the config file, use it
    # to set Distribution options.

    if "global" in self.command_options:
        for opt, (src, val) in self.command_options["global"].items():
            alias = self.negative_opt.get(opt)
            try:
                if alias:
                    setattr(self, alias, not strtobool(val))
                elif opt in ("verbose", "dry_run"):  # ugh!
                    setattr(self, opt, strtobool(val))
                else:
                    setattr(self, opt, val)
            except ValueError as msg:
                raise DistutilsOptionError(msg)


# Traceback (UnicodeDecodeError)
configparser # easy_install --version
setuptools 40.8.0 from c:\python37\lib\site-packages (Python 3.7)
configparser 3.7.2 # python setup.py egg_info
Traceback (most recent call last):
File "setup.py", line 5, in <module>
package_dir={'': 'src'},
File "C:\Python37\lib\site-packages\setuptools\__init__.py", line 144, in setup
_install_setup_requires(attrs)
File "C:\Python37\lib\site-packages\setuptools\__init__.py", line 137, in _install_setup_requires
dist.parse_config_files(ignore_option_errors=True)
File "C:\Python37\lib\site-packages\setuptools\dist.py", line 702, in parse_config_files
self._parse_config_files(filenames=filenames)
File "C:\Python37\lib\site-packages\setuptools\dist.py", line 599, in _parse_config_files
(parser.read_file if six.PY3 else parser.readfp)(reader)
File "C:\Python37\lib\configparser.py", line 717, in read_file
self._read(f, source)
File "C:\Python37\lib\configparser.py", line 1014, in _read
for lineno, line in enumerate(fp, start=1):
File "C:\Python37\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 103: character maps to <undefined>

# Fixed code
def _parse_config_files(self, filenames=None):
    """
    Adapted from distutils.dist.Distribution.parse_config_files,
    this method provides the same functionality in subtly-improved
    ways.
    """
    from setuptools.extern.six.moves.configparser import ConfigParser

    # Ignore install directory options if we have a venv
    if six.PY3 and sys.prefix != sys.base_prefix:
        ignore_options = [
            "install-base",
            "install-platbase",
            "install-lib",
            "install-platlib",
            "install-purelib",
            "install-headers",
            "install-scripts",
            "install-data",
            "prefix",
            "exec-prefix",
            "home",
            "user",
            "root",
        ]
    else:
        ignore_options = []

    ignore_options = frozenset(ignore_options)

    if filenames is None:
        filenames = self.find_config_files()

    if DEBUG:
        self.announce("Distribution.parse_config_files():")

    parser = ConfigParser()
    for filename in filenames:
        with io.open(filename, encoding="utf-8") as reader:
            if DEBUG:
                self.announce("  reading {filename}".format(**locals()))
            (parser.read_file if six.PY3 else parser.readfp)(reader)
        for section in parser.sections():
            options = parser.options(section)
            opt_dict = self.get_option_dict(section)

            for opt in options:
                if opt != "__name__" and opt not in ignore_options:
                    val = self._try_str(parser.get(section, opt))
                    opt = opt.replace("-", "_")
                    opt_dict[opt] = (filename, val)

        # Make the ConfigParser forget everything (so we retain
        # the original filenames that options come from)
        parser.__init__()

    # If there was a "global" section in the config file, use it
    # to set Distribution options.

    if "global" in self.command_options:
        for opt, (src, val) in self.command_options["global"].items():
            alias = self.negative_opt.get(opt)
            try:
                if alias:
                    setattr(self, alias, not strtobool(val))
                elif opt in ("verbose", "dry_run"):  # ugh!
                    setattr(self, opt, strtobool(val))
                else:
                    setattr(self, opt, val)
            except ValueError as msg:
                raise DistutilsOptionError(msg)