Skip to main content

Ahrara for Python

Create an email address from a Python application or test, wait for messages, and extract verification codes or links. Requires Python 3.11+; install with pip install ahrara. It provides synchronous and asynchronous APIs, plus a Robot Framework library. The SDK connects directly to the relay without a running Ahrara CLI.

from ahrara import create_inbox

with create_inbox() as inbox:
    # Submit inbox.address in your application.
    email = inbox.wait_for_email()  # No implicit timeout.
from ahrara import create_inbox_async

async def verify_signup():
    async with await create_inbox_async() as inbox:
        # Submit inbox.address in your application.
        email = await inbox.wait_for_email()
        return email

Creation waits for authentication/subscription. Reception continues before the first wait. Matching receives are consumed once, while stored messages remain available until deleted. Use separate inboxes for parallel tests. Concurrent waits on one inbox are queued and run one at a time. Always close the inbox or use its context manager.

Waiting and continuous reception

wait_for_email(timeout=None, *, subject=None, from_address=None, filter=None, cancel=None) waits indefinitely by default. A positive finite timeout is in seconds and raises TimeoutError. subject and from_address are ASCII case-insensitive substrings. Other native failures expose AhraraError.code. The sync API also accepts a threading.Event as cancel; cancellation raises AhraraError with code cancelled. Async task cancellation raises asyncio.CancelledError. Both preserve the inbox for reuse.

for email in inbox.wait_for_emails(timeout=10):
    verify(email)
    if finished():
        break

inbox.receive_emails(verify, timeout=10)

Async inboxes expose the same stream and callback methods:

from contextlib import aclosing

async with aclosing(inbox.wait_for_emails(timeout=10)) as emails:
    async for email in emails:
        await verify(email)
        if finished():
            break

await inbox.receive_emails(verify, timeout=10)

A finite stream timeout is the total duration, including consumer/callback work, and ends iteration normally. Omit it for an indefinite subscription. Callbacks execute sequentially; async receivers await async callbacks and also accept synchronous ones. Callback errors propagate. Breaking, closing a generator, normal completion, and cancellation keep the inbox open. No messages are prefetched while the consumer handles the previous message. Cancel an active async anext() task before closing its generator; synchronous waits accept a cancellation event.

Cancelled creation waits for its bounded connection attempt, then closes any result. A concurrently committed result may be delivered once before a stream propagates cancellation.

Filters and extracted values

EmailFilter is an optional TypedDict accepted by creation and wait/search calls. At creation, filter discards rejected arrivals before storage. On individual waits, it leaves rejected messages available for a later wait. Every field is ANDed: sender, subject, to, cc, text, and headers={name: value} match case-insensitive substrings. recipient, envelope_sender, and message_id match the whole value, ignoring case. has_attachments selects presence or absence; header names ignore case. Per-wait criteria add to the global receipt filter.

Use filter={"regex": True, "subject": r"^(Verification|Confirm email)$"} to interpret all string conditions as case-insensitive Rust regex patterns; regex queries below are case-sensitive unless ignore_case=True. The subject/from_address wait arguments stay literal. Robot Framework accepts the same dictionary, for example &{filter}= Create Dictionary regex=${TRUE} subject=^Verify$. See the shared filter rules for anchoring and limits.

match = inbox.wait_for_regex_match(
    r"code=(?<code>[0-9]+)",
    filter={"sender": "example.test", "subject": "Verify"},
    ignore_case=True,
)
code = next(group["value"] for group in match["groups"] if group.get("name") == "code")

for match in inbox.wait_for_regex_matches(r"[0-9]+", timeout=10):
    verify_code(match["value"])

link = inbox.wait_for_xpath('//a[@id="verify"]/@href')
for value in inbox.wait_for_xpath_values("//a/@href", timeout=10):
    verify_link(value)

AsyncInbox provides the same singular methods with await and plural methods with async for. Regex uses shared Rust syntax, including named captures; lookaround/backreferences are unsupported. Options are source="text" (full decoded text), "html", "subject", or "raw", plus ignore_case, multi_line, and dot_matches_new_line. Results contain email_id, value, and groups; group zero is the full match and optional unmatched groups contain None. XPath evaluates the original HTML and returns strings.

Singular searches throw TimeoutError on finite expiry. Plural searches yield all matches/values across multiple emails and end normally at their total deadline, including consumer work. None/omitted timeout is indefinite. Invalid queries or filters raise AhraraError before consuming messages. Sync cancellation uses cancel=threading.Event(); async cancellation uses the task. Closing a generator preserves the inbox. Each matched email is consumed once, so stopping midway through its results does not replay unused values; stored mail remains readable.

Persistence and credentials

Pass profile_path='/absolute/profile' to retain identity, address, encrypted history, and consumed-receive state across reopen. Omit it for an isolated temporary profile. The same active profile cannot be opened twice.

credentials = inbox.export_credentials()  # Await this for AsyncInbox.
inbox.close()
with create_inbox(credentials=credentials) as restored:
    email = restored.wait_for_email(timeout=10)

