bitranox/lib_regexp

View on GitHub
project_update.py

Summary

Maintainability
A
2 hrs
Test Coverage
"""

Usage:
    project_update.py [ --get_registered_shell_command ]

this module exposes no other useful functions to the commandline

"""
# stdlib
import pathlib
import shutil
from typing import Dict, List, Union

# EXT
import click

# OWN
import project_conf

# CONSTANTS
CLICK_CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])


def format_commandline_help_file() -> None:
    source_file = pathlib.Path(__file__).parent / '.docs/commandline_help.txt'
    if source_file.is_file():
        with open(source_file, 'r') as f_sourcefile:
            commandline_help_txt_lines = f_sourcefile.readlines()
        with open(source_file, 'w') as f_targetfile:
            target_lines = list()
            target_lines.append('.. code-block:: bash\n\n')
            target_lines.append('')
            for commandline_help_txt_line in commandline_help_txt_lines:
                target_lines.append('   ' + commandline_help_txt_line)
            f_targetfile.writelines(target_lines)
    else:
        with open(str(source_file), 'w') as f_targetfile:
            f_targetfile.write('.. code-block:: bash\n\n    there are no commandline options\n')


def create_commandline_help_file() -> None:
    """
    >>> create_commandline_help_file()

    """
    import subprocess
    import sys
    module_path = pathlib.Path('./{src_dir}/{module_name}.py'.format(src_dir=project_conf.src_dir, module_name=project_conf.module_name))
    if module_path.is_file():
        module_path = module_path.resolve()
        command = '{sys_executable} {module_path} -h > ./.docs/commandline_help.txt'.format(sys_executable=sys.executable, module_path=module_path)
        subprocess.run(command, shell=True)
    format_commandline_help_file()


def create_init_config_file() -> None:
    path_source_dir = get_path_template_dir_local() / 'templates'
    path_target_dir = pathlib.Path(__file__).parent.resolve() / project_conf.src_dir

    path_target_dir.mkdir(parents=True, exist_ok=True)

    # overwrite __init__conf__py from template
    path_targetfile = path_target_dir / '__init__conf__.py'
    path_sourcefile = path_source_dir / '__init__conf__.py'
    shutil.copy(str(path_sourcefile), str(path_targetfile))

    # replace the markers
    with open(path_targetfile, 'r') as f_targetfile:
        text = f_targetfile.read()
    text = text.replace('{version}', project_conf.version)
    text = text.replace('{title}', project_conf.init_config_title)
    text = text.replace('{name}', project_conf.init_config_name)
    text = text.replace('{url}', project_conf.url)
    text = text.replace('{author}', project_conf.author)
    text = text.replace('{author_email}', project_conf.author_email)
    text = text.replace('{shell_command}', project_conf.shell_command)
    with open(path_targetfile, 'w') as f_targetfile:
        f_targetfile.write(text)

    # copy __init__.py if not there from template
    path_targetfile = path_target_dir / '__init__.py'
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / '__init__.py'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy main.py if not there from template
    path_targetfile = path_target_dir / (project_conf.module_name + '.py')
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / 'main.py'
        shutil.copy(str(path_sourcefile), str(path_targetfile))


def is_in_own_project_folder() -> bool:
    if pathlib.Path(__file__).parts[-2] == 'lib_travis_template':
        return True
    else:
        return False


def get_path_template_dir_local() -> pathlib.Path:
    path_current_dir = pathlib.Path(__file__).parent.resolve()
    while True:
        path_current_dir = path_current_dir.parent
        path_current_subdirs = path_current_dir.glob('**/')
        for subdir in path_current_subdirs:
            if subdir.parts[-1] == 'lib_travis_template':
                return subdir


