<commit_before>__docformat__ = 'restructuredtext en'
__author__ = 'eflee'

import collections
import time
import random

import zope.interface
import ipaddress
import requests

import sources



# noinspection PyMethodMayBeStatic
class IIPProvider(zope.interface.Interface):
    def add_source(self, source):
        """Adds a source to the set being used by the provider"""

    def get_ip(self):
        """Round robin through the configured providers until one returns an IP, then cache it"""

    def get_info(self, required_info_keys=None):
        """Round robin through the configured providers until one returns the keys required, then cache it"""

    def invalidate_cache(self):
        """Invalidates the cache"""

    def is_cache_valid(self):
        """Evaluates the validity of the cache"""


class IPProvider(object):
    zope.interface.implements(IIPProvider)

    def __init__(self, source_list=None, cache_ttl=3600):
        """
        The IPProvider takes one or more IPSources and returns the results from them . If one
        provider does not respond it will move on to the next. It ensures that the age of the
        response on no older than the cache_ttl.

        :param source_list: The list of sources to bootstrap the provider with
        :type source_list: list of IIPSource providers
        :param cache_ttl: the seconds the ip cache remains valid
        :type cache_ttl: int
        """
        self._sources = collections.defaultdict(dict)

        if source_list:
            for source in source_list:
                self.add_source(source)

        self._cache_ttl = cache_ttl

        self._cache_ip = None
        self._cache_info = None
        self._cache_timestamp = 0

    def add_source(self, source):
        """
        Adds a source to the set being used by the provider
        :param source: The IIPSource provider to add to sources
        :type source: IIPSource provider
        """
        if sources.IIPSource.providedBy(source):
            self._sources[source]['fail_count'] = 0
        else:
            raise TypeError('gimmeip.sources.IIPSource must be provided by source argument.')

    def get_ip(self):
        """
        Round robin through the configured providers until one returns an IP, then cache it
        :return: the current ip
        :rtype: ipaddress.IPv4Address or ipaddress.IPv6Address
        """
        if not self.is_cache_valid() or self._cache_ip is None:
            self._cache_ip, self._cache_info = self._fetch_from_sources()
            self._cache_timestamp = time.time()
        return self._cache_ip

    def get_info(self, required_info_keys=None):
        """
        Round robin through the configured providers until one returns the keys required, then cache it
        :param required_info_keys: The keys required for the fetch to be valid
        :type required_info_keys: list(tuple)
        :return: The info dictionary returned by the provider
        :rtype: dict
        """
        if not self.is_cache_valid or not self._cache_info \
                or not self._verify_required_keys(self._cache_info, required_info_keys):
            self._cache_ip, self._cache_info = self._fetch_from_sources(required_info_keys)
            self._cache_timestamp = time.time()
        return self._cache_info

    def _fetch_from_sources(self, required_info_keys=None):
        srces = self._sources.keys()
        random.shuffle(srces)
        for source in srces:
            try:
                source._fetch()

                ip = source.ip
                info = source.info

                if not required_info_keys or self._verify_required_keys(info, required_info_keys):
                    return ip, info

            except (ValueError, requests.ConnectionError) as e:
                print e
                continue

        raise NullResponseFromSourcesError("No sources returned a valid response.")

    @staticmethod
    def _verify_required_keys(info, required_info_keys):
        keys_found = True
        if required_info_keys is not None:<commit_msg>Removing erroneous print statement<commit_after>__docformat__ = 'restructuredtext en'
__author__ = 'eflee'

import collections
import time
import random

import zope.interface
import ipaddress
import requests

import sources



# noinspection PyMethodMayBeStatic
class IIPProvider(zope.interface.Interface):
    def add_source(self, source):
        """Adds a source to the set being used by the provider"""

    def get_ip(self):
        """Round robin through the configured providers until one returns an IP, then cache it"""

    def get_info(self, required_info_keys=None):
        """Round robin through the configured providers until one returns the keys required, then cache it"""

    def invalidate_cache(self):
        """Invalidates the cache"""

    def is_cache_valid(self):
        """Evaluates the validity of the cache"""


class IPProvider(object):
    zope.interface.implements(IIPProvider)

    def __init__(self, source_list=None, cache_ttl=3600):
        """
        The IPProvider takes one or more IPSources and returns the results from them . If one
        provider does not respond it will move on to the next. It ensures that the age of the
        response on no older than the cache_ttl.

        :param source_list: The list of sources to bootstrap the provider with
        :type source_list: list of IIPSource providers
        :param cache_ttl: the seconds the ip cache remains valid
        :type cache_ttl: int
        """
        self._sources = collections.defaultdict(dict)

        if source_list:
            for source in source_list:
                self.add_source(source)

        self._cache_ttl = cache_ttl

        self._cache_ip = None
        self._cache_info = None
        self._cache_timestamp = 0

    def add_source(self, source):
        """
        Adds a source to the set being used by the provider
        :param source: The IIPSource provider to add to sources
        :type source: IIPSource provider
        """
        if sources.IIPSource.providedBy(source):
            self._sources[source]['fail_count'] = 0
        else:
            raise TypeError('gimmeip.sources.IIPSource must be provided by source argument.')

    def get_ip(self):
        """
        Round robin through the configured providers until one returns an IP, then cache it
        :return: the current ip
        :rtype: ipaddress.IPv4Address or ipaddress.IPv6Address
        """
        if not self.is_cache_valid() or self._cache_ip is None:
            self._cache_ip, self._cache_info = self._fetch_from_sources()
            self._cache_timestamp = time.time()
        return self._cache_ip

    def get_info(self, required_info_keys=None):
        """
        Round robin through the configured providers until one returns the keys required, then cache it
        :param required_info_keys: The keys required for the fetch to be valid
        :type required_info_keys: list(tuple)
        :return: The info dictionary returned by the provider
        :rtype: dict
        """
        if not self.is_cache_valid or not self._cache_info \
                or not self._verify_required_keys(self._cache_info, required_info_keys):
            self._cache_ip, self._cache_info = self._fetch_from_sources(required_info_keys)
            self._cache_timestamp = time.time()
        return self._cache_info

    def _fetch_from_sources(self, required_info_keys=None):
        srces = self._sources.keys()
        random.shuffle(srces)
        for source in srces:
            try:
                source._fetch()

                ip = source.ip
                info = source.info

                if not required_info_keys or self._verify_required_keys(info, required_info_keys):
                    return ip, info

            except (ValueError, requests.ConnectionError) as e:
                continue

        raise NullResponseFromSourcesError("No sources returned a valid response.")

    @staticmethod
    def _verify_required_keys(info, required_info_keys):
        keys_found = True
        if required_info_keys is not None:
{'fraction_non_alphanumeric': 0.06705145523762741, 'fraction_numerical': 0.0019648778091612426, 'mean_word_length': 2.507321274763135, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, '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': '7024957', 'n_tokens_mistral': 2208, 'n_tokens_neox': 2124, 'n_words': 730}