Open-source project
JessicaTegner/pypandoc avatar
JessicaTegner/pypandoc

JessicaTegner/pypandoc: a thin Python wrapper around pandoc, and the packaging problems it solves

Thin wrapper for "pandoc" (MIT)

1,153 stars121 forksPythonMIT

At a glance

What is it?
pypandoc calls pandoc, the universal document converter, from Python, and the interesting part of the repository is not the API but the deployment work: two identical packages, one with pandoc bundled and one without, a rule for choosing between an installed pandoc and a downloaded one, and an environment variable that pins the binary when a web server's user cannot see your PATH.
Who is it for?
Adopt pypandoc when your Python application has to produce documents in several formats, because it wraps the converter that already handles them and gives you a stable Python surface, an output file mode for the formats that need it, and a way to pin a specific pandoc binary. Do not adopt it for a single conversion in a script you control, since calling pandoc directly is a shorter path with one less dependency.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A wrapper, and the problem it actually solves

Pypandoc provides a thin wrapper for pandoc, a universal document converter, and the description is accurate to a fault. There is no format logic in this package, no parser and no renderer. What there is is a Python interface to a command line program, and the package is worth looking at because of how carefully it handles the awkward parts of doing that.

The core API is two functions. `convert_file` takes a file and an output format, and infers the input format from the filename, which you can override with the `format` argument. `convert_text` takes a string and requires the input format to be stated, since a bare string has no extension to infer from.

python
import pypandoc

# With an input file: it will infer the input format from the filename
output = pypandoc.convert_file('somefile.md', 'rst')

# ...but you can overwrite the format via the `format` argument:
output = pypandoc.convert_file('somefile.txt', 'rst', format='md')

# alternatively you could just pass some string. In this case you need to
# define the input format:
output = pypandoc.convert_text('# some title', 'rst', format='md')
# output == 'some title\r\n==========\r\n\r\n'

Two contracts are stated and worth respecting. `convert_text` expects its input to be unicode or utf-8 encoded bytes, and the convert functions always return a unicode string.

The project is MIT licensed, with `Juho Vepsäläinen` and `Jessica Tegner` as the authors, the development status is classified as mature, and the package metadata says it supports Python 3.7 through 3.15 on both CPython and PyPy. It is also on conda-forge, and the repository ships a Dockerfile and a docker-compose file, so containerised deployment is a supported path rather than an afterthought.

Two identical packages, and the version tiebreak

Pypandoc is published as two packages, `pypandoc` and `pypandoc_binary`, and the README is clear that they are identical apart from one thing: one includes pandoc and the other does not.

That single difference decides most of the installation decisions, and the documentation walks through the options. If you want to install pandoc yourself, or you are on an unsupported platform, you take `pypandoc` and install pandoc manually. If you want pandoc included out of the box, you take `pypandoc_binary`, which ships prebuilt wheels for Windows and macOS.

The Linux case is the interesting one, and it has a hard limit. If you use Linux and have your own wheelhouse, you can build a wheel that includes pandoc with `uv build --wheel binary/`, but the documentation warns that this works only on 64 bit Intel systems, because the pandoc binary is downloaded from the official releases rather than built from source.

The selection rule when both are present is also specified. If pandoc is already installed, that is, it is in the PATH, pypandoc uses the version with the higher version number, and if both are the same, the already installed version.

So the wrapper does not force a choice on you at import time, it resolves one. That is a small design decision that removes a class of surprise, since the alternative, a hard coded path to a bundled binary, is exactly the thing that breaks when a base image changes underneath you.

Three ways to get pandoc onto the machine

Pandoc has to exist on the machine before pypandoc can do anything, and the README gives three routes.

The first is to let pypandoc download it, on Windows, macOS or 64 bit Intel Linux:

python
pip install pypandoc
from pypandoc.pandoc_download import download_pandoc
# see the documentation how to customize the installation path
# but be aware that you then need to include it in the `PATH`
download_pandoc()

The default install location is included in the search path for pandoc, so you do not need to add it to the PATH yourself, and the latest pandoc version is installed by default, with a specific version available through `download_pandoc(version='1.19.1')`. The same thing is available from the command line, through the `pypandoc` console script, which the package declares as `pypandoc = "pypandoc.cli:main"`:

code
# install latest pandoc to default path
pypandoc download

# Download a specific version
pypandoc download --version 3.6

The second route is conda, where the conda packages also install the pandoc package, so pandoc is available in the installation. You install it with `conda install -c conda-forge pypandoc`, and adding the channel to your conda config lets you use `conda install pypandoc` and `conda update pypandoc` directly.

The third is the system package manager, and the list is worth scanning because it shows the platforms the project actually supports: apt on Ubuntu and Debian, yum on Fedora and Red Hat, pacman on Arch, Homebrew on macOS with `pandoc-citeproc` and the mactex cask, cabal on a machine with Haskell, the official installer on Windows, and `pkg install hs-pandoc` on FreeBSD.

One caveat follows all of them. Not all install mechanisms put pandoc in the PATH, so you either have to change the PATH yourself or set the full path to pandoc in `PYPANDOC_PANDOC`.

PYPANDOC_PANDOC and the web server problem

The environment variable is the most practically useful thing in the README, and the reason is a deployment failure mode that is easy to hit.

You can point to a specific pandoc binary by setting `PYPANDOC_PANDOC` to the full path, and if this environment variable is set, it is the only place where pandoc is searched for. That is a strong statement on purpose, since it removes the ambiguity of a PATH that happens to contain two pandocs.

The case the documentation highlights is specific and common: pandoc is installed, but a web server with its own user cannot find the binaries. The same problem that makes a cron job fail because the cron user has a smaller PATH. Setting the variable removes the dependency on the environment of whatever process happens to run your code.

