<commit_before># -*- coding: utf-8 -*-

"""Base exchange class"""

# -----------------------------------------------------------------------------

__version__ = '1.10.972'

# -----------------------------------------------------------------------------

from ccxt.base.errors import ExchangeError
from ccxt.base.errors import NotSupported
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RequestTimeout
from ccxt.base.errors import ExchangeNotAvailable

# -----------------------------------------------------------------------------

__all__ = [
    'Exchange',
]

# -----------------------------------------------------------------------------

# Python 2 & 3
import base64
import calendar
import collections
import datetime
import functools
import gzip
import hashlib
import hmac
import io
import json
import math
from numbers import Number
import re
from requests import Session
from requests.utils import default_user_agent
from requests.exceptions import ConnectionError, HTTPError, Timeout, TooManyRedirects, RequestException
# import socket
# import ssl
# import sys
import time
import uuid
import zlib
from decimal import Decimal

# -----------------------------------------------------------------------------

try:
    import urllib.parse as _urlencode  # Python 3
    import urllib.request as _urllib
    # import http.client as httplib
except ImportError:
    import urllib as _urlencode        # Python 2
    import urllib2 as _urllib
    # import httplib

# -----------------------------------------------------------------------------

try:
    basestring  # Python 3
except NameError:
    basestring = str  # Python 2

# -----------------------------------------------------------------------------


