3

I'm trying to write an R package with a function to send emails using python's smtplib module. The function works when I source it directly or with devtools::load_all(), but crashes R when I library my package and call the function.

I was using the mailR package, which uses java to send emails but I'm having issues after upgrading the version of java. I decided to write a quick package to replace it with a python version. Everything worked as expected until I installed it with devtools::install_local() and then tried calling the function. I've not got much experience with python, I feel like it might have something to do with how I'm importing the python modules, I tried moving the imports inside the function but that didn't help.

This is the python function that I'm sourcing with reticulate:

import smtplib  
import re
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.utils import COMMASPACE, formatdate


def py_sendmail(send_from, send_to, subject, 
                text_body = None, html_body = None, 
                inline = False, authenticate = False, ssl = False,
                smtp_server = None, port = 587, username = None,
                password = None, send = True, attach_files = None,
                debug = False):

  img_content = []

  msg = MIMEMultipart('related')
  msg['From'] = send_from
  msg['To'] = send_to
  msg['Date'] = formatdate(localtime=True)
  msg['Subject'] = subject

  if inline == True:

    img_re = re.compile("<img(?:.+?)src\s*=\s*(?P<quote>['\"])(?P<imgurl>.*?)(?P=quote)")
    imgs = img_re.findall(html_body)
    img_cnt = 1

    for(q, imgurl) in imgs:
      fp = open(imgurl, 'rb')
      msg_img = MIMEImage(fp.read())
      fp.close()
      img_id = "image" + str(img_cnt) 
      msg_img.add_header("Content-ID", "<"+img_id+">")
      img_content.append(msg_img)
      html_body = re.sub(imgurl, "cid:"+img_id, html_body)
      img_cnt = img_cnt + 1

  if text_body is not None and html_body is not None:
    msgAlt = MIMEMultipart('alternative')
    msg.attach(msgAlt)
    msgAlt.attach(MIMEText(text_body, 'plain'))
    msgAlt.attach(MIMEText(html_body, 'html'))

  elif text_body is not None:
    msg.attach(MIMEText(text_body, 'plain'))

  elif html_body is not None:
    msg.attach(MIMEText(html_body, 'html'))

  if attach_files is not None:

    for f in attach_files or []:
      with open(f, 'rb') as fil:
        part = MIMEApplication(
          fil.read(),
          Name=basename(f)
        )
        part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
        msg.attach(part)

  if len(img_content) > 0:
    for img in img_content:
      msg.attach(img)

  if send == True:

    if ssl == True:
      server = smtplib.SMTP_SSL(smtp_server, port)
    else:
      server = smtplib.SMTP(smtp_server, port)

    if authenticate == True:
      server.login(username, password)

    x = server.sendmail(send_from, send_to, msg.as_string())
    server.quit()
    return x == {}

  else:
    return msg.as_string()

It should send an email and it does when sourced into the global environment or when loaded with devtools::load_all(). But if I install it with devtools::install_local() and library it, it crashes R. When I run it in RStudio it crashes without explanation, when I run it from the command line i get this:

> library(pymailr)
> pymailr:::py_sendmail("<from_address>", "<to_address>", "Test",
+             text_body = "Testing", html_body = "<h1>Testing</h1>",
+             inline = FALSE, authenticate = TRUE, ssl = TRUE,
+             smtp_server = "<smtp_server>", port = 465, username = "<user_name>",
+             password = "<password>", send = TRUE)

 *** caught segfault ***
address (nil), cause 'memory not mapped'

Traceback:
 1: py_call_impl(callable, dots$args, dots$keywords)
 2: pymailr:::py_sendmail("<from_address>", "<to_address>", "Test",     text_body = "Testing", html_body = "<h1>Testing</h1>", inline = FALSE,     authenticate = TRUE, ssl = TRUE, smtp_server = "<smtp_server>",     port = 465, username = "<username>", password = "<password>",     send = TRUE)

0 Answers0