Source code for pylibtemplate

# -*- coding: utf-8 -*-
# Copyright 2025 Matthew Fitzpatrick.
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
r""":mod:`pylibtemplate` (short for 'Python Library Template') is a Python
library that generates ``git`` repository templates for building Python
libraries that are suitable for publication on PyPI.

"""



#####################################
## Load libraries/packages/modules ##
#####################################

# For converting relative paths to absolute paths, and for making directories.
import pathlib

# For timing the execution of different segments of code.
import time

# For getting the current year.
import datetime

# For pattern matching.
import re

# For removing directories.
import shutil

# For removing files, renaming directories, and getting directory trees.
import os

# For wrapping text.
import textwrap

# For parsing command line arguments.
import argparse



# For validating and converting objects.
import czekitout.check
import czekitout.convert

# For cloning ``git`` repositories.
import git



# Get version of current package.
from pylibtemplate.version import __version__



##################################
## Define classes and functions ##
##################################

# List of public objects in package.
__all__ = ["generate_local_git_repo_template"]



_pylibtemplate_lib_name_for_imports = "pylibtemplate"
_pylibtemplate_abbreviated_lib_name_for_docs = "PyLibTemplate"
_pylibtemplate_non_abbreviated_lib_name_for_docs = "Python Library Template"
_pylibtemplate_author = "Matthew Fitzpatrick"
_pylibtemplate_email = "matthew.rc.fitzpatrick@gmail.com"
_pylibtemplate_gist_id = "7baba2a56d07b59cc49b8323f44416e5"
_pylibtemplate_copyright_year = "2025"



def _check_and_convert_lib_name_for_imports(params):
    obj_name = "lib_name_for_imports"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    lib_name_for_imports = czekitout.convert.to_str_from_str_like(**kwargs)

    current_func_name = "_check_and_convert_lib_name_for_imports"

    if not lib_name_for_imports.isidentifier():
        err_msg = globals()[current_func_name+"_err_msg_1"]
        raise ValueError(err_msg)
    
    return lib_name_for_imports



def _check_and_convert_abbreviated_lib_name_for_docs(params):
    obj_name = \
        "abbreviated_lib_name_for_docs"
    kwargs = \
        {"obj": params[obj_name], "obj_name": obj_name}
    abbreviated_lib_name_for_docs = \
        czekitout.convert.to_str_from_str_like(**kwargs)

    current_func_name = "_check_and_convert_abbreviated_lib_name_for_docs"

    if not abbreviated_lib_name_for_docs.isidentifier():
        err_msg = globals()[current_func_name+"_err_msg_1"]
        raise ValueError(err_msg)
    
    return abbreviated_lib_name_for_docs



def _check_and_convert_non_abbreviated_lib_name_for_docs(params):
    obj_name = \
        "non_abbreviated_lib_name_for_docs"
    kwargs = \
        {"obj": params[obj_name], "obj_name": obj_name}
    non_abbreviated_lib_name_for_docs = \
        czekitout.convert.to_str_from_str_like(**kwargs)
    
    return non_abbreviated_lib_name_for_docs



def _check_and_convert_author(params):
    obj_name = "author"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    author = czekitout.convert.to_str_from_str_like(**kwargs)
    
    return author



def _check_and_convert_email(params):
    obj_name = "email"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    email = czekitout.convert.to_str_from_str_like(**kwargs)
    
    return email



def _check_and_convert_gist_id(params):
    obj_name = "gist_id"
    kwargs = {"obj": params[obj_name], "obj_name": obj_name}
    gist_id = czekitout.convert.to_str_from_str_like(**kwargs)
    
    return gist_id



def _check_and_convert_path_to_directory_to_contain_new_repo(params):
    obj_name = \
        "path_to_directory_to_contain_new_repo"
    kwargs = \
        {"obj": params[obj_name], "obj_name": obj_name}
    path_to_directory_to_contain_new_repo = \
        czekitout.convert.to_str_from_str_like(**kwargs)
    path_to_directory_to_contain_new_repo = \
        str(pathlib.Path(path_to_directory_to_contain_new_repo).absolute())
    
    return path_to_directory_to_contain_new_repo



