cookiecutter / cookiecutter/cookiecutter

[Bug]: Files starting with PACKAGE_NAME := ... misclassified as binary and skipped

Open
#2,205 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
25.1k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

### What happened?

## Summary
Cookiecutter incorrectly classifies certain plain text template files as binary when they start with:

```
PACKAGE_NAME := {{ cookiecutter.project_slug }}
```

As a result, the file is copied without Jinja rendering, leaving template variables unresolved.

---

## Expected Behavior
The file should be treated as text and rendered:

```
PACKAGE_NAME := example-package
```

---

## Actual Behavior
The file is treated as binary and copied as-is:

```
PACKAGE_NAME := {{ cookiecutter.project_slug }}
```

No warning or error is emitted.

---

## Root Cause
Cookiecutter uses the `binaryornot` library to determine whether a file is binary.

The file starts with:

```
PACKAGE_NAME := ...
```

The first bytes are:

```
PACK
```

`binaryornot` includes `b"PACK"` in its binary signature list, so:

```
chunk.startswith(b"PACK") → True
```

This results in a false positive binary classification, causing Cookiecutter to skip rendering entirely.

---

## Debug Evidence
```python
from binaryornot.check import is_binary
print(is_binary("foo_file")) # True
```

```python
from binaryornot.helpers import _BINARY_SIGNATURES
chunk = b'PACKAGE_NAME := {{ cookiecutter.project_slug }}\n'
for sig in _BINARY_SIGNATURES:
if chunk.startswith(sig):
print(sig)
```

**Output:**
```
b'PACK'
```

## Workarounds
Any of the following avoids the issue:

- Add a blank line
```
PACKAGE_NAME := {{ cookiecutter.project_slug }}
```

- Add a comment
```
# Generated by cookiecutter
PACKAGE_NAME := {{ cookiecutter.project_slug }}
```

- Rename variable
```
PROJCT_NAME := {{ cookiecutter.project_slug }}
```
## 💡 Proposed Enhancement
Since binary detection is heuristic and can produce false positives, it would be useful to provide a way to override it.

### Proposal
Add a configuration option similar to `_copy_without_render`, for example:

```json
{
"_force_render": [
"Makefile",
"foo_file",
"*.mk"
]
}
```

### Suggested Behavior
1. If path matches `_copy_without_render` → copy without rendering
2. If path matches `_force_render` → always render as text
3. Otherwise → use binary detection

---

## Benefits
- Avoids fragile reliance on heuristics
- Gives template authors explicit control
- Solves real-world edge cases like this one
- Maintains backward compatibility

---

## Impact
- Silent rendering failures
- Hard to debug without `--verbose`
- Affects common files (e.g., Makefile, config files)
- Any file starting with `PACK...` may be impacted

---

## Conclusion
This is a false positive in binary detection leading to skipped rendering. A configurable override would make Cookiecutter more robust and predictable.

### Steps to reproduce

1. Create a minimal template:

```json
cookiecutter.json
{
"project_slug": "example-package"
}
```

```
{{cookiecutter.project_slug}}/foo_file
PACKAGE_NAME := {{ cookiecutter.project_slug }}
```

2. Run:
```
cookiecutter --no-input /path/to/template
```

3. Inspect output:
```
cat example-package/foo_file
```

### Template

cookiecutter.json
```
{
"project_slug": "example-package"
}
```

### Output / traceback

```shell

```

### Cookiecutter version

2.7.1

### Python version

3.12

### Operating system

Linux

### How did you install Cookiecutter?

uv

### Additional context

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue with `cookiecutter --no-input` and inspect `binaryornot.check.is_binary` together with `_BINARY_SIGNATURES`. Trace how binary detection affects template rendering, then verify that the `PACKAGE_NAME := {{ cookiecutter.project_slug }}` file renders as `PACKAGE_NAME := example-package` while existing binary-copy behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.