Metadata-Version: 2.5
Name: bxc
Version: 1.3.0
Summary: A lightweight Python library and CLI tool to format BibTeX references using CSL
Project-URL: Homepage, https://forgejo.fchicout.dev/fchicout/bxc
Project-URL: Issues, https://forgejo.fchicout.dev/fchicout/bxc/issues
Author-email: Fabio Chicout <fchicout@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: bibliography,bibtex,citation,csl,references
Classifier: Development Status :: 5 - Production/Stable
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Text Processing :: Markup
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: bibtexparser<3,>=1.4.3
Requires-Dist: citeproc-py>=0.6.0
Requires-Dist: platformdirs>=4.0.0
Provides-Extra: lint
Requires-Dist: mypy>=2.0.0; extra == 'lint'
Requires-Dist: ruff>=0.14.0; extra == 'lint'
Provides-Extra: test
Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# bxc (Bibliographic Reference Formatter)

A lightweight Python library and Command Line Interface (CLI) tool designed to format BibTeX bibliography records into academic styles (like IEEE, ABNT, ACM) using **Citation Style Language (CSL)**. It natively outputs to Markdown, HTML, and Plain Text, making it perfect for static blogs, documentation websites, and automated citation pipelines.

---

## Key Features

*   **Zero-Configuration UX:** Automatically resolves, downloads, and caches standard academic styles (e.g., `ieee`, `nature`, `abnt`) on demand from the official CSL repositories.
*   **Offline-First Style Resolution:** Ships with a bundled, compressed snapshot of the entire CSL styles repository (10,000+ styles, ~2 MB). Style lookups check this local archive before ever touching the network, so `bxc format` works instantly and fully offline for any standard style - the CDN is only used for brand-new styles not yet in the bundle, or when explicitly syncing (`bxc cache update` / `bxc cache rebuild`).
*   **LaTeX to Unicode Normalization:** Seamlessly cleans and converts LaTeX accent escape codes (like `{\`e}`, `\c{c}`) to standard Unicode (e.g., `è`, `ç`) prior to formatting.
*   **Multiple Output Targets:** Renders styled reference lists natively to Markdown, HTML, and Plain Text.
*   **Local Cross-Platform Caching:** Caches downloaded styles locally according to OS-native standards (`~/.cache/bxc/` on Linux, `~/Library/Caches/bxc/` on macOS, `%LOCALAPPDATA%\\bxc\\Cache` on Windows; run `bxc cache status` to see yours).
*   **Flexible Interface:** Use it as a terminal CLI tool or import it as a standard Python library.

---

## Installation

Requires Python 3.11 or newer.

**pip / pipx** (any platform):

```bash
pipx install bxc      # or: pip install bxc
```

bxc works with **bibtexparser 1.4.3+ and 2.x**: pip installs the newest one, and an application that already depends on either major can install bxc next to it. The two give the same result: bxc reads values itself (macros, `#` concatenation, quoting, multi-line text), skips the same malformed entries and decodes LaTeX with the same code on both. One known difference: bibtexparser 2.x does not accept whitespace between the `@` and the entry type (`@  article{...}`), so such an entry is rejected with a parse error there.

**Native installers** are attached to each release on the project page, with a `SHA256SUMS` file to verify them:

| Platform | Package | Install |
|---|---|---|
| Debian 12+, Ubuntu 24.04+ | `bxc_<version>_all.deb` | `sudo apt install ./bxc_<version>_all.deb` |
| Fedora 40+, RHEL / AlmaLinux 10+ | `bxc-<version>-1.noarch.rpm` | `sudo dnf install ./bxc-<version>-1.noarch.rpm` |
| Windows 10+ (x64) | `bxc-<version>-x64.msi` | double-click, or `msiexec /i bxc-<version>-x64.msi` |

The deb and rpm bundle bxc's pure-Python dependencies and use your distribution's `python3` and `python3-lxml`; the package manager installs them for you. They need Python 3.11+, so older distributions (Ubuntu 22.04, RHEL 9) should use `pipx` with a newer Python instead. The Windows installer is self-contained (it ships its own Python) and adds `bxc` to the system `PATH`; it is not code-signed, so Windows SmartScreen may warn on first run. Open a new terminal after installing.