def is_ok_to_copy(path_source_file: pathlib.Path) -> bool:
    """ its ok when a file and not in the list """
    files_not_to_copy = ['requirements.txt', 'project_conf.py', '.travis.yml', 'README.rst',
                         'CHANGES.rst', 'description.rst', 'usage.rst', 'installation.rst', 'acknowledgment.rst',
                         'badges_project.rst', 'badges_with_jupyter.rst', 'badges_without_jupyter.rst',
                         'index.rst', 'index_jupyter.rst', 'try_in_jupyter.rst']
    if path_source_file.is_file():
        if path_source_file.name in files_not_to_copy:
            return False
        else:
            return True
    else:
        return False


def get_paths_to_copy(path_source_dir: pathlib.Path) -> List[pathlib.Path]:
    paths_source = list(path_source_dir.glob('*'))
    paths_source = paths_source + list(path_source_dir.glob('**/.docs/*'))
    paths_source = paths_source + list(path_source_dir.glob('**/tests/*'))
    paths_source = sorted(paths_source)
    return paths_source


def copy_project_files() -> None:
    """
    copy the template files to the current project on the local development machine
    we dont overwrite some files, see code
    """
    path_source_dir = get_path_template_dir_local()
    path_target_dir = pathlib.Path(__file__).parent.resolve()
    s_path_source_dir = str(path_source_dir)
    s_path_target_dir = str(path_target_dir)

    l_path_sourcefiles = get_paths_to_copy(path_source_dir)

    for path_sourcefile in l_path_sourcefiles:
        if is_ok_to_copy(path_sourcefile):
            s_path_sourcefile = str(path_sourcefile)
            s_path_targetfile = s_path_sourcefile.replace(s_path_source_dir, s_path_target_dir, 1)
            path_targetfile = pathlib.Path(s_path_targetfile)

            if not path_targetfile.parent.is_dir():
                path_targetfile.parent.mkdir(exist_ok=True)

            shutil.copy(s_path_sourcefile, s_path_targetfile)


def copy_template_files() -> None:
    path_source_dir = get_path_template_dir_local()
    path_target_dir = pathlib.Path(__file__).parent.resolve()

    # copy CHANGES.rst template if not there
    path_targetfile = path_target_dir / 'CHANGES.rst'
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / 'templates/CHANGES.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy usage.rst template if not there
    path_targetfile = path_target_dir / '.docs/usage.rst'
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / 'templates/usage.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy description.rst template if not there
    path_targetfile = path_target_dir / '.docs/description.rst'
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / 'templates/description.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy acknowledgment.rst template if not there
    path_targetfile = path_target_dir / '.docs/acknowledgment.rst'
    if not path_targetfile.is_file():
        path_sourcefile = path_source_dir / 'templates/acknowledgment.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy index.rst template if not there
    path_targetfile = path_target_dir / '.docs/index.rst'
    if not path_targetfile.is_file():
        if project_conf.badges_with_jupiter:
            path_sourcefile = path_source_dir / 'templates/index_jupyter.rst'
        else:
            path_sourcefile = path_source_dir / 'templates/index.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))

    # copy try_in_jupyter.rst template if not there
    path_targetfile = path_target_dir / '.docs/try_in_jupyter.rst'
    if project_conf.badges_with_jupiter:
        path_sourcefile = path_source_dir / 'templates/try_in_jupyter.rst'
        shutil.copy(str(path_sourcefile), str(path_targetfile))
    else:
        path_targetfile.unlink(missing_ok=True)

    # overwrite badges template
    if project_conf.badges_with_jupiter:
        path_sourcefile = path_source_dir / '.docs/badges_with_jupyter.rst'
    else:
        path_sourcefile = path_source_dir / '.docs/badges_without_jupyter.rst'
    path_targetfile = path_target_dir / '.docs/badges_project.rst'
    shutil.copy(str(path_sourcefile), str(path_targetfile))
    # overwrite installation.rst template
    path_targetfile = path_target_dir / '.docs/installation.rst'
    path_sourcefile = path_source_dir / 'templates/installation.rst'
    shutil.copy(str(path_sourcefile), str(path_targetfile))