class Exchange(object):
    """Base exchange class"""
    id = None
    version = None

    # rate limiter settings
    enableRateLimit = False
    rateLimit = 2000  # milliseconds = seconds * 1000
    timeout = 10000   # milliseconds = seconds * 1000
    asyncio_loop = None
    aiohttp_proxy = None
    session = None  # Session ()
    userAgent = None
    userAgents = {
        'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
        'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
    }
    verbose = False
    markets = None
    symbols = None
    precision = {}
    limits = {}
    fees = {'trading': {}, 'funding': {}}
    ids = None
    currencies = None
    tickers = None
    api = None
    parseJsonResponse = True
    exceptions = {}
    headers = {}
    balance = {}
    orderbooks = {}
    orders = {}
    trades = {}
    currencies = {}
    proxy = ''
    origin = '*'  # CORS origin
    proxies = None
    apiKey = ''
    secret = ''
    password = ''
    uid = ''
    twofa = False
    marketsById = None
    markets_by_id = None
    currencies_by_id = None
    options = {}  # Python does not allow to define properties in run-time with setattr

    hasPublicAPI = True
    hasPrivateAPI = True
    hasCORS = False
    hasFetchTicker = True
    hasFetchOrderBook = True
    hasFetchTrades = True
    hasFetchTickers = False
    hasFetchOHLCV = False
    hasDeposit = False
    hasWithdraw = False
    hasFetchBalance = True
    hasFetchOrder = False
    hasFetchOrders = False
    hasFetchOpenOrders = False
    hasFetchClosedOrders = False
    hasFetchMyTrades = False
    hasFetchCurrencies = False
    hasCreateOrder = hasPrivateAPI
    hasCancelOrder = hasPrivateAPI

    requiredCredentials = {
        'apiKey': True,
        'secret': True,
        'uid': False,
        'login': False,
        'password': <PASSWORD>,
    }

    # API method metainfo
    has = {
        'cancelOrder': hasPrivateAPI,
        'createDepositAddress': False,
        'createOrder': hasPrivateAPI,
        'deposit': False,
        'fetchBalance': True,
        'fetchClosedOrders': False,
        'fetchCurrencies': False,
        'fetchDepositAddress': False,
        'fetchMarkets': True,
        'fetchMyTrades': False,
        'fetchOHLCV': False,
        'fetchOpenOrders': False,
        'fetchOrder': False,
        'fetchOrderBook': True,
        'fetchOrders': False,
        'fetchTicker': True,
        'fetchTickers': False,
        'fetchTrades': True,
        'withdraw': False,
    }

    substituteCommonCurrencyCodes = True
    lastRestRequestTimestamp = 0
    lastRestPollTimestamp = 0
    restRequestQueue = None
    restPollerLoopIsRunning = False
    rateLimitTokens = 16
    rateLimitMaxTokens = 16
    rateLimitUpdateTime = 0
    last_http_response = None
    last_json_response = None

    def __init__(self, config={}):

        # version = '.'.join(map(str, sys.version_info[:3]))
        # self.userAgent = {
        #     'User-Agent': 'ccxt/' + __version__ + ' (+https://github.com/ccxt/ccxt) Python/' + version
        # }

        self.userAgent = default_user_agent()

        settings = self.deep_extend(self.describe(), config)

        for key in settings:
            if hasattr(self, key) and isinstance(getattr(self, key), dict):
                setattr(self, key, self.deep_extend(getattr(self, key), settings[key]))
            else:
                setattr(self, key, settings[key])

        if self.api:
            self.define_rest_api(self.api, 'request')

        if self.markets:
            self.set_markets(self.markets)

        # format camel case
        for attr in dir(self):
            if attr[0] != '_'and attr[-1] != '_' and '_' in attr:
                conv = attr.split('_')
                camel_case = conv[0] + ''.join(i[0].upper() + i[1:] for i in conv[1:])
                setattr(self, camel_case, getattr(self, attr))

        self.tokenBucket = self.extend({
            'refillRate': 1.0 / self.rateLimit,
            'delay': 1.0,
            'capacity': 1.0,
            'defaultCost': 1.0,
            'maxCapacity': 1000,
        }, getattr(self, 'tokenBucket') if hasattr(self, 'tokenBucket') else {})

        self.session = self.session if self.session else Session()

    def __del__(self):
        if self.session:
            self.session.close()

    def describe(self):
        return {}

    def define_rest_api(self, api, method_name, options={}):
        delimiters = re.compile('[^a-zA-Z0-9]')
        for api_type, methods in api.items():
            for http_method, urls in methods.items():
                for url in urls:
                    url = url.strip()
                    split_path = delimiters.split(url)

                    uppercase_method = http_method.upper()
                    lowercase_method = http_method.lower()
                    camelcase_method = lowercase_method.capitalize()
                    camelcase_suffix = ''.join([Exchange.capitalize(x) for x in split_path])
                    lowercase_path = [x.strip().lower() for x in split_path]
                    underscore_suffix = '_'.join([k for k in lowercase_path if len(k)])

                    camelcase = api_type + camelcase_method + Exchange.capitalize(camelcase_suffix)
                    underscore = api_type + '_' + lowercase_method + '_' + underscore_suffix.lower()

                    if 'suffixes' in options:
                        if 'camelcase' in options['suffixes']:
                            camelcase += options['suffixes']['camelcase']
                        if 'underscore' in options['suffixes']:
                            underscore += options['suffixes']['underscore']

                    partial = functools.partial(getattr(self, method_name), url, api_type, uppercase_method)
                    setattr(self, camelcase, partial)
                    setattr(self, underscore, partial)

    def raise_error(self, exception_type, url, method='GET', error=None, details=None):
        details = details if details else ''
        if error:
            if type(error) is _urllib.HTTPError:
                details = ' '.join([
                    str(error.code),
                    error.msg,
                    error.read().decode('utf-8'),
                    details,
                ])
            else:
                details = ' '.join([
                    str(error),
                    details,
                ])
            raise exception_type(' '.join([
                self.id,
                method,
                url,
                details,
            ]))
        else:
            raise exception_type(' '.join([self.id, method, url, details]))

    def throttle(self):
        now = float(self.milliseconds())
        elapsed = now - self.lastRestRequestTimestamp
        if elapsed < self.rateLimit:
            delay = self.rateLimit - elapsed
            time.sleep(delay / 1000.0)

    def fetch2(self, path, api='public', method='GET', params={}, headers=None, body=None):
        """A better wrapper over request for deferred signing"""
        if self.enableRateLimit:
            self.throttle()
        self.lastRestRequestTimestamp = self.milliseconds()
        request = self.sign(path, api, method, params, headers, body)
        return self.fetch(request['url'], request['method'], request['headers'], request['body'])

    def request(self, path, api='public', method='GET', params={}, headers=None, body=None):
        return self.fetch2(path, api, method, params, headers, body)

    @staticmethod
    def gzip_deflate(response, text):
        encoding = response.info().get('Content-Encoding')
        if encoding in ('gzip', 'x-gzip', 'deflate'):
            if encoding == 'deflate':
                return zlib.decompress(text, -zlib.MAX_WBITS)
            else:
                return gzip.GzipFile('', 'rb', 9, io.BytesIO(text)).read()
        return text

    def handle_errors(self, code, reason, url, method, headers, body):
        pass

    def prepare_request_headers(self, headers=None):
        headers = headers or {}
        headers.update(self.headers)
        if self.userAgent:
            if type(self.userAgent) is str:
                headers.update({'User-Agent': self.userAgent})
            elif (type(self.userAgent) is dict) and ('User-Agent' in self.userAgent):
                headers.update(self.userAgent)
        if self.proxy:
            headers.update({'Origin': self.origin})
        headers.update({'Accept-Encoding': 'gzip, deflate'})
        return headers

    def fetch(self, url, method='GET', headers=None, body=None):
        """Perform a HTTP request and return decoded JSON data"""
        headers = self.prepare_request_headers(headers)
        url = self.proxy + url
        if self.verbose:
            print(method, url, "\nRequest:", headers, body)
        if body:
            body = body.encode()

        self.session.cookies.clear()

        response = None
        try:
            response = self.session.request(
                method,
                url,
                data=body,
                headers=headers,
                timeout=int(self.timeout / 1000),
                proxies=self.proxies
            )
            self.last_http_response = response.text
            response.raise_for_status()

        except Timeout as e:
            raise RequestTimeout(' '.join([self.id, method, url, 'request timeout']))

        except ConnectionError as e:
            self.raise_error(ExchangeNotAvailable, url, method, e)

        except TooManyRedirects as e:
            self.raise_error(ExchangeError, url, method, e)

        except HTTPError as e:
            self.handle_errors(response.status_code, response.reason, url, method, None, self.last_http_response)
            self.handle_rest_errors(e, response.status_code, self.last_http_response, url, method)
            self.raise_error(ExchangeError, url, method, e, self.last_http_response)

        except RequestException as e:
            self.raise_error(ExchangeError, url, method, e)

        if self.verbose:
            print(method, url, "\nResponse:", str(response.headers), self.last_http_response)

        self.handle_errors(response.status_code, response.reason, url, method, None, self.last_http_response)
        return self.handle_rest_response(self.last_http_response, url, method, headers, body)

    def handle_rest_errors(self, exception, http_status_code, response, url, method='GET'):
        error = None
        if http_status_code in [418, 429]:
            error = DDoSProtection
        elif http_status_code in [404, 409, 500, 501, 502, 520, 521, 522, 525]:
            error = ExchangeNotAvailable
        elif http_status_code in [422]:
            error = ExchangeError
        elif http_status_code in [400, 403, 405, 503, 530]:
            # special case to detect ddos protection
            error = ExchangeNotAvailable<commit_msg>added http status codes to verbose responses in python #1726<commit_after># -*- coding: utf-8 -*-

