CLI tool
martinrusev/imbox avatar
martinrusev/imbox

imbox runs on one dependency, documents twelve configuration variables its own example file disagrees with on five, and ships two pytest configurations

Python IMAP for Agentic Workflows

1,218 stars185 forksPythonMIT

At a glance

What is it?
imbox is a small Python library and command line tool for reading IMAP mailboxes and turning message content into machine readable data, repositioned in its documentation for agentic workflows. It was first tagged in 2018 and the manifest now sits one patch ahead of the newest tag. The interesting material is all in the packaging file: one runtime dependency, a development extra that lists the type checker twice, two competing pytest configurations pointing at a test directory that is not in the repository, and three tools pinned to a newer Python than the one the package claims to support.
Who is it for?
imbox is worth using if you want IMAP read access from Python with almost no dependency surface, and worth reading if you are maintaining a library that has been alive long enough to accumulate toolchain archaeology. The single dependency means an audit is cheap and an install is fast, which is the strongest argument for it.
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 104 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 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One runtime dependency, and it is a charset detector

The runtime dependency list has one entry: a charset detection library, with a floor version in the double digits. Everything else the package needs to speak IMAP, parse MIME multipart structures, decode attachments, and hand back Python objects comes from the standard library. For a library whose entire job is talking to a twenty-year-old protocol and parsing whatever malformed mail arrives, that is the most interesting fact in the repository. There is no HTTP client, no templating layer, no data class library, and no framework requirement, so installing it adds one transitive dependency and importing it adds one import. The package is MIT licensed, declares a Python floor of 3.11, and exposes one console script whose entry point is the command module inside the package, named the same as the package itself. The documentation describes it as dual purpose: a library you import, and a command line tool that fetches mail straight to a JSON file, which is the part of it aimed at agent workflows.

The example environment file and the configuration table name different variables

The configuration surface is documented as a table of twelve variables with defaults, and there is a separate example file in the repository root. They do not describe the same set. The table includes a logging output type variable with two named values, one of which strips the library's own metadata out of the output. The example file does not mention it at all. The example file includes an SSL context variable and an output fields variable, and neither appears in the table. And the two disagree about the file output switch: the table gives its default as off while the example file sets it to on. Five discrepancies, none of which the documentation acknowledges. The example file is also opinionated in a way that is easy to miss, since it ships with a placeholder address, a placeholder app password, and an output fields value restricted to two fields, which means anyone who copies it unchanged gets a run that writes almost nothing. That combination of a divergent example, a table that omits half the knobs, and a friendly placeholder makes this the section most likely to cost somebody an afternoon.

Two pytest configurations, pointing at a test directory the repository listing does not show

There are two places pytest configuration lives in this repository. One is a standalone configuration file at the root, and the other is a table inside the packaging manifest that sets the test paths to a directory named tests. Both are present. When both exist, the standalone file is the one pytest reads, which means the table in the manifest may be doing nothing at all. On top of that, the root listing of the repository does not contain a tests directory. It contains the library package, a version manager configuration file, the packaging manifest, the lock file, the example environment file, a pre-commit configuration, a changelog, a licence, and the readme. The type checker configuration also carries a per-module override block for anything under tests, relaxing the requirement that definitions be annotated, so the toolchain is configured in detail for a directory that is not part of the visible tree. Whether that directory is generated, ignored, or simply absent is not answerable from what is here, and it is the first thing to check before you try to run the suite.

The type checker appears twice in the development extra

The development extra lists the type checker twice, once with a floor of one minor series and again with a floor several minors later, in the same list. Package managers resolve that by taking the stricter constraint, so it is harmless at install time and misleading at review time, since it reads as an accident rather than a decision. The rest of the extra is a conventional Python development set: a build frontend, the test runner, the formatter, a recording plugin for tests, the linter, and the pre-commit runner. What is unconventional is the toolchain configuration around them. Two formatters are configured at the same line length, one legacy and one modern, and the modern one has both a general section and a separate format section with quote style, indent style, trailing comma behaviour, and line ending settings, which means the project pays for two formatters where one would do. The linter's ignore list reads like an archive of other people's complaints, skipping rules for mutable default arguments, unnecessary dictionary calls, lambda assignment, commented-out code, and trailing commas, each with a parenthetical note. A security linter is configured too, excluding the test directory and skipping the assert rule, which is a common and reasonable pairing.

Three tools target Python 3.12 while the package claims 3.11 and upwards

The requirements section of the documentation lists three Python versions as supported, three point eleven through three point thirteen. Every tool in the repository is configured for three point twelve. The formatter's target version is set to that release, the type checker's Python version is set to it, and the linter's target version is set to it. A type checker configured for a different version than the one you run is a real source of false confidence, because it will judge your code against standard library and typing features that may not exist in the interpreter you actually use, and the package's own floor is a version below the one its tools model. That is not a bug on its own; many libraries set tool targets higher than their floor for good reason. It does mean the documentation and the tooling disagree about which interpreter is the baseline, and that the oldest supported version is the one least exercised by the tooling. The lock file and the version manager configuration in the root suggest development happens against one pinned interpreter, so the matrix is likely tested somewhere other than in a contributor's checkout.

