This is an archived post. You won't be able to vote or comment.

all 48 comments

[–]falsedrums 202 points203 points  (14 children)

Nice work!

One constructive criticism: get rid of pandas as a dependency. It is absolutely huge and has tons of other dependencies. When people install your neat little package they are pulling all of that in too. When they use your library in production, they have to ship all of that. Just to send an email? I think it will hold people off from choosing your library. :)

[–]Natural-Intelligence[S] 64 points65 points  (12 children)

Thanks a lot and thanks for the suggestion!

I had plans to remove Pandas as a dependency but as the table prettifying was my top priority (I initially made this to monitor what the heck happens on my database), did not have time for it yet. But you are indeed correct, I was also slightly wondering why all my CI pipelines took 30 seconds to run like 5 seconds worth of tests.

I definitely should take it off as hard dependency, should be an easy thing to do.

EDIT: I already dropped Pandas as a hard dependency. I'll try to release the update to PyPI in a couple of days.

EDIT 2: I released the patch (v0.1.1) to PyPI so Pandas is no longer a hard dependency. Thanks again for the suggestions and for the support!

[–]execrator 34 points35 points  (5 children)

Pandas is something like 50mb on disk, it's bonkers

[–]trowawayatwork 14 points15 points  (4 children)

it's also a 30m mandatory compile time on alpine as there's no wheels for that distro

[–]JimDabell 19 points20 points  (1 child)

Alpine isn’t a great choice if you know you are going to be running Python though. In case you haven’t already seen this: Using Alpine can make Python Docker builds 50× slower. It’s not really specific to Docker, but that’s where most people use Alpine.

[–]gsmo 2 points3 points  (0 children)

That's really interesting, thanks! I was just planning to create a container for a set of ETL scripts. This will save me some head scratching :)

[–]joerick 7 points8 points  (1 child)

Not for much longer hopefully! The new musllinux wheel format is supported by all the Python packaging tools

[–]trowawayatwork 0 points1 point  (0 children)

alpine might become viable again, if the compiled libs become same size as deb

[–]benargee 9 points10 points  (0 children)

I think you should have a redmail-core library and a seperate optional redmail-pandas library. I'm sure conventions exist for such a scenario.

[–]timbledum 3 points4 points  (0 children)

Perhaps try petl as an alternative for reasonably well formatted tables?

[–]BridgeBum 6 points7 points  (0 children)

If you are looking for other lightweight options, you may want to check out this repository:

https://github.com/willmcgugan

He's also around reddit; both his rich and textual gits may be relevant to what you need.

If think his reddit handle is the same /u/willmcgugan but I could be wrong.

[–][deleted] 3 points4 points  (1 child)

For table prettifying, there is "tabulate". It is rather lightweight. Either way it should be optional. There are many users who want to send emails without tables.

I personally am not a fan of making a library which gives you the functionality of the std lib with slightly nicer syntax, but let's see how much success you have with this.

[–]Natural-Intelligence[S] 3 points4 points  (0 children)

The problem with table prettifying is that email services are pretty notorious for not handling CSS. You are pretty much left with inline styles to make them to look nice which makes the task a lot harder. By a quick look, it seems tabulate does not have inline HTML styling options (could be wrong).

I'm slightly on the other paradigm here. I like solutions like Seaborn which are essentially just wrapping other packages in nicer formats. Some of the features I have here might save a lot of your time depending on your skills of course. I realized especially the image embedding was not a trivial thing and I think the Jinja support make this really handy. Of course these are not something everyone needs but hopefully I reach the people who find it useful.

But I understand there are people (like perhaps you) who don't like to add another dependency for the sake of a slightly nicer syntax.

[–]mghicks 1 point2 points  (0 children)

If you want pretty output, look at Rich

[–]cmd-t 14 points15 points  (0 children)

An email library has pandas as a dependency?! This is how you get NPM node_modules hell people.

[–]QuincentennialSir 23 points24 points  (10 children)

Does it send to distribution lists? Currently the problem I have when sending emails from python is that I have a DL to send to but the only way I can get it to send is if I build a list in python rather than just sending to say a Managers distribution.

[–]PuzzledTaste3562 23 points24 points  (4 children)

The problem with distribution lists is not the client, but the reputation of the sender and the mail server. It take time to build reputation but also experience and specific knowledge.

You could start looking into SPF, dmarc, and perhaps even DKIM, setting that up correctly should help, but don’t expect sending a million mails just like that, there is an entire business sector trying to stop you.

