<commit_before>import platform
import sys
import ssl
import re
import pkg_resources
try:
    from urllib.parse import urlencode
except ImportError:
    # support python 2
    from urllib import urlencode

import requests

from . import Error
from . import Resource


class Client(object):
    CLIENT_VERSION = '2.0.0a0'
    API_ENDPOINT   = 'https://api.mollie.com'
    API_VERSION    = 'v2'
    UNAME          = ' '.join(platform.uname())
    USER_AGENT     = ' '.join(vs.replace(r'\s+', '-') for vs in [
        'Mollie/'  + CLIENT_VERSION,
        'Python/'  + sys.version.split(' ')[0],
        'OpenSSL/' + ssl.OPENSSL_VERSION.split(' ')[1],
    ])

    @staticmethod
    def validateApiEndpoint(api_endpoint):
        return api_endpoint.strip().rstrip('/')

    @staticmethod
    def validateApiKey(api_key):
        api_key = api_key.strip()
        if not re.compile(r'^(live|test)_\w+$').match(api_key):
            raise Error('Invalid API key: "%s". An API key must start with "test_" or "live_".' % api_key)
        return api_key

    def __init__(self, api_key=None, api_endpoint=None):
        self.api_endpoint = self.validateApiEndpoint(api_endpoint or self.API_ENDPOINT)
        self.api_version = self.API_VERSION
        self.api_key = self.validateApiKey(api_key) if api_key else None
        self.payments = Resource.Payments(self)
        self.payment_refunds = Resource.PaymentRefunds(self)
        self.payment_chargebacks = Resource.PaymentChargebacks(self)
        self.methods = Resource.Methods(self)
        self.issuers = Resource.Issuers(self)
        self.refunds = Resource.Refunds(self)
        self.chargebacks = Resource.Chargebacks(self)
        self.customers = Resource.Customers(self)
        self.customer_mandates = Resource.CustomerMandates(self)
        self.customer_subscriptions = Resource.CustomerSubscriptions(self)
        self.customer_payments = Resource.CustomerPayments(self)

    def getApiEndpoint(self):
        return self.api_endpoint

    def setApiEndpoint(self, api_endpoint):
        self.api_endpoint = self.validateApiEndpoint(api_endpoint)

    def setApiKey(self, api_key):
        self.api_key = self.validateApiKey(api_key)

    def getCACert(self):
        cacert = pkg_resources.resource_filename('Mollie.API', 'cacert.pem')
        if not cacert or len(cacert) < 1:
            raise Error('Unable to load cacert.pem')
        return cacert

    def performHttpCall(self, http_method, path, data=None, params=None):
        if not self.api_key:
            raise Error('You have not set an API key. Please use setApiKey() to set the API key.')
        url = '%s/%s/%s' % (self.api_endpoint, self.api_version, path)
        data = '{}' if data is None else data

        query_string = generate_querystring(params)
        if query_string:
            url += '?' + query_string
            params = None

        try:
            response = requests.request(
                http_method, url,
                verify=self.getCACert(),
                headers={
                    'Accept': 'application/json',
                    'Authorization': 'Bearer ' + self.api_key,
                    'User-Agent': self.USER_AGENT,
                    'X-Mollie-Client-Info': self.UNAME,
                },
                params=params,
                data=data
            )
        except Exception as e:
            raise Error('Unable to communicate with Mollie: %s.' % str(e))
        return response


def generate_querystring(params):
    """
    Generate a querystring suitable for use in the v2 api.

    The Requests library doesn't know how to generate querystrings that encode dictionaries using square brackets:
    https://api.mollie.com/v2/methods?amount[value]=100.00&amount[currency]=USD

    Note: we use `sorted()` to work around a difference in iteration behaviour between Python 2 and 3.
    This makes the output predictable, and ordering of querystring parameters shouldn't matter.
    """
    parts = []
    for param, value in sorted(params.items()):
        if not isinstance(value, dict):
            parts.append(urlencode({param: value}))
        else:
            # encode dictionary with square brackets
            for key, sub_value in sorted(value.items()):
                composed = '%s[%s]' % (param, key)
                parts.append(urlencode({composed: sub_value}))
    if parts:
        return '&'.join(parts)
    else:
        return None<commit_msg>Fix querystring handling for empty params<commit_after>import platform
import sys
import ssl
import re
import pkg_resources
try:
    from urllib.parse import urlencode
