Source code for easysetup

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2009-2015 Joao Carlos Roseta Matos
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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 <http://www.gnu.org/licenses/>.

"""Helps creating a package distribution setup for Windows users."""

# Python 3 compatibility
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import datetime as dt
import glob
import io  # Python 3 compatibility
import os
import shutil as shu
import sys
import zipfile as zipf

from builtins import input  # Python 3 compatibility
import colorama as clrm

import common
import localization as lcl


DEFAULT_AUTHOR = 'CHANGE_ME'
DEFAULT_EMAIL = 'CHANGE_ME'
DEFAULT_URL = 'https://github.com/CHANGE_ME/'

DEFAULT_VERSION = '0.0.1'
DEFAULT_LICENSE = 'GNU General Public License v2 or later (GPLv2+)'

BAK_DIR = '_bak'
APPLICATION_TEMPLATE_FILE = '/APPLICATION_NAME.py'

app_name = ''
app_version = ''
app_license = ''
app_author = ''
app_email = ''
app_url = ''
app_keywords = ''
cur_date = str(dt.date.today())


[docs]def update_file(filename): """Update file with user input.""" with io.open(filename, encoding=lcl.UTF_ENC) as f_in: text = f_in.readlines() new_text = '' changed = False for line in text: if 'APPLICATION_NAME' in line: line = line.replace('APPLICATION_NAME', app_name) changed = True if 'APPLICATION_VERSION' in line: line = line.replace('APPLICATION_VERSION', app_version) changed = True if 'APPLICATION_LICENSE' in line: line = line.replace('APPLICATION_LICENSE', app_license) changed = True if 'APPLICATION_AUTHOR' in line: line = line.replace('APPLICATION_AUTHOR', app_author) changed = True if 'APPLICATION_EMAIL' in line: line = line.replace('APPLICATION_EMAIL', app_email) changed = True if 'APPLICATION_URL' in line: line = line.replace('APPLICATION_URL', app_url) changed = True if 'APPLICATION_KEYWORDS' in line: line = line.replace('APPLICATION_KEYWORDS', app_keywords) changed = True if 'CUR_DATE' in line: line = line.replace('CUR_DATE', cur_date) changed = True # quick hacks if 'README.rst' in filename and '================' in line: line = line.replace('================', '=' * len(app_name)) changed = True if 'reference.rst' in filename and '::::::::::::::::' in line: line = line.replace('::::::::::::::::', ':' * len(app_name)) changed = True new_text += line if changed: with io.open(filename, 'w', encoding=lcl.UTF_ENC) as f_out: f_out.writelines(new_text)
[docs]def get_app_info(): """Read application info from appinfo.py.""" global app_name, app_version, app_license, app_author, app_email, \ app_url, app_keywords with io.open(common.APP_INFO_FILENAME, encoding=lcl.UTF_ENC) as f_in: text = f_in.readlines() for line in text: if 'APP_NAME = ' in line: app_name = line.split("'")[1] if 'APP_VERSION = ' in line: app_version = line.split("'")[1] if 'APP_LICENSE = ' in line: app_license = line.split("'")[1] if 'APP_AUTHOR = ' in line: app_author = line.split("'")[1] if 'APP_EMAIL = ' in line: app_email = line.split("'")[1] if 'APP_URL = ' in line: app_url = line.split("'")[1] if 'APP_KEYWORDS = ' in line: app_keywords = line.split("'")[1]
[docs]def update_ref(): """Creates a new doc/reference.rst for Sphinx autodoc extension.""" filenames = glob.glob(app_name + '/*.py') # remove __init__.py filenames = [filename for filename in filenames if '__init__.py' not in filename and 'appinfo.py' not in filename] if filenames: # remove paths filenames = [filename.split(os.sep)[-1] for filename in filenames] # remove extensions filenames = [filename.split('.')[0] for filename in filenames] text = 'Reference\n---------\n' for filename in filenames: text += '\n' text += filename + '\n' + ':' * len(filename) + '\n\n' text += '.. automodule:: ' + filename + '\n' text += ' :members:\n' with io.open('doc/reference.rst', 'w', encoding=lcl.UTF_ENC) as f_out: f_out.writelines(text)
[docs]def update_doc(): """Update doc dir.""" filenames = glob.glob(common.DATA_PATH + 'template/doc/*') # copy template/doc files for filename in filenames: # if file exists delete it if os.path.isfile('doc/' + filename.split(os.sep)[-1]): os.remove('doc/' + filename.split(os.sep)[-1]) shu.copyfile(filename, 'doc/' + filename.split(os.sep)[-1]) filenames_to_update = ['conf.py', 'reference.rst'] # delete .pyc files and update files filenames = glob.glob('doc/*') for filename in filenames: if '.pyc' in filename: os.remove(filename) else: if filename.split(os.sep)[-1] in filenames_to_update: update_file(filename) update_ref()
[docs]def create_redir2rtd_zip(): """Create zip of index.html that redirects pythonhosted to RTD.""" filename = 'pythonhosted.org/index.html' with zipf.ZipFile('pythonhosted.org/redir2RTD.zip', 'w') as archive: archive.write(filename, filename.split('/')[-1])
[docs]def create_setup(): """Copy files from template and update them with user input.""" global app_name, app_version, app_license, app_author, app_email, \ app_url, app_keywords, DEFAULT_AUTHOR, DEFAULT_EMAIL, \ DEFAULT_LICENSE, DEFAULT_URL, DEFAULT_VERSION data_lst = common.load_data() if data_lst: (DEFAULT_AUTHOR, DEFAULT_EMAIL, DEFAULT_LICENSE, DEFAULT_URL, DEFAULT_VERSION) = data_lst while not app_name: app_name = input(lcl.Q_APP_NAME).decode(lcl.INPUT_ENC) app_version = input(lcl.Q_APP_VERSION + '[' + DEFAULT_VERSION + '] ').decode(lcl.INPUT_ENC) if not app_version: app_version = DEFAULT_VERSION app_license = input(lcl.Q_APP_LICENSE + '[' + DEFAULT_LICENSE + '] ').decode(lcl.INPUT_ENC) if not app_license: app_license = DEFAULT_LICENSE app_author = input(lcl.Q_APP_AUTHOR + '[' + DEFAULT_AUTHOR + '] ').decode(lcl.INPUT_ENC) if not app_author: app_author = DEFAULT_AUTHOR app_email = input(lcl.Q_APP_EMAIL + '[' + DEFAULT_EMAIL + '] ').decode(lcl.INPUT_ENC) if not app_email: app_email = DEFAULT_EMAIL app_url = input(lcl.Q_APP_URL + '[' + DEFAULT_URL + '] ').decode(lcl.INPUT_ENC) if not app_url: app_url = DEFAULT_URL app_keywords = input(lcl.Q_APP_KEYWORDS).decode(lcl.INPUT_ENC) if not app_keywords: app_keywords = app_name data_lst = [app_author, app_email, app_license, app_url, app_version] common.save_data(data_lst) app_url += app_name # backup existing files backup = False filenames = glob.glob('*') filenames += glob.glob('.*') if filenames: backup = True os.mkdir(BAK_DIR) for filename in filenames: dest = BAK_DIR + '/' + filename.split(os.sep)[-1] shu.move(filename, dest) filenames = glob.glob(common.DATA_PATH + 'template/*') filenames += glob.glob(common.DATA_PATH + 'template/.*') # remove doc dir filenames = [filename for filename in filenames if 'template' + os.sep + 'doc' not in filename] # copy files and dirs for filename in filenames: if os.path.isfile(filename): shu.copyfile(filename, filename.split(os.sep)[-1]) else: shu.copytree(filename, filename.split(os.sep)[-1]) common.sleep(2) os.rename('APPLICATION_NAME', app_name) # rename application dir # collect all filenames, including from 1st level subdirs filenames = glob.glob('*') filenames = [filename for filename in filenames if BAK_DIR not in filename] filenames += glob.glob('.*') new_filenames = [] for filename in filenames: if os.path.isdir(filename): new_filenames += glob.glob(filename + '/*') filenames += new_filenames exceptions = ['__init__.py', 'build.cmd', 'requirements.txt', 'requirements-dev.txt', 'setup.py', 'setup_py2exe.py', 'setup_utils.py'] # delete .pyc files and update files for filename in filenames: if os.path.isfile(filename): if '.pyc' in filename: os.remove(filename) else: if filename.split(os.sep)[-1] not in exceptions: update_file(filename) create_redir2rtd_zip() if backup: os.remove(app_name + APPLICATION_TEMPLATE_FILE) # remove app template # restore files from backup, but only if they don't already exist filenames = glob.glob(BAK_DIR + '/*') for filename in filenames: dest = app_name + '/' + filename.split(os.sep)[-1] if not os.path.isfile(dest): shu.copyfile(filename, dest) else: os.rename(app_name + APPLICATION_TEMPLATE_FILE, app_name + '/' + app_name + '.py') # rename app template print(lcl.REMINDERS)
[docs]def main(): """Process command line args.""" clrm.init() args = sys.argv[1:] if args: args_set = set(args) max_args = 2 # -q and one other doc_arg_set = set(['-d', '--doc']) help_arg_set = set(['-h', '--help']) license_arg_set = set(['-l', '--license']) quiet_arg_set = set(['-q', '--quiet']) reference_arg_set = set(['-r', '--reference']) version_arg_set = set(['-V', '--version']) legal_args_set = (doc_arg_set | help_arg_set | license_arg_set | quiet_arg_set | reference_arg_set | version_arg_set) # if any wrong arg, too many args or (max args and -q not among them) if (args_set - legal_args_set or len(args) > max_args or (len(args) == max_args and not (quiet_arg_set & args_set))): print(common.banner()) print(clrm.Fore.RED + lcl.WRONG_ARG + '\n') print(clrm.Fore.RESET + common.usage()) else: if not (quiet_arg_set & args_set): print(common.banner()) if doc_arg_set & args_set: get_app_info() update_doc() elif license_arg_set & args_set: print(common.license_()) elif help_arg_set & args_set: print(common.usage()) elif reference_arg_set & args_set: get_app_info() update_ref() elif version_arg_set & args_set: print(lcl.VERSION, common.version()) else: # only -q create_setup() else: print(common.banner()) create_setup()
if __name__ == '__main__': # import doctest # doctest.testmod(verbose=True) sys.exit(main()) # ToDo: Auto rebuild requirements.txt on each dist build. # ToDo: CXF in Py2 and Py3 # ToDo: checks and error messages # ToDo: Move build.cmd functionality to easysetup.py.