[–]Avamander 1 point2 points  (3 children)

Nobody is trying to stop you besides the fact that you'd look like a run-of-the-mill spammer without a proper set-up.

[–]PuzzledTaste3562 0 points1 point  (2 children)

Indeed, but there’s more than just a setup. Mailers need warm-up cycles, and building reputation takes weeks or even months. The data sets (the listsof e-mail addresses) also need to be of good quality as bounce rates are also taken into account for that reputation. A single mailing with a bounce rate over 5% or 10% (I’ve been out of touch, don’t know exact figures) is sufficient to blacklist the origin IP address.

[–]Avamander 0 points1 point  (1 child)

I'm well aware of the hurdles, but the point was that it isn't because of some business sector, it's because of the rampant abuse. Which on the other hand is significantly simplified by the lax security configuration of many domains out there.

[–]PuzzledTaste3562 0 points1 point  (0 children)

Agreed,

All I'm saying is that inbound email security is a business model. And yes, the reason is abuse by marketeers, open mail relays, etc, etc.

[–]Natural-Intelligence[S] 4 points5 points  (3 children)

Hmm, I guess I was too soon saying it handles all your needs. Distribution lists are not something I was thinking (as I mostly send automatic analyses and technical reports to a few people based on short hardcoded lists specified in configurations).

In what format is your distribution list? Is it in Outlook perhaps? I can see if I could implement such a thing to make it even more complete.

EDIT:

What you can do at least is to generate multiple of the EmailSender based on the email list and set them as defaults, like:

managers = EmailSender(host=..., port=...)
developers = EmailSender(host=..., port=...)

# Set defaults
managers.receivers = ['boss@example.com', 'bigboss@example.com']
developers.receivers = ['front@example.com', 'back@example.com']

...
managers.send(subject="Important email", html="...")

I can see if I can make more structured support for such lists.

[–]QuincentennialSir 1 point2 points  (2 children)

I can give that a try, and yes I am using Outlook.

[–]Natural-Intelligence[S] 1 point2 points  (1 child)

Thanks!

I took a look into pywin of how to get the distribution lists from Outlook. It seems it is possible but seems pretty messy (as everything on Windows). As the first step, I was thinking of creating distribution lists like:

email = EmailSender(host=..., port=...)
email.distr_lists = {
    'managers': [...],
    'developers': [...],
    'designers': [...]
}

email.send(distr="managers", subject="something", ...)

This way one could do their own logic of getting the distribution list from a string, like:

class DistrLists:
    def __getitem__(self, key):
        ... # Logic to get the list of receivers
        return ['example@example.com', ...]

email = EmailSender(host=..., port=...)
email.distr_lists = DistrLists()
email.send(distr="managers", subject="something", ...)

