Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit da7d5b9

Browse files
committed
Make empty input code a non fatal error
- Return an AST for the module for empty files - Dont lose the AST on errors that produced it anyway - Updated tests, improved typing
1 parent 56f549a commit da7d5b9

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

native/python_package/python_driver/requestprocessor.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pydetector import detector
55
from traceback import format_exc
66
from python_driver.version import __version__
7-
from typing import (Any, IO, NewType, Tuple, cast, List, Iterator, Dict)
7+
from typing import (Any, IO, NewType, Tuple, cast, List, Iterator, Dict, Optional)
88

99
# typing.AnyStr is bugged on this version of MyPy, so...:
1010
AnyStr = Any
@@ -30,6 +30,14 @@ class RequestCheckException(Exception):
3030
"""
3131
pass
3232

33+
class EmptyCodeException(Exception):
34+
"""
35+
Exception produced when the input code is empty. This should
36+
generate an error (because we can't parse anything) but not
37+
a fatal one (e. g. empty __init__.py files)
38+
"""
39+
pass
40+
3341

3442
def asstr(txt: AnyStr) -> str:
3543
"""
@@ -67,7 +75,7 @@ def _tostr_request(self, request: RawRequest) -> Request:
6775
"""
6876
pass
6977

70-
def _check_input_request(self, request: RawRequest) -> Tuple[str, str]:
78+
def _parse_input_request(self, request: RawRequest) -> Tuple[str, str]:
7179
"""
7280
Check the incoming request package and validate that the 'content' and
7381
'language' keys are not missing and that 'language' and 'language_version'
@@ -77,13 +85,13 @@ def _check_input_request(self, request: RawRequest) -> Tuple[str, str]:
7785
:param request: The incoming request, already deserialized.
7886
7987
.. raises::
80-
RequestCheckException if the request failed to validate.
88+
EmptyCodeException if the code was empty.
8189
"""
8290
str_request = self._tostr_request(request)
8391
code = asstr(str_request.get('content', ''))
8492

8593
if not code:
86-
raise RequestCheckException('Bad input message, missing content')
94+
raise EmptyCodeException('Bad input message, missing content')
8795

8896
return code, asstr(str_request.get('filepath', ''))
8997

@@ -99,7 +107,8 @@ def _send_response(self, response: Response) -> None:
99107
"""
100108
pass
101109

102-
def _return_error(self, filepath: AnyStr='', status: AnyStr='error') -> None:
110+
def _return_error(self, filepath: AnyStr='', status: AnyStr='error',
111+
ast: Optional[Dict[Any, Any]] = None) -> None:
103112
"""
104113
Build and send to stdout and error response. Also log
105114
the errors to the python_driver.log.
@@ -109,11 +118,24 @@ def _return_error(self, filepath: AnyStr='', status: AnyStr='error') -> None:
109118
:param status: error type, 'error' or 'fatal'
110119
"""
111120

121+
if status == 'fatal':
122+
ret_ast = None
123+
elif ast:
124+
ret_ast = ast
125+
else:
126+
# Empty modules are still modules
127+
ret_ast = {"PY3AST": {
128+
"ast_type" : "Module",
129+
"lineno" : 1,
130+
"col_offset" : 1,
131+
}}
132+
112133
logging.error('Filepath: {}, Errors: {}'.format(filepath, self.errors))
113134
response = Response({
114135
'status': status,
115136
'errors': self.errors,
116137
'driver': 'python23:%s' % __version__,
138+
'ast': ret_ast,
117139
})
118140
if filepath:
119141
response['filepath'] = filepath
@@ -134,7 +156,7 @@ def process_request(self, request: RawRequest) -> None:
134156
self.errors = []
135157

136158
try:
137-
code, filepath = self._check_input_request(request)
159+
code, filepath = self._parse_input_request(request)
138160

139161
# We want the code detection to be fast and we prefer Python3 AST so using
140162
# the stop_on_ok_ast will avoid running a Python2 subprocess to check the
@@ -168,6 +190,10 @@ def process_request(self, request: RawRequest) -> None:
168190

169191
self._send_response(response)
170192

193+
except EmptyCodeException:
194+
self.errors.append('Code field empty')
195+
self._return_error(filepath, status='error', ast=ast)
196+
171197
except:
172198
status = 'fatal' if ast is None else 'error'
173199
self.errors.append(format_exc())

native/python_package/test/test_python_driver.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from python_driver import __version__, get_processor_instance
1919
from python_driver.requestprocessor import (
2020
Request, Response, RequestProcessorJSON,
21-
InBuffer, RequestCheckException
21+
InBuffer, RequestCheckException, EmptyCodeException
2222
)
2323

2424
if TEST_MSGPACK:
@@ -251,16 +251,16 @@ def test_10_check_input(self) -> None:
251251
self._restart_data('json')
252252
brequest = convert_bytes(self.data, to_bytes=True)
253253
processor = RequestProcessorMSGPack(self.recvbuffer)
254-
res = processor._check_input_request(brequest)
254+
res = processor._parse_input_request(brequest)
255255
self.assertEqual(res[1], 'test.py')
256256

257257
def test_20_check_input_bad(self) -> None:
258258
self._restart_data('msgpack')
259259
del self.data['content']
260260
brequest = convert_bytes(self.data, to_bytes=True)
261261
processor = RequestProcessorMSGPack(self.recvbuffer)
262-
with self.assertRaises(RequestCheckException) as _: # noqa: F841
263-
processor._check_input_request(brequest)
262+
with self.assertRaises(EmptyCodeException) as _: # noqa: F841
263+
processor._parse_input_request(brequest)
264264

265265
def test_30_send_response_msgpack(self) -> None:
266266
self._restart_data('msgpack')
@@ -290,6 +290,7 @@ def test_50_return_error(self) -> None:
290290
self.assertDictEqual(res[0] , {'driver': 'python23:%s' % __version__,
291291
'errors': ['test error'],
292292
'filepath': 'test.py',
293+
'ast': None,
293294
'status': 'fatal'})
294295

295296

0 commit comments

Comments
 (0)