<commit_before>#!/usr/bin/python2.5

'''A minimalist Python interface for the Recurly API'''

__author__ = '<NAME> <<EMAIL>>'
__license__ = 'MIT License'
__version__ = '1.1-devel'


import base64
import types
import re
import urllib
import urllib2

from urllib2 import URLError, HTTPError
from xml.dom import minidom


URL = 'https://app.recurly.com'

# Names of each action and its related request method
CRUD_METHODS = {
        'create': 'POST',
        'read': 'GET',
        'update': 'PUT',
        'delete': 'DELETE'
    }

# A list of resources and primary keys for each. If you wish 
# to use 'id' for every resource, just remove these
PK = {
        'account': 'account_code',
        'plan': 'plan_code',
        'transaction': 'transaction_id',
        'invoice': 'invoice_id',
    }

# Name of each resource that should be treated as an array
# no matter the number of siblings it has. However, this
# is only applicable when its parent is of type 'collection'
# or 'array'
MULTIPLE = [
        'account',
        'charge',
        'credit',
        'invoice',
        'plan',
        'transaction',
    ]

# These resources should always be treated as an array
# even the root element is not of the 'array' type
FORCED_MULTIPLE = [
        'error',
    ]

class RecurlyException(Exception): pass
class RecurlyValidationException(RecurlyException): pass
class RecurlyConnectionException(RecurlyException): pass
class RecurlyNotFoundException(RecurlyException): pass
class RecurlyServerException(RecurlyException): pass
class RecurlyServiceUnavailableException(RecurlyException): pass


class Recurly(object):
    username = ''
    password = ''
    uri = ''
    response = None
    errors = None
    
    def __init__(self, username='', password='', uri=''):
        self.username = username
        self.password = password
        self.uri = uri
    
    
    def __getattr__(self, k):
        try:
            return object.__getattr__(self, k)
        except AttributeError:
            return Recurly(self.username, self.password, self.uri + '/' + k)
    
    
    def __call__(self, **kwargs):
        # Split out uri into segments and assume the last segment is an 
        # action. If it isn't place it on the end of the url again
        urili = self.uri.split('/')
        
        # Determine method with which to to request uri
        action = urili.pop()
        try:
            method = CRUD_METHODS[action]
        except KeyError:
            urili.append(action)
            method = 'GET'
        
        r = Recurly.singularize(urili[1])
        try:
            pk = PK[r]
        except KeyError:
            pk = 'id'
                
        model = Recurly.singularize(urili[-1])
                
        # If pk is set in arguments, place it in url instead
        uid = kwargs.pop(pk, False)
        if uid:
            urili.insert(2, uid)
        
        # Also, remove data from arguments and convert it to XML
        data = kwargs.pop('data', None)
        if data:
            data = Recurly.dict_to_xml(model, data)
        
        # Build argument list if necessary
        args = ''
        if method == 'GET' and kwargs:
            args = "?%s" % (urllib.urlencode(kwargs.items()))
        
        # Build url from the pieces
        url = URL + '/'.join(urili) + args
                   
        # Build request with our new url, method, and data
        opener = urllib2.build_opener(urllib2.HTTPHandler)
        self._request = urllib2.Request(url=url, data=data)
        self._request.get_method = lambda: method
        self._request.add_header('Accept', 'application/xml')
        self._request.add_header('Content-Type', 'application/xml')
        self._request.add_header('User-Agent', 'Recurly Python Client (v' + __version__ + ')')
        self._request.add_header('Authorization', 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password))[:-1])
                
        try:                        
            response = opener.open(self._request)
            xml_response = response.read()
        except HTTPError, e:
            xml_response = e.read()
            
            # All responses in this range are successes
            if e.code in range(200, 205):
                pass
            elif e.code == 422:
                msg = self.parse_errors(xml_response)
                raise RecurlyValidationException(msg)
            elif e.code == 404:
                msg = self.parse_errors(xml_response)
                raise RecurlyNotFoundException(msg)
            elif e.code == 500:
                raise RecurlyServerException(e)
            elif e.code == 503:
                raise RecurlyServiceUnavailableException(e)
            else:
                raise RecurlyException(e)
        except URLError, e:
            raise RecurlyConnectionException(e)
                <commit_msg>fix auth error due to base64 encoder putting newlines in user:pass<commit_after>#!/usr/bin/python2.5

'''A minimalist Python interface for the Recurly API'''

