ScoDoc/refactor.py

167 lines
4.5 KiB
Python

# -*- coding: UTF-8 -*
"""Outil pour migration ScoDoc 7 => ScoDoc 8
Pour chaque module dans views:
- construire la liste des fonctions définies dans ce module:
get_module_functions
Pour chaque module dans views et dans scodoc:
- remplacer context.xxx par app.views.M.xxx
où M est le module de views définissant xxx
Si xxx n'est pas trouvé, erreur !
"""
from __future__ import print_function
import re
from pprint import pprint as pp
import os
import sys
import types
import tempfile
import shutil
import click
# import flask
# import app
# from app import create_app, cli, db
# from app.auth.models import User, Role, UserRole
# from app.views import notes
TYPES_TO_SCAN = {
types.FunctionType,
# types.ClassType,
# types.DictionaryType,
# types.FloatType,
# types.IntType,
# types.ListType,
# types.StringType,
# types.TupleType,
}
def get_module_symbols(module):
"""returns list of symbols (functions and constants) defined in the given module"""
return [
f.__name__
for f in [getattr(module, name) for name in dir(module)]
if (type(f) in TYPES_TO_SCAN)
and ((type(f) != types.FunctionType) or (f.__module__ == module.__name__))
]
# print("\n".join(f.__name__ for f in get_module_functions(notes)))
def scan_views_symbols():
"""Scan modules in app.views and returns
{ }
"""
import app
views_modules = [
getattr(app.views, mod_name)
for mod_name in dir(app.views)
if type(getattr(app.views, mod_name)) == types.ModuleType
]
sym2mod = {} # symbole_name : module
for module in views_modules:
start = "app.views."
assert module.__name__.startswith(start)
module_name = module.__name__[len(start) :]
symbols = set(get_module_symbols(module))
print("%d symbols defined in %s" % (len(symbols), module))
dups = symbols.intersection(sym2mod)
if len(dups):
print("duplicated symbols !")
for dup in dups:
print("%s:\t%s\t%s" % (dup, sym2mod[dup], module_name))
sym2mod.update({s: module_name for s in symbols})
return sym2mod
def replace_context_calls(sourcefilename, sym2mod):
undefined_list = [] # noms de fonctions non présents dans les modules "views"
def repl(m):
funcname = m.group(1)
module = sym2mod.get(funcname, False)
if module:
return module + "." + funcname
else:
undefined_list.append((sourcefilename, funcname))
return m.group(0) # leave unchanged
print("reading %s" % sourcefilename)
source = open(sourcefilename).read()
exp = re.compile(r"context\.([a-zA-Z0-9_]+)")
source2 = exp.sub(repl, source)
return source2, undefined_list
# sym2mod = scan_views_symbols()
# source2, undefined_list = replace_context_calls("app/scodoc/sco_core.py", sym2mod)
def list_context_calls(sourcefilename):
"""List of methods called on context in this file"""
source = open(sourcefilename).read()
exp = re.compile(r"context\.([a-zA-Z0-9_]+)")
return sorted(set(exp.findall(source)))
@click.group()
def cli():
pass
@cli.command()
@click.argument("src_filenames", nargs=-1)
def showcontextcalls(src_filenames):
click.echo("Initialized the database")
S = {}
for sourcefilename in src_filenames:
l = list_context_calls(sourcefilename)
module_name = os.path.splitext(os.path.split(sourcefilename)[1])[0]
for m in l:
if m in S:
S[m].append(module_name)
else:
S[m] = [module_name]
#
for method in sorted(S.keys()):
print(method + ":\t" + ", ".join(S[method]))
@cli.command()
@click.argument("method", nargs=1)
@click.argument("module", nargs=1)
@click.argument("src_filenames", nargs=-1)
def refactor(method, module, src_filenames):
"""Replace call context.method
by module.method
in all given source filenames
"""
backup = tempfile.mkdtemp(dir="/tmp")
for sourcefilename in src_filenames:
print("reading %s" % sourcefilename)
source = open(sourcefilename).read()
source2 = source.replace("context." + method, module + "." + method)
shutil.move(sourcefilename, backup)
open(sourcefilename, "w").write(source2)
print("Done.\noriginal files saved in %s\n" % backup)
if __name__ == "__main__":
try:
cli(obj={})
except SystemExit as e:
if e.code != 0:
raise