Repository: python-gitlab/python-gitlab

Issue:
Allow to set a global per_page in the configuration
## Description of the problem, including code/CLI snippet
This is a feature request.

I would like to be able to set per_page globally in the configuration file instead of setting it for each request.

## Expected Behavior
Setting per_page in http://python-gitlab.readthedocs.io/en/stable/cli.html#configuration would set per page for all request with the configured gitlab.Gitlab instance

## Actual Behavior
per_page must be given everytime in ListMixin.list.

## Specifications

The setting could then be defined in:
  - the configuration file
  - gitlab.Gitlab constructor
  - ListMixin.list


  - python-gitlab version: python-gitlab==1.4.0
  - API version you are using (v3/v4): v4
  - Gitlab server version (or gitlab.com): 9.4.5-ee



Discussion:

Resolving patch:
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -70,7 +70,7 @@ class Gitlab(object):
     def __init__(self, url, private_token=None, oauth_token=None, email=None,
                  password=None, ssl_verify=True, http_username=None,
                  http_password=None, timeout=None, api_version='4',
-                 session=None):
+                 session=None, per_page=None):
 
         self._api_version = str(api_version)
         self._server_version = self._server_revision = None
@@ -97,6 +97,8 @@ def __init__(self, url, private_token=None, oauth_token=None, email=None,
         #: Create a session object for requests
         self.session = session or requests.Session()
 
+        self.per_page = per_page
+
         objects = importlib.import_module('gitlab.v%s.objects' %
                                           self._api_version)
         self._objects = objects
@@ -177,7 +179,8 @@ def from_config(gitlab_id=None, config_files=None):
                       ssl_verify=config.ssl_verify, timeout=config.timeout,
                       http_username=config.http_username,
                       http_password=config.http_password,
-                      api_version=config.api_version)
+                      api_version=config.api_version,
+                      per_page=config.per_page)
 
     def auth(self):
         """Performs an authentication.
diff --git a/gitlab/config.py b/gitlab/config.py
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -140,3 +140,13 @@ def __init__(self, gitlab_id=None, config_files=None):
         if self.api_version not in ('4',):
             raise GitlabDataError("Unsupported API version: %s" %
                                   self.api_version)
+
+        self.per_page = None
+        for section in ['global', self.gitlab_id]:
+            try:
+                self.per_page = self._config.getint(section, 'per_page')
+            except Exception:
+                pass
+        if self.per_page is not None and not 0 <= self.per_page <= 100:
+            raise GitlabDataError("Unsupported per_page number: %s" %
+                                  self.per_page)
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
--- a/gitlab/mixins.py
+++ b/gitlab/mixins.py
@@ -114,6 +114,8 @@ def list(self, **kwargs):
 
         # Duplicate data to avoid messing with what the user sent us
         data = kwargs.copy()
+        if self.gitlab.per_page:
+            data.setdefault('per_page', self.gitlab.per_page)
 
         # We get the attributes that need some special transformation
         types = getattr(self, '_types', {})


Test patch:
diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py
--- a/gitlab/tests/test_config.py
+++ b/gitlab/tests/test_config.py
@@ -45,6 +45,7 @@
 url = https://three.url
 private_token = MNOPQR
 ssl_verify = /path/to/CA/bundle.crt
+per_page = 50
 
 [four]
 url = https://four.url
@@ -66,6 +67,11 @@
 
 [three]
 meh = hem
+
+[four]
+url = http://four.url
+private_token = ABCDEF
+per_page = 200
 """
 
 
@@ -87,13 +93,19 @@ def test_invalid_id(self, m_open):
     @mock.patch('six.moves.builtins.open')
     def test_invalid_data(self, m_open):
         fd = six.StringIO(missing_attr_config)
-        fd.close = mock.Mock(return_value=None)
+        fd.close = mock.Mock(return_value=None,
+                             side_effect=lambda: fd.seek(0))
         m_open.return_value = fd
         config.GitlabConfigParser('one')
+        config.GitlabConfigParser('one')
         self.assertRaises(config.GitlabDataError, config.GitlabConfigParser,
                           gitlab_id='two')
         self.assertRaises(config.GitlabDataError, config.GitlabConfigParser,
                           gitlab_id='three')
+        with self.assertRaises(config.GitlabDataError) as emgr:
+            config.GitlabConfigParser('four')
+        self.assertEqual('Unsupported per_page number: 200',
+                         emgr.exception.args[0])
 
     @mock.patch('six.moves.builtins.open')
     def test_valid_data(self, m_open):
@@ -108,6 +120,7 @@ def test_valid_data(self, m_open):
         self.assertEqual(None, cp.oauth_token)
         self.assertEqual(2, cp.timeout)
         self.assertEqual(True, cp.ssl_verify)
+        self.assertIsNone(cp.per_page)
 
         fd = six.StringIO(valid_config)
         fd.close = mock.Mock(return_value=None)
@@ -130,6 +143,7 @@ def test_valid_data(self, m_open):
         self.assertEqual(None, cp.oauth_token)
         self.assertEqual(2, cp.timeout)
         self.assertEqual("/path/to/CA/bundle.crt", cp.ssl_verify)
+        self.assertEqual(50, cp.per_page)
 
         fd = six.StringIO(valid_config)
         fd.close = mock.Mock(return_value=None)