# -*- 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)