<commit_before>import functools
import urlparse
import urllib
import json
import endpoint
import protocol
from response import Response
from temporal.validate import check_time_param


def make_series_url(key):
    """Given a series key, generate a valid URL to the series endpoint for
    that key.

    :param string key: the series key
    :rtype: string"""

    url = urlparse.urljoin(endpoint.SERIES_ENDPOINT, 'key/')
    url = urlparse.urljoin(url, urllib.quote_plus(key))
    return url


class with_response_type(object):
    """Decorator for ensuring the Response object returned by the
    :class:`Client` object has a data attribute that corresponds to the
    object type expected from the TempoDB API.  This class should not be
    used by user code.

    The "t" argument should be a string corresponding to the name of a class
    from the :mod:`tempodb.protocol.objects` module, or a single element list
    with the element being the name of a class from that module if the API
    endpoint will return a list of those objects.

    :param t: the type of object to cast the TempoDB response to
    :type t: list or string"""

    def __init__(self, t):
        self.t = t

    def __call__(self, f, *args, **kwargs):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            resp = f(*args, **kwargs)
            #dont try this at home kids
            session = args[0].session
            resp_obj = Response(resp, session)
            if resp_obj.status == 200:
                resp_obj._cast_payload(self.t)
            return resp_obj
        return wrapper


