Skip to main content

Manokit

Table of contents

General information

Manokit is a simple, fully native library for sending emails. It provides an easy-to-use API for creating and sending emails, as well as offers a very customizable structure.

Manokit provides 2 modules: the base module name manokit, where the Email base class is defined, and a module called manokit.exceptions where custom exceptions, used by manokit, are defined.

Installation

Manokit can be installed from the PyPI using the following command:

pip install manokit

You can also download the package from the releases section, or assemble it from source.

V1 vs. V2

Manokit has reached a point of version separation. Manokit v2's API has been overhauled to provide a more consistent and predictable experience.

These changes include:

  • All functionality has been moved to a single class, instead of being scattered across 3 classes.
  • Explicit type checking was removed
  • Complete overhaul of properties and their behaviours

See additional info in our Changelog.

All these changes and updates are aimed at bringing a more pleasant experience to the end user, but they break compatibility. If you are currently using manokit and want to upgrade, make sure to update your codebase accordingly.

Structure

This section describes the parts of Manokit, and how to use them.

Email class

The Email class is where all the functionality resides.

Initialization

To begin working with Manokit, simply import the Email class and create an instance.

from manokit import Email

email = Email(smtp_host="smtp.gmail.com", smtp_port=587)

During initialization you can also adjust the attachment size limit (see attachment size limit for details), by specifying the new limit in bytes, like this:

from manokit import Email

small_email = Email("smtp.gmail.com", 587, filesize_limit=100)

In the example above we have reduced the attachment size limit from 25 MB to 100 bytes.

Authentication

In order to send an email, the client must first authenticate themselves with their SMTP server. Using Manokit, the authentication process looks like this:

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")

This example passes the credentials as plain text for the sake of illustration. Please use environment variables/other secure ways of storing credentials when logging in!

As of May 30, 2022, Google introduced some changes to their API that now require users to have an App Password. Manokit supports logging in with your App Password. Just pass your App Password instead of your regular password, like this:

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="ccfv jels tttm hshe")

By default, Manokit uses STARTTLS to encrypt the communication and make it more secure. If you would like to use SSL instead, simply disable STARTTLS during authentication and updating the port.

from manokit import Email

email = Email("smtp.gmail.com", 465)
email.login(
    username="manokit@gmail.com", 
    password="manokit_is_cool", 
    use_starttls=False,
)

Manokit does not support unencrypted communication over SMTP port 25.

After you have finished with Manokit, you need to call logout() function to close the SMTP session.

from manokit import Email

email = Email("smtp.gmail.com", 465)
email.login(
    username="manokit@gmail.com", 
    password="manokit_is_cool", 
    use_starttls=False,
)

# Your email stuff

email.logout()

Adding recipients

To add an email address to the list of recipients, simply call the appropriate function:

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("buddy@examplecorp.com")

email.logout()

For now, you can add recipients one at a time, so for each one you need to call the add_recipient function separately.

If you want to CC a person instead, replace the add_recipient function with add_cc:

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("boss@examplecorp.com") # This is important
email.add_cc("buddy@examplecorp.com")

email.logout()

However, make sure to add at least one recipient, or else the email will not be sent.

Same thing for BCC:

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("boss@examplecorp.com")
email.add_bcc("spy@rivalcorp.com")

email.logout()

It is important to know that if you try to add an email address to either recipients, CC, or BCC when it already is added to one of them, these function will have no effect.

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("buddy@examplecorp.com")
email.add_cc("buddy@examplecorp.com")

print(len(email.rec)) # Output: 1
print(len(email.cc)) # Output: 0

email.logout()

Composing an email

A simple email consists of a subject and a body. Both of these things are set by Manokit's set_subject and set_body functions, respectively.

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("buddy@examplecorp.com")

email.set_subject("Manokit is kinda cool!")
email.set_body("Hey, you heard about that library called Manokit? I tried it and it is nice ngl. Give it a try!")

email.logout()

The use of these functions is not compulsory, however. Manokit defaults the subject and the body to <no subject> and <no body>, respectively.

Manokit encodes the body of an email as text/html, rather than text/plain. This allows you to use HTML markup for styling and emphasis.

Custom Email Validation

By default, Manokit will validate user emails in certain cases (e.g. adding recipients, logging in, adding addresses to CC and BCC, etc) using a general-purpose regular expression.

Specifically, the regular expression used is this one: ^[-_+.\d\w]+@[-_+\d\w]+(?:\.{1}[\w]+)+$

However, if you find this validation mechanism unsuitable for their needs, like if you need to limit the domain that can be used, you can easily override it by providing your own validation logic.

The validator must be callable that accepts a single parameter of type str and returns a value of type bool. Validator type: Callable[[str], bool]

from manokit import Email

def better_email_validator(addr: str) -> bool:
    return addr.endswith("@our_company.com")

