Skip to content

[Detail Bug] Packaging: Installing from source fails when README.md is not read as UTF-8 or relative to setup.py #426

@detail-app

Description

@detail-app

Summary

  • Context: The setup.py file is the entry point for installing the django-webpack-loader package and generating its metadata.
  • Bug: setup.py attempts to read the README.md file without specifying an encoding and without using the project's own rel() function to ensure the file is found relative to the script's directory.
  • Actual vs. expected: It attempts to read README.md using the default system encoding and expects the file to be in the current working directory, whereas it should always use UTF-8 and find the file relative to setup.py.
  • Impact: The installation will crash with a UnicodeDecodeError on systems where the default encoding is not UTF-8 (such as many Windows installations) because README.md contains emojis and special characters. It also fails with a FileNotFoundError if setup.py is executed from outside the project root directory.

Code with bug

12: with open("README.md", "r") as handler: # <-- BUG 🔴 [Missing rel() and encoding="utf-8"]
13:     README = handler.read()

Evidence

1. UnicodeDecodeError in non-UTF-8 environments

The README.md file contains UTF-8 characters such as emojis (💥, ⚠️). On systems with a different default encoding (e.g., CP1252 on Windows), the open("README.md", "r") call will fail.

# Simulating CP1252 environment (typical of Windows)
python3 -c 'with open("README.md", "r", encoding="cp1252") as f: f.read()'
# Traceback (most recent call last):
#   ...
#   File "/root/.pyenv/versions/3.12.10/lib/python3.12/encodings/cp1252.py", line 23, in decode
#     return codecs.charmap_decode(input,self.errors,decoding_table)[0]
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 4654: character maps to <undefined>

2. FileNotFoundError when run from outside project root

Although a rel() helper function is defined to find files relative to the project directory, it is not used for the README.md file.

# Executing setup.py from a different directory
cd /tmp && python3 /home/user/django-webpack-loader/setup.py --version
# Traceback (most recent call last):
#   File "/home/user/django-webpack-loader/setup.py", line 12, in <module>
#     with open("README.md", "r") as handler:
#          ^^^^^^^^^^^^^^^^^^^^^^
# FileNotFoundError: [Errno 2] No such file or directory: 'README.md'

Note that rel() is correctly used for __init__.py just a few lines later:

15: with open(rel("webpack_loader", "__init__.py")) as handler:

Why has this bug gone undetected?

Most developers and automated CI systems run python setup.py or pip install . from the root of the project, where the current working directory contains README.md. Additionally, many of these environments default to UTF-8 encoding (e.g., Linux/macOS), masking the encoding issue. The bug only surfaces when installing from source on platforms like Windows or when using build tools that execute setup.py from a different working directory.

Recommended fix

Use the rel() function and explicitly set the encoding to utf-8:

with open(rel("README.md"), "r", encoding="utf-8") as handler: # <-- FIX 🟢
    README = handler.read()

Related bugs

The packages list uses non-standard slash notation which leads to incorrect metadata in top_level.txt, although it happens to work for installation in most cases:

packages=[
    "webpack_loader",
    "webpack_loader/templatetags", # Should be "webpack_loader.templatetags"
    "webpack_loader/contrib",      # Should be "webpack_loader.contrib"
],

Additionally, setup.cfg references a non-existent README.rst file:

[metadata]
description_file = README.rst # Should be README.md

History

This bug was introduced in commit c81d425 (@owais, 2016-11-06). While attempting to add support for README.rst for PyPI, the rel() helper function (which ensures path resolution works from any directory) was omitted for the README file, though it was preserved for reading the package's __init__.py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions