Tabea-K
7/2/2018 - 1:29 PM

Bash script to generate the directory structure and files needed for the generation of an empty python project template. The first argument

Bash script to generate the directory structure and files needed for the generation of an empty python project template. The first argument supplied is used as the main name of the python project, the second as the name of the main python code file. A directory "tests" is generated for unit tests, which also includes an example test case.

#!/bin/bash
# This creates a more or less empty template for a python package
# containing unit tests
# Author: Tabea Kischka, 2018-07-02
# Version 2.0, last update: 2018-08-28

###########################################################################################################
# get options
###########################################################################################################

usage(){
 echo -ne "Run like this:\ncreate_empty_python_package_template.sh -p PACKAGENAME -m NAMEOFMAINSCRIPT -a AUTHORNAME\n"
 echo -ne "-p\tPACKAGENAME\t\tThe name you choose for your python package\n"
 echo -ne "-m\tNAMEOFMAINSCRIPT\t\tThe name you choose for the main script of your package\n"
 echo -ne "-a\tAUTHOR\t\tYour name (will be included in the README file)\n"
 echo -ne -ne "\n\nIn the end the directory structure will look like this:\n\n
    .
    ├── README.rst
    ├── package_name
    │   ├── __init__.py
    │   ├── main_script_name.py
    │   └── tests
    │       ├── test_package_name.py
    │       └── testdata.txt
    └── setup.py\n\n"
 exit 1
 }


while getopts "p:m:a:h" opt
 do
  case "${opt}"
   in
    p) PACKAGENAME="$OPTARG";;
    m) MAINSCRIPTNAME="$OPTARG";;
    a) AUTHOR="$OPTARG";;
    h) usage;;
    *) usage;;
    \?) echo "Invalid option: -$OPTARG" >&2; exit 1;;
    :) echo "Option -$OPTARG requires an argument." >&2; exit 1;;

  esac
 done

if [ -z "${PACKAGENAME}" ] || [ -z "${MAINSCRIPTNAME}" ] || [ -z "${AUTHOR}" ]; then
    usage
fi

if [ -d  "$PACKAGENAME" ]
 then
  echo "ERROR: $PACKAGENAME already exists. Please choose another name."
  exit 1
 fi


###########################################################################################################
# start generating files...
###########################################################################################################

D=`date`

echo "
------------------------

Will now generate directory structure and files for the python project $PACKAGENAME
under the folder $PWD

------------------------
"

###########################################################################################################
# make main dir
###########################################################################################################

mkdir "$PACKAGENAME"
cd "$PACKAGENAME"


###########################################################################################################
# create readme files
###########################################################################################################

echo "Python package $PACKAGENAME created on $D by $AUTHOR" > README.rst


###########################################################################################################
# generate file setup.py
###########################################################################################################

echo "from setuptools import setup


def readme():
    with open('README.rst') as f:
        return f.read()

# if you want to include more test data within your package, you have to list it inside the list package_data in setup!

setup(name='${PACKAGENAME}',
      version='0.1',
      description='A super cool package that was generated with the python project template generator.',
      long_description=readme(),
      author='${AUTHOR}',
      author_email='',
      license='',
      packages=['$PACKAGENAME'],
      test_suite='nose.collector',
      tests_require=['nose'],
      package_data={'${PACKAGENAME}': ['README.rst', 'tests/testdata.txt']},
      include_package_data=True,
      entry_points={
          'console_scripts': ['${PACKAGENAME}=${PACKAGENAME}.${MAINSCRIPTNAME}:main'],
      },
      zip_safe=False)" > setup.py


###########################################################################################################
# make python script dir
###########################################################################################################

mkdir "$PACKAGENAME"
cd "$PACKAGENAME"


###########################################################################################################
# make init file
###########################################################################################################

echo "from $MAINSCRIPTNAME import *" > __init__.py
echo -ne "#!/usr/bin/env python

import sys
import os
import argparse
import logging

__version__ = '1.0'
script_description = 'Description of the $MAINSCRIPTNAME script'

def hello_world():
    return('hello world')

def print_infile_name(infile_fh):
    return(infile_fh.name)

def main():
    # parse arguments
    parser = argparse.ArgumentParser(description=script_description)
    parser.add_argument('infile',
                        help='input file',
                        nargs='?',
                        type=argparse.FileType('r'),
                        default=sys.stdin)
    parser.add_argument('-d',
                        help='run in debug mode, print lots of information',
                        action='store_const',
                        dest='loglevel',
                        const=logging.DEBUG,
                        default=logging.WARNING)
    parser.add_argument('-d',
                        help='run in debug mode, print lots of information',
                        action='store_const',
                        dest='loglevel',
                        const=logging.DEBUG,
                        default=logging.WARNING)
    parser.add_argument('-v',
                        help='run in verbose mode, print some more information',
                        action='store_const',
                        dest='loglevel',
                        const=logging.INFO,
                        default=logging.WARNING)
    args = parser.parse_args()

    # set up logging module
    logging.basicConfig(format='[%(asctime)s - %(levelname)s] %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        level=args.loglevel)
    logger = logging.getLogger(__name__)
    logger.debug('logging set up!')
    logger.debug('All arguments: %s' % args)
    logger.info('Script starts!')

    hello_world()
    print_infile_name(args.infile)

" > "${MAINSCRIPTNAME}.py"


###########################################################################################################
# make test files
###########################################################################################################

mkdir tests
cd tests
echo "#!/usr/bin/env python
import unittest
import $MAINSCRIPTNAME

class MyTestCase(unittest.TestCase):
    def test_hello_world(self):
        text = $MAINSCRIPTNAME.hello_world()
        self.assertEqual(text, 'hello world')


# Add more tests here
"> "test_${PACKAGENAME}.py"

echo -ne "This could be your test data\n" > testdata.txt


###########################################################################################################
# Last steps...
###########################################################################################################

# go back to the initial directory
cd ../..

echo "
Finished!
You now have an empty template for your $PACKAGENAME python project!

You can add unit tests to the file $PACKAGENAME/tests/test_${PACKAGENAME}.py and run the
unit tests using this command:

cd $PWD/$PACKAGENAME && \
python -m unittest discover -v -s tests

"