Repository: auth0/auth0-python
Pull request #111 (2018-06-19 18:09:06)

Replace default mutable arguments with None
This PR fixes a possible issue with default mutable arguments. More info [here](https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html#anti-pattern).

The `request` library has as default `None` for all optional arguments so this is not a breaking change. [(source)](http://docs.python-requests.org/en/master/_modules/requests/api/?highlight=get)

Related PR: https://github.com/auth0/auth0-python/pull/93


Patch:
diff --git a/auth0/v3/authentication/passwordless.py b/auth0/v3/authentication/passwordless.py
index 0c9b5f7..91b4738 100644
--- a/auth0/v3/authentication/passwordless.py
+++ b/auth0/v3/authentication/passwordless.py
@@ -12,7 +12,7 @@ class Passwordless(AuthenticationBase):
     def __init__(self, domain):
         self.domain = domain
 
-    def email(self, client_id, email, send='link', auth_params={}):
+    def email(self, client_id, email, send='link', auth_params=None):
         """Start flow sending an email.
 
         Given the user email address, it will send an email with:
@@ -43,7 +43,7 @@ class Passwordless(AuthenticationBase):
                 'connection': 'email',
                 'email': email,
                 'send': send,
-                'authParams': auth_params,
+                'authParams': auth_params
             },
             headers={'Content-Type': 'application/json'}
         )
diff --git a/auth0/v3/management/clients.py b/auth0/v3/management/clients.py
index 04c48ce..81da393 100644
--- a/auth0/v3/management/clients.py
+++ b/auth0/v3/management/clients.py
@@ -24,7 +24,7 @@ class Clients(object):
             return url + '/' + id
         return url
 
-    def all(self, fields=[], include_fields=True, page=None, per_page=None, extra_params={}):
+    def all(self, fields=None, include_fields=True, page=None, per_page=None, extra_params=None):
         """Retrieves a list of all the applications.
 
         Important: The client_secret and encryption_key attributes can only be
@@ -46,8 +46,8 @@ class Clients(object):
              the request. The fields, include_fields, page and per_page values
              specified as parameters take precedence over the ones defined here.
         """
-        params = extra_params
-        params['fields'] = ','.join(fields) or None
+        params = extra_params or {}
+        params['fields'] = fields and ','.join(fields) or None
         params['include_fields'] = str(include_fields).lower()
         params['page'] = page
         params['per_page'] = per_page
@@ -64,7 +64,7 @@ class Clients(object):
 
         return self.client.post(self._url(), data=body)
 
-    def get(self, id, fields=[], include_fields=True):
+    def get(self, id, fields=None, include_fields=True):
         """Retrieves an application by its id.
 
         Important: The client_secret, encryption_key and signing_keys
@@ -81,7 +81,7 @@ class Clients(object):
               to be include in the result, False otherwise.
         """
 
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower()}
 
         return self.client.get(self._url(id), params=params)
diff --git a/auth0/v3/management/connections.py b/auth0/v3/management/connections.py
index 0206a72..3ef2ad7 100644
--- a/auth0/v3/management/connections.py
+++ b/auth0/v3/management/connections.py
@@ -23,7 +23,7 @@ class Connections(object):
             return url + '/' + id
         return url
 
-    def all(self, strategy=None, fields=[], include_fields=True, page=None, per_page=None, extra_params={}):
+    def all(self, strategy=None, fields=None, include_fields=True, page=None, per_page=None, extra_params=None):
         """Retrieves all connections.
 
         Args:
@@ -49,16 +49,16 @@ class Connections(object):
            A list of connection objects.
         """
         
-        params = extra_params
+        params = extra_params or {}
         params['strategy'] = strategy or None
-        params['fields'] = ','.join(fields) or None
+        params['fields'] = fields and ','.join(fields) or None
         params['include_fields'] = str(include_fields).lower()
         params['page'] = page
         params['per_page'] = per_page
 
         return self.client.get(self._url(), params=params)
 
-    def get(self, id, fields=[], include_fields=True):
+    def get(self, id, fields=None, include_fields=True):
         """Retrieve connection by id.
 
         Args:
@@ -75,7 +75,7 @@ class Connections(object):
             A connection object.
         """
 
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower()}
 
         return self.client.get(self._url(id), params=params)
diff --git a/auth0/v3/management/device_credentials.py b/auth0/v3/management/device_credentials.py
index c194b5e..348907f 100644
--- a/auth0/v3/management/device_credentials.py
+++ b/auth0/v3/management/device_credentials.py
@@ -24,7 +24,7 @@ class DeviceCredentials(object):
             return url + '/' + id
         return url
 
-    def get(self, user_id, client_id, type, fields=[], include_fields=True):
+    def get(self, user_id, client_id, type, fields=None, include_fields=True):
         """List device credentials.
 
         Args:
@@ -44,7 +44,7 @@ class DeviceCredentials(object):
         """
 
         params = {
-            'fields': ','.join(fields) or None,
+            'fields': fields and ','.join(fields) or None,
             'include_fields': str(include_fields).lower(),
             'user_id': user_id,
             'client_id': client_id,
diff --git a/auth0/v3/management/emails.py b/auth0/v3/management/emails.py
index 72f2c34..f85bad8 100644
--- a/auth0/v3/management/emails.py
+++ b/auth0/v3/management/emails.py
@@ -24,7 +24,7 @@ class Emails(object):
             return url + '/' + id
         return url
 
-    def get(self, fields=[], include_fields=True):
+    def get(self, fields=None, include_fields=True):
         """Get the email provider.
 
         Args:
@@ -35,7 +35,7 @@ class Emails(object):
             include_fields (bool, optional): True if the fields specified are
                 to be include in the result, False otherwise.
         """
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower()}
 
         return self.client.get(self._url(), params=params)
diff --git a/auth0/v3/management/rest.py b/auth0/v3/management/rest.py
index 9ff7626..b530308 100644
--- a/auth0/v3/management/rest.py
+++ b/auth0/v3/management/rest.py
@@ -47,7 +47,7 @@ class RestClient(object):
         else:
             self.base_headers = {}
 
-    def get(self, url, params={}):
+    def get(self, url, params=None):
         headers = self.base_headers.copy()
         headers.update({
             'Authorization': 'Bearer %s' % self.jwt,
@@ -56,17 +56,17 @@ class RestClient(object):
         response = requests.get(url, params=params, headers=headers)
         return self._process_response(response)
 
-    def post(self, url, data={}):
+    def post(self, url, data=None):
         headers = self.base_headers.copy()
         headers.update({
             'Authorization': 'Bearer %s' % self.jwt,
             'Content-Type': 'application/json'
         })
 
-        response = requests.post(url, data=json.dumps(data), headers=headers)
+        response = requests.post(url, data=json.dumps(data or {}), headers=headers)
         return self._process_response(response)
 
-    def file_post(self, url, data={}, files={}):
+    def file_post(self, url, data=None, files=None):
         headers = self.base_headers.copy()
         headers.pop('Content-Type', None)
         headers.update({
@@ -76,33 +76,33 @@ class RestClient(object):
         response = requests.post(url, data=data, files=files, headers=headers)
         return self._process_response(response)
 
-    def patch(self, url, data={}):
+    def patch(self, url, data=None):
         headers = self.base_headers.copy()
         headers.update({
             'Authorization': 'Bearer %s' % self.jwt,
             'Content-Type': 'application/json'
         })
 
-        response = requests.patch(url, data=json.dumps(data), headers=headers)
+        response = requests.patch(url, data=json.dumps(data or {}), headers=headers)
         return self._process_response(response)
 
-    def put(self, url, data={}):
+    def put(self, url, data=None):
         headers = self.base_headers.copy()
         headers.update({
             'Authorization': 'Bearer %s' % self.jwt,
             'Content-Type': 'application/json'
         })
 
-        response = requests.put(url, data=json.dumps(data), headers=headers)
+        response = requests.put(url, data=json.dumps(data or {}), headers=headers)
         return self._process_response(response)
 
-    def delete(self, url, params={}):
+    def delete(self, url, params=None):
         headers = self.base_headers.copy()
         headers.update({
             'Authorization': 'Bearer %s' % self.jwt,
         })
 
-        response = requests.delete(url, headers=headers, params=params)
+        response = requests.delete(url, headers=headers, params=params or {})
         return self._process_response(response)
 
     def _process_response(self, response):
diff --git a/auth0/v3/management/rules.py b/auth0/v3/management/rules.py
index a6e8d7c..1ab57b6 100644
--- a/auth0/v3/management/rules.py
+++ b/auth0/v3/management/rules.py
@@ -24,7 +24,7 @@ class Rules(object):
             return url + '/' + id
         return url
 
-    def all(self, stage='login_success', enabled=True, fields=[],
+    def all(self, stage='login_success', enabled=True, fields=None,
             include_fields=True):
         """Retrieves a list of all rules.
 
@@ -44,7 +44,7 @@ class Rules(object):
                 stage (defaults to login_success).
         """
 
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower(),
                   'stage': stage}
 
@@ -62,7 +62,7 @@ class Rules(object):
         """
         return self.client.post(self._url(), data=body)
 
-    def get(self, id, fields=[], include_fields=True):
+    def get(self, id, fields=None, include_fields=True):
         """Retrieves a rule by its ID.
 
         Args:
@@ -76,7 +76,7 @@ class Rules(object):
                 to be included in the result, False otherwise
                 (defaults to true).
         """
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower()}
         return self.client.get(self._url(id), params=params)
 
diff --git a/auth0/v3/management/tenants.py b/auth0/v3/management/tenants.py
index 3f57eb8..72a1b5e 100644
--- a/auth0/v3/management/tenants.py
+++ b/auth0/v3/management/tenants.py
@@ -21,7 +21,7 @@ class Tenants(object):
     def _url(self):
         return 'https://%s/api/v2/tenants/settings' % self.domain
 
-    def get(self, fields=[], include_fields=True):
+    def get(self, fields=None, include_fields=True):
         """Get tenant settings.
 
         Args:
@@ -33,7 +33,7 @@ class Tenants(object):
               to be include in the result, False otherwise.
         """
 
-        params = {'fields': ','.join(fields) or None,
+        params = {'fields': fields and ','.join(fields) or None,
                   'include_fields': str(include_fields).lower()}
 
         return self.client.get(self._url(), params=params)
diff --git a/auth0/v3/management/users.py b/auth0/v3/management/users.py
index a4b2092..f82425e 100644
--- a/auth0/v3/management/users.py
+++ b/auth0/v3/management/users.py
@@ -25,7 +25,7 @@ class Users(object):
         return url
 
     def list(self, page=0, per_page=25, sort=None, connection=None, q=None,
-             search_engine='v1', include_totals=True, fields=[],
+             search_engine='v1', include_totals=True, fields=None,
              include_fields=True):
         """List or search users.
 
@@ -59,7 +59,7 @@ class Users(object):
             'include_totals': str(include_totals).lower(),
             'sort': sort,
             'connection': connection,
-            'fields': ','.join(fields) or None,
+            'fields': fields and ','.join(fields) or None,
             'include_fields': str(include_fields).lower(),
             'q': q,
             'search_engine': search_engine
@@ -79,7 +79,7 @@ class Users(object):
         """
         return self.client.delete(self._url())
 
-    def get(self, id, fields=[], include_fields=True):
+    def get(self, id, fields=None, include_fields=True):
         """Get a user.
 
         Args:
@@ -93,7 +93,7 @@ class Users(object):
                 to be include in the result, False otherwise.
         """
         params = {
-            'fields': ','.join(fields) or None,
+            'fields': fields and ','.join(fields) or None,
             'include_fields': str(include_fields).lower()
         }
 


Test patch:
diff --git a/auth0/v3/test/management/test_rest.py b/auth0/v3/test/management/test_rest.py
index 6a7418b..2d067ed 100644
--- a/auth0/v3/test/management/test_rest.py
+++ b/auth0/v3/test/management/test_rest.py
@@ -16,7 +16,7 @@ class TestRest(unittest.TestCase):
         mock_get.return_value.status_code = 200
 
         response = rc.get('the-url')
-        mock_get.assert_called_with('the-url', params={}, headers=headers)
+        mock_get.assert_called_with('the-url', params=None, headers=headers)
 
         self.assertEqual(response, ['a', 'b'])