__author__ = '<NAME> <<EMAIL>>'
__license__ = 'MIT License'
__version__ = '1.1-devel'


import base64
import types
import re
import urllib
import urllib2

from urllib2 import URLError, HTTPError
from xml.dom import minidom


URL = 'https://app.recurly.com'

# Names of each action and its related request method
CRUD_METHODS = {
        'create': 'POST',
        'read': 'GET',
        'update': 'PUT',
        'delete': 'DELETE'
    }

# A list of resources and primary keys for each. If you wish 
# to use 'id' for every resource, just remove these
PK = {
        'account': 'account_code',
        'plan': 'plan_code',
        'transaction': 'transaction_id',
        'invoice': 'invoice_id',
    }

# Name of each resource that should be treated as an array
# no matter the number of siblings it has. However, this
# is only applicable when its parent is of type 'collection'
# or 'array'
MULTIPLE = [
        'account',
        'charge',
        'credit',
        'invoice',
        'plan',
        'transaction',
    ]

# These resources should always be treated as an array
# even the root element is not of the 'array' type
FORCED_MULTIPLE = [
        'error',
    ]

class RecurlyException(Exception): pass
class RecurlyValidationException(RecurlyException): pass
class RecurlyConnectionException(RecurlyException): pass
class RecurlyNotFoundException(RecurlyException): pass
class RecurlyServerException(RecurlyException): pass
class RecurlyServiceUnavailableException(RecurlyException): pass


class Recurly(object):
    username = ''
    password = ''
    uri = ''
    response = None
    errors = None
    
    def __init__(self, username='', password='', uri=''):
        self.username = username
        self.password = password
        self.uri = uri
    
    
    def __getattr__(self, k):
        try:
            return object.__getattr__(self, k)
        except AttributeError:
            return Recurly(self.username, self.password, self.uri + '/' + k)
    
    
    def __call__(self, **kwargs):
        # Split out uri into segments and assume the last segment is an 
        # action. If it isn't place it on the end of the url again
        urili = self.uri.split('/')
        
        # Determine method with which to to request uri
        action = urili.pop()
        try:
            method = CRUD_METHODS[action]
        except KeyError:
            urili.append(action)
            method = 'GET'
        
        r = Recurly.singularize(urili[1])
        try:
            pk = PK[r]
        except KeyError:
            pk = 'id'
                
        model = Recurly.singularize(urili[-1])
                
        # If pk is set in arguments, place it in url instead
        uid = kwargs.pop(pk, False)
        if uid:
            urili.insert(2, uid)
        
        # Also, remove data from arguments and convert it to XML
        data = kwargs.pop('data', None)
        if data:
            data = Recurly.dict_to_xml(model, data)
        
        # Build argument list if necessary
        args = ''
        if method == 'GET' and kwargs:
            args = "?%s" % (urllib.urlencode(kwargs.items()))
        
        # Build url from the pieces
        url = URL + '/'.join(urili) + args
                   
        # Build request with our new url, method, and data
        opener = urllib2.build_opener(urllib2.HTTPHandler)
        self._request = urllib2.Request(url=url, data=data)
        self._request.get_method = lambda: method
        self._request.add_header('Accept', 'application/xml')
        self._request.add_header('Content-Type', 'application/xml')
        self._request.add_header('User-Agent', 'Recurly Python Client (v' + __version__ + ')')
        self._request.add_header('Authorization', 'Basic %s' % base64.standard_b64encode('%s:%s' % (self.username, self.password)))
                
        try:                        
            response = opener.open(self._request)
            xml_response = response.read()
        except HTTPError, e:
            xml_response = e.read()
            
            # All responses in this range are successes
            if e.code in range(200, 205):
                pass
            elif e.code == 422:
                msg = self.parse_errors(xml_response)
                raise RecurlyValidationException(msg)
            elif e.code == 404:
                msg = self.parse_errors(xml_response)
                raise RecurlyNotFoundException(msg)
            elif e.code == 500:
                raise RecurlyServerException(e)
            elif e.code == 503:
                raise RecurlyServiceUnavailableException(e)
            else:
                raise RecurlyException(e)
        except URLError, e:
            raise RecurlyConnectionException(e)
                
{'fraction_non_alphanumeric': 0.09735503560528994, 'fraction_numerical': 0.007426246185147508, 'mean_word_length': 2.02120467117394, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 16}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5662786', 'n_tokens_mistral': 2856, 'n_tokens_neox': 2771, 'n_words': 820}