email = Email("smtp.gmail.com", 587)
email.login(username="you@our_company.com", password="manokit_is_cool")
email.set_custom_email_validator(better_email_validator)
email.add_recipient("buddy@our_company.com") # This will pass
email.add_bcc("spy@rival_corp.com") # This will raise an exception

Manokit also implements validation scopes. That means that, if you want to apply your custom validator only to certain parts, e.g. only to people who are BCC'd into the email, you can do it.

from manokit import Email

def check_who_is_in_bcc(addr: str) -> bool:
    return addr.endswith("@our_company.com")

email = Email("smtp.gmail.com", 587)
email.login(username="you@our_company.com", password="manokit_is_cool")
email.set_custom_email_validator(check_who_is_in_bcc, scopes=["bcc"])
email.add_recipient("client@clientperonal.org") # This will pass
email.add_cc("clientswife@gmail.com") # This will also pass
email.add_bcc("spy@rival_corp.com") # This will raise an exception

Available scopes:

  • all -- apply your validation rules to all addresses. This is the default option used by Manokit
  • author -- apply your validation rules only to the sender's email. This validator will be applied during login(), so the setter needs to be called before that
  • recipients -- apply your validation rules only to recipients' addresses
  • cc -- apply your validation rules only to addresses that will be CC'd into the email
  • bcc -- apply your validation rules only to addresses that will be BCC'd into the email

Adding attachments

Sometimes we need to send an email with a file attached to it. To attach the file to your email, simply call the add_attachment function and provide a path to the file.

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("boss@examplecorp.com")
email.add_attachment("./reports/quarterly_q3_q4.pdf")

email.logout()

Just as with add_recipient, add_bcc and add_cc, the add_attachment function adds one file at a time.

If an attachment has already been added, or if the size of the file being attach is 0, the function will have no effect.

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("boss@examplecorp.com")
email.add_attachment("./reports/empty_report.pdf")

print(len(email.attachments)) # Output: 0

email.logout()

Attachment size limit

By default, Manokit limits the combined size of the attachments to 25 MB (can be adjusted during the Initialization).

Manokit also maintains an internal counter of how much space is available for future attachments.

from manokit import Email

email = Email("smtp.gmail.com", 587, filesize_limit=100)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("boss@examplecorp.com")
email.add_attachment("./reports/40kb_report.txt")

print(email.available_filesize) # Output: 60

email.logout()

If the attachment's size exceeds the limit, an AttachmentError will be raised.

Sending an email

To send an email, just call send()

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(username="manokit@gmail.com", password="manokit_is_cool")
email.add_recipient("buddy@examplecorp.com")
email.add_cc("buddy@examplecorp.com")

email.send()

email.logout()

This function can raise an EmailError if no recipients are defined or there was a problem while sending an email.

Method chaining

Manokit's functions support method chaining

from manokit import Email

email = Email("smtp.gmail.com", 587)
email.login(
    username="manokit@gmail.com", 
    password="manokit_is_cool",
    ).add_recipient("buddy@examplecorp.com")
    .add_cc("buddy@examplecorp.com")
    .send()
    .logout()

The logout() function, however, is considered a logical endpoint, and thus does not support method chaining. In other words, the logout() function must be the last to be called.

Exceptions

NotAValidEmailAddressError

This exception is raised when the email address fails validation.

Functions that can raise it:

  • login()
  • add_recipient()
  • add_cc()
  • add_bcc()

EmailError

This exception is raised when there is a problem with the email itself. For now this exception is only raised by the send() function if there is no recipients or if there was a problem with sending an email

AttachmentError

This exception is raised when there is a problem with email's attachments. for now this exception is only raised by the add_attachment() function if the path provided does not point to a file or the attachment's size is larger that the available space.

Changelog

See the full changelog here.

Release files for manokit 2.1.0

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

Source distribution (sdist)

Source distribution for manokit 2.1.0
File Size Uploaded
manokit-2.1.0.tar.gz 47.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for manokit 2.1.0
File Interpreter ABI Platform
manokit-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 80.6 kB

Release files / manokit-2.1.0.tar.gz

Download URL manokit-2.1.0.tar.gz
Size 47.8 kB
Tags Source
SHA-256 checksum
How to use checksums
35a89af4f1c2a05783ea648ee3c5524f6fd730aeabdf68f9a1ebd0aec34e0993
BLAKE2b-256 checksum
How to use checksums
08e2c03dfe9502677ecce9582611e79008f7e32fd8dce1a77eb1b35690377910
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.4

Release files / manokit-2.1.0-py3-none-any.whl

Download URL manokit-2.1.0-py3-none-any.whl
Size 32.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
75b59ce918aa48a7e3f026220b47007d68203e86420568367043f3e072c4be2e
BLAKE2b-256 checksum
How to use checksums
8da95804036ceb8e345b92cc61a3f896d8bc16bebd6bcee71b6d9ffc393e56eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.4

Release history Release notifications | RSS feed

This release

2.1.0 This release

2 release files

2.0.0

2 release files

1.2.1

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 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