Beautiful, Beautiful Python

Today I was working on a USA ePay payment module for Satchmo. I had access to some work done by another chap, but I wanted to get a better feel for what needed to be happening in the payment module, so I examined some of the stuff that other people had done as well. I studied the "official" PHP version of the payment module for a little while. Things seemed pretty self-explanatory, so I decided I might as well translate the PHP library into Python.

Most of the translation process was quite mundane... removing dollar signs here and there, getting rid of the dirty -> junk, etc. However, toward the end of the translation process, I started to actually enjoy myself. That's because I wasn't just defining variables left and right--I actually started doing some stuff for processing. Here's some of the PHP code:

<?php
$this->result=(isset($tmp["UMstatus"])?$tmp["UMstatus"]:"Error");
$this->resultcode=(isset($tmp["UMresult"])?$tmp["UMresult"]:"E");
$this->authcode=(isset($tmp["UMauthCode"])?$tmp["UMauthCode"]:"");
$this->refnum=(isset($tmp["UMrefNum"])?$tmp["UMrefNum"]:"");
$this->batch=(isset($tmp["UMbatch"])?$tmp["UMbatch"]:"");
$this->avs_result=(isset($tmp["UMavsResult"])?$tmp["UMavsResult"]:"");
$this->avs_result_code=(isset($tmp["UMavsResultCode"])?$tmp["UMavsResultCode"]:"");
$this->cvv2_result=(isset($tmp["UMcvv2Result"])?$tmp["UMcvv2Result"]:"");
$this->cvv2_result_code=(isset($tmp["UMcvv2ResultCode"])?$tmp["UMcvv2ResultCode"]:"");
$this->vpas_result_code=(isset($tmp["UMvpasResultCode"])?$tmp["UMvpasResultCode"]:"");
$this->convertedamount=(isset($tmp["UMconvertedAmount"])?$tmp["UMconvertedAmount"]:"");
$this->convertedamountcurrency=(isset($tmp["UMconvertedAmountCurrency"])?$tmp["UMconvertedAmountCurrency"]:"");
$this->conversionrate=(isset($tmp["UMconversionRate"])?$tmp["UMconversionRate"]:"");
$this->error=(isset($tmp["UMerror"])?$tmp["UMerror"]:"");
$this->errorcode=(isset($tmp["UMerrorcode"])?$tmp["UMerrorcode"]:"10132");
$this->custnum=(isset($tmp["UMcustnum"])?$tmp["UMcustnum"]:"");

$this->avs=(isset($tmp["UMavsResult"])?$tmp["UMavsResult"]:"");
$this->cvv2=(isset($tmp["UMcvv2Result"])?$tmp["UMcvv2Result"]:"");
?>

Seems fairly self-explanatory, right? Heh. Now let's look at the Python version of this:

self.result = res.get('UMstatus', 'Error')
self.resultcode = res.get('UMresult', 'E')
self.authcode = res.get('UMauthCode', '')
self.refnum = res.get('UMrefNum', '')
self.batch = res.get('UMbatch', '')
self.avs_result = res.get('UMavsResult', '')
self.avs_result_code = res.get('UMavsResultCode', '')
self.cvv2_result = res.get('UMcvv2Result', '')
self.cvv2_result_code = res.get('UMcvv2ResultCode', '')
self.vpas_result_code = res.get('UMvpasResultCode', '')
self.convertedamount = res.get('UMconvertedAmount', '')
self.convertedamountcurrency = res.get('UMconvertedAmountCurrency', '')
self.conversionrate = res.get('UMconversionRate', '')
self.error = res.get('UMerror', '')
self.errorcode = res.get('UMerrorcode', '10132')
self.custnum = res.get('UMcustnum', '')

self.avs = res.get('UMavsResult', '')
self.cvv2 = res.get('UMcvv2Result', '')

<sarcasm>Wow, yeah... PHP really does rock! It must be the coolness factor behind the ternary operator they use! The Python code is just horrible compared to the PHP code.

