Source code for pyscaffold.extensions.namespace

# -*- coding: utf-8 -*-
"""
Extension that adjust project file tree to include a namespace package.

This extension adds a **namespace** option to
:obj:`~pyscaffold.api.create_project` and provides correct values for the
options **root_pkg** and **namespace_pkg** to the following functions in the
action list.
"""
from __future__ import absolute_import

import argparse
import os
from os.path import join as join_path
from os.path import isdir

from .. import templates, utils
from ..api import Extension
from ..api import helpers
from ..log import logger


[docs]class Namespace(Extension): """Omit creation of skeleton.py"""
[docs] def augment_cli(self, parser): """Add an option to parser that enables the namespace extension. Args: parser (argparse.ArgumentParser): CLI parser object """ parser.add_argument( self.flag, dest=self.name, default=None, action=create_namespace_parser(self), metavar="NS1[.NS2]", help="put your project inside a namespace package")
[docs] def activate(self, actions): """Register an action responsible for adding namespace to the package. Args: actions (list): list of actions to perform Returns: list: updated list of actions """ actions = helpers.register(actions, enforce_namespace_options, after='get_default_options') actions = helpers.register(actions, add_namespace, before='apply_update_rules') return helpers.register(actions, move_old_package, after='create_structure')
[docs]def create_namespace_parser(obj_ref): """Create a namespace parser. Args: obj_ref (Extension): object reference to the actual extension Returns: NamespaceParser: parser for namespace cli argument """ class NamespaceParser(argparse.Action): """Consumes the values provided, but also appends the extension function to the extensions list. """ def __call__(self, parser, namespace, values, option_string=None): # First ensure the extension function is stored inside the # 'extensions' attribute: extensions = getattr(namespace, 'extensions', []) extensions.append(obj_ref) setattr(namespace, 'extensions', extensions) # Now the extra parameters can be stored setattr(namespace, self.dest, values) # save the namespace cli argument for later obj_ref.args = values return NamespaceParser
[docs]def enforce_namespace_options(struct, opts): """Make sure options reflect the namespace usage.""" opts.setdefault('namespace', None) if opts['namespace']: opts['namespace'] = utils.prepare_namespace(opts['namespace']) opts['root_pkg'] = opts['namespace'][0] opts['qual_pkg'] = ".".join([opts['namespace'][-1], opts['package']]) return struct, opts
[docs]def add_namespace(struct, opts): """Prepend the namespace to a given file structure Args: struct (dict): directory structure as dictionary of dictionaries opts (dict): options of the project Returns: tuple(dict, dict): directory structure as dictionary of dictionaries and input options """ if not opts['namespace']: return struct, opts namespace = opts['namespace'][-1].split('.') base_struct = struct struct = base_struct[opts['project']]['src'] pkg_struct = struct[opts['package']] 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, opts
[docs]def move_old_package(struct, opts): """Move old package that may be eventually created without namespace Args: struct (dict): directory structure as dictionary of dictionaries opts (dict): options of the project Returns: tuple(dict, dict): directory structure as dictionary of dictionaries and input options """ old_path = join_path(opts['project'], 'src', opts['package']) namespace_path = opts['qual_pkg'].replace('.', os.sep) target = join_path(opts['project'], 'src', namespace_path) old_exists = opts['pretend'] or isdir(old_path) # ^ When pretending, pretend also an old folder exists # to show a worst case scenario log to the user... if old_exists and opts['qual_pkg'] != opts['package']: if not opts['pretend']: logger.warning( '\nA folder %r exists in the project directory, and it is ' 'likely to have been generated by a PyScaffold extension or ' 'manually by one of the current project authors.\n' 'Moving it to %r, since a namespace option was passed.\n' 'Please make sure to edit all the files that depend on this ' 'package to ensure the correct location.\n', opts['package'], namespace_path) utils.move(old_path, target=target, log=True, pretend=opts['pretend']) return struct, opts