"""Base exchange class"""

# -----------------------------------------------------------------------------

__version__ = '1.10.972'

# -----------------------------------------------------------------------------

from ccxt.base.errors import ExchangeError
from ccxt.base.errors import NotSupported
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RequestTimeout
from ccxt.base.errors import ExchangeNotAvailable

# -----------------------------------------------------------------------------

__all__ = [
    'Exchange',
]

# -----------------------------------------------------------------------------

# Python 2 & 3
import base64
import calendar
import collections
import datetime
import functools
import gzip
import hashlib
import hmac
import io
import json
import math
from numbers import Number
import re
from requests import Session
from requests.utils import default_user_agent
from requests.exceptions import ConnectionError, HTTPError, Timeout, TooManyRedirects, RequestException
# import socket
# import ssl
# import sys
import time
import uuid
import zlib
from decimal import Decimal

# -----------------------------------------------------------------------------

try:
    import urllib.parse as _urlencode  # Python 3
    import urllib.request as _urllib
    # import http.client as httplib
except ImportError:
    import urllib as _urlencode        # Python 2
    import urllib2 as _urllib
    # import httplib

# -----------------------------------------------------------------------------

try:
    basestring  # Python 3
except NameError:
    basestring = str  # Python 2

# -----------------------------------------------------------------------------


