Skip to content

Instantly share code, notes, and snippets.

@LeMeteore
Created June 18, 2019 17:59
Show Gist options
  • Save LeMeteore/cac019562822388a5d11c0ead629848c to your computer and use it in GitHub Desktop.
Save LeMeteore/cac019562822388a5d11c0ead629848c to your computer and use it in GitHub Desktop.

Revisions

  1. LeMeteore created this gist Jun 18, 2019.
    243 changes: 243 additions & 0 deletions transform.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,243 @@
    import re
    import io
    import os
    import fnmatch
    import argparse
    import mimetypes
    import subprocess
    import urllib.request
    from contextlib import contextmanager


    @contextmanager
    def cd(newdir):
    """ Custom change directory command. """
    prevdir = os.getcwd()
    os.chdir(os.path.expanduser(newdir))
    try:
    yield
    finally:
    os.chdir(prevdir)


    def get_python_files(path):
    """ Retrive all Python files inside cube. """
    python_files = []

    with cd(path):
    hg_files = subprocess.check_output(
    "hg status -A", shell=True).decode().split("\n")

    tracked_files = [x.split(" ", 1)[1] for x in hg_files if x.startswith("C ")]
    tracked_files = filter(None, tracked_files)

    for f in tracked_files:
    typ = mimetypes.MimeTypes().guess_type(f)[0]
    if typ == 'text/x-python':
    python_files.append(f)

    return python_files


    def print_warning(msg):
    """ Custom print coloring printed message. """
    print(f'\x1b[1;33;40m {msg} \x1b[0m')


    def _dtnm(path):
    """ Returns a list of folders to not move. """

    folders_not_to_move = []
    for root, dirs, files in os.walk(path):
    for d in ['bin', 'debian', 'test', '.hg', '.tox', 'cubicweb_*']:
    for folder in fnmatch.filter(dirs, d):
    folders_not_to_move.append(folder)
    break
    return folders_not_to_move


    def _ftnm(path):
    """ Returns a list of files to not move. """

    files_not_to_move = []
    for root, dirs, files in os.walk(path):
    for f in ['setup.py', 'README', '*.ini', 'MANIFEST.in',
    '*.txt', '*.spec', '.hgtags', '.hgrc', 'test*']:
    for filename in fnmatch.filter(files, f):
    files_not_to_move.append(filename)
    break
    return files_not_to_move


    def create_cube_folder(cube_root, cube_folder):
    """ Creates the cube folder. """

    with cd(cube_root):
    try:
    if not os.path.exists(cube_folder):
    os.makedirs(cube_folder)
    print_warning(f'Info: {cube_folder} successfully created')
    return cube_folder
    else:
    print_warning(f'Error: {cube_folder} already exists.')
    except OSError:
    print_warning(f'Error: Creating directory {cube_folder}.')


    def update_pkginfo(pkginfo):
    """ Update __pkginfo__.py content. """

    # retrieve numversion, depends, recommends
    __pkginfo__ = {}
    with open(pkginfo, "r") as f:
    exec(f.read(), __pkginfo__)


    context = {
    'numversion': __pkginfo__['numversion'],
    'cubename': os.path.split(pkginfo)[0],
    'distname': 'cubicweb-' + os.path.split(pkginfo)[0],
    'license': __pkginfo__['license'],
    'author': __pkginfo__['author'],
    'author-email': __pkginfo__['author_email'],
    'shortdesc': __pkginfo__['description'],
    'dependencies': __pkginfo__['__depends__'],
    }

    if '__recommends__' in __pkginfo__:
    context['__recommends__'] = __pkginfo__['__recommends__']

    command = f"hg rm {pkginfo}"
    subprocess.Popen(command, shell=True).wait()

    url = "https://hg.logilab.org/master/cubicweb/raw-file/tip/cubicweb/skeleton/cubicweb_CUBENAME/__pkginfo__.py.tmpl"
    with urllib.request.urlopen(url) as response, open(pkginfo, 'w') as out:
    template = response.read().decode()
    new_pkginfo = template % context
    new_pkginfo = new_pkginfo.split('\n')
    for l in new_pkginfo:
    if "numversion" in l:
    l = re.sub(r"^numversion ?= ?.*$", f"numversion = {context['numversion']}", l)
    # if '>= ' in l: # not sure how to update cubicweb version to 3.24
    # l = re.sub(r"'^.*cubicweb' ?: ?'>?= ?'\d{1}\.\d{1}\.\d{1}',?.*", "'cubicweb': '>= 3.24.0'", l)
    out.write(l+'\n')

    command = f"hg add {pkginfo}"
    subprocess.Popen(command, shell=True).wait()
    print_warning(f'Info: Successfully updated __pkginfo__.py.')


    def move_cube_files(cube_root, cube_folder):
    """ Move cube files from root to cube folder. """

    ftnm = _ftnm(cube_root)
    dtnm = _dtnm(cube_root)
    cf = []

    for root, dirs, files in os.walk(cube_root):
    for filename in files:
    if fnmatch.fnmatch(filename, '__pkginfo__.py'):
    update_pkginfo(os.path.join(cube_root,filename))
    if filename not in ftnm:
    cf.append(filename)

    for dirname in dirs:
    if dirname not in dtnm:
    cf.append(dirname)
    break # we just need level 1 walk

    with cd(cube_root):
    for i in cf:
    command = f"hg mv {i} {cube_folder}"
    subprocess.Popen(command, shell=True).wait()

    print_warning(f'Info: cube files successfully moved into {cube_folder}')


    def replace_cube_file(cube_root, filename):
    """ Delete & replace files that needs to be rewritten using the skeleton. """

    with cd(cube_root):
    command = f"hg rm {filename}"
    subprocess.Popen(command, shell=True).wait()

    url = f"https://hg.logilab.org/master/cubicweb/raw-file/tip/cubicweb/skeleton/{filename}.tmpl"
    context = {'cubename': cube_root, 'distname': 'cubicweb-'+cube_root}
    with urllib.request.urlopen(url) as response, open(filename, 'w') as out:
    template = response.read().decode()
    out.write(template % context)

    command = f"hg add {filename}"
    subprocess.Popen(command, shell=True).wait()
    print_warning(f'Info: {filename} successfully updated')


    def remove_useless_files(cube_root):
    """ Remove all the files that are no more used. """

    with cd(cube_root):
    for filename in ['apycot.ini', 'pytestconf.py']:
    command = f"hg rm {filename}"
    subprocess.Popen(command, shell=True).wait()
    print_warning(f'Info: {filename} successfully removed')


    def fix_unittest_import(cube_root, filename):
    """ Make sure we use unittest from standard lib."""
    with cd(cube_root):
    content = []
    with open(filename, 'r') as f:
    content = f.readlines()

    with open(filename, 'w') as f:
    for l in content:
    if "unittest_main" in l:
    l = re.sub(r"^from logilab\.common\.testlib import unittest_main$", r"import unittest", l)
    l = re.sub(r"^ from logilab\.common\.testlib import unittest_main$", r" import unittest", l)
    l = re.sub(r"^ unittest_main\(\)$", " unittest.main()", l)
    f.write(l)


    def fix_cube_import(cube_root, filename):
    """ Make sure we import cube using new layout."""
    with cd(cube_root):
    content = []
    with open(filename, 'r') as f:
    content = f.readlines()

    with open(filename, 'w') as f:
    for l in content:
    if "cubes." in l:
    l = re.sub(r"", r"", l)
    f.write(l)



    def main():
    parser = argparse.ArgumentParser(description='Cube mgt system.')
    parser.add_argument('-p', '--path', default=".", help='cube path to migrate')

    args = parser.parse_args()
    path = os.path.realpath(os.path.expanduser(args.path))

    cube_root = os.path.basename(path)
    cube_folder = f'cubicweb_{cube_root}'

    # setup.py and MANIFEST.in can just be replaced
    for i in ['setup.py', 'MANIFEST.in', 'tox.ini']:
    replace_cube_file(cube_root, i)

    create_cube_folder(cube_root, cube_folder)
    move_cube_files(cube_root, cube_folder)

    py_files = get_python_files(cube_root)
    # remove_useless_files

    for f in py_files:
    fix_unittest_import(cube_root, f)
    # fix_cubes_import
    # automatically call autopep8?

    if __name__ == '__main__':
    main()