Source code for pyscaffold.structure

# -*- coding: utf-8 -*-
"""
Functionality to generate and work with the directory structure of a project
"""
from __future__ import absolute_import, print_function

import os
from os.path import join as join_path

from six import string_types

from . import shell, templates, utils

__author__ = "Florian Wilhelm"
__copyright__ = "Blue Yonder"
__license__ = "new BSD"


[docs]class FileOp(object): """ Namespace for file operations during an update - *NO_OVERWRITE*: Do not overwrite an existing file during update - *NO_CREATE*: Do not create the file during an update """ NO_OVERWRITE = 0 NO_CREATE = 1
[docs]def add_namespace(opts, struct): """ Prepend the namespace to a given file structure :param opts: options as dictionary :param struct: directory structure as dictionary of dictionaries :return: directory structure as dictionary of dictionaries """ if not opts['namespace']: return struct namespace = opts['namespace'][-1].split('.') base_struct = struct pkg_struct = struct[opts['project']][opts['package']] struct = base_struct[opts['project']] del struct[opts['package']] for sub_package in namespace: struct[sub_package] = {'__init__.py': templates.namespace(opts)} struct = struct[sub_package] struct[opts['package']] = pkg_struct return base_struct
[docs]def make_structure(opts): """ Creates the project structure as dictionary of dictionaries :param opts: options as dictionary :return: structure as dictionary of dictionaries """ struct = {opts['project']: { '.gitignore': templates.gitignore(opts), opts['package']: {'__init__.py': templates.init(opts), 'skeleton.py': templates.skeleton(opts)}, 'tests': {'conftest.py': templates.conftest_py(opts), 'test_skeleton.py': templates.test_skeleton(opts)}, 'docs': {'conf.py': templates.sphinx_conf(opts), 'authors.rst': templates.sphinx_authors(opts), 'index.rst': templates.sphinx_index(opts), 'license.rst': templates.sphinx_license(opts), 'changes.rst': templates.sphinx_changes(opts), 'Makefile': templates.sphinx_makefile(opts), '_static': { '.gitignore': templates.gitignore_empty(opts)}}, 'README.rst': templates.readme(opts), 'AUTHORS.rst': templates.authors(opts), 'LICENSE.txt': templates.license(opts), 'CHANGES.rst': templates.changes(opts), 'setup.py': templates.setup_py(opts), 'setup.cfg': templates.setup_cfg(opts), 'requirements.txt': templates.requirements(opts), 'test-requirements.txt': templates.test_requirements(opts), '.coveragerc': templates.coveragerc(opts)}} proj_dir = struct[opts['project']] if opts['travis']: proj_dir['.travis.yml'] = templates.travis(opts) proj_dir['tests']['travis_install.sh'] = templates.travis_install(opts) if opts['pre_commit']: proj_dir['.pre-commit-config.yaml'] = templates.pre_commit_config(opts) if opts['tox']: proj_dir['tox.ini'] = templates.tox(opts) if opts['update'] and not opts['force']: # Do not overwrite following files rules = {opts['project']: { '.gitignore': FileOp.NO_OVERWRITE, '.gitattributes': FileOp.NO_OVERWRITE, 'setup.cfg': FileOp.NO_OVERWRITE, 'README.rst': FileOp.NO_OVERWRITE, 'CHANGES.rst': FileOp.NO_OVERWRITE, 'LICENSE.txt': FileOp.NO_OVERWRITE, 'AUTHORS.rst': FileOp.NO_OVERWRITE, 'requirements.txt': FileOp.NO_OVERWRITE, 'test-requirements.txt': FileOp.NO_OVERWRITE, '.travis.yml': FileOp.NO_OVERWRITE, '.coveragerc': FileOp.NO_OVERWRITE, '.pre-commit-config.yaml': FileOp.NO_OVERWRITE, 'tox.ini': FileOp.NO_OVERWRITE, opts['package']: {'skeleton.py': FileOp.NO_CREATE}, 'tests': {'conftest.py': FileOp.NO_OVERWRITE, 'travis_install.sh': FileOp.NO_OVERWRITE, 'test_skeleton.py': FileOp.NO_CREATE}, 'docs': {'index.rst': FileOp.NO_OVERWRITE} }} struct = apply_update_rules(rules, struct) struct = add_namespace(opts, struct) return struct
[docs]def create_structure(struct, prefix=None, update=False): """ Manifests a directory structure in the filesystem :param struct: directory structure as dictionary of dictionaries :param prefix: prefix path for the structure :param update: update an existing directory structure as boolean """ if prefix is None: prefix = os.getcwd() for name, content in struct.items(): if isinstance(content, string_types): with open(join_path(prefix, name), 'w') as fh: fh.write(utils.utf8_encode(content)) elif isinstance(content, dict): try: os.mkdir(join_path(prefix, name)) except OSError: if not update: raise create_structure(struct[name], prefix=join_path(prefix, name), update=update) elif content is None: pass else: raise RuntimeError("Don't know what to do with content type " "{type}.".format(type=type(content)))
[docs]def create_django_proj(opts): """ Creates a standard Django project with django-admin.py :param opts: options as dictionary """ try: shell.django_admin('--version') except: raise RuntimeError("django-admin.py is not installed, " "run: pip install django") shell.django_admin('startproject', opts['project'])
[docs]def create_cookiecutter(opts): """ Create a cookie cutter template :param opts: options as dictionary """ try: from cookiecutter.main import cookiecutter except: raise RuntimeError("cookiecutter is not installed, " "run: pip install cookiecutter") extra_context = dict(full_name=opts['author'], email=opts['email'], project_name=opts['project'], repo_name=opts['package'], project_short_description=opts['description'], release_date=opts['release_date'], version='unknown', # will be replaced later year=opts['year']) cookiecutter(opts['cookiecutter_template'], no_input=True, extra_context=extra_context)
[docs]def apply_update_rules(rules, struct, prefix=None): """ Apply update rules using :obj:`~.FileOp` to a directory structure :param rules: directory structure as dictionary of dictionaries with :obj:`~.FileOp` keys. The structure will be modified. :param struct: directory structure as dictionary of dictionaries :param prefix: prefix path for the structure :return: directory structure with keys removed according to the rules """ if prefix is None: prefix = os.getcwd() for k, v in rules.items(): if isinstance(v, dict): apply_update_rules(v, struct[k], join_path(prefix, k)) else: path = join_path(prefix, k) if v == FileOp.NO_OVERWRITE and os.path.exists(path): struct.pop(k, None) elif v == FileOp.NO_CREATE: struct.pop(k, None) return struct