The version sequence skips two numbers and the manifest runs ahead of the newest tag

The release history tells a story that the manifest does not summarise. The oldest visible tag is version zero point nine point six, dated 2018. The next is zero point nine point nine, dated 2022, so two numbers are skipped in the four years between them. The newest tag is zero point ten, dated in the first quarter of 2026, and the packaging manifest says zero point ten point one. So the file describes a version that has not been tagged, and the sequence that led there skipped two releases without a changelog entry visible in the tag list. The default branch is named master rather than main, which is consistent with a project that started in 2018 and never changed. The last recorded push is dated 2026-06-23, which is roughly three months after the newest tag and shows that work continues on the branch without a release following it immediately. Anyone pinning this dependency by tag is pinning something older than the code they would get from a branch checkout, and anyone pinning by the packaging manifest is pinning a version that may not exist on any index.

Both worked examples stop in the middle of a statement

The two code examples in the documentation are both cut off, which is a pity because they are the two things a newcomer copies. The command line example is a sequence of filter flags, and it ends on a bare comment with no command under it, so the last filter shown is a date range and whatever filter came after it is not there. The library example ends inside a keyword argument, mid-word, on a call that has not been closed. What survives is still enough to read the design: the connection takes a host, a username, a password, a boolean for implicit TLS, a slot for a caller-supplied TLS context, and a boolean for opportunistic TLS, with a comment linking to the standard library's documentation for building that context. The two booleans are the interesting pair, because the defaults favour implicit TLS on the standard secure port and leave opportunistic TLS off, so a server that only offers the older upgrade path needs both flipped and the port changed as well. For a debugging recipe, the documented form is two environment variables set inline plus an unread filter, which is the pattern to copy when a fetch returns nothing:

bash
DEBUG=true LOG_LEVEL=DEBUG uv run imbox messages --unread

And the library example, which stops inside a keyword argument with the call unclosed:

python
from imbox import Imbox

# SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context

with Imbox('imap.gmail.com',
        username='username',
        password='password',
        ssl=True,
        ssl_context=None,
        starttls=False) as imbox:

    # Get all folders
    status, folders_with_additional_info = imbox.folders()

    # Gets all messages from the inbox
    all_inbox_messages = imbox.messages()

    # Unread messages
    unread_messages = imbox.messages(unread=True)

    # Flagged messages
    flagged_messages = imbox.messages(flagged=Tr

Editorial conclusion

imbox is worth using if you want IMAP read access from Python with almost no dependency surface, and worth reading if you are maintaining a library that has been alive long enough to accumulate toolchain archaeology. The single dependency means an audit is cheap and an install is fast, which is the strongest argument for it. Before you rely on it, check four things. Which configuration variables you actually set, because the shipped example file and the documented table name five different sets of variables and disagree on a sixth, so read the example rather than the table. Which Python you run, since the package claims 3.11 and upwards while the formatter, the type checker, and the linter are all configured for 3.12. Which pytest configuration wins on your machine, because there are two and the file outside the packaging manifest usually takes precedence. And how fresh your install is, since the newest tag is one version behind the packaging file and the previous tags are three and eight years old.

Frequently asked questions

What does the imbox Python library do?

It reads IMAP mailboxes and converts email content into machine readable data, both as an importable library and as a command line tool that fetches mail to a JSON file. It is MIT licensed, requires Python 3.11 or newer, and ships exactly one runtime dependency, a charset detection library.

How do I install and run the imbox CLI?

Install the package from the index, then run the command through the uv tool runner, for example uv run imbox messages or uv run imbox folders. The command line interface reads its configuration from environment variables rather than a configuration file, with a URL, username, password, an implicit TLS flag on by default, a port defaulting to the secure one, and an opportunistic TLS flag off by default.

Which configuration variables does imbox support?

The documentation table lists twelve, covering the server address, credentials, the two TLS switches, a port, debug and logging settings, a metadata-inclusion switch, and file output with a folder and filename. The example environment file shipped in the repository names two variables the table omits and omits one the table lists, and the two disagree about whether file output is on.

Which imbox version should I install?

Check the index rather than the repository. The packaging file declares a version one patch ahead of the newest published tag, and the tag history skips two numbers between a 2018 release and a 2022 one. The default branch is named master and its last recorded push is dated 2026-06-23, so branch checkouts run ahead of any release.

How is the imbox test suite configured?

In two places at once, which is the problem. There is a standalone pytest configuration file at the repository root and a table inside the packaging manifest that sets the test paths, and the standalone file usually wins. The repository listing does not show a tests directory, though the type checker configuration carries a relaxed override block for one, and the development extra includes a recording plugin so tests can replay captured sessions.

Does imbox need many dependencies?

No. The runtime dependency list has one entry, a charset detection library with a version floor. Everything else, IMAP itself, MIME parsing, attachment handling, and the JSON output, comes from the standard library, and the development extra carries the test, lint, format, type check, build, and pre-commit tooling instead.

Official sources

  1. Issues
  2. License: MIT
  3. martinrusev/imbox on GitHub
  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/martinrusev-imbox.svg)](https://hysenlabs.com/projects/martinrusev-imbox)