class Exchange(object):
    """Base exchange class"""
    id = None
    version = None

    # rate limiter settings
    enableRateLimit = False
    rateLimit = 2000  # milliseconds = seconds * 1000
    timeout = 10000   # milliseconds = seconds * 1000
    asyncio_loop = None
    aiohttp_proxy = None
    session = None  # Session ()
    userAgent = None
    userAgents = {
        'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
        'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
    }
    verbose = False
    markets = None
    symbols = None
    precision = {}
    limits = {}
    fees = {'trading': {}, 'funding': {}}
    ids = None
    currencies = None
    tickers = None
    api = None
    parseJsonResponse = True
    exceptions = {}
    headers = {}
    balance = {}
    orderbooks = {}
    orders = {}
    trades = {}
    currencies = {}
    proxy = ''
    origin = '*'  # CORS origin
    proxies = None
    apiKey = ''
    secret = ''
    password = ''
    uid = ''
    twofa = False
    marketsById = None
    markets_by_id = None
    currencies_by_id = None
    options = {}  # Python does not allow to define properties in run-time with setattr

    hasPublicAPI = True
    hasPrivateAPI = True
    hasCORS = False
    hasFetchTicker = True
    hasFetchOrderBook = True
    hasFetchTrades = True
    hasFetchTickers = False
    hasFetchOHLCV = False
    hasDeposit = False
    hasWithdraw = False
    hasFetchBalance = True
    hasFetchOrder = False
    hasFetchOrders = False
    hasFetchOpenOrders = False
    hasFetchClosedOrders = False
    hasFetchMyTrades = False
    hasFetchCurrencies = False
    hasCreateOrder = hasPrivateAPI
    hasCancelOrder = hasPrivateAPI

    requiredCredentials = {
        'apiKey': True,
        'secret': True,
        'uid': False,
        'login': False,
        'password': False,
    }

    # API method metainfo
    has = {
        'cancelOrder': hasPrivateAPI,
        'createDepositAddress': False,
        'createOrder': hasPrivateAPI,
        'deposit': False,
        'fetchBalance': True,
        'fetchClosedOrders': False,
        'fetchCurrencies': False,
        'fetchDepositAddress': False,
        'fetchMarkets': True,
        'fetchMyTrades': False,
        'fetchOHLCV': False,
        'fetchOpenOrders': False,
        'fetchOrder': False,
        'fetchOrderBook': True,
        'fetchOrders': False,
        'fetchTicker': True,
        'fetchTickers': False,
        'fetchTrades': True,
        'withdraw': False,
    }

    substituteCommonCurrencyCodes = True
    lastRestRequestTimestamp = 0
    lastRestPollTimestamp = 0
    restRequestQueue = None
    restPollerLoopIsRunning = False
    rateLimitTokens = 16
    rateLimitMaxTokens = 16
    rateLimitUpdateTime = 0
    last_http_response = None
    last_json_response = None

    def __init__(self, config={}):

        # version = '.'.join(map(str, sys.version_info[:3]))
        # self.userAgent = {
        #     'User-Agent': 'ccxt/' + __version__ + ' (+https://github.com/ccxt/ccxt) Python/' + version
        # }

        self.userAgent = default_user_agent()

        settings = self.deep_extend(self.describe(), config)

        for key in settings:
            if hasattr(self, key) and isinstance(getattr(self, key), dict):
                setattr(self, key, self.deep_extend(getattr(self, key), settings[key]))
            else:
                setattr(self, key, settings[key])

        if self.api:
            self.define_rest_api(self.api, 'request')

        if self.markets:
            self.set_markets(self.markets)

        # format camel case
        for attr in dir(self):
            if attr[0] != '_'and attr[-1] != '_' and '_' in attr:
                conv = attr.split('_')
                camel_case = conv[0] + ''.join(i[0].upper() + i[1:] for i in conv[1:])
                setattr(self, camel_case, getattr(self, attr))

        self.tokenBucket = self.extend({
            'refillRate': 1.0 / self.rateLimit,
            'delay': 1.0,
            'capacity': 1.0,
            'defaultCost': 1.0,
            'maxCapacity': 1000,
        }, getattr(self, 'tokenBucket') if hasattr(self, 'tokenBucket') else {})

        self.session = self.session if self.session else Session()

    def __del__(self):
        if self.session:
            self.session.close()

    def describe(self):
        return {}

    def define_rest_api(self, api, method_name, options={}):
        delimiters = re.compile('[^a-zA-Z0-9]')
        for api_type, methods in api.items():
            for http_method, urls in methods.items():
                for url in urls:
                    url = url.strip()
                    split_path = delimiters.split(url)

                    uppercase_method = http_method.upper()
                    lowercase_method = http_method.lower()
                    camelcase_method = lowercase_method.capitalize()
                    camelcase_suffix = ''.join([Exchange.capitalize(x) for x in split_path])
                    lowercase_path = [x.strip().lower() for x in split_path]
                    underscore_suffix = '_'.join([k for k in lowercase_path if len(k)])

                    camelcase = api_type + camelcase_method + Exchange.capitalize(camelcase_suffix)
                    underscore = api_type + '_' + lowercase_method + '_' + underscore_suffix.lower()

                    if 'suffixes' in options:
                        if 'camelcase' in options['suffixes']:
                            camelcase += options['suffixes']['camelcase']
                        if 'underscore' in options['suffixes']:
                            underscore += options['suffixes']['underscore']

                    partial = functools.partial(getattr(self, method_name), url, api_type, uppercase_method)
                    setattr(self, camelcase, partial)
                    setattr(self, underscore, partial)

    def raise_error(self, exception_type, url, method='GET', error=None, details=None):
        details = details if details else ''
        if error:
            if type(error) is _urllib.HTTPError:
                details = ' '.join([
                    str(error.code),
                    error.msg,
                    error.read().decode('utf-8'),
                    details,
                ])
            else:
                details = ' '.join([
                    str(error),
                    details,
                ])
            raise exception_type(' '.join([
                self.id,
                method,
                url,
                details,
            ]))
        else:
            raise exception_type(' '.join([self.id, method, url, details]))

    def throttle(self):
        now = float(self.milliseconds())
        elapsed = now - self.lastRestRequestTimestamp
        if elapsed < self.rateLimit:
            delay = self.rateLimit - elapsed
            time.sleep(delay / 1000.0)

    def fetch2(self, path, api='public', method='GET', params={}, headers=None, body=None):
        """A better wrapper over request for deferred signing"""
        if self.enableRateLimit:
            self.throttle()
        self.lastRestRequestTimestamp = self.milliseconds()
        request = self.sign(path, api, method, params, headers, body)
        return self.fetch(request['url'], request['method'], request['headers'], request['body'])

    def request(self, path, api='public', method='GET', params={}, headers=None, body=None):
        return self.fetch2(path, api, method, params, headers, body)

    @staticmethod
    def gzip_deflate(response, text):
        encoding = response.info().get('Content-Encoding')
        if encoding in ('gzip', 'x-gzip', 'deflate'):
            if encoding == 'deflate':
                return zlib.decompress(text, -zlib.MAX_WBITS)
            else:
                return gzip.GzipFile('', 'rb', 9, io.BytesIO(text)).read()
        return text

    def handle_errors(self, code, reason, url, method, headers, body):
        pass

    def prepare_request_headers(self, headers=None):
        headers = headers or {}
        headers.update(self.headers)
        if self.userAgent:
            if type(self.userAgent) is str:
                headers.update({'User-Agent': self.userAgent})
            elif (type(self.userAgent) is dict) and ('User-Agent' in self.userAgent):
                headers.update(self.userAgent)
        if self.proxy:
            headers.update({'Origin': self.origin})
        headers.update({'Accept-Encoding': 'gzip, deflate'})
        return headers

    def fetch(self, url, method='GET', headers=None, body=None):
        """Perform a HTTP request and return decoded JSON data"""
        headers = self.prepare_request_headers(headers)
        url = self.proxy + url
        if self.verbose:
            print(method, url, "\nRequest:", headers, "\n", body)
        if body:
            body = body.encode()

        self.session.cookies.clear()

        response = None
        try:
            response = self.session.request(
                method,
                url,
                data=body,
                headers=headers,
                timeout=int(self.timeout / 1000),
                proxies=self.proxies
            )
            self.last_http_response = response.text
            response.raise_for_status()

        except Timeout as e:
            raise RequestTimeout(' '.join([self.id, method, url, 'request timeout']))

        except ConnectionError as e:
            self.raise_error(ExchangeNotAvailable, url, method, e)

        except TooManyRedirects as e:
            self.raise_error(ExchangeError, url, method, e)

        except HTTPError as e:
            self.handle_errors(response.status_code, response.reason, url, method, None, self.last_http_response)
            self.handle_rest_errors(e, response.status_code, self.last_http_response, url, method)
            self.raise_error(ExchangeError, url, method, e, self.last_http_response)

        except RequestException as e:
            self.raise_error(ExchangeError, url, method, e)

        if self.verbose:
            print(method, url, str(response.status_code), "\nResponse:", str(response.headers), "\n", self.last_http_response)

        self.handle_errors(response.status_code, response.reason, url, method, None, self.last_http_response)
        return self.handle_rest_response(self.last_http_response, url, method, headers, body)

    def handle_rest_errors(self, exception, http_status_code, response, url, method='GET'):
        error = None
        if http_status_code in [418, 429]:
            error = DDoSProtection
        elif http_status_code in [404, 409, 500, 501, 502, 520, 521, 522, 525]:
            error = ExchangeNotAvailable
        elif http_status_code in [422]:
            error = ExchangeError
        elif http_status_code in [400, 403, 405, 503, 530]:
            # special case to detect ddos protection
            error = ExchangeNotAvailable
{'fraction_non_alphanumeric': 0.1425922348850686, 'fraction_numerical': 0.014062198184276608, 'mean_word_length': 1.8981191222570533, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 4, 'https://': 2, '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': '491736', 'n_tokens_mistral': 7483, 'n_tokens_neox': 6977, 'n_words': 1819}