aboutsummaryrefslogtreecommitdiffstats
path: root/pygithub3/core/result/smart.py
blob: 0343a9b257024314787f9752eb1d7d9fb24155dd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

from . import base
from .link import Link


class Method(base.Method):
    """ Lazy and cache support """

    def cached(func):
        """ Decorator to don't do a request if it's cached """
        def wrapper(self, page=1):
            if str(page) in self.cache:
                return self.cache[str(page)]
            return func(self, page)
        return wrapper

    def if_needs_lastpage(func):
        """ Decorator to set last page only if it can and it hasn't retrieved
        before """
        def wrapper(self, has_link):
            has_last_page = hasattr(self, 'last_page')
            if not has_last_page and has_link:
                return func(self, has_link)
            elif not has_last_page and not has_link:
                self.last_page = 1
        return wrapper

    @if_needs_lastpage
    def __set_last_page_from(self, link_header):
        """ Get and set last_page form link header """
        link = Link(link_header)
        self.last_page = int(link.last.params.get('page'))

    @cached
    def __call__(self, page=1):
        """ Call a real request """
        response = self.method(page=page)
        self.__set_last_page_from(response.headers.get('link'))
        self.cache[str(page)] = self.resource.loads(response.content)
        return self.cache[str(page)]

    @property
    def last(self):
        if not hasattr(self, 'last_page'):
            self()
        return self.last_page


class Result(base.Result):
    """
    It's a very **lazy** paginator beacuse only do a real request
    when is needed, besides it's **cached**, so never repeats a request.

    You have several ways to consume it

    #. Iterating over the result::

        result = some_request()
        for page in result:
            for resource in page:
                print resource

    #. With a generator::

        result = some_request()
        for resource in result.iterator():
            print resource

    #. As a list::

        result = some_request()
        print result.all()

    #. Also you can request some page manually

        Each ``Page`` is an iterator and contains resources::

            result = some_request()
            assert result.pages > 3
            page3 = result.get_page(3)
            page3_resources = list(page3)
    """

    def __init__(self, method):
        super(Result, self).__init__(method)
        self.page = base.Page(self.getter)

    def __next__(self):
        if self.page <= self.pages:
            page_to_return = self.page
            self.page = base.Page(self.getter, page_to_return + 1)
            return page_to_return
        self.page = base.Page(self.getter)
        raise StopIteration

    @property
    def pages(self):
        """ Total number of pages in request """
        return self.getter.last

    def get_page(self, page):
        """ Get ``Page`` of resources

        :param int page: Page number
        """
        if page in xrange(1, self.pages + 1):
            return base.Page(self.getter, page)
        return None