except ImportError:
    # support python 2
    from urllib import urlencode

import requests

from . import Error
from . import Resource


class Client(object):
    CLIENT_VERSION = '2.0.0a0'
    API_ENDPOINT   = 'https://api.mollie.com'
    API_VERSION    = 'v2'
    UNAME          = ' '.join(platform.uname())
    USER_AGENT     = ' '.join(vs.replace(r'\s+', '-') for vs in [
        'Mollie/'  + CLIENT_VERSION,
        'Python/'  + sys.version.split(' ')[0],
        'OpenSSL/' + ssl.OPENSSL_VERSION.split(' ')[1],
    ])

    @staticmethod
    def validateApiEndpoint(api_endpoint):
        return api_endpoint.strip().rstrip('/')

    @staticmethod
    def validateApiKey(api_key):
        api_key = api_key.strip()
        if not re.compile(r'^(live|test)_\w+$').match(api_key):
            raise Error('Invalid API key: "%s". An API key must start with "test_" or "live_".' % api_key)
        return api_key

    def __init__(self, api_key=None, api_endpoint=None):
        self.api_endpoint = self.validateApiEndpoint(api_endpoint or self.API_ENDPOINT)
        self.api_version = self.API_VERSION
        self.api_key = self.validateApiKey(api_key) if api_key else None
        self.payments = Resource.Payments(self)
        self.payment_refunds = Resource.PaymentRefunds(self)
        self.payment_chargebacks = Resource.PaymentChargebacks(self)
        self.methods = Resource.Methods(self)
        self.issuers = Resource.Issuers(self)
        self.refunds = Resource.Refunds(self)
        self.chargebacks = Resource.Chargebacks(self)
        self.customers = Resource.Customers(self)
        self.customer_mandates = Resource.CustomerMandates(self)
        self.customer_subscriptions = Resource.CustomerSubscriptions(self)
        self.customer_payments = Resource.CustomerPayments(self)

    def getApiEndpoint(self):
        return self.api_endpoint

    def setApiEndpoint(self, api_endpoint):
        self.api_endpoint = self.validateApiEndpoint(api_endpoint)

    def setApiKey(self, api_key):
        self.api_key = self.validateApiKey(api_key)

    def getCACert(self):
        cacert = pkg_resources.resource_filename('Mollie.API', 'cacert.pem')
        if not cacert or len(cacert) < 1:
            raise Error('Unable to load cacert.pem')
        return cacert

    def performHttpCall(self, http_method, path, data=None, params=None):
        if not self.api_key:
            raise Error('You have not set an API key. Please use setApiKey() to set the API key.')
        url = '%s/%s/%s' % (self.api_endpoint, self.api_version, path)
        data = '{}' if data is None else data

        query_string = generate_querystring(params)
        if query_string:
            url += '?' + query_string
            params = None

        try:
            response = requests.request(
                http_method, url,
                verify=self.getCACert(),
                headers={
                    'Accept': 'application/json',
                    'Authorization': 'Bearer ' + self.api_key,
                    'User-Agent': self.USER_AGENT,
                    'X-Mollie-Client-Info': self.UNAME,
                },
                params=params,
                data=data
            )
        except Exception as e:
            raise Error('Unable to communicate with Mollie: %s.' % str(e))
        return response


def generate_querystring(params):
    """
    Generate a querystring suitable for use in the v2 api.

    The Requests library doesn't know how to generate querystrings that encode dictionaries using square brackets:
    https://api.mollie.com/v2/methods?amount[value]=100.00&amount[currency]=USD

    Note: we use `sorted()` to work around a difference in iteration behaviour between Python 2 and 3.
    This makes the output predictable, and ordering of querystring parameters shouldn't matter.
    """
    if not params:
        return
    parts = []
    for param, value in sorted(params.items()):
        if not isinstance(value, dict):
            parts.append(urlencode({param: value}))
        else:
            # encode dictionary with square brackets
            for key, sub_value in sorted(value.items()):
                composed = '%s[%s]' % (param, key)
                parts.append(urlencode({composed: sub_value}))
    if parts:
        return '&'.join(parts)
{'fraction_non_alphanumeric': 0.10368715083798882, 'fraction_numerical': 0.004022346368715084, 'mean_word_length': 2.449325626204239, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '5790121', 'n_tokens_mistral': 2691, 'n_tokens_neox': 2561, 'n_words': 655}