@@ -106,6 +106,14 @@ class documentation for more information.
106106DOCSTRING_START_REGEX = re .compile (r'^u?r?(?P<kind>["\']{3})' )
107107ENABLE_REGEX = re .compile (r'# *(fmt|autopep8): *on' )
108108DISABLE_REGEX = re .compile (r'# *(fmt|autopep8): *off' )
109+ ENCODING_MAGIC_COMMENT = re .compile (
110+ r'^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)'
111+ )
112+ COMPARE_TYPE_REGEX = re .compile (
113+ r'([=!]=)\s+type(?:\s*\(\s*([^)]*[^ )])\s*\))'
114+ r'|\btype(?:\s*\(\s*([^)]*[^ )])\s*\))\s+([=!]=)'
115+ )
116+ TYPE_REGEX = re .compile (r'(type\s*\(\s*[^)]*?[^\s)]\s*\))' )
109117
110118EXIT_CODE_OK = 0
111119EXIT_CODE_ERROR = 1
@@ -129,25 +137,6 @@ class documentation for more information.
129137# to be enabled, disable both of them
130138CONFLICTING_CODES = ('W503' , 'W504' )
131139
132- # W602 is handled separately due to the need to avoid "with_traceback".
133- CODE_TO_2TO3 = {
134- 'E231' : ['ws_comma' ],
135- 'E721' : ['idioms' ],
136- 'W690' : ['apply' ,
137- 'except' ,
138- 'exitfunc' ,
139- 'numliterals' ,
140- 'operator' ,
141- 'paren' ,
142- 'reduce' ,
143- 'renames' ,
144- 'standarderror' ,
145- 'sys_exc' ,
146- 'throw' ,
147- 'tuple_params' ,
148- 'xreadlines' ]}
149-
150-
151140if sys .platform == 'win32' : # pragma: no cover
152141 DEFAULT_CONFIG = os .path .expanduser (r'~\.pycodestyle' )
153142else :
@@ -175,16 +164,31 @@ def open_with_encoding(filename, mode='r', encoding=None, limit_byte_check=-1):
175164 newline = '' ) # Preserve line endings
176165
177166
167+ def _detect_encoding_from_file (filename : str ):
168+ try :
169+ with open (filename ) as input_file :
170+ for idx , line in enumerate (input_file ):
171+ if idx == 0 and line [0 ] == '\ufeff ' :
172+ return "utf-8-sig"
173+ if idx >= 2 :
174+ break
175+ match = ENCODING_MAGIC_COMMENT .search (line )
176+ if match :
177+ return match .groups ()[0 ]
178+ except Exception :
179+ pass
180+ # Python3's default encoding
181+ return 'utf-8'
182+
183+
178184def detect_encoding (filename , limit_byte_check = - 1 ):
179185 """Return file encoding."""
186+ encoding = _detect_encoding_from_file (filename )
187+ if encoding == "utf-8-sig" :
188+ return encoding
180189 try :
181- with open (filename , 'rb' ) as input_file :
182- from lib2to3 .pgen2 import tokenize as lib2to3_tokenize
183- encoding = lib2to3_tokenize .detect_encoding (input_file .readline )[0 ]
184-
185190 with open_with_encoding (filename , encoding = encoding ) as test_file :
186191 test_file .read (limit_byte_check )
187-
188192 return encoding
189193 except (LookupError , SyntaxError , UnicodeDecodeError ):
190194 return 'latin-1'
@@ -449,7 +453,7 @@ class FixPEP8(object):
449453 - e502
450454 - e701,e702,e703,e704
451455 - e711,e712,e713,e714
452- - e722
456+ - e721, e722
453457 - e731
454458 - w291
455459 - w503,504
@@ -1262,6 +1266,58 @@ def fix_e714(self, result):
12621266 new_target [:pos_start ], 'is not' , new_target [pos_end :])
12631267 self .source [line_index ] = new_target
12641268
1269+ def fix_e721 (self , result ):
1270+ """fix comparison type"""
1271+ (line_index , _ , target ) = get_index_offset_contents (result ,
1272+ self .source )
1273+ match = COMPARE_TYPE_REGEX .search (target )
1274+ if match :
1275+ # NOTE: match objects
1276+ # * type(a) == type(b) -> (None, None, 'a', '==')
1277+ # * str == type(b) -> ('==', 'b', None, None)
1278+ # * type("") != type(b) -> (None, None, '""', '!=')
1279+ start = match .start ()
1280+ end = match .end ()
1281+ _prefix = ""
1282+ _suffix = ""
1283+ first_match_type_obj = match .groups ()[1 ]
1284+ if first_match_type_obj is None :
1285+ _target_obj = match .groups ()[2 ]
1286+ else :
1287+ _target_obj = match .groups ()[1 ]
1288+ _suffix = target [end :]
1289+
1290+ isinstance_stmt = " isinstance"
1291+ is_not_condition = (
1292+ match .groups ()[0 ] == "!=" or match .groups ()[3 ] == "!="
1293+ )
1294+ if is_not_condition :
1295+ isinstance_stmt = " not isinstance"
1296+
1297+ _type_comp = f"{ _target_obj } , { target [:start ]} "
1298+
1299+ _prefix_tmp = target [:start ].split ()
1300+ if len (_prefix_tmp ) >= 1 :
1301+ _type_comp = f"{ _target_obj } , { target [:start ]} "
1302+ if first_match_type_obj is not None :
1303+ _prefix = " " .join (_prefix_tmp [:- 1 ])
1304+ _type_comp = f"{ _target_obj } , { _prefix_tmp [- 1 ]} "
1305+ else :
1306+ _prefix = " " .join (_prefix_tmp )
1307+
1308+ _suffix_tmp = target [end :]
1309+ _suffix_type_match = TYPE_REGEX .search (_suffix_tmp )
1310+ if len (_suffix_tmp .split ()) >= 1 and _suffix_type_match :
1311+ if _suffix_type_match :
1312+ type_match_end = _suffix_type_match .end ()
1313+ _suffix = _suffix_tmp [type_match_end :]
1314+ if _suffix_type_match :
1315+ cmp_b = _suffix_type_match .groups ()[0 ]
1316+ _type_comp = f"{ _target_obj } , { cmp_b } "
1317+
1318+ fix_line = f"{ _prefix } { isinstance_stmt } ({ _type_comp } ){ _suffix } "
1319+ self .source [line_index ] = fix_line
1320+
12651321 def fix_e722 (self , result ):
12661322 """fix bare except"""
12671323 (line_index , _ , target ) = get_index_offset_contents (result ,
@@ -1717,69 +1773,6 @@ def split_and_strip_non_empty_lines(text):
17171773 return [line .strip () for line in text .splitlines () if line .strip ()]
17181774
17191775
1720- def refactor (source , fixer_names , ignore = None , filename = '' ):
1721- """Return refactored code using lib2to3.
1722-
1723- Skip if ignore string is produced in the refactored code.
1724-
1725- """
1726- not_found_end_of_file_newline = source and source .rstrip ("\r \n " ) == source
1727- if not_found_end_of_file_newline :
1728- input_source = source + "\n "
1729- else :
1730- input_source = source
1731-
1732- from lib2to3 import pgen2
1733- try :
1734- new_text = refactor_with_2to3 (input_source ,
1735- fixer_names = fixer_names ,
1736- filename = filename )
1737- except (pgen2 .parse .ParseError ,
1738- SyntaxError ,
1739- UnicodeDecodeError ,
1740- UnicodeEncodeError ):
1741- return source
1742-
1743- if ignore :
1744- if ignore in new_text and ignore not in source :
1745- return source
1746-
1747- if not_found_end_of_file_newline :
1748- return new_text .rstrip ("\r \n " )
1749-
1750- return new_text
1751-
1752-
1753- def code_to_2to3 (select , ignore , where = '' , verbose = False ):
1754- fixes = set ()
1755- for code , fix in CODE_TO_2TO3 .items ():
1756- if code_match (code , select = select , ignore = ignore ):
1757- if verbose :
1758- print ('---> Applying {} fix for {}' .format (where ,
1759- code .upper ()),
1760- file = sys .stderr )
1761- fixes |= set (fix )
1762- return fixes
1763-
1764-
1765- def fix_2to3 (source ,
1766- aggressive = True , select = None , ignore = None , filename = '' ,
1767- where = 'global' , verbose = False ):
1768- """Fix various deprecated code (via lib2to3)."""
1769- if not aggressive :
1770- return source
1771-
1772- select = select or []
1773- ignore = ignore or []
1774-
1775- return refactor (source ,
1776- code_to_2to3 (select = select ,
1777- ignore = ignore ,
1778- where = where ,
1779- verbose = verbose ),
1780- filename = filename )
1781-
1782-
17831776def find_newline (source ):
17841777 """Return type of newline used in source.
17851778
@@ -3175,24 +3168,6 @@ def _leading_space_count(line):
31753168 return i
31763169
31773170
3178- def refactor_with_2to3 (source_text , fixer_names , filename = '' ):
3179- """Use lib2to3 to refactor the source.
3180-
3181- Return the refactored source code.
3182-
3183- """
3184- from lib2to3 .refactor import RefactoringTool
3185- fixers = ['lib2to3.fixes.fix_' + name for name in fixer_names ]
3186- tool = RefactoringTool (fixer_names = fixers , explicit = fixers )
3187-
3188- from lib2to3 .pgen2 import tokenize as lib2to3_tokenize
3189- try :
3190- # The name parameter is necessary particularly for the "import" fixer.
3191- return str (tool .refactor_string (source_text , name = filename ))
3192- except lib2to3_tokenize .TokenError :
3193- return source_text
3194-
3195-
31963171def check_syntax (code ):
31973172 """Return True if syntax is okay."""
31983173 try :
@@ -3685,14 +3660,6 @@ def apply_global_fixes(source, options, where='global', filename='',
36853660 source = function (source ,
36863661 aggressive = options .aggressive )
36873662
3688- source = fix_2to3 (source ,
3689- aggressive = options .aggressive ,
3690- select = options .select ,
3691- ignore = options .ignore ,
3692- filename = filename ,
3693- where = where ,
3694- verbose = options .verbose )
3695-
36963663 return source
36973664
36983665
@@ -4127,10 +4094,6 @@ def supported_fixes():
41274094 yield (code .upper () + (4 - len (code )) * ' ' ,
41284095 re .sub (r'\s+' , ' ' , docstring_summary (function .__doc__ )))
41294096
4130- for code in sorted (CODE_TO_2TO3 ):
4131- yield (code .upper () + (4 - len (code )) * ' ' ,
4132- re .sub (r'\s+' , ' ' , docstring_summary (fix_2to3 .__doc__ )))
4133-
41344097
41354098def docstring_summary (docstring ):
41364099 """Return summary of docstring."""
0 commit comments