class Client(object):
    """Entry point class into the TempoDB API.  The client should be
    initialized with your API key and secret obtained from your TempoDB
    login.

    The methods are grouped as follows:

    SERIES

        * :meth:`create_series`
        * :meth:`delete_series`
        * :meth:`get_series`
        * :meth:`list_series`
        * :meth:`update_series`

    READING DATA

        * :meth:`read_data`
        * :meth:`aggregate_data`

    WRITING DATA

        * :meth:`write_data`
        * :meth:`write_multi`

    DELETING

        * :meth:`delete`

    SINGLE VALUE

        * :meth:`single_value`
        * :meth:`multi_series_single_value`

    :param string key: your API key
    :param string secret: your API secret"""

    def __init__(self, key, secret, base_url=endpoint.BASE_URL):
        self.session = endpoint.HTTPEndpoint(key, secret, base_url)

    #SERIES METHODS
    @with_response_type('Nothing')
    def create_series(self, key=None, tags=[], attrs={}):
        """Create a new series with an optional string key.  A list of tags
        and a map of attributes can also be optionally supplied.

        :param string key: (optional) a string key for the series
        :param list tags: (optional) the tags to create the series with
        :param dict attrs: (optional) the attributes to the create the series
                           with
        :rtype: :class:`tempodb.response.Response` object"""

        body = protocol.make_series_key(key, tags, attrs)
        resp = self.session.post(endpoint.SERIES_ENDPOINT, body)
        return resp

    @with_response_type('Nothing')
    def delete_series(self, key=None, tag=None, attr=None,
                      allow_truncation=False):
        """Delete a series according to the given criteria.

        **Note:** for the key argument, the filter will return the *union* of
        those values.  For the tag and attr arguments, the filter will return
        the *intersection* of those values.

        :param key: filter by one or more series keys
        :type key: list or string
        :param tag: filter by one or more tags
        :type tag: list or string
        :param dict attr: filter by one or more key-value attributes
        :param bool allow_truncation: whether to allow full deletion of a
                                      database
        :rtype: :class:`tempodb.response.Response` object"""

        params = {
            'key': key,
            'tag': tag,
            'attr': attr,
            'allow_truncation': str(allow_truncation).lower()
        }
        url_args = endpoint.make_url_args(params)
        url = '?'.join([endpoint.SERIES_ENDPOINT, url_args])
        resp = self.session.delete(url)
        return resp

    @with_response_type('Series')
    def get_series(self, key):
        """Get a series object from TempoDB given its key.
<commit_msg>make sure series get returns cursor<commit_after>import functools
import urlparse
import urllib
import json
import endpoint
import protocol
from response import Response
from temporal.validate import check_time_param


def make_series_url(key):
    """Given a series key, generate a valid URL to the series endpoint for
    that key.

    :param string key: the series key
    :rtype: string"""

    url = urlparse.urljoin(endpoint.SERIES_ENDPOINT, 'key/')
    url = urlparse.urljoin(url, urllib.quote_plus(key))
    return url


class with_response_type(object):
    """Decorator for ensuring the Response object returned by the
    :class:`Client` object has a data attribute that corresponds to the
    object type expected from the TempoDB API.  This class should not be
    used by user code.

    The "t" argument should be a string corresponding to the name of a class
    from the :mod:`tempodb.protocol.objects` module, or a single element list
    with the element being the name of a class from that module if the API
    endpoint will return a list of those objects.

    :param t: the type of object to cast the TempoDB response to
    :type t: list or string"""

    def __init__(self, t):
        self.t = t

    def __call__(self, f, *args, **kwargs):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            resp = f(*args, **kwargs)
            #dont try this at home kids
            session = args[0].session
            resp_obj = Response(resp, session)
            if resp_obj.status == 200:
                resp_obj._cast_payload(self.t)
            return resp_obj
        return wrapper


class Client(object):
    """Entry point class into the TempoDB API.  The client should be
    initialized with your API key and secret obtained from your TempoDB
    login.

    The methods are grouped as follows:

    SERIES

        * :meth:`create_series`
        * :meth:`delete_series`
        * :meth:`get_series`
        * :meth:`list_series`
        * :meth:`update_series`

    READING DATA

        * :meth:`read_data`
        * :meth:`aggregate_data`

    WRITING DATA

        * :meth:`write_data`
        * :meth:`write_multi`

    DELETING

        * :meth:`delete`

    SINGLE VALUE

        * :meth:`single_value`
        * :meth:`multi_series_single_value`

    :param string key: your API key
    :param string secret: your API secret"""

    def __init__(self, key, secret, base_url=endpoint.BASE_URL):
        self.session = endpoint.HTTPEndpoint(key, secret, base_url)

    #SERIES METHODS
    @with_response_type('Nothing')
    def create_series(self, key=None, tags=[], attrs={}):
        """Create a new series with an optional string key.  A list of tags
        and a map of attributes can also be optionally supplied.

        :param string key: (optional) a string key for the series
        :param list tags: (optional) the tags to create the series with
        :param dict attrs: (optional) the attributes to the create the series
                           with
        :rtype: :class:`tempodb.response.Response` object"""

        body = protocol.make_series_key(key, tags, attrs)
        resp = self.session.post(endpoint.SERIES_ENDPOINT, body)
        return resp

    @with_response_type('Nothing')
    def delete_series(self, key=None, tag=None, attr=None,
                      allow_truncation=False):
        """Delete a series according to the given criteria.

        **Note:** for the key argument, the filter will return the *union* of
        those values.  For the tag and attr arguments, the filter will return
        the *intersection* of those values.

        :param key: filter by one or more series keys
        :type key: list or string
        :param tag: filter by one or more tags
        :type tag: list or string
        :param dict attr: filter by one or more key-value attributes
        :param bool allow_truncation: whether to allow full deletion of a
                                      database
        :rtype: :class:`tempodb.response.Response` object"""

        params = {
            'key': key,
            'tag': tag,
            'attr': attr,
            'allow_truncation': str(allow_truncation).lower()
        }
        url_args = endpoint.make_url_args(params)
        url = '?'.join([endpoint.SERIES_ENDPOINT, url_args])
        resp = self.session.delete(url)
        return resp

    @with_response_type('SeriesCursor')
    def get_series(self, key):
        """Get a series object from TempoDB given its key.

{'fraction_non_alphanumeric': 0.08396101019051838, 'fraction_numerical': 0.0008861320336730173, 'mean_word_length': 2.467357910906298, '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': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4180939', 'n_tokens_mistral': 2579, 'n_tokens_neox': 2470, 'n_words': 931}