python-bidi

Bidirectional (BiDi) text layout for Python, powered by a Rust unicode-bidi core with a pure-Python fallback.

Library
PyPI
v0.6.11
126stars
LGPL-3.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
53/100Fair
Development Activity36
Maintenance36
Community68
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture70
Code Quality68
Innovation60
Learning Curve82

python-bidi implements the Unicode Bidirectional Algorithm (UAX #9) for reordering mixed left-to-right and right-to-left text, such as Hebrew or Arabic embedded in English, into its correct visual display order. It ships two implementations: a fast Rust-based wrapper around the unicode-bidi crate (the default, exposed via bidi.get_display) and a pure-Python implementation (bidi.algorithm.get_display) kept for backward compatibility with older releases.

Beyond the library API, it installs a pybidi command-line tool that reads text from an argument or stdin and prints the reordered display string, useful for quick terminal or shell-script conversions of RTL text. The project has been maintained since 2010 and is a common dependency wherever Python applications need to render Hebrew, Arabic, or other RTL scripts correctly — PDF generation, terminal UIs, and text rendering pipelines that lack native bidi support.

What You Get

  • A Rust-backed get_display() function wrapping the unicode-bidi crate for fast, spec-compliant bidi reordering
  • A pure-Python bidi.algorithm.get_display() implementation kept for compatibility with pre-Rust releases
  • A pybidi command-line tool for reordering text from arguments or stdin without writing any code
  • Support for both str and bytes input/output with explicit encoding control
  • A get_base_level() helper to detect whether a paragraph’s base direction is LTR or RTL

Common Use Cases

  • Rendering Hebrew/Arabic in PDFs - developers using PDF libraries without native bidi support call get_display() before drawing RTL text so glyphs render in correct visual order
  • Terminal and log output - scripts pipe mixed-direction text through the pybidi CLI so RTL substrings display correctly in terminals that don’t apply bidi reordering themselves
  • Legacy system compatibility - projects pinned to the pure-Python algorithm module keep using bidi.algorithm.get_display() when they can’t add a Rust/maturin build step
  • Text rendering pipelines - image/graphics libraries that draw text glyph-by-glyph use get_display() to precompute visual order before layout

Under The Hood

Architecture The package is a thin, cleanly-separated stack: src/lib.rs is a small PyO3 module (bidi.bidi) built via maturin that wraps the unicode-bidi Rust crate’s BidiInfo type to compute paragraph levels and reordered lines; bidi/wrapper.py is the sole Python consumer of that native module, translating between str/bytes and exposing get_display()/get_base_level(); bidi/algorithm.py (673 lines) is an independent, self-contained pure-Python reimplementation of the same algorithm kept for backward compatibility, alongside bidi/mirror.py (388 lines) holding the character-mirroring table it needs; bidi/__init__.py re-exports the Rust-backed API and defines the pybidi CLI entry point. There is no dependency injection or internal layering beyond this — it’s a flat, functional design where each file has one clear responsibility, so a change to the Rust core’s signature only requires updating wrapper.py.

Tech Stack Built with maturin (PyO3) compiling the Rust unicode-bidi crate into a native bidi.bidi extension module, targeting Python 3.9 through 3.14. Development uses uv for environment management, nox (configured via noxfile.py) as the session runner, and pytest for tests; CI is defined in .github/workflows/CI.yml with Dependabot enabled for dependency updates. Documentation is built with Sphinx (docs/conf.py) and published to Read the Docs. As a computational text library it has no web, ORM, or database dependencies — its only external dependency is the unicode-bidi Rust crate.

Code Quality Tests live in three focused files — parity tests for the Python implementation, parity tests for the Rust implementation, and a dedicated free-threaded (no-GIL) CPython compatibility test — run via pytest/nox and wired into CI. The Rust boundary raises typed PyValueErrors for invalid input (e.g. a bad base_dir or empty text) that propagate cleanly into Python; the wrapper module carries explicit type hints (Optional, Union) while the legacy pure-Python algorithm module is comparatively less typed, reflecting its age. Overall the codebase is compact, has clear naming, and maintains real test coverage for a project of its size.

What Makes It Unique Its distinguishing choice is architectural rather than algorithmic: it maintains both a Rust-native implementation (wrapping the same unicode-bidi crate used in other Rust text-layout stacks) and a legacy pure-Python implementation side by side, so downstream users can choose speed or a zero-build-step dependency. It also explicitly tests and supports free-threaded (no-GIL) CPython builds ahead of most small libraries, and exposes a ready-to-use CLI on top of the library API — a level of polish uncommon for a narrowly-scoped algorithm implementation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search