#!/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: 2007 Pallets
:license: BSD-3-Clause
"""
import difflib
import os
import posixpath
import re
import sys
_from_import_re = re.compile(r"(\s*(>>>|\.\.\.)?\s*)from werkzeug import\s+")
_direct_usage = re.compile(r"(?<!`)(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.items():
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()