pathvalidate
A zero-dependency Python library that sanitizes and validates filenames, file paths, and LTSV labels across Windows, Linux, macOS, and POSIX platforms.
Repository Health
Technical Analysis
pathvalidate solves a narrow but recurring problem: turning arbitrary, possibly user-supplied strings into filenames and file paths that are actually safe to write to disk. It exposes both a validation API that raises a typed ValidationError when a string is unusable, and a sanitization API that rewrites the string into something valid — stripping unprintable characters, swapping out characters that are illegal on the target platform, truncating to the platform’s maximum path length, and renaming values that collide with reserved OS names like CON or NUL on Windows.
The library is explicitly multi-platform: callers can validate or sanitize against Linux, Windows, macOS, POSIX, or a conservative universal profile that is valid everywhere at once, which is the default. It also ships ready-made integrations for argparse and click, so a CLI author can drop a validator or sanitizer straight into an argument parser instead of writing that plumbing by hand.
Because it has zero runtime dependencies and a pure-Python implementation, it slots into scripts, CLI tools, upload handlers, and build tooling without adding to a project’s dependency surface — which is likely why it accumulates over three million downloads a week despite being a small, single-purpose utility.
What You Get
validate_filename()/validate_filepath()that raise a typedValidationErrordescribing exactly why a name is invalid (invalid character, reserved name, length limit, absolute-path mismatch)sanitize_filename()/sanitize_filepath()that rewrite an unsafe string into a valid one, with pluggable handlers for empty results and reserved-name collisionsis_valid_filename()/is_valid_filepath()boolean helpers for quick checks without catching exceptions- Per-platform validation profiles (
Windows,Linux,macOS,POSIX,universal) so the same code can target one OS or stay portable across all of them - Drop-in
argparseandclickargument validators/sanitizers (pathvalidate.argparse,pathvalidate.click) for CLI tools - LTSV (Labeled Tab-separated Values) label validation and sanitization via
validate_ltsv_label()/sanitize_ltsv_label() - Structured error codes (e.g.
PV1100for invalid characters) via theErrorReasonenum, so calling code can branch on the failure reason instead of parsing message strings
Common Use Cases
- Turning user-uploaded file titles into safe filenames before writing them to disk in a web upload handler
- Validating CLI arguments that will become output filenames, with
argparse/clickintegration doing the checking automatically - Sanitizing filenames generated from external data (API responses, scraped titles, database rows) before saving files in a batch job
- Enforcing cross-platform-safe filenames in tools that must run identically on Windows, Linux, and macOS (e.g. build tools, archivers, sync clients)
- Rejecting reserved Windows device names (
CON,PRN,COM1, etc.) that would silently fail to create on Windows but work fine elsewhere
Under The Hood
Architecture
pathvalidate is organized as small, focused modules rather than one large class hierarchy: _base.py defines the BaseFile/AbstractValidator/AbstractSanitizer/BaseValidator abstract base classes that encode shared platform logic (max path length per OS, POSIX/Windows/macOS/universal checks), while _filename.py and _filepath.py each provide a concrete *Validator/*Sanitizer pair built on those bases, plus module-level convenience functions (validate_filename, sanitize_filename, etc.) that are thin wrappers instantiating the class and calling it once. Sanitization is implemented as a regex substitution pass (built once per platform via _get_sanitize_regexp()) followed by re-validation, so a FileNameSanitizer.sanitize() call strips invalid characters, truncates to the byte-length limit via truncate_str(), and then delegates reserved-name and trailing-whitespace/period handling back to its internal _validator. Errors flow through a single ValidationError subclassing ValueError, carrying a structured ErrorReason enum (with a PVxxxx error code) so callers can pattern-match on failure cause rather than string-matching exception text. argparse.py and click.py are separate integration modules that wrap the core validators/sanitizers as parser callbacks, keeping the core library free of any CLI-framework dependency.
Tech Stack
The library targets Python 3.9+ and, per its README and pyproject.toml, ships with zero runtime dependencies — everything is implemented with the standard library (re, os, posixpath, pathlib, enum, abc). Packaging uses a setuptools + setuptools_scm build backend for git-tag-derived versioning. Tooling is comprehensive: black and ruff for formatting/linting, isort for import ordering, pyright for type checking (the package ships a py.typed marker for downstream type-checking support), pytest with branch coverage via coverage.py and Coveralls integration, and tox for running the matrix across Python versions. CI is GitHub Actions-based, running Linux/macOS/Windows matrices plus a CodeQL security scan, matching the project’s own cross-platform validation claims.
Code Quality
The test/ directory contains roughly 2,700 lines across dedicated test files for filename, filepath, common, symbol, LTSV, handler, error, argparse, and click behavior — a substantial and well-partitioned suite for a library this size, run against every supported platform in CI rather than just one. Code favors explicit, typed exception classes (InvalidCharError, ReservedNameError, NullNameError, all subclassing ValidationError) over silent failures or generic exceptions, and functions consistently carry full type hints together with docstrings documenting arguments, raised exceptions, and cross-references to related functions. The abstract base class hierarchy (AbstractValidator/AbstractSanitizer) keeps validator and sanitizer responsibilities cleanly separated, and deprecated parameters (like check_reserved on sanitize_filename) are phased out via DeprecationWarning rather than silently changing behavior.
What Makes It Unique
pathvalidate’s distinguishing choice is treating “validate” and “sanitize” as two first-class, separately composable operations against the same set of per-platform rules, rather than only offering one or the other as many similar utilities do. Its platform model is also unusually thorough for a single-purpose library — it doesn’t just special-case Windows reserved names, it separately encodes POSIX, macOS-specific reserved characters (:), and a conservative universal profile that intersects the constraints of every platform so a name validated as universal is guaranteed safe everywhere. The built-in argparse/click integrations and structured, code-bearing exceptions are conveniences aimed squarely at CLI-tool authors, a use case most generic string-validation libraries don’t address directly.