Program for sending emails Disclaimer: This question is very much like the one posted here. I gathered some opinions about my options from the answer there. Here, I just want validation about the choices I'm deciding to stick to and see what people think about the decisions specifically. Also, gather suggestions about other parts of the code I'm not specifically asking about, since I'm new to Python OOP.

Scenario: I'm writing a program that will send emails. For an email, the to, from, text and subject fields will be required and other fields like cc and bcc will be optional. Also, there will be a bunch of classes that will implement the core mail functionality, so they will derive from a base class (Mailer).

Following is my incomplete code snippet:

class Mailer(object):
    __metaclass__ == abc.ABCMeta

    def __init__(self,key):
        self.key = key

    @abc.abstractmethod
    def send_email(self, mailReq):
        pass


class MailGunMailer(Mailer):
    def __init__(self,key):
        super(MailGunMailer, self).__init__(key)

    def send_email(self, mailReq):
        from = mailReq.from
        to = mailReq.to
        subject= mailReq.subject
        text = mailReq.text
        options = getattr(mailReq,'options',None)
        if(options != None):
            if MailRequestOptions.BCC in options:
                #use this property
                pass
            if MailRequestOptions.CC in options:
                #use this property
                pass



class MailRequest():
    def __init__(self,from,to,subject,text):
        self.from = from
        self.to = to
        self.subject = subject
        self.text = text

    def set_options(self,options):
        self.options = options

class MailRequestOptions():
    BCC = "bcc"
    CC = "cc"


I've made the following decisions about code designs. What do you think about them?


The send_email() method will take four required parameters - to, from, subject and text, and bunch of other optional parameters like cc, bcc etc. So I decided to create the MailRequest wrapper, which will take the 4 required fields in the constructor, and the other optional parameters will go in the options dict.

Is this an acceptable way of doing this?  Why not use **kwargs?

Because if you define something like:

def foo(**kwargs):
    pass


Then to call it, you can't do something like:

options = []
options["one"] = 1
options["two"] = 2
foo(options)


Right?

You'd have to do something like:

foo(one=1,two=2)


Now if I have 15 parameters that foo accepts, then **kwargs isn't a good way to do this right?
I've created the MailRequestOptions class to contain static strings. The reason behind the existence of this class is, even if the user knows he has to pass some options in the options dict of the MailRequest object, how would he know which options can he set. This class could probably help the user know about what options can be set. This will also be helpful if the user has auto complete in an IDE or something. Do you think I'm thinking right? Or is this a somewhat unusual way of doing things?
Because if you define something like:

def foo(**kwargs):
    pass

  
  Then to call it, you can't do something like:

options = []
options["one"] = 1
options["two"] = 2
foo(options)

  
  Right?


The aswer is Yes. But there are ways,  

**name is used in two ways.  

To define optional keyword argumnets in function definiton.  

def f(**kwargs): pass. Here the argument of function f, kwargs is optional. You can legitimately call f like f() i.e without arguments. It's utility is you can pass unlimited number of keyword arguments like one=1,...  

To unpack a dict to pass arguments at function invocation.  

If a function is defined with some arguments whether it's required or optional, its arguments can be passed by **dict when invoking the function. f.e  

def f(a,b=2): pass


This function has two arguments one is required. To call it with one argument, you can do,  

arg = dict(a=111)
f(**arg)


With two argumnets,  

arg = dict(a=111,b=222)
f(**arg)


Combining the two **name form.  

def f(**kwargs): pass  


can be called as follows,  

f()
f(a=1,b=2,c=3)
arg = dict(foo=1,bar=2)
f(**arg)


The send_email method can be simplified.  

def send_email(self, from, to, sub, body, **options):
    ''' 
        This method sends an email by taking four req. args
        from, to, sub, body and optional 'cc' and 'bcc' keyword args
    ''' 
    from = from
    to = to
    subject= sub
    text = body
    cc = options.get('cc')
    if cc is not None:
            #use this property
    bcc = options.get('bcc')
    if bcc is not None:
            #use this property


The 'docstring' can be checked by calling,  

help('classname or obj'.send_email)
'classname or obj'.send_email.__doc__


and can be called as follows,  

obj.send_email('me@example.com', 'you@example.com', 'hi', 'howdy', cc='he@example.com')  


or  

mail1 = dict(from='she@example.com', to='he@example.com', sub='date', body='free tonight?', bcc='you@example.com')
obj.send_email(**mail1)