diff --git a/tools/lint-all b/tools/lint-all index 944302be7f..2c7f0854dd 100755 --- a/tools/lint-all +++ b/tools/lint-all @@ -278,6 +278,13 @@ python_rules = cast(RuleList, [ 'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, {'pattern': '([a-zA-Z0-9_]+)=REQ\([\'"]\\1[\'"]', 'description': 'REQ\'s first argument already defaults to parameter name'}, + {'pattern': 'self\.client\.(get|post|patch|put|delete)', + 'exclude': set(['zilencer/tests.py']), + 'description': \ +'''Do not call self.client directly for put/patch/post/get. +See WRAPPER_COMMENT in test_helpers.py for details. +'''}, + ]) + whitespace_rules bash_rules = [ {'pattern': '#!.*sh [-xe]', diff --git a/zerver/lib/test_helpers.py b/zerver/lib/test_helpers.py index 27171bdcb9..0579315924 100644 --- a/zerver/lib/test_helpers.py +++ b/zerver/lib/test_helpers.py @@ -196,12 +196,27 @@ class POSTRequestMock(object): self.META = {'PATH_INFO': 'test'} class AuthedTestCase(TestCase): - # Helper because self.client.patch annoying requires you to urlencode + ''' + WRAPPER_COMMENT: + + We wrap calls to self.client.{patch,put,get,post,delete} for various + reasons. Some of this has to do with fixing encodings before calling + into the Django code. Some of this has to do with providing a future + path for instrumentation. Some of it's just consistency. + + The linter will prevent direct calls to self.client.foo, so the wrapper + functions have to fake out the linter by using a local variable called + django_client to fool the regext. + ''' def client_patch(self, url, info={}, **kwargs): # type: (text_type, Dict[str, Any], **Any) -> HttpResponse + """ + We need to urlencode, since Django's function won't do it for us. + """ encoded = urllib.parse.urlencode(info) - return self.client.patch(url, encoded, **kwargs) + django_client = self.client # see WRAPPER_COMMENT + return django_client.patch(url, encoded, **kwargs) def client_patch_multipart(self, url, info={}, **kwargs): # type: (text_type, Dict[str, Any], **Any) -> HttpResponse @@ -214,7 +229,8 @@ class AuthedTestCase(TestCase): automatically, but not patch.) """ encoded = encode_multipart(BOUNDARY, info) - return self.client.patch( + django_client = self.client # see WRAPPER_COMMENT + return django_client.patch( url, encoded, content_type=MULTIPART_CONTENT, @@ -223,18 +239,22 @@ class AuthedTestCase(TestCase): def client_put(self, url, info={}, **kwargs): # type: (text_type, Dict[str, Any], **Any) -> HttpResponse encoded = urllib.parse.urlencode(info) - return self.client.put(url, encoded, **kwargs) + django_client = self.client # see WRAPPER_COMMENT + return django_client.put(url, encoded, **kwargs) def client_delete(self, url, info={}, **kwargs): # type: (text_type, Dict[str, Any], **Any) -> HttpResponse encoded = urllib.parse.urlencode(info) - return self.client.delete(url, encoded, **kwargs) + django_client = self.client # see WRAPPER_COMMENT + return django_client.delete(url, encoded, **kwargs) def client_post(self, url, info={}, **kwargs): - return self.client.post(url, info, **kwargs) + django_client = self.client # see WRAPPER_COMMENT + return django_client.post(url, info, **kwargs) def client_get(self, url, info={}, **kwargs): - return self.client.get(url, info, **kwargs) + django_client = self.client # see WRAPPER_COMMENT + return django_client.get(url, info, **kwargs) def login_with_return(self, email, password=None): # type: (text_type, Optional[text_type]) -> HttpResponse