How about the part where you check to see if a credit card number fits the profile of a known credit card? The PHP:

<?php
switch(substr($ccnum,0,1))
{
    case 2: //enRoute - First four digits must be 2014 or 2149. Only valid length is 15 digits
        if((substr($ccnum,0,4) == "2014" || substr($ccnum,0,4) == "2149") && strlen($ccnum) == 15) return 20;
        break;
    case 3: //JCB - Um yuck, read the if statement below, and oh by the way 300 through 309 overlaps with diners club.  bummer.
        if((substr($ccnum,0,4) == "3088" || substr($ccnum,0,4) == "3096" || substr($ccnum,0,4) == "3112" || substr($ccnum,0,4) == "3158" || substr($ccnum,0,4) == "3337" ||
            (substr($ccnum,0,8) >= "35280000" ||substr($ccnum,0,8) <= "358999999")) && strlen($ccnum)==16)
        {
            return 28;
        } else {
            switch(substr($ccnum,1,1))
            {
                case 4:
                case 7: // American Express - First digit must be 3 and second digit 4 or 7. Only Valid length is 15
                    if(strlen($ccnum) == 15) return 3;
                    break;
                    case 0:
                case 6:
                case 8: //Diners Club/Carte Blanche - First digit must be 3 and second digit 0, 6 or 8. Only valid length is 14
                    if(strlen($ccnum) == 14) return 4;
                    break;
            }
        }
        break;
    case 4: // Visa - First digit must be a 4 and length must be either 13 or 16 digits.
        if(strlen($ccnum) == 13 || strlen($ccnum) == 16)
        {
             return 2;
        }
        break;

    case 5: // Mastercard - First digit must be a 5 and second digit must be int the range 1 to 5 inclusive. Only valid length is 16
        if((substr($ccnum,1,1) >=1 && substr($ccnum,1,1) <=5) && strlen($ccnum) == 16)
        {
            return 1;
        }
        break;
case 6: // Discover - First four digits must be 6011. Only valid length is 16 digits.
        if(substr($ccnum,0,4) == "6011" && strlen($ccnum) == 16) return 10;
}
?>

Very eloquent. Let's see how bad the Python looks on this one:

first = ccnum[0]
four = ccnum[0:4]
if first == '2' and len(ccnum) == 15 and four in ['2014', '2149']:
    # enRoute: first four digits must be 2014 or 2149. Only valid length
    # is 15 digits
    return 20
elif first == '3':
    # JCB
    if len(ccnum) == 16 and four in ['3088', '3096', '3112', '3158', '3337'] \
        or ccnum[0:8] in ['35280000', '35899999']:
        return 28
    else:
        if len(ccnum) == 15 and ccnum[1] in ['4', '7']:
            # American Express: first digit must be 3 and second must be
            # 4 or 7.  Only valid length is 15 digits
            return 3
        elif len(ccnum) == 14 and ccnum[1] in ['0', '6', '8']:
            # Diners Club/Carte Blanche: first digit must be 3 and second
            # digit must be 0, 6, or 8.  Only valid length is 14
            return 4
elif first == '4' and len(ccnum) in [13, 16]:
    # Visa: first digit must be 4 and length must be either 13 or 16
    return 2
elif first == '5' and len(ccnum) == 16:
    # Mastercard: first digit must be a 5 and second must be in the range
    # 1 to 5 inclusive.  Only valid length is 16
    if int(ccnum[1]) in range(1, 6):
        return 1
elif first == '6' and len(ccnum) == 16 and four == '6011':
    # Discover: first four digits must be 6011.  Only valid length is 16
    return 10

Eek gads!!! It's so obvious why PHP is the language of choice for so many people out there. Python doesn't even have a switch statement, for crying out loud! Inconceivable!</sarcasm>

I'm so glad I was able to escape the grips of PHP.

Comments

Comments powered by Disqus