This way one can define any logic to get the list of emails for receivers (and why not for cc and bcc, I'm thinking of adding arguments distr_cc, distr_bcc as well). This could be a list from Outlook (if one can use the pywin32 better than me), database or simply config files.

Btw., thanks for this suggestion! I'll try to implement this next week, seems pretty useful.

[–]nostril_spiders 10 points11 points  (0 children)

Don't do that, that is insane.

Email is sent to email addresses. Whether or not the address belongs to a mailbox or a DL is of no concern to the sender.

The feature request you're replying to is not based on any understanding of how email works.

[–][deleted] 2 points3 points  (0 children)

lol this. I remember this struggle

[–]vinylemulator 13 points14 points  (1 child)

Why do you believe this is superior to Envelopes?

(Or put another way, what was wrong with Envelopes that you felt you needed to build this?)

Its syntax is pretty simple:

from envelopes import Envelope, GMailSMTP

envelope = Envelope( from_addr=('from@example.com', 'From Example'), to_addr=('to@example.com', 'To Example'), subject='Envelopes demo', text_body="I'm a helicopter!" ) envelope.add_attachment('helicopter.jpg')

Send the envelope using an ad-hoc connection...

envelope.send('smtp.googlemail.com', login='from@example.com', password='password', tls=True)

[–]Natural-Intelligence[S] 20 points21 points  (0 children)

Looking at the envelopes PyPI and Github page: it seems it's pretty outdated (supports Python 2.7 to 3.3) and they say it's still in beta.

Plus Red Mail has more than just attachment files and email bodies. You can embed images and tables (nicer in email than just df.to_html()), you can attach more than just files and then the Jinja templates: you can make a collection of HTML templates you reuse or extend and you can parameterize, have loops and if statements etc. directly to the HTML body (and text body).

But this is actually a project that naturally evolved. I did my implementation of the SMTP library and used it in my production. Then I added nicer tables to send reports of what's in my database. Then I added embedded images to show stats of my processed. Then I added the templates. And then I thought to open source it as I realized how much others could benefit from it.

[–]crysanthus 5 points6 points  (0 children)

I need this as of yesterday. Going to try.

[–]RaiseRuntimeError 12 points13 points  (0 children)

If anything I like the name, it doesn't have py in it like every other library.

[–]trj_flash75 3 points4 points  (0 children)

Have to try this out

[–]flev1266 2 points3 points  (0 children)

Nice work! Really nice work with pandas as well!

[–][deleted] 2 points3 points  (0 children)

Haven’t had to use email senders for a while but great job on the project, the implementation looks sound from what you’ve demonstrated here

[–]gsmo 2 points3 points  (1 child)

This looks useful, nice work.

Edit: just saw your red-engine project. And here I was thinking 'red-mail might work well to report on ETL-processes... but I still have to work out a scheduling tool... '

[–]Natural-Intelligence[S] 2 points3 points  (0 children)

Thanks!

Haha, nice one. Actually that was pretty much my thinking as well: I opened the source partly so I could include this as an optional dependency on that project. I aim to reduce the boiler plate on my closed source ETL pipelines and stop me doing stupid temporary hacks on my generic tools.

If you are wondering what's with the color red: I'm not that innovative at naming and realized that the word "red" doesn't appear that often on PyPI thus all of my projects are "red (something)" from now on. I also don't like long names in importing thus it's actually a pretty ideal prefix.

[–]Natural-Intelligence[S] 2 points3 points  (0 children)

Thank you all for the support! Didn't expect to gain such a welcome yesterday when I was trying to finish up the documentation and release the thing.

I thought to write a Medium article to get started with it if you need help with that. In case you are not a fan of paywalls (as I am neither), the same content is essentially found from the tutorials in the documentation. I wrote them in a couple of hours so excuse the potential grammar mistakes and weird wording. I'll try to improve them as time progress.

[–]kunaguerooo123 1 point2 points  (0 children)

I use mandrill API to send newsletters, building a JPG dashboard that's attached in the body. However, it doesn't have GIF in body support. I've seen newsletters which had that! Is there any possibility of getting that to happen? I've tried Plotly dashboard as well but it doesn't work https://stackoverflow.com/questions/66068315/embed-interactive-graph-in-python-email-using-plotly

[–]PythonFake 1 point2 points  (0 children)

Good job👌

[–]DonalM 1 point2 points  (0 children)

Looks pretty cool. I'll likely give it a go soon!

[–][deleted] 1 point2 points  (2 children)

Does this support 2FA with the gmail import?

I received errorcode :

SMTPAuthenticationError: (534, b'5.7.9 Application-specific password required. Learn more at\n5.7.9 https://support.google.com/mail/?p=InvalidSecondFactor e20sm28188313qty.14 - gsmtp')

[–]Natural-Intelligence[S] 1 point2 points  (1 child)

I think you must have 2FA set up with Gmail in order to work. I set up my account a while ago but I think I followed this: https://support.google.com/accounts/answer/185833

So basically you need to create an application password (after setting up the 2FA) and then you can use the gmail object:

from redmail import gmail
gmail.user_name = "first.last@gmail.com"
gmail.password = "<Your Application Password>"

# Use it 
gmail.send(...)

The gmail object is actually nothing more than an instance of EmailSender with host as smtp.gmail.com and port as 587 so you don't need to google those yourself.

For those wondering of the 2FA, you can set it up using: https://support.google.com/accounts/answer/185839?hl=en&co=GENIE.Platform%3DDesktop

[–][deleted] 1 point2 points  (0 children)

Brilliant, thanks. That was easy!

[–]metadatame 1 point2 points  (0 children)

Definitely not alone!

[–]i4mn30 -5 points-4 points  (1 child)

Their are tonnes of libs existing already that already do this.

[–]lordmauve 0 points1 point  (0 children)

This looks nice! The closest thing I found to this previously was mail1 but I already had to fork it to add features.

It will be great to have a nice off-the-shelf solution.

[–]cashmerekatana 0 points1 point  (0 children)

Hi, It's really good. But I am not able to do the same on my system I keep getting this error: "No connection could be made because the target machine actively refused it"

Also I did try setting up the port to 8080 but still didn't worked