#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Werkzeug Import Rewriter
~~~~~~~~~~~~~~~~~~~~~~~~
Changes the deprecated werkzeug imports to the full canonical imports.
This is a terrible hack, don't trust the diff untested.
:copyright: (c) 2014 by the Werkzeug Team.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
import sys
import os
import re
import posixpath
import difflib
_from_import_re = re.compile(r'(\s*(>>>|\.\.\.)?\s*)from werkzeug import\s+')
_direct_usage = re.compile('(?<!`)(werkzeug\.)([a-zA-Z_][a-zA-Z0-9_]+)')
# not necessarily in sync with current werkzeug/__init__.py
all_by_module = {
'werkzeug.debug': ['DebuggedApplication'],
'werkzeug.local': ['Local', 'LocalManager', 'LocalProxy', 'LocalStack',
'release_local'],
'werkzeug.serving': ['run_simple'],
'werkzeug.test': ['Client', 'EnvironBuilder', 'create_environ',
'run_wsgi_app'],
'werkzeug.testapp': ['test_app'],
'werkzeug.exceptions': ['abort', 'Aborter'],
'werkzeug.urls': ['url_decode', 'url_encode', 'url_quote',
'url_quote_plus', 'url_unquote', 'url_unquote_plus',
'url_fix', 'Href', 'iri_to_uri', 'uri_to_iri'],
'werkzeug.formparser': ['parse_form_data'],
'werkzeug.utils': ['escape', 'environ_property', 'append_slash_redirect',
'redirect', 'cached_property', 'import_string',
'dump_cookie', 'parse_cookie', 'unescape',
'format_string', 'find_modules', 'header_property',
'html', 'xhtml', 'HTMLBuilder', 'validate_arguments',
'ArgumentValidationError', 'bind_arguments',
'secure_filename'],
'werkzeug.wsgi': ['get_current_url', 'get_host', 'pop_path_info',
'peek_path_info', 'SharedDataMiddleware',
'DispatcherMiddleware', 'ClosingIterator', 'FileWrapper',
'make_line_iter', 'LimitedStream', 'responder',
'wrap_file', 'extract_path_info'],
'werkzeug.datastructures': ['MultiDict', 'CombinedMultiDict', 'Headers',
'EnvironHeaders', 'ImmutableList',
'ImmutableDict', 'ImmutableMultiDict',
'TypeConversionDict',
'ImmutableTypeConversionDict', 'Accept',
'MIMEAccept', 'CharsetAccept',
'LanguageAccept', 'RequestCacheControl',
'ResponseCacheControl', 'ETags', 'HeaderSet',
'WWWAuthenticate', 'Authorization',
'FileMultiDict', 'CallbackDict', 'FileStorage',
'OrderedMultiDict', 'ImmutableOrderedMultiDict'
],
'werkzeug.useragents': ['UserAgent'],
'werkzeug.http': ['parse_etags', 'parse_date', 'http_date', 'cookie_date',
'parse_cache_control_header', 'is_resource_modified',
'parse_accept_header', 'parse_set_header', 'quote_etag',
'unquote_etag', 'generate_etag', 'dump_header',
'parse_list_header', 'parse_dict_header',
'parse_authorization_header',
'parse_www_authenticate_header', 'remove_entity_headers',
'is_entity_header', 'remove_hop_by_hop_headers',
'parse_options_header', 'dump_options_header',
'is_hop_by_hop_header', 'unquote_header_value',
'quote_header_value', 'HTTP_STATUS_CODES'],
'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request', 'Response',
'AcceptMixin', 'ETagRequestMixin',
'ETagResponseMixin', 'ResponseStreamMixin',
'CommonResponseDescriptorsMixin', 'UserAgentMixin',
'AuthorizationMixin', 'WWWAuthenticateMixin',
'CommonRequestDescriptorsMixin'],
'werkzeug.security': ['generate_password_hash', 'check_password_hash'],
# the undocumented easteregg ;-)
'werkzeug._internal': ['_easteregg']
}
by_item = {}
for module, names in all_by_module.iteritems():
for name in names:
by_item[name] = module
def find_module(item):
return by_item.get(item, 'werkzeug')
def complete_fromlist(fromlist, lineiter):
fromlist = fromlist.strip()
if not fromlist:
return []
if fromlist[0] == '(':
if fromlist[-1] == ')':
return fromlist[1:-1].strip().split(',')
fromlist = fromlist[1:].strip().split(',')
for line in lineiter:
line = line.strip()
abort = False
if line.endswith(')'):
line = line[:-1]
abort = True
fromlist.extend(line.split(','))
if abort:
break
return fromlist
elif fromlist[-1] == '\\':
fromlist = fromlist[:-1].strip().split(',')
for line in lineiter:
line = line.strip()
abort = True
if line.endswith('\\'):
abort = False
line = line[:-1]
fromlist.extend(line.split(','))
if abort:
break
return fromlist
return fromlist.split(',')
def rewrite_from_imports(fromlist, indentation, lineiter):
parsed_items = []
for item in complete_fromlist(fromlist, lineiter):
item = item.strip().split()
if not item:
continue
if len(item) == 1:
parsed_items.append((item[0], None))
elif len(item) == 3 and item[1] == 'as':
parsed_items.append((item[0], item[2]))
else:
raise ValueError('invalid syntax for import')
new_imports = {}
for item, alias in parsed_items:
new_imports.setdefault(find_module(item), []).append((item, alias))
for module_name, items in sorted(new_imports.items()):
fromlist_items = sorted(['%s%s' % (
item,
alias is not None and (' as ' + alias) or ''
) for (item, alias) in items], reverse=True)
prefix = '%sfrom %s import ' % (indentation, module_name)
item_buffer = []
while fromlist_items:
item_buffer.append(fromlist_items.pop())
fromlist = ', '.join(item_buffer)
if len(fromlist) + len(prefix) > 79:
yield prefix + ', '.join(item_buffer[:-1]) + ', \\'
item_buffer = [item_buffer[-1]]
# doctest continuations
indentation = indentation.replace('>', '.')
prefix = indentation + ' '
yield prefix + ', '.join(item_buffer)
def inject_imports(lines, imports):
pos = 0
for idx, line in enumerate(lines):
if re.match(r'(from|import)\s+werkzeug', line):
pos = idx
break
lines[pos:pos] = ['from %s import %s' % (mod, ', '.join(sorted(attrs)))
for mod, attrs in sorted(imports.items())]
def rewrite_file(filename):
with open(filename) as f:
old_file = f.read().splitlines()
new_file = []
deferred_imports = {}
lineiter = iter(old_file)
for line in lineiter:
# rewrite from imports
match = _from_import_re.search(line)
if match is not None:
fromlist = line[match.end():]
new_file.extend(rewrite_from_imports(fromlist,
match.group(1),
lineiter))
continue
def _handle_match(match):
# rewrite attribute access to 'werkzeug'
attr = match.group(2)
mod = find_module(attr)
if mod == 'werkzeug':
return match.group(0)
deferred_imports.setdefault(mod, []).append(attr)
return attr
new_file.append(_direct_usage.sub(_handle_match, line))
if deferred_imports:
inject_imports(new_file, deferred_imports)
for line in difflib.unified_diff(
old_file, new_file,
posixpath.normpath(posixpath.join('a', filename)),
posixpath.normpath(posixpath.join('b', filename)),
lineterm=''
):
print(line)
def rewrite_in_folders(folders):
for folder in folders:
for dirpath, dirnames, filenames in os.walk(folder):
for filename in filenames:
filename = os.path.join(dirpath, filename)
if filename.endswith(('.rst', '.py')):
rewrite_file(filename)
def main():
if len(sys.argv) == 1:
print('usage: werkzeug-import-rewrite.py [folders]')
sys.exit(1)
rewrite_in_folders(sys.argv[1:])
if __name__ == '__main__':
main()