binaryornot
BinaryOrNot is a zero-dependency Python library and command-line program that guesses whether a file or byte chunk is binary or text. Version 0.6 reads at most the first 512 bytes, checks known file extensions and magic signatures, then applies a trained decision tree to byte ratios, entropy, printable runs, byte-order marks, and validity under several Unicode and CJK encodings. The answer is a Boolean classification, not an encoding name, MIME type, or guarantee that later bytes are text-safe.
BinaryOrNot is a good lightweight routing heuristic and much smarter than searching for a null byte. Do not use its Boolean as proof that arbitrary content is safe, correctly encoded, or a particular file type.
Use it if
- You need a fast first-pass filter before opening unknown files as text
- You process mixed UTF, legacy CJK, archive, image, font, executable, and document files where a null-byte check is too crude
- You want both a one-function Python API and a tiny CLI with no runtime dependencies
- You can tolerate heuristic classification and have a separate policy for ambiguous or high-risk inputs
- You need a MIME type or exact file format: BinaryOrNot returns only True or False, while filetype, puremagic, or libmagic identify formats
- You need to detect a text encoding: the classifier tests whether several encodings are plausible but does not tell you which decoder to use
- A false text result creates a security boundary: the model is a heuristic trained on samples, and adversarial or polyglot content can defeat a binary-versus-text guess
- Important evidence may appear after byte 512: is_binary reads only the starting chunk, so files with a long text prefix and binary payload can be misclassified
- You still support Python 3.9 or older: version 0.6.0 declares Python 3.10 or newer
Setup reality
pip install binaryornot installs version 0.6 with no declared runtime dependencies, but it now requires Python 3.10 or newer. The common API accepts a string path, bytes path, or pathlib.Path and opens the target in binary mode. Missing files and permission errors are normal filesystem exceptions; the library does not turn them into False. By default it first checks the filename extension against a packaged list of known binary extensions, then reads only the first 512 bytes if the extension did not decide the result. Set check_extensions=False when a misleading user-controlled suffix should not outweigh content, or call is_binary_string when you already hold bytes. Empty bytes are classified as text. The decision tree considers 24 features and known signatures, but it is still a classifier, not a parser. The docs list four known encoding gaps, including ISO-2022-KR and three EBCDIC code pages, and only the file prefix is examined. A file may also be both meaningfully text and structurally binary, such as a container with a readable preamble. Treat the Boolean as routing advice: attempt decoding with explicit error handling, enforce size and path controls separately, and let a real format parser validate any untrusted upload. The CLI accepts one filename and prints True or False; it has no recursive directory mode, stdin byte mode, JSON output, or confidence score, so batch workflows need a small Python wrapper.
Patterns
Classify a file pathclassify-file
from binaryornot.check import is_binary
if is_binary("uploads/report.csv"):
print("binary")
else:
print("text")The result is a heuristic based on extension and at most the first 512 bytes.
Classify pathlib entriesuse-pathlib-path
from pathlib import Path
from binaryornot.check import is_binary
text_files = [path for path in Path("docs").rglob("*") if path.is_file() and not is_binary(path)]Handle permission and disappearing-file errors if the directory can change during traversal.
Classify content despite its suffixignore-file-extension
from binaryornot.check import is_binary
content_says_binary = is_binary("suspicious.txt", check_extensions=False)Disabling extension checks is useful for untrusted names, but the content decision still sees only the first chunk.
Classify bytes already in memoryclassify-byte-chunk
from binaryornot.helpers import is_binary_string
with open("mystery.dat", "rb") as stream:
chunk = stream.read(512)
print(is_binary_string(chunk))Pass bytes, not str. Empty bytes return False and are treated as text-like.
Use classification before explicit decodingroute-text-decoding
from pathlib import Path
from binaryornot.check import is_binary
path = Path("incoming/message.txt")
if is_binary(path, check_extensions=False):
raise ValueError("binary input is not accepted")
text = path.read_text(encoding="utf-8", errors="strict")A False result does not identify UTF-8, so decoding still needs a chosen encoding and error policy.
Separate a batch for later processingfilter-upload-batch
from binaryornot.check import is_binary
text_paths, binary_paths = [], []
for path in uploaded_paths:
target = binary_paths if is_binary(path, check_extensions=False) else text_paths
target.append(path)Use a real parser and independent upload limits after routing; classification is not validation.
Handle unreadable paths explicitlyhandle-file-errors
from binaryornot.check import is_binary
try:
binary = is_binary(path)
except (FileNotFoundError, PermissionError, IsADirectoryError) as error:
logger.warning("cannot inspect %s: %s", path, error)
binary = NoneThe library lets filesystem exceptions propagate instead of converting an unreadable target into a guess.
Check one file from the shelluse-command-line
binaryornot README.md
# False
binaryornot image.png
# TrueThe CLI handles one filename per invocation and prints a Python-style Boolean, not JSON or a confidence score.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| filetype | PyPI | You need a pure-Python guess of the specific binary format from magic bytes |
| puremagic | PyPI | You want pure-Python magic-number matching with extension and MIME information |
| python-magic | PyPI | You can install the system libmagic library and need broad MIME or descriptive type detection |