Exported InboxCredentials(address, recovery_key) restores identity/address with fresh local history unless a persistent profile is supplied. Its repr is redacted; access recovery_key only for deliberate secure storage and never log it. Alternatively pass recovery_key with optional owned address; these options cannot be combined with credentials. An address alone needs an existing profile identity. Mismatched keys or unowned addresses fail.

Connection options are server_url, server_public_key, tls_ca_path, and connect_timeout_ms (1..300000 ms, default 20000). Omit them for the hosted relay, whose verified public key the package embeds. Custom CA files keep peer and hostname verification enabled.

Email CRUD and attachments

All operations are scoped to the inbox's address and have async counterparts. Reading stored messages does not consume receive state.

Method Result
list_emails(after_id=0, limit=20, subject=None, from_address=None) EmailPage metadata; limit 1..100
get_email(id) Full Email TypedDict with attachment metadata
delete_email(id) / clear_emails() Number of messages removed
get_attachment(id, attachment_id) AttachmentData containing metadata and bytes
save_attachment(id, attachment_id, absolute_path) Saved pathlib.Path; never overwrites; directories use a sanitized filename
get_raw_email(id) Original RFC 822 bytes
get_html(id, allow_remote=False) Sanitized HtmlEmail; remote resources blocked by default

Email fields follow the native schema, including from, to, subject, text_body, attachments, and parse_error. The core caps extracted text at 256 KiB and reports text_body_truncated. Message content is untrusted data.

pytest and Robot Framework

pytest can use the context manager in a yield fixture. Robot imports ahrara.robot.AhraraLibrary; its optional constructor arguments are server_url, server_public_key, tls_ca_path, and profile_path.

*** Settings ***
Library    ahrara.robot.AhraraLibrary
Test Teardown    Close Inbox

*** Test Cases ***
Verify Signup
    ${address}=    Create Inbox
    # Submit ${address} in the application here.
    ${email}=    Wait For Email    subject=Verify
    ${page}=    List Emails
    ${stored}=    Get Email    ${email}[id]

Robot's wait has no implicit timeout. The library also provides credentials, CRUD, attachment, raw-email, HTML, Wait For Regex Match/Matches, and Wait For XPath/Values keywords; always use Close Inbox as teardown.

Metadata

Release files for ahrara 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for ahrara 0.1.0
File
ahrara-0.1.0-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details
ahrara-0.1.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
ahrara-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
ahrara-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
ahrara-0.1.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
ahrara-0.1.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 40.2 MB

Release files / ahrara-0.1.0-py3-none-win_arm64.whl

Download URL ahrara-0.1.0-py3-none-win_arm64.whl
Size 6.4 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
96ca223a103a4ca2eb8b87307c6fdcc819ca31820deb1dccbe9c7e67afe13755
BLAKE2b-256 checksum
How to use checksums
7cd36e0c2d8d987b2788162351c8fb055e002225f4a26e487713dca89c5ead6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ahrara-0.1.0-py3-none-win_amd64.whl

Download URL ahrara-0.1.0-py3-none-win_amd64.whl
Size 7.1 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
27cbfcb963ab0469f8bbce8b4f97fd5d551f4c37c7774663fbdaaa84cc1d8120
BLAKE2b-256 checksum
How to use checksums
8a8067cb620f89f72a276ffcf16c176210a94fa9e0ad587089fca1ecdda301d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ahrara-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ahrara-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 5.2 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
7da9e907252f32c0559e5527060d44fa67bf261b7041fe88d94ebec9bca30a2f
BLAKE2b-256 checksum
How to use checksums
35cee15f3b0c7efbc3cd9a44306eed04dde7bfe64815e9b5fc461dafa5041005
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ahrara-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL ahrara-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 5.3 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
61865c3aa811d7bcc5d412a199ec18cf886fa4ca71a7649c81851af0c75ea4a6
BLAKE2b-256 checksum
How to use checksums
4e6df3ca1f1af816709c74f0a06550b8489740863d76b5e57511b6de675265e1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ahrara-0.1.0-py3-none-macosx_11_0_x86_64.whl

Download URL ahrara-0.1.0-py3-none-macosx_11_0_x86_64.whl
Size 7.9 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
6189f988282270136ecd0e2d074bacddb1e120f91c6ecf7f006736851ddbeb33
BLAKE2b-256 checksum
How to use checksums
6630905e11595d386e5822014ec4cf5068619c4aec1695bca7273fb20a89057d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ahrara-0.1.0-py3-none-macosx_11_0_arm64.whl

Download URL ahrara-0.1.0-py3-none-macosx_11_0_arm64.whl
Size 8.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
ef92c27bb2219ba0d680cb9b3badc9f98f4a24a21650fa716fa8eb61c3e26728
BLAKE2b-256 checksum
How to use checksums
769741aca1ea0e182cc3c02db4401816ec998cf843f55db004163d206cbd7129
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

6 release files

0.0.1

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page