Skip to content

Commit

Permalink
Add suggestions for typos related to __future__
Browse files Browse the repository at this point in the history
  • Loading branch information
SylvainDe committed Apr 14, 2015
1 parent 1d02dcf commit 2d5b468
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 29 deletions.
54 changes: 29 additions & 25 deletions didyoumean/didyoumean.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import inspect
from didyoumean_re import UNBOUNDERROR_RE, NAMENOTDEFINED_RE,\
ATTRIBUTEERROR_RE, UNSUBSCRIBTABLE_RE, UNEXPECTED_KEYWORDARG_RE,\
NOMODULE_RE, CANNOTIMPORT_RE, INVALID_COMP_RE, OUTSIDE_FUNCTION_RE
NOMODULE_RE, CANNOTIMPORT_RE, INVALID_COMP_RE, OUTSIDE_FUNCTION_RE,\
FUTURE_FEATURE_NOT_DEF_RE

#: Standard modules we'll consider while searching for undefined values
# To be completed
STAND_MODULES = set(['string', 'os', 'sys', 're', 'math', 'random',
'datetime', 'timeit', 'unittest', 'itertools',
'functools'])
'functools', '__future__'])

#: Almost synonyms methods that can be confused from one type to another
# To be completed
Expand Down Expand Up @@ -221,11 +222,10 @@ def get_import_error_sugg(type_, value, frame):
if match:
module_str, = match.groups()
return get_module_name_suggestion(module_str)
else:
match = re.match(CANNOTIMPORT_RE, error_msg)
if match:
imported_name, = match.groups()
return get_imported_name_suggestion(imported_name, frame)
match = re.match(CANNOTIMPORT_RE, error_msg)
if match:
imported_name, = match.groups()
return get_imported_name_suggestion(imported_name, frame)
return []


Expand All @@ -237,15 +237,15 @@ def get_module_name_suggestion(module_str):

def get_imported_name_suggestion(imported_name, frame):
"""Get the suggestions closest to the failing import."""
module_name = frame.f_code.co_names[0]
return itertools.chain(
suggest_imported_name_as_typo(imported_name, frame),
suggest_imported_name_as_typo(imported_name, module_name, frame),
suggest_import_from_module(imported_name, frame))


def suggest_imported_name_as_typo(imported_name, frame):
def suggest_imported_name_as_typo(imported_name, module_name, frame):
"""Suggest that imported name could be a typo from actual name in module.
Example: 'from math import pie' -> 'from math import pi'."""
module_name = frame.f_code.co_names[0]
return get_close_matches(
imported_name,
dir(import_from_frame(module_name, frame)))
Expand Down Expand Up @@ -282,25 +282,29 @@ def get_type_error_sugg(type_, value, frame):


# Functions related to SyntaxError
def get_syntax_error_sugg(type_, value, _):
def get_syntax_error_sugg(type_, value, frame):
"""Get suggestions for SyntaxError exception."""
assert issubclass(type_, SyntaxError)
error_msg = value.args[0]
if re.match(INVALID_COMP_RE, error_msg):
match = re.match(OUTSIDE_FUNCTION_RE, error_msg)
if match:
yield "to indent it"
word, = match.groups()
if word == 'return':
yield "'sys.exit([arg])'"
match = re.match(FUTURE_FEATURE_NOT_DEF_RE, error_msg)
if match:
feature, = match.groups()
for m in suggest_imported_name_as_typo(feature, '__future__', frame):
yield m
match = re.match(INVALID_COMP_RE, error_msg)
if match:
yield quote('!=')
else:
match = re.match(OUTSIDE_FUNCTION_RE, error_msg)
if match:
yield "to indent it"
word, = match.groups()
if word == 'return':
yield "'sys.exit([arg])'"
else:
offset = value.offset
if offset is not None and offset > 2:
two_last = value.text[offset - 2:offset]
if two_last == '<>':
yield quote('!=')
offset = value.offset
if offset is not None and offset > 2:
two_last = value.text[offset - 2:offset]
if two_last == '<>':
yield quote('!=')


# Functions related to ValueError
Expand Down
14 changes: 10 additions & 4 deletions didyoumean/didyoumean_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,11 @@ def test_no_module4(self):

def test_import_future_nomodule(self):
""" Should be '__future__' ."""
code = 'from __future_ import division'
self.throws(code, NOMODULE)
code = 'import {0}'
typo, sugg = '__future_', '__future__'
bad_code, good_code = format_str(code, typo, sugg)
self.throws(bad_code, NOMODULE, "'" + sugg + "'")
self.runs(good_code)

def test_no_name_no_sugg(self):
"""No suggestion."""
Expand Down Expand Up @@ -821,8 +824,11 @@ def test_import_future_not_first(self):

def test_import_future_not_def(self):
""" Should be 'division' ."""
code = 'from __future__ import divisio'
self.throws(code, FUTFEATNOTDEF)
code = 'from __future__ import {0}'
typo, sugg = 'divisio', 'division'
bad_code, good_code = format_str(code, typo, sugg)
self.throws(bad_code, FUTFEATNOTDEF, "'" + sugg + "'")
self.runs(good_code)


class ValueErrorTests(AbstractTests):
Expand Down

0 comments on commit 2d5b468

Please sign in to comment.