Source code for skelpy.makers.project

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""This module defines :class:`ProjectMaker` class.

"""

from __future__ import absolute_import, print_function

import os
import sys
import subprocess
from tempfile import gettempdir

from skelpy.utils import opener, helpers
from . import get_maker, settings
from .base import BaseMaker
from .license import LicenseMaker


[docs]class ProjectMaker(BaseMaker): """The master *Maker* class responsible for creating the whole project tree After creating the project directory, ``ProjectMaker`` creates several configuration files in the directory using the appropriate *Maker*. Below are the configuration files to be created:: * setup.py * setup.cfg * LICENSE * README.rst * .gitignore Then ``ProjectMaker`` creates and runs three sub-makers: ``PackageMaker``, ``DockMaker``, ``TestMaker``. Args: projectName (str): project name, in effect, the directory to create. projectDir (str): absolute path of project directory to be created. .. note:: projectDir should include the *projectName* as its last component. In other words, the following statement must be True. ``os.path.split(projectDir)[-1] == projectName`` merge (bool): whether to overlap the project directory if the directory already exists force (bool): whether to overwrite if the file with the same name already exists """ def __init__(self, projectDir, projectName, quiet, merge, force, **kwargs): self.projectDir = projectDir self.projectName = projectName self.quiet = quiet self.merge = merge self.force = force self._update_info() def _update_info(self): """update :attr:`maker.settings` dictionary""" defaults = { 'author': helpers.get_userName(), 'author_email': helpers.get_email(), 'version': '1.0.0', 'license': LicenseMaker.default_license, 'description': 'ADD SHORT DESCRIPTION ON THE PROJECT HERE'} settings.update(defaults) def _get_info(self): """collect project information from the user This private method opens the default text editor and get the user input on the project such as license, short description of the project. Collected information is used to create setup.py and setup.cfg. Returns: bool: True if successful, False otherwise """ if self.quiet: return try: from configparser import ConfigParser # python3 except ImportError: from ConfigParser import ConfigParser # python2 infoFile = os.path.join(gettempdir(), 'info.txt') if not self.write_file('info', infoFile): return False if opener.open_with_associated_application(infoFile, block=True) == -1: return False parser = ConfigParser() try: with open(infoFile, 'r') as f: if sys.version_info[0] == 2: parser.readfp(f) else: parser.read_file(f) except Exception: return False os.remove(infoFile) for section in parser.sections(): settings.update(parser.items(section)) return True def _check_license(self): """check if the license is valid. If the license is invalid or unsupported the default license(MIT) is used instead. """ license = settings.get('license') if not LicenseMaker.is_supported_license(license): self.logger.info( "Invalid license: '{}'\n".format(license) + "default '{}' license will be used.\n".format(LicenseMaker.default_license) + "* You can change the license later with 'license' sub-command.\n" + "For help, see 'skelpy license -h/--help'.") settings['license'] = LicenseMaker.default_license else: settings['license'] = license.upper() def _create_config_files(self): """create configuration files Returns: bool: True if successful, False otherwise """ makers = ['setup_cfg', 'setup', 'license', 'readme'] for m in makers: maker_cls = get_maker(m) if not maker_cls: self.logger.warning(' skipping...') continue maker = maker_cls(**settings) if not maker.generate(): return False return True def _create_miscellaneous(self): """create other miscellaneous configuration files Currently, this method only creates .gitignore when it detects `git <https://git-scm.com/>`_ is installed. You can extend this method to create other files by adding a couple of codes as shown in the example below. Example: Add `.editorconfig <https://editorconfig.org/>`_ provided that .editorconfig.tpl is in the templates directory:: if not self.write_file('.editorconfig', os.path.join(self.projectDir, '.editorconfig')): return False Returns: bool: True if successful, False otherwise """ # .gitignore if helpers.has_command('git'): if not self.write_file('.gitignore', os.path.join(self.projectDir, '.gitignore')): return False return True def _run_subworkers(self): """ create & run sub-makers Returns: bool: True if successful, False otherwise """ makers = [ 'package', 'docs', 'tests', ] for m in makers: maker_cls = get_maker(m) if not maker_cls: return False maker = maker_cls(**settings) if not maker.generate(): return False return True
[docs] def generate(self): """Worker method of :class:`ProjectMaker` Returns: bool: True if successful, False otherwise """ if not self.create_dir(self.projectDir, recursive=True): return False if not self._get_info(): return False self._check_license() if not self._create_config_files(): return False if not self._create_miscellaneous(): return False if not self._run_subworkers(): return False return True