You can also set it at runtime, which is the way to do it inside an application that controls its own environment:

python
import os
os.environ.setdefault('PYPANDOC_PANDOC', '/home/x/whatever/pandoc')

The `setdefault` is the detail worth copying. It respects an operator who has already set the variable in the environment, and only fills it in when nothing has, so a deployment can override the application's default without a code change. For a library that shells out to an external binary, that is the difference between a configuration mistake and an outage.

There is one more dependency you cannot escape, and the README says so plainly: to use pandoc filters, you must have the relevant filters installed on your machine. Pypandoc does not manage them.

Output files, and the formats that require them

Some conversions cannot be returned as a string, and the API handles that with an `outputfile` argument.

It is also possible to let pandoc write the output to a file directly, and the documentation is explicit that this is the only way to convert to some output formats, giving odt, docx, epub, epub3 and pdf as the examples. In that case `convert_*()` returns an empty string.

python
import pypandoc

output = pypandoc.convert_file('somefile.md', 'docx', outputfile="somefile.docx")
assert output == ""

The empty string return is the part that will surprise you if you do not read it, since a caller that checks the return value for truthiness will treat a successful conversion as a failure, and a caller that logs the return value will log nothing useful. The assertion in the example is the documentation doing its job.

The reason is in the tools themselves. A PDF is a binary format that a converter has to write to a file, and a docx or an epub is a zip container, and neither survives being passed back through a pipe as a Python string. The wrapper is honest about the boundary by handing the writing over to pandoc and returning nothing.

Note also the optional dependency in the packaging, a `tinytex` extra that pulls in `pytinytex`, which is the LaTeX distribution manager. Pandoc needs a PDF engine to produce a PDF, and that is what this extra is for, which is the dependency you will meet next if you try to generate PDFs.

The repository also has a `filter_test.md` file and a `test_data/` directory, and the development group pulls in the `pandocfilters` library.

Where this is the wrong choice

The first wrong case is a one off conversion in a script you control. If you are shelling out once, `subprocess.run` with a list of arguments is about the same amount of code, and it has one less dependency and one less version to track.

The second is an environment where you cannot install a binary. Pypandoc ultimately needs a pandoc executable on the machine, and the bundled binary package only covers Windows, macOS and 64 bit Intel Linux, with the Linux wheel build limited to the same architecture. A container image for ARM is not something the package provides.

The third is a document pipeline that needs the filters to be managed for you. The documentation is clear that filters must be installed on your machine, and the `pypandoc download` command fetches pandoc, not its filter ecosystem. If your pipeline depends on a specific set of filters, that is a separate installation problem you own.

The fourth is a rendering requirement that goes beyond pandoc. PDF output pulls in the LaTeX toolchain, and the `tinytex` extra exists because of it, so a project that only needs HTML or Markdown conversion should not install that extra and should not assume the PDF path is free.

The alternative approach is worth naming. Calling pandoc directly is the same program with one less layer, and it gives you exact control over the arguments. What pypandoc adds is the version resolution between an installed and a bundled binary, the `PYPANDOC_PANDOC` override, the download helper, the CLI, and a Python surface that returns strings and files in a predictable shape, and for a service that converts documents on request, those are worth having.

On the state of the project, the last push was on 2026-10-05, and the release list is a little unusual, with a `latest` tag labelled as the latest development version dated 2026-03-15, v1.13 from 2024-02-16 and v1.10 from 2022-10-28. The repository is not archived, and it carries a `release.md`, a `uv.lock`, a `.flake8` and a pre-commit configuration.

Editorial conclusion

Adopt pypandoc when your Python application has to produce documents in several formats, because it wraps the converter that already handles them and gives you a stable Python surface, an output file mode for the formats that need it, and a way to pin a specific pandoc binary. Do not adopt it for a single conversion in a script you control, since calling pandoc directly is a shorter path with one less dependency. Verify first that pandoc is available to the account that runs your application, since a web server's user often cannot see the pandoc on your PATH, that the filters you rely on are installed on the machine, since pypandoc does not install them, and that your Python version is inside the supported range, which the package declares as 3.7 and above.

Frequently asked questions

What is the difference between pypandoc and pypandoc_binary?

The two packages are identical except that one includes pandoc and the other does not. `pypandoc_binary` ships prebuilt wheels with pandoc included for Windows and macOS, and on Linux you can build such a wheel with `uv build --wheel binary/` if you have your own wheelhouse, which works only on 64 bit Intel systems.

How do I convert a file with pypandoc?

Use `pypandoc.convert_file('somefile.md', 'rst')`, which infers the input format from the filename and can be overridden with the `format` argument. `convert_text` does the same for a string but requires the input format to be given. Both return a unicode string.

How do I make pypandoc find pandoc on a server?

Set the `PYPANDOC_PANDOC` environment variable to the full path of the pandoc binary. If it is set, it is the only place pandoc is searched for, which solves the case where a web server's own user cannot see the pandoc on your PATH. At runtime the same can be done with `os.environ.setdefault('PYPANDOC_PANDOC', path)`.

Why does convert_file return an empty string when I use outputfile?

When pandoc writes the output to a file directly, the convert function returns an empty string. The documentation notes that writing to a file is the only way to convert to some output formats, such as odt, docx, epub, epub3 and pdf, so an empty return is the expected result rather than an error.

Do I need to install pandoc filters separately?

Yes. The README states that to use pandoc filters you must have the relevant filters installed on your machine, and pypandoc does not install them for you.

Official sources

  1. JessicaTegner/pypandoc on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jessicategner-pypandoc.svg)](https://hysenlabs.com/projects/jessicategner-pypandoc)

Was this useful?