---

## Quick Start

### Command Line Interface (CLI)

Format a `.bib` file to Markdown using IEEE style:
```bash
bxc format citations.bib --style ieee --output markdown
```

Search for a citation style:
```bash
bxc search "ABNT"
```

Clear, sync, or inspect the local cache:
```bash
bxc cache update   # refresh the style search index from the CDN
bxc cache rebuild   # re-download every cached style from the CDN (sync with CDN)
bxc cache status   # show cache location and stats
bxc cache clear    # remove all cached styles and the index
```

`bxc cache update` and `bxc cache rebuild` are the commands that talk to the CDN on purpose - they're how you get styles fresher than the bundled snapshot. Everyday `bxc format` calls never need them: they resolve styles from the bundled local archive first (see [Offline-First Style Resolution](#key-features) above), which is why bxc works out of the box without a network connection.

Maintainers can refresh that bundled archive itself (a periodic task, not something end users run) with:
```bash
python build_styles_bundle.py
```

### Offline mode

Style lookup is already offline-first, but a style that is not in the bundle makes bxc try the network. To forbid that completely, pass `offline=True`, use `bxc --offline ...`, or set `BXC_OFFLINE=1` (also `true`, `yes`, `on`):

```python
bxc.format_bibtex(source, style="ieee", offline=True)
```

Offline, styles come from the bundle, the local cache or a local `.csl` path. A style that is none of these raises `StyleNotFoundError` ("... is not in the bundled styles or the local cache, and offline mode is on") **without any connection attempt**; `bxc cache update` and `bxc cache rebuild` refuse to run; `bxc search` uses the bundled index. An explicit `offline=True/False` argument wins over the variable.

### Network safety

Remote styles and the style index are downloaded over `https` by default. A private mirror set with `BXC_REMOTE_URL` may still use `http` or `ftp` (it works, with a warning unless the host is loopback), or `file:` for a local folder. A download is refused if it is larger than 5 MB (20 MB for the index), if an `https` request is redirected to a non-`https` URL, or if it is not a CSL style (well-formed XML with a `<style>` root), so a bad response is never cached. Style files you pass by path are limited to 5 MB and BibTeX input to 100 MB. New cache directories are created readable only by you.

### Using bxc without side effects

By default a style is written to the per-user cache on first use. To keep a call free of filesystem writes (unit tests, read-only or sandboxed environments, a library embedded in another tool), pass `cache=False` or set `BXC_CACHE=off`:

```python
bxc.format_bibtex(source, style="ieee", cache=False)   # nothing is written; the style is read from the bundle
```

With caching off, the style comes from an existing cache file, the bundled styles, or (for a style not in the bundle) the remote, and is kept in memory. `BXC_CACHE=off` (also `0`, `false`, `no`) does the same without changing code and also stops `bxc search` from writing the style index; an explicit `cache=True/False` argument wins over the variable. If the cache directory cannot be created or written, a normal call falls back to the same in-memory path instead of failing.

### Bundling with PyInstaller

bxc works inside PyInstaller apps (one-dir and one-file) with no extra options: it registers a PyInstaller hook through the `pyinstaller40` entry point, which adds the bundled style snapshot and style index, and the locale and schema data of its `citeproc-py` dependency (PyInstaller does not collect package data files on its own, so without this a frozen app fails at start). Install bxc into the environment you freeze from, then:

```bash
pyinstaller --onefile myapp.py
```

If your build does not pick up installed hooks, add `--collect-data bxc --collect-data citeproc`. For a frozen app that must never touch the network or the disk, use `offline=True, cache=False` (see above). `packaging/frozen_smoke.py` is the script CI freezes and runs with every network call blocked; it is a good starting point for your own check. Verified on Linux with PyInstaller 6; the Windows build is expected to work the same way but is not covered by CI.