def replace_marker(text: str, marker: str, src_filename: str, replace_marker_with_src_file: bool = True) -> str:
    """ replace a marker in the text with the content of a file, or with '' """
    if replace_marker_with_src_file:
        path_base_dir = pathlib.Path(__file__).parent
        path_src_filename = path_base_dir / src_filename
        with open(str(path_src_filename), 'r') as f_src_filename:
            s_src = f_src_filename.read()
            text = text.replace(marker, s_src)
    else:
        text = text.replace(marker, '')
    return text


def create_travis_file() -> None:

    if not project_conf.travis_pypi_secure_code:
        travis_pypi_secure_code = '# - secure: "none"'
    else:
        travis_pypi_secure_code = '- secure: "{code}"'.format(code=project_conf.travis_pypi_secure_code)

    path_base_dir = pathlib.Path(__file__).parent
    text = '{travis_template}\n'
    text = replace_marker(text=text, marker='{travis_template}', src_filename='.travis_template.yml')
    text = replace_marker(text=text, marker='{travis_template_linux_addon}',
                          src_filename='.travis_template_linux_addon.yml', replace_marker_with_src_file=project_conf.linux_tests)
    text = replace_marker(text=text, marker='{travis_template_osx_addon}',
                          src_filename='.travis_template_osx_addon.yml', replace_marker_with_src_file=project_conf.osx_tests)
    text = replace_marker(text=text, marker='{travis_template_pypy_addon}',
                          src_filename='.travis_template_pypy_addon.yml', replace_marker_with_src_file=project_conf.pypy_tests)
    text = replace_marker(text=text, marker='{travis_template_windows_addon}',
                          src_filename='.travis_template_windows_addon.yml', replace_marker_with_src_file=project_conf.windows_tests)
    text = replace_marker(text=text, marker='{travis_template_wine_addon}',
                          src_filename='.travis_template_wine_addon.yml', replace_marker_with_src_file=project_conf.wine_tests)
    text = text.replace('{package_name}', project_conf.package_name)
    text = text.replace('{cc_test_reporter_id}', project_conf.cc_test_reporter_id)
    text = text.replace('{travis_pypi_secure_code}', travis_pypi_secure_code)
    text = text.replace('{travis_repo_slug}', project_conf.travis_repo_slug)
    text = text.replace('{github_master}', project_conf.github_master)
    target_file = path_base_dir / '.travis.yml'
    with open(target_file, 'w') as f_target_file:
        f_target_file.write(text)

    if not is_in_own_project_folder():
        (path_base_dir / '.travis_template.yml').unlink()
        (path_base_dir / '.travis_template_linux_addon.yml').unlink()
        (path_base_dir / '.travis_template_osx_addon.yml').unlink()
        (path_base_dir / '.travis_template_pypy_addon.yml').unlink()
        (path_base_dir / '.travis_template_windows_addon.yml').unlink()
        (path_base_dir / '.travis_template_wine_addon.yml').unlink()


def main() -> None:
    """ create and update travis python projects """
    create_init_config_file()

    # copy files from template folder to current project
    if not is_in_own_project_folder():  # we dont want to copy if we run this in the template project itself
        copy_project_files()
        copy_template_files()

    # create travis file
    create_travis_file()

    # create readme.rst
    create_commandline_help_file()
    import build_docs
    build_docs_args = dict()
    build_docs_args['<TRAVIS_REPO_SLUG>'] = '{}/{}'.format(project_conf.github_account, project_conf.package_name)
    build_docs.main(build_docs_args)


@click.group(context_settings=CLICK_CONTEXT_SETTINGS, invoke_without_command=True)
@click.pass_context
def main_commandline(click_context) -> None:
    if click_context.invoked_subcommand is None:
        main()


@main_commandline.command('get_registered_shell_command')
def get_registered_shell_command():
    """ returns the shell command which will be registered with the shell"""
    print(project_conf.shell_command)


# entry point if main
if __name__ == '__main__':
    main_commandline()