_default_lib_name_for_imports = \
    _pylibtemplate_lib_name_for_imports
_default_abbreviated_lib_name_for_docs = \
    _pylibtemplate_abbreviated_lib_name_for_docs
_default_non_abbreviated_lib_name_for_docs = \
    _pylibtemplate_non_abbreviated_lib_name_for_docs
_default_author = \
    _pylibtemplate_author
_default_email = \
    _pylibtemplate_email
_default_gist_id = \
    _pylibtemplate_gist_id
_default_path_to_directory_to_contain_new_repo = \
    ""



[docs]def generate_local_git_repo_template( lib_name_for_imports=\ _default_lib_name_for_imports, abbreviated_lib_name_for_docs=\ _default_abbreviated_lib_name_for_docs, non_abbreviated_lib_name_for_docs=\ _default_non_abbreviated_lib_name_for_docs, author=\ _default_author, email=\ _default_email, gist_id=\ _default_gist_id, path_to_directory_to_contain_new_repo=\ _default_path_to_directory_to_contain_new_repo): r"""Generate a local ``git`` repository template. The primary purpose of generating a local ``git`` repository template is to modify it subsequently to develop a new Python library. This Python function will perform several actions: First, it will clone the ``git`` commit of the ``pylibtemplate`` GitHub repository corresponding to the version of ``pylibtemplate`` being used currently, in the directory at the path ``path_to_directory_to_contain_new_repo``, i.e. the ``git clone`` command is executed while the working directory is set temporarily to the path ``path_to_directory_to_contain_new_repo``; Next, it will rename the cloned repository to ``lib_name_for_imports`` such that the path to the cloned repository becomes ``path_to_directory_to_contain_new_repo+"/"+lib_name_for_imports``; Next, all instances of the string of characters "pylibtemplate" are replaced with ``lib_name_for_imports``, be it in file contents, directory basenames, or file basenames; Next, all instances of the string of characters "PyLibTemplate" are replaced with ``abbreviated_lib_name_for_docs``; Next, all instances of the string of characters "Python Library Template" are replaced with ``non_abbreviated_lib_name_for_docs``; Next, all email address placeholders (i.e. instances of the string of characters "matthew.rc.fitzpatrick@gmail.com") are replaced with ``email``; Next, all instances of the gist ID of ``pylibtemplate`` are replaced with ``gist_id``; Next, all author placeholders (i.e. instances of the string of characters "Matthew Fitzpatrick") are replaced with ``author``; Next, all copyright statements are updated according to the current year; And lastly, the following file is removed:: * ``<local_repo_root>/docs/how_to_create_a_python_library_using_pylibtemplate.rst`` where ``<local_repo_root>`` is the root of the local ``git`` repository, as well as the following directory:: * ``<local_repo_root>/.git`` On a unrelated note, for the purposes of demonstrating the citing of literature, we cite Ref. [RefLabel1]_ and [RefLabel2]_. Parameters ---------- lib_name_for_imports : `str`, optional The name of the new Python library, as it would appear in Python import commands. This parameter needs to be a valid Python identifier. abbreviated_lib_name_for_docs : `str`, optional An abbreviated format of the name of the new Python library that appears in documentation pages. This parameter needs to be a valid Python identifier. It can be set to the same value as ``lib_name_for_imports``. non_abbreviated_lib_name_for_docs : `str`, optional A non-abbreviated format of the name of the new Python library that appears in documentation pages. author : `str`, optional The name of the author of the new Python library. email : `str`, optional The email address of the author. gist_id : `str`, optional The ID of the GitHub gist to be used to record the code coverage from your unit tests. See the :ref:`how_to_create_a_python_library_using_pylibtemplate_sec` page for instructions on how to create a GitHub gist for your new Python library. path_to_directory_to_contain_new_repo : `str`, optional The path to the directory inside which the ``git`` commit --- of the ``pylibtemplate`` GitHub repository corresponding to the version of ``pylibtemplate`` being used currently --- is to be cloned, i.e. the ``git clone`` command is executed while the working directory is set temporarily to ``path_to_directory_to_contain_new_repo``. """ params = locals() global_symbol_table = globals() for param_name in params: func_name = "_check_and_convert_" + param_name func_alias = global_symbol_table[func_name] params[param_name] = func_alias(params) kwargs = params result = _generate_local_git_repo_template(**kwargs) return None
def _generate_local_git_repo_template(lib_name_for_imports, abbreviated_lib_name_for_docs, non_abbreviated_lib_name_for_docs, author, email, gist_id, path_to_directory_to_contain_new_repo): kwargs = locals() _print_generate_local_git_repo_template_starting_msg(**kwargs) start_time = time.time() kwargs = {"path_to_directory_to_contain_new_repo": \ path_to_directory_to_contain_new_repo, "lib_name_for_imports": \ lib_name_for_imports} _clone_pylibtemplate_repo(**kwargs) _rm_file_subset_of_local_git_repo(**kwargs) _mv_directory_subset_of_local_git_repo(**kwargs) filenames = _get_paths_to_files_in_local_git_repo(**kwargs) text_replacement_map = {_pylibtemplate_lib_name_for_imports: \ lib_name_for_imports, _pylibtemplate_abbreviated_lib_name_for_docs: \ abbreviated_lib_name_for_docs, _pylibtemplate_non_abbreviated_lib_name_for_docs: \ non_abbreviated_lib_name_for_docs, _pylibtemplate_author: \ author, _pylibtemplate_email: \ email, _pylibtemplate_gist_id: \ gist_id, _pylibtemplate_copyright_year: \ str(datetime.datetime.now().year)} kwargs = {"paths_to_files_in_local_git_repo": filenames, "text_replacement_map": text_replacement_map} _replace_and_wrap_text(**kwargs) kwargs = {"start_time": \ start_time, "path_to_directory_to_contain_new_repo": \ path_to_directory_to_contain_new_repo, "lib_name_for_imports": \ lib_name_for_imports} _print_generate_local_git_repo_template_end_msg(**kwargs) return None def _print_generate_local_git_repo_template_starting_msg( lib_name_for_imports, abbreviated_lib_name_for_docs, non_abbreviated_lib_name_for_docs, author, email, gist_id, path_to_directory_to_contain_new_repo): unformatted_msg = ("Generating a local ``git`` repository with the " "following parameters:\n" "\n" "``lib_name_for_imports`` = ``'{}'``\n" "``abbreviated_lib_name_for_docs`` = ``'{}'``\n" "``non_abbreviated_lib_name_for_docs`` = ``'{}'``\n" "``author`` = ``'{}'``\n" "``email`` = ``'{}'``\n" "``gist_id`` = ``'{}'``\n" "``path_to_directory_to_contain_new_repo`` = ``'{}'``\n" "\n" "..." "\n") msg = unformatted_msg.format(lib_name_for_imports, abbreviated_lib_name_for_docs, non_abbreviated_lib_name_for_docs, author, email, gist_id, path_to_directory_to_contain_new_repo) print(msg) return None def _clone_pylibtemplate_repo(path_to_directory_to_contain_new_repo, lib_name_for_imports): current_func_name = "_clone_pylibtemplate_repo" try: kwargs = {"output_dirname": path_to_directory_to_contain_new_repo} _make_output_dir(**kwargs) github_url = "https://github.com/mrfitzpa/pylibtemplate.git" kwargs = {"path_to_directory_to_contain_new_repo": \ path_to_directory_to_contain_new_repo, "lib_name_for_imports": \ lib_name_for_imports} path_to_new_repo = _generate_path_to_new_repo(**kwargs) tag = _get_pylibtemplate_tag() cloning_options = (tuple() if (tag is None) else ("--depth 1", "--branch {}".format(tag))) kwargs = {"url": github_url, "to_path": path_to_new_repo, "multi_options": cloning_options} git.Repo.clone_from(**kwargs) except: err_msg = globals()[current_func_name+"_err_msg_1"] IOError(err_msg) return None def _make_output_dir(output_dirname): current_func_name = "_make_output_dir" try: pathlib.Path(output_dirname).mkdir(parents=True, exist_ok=True) except: unformatted_err_msg = globals()[current_func_name+"_err_msg_1"] err_msg = unformatted_err_msg.format(output_dirname) raise IOError(err_msg) return None def _generate_path_to_new_repo(path_to_directory_to_contain_new_repo, lib_name_for_imports): path_to_new_repo = "{}/{}".format(path_to_directory_to_contain_new_repo, lib_name_for_imports) return path_to_new_repo def _get_pylibtemplate_tag(): pattern = r"[0-9]\.[0-9]\.[0-9]" pylibtemplate_version = __version__ pylibtemplate_tag = ("v{}".format(pylibtemplate_version) if re.fullmatch(pattern, pylibtemplate_version) else None) return pylibtemplate_tag def _rm_file_subset_of_local_git_repo(path_to_directory_to_contain_new_repo, lib_name_for_imports): kwargs = {"path_to_directory_to_contain_new_repo": \ path_to_directory_to_contain_new_repo, "lib_name_for_imports": \ lib_name_for_imports} path_to_new_repo = _generate_path_to_new_repo(**kwargs) path_to_dir_to_rm = path_to_new_repo + "/.git" shutil.rmtree(path_to_dir_to_rm, ignore_errors=True) basename = "how_to_create_a_python_library_using_pylibtemplate.rst" path_to_file_to_rm = path_to_new_repo + "/docs/" + basename os.remove(path_to_file_to_rm) return None def _mv_directory_subset_of_local_git_repo( path_to_directory_to_contain_new_repo, lib_name_for_imports): kwargs = locals() path_to_new_repo = _generate_path_to_new_repo(**kwargs) os.rename(path_to_new_repo + "/pylibtemplate", path_to_new_repo + "/" + lib_name_for_imports) return None def _get_paths_to_files_in_local_git_repo(path_to_directory_to_contain_new_repo, lib_name_for_imports): kwargs = locals() path_to_new_repo = _generate_path_to_new_repo(**kwargs) dir_tree = list(os.walk(path_to_new_repo)) dirname = os.path.abspath(dir_tree[0][0]) basename = os.path.basename(dirname) filenames = [] subdirnames = [] for x in dir_tree: subdirname = x[0] subdirnames += [subdirname] file_basenames = x[2] for file_basename in file_basenames: filename = subdirname + '/' + file_basename filenames += [filename] subdirnames.pop(0) paths_to_files_in_local_git_repo = filenames return paths_to_files_in_local_git_repo def _replace_and_wrap_text(paths_to_files_in_local_git_repo, text_replacement_map): filenames = paths_to_files_in_local_git_repo for filename in filenames: basename = os.path.basename(filename) kwargs = {"filename": filename, "text_replacement_map": text_replacement_map} new_file_contents = _generate_new_file_contents(**kwargs) with open(filename, "w") as file_obj: line_set = new_file_contents file_obj.write("\n".join(line_set)) return None def _generate_new_file_contents(filename, text_replacement_map): with open(filename, "r") as file_obj: line_set = file_obj.read().splitlines() original_file_contents = line_set kwargs = {"line_set_to_modify": original_file_contents, "text_replacement_map": text_replacement_map} modified_line_set = _apply_text_replacements_to_line_set(**kwargs) modified_file_contents = modified_line_set kwargs = {"line_set_to_modify": modified_file_contents, "filename": filename} modified_line_set = _apply_text_wrapping_to_line_set(**kwargs) modified_file_contents = modified_line_set new_file_contents = modified_file_contents return new_file_contents def _apply_text_replacements_to_line_set(line_set_to_modify, text_replacement_map): modified_line_set = line_set_to_modify.copy() for line_idx, line_to_modify in enumerate(line_set_to_modify): modified_line = line_to_modify.rstrip() for old_substring, new_substring in text_replacement_map.items(): modified_line = modified_line.replace(old_substring, new_substring) modified_line_set[line_idx] = modified_line if ((re.fullmatch("=+", modified_line) or re.fullmatch("-+", modified_line) or re.fullmatch("~+", modified_line)) and (len(modified_line) > 3)): modified_line_set[line_idx] = (modified_line[0] * len(modified_line_set[line_idx-1])) return modified_line_set def _apply_text_wrapping_to_line_set(line_set_to_modify, filename): file_extension = os.path.splitext(filename)[1] if file_extension == ".py": func_alias = _apply_text_wrapping_to_line_set_of_py_file elif file_extension in (".md", ".rst"): func_alias = _apply_text_wrapping_to_line_set_of_md_or_rst_file elif file_extension == ".sh": func_alias = _apply_text_wrapping_to_line_set_of_sh_file kwargs = {"line_set_to_modify": line_set_to_modify} modified_line_set = (func_alias(**kwargs) if (file_extension in (".py", ".md", ".sh", ".rst")) else line_set_to_modify.copy()) return modified_line_set def _apply_text_wrapping_to_line_set_of_py_file(line_set_to_modify): modified_line_set = line_set_to_modify.copy() pattern_1 = r"#\ ((Copyright)|(For\ setting\ up))\ .*" pattern_2 = r"\s*((lib_name)|(project)|(author))\ =\ \".+" pattern_3 = r"\s*r\"\"\".+\ ``.+``.+" end_of_file_has_not_been_reached = True line_idx = 0 while end_of_file_has_not_been_reached: modified_line = modified_line_set[line_idx].rstrip() if re.fullmatch(pattern_1, modified_line): func_alias = _apply_text_wrapping_to_single_line_python_comment elif re.fullmatch(pattern_2, modified_line): func_alias = _apply_text_wrapping_to_single_line_variable_assignment elif re.fullmatch(pattern_3, modified_line): func_alias = _apply_text_wrapping_to_single_line_partial_doc_str else: func_alias = None kwargs = {"line_to_modify": modified_line} modified_line_subset = ([modified_line] if (func_alias is None) else func_alias(**kwargs)) modified_line_set = (modified_line_set[:line_idx] + modified_line_subset + modified_line_set[line_idx+1:]) line_idx += len(modified_line_subset) if line_idx >= len(modified_line_set): end_of_file_has_not_been_reached = False return modified_line_set _char_limit_per_line = 80 def _apply_text_wrapping_to_single_line_python_comment(line_to_modify): modified_line = line_to_modify.rstrip() char_idx = len(modified_line) - len(modified_line.lstrip()) indent = " "*char_idx modified_line = modified_line.lstrip()[2:] kwargs = {"text": modified_line, "width": _char_limit_per_line-char_idx-2, "break_long_words": False} lines_resulting_from_text_wrapping = textwrap.wrap(**kwargs) for line_idx, line in enumerate(lines_resulting_from_text_wrapping): lines_resulting_from_text_wrapping[line_idx] = indent + "# " + line return lines_resulting_from_text_wrapping def _apply_text_wrapping_to_single_line_variable_assignment(line_to_modify): modified_line = line_to_modify.rstrip() if len(modified_line) <= _char_limit_per_line: lines_resulting_from_text_wrapping = [modified_line] else: char_idx_1 = modified_line.find("=") char_idx_2 = _char_limit_per_line - (char_idx_1+4) indent = " "*(char_idx_1+3) modified_line = modified_line[char_idx_1+2:] line_resulting_from_text_wrapping = (line_to_modify[:char_idx_1+2] + "(\"" + modified_line[1:char_idx_2] + "\"") lines_resulting_from_text_wrapping = [line_resulting_from_text_wrapping] modified_line = modified_line[char_idx_2:] while len(indent+modified_line)+2 > _char_limit_per_line: line_resulting_from_text_wrapping = \ indent + "\"" + modified_line[:char_idx_2-1] + "\"" lines_resulting_from_text_wrapping += \ [line_resulting_from_text_wrapping] modified_line = modified_line[char_idx_2-1:] line_resulting_from_text_wrapping = \ indent + "\"" + modified_line + ")" lines_resulting_from_text_wrapping += \ [line_resulting_from_text_wrapping] return lines_resulting_from_text_wrapping def _apply_text_wrapping_to_single_line_partial_doc_str(line_to_modify): modified_line = line_to_modify.rstrip() char_idx = len(modified_line) - len(modified_line.lstrip()) indent = " "*char_idx modified_line = line_to_modify.lstrip() kwargs = {"text": modified_line, "width": _char_limit_per_line - char_idx, "break_long_words": False} lines_resulting_from_text_wrapping = textwrap.wrap(**kwargs) for line_idx, line in enumerate(lines_resulting_from_text_wrapping): lines_resulting_from_text_wrapping[line_idx] = indent + line return lines_resulting_from_text_wrapping def _apply_text_wrapping_to_line_set_of_md_or_rst_file(line_set_to_modify): modified_line_set = line_set_to_modify.copy() pattern_1 = (r"((\[!\[)|(\s+)|(\*\ )|(\-\ \`)|(\.\.\ )|(\{\%)" r"|(\{\{)|(##)|(#\ \-)|(----+)|(====+)|(~~~~+)).*") pattern_2 = r"((----+)|(====+)|(~~~~+))" end_of_file_has_not_been_reached = True line_idx = 1 while end_of_file_has_not_been_reached: modified_line_1 = modified_line_set[line_idx].rstrip() if (re.fullmatch(pattern_1, modified_line_1) or (modified_line_1 in ("", "#"))): modified_line_subset = [modified_line_1] else: end_of_paragraph_has_not_been_reached = True while end_of_paragraph_has_not_been_reached: if line_idx+1 < len(modified_line_set): modified_line_2 = modified_line_set[line_idx+1].rstrip() if (re.fullmatch(pattern_1, modified_line_2) or (modified_line_2 in ("", "#"))): end_of_paragraph_has_not_been_reached = False else: modified_line_1 += " " + modified_line_2.lstrip() modified_line_set.pop(line_idx+1) else: end_of_paragraph_has_not_been_reached = False if re.fullmatch(pattern_2, modified_line_2): modified_line_subset = [modified_line_1] else: kwargs = {"text": modified_line_1, "width": _char_limit_per_line, "break_long_words": False} modified_line_subset = textwrap.wrap(**kwargs) modified_line_set = (modified_line_set[:line_idx] + modified_line_subset + modified_line_set[line_idx+1:]) line_idx += len(modified_line_subset) if line_idx >= len(modified_line_set): end_of_file_has_not_been_reached = False lines_resulting_from_text_wrapping = modified_line_set return lines_resulting_from_text_wrapping def _apply_text_wrapping_to_line_set_of_sh_file(line_set_to_modify): modified_line_set = line_set_to_modify.copy() pattern = r"#\ .+" end_of_file_has_not_been_reached = True line_idx = 2 while end_of_file_has_not_been_reached: modified_line_1 = modified_line_set[line_idx].rstrip() if re.fullmatch(pattern, modified_line_1): prefix = "# " end_of_paragraph_has_not_been_reached = True while end_of_paragraph_has_not_been_reached: modified_line_2 = modified_line_set[line_idx+1].rstrip() if re.fullmatch(pattern, modified_line_2): modified_line_1 += " " + modified_line_2.lstrip(prefix) modified_line_set.pop(line_idx+1) else: end_of_paragraph_has_not_been_reached = False kwargs = {"text": modified_line_1, "width": _char_limit_per_line, "subsequent_indent": prefix, "break_long_words": False} modified_line_subset = textwrap.wrap(**kwargs) else: modified_line_subset = [modified_line_1] modified_line_set = (modified_line_set[:line_idx] + modified_line_subset + modified_line_set[line_idx+1:]) line_idx += len(modified_line_subset) if line_idx >= len(modified_line_set): end_of_file_has_not_been_reached = False lines_resulting_from_text_wrapping = modified_line_set return lines_resulting_from_text_wrapping def _print_generate_local_git_repo_template_end_msg( start_time, path_to_directory_to_contain_new_repo, lib_name_for_imports): elapsed_time = time.time() - start_time path_to_local_git_repo_template = (path_to_directory_to_contain_new_repo + "/" + lib_name_for_imports) unformatted_msg = ("Finished generating the local ``git`` repository, " "which is located at the path ``'{}'``. Time taken to " "generate the local ``git`` repository: {} " "s.\n\n\n") msg = unformatted_msg.format(path_to_local_git_repo_template, elapsed_time) print(msg) return None def _run_pylibtemplate_as_an_app(cmd_line_args=None): converted_cmd_line_args = _parse_and_convert_cmd_line_args(cmd_line_args) kwargs = converted_cmd_line_args generate_local_git_repo_template(**kwargs) return None def _parse_and_convert_cmd_line_args(cmd_line_args): arg_names = ("lib_name_for_imports", "abbreviated_lib_name_for_docs", "non_abbreviated_lib_name_for_docs", "author", "email", "gist_id", "path_to_directory_to_contain_new_repo") parser = argparse.ArgumentParser() global_symbol_table = globals() for arg_name in arg_names: obj_name = "_default_" + arg_name default_arg_val = global_symbol_table[obj_name] arg_type = type(default_arg_val) parser.add_argument("--"+arg_name, default=default_arg_val, type=arg_type) current_func_name = "_parse_and_convert_cmd_line_args" try: args = parser.parse_args(args=cmd_line_args) except: err_msg = globals()[current_func_name+"_err_msg_1"] raise SystemExit(err_msg) converted_cmd_line_args = dict() for arg_name in arg_names: converted_cmd_line_args[arg_name] = getattr(args, arg_name) return converted_cmd_line_args ########################### ## Define error messages ## ########################### _check_and_convert_lib_name_for_imports_err_msg_1 = \ ("The object ``lib_name_for_imports`` must be a string that is a valid " "identifier.") _check_and_convert_abbreviated_lib_name_for_docs_err_msg_1 = \ _check_and_convert_lib_name_for_imports_err_msg_1 _make_output_dir_err_msg_1 = \ ("An error occurred in trying to make the directory ``'{}'``: see " "traceback for details.") _clone_pylibtemplate_repo_err_msg_1 = \ ("An error occurred while trying to clone the ``pylibtemplate`` " "repository: see the traceback for details.") _parse_and_convert_cmd_line_args_err_msg_1 = \ ("The correct form of the command is:\n" "\n" " pylibtemplate " "--lib_name_for_imports=" "<lib_name_for_imports> " "--abbreviated_lib_name_for_docs=" "<abbreviated_lib_name_for_docs> " "--non_abbreviated_lib_name_for_docs=" "<non_abbreviated_lib_name_for_docs> " "--author=" "<author> " "--email=" "<email> " "--gist_id=" "<gist_id> " "--path_to_directory_to_contain_new_repo=" "<path_to_directory_to_contain_new_repo>\n" "\n" "where the command line arguments are the same as the parameters of the " "function ``pylibtemplate.generate_local_git_repo_template``. For " "details, see the documentation for the libary ``pylibtemplate`` via the " "link https://mrfitzpa.github.io/pylibtemplate, and navigate the " "reference guide for the version of the library that is installed.") ######################### ## Main body of script ## ######################### _ = (_run_pylibtemplate_as_an_app() if (__name__ == "__main__") else None)