### Python API

```python
import bxc

# Parses, converts LaTeX escapes, fetches CSL, and formats citation
markdown_ref = bxc.format_bibtex(
    bibtex_source="@article{smith2026, ...}",
    style="ieee",
    output_format="markdown"
)

print(markdown_ref)
```

---

## CLI reference

`bxc format [BIB] --style STYLE [--output plain|markdown|html] [--mode bibliography|citation] [--cite KEYS] [--out-file PATH]`

*   `BIB` (or `--bib/-b`): path to a `.bib` file; use `-` to read from stdin.
*   `--style/-s`: a CSL style name (e.g. `ieee`, `apa`) or the path to a local `.csl` file.
*   `--cite/-c`: comma-separated citation keys to format.
*   Exit codes: `0` success, `1` error (parse failure, style not found, ...), `2` invalid usage, `130` interrupted. With the global option `--detailed-exit-codes` (or `BXC_DETAILED_EXIT_CODES=1`) the errors that have their own code exit with `3` (the BibTeX input cannot be parsed), `4` (style not found) or `5` (a download failed or is not allowed, e.g. offline mode); every other error is still `1`.

`bxc search [QUERY] [--category CATEGORY]` searches the style registry; `bxc cache {status,update,rebuild,clear}` manages the local cache.

## Public API and versioning

bxc follows [Semantic Versioning](https://semver.org/): a **major** release may break the public API, a **minor** release only adds to it, and a **patch** release only fixes bugs.

**What is public** (covered by that promise):

<!-- public-api:start -->
- `bxc.format_bibtex`
- `bxc.parse_bibtex`
- `bxc.search_styles`
- `bxc.resolve_style`
- `bxc.get_cache_dir`
- `bxc.get_cache_status`
- `bxc.update_cache`
- `bxc.rebuild_cache`
- `bxc.clear_cache`
- `bxc.BxcError`
- `bxc.BibTeXParseError`
- `bxc.StyleNotFoundError`
- `bxc.NetworkError`
<!-- public-api:end -->

Also public: the `bxc` command-line interface (subcommands, options and the exit codes above) and the environment variables `BXC_CACHE_DIR`, `BXC_REMOTE_URL`, `BXC_CACHE`, `BXC_OFFLINE` and `BXC_DETAILED_EXIT_CODES`. `BibTeXParseError`, `StyleNotFoundError` and `NetworkError` are subclasses of `BxcError`; `parse_bibtex` returns a list of dicts with the keys `ID` and `ENTRYTYPE` plus one lower-case key per BibTeX field.

**What is not public:** anything not listed above, including every submodule (`bxc.cache`, `bxc.formatter`, `bxc.parser`, `bxc.latex`, `bxc.author`, `bxc.registry`) and its classes, helpers and constants, the `visited` argument of `resolve_style`, and the layout and file names of the bundled style data. Do not import from them; they can change in any release. The exact text a style renders is not byte-stable either: bug fixes and updated bundled styles can change it in a minor or patch release.

**Breaking changes** (major release only): removing or renaming a public name; removing a parameter, changing the order of positional parameters or making an optional one required; changing what an existing call returns or which exception it raises; removing or changing the meaning of a command-line option, exit code or environment variable; raising the minimum Python version; dropping support for a supported major version of a dependency (for example `bibtexparser` 1.x).

**Not breaking** (minor or patch release): new functions, new optional parameters, new subcommands, options and environment variables, new `BxcError` subclasses, supporting more Python or dependency versions, bug fixes, and internal changes. Optional parameters added after 1.1.0 are **keyword-only**, so adding more never changes how an existing call is written.

**Deprecation:** something public is first marked deprecated (a `DeprecationWarning` and a note under "Deprecated" in the [changelog](CHANGELOG.md)) for at least one minor release before it is removed in the next major release.

## License

Apache License 2.0. The bundled CSL styles are from the [Citation Style Language styles repository](https://github.com/citation-style-language/styles) and are licensed under CC BY-SA 3.0; see `NOTICE`.
