pybaker.py
A program to change the metadata of a PyPlate project and create a dist
This module sets the project metadata in each of the files, according to the data present in the conf files. It then sets up the dist folder with all necessary files to create a complete distribution of the project.
Run pybaker -h for more options.
PyBaker
Bases: PyPlate
The main class, responsible for the operation of the program
Public methods
main: The main method of the program
This class implements all the needed functionality of PyBaker, to create a distribution from a PyPlate project.
Source code in src/pybaker.py
class PyBaker(PyPlate):
"""
The main class, responsible for the operation of the program
Public methods:
main: The main method of the program
This class implements all the needed functionality of PyBaker, to create a
distribution from a PyPlate project.
"""
# --------------------------------------------------------------------------
# Class constants
# --------------------------------------------------------------------------
# ide option strings
S_ARG_IDE_OPTION = "-i"
S_ARG_IDE_ACTION = "store_true"
S_ARG_IDE_DEST = "IDE_DEST"
# I18N help string for ide cmd line option
S_ARG_IDE_HELP = _("ask for project folder when running in IDE")
# about string
S_ABOUT = (
"\n"
f"{'PyPlate/PyBaker'}\n"
f"{PyPlate.S_PP_SHORT_DESC}\n"
f"{PyPlate.S_PP_VERSION}\n"
f"https://github.com/cyclopticnerve/PyPlate\n"
)
# I18N cmd line instructions string
S_EPILOG = _("Run this program from the parent directory of the project " \
"you want to build.")
# --------------------------------------------------------------------------
# Instance methods
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# Initialize the new object
# --------------------------------------------------------------------------
def __init__(self):
"""
Initialize the new object
Initializes a new instance of the class, setting the default values
of its properties, and any other code that needs to run to create a
new object.
"""
# do super init
super().__init__()
# set the initial values of properties
self._ide = False
# --------------------------------------------------------------------------
# Public methods
# --------------------------------------------------------------------------
# --------------------------------------------------------------------------
# The main method of the program
# --------------------------------------------------------------------------
def main(self):
"""
The main method of the program
This method is the main entry point for the program, initializing the
program, and performing its steps.
"""
# do super main
super().main()
# do any fixing up of dicts (like meta keywords, etc)
self._do_before_fix()
# do replacements in final project location
self._do_fix()
# do extra stuff to final dir after fix
self._do_after_fix()
# do any fixing up of dicts (like meta keywords, etc)
self._do_before_dist()
# copy project files into dist folder
self._do_dist()
# do any fixing up of dicts (like meta keywords, etc)
self._do_after_dist()
# --------------------------------------------------------------------------
# Private methods
# --------------------------------------------------------------------------
# NB: these are the main steps, called in order from main
# --------------------------------------------------------------------------
# Boilerplate to use at the start of main
# --------------------------------------------------------------------------
def _setup(self):
"""
Boilerplate to use at the start of main
Perform some mundane stuff like setting properties.
"""
# do setup
super()._setup()
# add ide option
self._parser.add_argument(
self.S_ARG_IDE_OPTION,
dest=self.S_ARG_IDE_DEST,
help=self.S_ARG_IDE_HELP,
action=self.S_ARG_IDE_ACTION,
)
# parse command line
self._do_cmd_line()
if self._debug:
self._dict_debug = C.D_DBG_PB
# get ide flag from cmd line
self._ide = self._dict_args.get(self.S_ARG_IDE_DEST, False)
# if ide=yes, ask for prj name
if self._ide:
# ask for prj name rel to cwd
in_str = C.S_ASK_IDE.format(self._dir_prj)
while True:
prj_name = input(in_str)
if prj_name == "":
continue
# if running in ide, cwd is pyplate prj dir, so move up + down
tmp_dir = Path(self._dir_prj / prj_name).resolve()
# check if project exists
if not tmp_dir.exists():
e_str = C.S_ERR_NOT_EXIST.format(tmp_dir)
print(e_str)
continue
# set project dir and exit loop
self._dir_prj = tmp_dir
print()
break
# --------------------------------------------------------------------------
# Get project info
# --------------------------------------------------------------------------
def _get_project_info(self):
"""
Get project info
Check that the PyPlate data is present and correct, so we don't crash
looking for non-existent files.
"""
# check if dir_prj has pyplate folder for a valid prj
path_pyplate = self._dir_prj / C.S_PRJ_PP_DIR
if not path_pyplate.exists():
print(C.S_ERR_NOT_PRJ)
sys.exit()
# check if data files exist
path_prv = self._dir_prj / C.S_PRJ_PRV_CFG
path_pub = self._dir_prj / C.S_PRJ_PUB_CFG
if not path_prv.exists() or not path_pub.exists():
print(C.S_ERR_PP_MISSING)
sys.exit()
# check if files are valid json
try:
# get global and calculated settings dicts in private.json
self._dict_prv = F.load_dicts([path_prv], {})
# get individual dicts in project.json
self._dict_pub = F.load_dicts([path_pub], {})
# reload dict pointers after dict change
self._reload_dicts()
except OSError:
print(C.S_ERR_PP_INVALID)
sys.exit()
# --------------------------------------------------------------------------
# Do any work before making dist
# --------------------------------------------------------------------------
def _do_before_dist(self):
"""
Do any work before making dist
Do any work on the dist folder before it is created. This method is
called after _do_after_fix, and before _do_dist.
"""
C.do_before_dist(
self._dir_prj, self._dict_prv, self._dict_pub, self._dict_debug
)
# --------------------------------------------------------------------------
# Copy fixed files to final location
# --------------------------------------------------------------------------
def _do_dist(self):
"""
Copy fixed files to final location
Gets dirs/files from project and copies them to the dist/assets dir.
"""
# print info
print(C.S_ACTION_DIST, end="", flush=True)
# ----------------------------------------------------------------------
# do common dist stuff
# find old dist? nuke it from orbit! it's the only way to be sure!
a_dist = self._dir_prj / C.S_DIR_DIST
if a_dist.is_dir():
shutil.rmtree(a_dist)
# make child dir in case we nuked
name_fmt = self._dict_prv_prj["__PP_DIST_DIR__"]
p_dist = a_dist / name_fmt
p_dist.mkdir(parents=True)
# for each key, val (type, dict)
for key, val in self._dict_pub_dist.items():
# get src/dst rel to prj dir/dist dir
src = self._dir_prj / key
dst = p_dist / val
if not dst.exists():
dst.mkdir(parents=True)
dst = dst / src.name
# do the copy
if src.exists() and src.is_dir():
shutil.copytree(src, dst, dirs_exist_ok=True)
elif src.exists() and src.is_file():
shutil.copy2(src, dst)
# done copying project files
print(C.S_ACTION_DONE)
# --------------------------------------------------------------------------
# Do any work after making dist
# --------------------------------------------------------------------------
def _do_after_dist(self):
"""
Do any work after making dist
Do any work on the dist folder after it is created. This method is
called after _do_dist. Currently, this method purges any "ABOUT" file
used as placeholders for github syncing. It also tars the source folder
if it is a package, making for one (or two) less steps in the user's
install process.
"""
C.do_after_dist(
self._dir_prj, self._dict_prv, self._dict_pub, self._dict_debug
)
__init__()
Initialize the new object
Initializes a new instance of the class, setting the default values of its properties, and any other code that needs to run to create a new object.
Source code in src/pybaker.py
def __init__(self):
"""
Initialize the new object
Initializes a new instance of the class, setting the default values
of its properties, and any other code that needs to run to create a
new object.
"""
# do super init
super().__init__()
# set the initial values of properties
self._ide = False
main()
The main method of the program
This method is the main entry point for the program, initializing the program, and performing its steps.
Source code in src/pybaker.py
def main(self):
"""
The main method of the program
This method is the main entry point for the program, initializing the
program, and performing its steps.
"""
# do super main
super().main()
# do any fixing up of dicts (like meta keywords, etc)
self._do_before_fix()
# do replacements in final project location
self._do_fix()
# do extra stuff to final dir after fix
self._do_after_fix()
# do any fixing up of dicts (like meta keywords, etc)
self._do_before_dist()
# copy project files into dist folder
self._do_dist()
# do any fixing up of dicts (like meta keywords, etc)
self._do_after_dist()