# Buggy code
def get_entrypoints(filename):
    # type: (str) -> Tuple[Dict[str, str], Dict[str, str]]
    if not os.path.exists(filename):
        return {}, {}

    # This is done because you can pass a string to entry_points wrappers which
    # means that they may or may not be valid INI files. The attempt here is to
    # strip leading and trailing whitespace in order to make them valid INI
    # files.
    with open(filename) as fp:
        data = StringIO()
        for line in fp:
            data.write(line.strip())
            data.write("\n")
        data.seek(0)

    # get the entry points and then the script names
    entry_points = pkg_resources.EntryPoint.parse_map(data)
    console = entry_points.get("console_scripts", {})
    gui = entry_points.get("gui_scripts", {})

    def _split_ep(s):
        # type: (pkg_resources.EntryPoint) -> Tuple[str, str]
        """get the string representation of EntryPoint,
        remove space and split on '='
        """
        split_parts = str(s).replace(" ", "").split("=")
        return split_parts[0], split_parts[1]

    # convert the EntryPoint objects into strings with module:function
    console = dict(_split_ep(v) for v in console.values())
    gui = dict(_split_ep(v) for v in gui.values())
    return console, gui


# Traceback (UnicodeDecodeError)
pip install demo-game-guess-number
Collecting demo-game-guess-number
Using cached demo_game_guess_number-0.0.5-py3-none-any.whl (2.5 kB)
Installing collected packages: demo-game-guess-number
ERROR: Exception:
Traceback (most recent call last):
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\cli\base_command.py", line 188, in _main
status = self.run(options, args)
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\cli\req_command.py", line 185, in wrapper
return func(self, options, args)
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\commands\install.py", line 407, in run
use_user_site=options.use_user_site,
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\req\__init__.py", line 71, in install_given_reqs
**kwargs
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\req\req_install.py", line 811, in install
direct_url=direct_url,
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\operations\install\wheel.py", line 630, in install_wheel
direct_url=direct_url,
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\operations\install\wheel.py", line 426, in install_unpacked_
wheel
console, gui = get_entrypoints(ep_file)
File "c:\users\win7_laptop\appdata\local\programs\python\python37\lib\site-pac
kages\pip\_internal\operations\install\wheel.py", line 119, in get_entrypoints
for line in fp:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x97 in position 26: illegal m
ultibyte sequence

# Fixed code
def get_entrypoints(filename):
    # type: (str) -> Tuple[Dict[str, str], Dict[str, str]]
    if not os.path.exists(filename):
        return {}, {}

    # This is done because you can pass a string to entry_points wrappers which
    # means that they may or may not be valid INI files. The attempt here is to
    # strip leading and trailing whitespace in order to make them valid INI
    # files.
    with io.open(filename, encoding="utf-8") as fp:
        data = io.StringIO()
        for line in fp:
            data.write(line.strip())
            data.write("\n")
        data.seek(0)

    # get the entry points and then the script names
    entry_points = pkg_resources.EntryPoint.parse_map(data)
    console = entry_points.get("console_scripts", {})
    gui = entry_points.get("gui_scripts", {})

    def _split_ep(s):
        # type: (pkg_resources.EntryPoint) -> Tuple[str, str]
        """get the string representation of EntryPoint,
        remove space and split on '='
        """
        split_parts = str(s).replace(" ", "").split("=")
        return split_parts[0], split_parts[1]

    # convert the EntryPoint objects into strings with module:function
    console = dict(_split_ep(v) for v in console.values())
    gui = dict(_split_ep(v) for v in gui.values())
    return console, gui