public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-11 11:14 André Erdmann
  0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-11 11:14 UTC (permalink / raw
  To: gentoo-commits

commit:     03126a24a417833c13b4f27e3e7996d628aef988
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Wed Sep 11 11:13:32 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Wed Sep 11 11:13:32 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=03126a24

split roverlay/setupscript.py into several modules

---
 roverlay/runtime.py              |   13 +
 roverlay/setupscript.py          | 1069 --------------------------------------
 roverlay/setupscript/__init__.py |    2 +
 roverlay/setupscript/baseenv.py  |   72 +++
 roverlay/setupscript/hookenv.py  |  230 ++++++++
 roverlay/setupscript/initenv.py  |  308 +++++++++++
 roverlay/setupscript/runtime.py  |  474 +++++++++++++++++
 7 files changed, 1099 insertions(+), 1069 deletions(-)

diff --git a/roverlay/runtime.py b/roverlay/runtime.py
index 5ef540d..68580f0 100644
--- a/roverlay/runtime.py
+++ b/roverlay/runtime.py
@@ -272,6 +272,14 @@ class IndependentRuntimeEnvironment ( MinimalRuntimeEnvironment ):
    LOG_FORMAT = None
    LOG_LEVEL  = None
 
+   @classmethod
+   def run_default_main ( cls, *args, **kwargs ):
+      instance = cls ( *args, **kwargs )
+      instance.setup()
+      instance.default_main()
+      return instance
+   # --- end of run_default_main (...) ---
+
    def __init__ ( self, installed=True, stdout=None, stderr=None ):
       super ( IndependentRuntimeEnvironment, self ).__init__()
 
@@ -295,6 +303,11 @@ class IndependentRuntimeEnvironment ( MinimalRuntimeEnvironment ):
          self.INSTALLINFO = None
    # --- end of __init__ (...) ---
 
+   @roverlay.util.objects.abstractmethod
+   def default_main ( self ):
+      pass
+   # --- end of default_main (...) ---
+
    def create_new_config ( self, config_str=None, apply_defaults=True ):
       ctree = roverlay.config.tree.ConfigTree ( register_static=False )
 

diff --git a/roverlay/setupscript.py b/roverlay/setupscript.py
deleted file mode 100644
index 0e59015..0000000
--- a/roverlay/setupscript.py
+++ /dev/null
@@ -1,1069 +0,0 @@
-# R overlay -- setup script
-# -*- coding: utf-8 -*-
-
-from __future__ import print_function
-
-import argparse
-import collections
-import errno
-import logging
-import os
-import shutil
-import stat
-import sys
-import textwrap
-
-import roverlay.argutil
-import roverlay.argparser
-import roverlay.fsutil
-import roverlay.runtime
-
-import roverlay.config.defconfig
-import roverlay.config.entrymap
-import roverlay.config.entryutil
-
-import roverlay.static.hookinfo
-
-import roverlay.util.counter
-
-if sys.hexversion >= 0x3000000:
-   read_user_input = input
-else:
-   read_user_input = raw_input
-
-
-
-
-
-
-def arg_stdout_or_fs ( value ):
-   if value == '-':
-      return value
-   else:
-      return os.path.abspath ( os.path.expanduser ( value ) )
-# --- end of arg_stdout_or_fs (...) ---
-
-
-
-
-class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
-   MULTIPLE_COMMANDS = False
-   COMMAND_DESCRIPTION = {
-      'init':     'initialize roverlay\'s config and filesystem layout',
-      'mkconfig': 'generate a config file',
-   }
-   DEFAULT_COMMAND = "init"
-
-   COMMANDS_WITH_PRETEND = frozenset ({ 'init', })
-
-   SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', )
-   PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', )
-
-
-   def setup_setup ( self ):
-      arg = self.setup_setup_minimal ( title='common options' )
-
-      arg (
-         '--output', '-O', metavar="<file|dir|->", dest='output',
-         default='-', type=arg_stdout_or_fs,
-         flags=self.ARG_WITH_DEFAULT,
-         help='output file/dir/stream used by various commands (\'-\' for stdout)',
-      )
-
-      arg (
-         '--pretend', '-p', dest='pretend',
-         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
-         help='show what would be done',
-      )
-
-      arg (
-         '--ask', '-a', dest='wait_confirm',
-         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
-         help='get confirmation before actually doing anything',
-      )
-
-      return arg
-   # --- end of setup_setup (...) ---
-
-   def parse_setup ( self ):
-      self.parse_setup_minimal()
-
-      if self.parsed ['pretend']:
-         for cmd in self.get_commands():
-            if cmd not in self.__class__.COMMANDS_WITH_PRETEND:
-               self.parser.exit (
-                  "{!r} command does not support --pretend.".format ( cmd )
-               )
-   # --- end of parse_setup (...) ---
-
-   def setup_config ( self ):
-      arg = self.add_argument_group (
-         "config", title="options for the main config file"
-      )
-
-      arg (
-         '--expand-user', dest='config_expand_user',
-         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
-         help="expand \'~\' to the target user\'s home directory",
-      )
-
-      arg (
-         '--additions-dir', '-A', dest='additions_dir',
-         flags=self.ARG_WITH_DEFAULT|self.ARG_META_DIR,
-         type=roverlay.argutil.couldbe_dirstr_existing,
-         help=(
-            'directory for user-provided content '
-            '(patches, hand-written ebuilds, hooks)'
-         ),
-      )
-
-      arg (
-         '--variable', '-v', metavar="<key=\"value\">", dest='config_vars',
-         default=[], action='append',
-         type=roverlay.argutil.is_config_opt,
-         help="additional variables",
-      )
-
-      return arg
-   # --- end of setup_config (...) ---
-
-   def parse_config ( self ):
-      for kv in self.parsed ['config_vars']:
-         key, eq_sign, val = kv.partition('=')
-         if key in { 'ADDITIONS_DIR', 'OVERLAY_ADDITIONS_DIR' }:
-            self.parser.exit (
-               'use \'--additions-dir {0}\' instead of '
-               '\'--variable ADDITIONS_DIR={0}\'.'.format ( val )
-            )
-
-      self.parsed ['config_vars'].append (
-         "ADDITIONS_DIR=" + self.parsed ['additions_dir']
-      )
-   # --- end of parse_config (...) ---
-
-   def setup_init ( self ):
-      arg = self.add_argument_group (
-         'init', title='options for the \'init\' command'
-      )
-
-      arg (
-         '--enable-default-hooks', dest='want_default_hooks',
-         default=True,
-         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
-         help='enable/update the default hooks',
-      )
-
-      arg (
-         '--no-default-hooks', dest='want_default_hooks',
-         flags=self.ARG_SHARED|self.ARG_OPT_OUT,
-         help='disable the default hooks',
-      )
-
-      arg (
-         '--import-config', '-I', dest='import_config',
-         default="symlink",
-         choices=[
-            "disable",
-            "symlink", "symlink=root",
-            "symlink=dirs", "symlink=files",
-            "copy"
-         ],
-         metavar='<mode>',
-         flags=self.ARG_WITH_DEFAULT,
-         help=(
-            'choose whether and how --conf-root should be imported: '
-             '%(choices)s'
-         ),
-      )
-
-      arg (
-         '--no-import-config', dest='import_config',
-         action='store_const', const='disable',
-         help='disable config import (same as \'--import-config disable\')',
-      )
-
-
-      arg (
-         '--target-uid', dest='target_uid', default=os.getuid(),
-         metavar='<uid>', type=roverlay.argutil.is_uid,
-         flags=self.ARG_WITH_DEFAULT,
-         help="uid of the user that will run roverlay",
-      )
-
-      arg (
-         '--target-gid', dest='target_gid', default=os.getgid(),
-         metavar='<gid>', type=roverlay.argutil.is_gid,
-         flags=self.ARG_WITH_DEFAULT,
-         help='gid of the user that will run roverlay',
-      )
-
-   # --- end of setup_init (...) ---
-
-   def parse_init ( self ):
-      my_uid = os.getuid()
-
-      if my_uid and self.parsed ['target_uid'] != my_uid:
-         if self.parsed ['pretend']:
-            sys.stderr.write (
-               "!!! --target-uid: users cannot configure other users.\n\n"
-            )
-         else:
-            self.parser.exit (
-               "--target-uid: users cannot configure other users."
-            )
-   # --- end of parse_init (...) ---
-
-
-# --- end of SetupArgParser ---
-
-
-class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
-
-   LOG_LEVEL         = logging.INFO
-
-   SHARED_DIR_MODE   = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
-   PRIVATE_DIR_MODE  = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
-   SHARED_FILE_MODE  = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
-   PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
-
-   def __init__ ( self, *args, **kwargs ):
-      super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )
-
-      self.UID             = os.getuid()
-      self.GID             = os.getgid()
-
-      self.expanduser      = None
-      self.fs_ops          = None
-      self.fs_ops_virtual  = None
-
-      self.want_chown      = None
-      self.data_root       = None
-      self.work_root       = None
-      self.conf_root       = None
-      self.user_conf_root  = None
-
-# not used
-#      COLUMNS = os.environ.get ( 'COLUMNS', 78 )
-#
-#      self.text_wrapper = textwrap.TextWrapper (
-#         width=COLUMNS, initial_indent='', subsequent_indent='',
-#         break_long_words=False, break_on_hyphens=False,
-#      )
-   # --- end of __init__ (...) ---
-
-   def create_argparser ( self ):
-      instinfo = self.access_constant ( 'INSTALLINFO' )
-
-      return SetupArgParser (
-         description = 'roverlay setup script',
-         defaults    = {
-            'work_root'         : instinfo ['workroot'],
-            'data_root'         : instinfo ['libexec'],
-            'conf_root'         : instinfo ['confroot'],
-            'private_conf_root' : instinfo ['workroot'] + os.sep + 'config',
-            'import_config'     : 'symlink=root',
-            'additions_dir'     : instinfo ['workroot'] + os.sep + 'files',
-         },
-      )
-   # --- end of create_argparser (...) ---
-
-   def _get_config_roots ( self ):
-      return (
-         os.path.realpath ( self.conf_root ),
-         os.path.realpath ( self.user_conf_root )
-      )
-   # --- end of get_config_roots (...) ---
-
-   def get_user_config_root ( self ):
-      conf_root, user_conf_root = self._get_config_roots()
-      if conf_root == user_conf_root:
-         return None
-      else:
-         return user_conf_root
-   # --- end of get_user_config_root (...) ---
-
-   def get_config_file_path ( self ):
-      return (
-         self.work_root + os.sep + self.access_constant ( 'config_file_name' )
-      )
-   # --- end of get_config_file_path (...) ---
-
-   def create_new_target_config ( self ):
-      return self.create_new_config (
-         config_str=self.create_config_file ( expand_user=True )
-      )
-   # --- end of create_new_target_config (...) ---
-
-   def _expanduser_pwd ( self, fspath ):
-      return roverlay.fsutil.pwd_expanduser (
-         fspath, self.options ['target_uid']
-      )
-   # --- end of _expanduser_pwd (...) ---
-
-   def create_config_file ( self, expand_user=False ):
-      conf_creator = roverlay.config.defconfig.RoverlayConfigCreation (
-         is_installed = self.is_installed(),
-         work_root    = (
-            self.work_root if expand_user else self.options ['work_root']
-         ),
-         data_root    = (
-            self.data_root if expand_user else self.options ['data_root']
-         ),
-         conf_root    = (
-            self.user_conf_root if expand_user
-            else self.options ['private_conf_root']
-         ),
-      )
-
-      for kv in self.options ['config_vars']:
-         key, sepa, value = kv.partition ( '=' )
-         if not sepa:
-            raise Exception ( "bad variable given: {!r}".format ( kv ) )
-         else:
-            conf_creator.set_option ( key, value )
-
-      return conf_creator.get_str()
-   # --- end of create_config_file (...) ---
-
-   def write_config_file ( self, output=None, expand_user=None ):
-      config_file_str = self.create_config_file (
-         expand_user = (
-            self.options ['config_expand_user'] if expand_user is None
-            else expand_user
-         ),
-      )
-      if not output or output == '-':
-         self.info ( config_file_str )
-      else:
-         with open ( output, 'wt' ) as FH:
-            FH.write ( config_file_str )
-   # --- end of write_config_file (...) ---
-
-   def auto_reconfigure ( self ):
-      self.reconfigure ( self.create_config_file() )
-   # --- end of auto_reconfigure (...) ---
-
-   def setup ( self ):
-      self.PWD_INITIAL = os.getcwd()
-      self.setup_common()
-
-      # ref
-      options = self.options
-
-      target_uid = options ['target_uid']
-      target_gid = options ['target_gid']
-
-      self.want_chown = ( target_uid != self.UID or target_gid != self.GID )
-
-      if self.UID == target_uid:
-         expanduser      = os.path.expanduser
-         self.expanduser = os.path.expanduser
-      else:
-         expanduser      = self._expanduser_pwd
-         self.expanduser = self._expanduser_pwd
-
-      self.work_root      = expanduser ( options ['work_root'] )
-      self.data_root      = expanduser ( options ['data_root'] )
-      self.conf_root      = expanduser ( options ['conf_root'] )
-      self.user_conf_root = expanduser ( options ['private_conf_root'] )
-
-
-      self.fs_ops_virtual = {
-         'private_dir': roverlay.fsutil.VirtualFsOperations (
-            uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
-         ),
-         'shared_dir': roverlay.fsutil.VirtualFsOperations (
-            uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
-         ),
-         'private_file': roverlay.fsutil.VirtualFsOperations (
-            uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
-         ),
-         'shared_file': roverlay.fsutil.VirtualFsOperations (
-            uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
-         ),
-      }
-
-      if options ['pretend']:
-         self.fs_ops = self.fs_ops_virtual
-      else:
-         self.fs_ops =  {
-            'private_dir': roverlay.fsutil.FsOperations (
-               uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
-            ),
-            'shared_dir': roverlay.fsutil.FsOperations (
-               uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
-            ),
-            'private_file': roverlay.fsutil.FsOperations (
-               uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
-            ),
-            'shared_file': roverlay.fsutil.FsOperations (
-               uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
-            ),
-         }
-
-      # bind fs_ops
-      self.private_dir  = self.fs_ops ['private_dir']
-      self.shared_dir   = self.fs_ops ['shared_dir']
-      self.private_file = self.fs_ops ['private_file']
-      self.shared_file  = self.fs_ops ['shared_file']
-   # --- end of setup (...) ---
-
-   def wait_confirm ( self,
-      message=None, message_inline=None,
-      prepend_newline=True, append_newline=True
-   ):
-      try:
-         if prepend_newline:
-            self.info ( '\n' )
-
-         if message is not None:
-            self.info ( str ( message ) + '\n' )
-
-         if message_inline:
-            self.info (
-               "Press Enter to continue ({!s}) ... ".format ( message_inline )
-            )
-         else:
-            self.info ( "Press Enter to continue ... " )
-
-         self.stdout.flush()
-
-         ret = read_user_input().strip()
-
-      except ( KeyboardInterrupt, EOFError ):
-         self.info ( "\n" )
-         sys.exit ( 130 )
-      else:
-         if append_newline:
-            self.info ( '\n' )
-
-         return ret
-   # --- end of wait_confirm (...) ---
-
-   def wait_confirm_can_skip ( self,
-      SKIP_WORDS=frozenset({ 'skip', 'no', 'n'}), **kwargs
-   ):
-      assert 'message_inline' not in kwargs
-
-      if self.options ['wait_confirm']:
-
-         user_reply = self.wait_confirm (
-            message_inline="type {} to skip this step".format (
-               '/'.join ( repr( word ) for word in sorted( SKIP_WORDS ) )
-            ),
-            **kwargs
-         )
-
-         return user_reply.lower() not in SKIP_WORDS
-      else:
-         return True
-   # --- end of wait_confirm (...) ---
-
-   def get_init_env ( self ):
-      return SetupInitEnvironment ( self )
-   # --- end of get_init_env (...) ---
-
-   def get_hook_env ( self ):
-      return SetupHookEnvironment ( self )
-   # --- end of get_hook_env (...) ---
-
-# --- end of SetupEnvironment ---
-
-
-class SetupSubEnvironment ( object ):
-
-   NEEDS_CONFIG_TREE = False
-
-   ACTIONS = None
-
-   def __init__ ( self, setup_env ):
-      super ( SetupSubEnvironment, self ).__init__()
-
-      self.setup_env = setup_env
-      self.stdout    = setup_env.stdout
-      self.stderr    = setup_env.stderr
-      self.info      = setup_env.info
-      self.error     = setup_env.error
-
-      if self.NEEDS_CONFIG_TREE:
-         self.config = self.setup_env.create_new_target_config()
-      else:
-         self.config = None
-
-      self.setup()
-   # --- end of __init__ (...) ---
-
-   def setup ( self ):
-      pass
-   # --- end of setup (...) ---
-
-   def run ( self, steps_to_skip=None, verbose_skip=True, steps=None ):
-      pretend = self.setup_env.options ['pretend']
-      ACTIONS = steps if steps is not None else self.ACTIONS
-
-      if ACTIONS:
-         if steps_to_skip:
-            methods_to_call = [
-               (
-                  None if item[0] in steps_to_skip
-                  else getattr ( self, 'do_' + item[0] )
-               ) for item in ACTIONS
-            ]
-         else:
-            methods_to_call = [
-               getattr ( self, 'do_' + item[0] ) for item in ACTIONS
-            ]
-
-         wait_confirm_can_skip = self.setup_env.wait_confirm_can_skip
-
-
-         for method, action in zip ( methods_to_call, ACTIONS ):
-            if method is None:
-               if verbose_skip:
-                  self.info ( "{}: skipped.\n".format ( action[0] ) )
-
-            elif not action[1]:
-               method ( pretend=pretend )
-
-            elif wait_confirm_can_skip (
-               message=method.__doc__, append_newline=False
-            ):
-               method ( pretend=pretend )
-            else:
-               self.info ( "skipped.\n" )
-
-      else:
-         raise NotImplementedError (
-            "{}.{}()".format ( self.__class__.__name__, "do_all" )
-         )
-   # --- end of run (...) ---
-
-
-# --- end of SetupSubEnvironment ---
-
-
-class SetupInitEnvironment ( SetupSubEnvironment ):
-
-   ACTIONS = (
-      ( 'pretend', False ),
-      ( 'prepare_config_file', False ),
-      ( 'import_config', True ),
-      ( 'setupdirs', True ),
-      ( 'write_config_file', True ),
-      ( 'enable_default_hooks', True ),
-   )
-
-   NEEDS_CONFIG_TREE = True
-
-   def setup ( self ):
-      self.config_file_str = None
-   # --- end of setup (...) ---
-
-   IMPORT_CONFIG_DESC = {
-      'disable'       : False,
-      'symlink'       : 'symlink {conf_root} to {user_conf_root}',
-      'symlink=root'  : 'symlink {conf_root} to {user_conf_root}',
-      'symlink=dirs'  : (
-         'symlink files/dirs from {conf_root} to {user_conf_root}/'
-      ),
-      'symlink=files' : (
-         'recursively copy {conf_root} to {user_conf_root}, '
-         'but symlink files instead of copying them'
-      ),
-      'copy'          : 'recursively copy {conf_root} to {user_conf_root}',
-   }
-
-   def gen_pretend_options ( self ):
-      options = self.setup_env.options
-
-      def get_option ( key ):
-         val = options [key]
-         if isinstance ( val, str ):
-            return val
-         elif hasattr ( val, '__iter__' ):
-            return ' '.join ( str(x) for x in val )
-         elif isinstance ( val, bool ):
-            return "yes" if val else "no"
-         elif val is None:
-            return "<undef>"
-         else:
-            return str ( val )
-      # --- end of get_option (...) ---
-
-      comment_path    = lambda a, b: a if a == b else ( a + ' (' + b + ')' )
-      get_path_option = lambda k, b: comment_path ( get_option ( k ), b )
-
-      fmt_vars = {
-         'conf_root'     : options ['conf_root'],
-         'user_conf_root': options ['private_conf_root']
-      }
-
-      yield ( "user/uid",  get_option ( 'target_uid' ) )
-      yield ( "group/gid", get_option ( 'target_gid' ) )
-
-      yield ( "work root",
-         get_path_option ( 'work_root', self.setup_env.work_root )
-      )
-      yield ( "data root",
-         get_path_option ( 'data_root', self.setup_env.data_root )
-      )
-      yield ( "roverlay\'s config root",
-         get_path_option ( 'conf_root', self.setup_env.conf_root )
-      )
-      yield ( "user\'s config root",
-         get_path_option ( 'private_conf_root', self.setup_env.user_conf_root )
-      )
-
-      import_config = get_option ( 'import_config' )
-      if import_config == 'disable':
-         yield ( "import config", "no" )
-      else:
-         yield ( "import config",
-            "yes, "
-            + self.IMPORT_CONFIG_DESC [import_config].format ( **fmt_vars )
-         )
-
-      yield ( "enable default hooks", get_option ( 'want_default_hooks' ) )
-      yield ( "additional config variables", get_option ( 'config_vars' ) )
-   # --- end of gen_pretend_options (...) ---
-
-   def gen_pretend_lines ( self, append_newline=True ):
-      options  = list ( self.gen_pretend_options() )
-      COLUMNS  = os.environ.get ( 'COLUMNS', 78 )
-      desc_len = min ( COLUMNS // 2,
-         1 + max ( len(desc) for desc, value in options )
-      )
-
-      line_wrapper = textwrap.TextWrapper (
-         width=COLUMNS, initial_indent='',
-         subsequent_indent=( (desc_len+5) * ' ' ),
-         break_long_words=False, break_on_hyphens=False,
-      )
-
-
-      yield 'Configuration:'
-      for desc, value in options:
-         yield line_wrapper.fill (
-            "- {desc:<{l}}: {value}".format (
-               desc=desc, l=desc_len, value=value,
-            )
-         )
-
-      if append_newline:
-         yield ""
-   # --- end of gen_pretend_lines (...) ---
-
-   def do_pretend ( self, pretend ):
-      """Shows what would be done."""
-      self.info ( '\n'.join ( self.gen_pretend_lines() ) + '\n' )
-   # --- end of do_pretend (...) ---
-
-   def do_prepare_config_file ( self, pretend ):
-      """Creates the config file (in memory)."""
-      self.config_file_str = self.setup_env.create_config_file (
-         expand_user=self.setup_env.options ['config_expand_user']
-      )
-   # --- end of do_prepare_config_file (...) ---
-
-   def do_import_config ( self, pretend ):
-      """Imports the config."""
-      mode           = self.setup_env.options ['import_config']
-      fs_ops         = self.setup_env.private_dir
-      user_conf_root = self.setup_env.get_user_config_root()
-      # assert os.path.isdir ( os.path.dirname(user_conf_root) == work_root )
-
-      if user_conf_root is None and (
-         fs_ops.unlink ( self.setup_env.user_conf_root )
-      ):
-         # config_root was a symlink
-
-         if pretend:
-            user_conf_root = self.setup_env.user_conf_root
-         else:
-            user_conf_root = self.setup_env.get_user_config_root()
-
-      # -- end if
-
-
-      if user_conf_root is None:
-         self.info (
-            "user has no private config directory - skipping config import.\n"
-         )
-
-      elif mode in { 'symlink=root', 'symlink' }:
-         if not fs_ops.wipe ( user_conf_root ):
-            self.setup_env.die (
-               "failed to remove {!r}.\n".format ( user_conf_root )
-            )
-         elif not fs_ops.symlink ( self.setup_env.conf_root, user_conf_root ):
-            self.setup_env.die (
-               "could not create symlink to {!r}.".format (
-                  self.setup_env.conf_root
-               )
-            )
-
-
-         pass
-      else:
-         raise NotImplementedError ( mode )
-   # --- end of do_import_config (...) ---
-
-   def do_setupdirs ( self, pretend ):
-      """Creates directories with proper permissions."""
-      create_subdir_check = roverlay.fsutil.create_subdir_check
-      config              = self.config
-      find_config_path    = roverlay.config.entryutil.find_config_path
-      dodir_private       = self.setup_env.private_dir.dodir
-      dodir_shared        = self.setup_env.shared_dir.dodir
-
-
-      WANT_USERDIR = roverlay.config.entrymap.WANT_USERDIR
-      WANT_PRIVATE = roverlay.config.entrymap.WANT_PRIVATE
-      WANT_FILEDIR = roverlay.config.entrymap.WANT_FILEDIR
-
-      listlike    = lambda a: (
-         hasattr(a, '__iter__') and not isinstance(a, str)
-      )
-      iter_values = lambda b: (
-         () if b is None else (b if listlike(b) else ( b, ))
-      )
-
-      dirs_exclude = [
-         create_subdir_check ( self.setup_env.conf_root ),
-         create_subdir_check ( self.setup_env.data_root ),
-      ]
-      if self.setup_env.get_user_config_root() is None:
-         dirs_exclude.append (
-            create_subdir_check ( self.setup_env.user_conf_root )
-         )
-      else:
-         print ( self.setup_env.get_user_config_root() )
-
-      # don't print exclude/skip messages more than once per dir
-      dirs_already_excluded = set()
-
-      private_dirs       = set()
-      private_dirs_chown = set()
-      shared_dirs        = set()
-      shared_dirs_chown  = set()
-
-      # it's not necessary to create all of the listed dirs because some of
-      # them are automatically created at runtime, but doing so results in
-      # a (mostly) complete filesystem layout
-      #
-      for config_key, entry in (
-         roverlay.config.entrymap.CONFIG_ENTRY_MAP.items()
-      ):
-         if isinstance ( entry, dict ) and 'want_dir_create' in entry:
-            for value in iter_values (
-               config.get ( find_config_path ( config_key ), None )
-            ):
-               dirmask = entry ['want_dir_create']
-               dirpath = (
-                  os.path.dirname ( value.rstrip ( os.sep ) )
-                  if dirmask & WANT_FILEDIR else value.rstrip ( os.sep )
-               )
-
-               if not dirpath or dirpath in dirs_already_excluded:
-                  pass
-
-               elif any ( ex ( dirpath ) for ex in dirs_exclude ):
-                  self.info (
-                     "setupdirs: excluding {!r}\n".format ( dirpath )
-                  )
-                  dirs_already_excluded.add ( dirpath )
-
-               elif os.path.islink ( dirpath ):
-                  self.info (
-                     '{!r} is a symlink - skipping setupdir '
-                     'actions.\n'.format ( dirpath )
-                  )
-                  dirs_already_excluded.add ( dirpath )
-
-               elif dirmask & WANT_USERDIR:
-                  if dirmask & WANT_PRIVATE:
-                     private_dirs_chown.add ( dirpath )
-                  else:
-                     shared_dirs_chown.add ( dirpath )
-
-               elif dirmask & WANT_PRIVATE:
-                  private_dirs.add ( dirpath )
-
-               else:
-                  shared_dirs.add ( dirpath )
-      # -- end for
-
-
-      private_dirs      -= private_dirs_chown
-      shared_dirs_chown -= private_dirs
-      shared_dirs       -= shared_dirs_chown
-
-      for dirpath in shared_dirs:
-         dodir_shared ( dirpath, chown=False )
-
-      for dirpath in shared_dirs_chown:
-         dodir_shared ( dirpath, chown=True )
-
-      for dirpath in private_dirs:
-         dodir_private ( dirpath, chown=False )
-
-      for dirpath in private_dirs_chown:
-         dodir_private ( dirpath, chown=True )
-
-      self.setup_env.private_dir.chmod_chown ( self.setup_env.work_root )
-   # --- end of do_setupdirs (...) ---
-
-   def do_write_config_file ( self, pretend ):
-      """Writes the config file to disk."""
-      cfile = self.setup_env.get_config_file_path()
-      if not self.config_file_str:
-         self.setup_env.die ( "no config file created!" )
-      elif pretend:
-         self.info ( "Would write config file to {!r}.\n".format ( cfile ) )
-      else:
-         with open ( cfile, 'wt' ) as FH:
-            FH.write ( self.config_file_str )
-
-      self.setup_env.private_file.chmod_chown ( cfile )
-   # --- end of do_write_config_file (...) ---
-
-   def do_enable_default_hooks ( self, pretend ):
-      """Enables the default hooks, e.g. git history creation."""
-      hook_env = self.setup_env.get_hook_env()
-      if not hook_env.enable_defaults():
-         self.setup_env.die ( "failed to enable hooks." )
-   # --- end of do_enable_default_hooks (...) ---
-
-# --- end of SetupInitEnvironment ---
-
-
-class HookScript ( object ):
-
-   def __init__ ( self, fspath, filename=None ):
-      super ( HookScript, self ).__init__()
-      fname = (
-         filename if filename is not None else os.path.basename ( fspath )
-      )
-
-      self.fspath  = fspath
-      self.name    = os.path.splitext ( fname )[0] or fname
-      static_entry = roverlay.static.hookinfo.get ( self.name, None )
-
-      if static_entry is not None:
-         self.default_events = static_entry[0]
-         self.priority       = static_entry[1]
-         self.is_hidden      = static_entry[2]
-      else:
-         self.default_events = False
-         self.priority       = None
-         self.is_hidden      = False
-   # --- end of __init__ (...) ---
-
-   def is_visible ( self ):
-      return not self.is_hidden and (
-         self.priority is None or self.priority >= 0
-      )
-   # --- end of is_visible (...) ---
-
-   def __str__ ( self ):
-      yesno = lambda k: 'y' if k else 'n'
-      return "<{cls} {name!r}, hidden={h} prio={p}>".format (
-         cls=self.__class__.__name__,
-         name=self.name,
-         h=yesno ( self.is_hidden ),
-         p=(
-            "auto" if self.priority is None else
-               ( "IGNORE" if self.priority < 0 else self.priority )
-         ),
-      )
-   # --- end of __str__ (...) ---
-
-   def set_priority_from_generator ( self, number_gen, only_if_unset=True ):
-      if self.priority is None:
-         self.priority = next ( number_gen )
-         return True
-      elif only_if_unset or self.priority < 0:
-         return False
-      else:
-         self.priority = next ( number_gen )
-         return True
-   # --- end of set_priority_from_generator (...) ---
-
-   def get_dest_name ( self, file_ext='.sh', digit_len=2 ):
-      # file_ext has to be .sh, else the script doesn't get recognized
-      # by mux.sh
-
-      prio = self.priority
-      if prio is None or prio < 0:
-         raise AssertionError ( "hook script has no priority." )
-
-      return "{prio:0>{l}d}-{fname}{f_ext}".format (
-         prio=prio, fname=self.name, f_ext=file_ext, l=digit_len,
-      )
-   # --- end of get_dest_name (...) ---
-
-
-# --- end of HookScript ---
-
-
-class HookScriptDir ( object ):
-
-   def __init__ ( self, root ):
-      super ( HookScriptDir, self ).__init__()
-
-      self.root      = root
-      self._scripts  = collections.OrderedDict()
-   # --- end of __init__ (...) ---
-
-   def __bool__ ( self ):
-      return bool ( self._scripts )
-   # --- end of __bool__ (...) ---
-
-   def get_script ( self, name ):
-      script = self._scripts [name]
-      return script if script.is_visible() else None
-   # --- end of get_scripts (...) ---
-
-   def iter_default_scripts ( self, unpack=False ):
-      if unpack:
-         for script in self._scripts.values():
-            if script.default_events:
-               for event in script.default_events:
-                  yield ( event, script )
-      else:
-         for script in self._scripts.values():
-            if script.default_events:
-               yield script
-   # --- end of iter_default_scripts (...) ---
-
-   def get_default_scripts ( self ):
-      scripts = dict()
-      for event, script in self.iter_default_scripts ( unpack=True ):
-         if event not in scripts:
-            scripts [event] = [ script ]
-         else:
-            scripts [event].append ( script )
-
-      return scripts
-   # --- end of get_default_scripts (...) ---
-
-   def iter_scripts ( self ):
-      for script in self._scripts.values():
-         if script.is_visible():
-            yield script
-   # --- end of iter_scripts (...) ---
-
-   def scan ( self ):
-      root = self.root
-      try:
-         filenames = sorted ( os.listdir ( root ) )
-      except OSError as oserr:
-         if oserr.errno != errno.ENOENT:
-            raise
-
-      else:
-         for fname in filenames:
-            fspath = root + os.sep + fname
-            if os.path.isfile ( fspath ):
-               script_obj = HookScript ( fspath, fname )
-               self._scripts [script_obj.name] = script_obj
-   # --- end of scan (...) ---
-
-# --- end of HookScriptDir ---
-
-
-class SetupHookEnvironment ( SetupSubEnvironment ):
-
-   NEEDS_CONFIG_TREE = True
-
-   def setup ( self ):
-      additions_dir = self.config.get ( 'OVERLAY.additions_dir', None )
-      if additions_dir:
-         self.user_hook_root = os.path.join ( additions_dir, 'hooks' )
-         self.writable       = self.setup_env.private_file.check_writable (
-            self.user_hook_root + os.sep + '.keep'
-         )
-      else:
-         self.user_hook_root = None
-         self.writable       = None
-
-      self.hook_root = HookScriptDir (
-         os.path.join ( self.setup_env.data_root, 'hooks' )
-      )
-      self.hook_root.scan()
-      self._prio_gen = roverlay.util.counter.UnsafeCounter ( 30 )
-   # --- end of setup (...) ---
-
-   def _link_hook ( self, source, link ):
-      if os.path.lexists ( link ):
-         linkdest = os.path.realpath ( link )
-
-         message = 'Skipping activation of hook {!r} - '.format ( link )
-
-         if linkdest == source or linkdest == os.path.realpath ( source ):
-            self.info ( message + "already set up.\n" )
-            return True
-
-         elif link != linkdest:
-            # symlink or link was relative
-            self.error ( message + "is a link to another file.\n" )
-         else:
-            self.error ( message + "exists, but is not a link.\n" )
-
-         return None
-      else:
-         return self.setup_env.private_file.symlink ( source, link )
-   # --- end of _link_hook (...) ---
-
-   def link_hooks_v ( self, event_name, hooks ):
-      success = False
-
-      if self.writable and self.user_hook_root:
-         destdir = self.user_hook_root + os.sep + event_name
-         self.setup_env.private_dir.dodir ( destdir )
-
-         to_link = []
-         for script in hooks:
-            script.set_priority_from_generator ( self._prio_gen )
-            to_link.append (
-               ( script.fspath, destdir + os.sep + script.get_dest_name() )
-            )
-
-         success = True
-         for source, link_name in to_link:
-            if self._link_hook ( source, link_name ) is False:
-               success = False
-      # -- end if
-
-      return success
-   # --- end of link_hooks_v (...) ---
-
-   def enable_defaults ( self ):
-      # not strict: missing hooks are ignored
-      success = False
-      if self.hook_root:
-         success = True
-         default_hooks = self.hook_root.get_default_scripts()
-         for event, hooks in default_hooks.items():
-            if not self.link_hooks_v ( event, hooks ):
-               success = False
-      # -- end if
-
-      return success
-   # --- end of enable_defaults (...) ---
-
-
-# --- end of SetupHookEnvironment ---
-
-
-def setup_main():
-   env = SetupEnvironment()
-   env.setup()
-
-   if env.wants_command ( "mkconfig" ):
-      env.write_config_file ( env.options ['output'] )
-   elif env.wants_command ( "init" ):
-      env.get_init_env().run()
-
-
-# --- end of setup_main (...) ---

diff --git a/roverlay/setupscript/__init__.py b/roverlay/setupscript/__init__.py
new file mode 100644
index 0000000..740a69c
--- /dev/null
+++ b/roverlay/setupscript/__init__.py
@@ -0,0 +1,2 @@
+# R overlay -- setup script
+# -*- coding: utf-8 -*-

diff --git a/roverlay/setupscript/baseenv.py b/roverlay/setupscript/baseenv.py
new file mode 100644
index 0000000..349d077
--- /dev/null
+++ b/roverlay/setupscript/baseenv.py
@@ -0,0 +1,72 @@
+# R overlay -- setup script, base env
+# -*- coding: utf-8 -*-
+
+class SetupSubEnvironment ( object ):
+
+   NEEDS_CONFIG_TREE = False
+
+   ACTIONS = None
+
+   def __init__ ( self, setup_env ):
+      super ( SetupSubEnvironment, self ).__init__()
+
+      self.setup_env = setup_env
+      self.stdout    = setup_env.stdout
+      self.stderr    = setup_env.stderr
+      self.info      = setup_env.info
+      self.error     = setup_env.error
+
+      if self.NEEDS_CONFIG_TREE:
+         self.config = self.setup_env.create_new_target_config()
+      else:
+         self.config = None
+
+      self.setup()
+   # --- end of __init__ (...) ---
+
+   def setup ( self ):
+      pass
+   # --- end of setup (...) ---
+
+   def run ( self, steps_to_skip=None, verbose_skip=True, steps=None ):
+      pretend = self.setup_env.options ['pretend']
+      ACTIONS = steps if steps is not None else self.ACTIONS
+
+      if ACTIONS:
+         if steps_to_skip:
+            methods_to_call = [
+               (
+                  None if item[0] in steps_to_skip
+                  else getattr ( self, 'do_' + item[0] )
+               ) for item in ACTIONS
+            ]
+         else:
+            methods_to_call = [
+               getattr ( self, 'do_' + item[0] ) for item in ACTIONS
+            ]
+
+         wait_confirm_can_skip = self.setup_env.wait_confirm_can_skip
+
+
+         for method, action in zip ( methods_to_call, ACTIONS ):
+            if method is None:
+               if verbose_skip:
+                  self.info ( "{}: skipped.\n".format ( action[0] ) )
+
+            elif not action[1]:
+               method ( pretend=pretend )
+
+            elif wait_confirm_can_skip (
+               message=method.__doc__, append_newline=False
+            ):
+               method ( pretend=pretend )
+            else:
+               self.info ( "skipped.\n" )
+
+      else:
+         raise NotImplementedError (
+            "{}.{}()".format ( self.__class__.__name__, "do_all" )
+         )
+   # --- end of run (...) ---
+
+# --- end of SetupSubEnvironment ---

diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py
new file mode 100644
index 0000000..9c5fedb
--- /dev/null
+++ b/roverlay/setupscript/hookenv.py
@@ -0,0 +1,230 @@
+# R overlay -- setup script, env for managing hooks
+# -*- coding: utf-8 -*-
+
+import collections
+import errno
+import os
+
+
+import roverlay.static.hookinfo
+import roverlay.util.counter
+
+
+class HookScript ( object ):
+
+   def __init__ ( self, fspath, filename=None ):
+      super ( HookScript, self ).__init__()
+      fname = (
+         filename if filename is not None else os.path.basename ( fspath )
+      )
+
+      self.fspath  = fspath
+      self.name    = os.path.splitext ( fname )[0] or fname
+      static_entry = roverlay.static.hookinfo.get ( self.name, None )
+
+      if static_entry is not None:
+         self.default_events = static_entry[0]
+         self.priority       = static_entry[1]
+         self.is_hidden      = static_entry[2]
+      else:
+         self.default_events = False
+         self.priority       = None
+         self.is_hidden      = False
+   # --- end of __init__ (...) ---
+
+   def is_visible ( self ):
+      return not self.is_hidden and (
+         self.priority is None or self.priority >= 0
+      )
+   # --- end of is_visible (...) ---
+
+   def __str__ ( self ):
+      yesno = lambda k: 'y' if k else 'n'
+      return "<{cls} {name!r}, hidden={h} prio={p}>".format (
+         cls=self.__class__.__name__,
+         name=self.name,
+         h=yesno ( self.is_hidden ),
+         p=(
+            "auto" if self.priority is None else
+               ( "IGNORE" if self.priority < 0 else self.priority )
+         ),
+      )
+   # --- end of __str__ (...) ---
+
+   def set_priority_from_generator ( self, number_gen, only_if_unset=True ):
+      if self.priority is None:
+         self.priority = next ( number_gen )
+         return True
+      elif only_if_unset or self.priority < 0:
+         return False
+      else:
+         self.priority = next ( number_gen )
+         return True
+   # --- end of set_priority_from_generator (...) ---
+
+   def get_dest_name ( self, file_ext='.sh', digit_len=2 ):
+      # file_ext has to be .sh, else the script doesn't get recognized
+      # by mux.sh
+
+      prio = self.priority
+      if prio is None or prio < 0:
+         raise AssertionError ( "hook script has no priority." )
+
+      return "{prio:0>{l}d}-{fname}{f_ext}".format (
+         prio=prio, fname=self.name, f_ext=file_ext, l=digit_len,
+      )
+   # --- end of get_dest_name (...) ---
+
+
+# --- end of HookScript ---
+
+
+class HookScriptDir ( object ):
+
+   def __init__ ( self, root ):
+      super ( HookScriptDir, self ).__init__()
+
+      self.root      = root
+      self._scripts  = collections.OrderedDict()
+   # --- end of __init__ (...) ---
+
+   def __bool__ ( self ):
+      return bool ( self._scripts )
+   # --- end of __bool__ (...) ---
+
+   def get_script ( self, name ):
+      script = self._scripts [name]
+      return script if script.is_visible() else None
+   # --- end of get_scripts (...) ---
+
+   def iter_default_scripts ( self, unpack=False ):
+      if unpack:
+         for script in self._scripts.values():
+            if script.default_events:
+               for event in script.default_events:
+                  yield ( event, script )
+      else:
+         for script in self._scripts.values():
+            if script.default_events:
+               yield script
+   # --- end of iter_default_scripts (...) ---
+
+   def get_default_scripts ( self ):
+      scripts = dict()
+      for event, script in self.iter_default_scripts ( unpack=True ):
+         if event not in scripts:
+            scripts [event] = [ script ]
+         else:
+            scripts [event].append ( script )
+
+      return scripts
+   # --- end of get_default_scripts (...) ---
+
+   def iter_scripts ( self ):
+      for script in self._scripts.values():
+         if script.is_visible():
+            yield script
+   # --- end of iter_scripts (...) ---
+
+   def scan ( self ):
+      root = self.root
+      try:
+         filenames = sorted ( os.listdir ( root ) )
+      except OSError as oserr:
+         if oserr.errno != errno.ENOENT:
+            raise
+
+      else:
+         for fname in filenames:
+            fspath = root + os.sep + fname
+            if os.path.isfile ( fspath ):
+               script_obj = HookScript ( fspath, fname )
+               self._scripts [script_obj.name] = script_obj
+   # --- end of scan (...) ---
+
+# --- end of HookScriptDir ---
+
+
+class SetupHookEnvironment (
+   roverlay.setupscript.baseenv.SetupSubEnvironment
+):
+
+   NEEDS_CONFIG_TREE = True
+
+   def setup ( self ):
+      additions_dir = self.config.get ( 'OVERLAY.additions_dir', None )
+      if additions_dir:
+         self.user_hook_root = os.path.join ( additions_dir, 'hooks' )
+         self.writable       = self.setup_env.private_file.check_writable (
+            self.user_hook_root + os.sep + '.keep'
+         )
+      else:
+         self.user_hook_root = None
+         self.writable       = None
+
+      self.hook_root = HookScriptDir (
+         os.path.join ( self.setup_env.data_root, 'hooks' )
+      )
+      self.hook_root.scan()
+      self._prio_gen = roverlay.util.counter.UnsafeCounter ( 30 )
+   # --- end of setup (...) ---
+
+   def _link_hook ( self, source, link ):
+      if os.path.lexists ( link ):
+         linkdest = os.path.realpath ( link )
+
+         message = 'Skipping activation of hook {!r} - '.format ( link )
+
+         if linkdest == source or linkdest == os.path.realpath ( source ):
+            self.info ( message + "already set up.\n" )
+            return True
+
+         elif link != linkdest:
+            # symlink or link was relative
+            self.error ( message + "is a link to another file.\n" )
+         else:
+            self.error ( message + "exists, but is not a link.\n" )
+
+         return None
+      else:
+         return self.setup_env.private_file.symlink ( source, link )
+   # --- end of _link_hook (...) ---
+
+   def link_hooks_v ( self, event_name, hooks ):
+      success = False
+
+      if self.writable and self.user_hook_root:
+         destdir = self.user_hook_root + os.sep + event_name
+         self.setup_env.private_dir.dodir ( destdir )
+
+         to_link = []
+         for script in hooks:
+            script.set_priority_from_generator ( self._prio_gen )
+            to_link.append (
+               ( script.fspath, destdir + os.sep + script.get_dest_name() )
+            )
+
+         success = True
+         for source, link_name in to_link:
+            if self._link_hook ( source, link_name ) is False:
+               success = False
+      # -- end if
+
+      return success
+   # --- end of link_hooks_v (...) ---
+
+   def enable_defaults ( self ):
+      # not strict: missing hooks are ignored
+      success = False
+      if self.hook_root:
+         success = True
+         default_hooks = self.hook_root.get_default_scripts()
+         for event, hooks in default_hooks.items():
+            if not self.link_hooks_v ( event, hooks ):
+               success = False
+      # -- end if
+
+      return success
+   # --- end of enable_defaults (...) ---
+
+# --- end of SetupHookEnvironment ---

diff --git a/roverlay/setupscript/initenv.py b/roverlay/setupscript/initenv.py
new file mode 100644
index 0000000..f07953a
--- /dev/null
+++ b/roverlay/setupscript/initenv.py
@@ -0,0 +1,308 @@
+# R overlay -- setup script, env for the "init" command
+# -*- coding: utf-8 -*-
+
+import os
+import textwrap
+
+
+import roverlay.fsutil
+
+import roverlay.config.entrymap
+import roverlay.config.entryutil
+
+import roverlay.setupscript.baseenv
+
+
+class SetupInitEnvironment (
+   roverlay.setupscript.baseenv.SetupSubEnvironment
+):
+
+   ACTIONS = (
+      ( 'pretend', False ),
+      ( 'prepare_config_file', False ),
+      ( 'import_config', True ),
+      ( 'setupdirs', True ),
+      ( 'write_config_file', True ),
+      ( 'enable_default_hooks', True ),
+   )
+
+   NEEDS_CONFIG_TREE = True
+
+   def setup ( self ):
+      self.config_file_str = None
+   # --- end of setup (...) ---
+
+   IMPORT_CONFIG_DESC = {
+      'disable'       : False,
+      'symlink'       : 'symlink {conf_root} to {user_conf_root}',
+      'symlink=root'  : 'symlink {conf_root} to {user_conf_root}',
+      'symlink=dirs'  : (
+         'symlink files/dirs from {conf_root} to {user_conf_root}/'
+      ),
+      'symlink=files' : (
+         'recursively copy {conf_root} to {user_conf_root}, '
+         'but symlink files instead of copying them'
+      ),
+      'copy'          : 'recursively copy {conf_root} to {user_conf_root}',
+   }
+
+   def gen_pretend_options ( self ):
+      options = self.setup_env.options
+
+      def get_option ( key ):
+         val = options [key]
+         if isinstance ( val, str ):
+            return val
+         elif hasattr ( val, '__iter__' ):
+            return ' '.join ( str(x) for x in val )
+         elif isinstance ( val, bool ):
+            return "yes" if val else "no"
+         elif val is None:
+            return "<undef>"
+         else:
+            return str ( val )
+      # --- end of get_option (...) ---
+
+      comment_path    = lambda a, b: a if a == b else ( a + ' (' + b + ')' )
+      get_path_option = lambda k, b: comment_path ( get_option ( k ), b )
+
+      fmt_vars = {
+         'conf_root'     : options ['conf_root'],
+         'user_conf_root': options ['private_conf_root']
+      }
+
+      yield ( "user/uid",  get_option ( 'target_uid' ) )
+      yield ( "group/gid", get_option ( 'target_gid' ) )
+
+      yield ( "work root",
+         get_path_option ( 'work_root', self.setup_env.work_root )
+      )
+      yield ( "data root",
+         get_path_option ( 'data_root', self.setup_env.data_root )
+      )
+      yield ( "roverlay\'s config root",
+         get_path_option ( 'conf_root', self.setup_env.conf_root )
+      )
+      yield ( "user\'s config root",
+         get_path_option ( 'private_conf_root', self.setup_env.user_conf_root )
+      )
+
+      import_config = get_option ( 'import_config' )
+      if import_config == 'disable':
+         yield ( "import config", "no" )
+      else:
+         yield ( "import config",
+            "yes, "
+            + self.IMPORT_CONFIG_DESC [import_config].format ( **fmt_vars )
+         )
+
+      yield ( "enable default hooks", get_option ( 'want_default_hooks' ) )
+      yield ( "additional config variables", get_option ( 'config_vars' ) )
+   # --- end of gen_pretend_options (...) ---
+
+   def gen_pretend_lines ( self, append_newline=True ):
+      options  = list ( self.gen_pretend_options() )
+      COLUMNS  = os.environ.get ( 'COLUMNS', 78 )
+      desc_len = min ( COLUMNS // 2,
+         1 + max ( len(desc) for desc, value in options )
+      )
+
+      line_wrapper = textwrap.TextWrapper (
+         width=COLUMNS, initial_indent='',
+         subsequent_indent=( (desc_len+5) * ' ' ),
+         break_long_words=False, break_on_hyphens=False,
+      )
+
+
+      yield 'Configuration:'
+      for desc, value in options:
+         yield line_wrapper.fill (
+            "- {desc:<{l}}: {value}".format (
+               desc=desc, l=desc_len, value=value,
+            )
+         )
+
+      if append_newline:
+         yield ""
+   # --- end of gen_pretend_lines (...) ---
+
+   def do_pretend ( self, pretend ):
+      """Shows what would be done."""
+      self.info ( '\n'.join ( self.gen_pretend_lines() ) + '\n' )
+   # --- end of do_pretend (...) ---
+
+   def do_prepare_config_file ( self, pretend ):
+      """Creates the config file (in memory)."""
+      self.config_file_str = self.setup_env.create_config_file (
+         expand_user=self.setup_env.options ['config_expand_user']
+      )
+   # --- end of do_prepare_config_file (...) ---
+
+   def do_import_config ( self, pretend ):
+      """Imports the config."""
+      mode           = self.setup_env.options ['import_config']
+      fs_ops         = self.setup_env.private_dir
+      user_conf_root = self.setup_env.get_user_config_root()
+      # assert os.path.isdir ( os.path.dirname(user_conf_root) == work_root )
+
+      if user_conf_root is None and (
+         fs_ops.unlink ( self.setup_env.user_conf_root )
+      ):
+         # config_root was a symlink
+
+         if pretend:
+            user_conf_root = self.setup_env.user_conf_root
+         else:
+            user_conf_root = self.setup_env.get_user_config_root()
+
+      # -- end if
+
+
+      if user_conf_root is None:
+         self.info (
+            "user has no private config directory - skipping config import.\n"
+         )
+
+      elif mode in { 'symlink=root', 'symlink' }:
+         if not fs_ops.wipe ( user_conf_root ):
+            self.setup_env.die (
+               "failed to remove {!r}.\n".format ( user_conf_root )
+            )
+         elif not fs_ops.symlink ( self.setup_env.conf_root, user_conf_root ):
+            self.setup_env.die (
+               "could not create symlink to {!r}.".format (
+                  self.setup_env.conf_root
+               )
+            )
+
+
+         pass
+      else:
+         raise NotImplementedError ( mode )
+   # --- end of do_import_config (...) ---
+
+   def do_setupdirs ( self, pretend ):
+      """Creates directories with proper permissions."""
+      create_subdir_check = roverlay.fsutil.create_subdir_check
+      config              = self.config
+      find_config_path    = roverlay.config.entryutil.find_config_path
+      dodir_private       = self.setup_env.private_dir.dodir
+      dodir_shared        = self.setup_env.shared_dir.dodir
+
+
+      WANT_USERDIR = roverlay.config.entrymap.WANT_USERDIR
+      WANT_PRIVATE = roverlay.config.entrymap.WANT_PRIVATE
+      WANT_FILEDIR = roverlay.config.entrymap.WANT_FILEDIR
+
+      listlike    = lambda a: (
+         hasattr(a, '__iter__') and not isinstance(a, str)
+      )
+      iter_values = lambda b: (
+         () if b is None else (b if listlike(b) else ( b, ))
+      )
+
+      dirs_exclude = [
+         create_subdir_check ( self.setup_env.conf_root ),
+         create_subdir_check ( self.setup_env.data_root ),
+      ]
+      if self.setup_env.get_user_config_root() is None:
+         dirs_exclude.append (
+            create_subdir_check ( self.setup_env.user_conf_root )
+         )
+
+      # don't print exclude/skip messages more than once per dir
+      dirs_already_excluded = set()
+
+      private_dirs       = set()
+      private_dirs_chown = set()
+      shared_dirs        = set()
+      shared_dirs_chown  = set()
+
+      # it's not necessary to create all of the listed dirs because some of
+      # them are automatically created at runtime, but doing so results in
+      # a (mostly) complete filesystem layout
+      #
+      for config_key, entry in (
+         roverlay.config.entrymap.CONFIG_ENTRY_MAP.items()
+      ):
+         if isinstance ( entry, dict ) and 'want_dir_create' in entry:
+            for value in iter_values (
+               config.get ( find_config_path ( config_key ), None )
+            ):
+               dirmask = entry ['want_dir_create']
+               dirpath = (
+                  os.path.dirname ( value.rstrip ( os.sep ) )
+                  if dirmask & WANT_FILEDIR else value.rstrip ( os.sep )
+               )
+
+               if not dirpath or dirpath in dirs_already_excluded:
+                  pass
+
+               elif any ( ex ( dirpath ) for ex in dirs_exclude ):
+                  self.info (
+                     "setupdirs: excluding {!r}\n".format ( dirpath )
+                  )
+                  dirs_already_excluded.add ( dirpath )
+
+               elif os.path.islink ( dirpath ):
+                  self.info (
+                     '{!r} is a symlink - skipping setupdir '
+                     'actions.\n'.format ( dirpath )
+                  )
+                  dirs_already_excluded.add ( dirpath )
+
+               elif dirmask & WANT_USERDIR:
+                  if dirmask & WANT_PRIVATE:
+                     private_dirs_chown.add ( dirpath )
+                  else:
+                     shared_dirs_chown.add ( dirpath )
+
+               elif dirmask & WANT_PRIVATE:
+                  private_dirs.add ( dirpath )
+
+               else:
+                  shared_dirs.add ( dirpath )
+      # -- end for
+
+
+      private_dirs      -= private_dirs_chown
+      shared_dirs_chown -= private_dirs
+      shared_dirs       -= shared_dirs_chown
+
+      for dirpath in shared_dirs:
+         dodir_shared ( dirpath, chown=False )
+
+      for dirpath in shared_dirs_chown:
+         dodir_shared ( dirpath, chown=True )
+
+      for dirpath in private_dirs:
+         dodir_private ( dirpath, chown=False )
+
+      for dirpath in private_dirs_chown:
+         dodir_private ( dirpath, chown=True )
+
+      self.setup_env.private_dir.chmod_chown ( self.setup_env.work_root )
+   # --- end of do_setupdirs (...) ---
+
+   def do_write_config_file ( self, pretend ):
+      """Writes the config file to disk."""
+      cfile = self.setup_env.get_config_file_path()
+      if not self.config_file_str:
+         self.setup_env.die ( "no config file created!" )
+      elif pretend:
+         self.info ( "Would write config file to {!r}.\n".format ( cfile ) )
+      else:
+         with open ( cfile, 'wt' ) as FH:
+            FH.write ( self.config_file_str )
+
+      self.setup_env.private_file.chmod_chown ( cfile )
+   # --- end of do_write_config_file (...) ---
+
+   def do_enable_default_hooks ( self, pretend ):
+      """Enables the default hooks, e.g. git history creation."""
+      hook_env = self.setup_env.get_hook_env()
+      if not hook_env.enable_defaults():
+         self.setup_env.die ( "failed to enable hooks." )
+   # --- end of do_enable_default_hooks (...) ---
+
+# --- end of SetupInitEnvironment ---

diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
new file mode 100644
index 0000000..9ba3eec
--- /dev/null
+++ b/roverlay/setupscript/runtime.py
@@ -0,0 +1,474 @@
+# R overlay -- setup script, runtime
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+
+import logging
+import os
+import sys
+#import textwrap
+
+import roverlay.argparser
+import roverlay.argutil
+import roverlay.fsutil
+import roverlay.runtime
+
+import roverlay.config.defconfig
+
+import roverlay.setupscript.initenv
+import roverlay.setupscript.hookenv
+
+
+
+if sys.hexversion >= 0x3000000:
+   read_user_input = input
+else:
+   read_user_input = raw_input
+
+
+
+def setup_main_installed():
+   return setup_main ( True )
+# --- end of setup_main_installed (...) ---
+
+def setup_main ( installed ):
+   SetupEnvironment.run_default_main ( installed=installed )
+   return os.EX_OK
+# --- end of setup_main (...) ---
+
+def arg_stdout_or_fs ( value ):
+   if value == '-':
+      return value
+   else:
+      return os.path.abspath ( os.path.expanduser ( value ) )
+# --- end of arg_stdout_or_fs (...) ---
+
+
+class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
+   MULTIPLE_COMMANDS = False
+   COMMAND_DESCRIPTION = {
+      'init':     'initialize roverlay\'s config and filesystem layout',
+      'mkconfig': 'generate a config file',
+   }
+   DEFAULT_COMMAND = "init"
+
+   COMMANDS_WITH_PRETEND = frozenset ({ 'init', })
+
+   SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', )
+   PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', )
+
+
+   def setup_setup ( self ):
+      arg = self.setup_setup_minimal ( title='common options' )
+
+      arg (
+         '--output', '-O', metavar="<file|dir|->", dest='output',
+         default='-', type=arg_stdout_or_fs,
+         flags=self.ARG_WITH_DEFAULT,
+         help='output file/dir/stream used by various commands (\'-\' for stdout)',
+      )
+
+      arg (
+         '--pretend', '-p', dest='pretend',
+         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+         help='show what would be done',
+      )
+
+      arg (
+         '--ask', '-a', dest='wait_confirm',
+         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+         help='get confirmation before actually doing anything',
+      )
+
+      return arg
+   # --- end of setup_setup (...) ---
+
+   def parse_setup ( self ):
+      self.parse_setup_minimal()
+
+      if self.parsed ['pretend']:
+         for cmd in self.get_commands():
+            if cmd not in self.__class__.COMMANDS_WITH_PRETEND:
+               self.parser.exit (
+                  "{!r} command does not support --pretend.".format ( cmd )
+               )
+   # --- end of parse_setup (...) ---
+
+   def setup_config ( self ):
+      arg = self.add_argument_group (
+         "config", title="options for the main config file"
+      )
+
+      arg (
+         '--expand-user', dest='config_expand_user',
+         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+         help="expand \'~\' to the target user\'s home directory",
+      )
+
+      arg (
+         '--additions-dir', '-A', dest='additions_dir',
+         flags=self.ARG_WITH_DEFAULT|self.ARG_META_DIR,
+         type=roverlay.argutil.couldbe_dirstr_existing,
+         help=(
+            'directory for user-provided content '
+            '(patches, hand-written ebuilds, hooks)'
+         ),
+      )
+
+      arg (
+         '--variable', '-v', metavar="<key=\"value\">", dest='config_vars',
+         default=[], action='append',
+         type=roverlay.argutil.is_config_opt,
+         help="additional variables",
+      )
+
+      return arg
+   # --- end of setup_config (...) ---
+
+   def parse_config ( self ):
+      for kv in self.parsed ['config_vars']:
+         key, eq_sign, val = kv.partition('=')
+         if key in { 'ADDITIONS_DIR', 'OVERLAY_ADDITIONS_DIR' }:
+            self.parser.exit (
+               'use \'--additions-dir {0}\' instead of '
+               '\'--variable ADDITIONS_DIR={0}\'.'.format ( val )
+            )
+
+      self.parsed ['config_vars'].append (
+         "ADDITIONS_DIR=" + self.parsed ['additions_dir']
+      )
+   # --- end of parse_config (...) ---
+
+   def setup_init ( self ):
+      arg = self.add_argument_group (
+         'init', title='options for the \'init\' command'
+      )
+
+      arg (
+         '--enable-default-hooks', dest='want_default_hooks',
+         default=True,
+         flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+         help='enable/update the default hooks',
+      )
+
+      arg (
+         '--no-default-hooks', dest='want_default_hooks',
+         flags=self.ARG_SHARED|self.ARG_OPT_OUT,
+         help='disable the default hooks',
+      )
+
+      arg (
+         '--import-config', '-I', dest='import_config',
+         default="symlink",
+         choices=[
+            "disable",
+            "symlink", "symlink=root",
+            "symlink=dirs", "symlink=files",
+            "copy"
+         ],
+         metavar='<mode>',
+         flags=self.ARG_WITH_DEFAULT,
+         help=(
+            'choose whether and how --conf-root should be imported: '
+             '%(choices)s'
+         ),
+      )
+
+      arg (
+         '--no-import-config', dest='import_config',
+         action='store_const', const='disable',
+         help='disable config import (same as \'--import-config disable\')',
+      )
+
+
+      arg (
+         '--target-uid', dest='target_uid', default=os.getuid(),
+         metavar='<uid>', type=roverlay.argutil.is_uid,
+         flags=self.ARG_WITH_DEFAULT,
+         help="uid of the user that will run roverlay",
+      )
+
+      arg (
+         '--target-gid', dest='target_gid', default=os.getgid(),
+         metavar='<gid>', type=roverlay.argutil.is_gid,
+         flags=self.ARG_WITH_DEFAULT,
+         help='gid of the user that will run roverlay',
+      )
+
+   # --- end of setup_init (...) ---
+
+   def parse_init ( self ):
+      my_uid = os.getuid()
+
+      if my_uid and self.parsed ['target_uid'] != my_uid:
+         if self.parsed ['pretend']:
+            sys.stderr.write (
+               "!!! --target-uid: users cannot configure other users.\n\n"
+            )
+         else:
+            self.parser.exit (
+               "--target-uid: users cannot configure other users."
+            )
+   # --- end of parse_init (...) ---
+
+# --- end of SetupArgParser ---
+
+
+class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
+
+   LOG_LEVEL         = logging.INFO
+
+   SHARED_DIR_MODE   = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
+   PRIVATE_DIR_MODE  = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
+   SHARED_FILE_MODE  = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
+   PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
+
+   def __init__ ( self, *args, **kwargs ):
+      super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )
+
+      self.UID             = os.getuid()
+      self.GID             = os.getgid()
+
+      self.expanduser      = None
+      self.fs_ops          = None
+      self.fs_ops_virtual  = None
+
+      self.want_chown      = None
+      self.data_root       = None
+      self.work_root       = None
+      self.conf_root       = None
+      self.user_conf_root  = None
+
+# not used
+#      COLUMNS = os.environ.get ( 'COLUMNS', 78 )
+#
+#      self.text_wrapper = textwrap.TextWrapper (
+#         width=COLUMNS, initial_indent='', subsequent_indent='',
+#         break_long_words=False, break_on_hyphens=False,
+#      )
+   # --- end of __init__ (...) ---
+
+   def create_argparser ( self ):
+      instinfo = self.access_constant ( 'INSTALLINFO' )
+
+      return SetupArgParser (
+         description = 'roverlay setup script',
+         defaults    = {
+            'work_root'         : instinfo ['workroot'],
+            'data_root'         : instinfo ['libexec'],
+            'conf_root'         : instinfo ['confroot'],
+            'private_conf_root' : instinfo ['workroot'] + os.sep + 'config',
+            'import_config'     : 'symlink=root',
+            'additions_dir'     : instinfo ['workroot'] + os.sep + 'files',
+         },
+      )
+   # --- end of create_argparser (...) ---
+
+   def _get_config_roots ( self ):
+      return (
+         os.path.realpath ( self.conf_root ),
+         os.path.realpath ( self.user_conf_root )
+      )
+   # --- end of get_config_roots (...) ---
+
+   def get_user_config_root ( self ):
+      conf_root, user_conf_root = self._get_config_roots()
+      if conf_root == user_conf_root:
+         return None
+      else:
+         return user_conf_root
+   # --- end of get_user_config_root (...) ---
+
+   def get_config_file_path ( self ):
+      return (
+         self.work_root + os.sep + self.access_constant ( 'config_file_name' )
+      )
+   # --- end of get_config_file_path (...) ---
+
+   def create_new_target_config ( self ):
+      return self.create_new_config (
+         config_str=self.create_config_file ( expand_user=True )
+      )
+   # --- end of create_new_target_config (...) ---
+
+   def _expanduser_pwd ( self, fspath ):
+      return roverlay.fsutil.pwd_expanduser (
+         fspath, self.options ['target_uid']
+      )
+   # --- end of _expanduser_pwd (...) ---
+
+   def create_config_file ( self, expand_user=False ):
+      conf_creator = roverlay.config.defconfig.RoverlayConfigCreation (
+         is_installed = self.is_installed(),
+         work_root    = (
+            self.work_root if expand_user else self.options ['work_root']
+         ),
+         data_root    = (
+            self.data_root if expand_user else self.options ['data_root']
+         ),
+         conf_root    = (
+            self.user_conf_root if expand_user
+            else self.options ['private_conf_root']
+         ),
+      )
+
+      for kv in self.options ['config_vars']:
+         key, sepa, value = kv.partition ( '=' )
+         if not sepa:
+            raise Exception ( "bad variable given: {!r}".format ( kv ) )
+         else:
+            conf_creator.set_option ( key, value )
+
+      return conf_creator.get_str()
+   # --- end of create_config_file (...) ---
+
+   def write_config_file ( self, output=None, expand_user=None ):
+      config_file_str = self.create_config_file (
+         expand_user = (
+            self.options ['config_expand_user'] if expand_user is None
+            else expand_user
+         ),
+      )
+      if not output or output == '-':
+         self.info ( config_file_str )
+      else:
+         with open ( output, 'wt' ) as FH:
+            FH.write ( config_file_str )
+   # --- end of write_config_file (...) ---
+
+   def auto_reconfigure ( self ):
+      self.reconfigure ( self.create_config_file() )
+   # --- end of auto_reconfigure (...) ---
+
+   def setup ( self ):
+      self.PWD_INITIAL = os.getcwd()
+      self.setup_common()
+
+      # ref
+      options = self.options
+
+      target_uid = options ['target_uid']
+      target_gid = options ['target_gid']
+
+      self.want_chown = ( target_uid != self.UID or target_gid != self.GID )
+
+      if self.UID == target_uid:
+         expanduser      = os.path.expanduser
+         self.expanduser = os.path.expanduser
+      else:
+         expanduser      = self._expanduser_pwd
+         self.expanduser = self._expanduser_pwd
+
+      self.work_root      = expanduser ( options ['work_root'] )
+      self.data_root      = expanduser ( options ['data_root'] )
+      self.conf_root      = expanduser ( options ['conf_root'] )
+      self.user_conf_root = expanduser ( options ['private_conf_root'] )
+
+
+      self.fs_ops_virtual = {
+         'private_dir': roverlay.fsutil.VirtualFsOperations (
+            uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
+         ),
+         'shared_dir': roverlay.fsutil.VirtualFsOperations (
+            uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
+         ),
+         'private_file': roverlay.fsutil.VirtualFsOperations (
+            uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
+         ),
+         'shared_file': roverlay.fsutil.VirtualFsOperations (
+            uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
+         ),
+      }
+
+      if options ['pretend']:
+         self.fs_ops = self.fs_ops_virtual
+      else:
+         self.fs_ops =  {
+            'private_dir': roverlay.fsutil.FsOperations (
+               uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
+            ),
+            'shared_dir': roverlay.fsutil.FsOperations (
+               uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
+            ),
+            'private_file': roverlay.fsutil.FsOperations (
+               uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
+            ),
+            'shared_file': roverlay.fsutil.FsOperations (
+               uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
+            ),
+         }
+
+      # bind fs_ops
+      self.private_dir  = self.fs_ops ['private_dir']
+      self.shared_dir   = self.fs_ops ['shared_dir']
+      self.private_file = self.fs_ops ['private_file']
+      self.shared_file  = self.fs_ops ['shared_file']
+   # --- end of setup (...) ---
+
+   def wait_confirm ( self,
+      message=None, message_inline=None,
+      prepend_newline=True, append_newline=True
+   ):
+      try:
+         if prepend_newline:
+            self.info ( '\n' )
+
+         if message is not None:
+            self.info ( str ( message ) + '\n' )
+
+         if message_inline:
+            self.info (
+               "Press Enter to continue ({!s}) ... ".format ( message_inline )
+            )
+         else:
+            self.info ( "Press Enter to continue ... " )
+
+         self.stdout.flush()
+
+         ret = read_user_input().strip()
+
+      except ( KeyboardInterrupt, EOFError ):
+         self.info ( "\n" )
+         sys.exit ( 130 )
+      else:
+         if append_newline:
+            self.info ( '\n' )
+
+         return ret
+   # --- end of wait_confirm (...) ---
+
+   def wait_confirm_can_skip ( self,
+      SKIP_WORDS=frozenset({ 'skip', 'no', 'n'}), **kwargs
+   ):
+      assert 'message_inline' not in kwargs
+
+      if self.options ['wait_confirm']:
+
+         user_reply = self.wait_confirm (
+            message_inline="type {} to skip this step".format (
+               '/'.join ( repr( word ) for word in sorted( SKIP_WORDS ) )
+            ),
+            **kwargs
+         )
+
+         return user_reply.lower() not in SKIP_WORDS
+      else:
+         return True
+   # --- end of wait_confirm (...) ---
+
+   def get_init_env ( self ):
+      return roverlay.setupscript.initenv.SetupInitEnvironment ( self )
+   # --- end of get_init_env (...) ---
+
+   def get_hook_env ( self ):
+      return roverlay.setupscript.hookenv.SetupHookEnvironment ( self )
+   # --- end of get_hook_env (...) ---
+
+   def default_main ( self ):
+      if self.wants_command ( "mkconfig" ):
+         self.write_config_file ( self.options ['output'] )
+      elif self.wants_command ( "init" ):
+         self.get_init_env().run()
+   # --- end of default_main (...) ---
+
+# --- end of SetupEnvironment ---


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-16 13:43 André Erdmann
  0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-16 13:43 UTC (permalink / raw
  To: gentoo-commits

commit:     b6d2a9ad04f59e59feca8a14e98470fda1451ad1
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Mon Sep 16 10:32:03 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Mon Sep 16 10:32:42 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=b6d2a9ad

roverlay/fsutil: RWX, FsPermissions

---
 roverlay/fsutil.py              | 198 +++++++++++++++++++++++++++++-----------
 roverlay/setupscript/runtime.py |   8 +-
 2 files changed, 148 insertions(+), 58 deletions(-)

diff --git a/roverlay/fsutil.py b/roverlay/fsutil.py
index 7f3296a..2cf0baa 100644
--- a/roverlay/fsutil.py
+++ b/roverlay/fsutil.py
@@ -115,57 +115,147 @@ def get_bitwise_sum ( iterable, initial_value=None ):
    return ret
 # --- end of get_bitwise_sum (...) ---
 
-def get_stat_mode ( mode_str ):
+class RWX ( object ):
+
+   @classmethod
+   def from_str ( cls, s, strict=False ):
+      readable, writable, executable = False, False, False
+
+      if strict:
+         _s = s.lower()
+         readable   = _s[0] == 'r'
+         writable   = _s[1] == 'w'
+         executable = _s[2] == 'x'
+
+      elif s:
+         for char in s.lower():
+            if char == 'r':
+               readable   = True
+            elif char == 'w':
+               writable   = True
+            elif char == 'x':
+               executable = True
+         # -- end for
+      # -- end if
+
+      return cls ( readable, writable, executable )
+   # --- end of from_str (...) ---
+
+   @classmethod
+   def from_bitmask ( cls, mode, rwx_bits ):
+      return cls (
+         mode & rwx_bits[0], mode & rwx_bits[1], mode & rwx_bits[2],
+      )
+   # --- end of from_bitmask (...) ---
+
+   def __init__ ( self, readable, writable, executable ):
+      super ( RWX, self ).__init__()
+      self.readable   = bool ( readable )
+      self.writable   = bool ( writable )
+      self.executable = bool ( executable )
+   # --- end of __init__ (...) ---
+
+   def __hash__ ( self ):
+      return id ( self )
+   # --- end of __hash__ (...) ---
+
+   def __repr__ ( self ):
+      return "<{cls.__name__}({val}) at 0x{addr:x}>".format (
+         cls  = self.__class__,
+         val  = self.get_str(),
+         addr = id ( self ),
+      )
+   # --- end of __repr__ (...) ---
+
+   def get_str ( self, fillchar='-' ):
+      return (
+         ( 'r' if self.readable   else fillchar ) +
+         ( 'w' if self.writable   else fillchar ) +
+         ( 'x' if self.executable else fillchar )
+      )
+   # --- end of get_str (...) ---
+
+   __str__ = get_str
+
+   def get_bitmask ( self, rwx_bits ):
+      ret = 0
+      if self.readable:
+         ret |= rwx_bits[0]
+
+      if self.writable:
+         ret |= rwx_bits[1]
+
+      if self.executable:
+         ret |= rwx_bits[2]
+
+      return ret
+   # --- end of get_bitmask (...) ---
+
+# --- end of RWX ---
+
+
+class FsPermissions ( object ):
 
-   def iter_mode_values ( mode_str ):
-      # rwxrwxrwx
-      # 012345678
-      # r -> pos % 3 == 0
-      # w -> pos % 3 == 1
-      # x -> pos % 3 == 2
-
-
-      # COULDFIX: parse sticky bit etc. (not necessary, currently)
-      if len ( mode_str ) > 9:
-         raise ValueError ( mode_str )
-
-      for pos, char in enumerate ( mode_str ):
-         if char != '-':
-            block  = pos // 3
-            subpos = pos % 3
-            if subpos == 0:
-               # r
-               assert char == 'r'
-               if block == 0:
-                  yield stat.S_IRUSR
-               elif block == 1:
-                  yield stat.S_IRGRP
-               else:
-                  yield stat.S_IROTH
-
-            elif subpos == 1:
-               # w
-               assert char == 'w'
-               if block == 0:
-                  yield stat.S_IWUSR
-               elif block == 1:
-                  yield stat.S_IWGRP
-               else:
-                  yield stat.S_IWOTH
-
-            elif subpos == 2:
-               # x
-               assert char == 'x'
-               if block == 0:
-                  yield stat.S_IXUSR
-               elif block == 1:
-                  yield stat.S_IXGRP
-               else:
-                  yield stat.S_IXOTH
-
-   # --- end of iter_mode_values (...) ---
-
-   return get_bitwise_sum ( iter_mode_values ( mode_str ) )
+   USR_BITS = ( stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR )
+   GRP_BITS = ( stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP )
+   OTH_BITS = ( stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH )
+
+   @classmethod
+   def from_str ( cls, s, strict=False ):
+      rwx_user   = RWX.from_str ( s[0:3], strict=strict )
+      rwx_group  = RWX.from_str ( s[3:6], strict=strict )
+      rwx_others = RWX.from_str ( s[6:9], strict=strict )
+      return cls ( rwx_user, rwx_group, rwx_others )
+   # --- end of from_str (...) ---
+
+   @classmethod
+   def from_stat_mode ( cls, stat_mode ):
+      return cls (
+         RWX.from_bitmask ( stat_mode, cls.USR_BITS ),
+         RWX.from_bitmask ( stat_mode, cls.GRP_BITS ),
+         RWX.from_bitmask ( stat_mode, cls.OTH_BITS ),
+      )
+   # --- end of from_stat_mode (...) ---
+
+   def __init__ ( self, rwx_user, rwx_group, rwx_others ):
+      super ( FsPermissions, self ).__init__()
+      self.user   = rwx_user
+      self.group  = rwx_group
+      self.others = rwx_others
+   # --- end of __init__ (...) ---
+
+   def __repr__ ( self ):
+      return "<{cls.__name__}({val}) at 0x{addr:x}>".format (
+         cls  = self.__class__,
+         val  = self.get_str(),
+         addr = id ( self ),
+      )
+   # --- end of __repr__ (...) ---
+
+   def get_str ( self, fillchar='-' ):
+      return "".join (
+         rwx.get_str ( fillchar=fillchar )
+         for rwx in ( self.user, self.group, self.others )
+      )
+   # --- end of get_str (...) ---
+
+   __str__ = get_str
+
+   def get_stat_mode ( self ):
+      return (
+         self.user.get_bitmask   ( self.USR_BITS ) |
+         self.group.get_bitmask  ( self.GRP_BITS ) |
+         self.others.get_bitmask ( self.OTH_BITS )
+      )
+   # --- end of get_stat_mode (...) ---
+
+   __int__   = get_stat_mode
+   __index__ = get_stat_mode
+
+# --- end of FsPermissions ---
+
+def get_stat_mode ( mode_str ):
+   return FsPermissions.from_str ( mode_str ).get_stat_mode()
 # --- end of get_stat_mode (...) ---
 
 class ChownChmod ( object ):
@@ -176,8 +266,8 @@ class ChownChmod ( object ):
       self.pretend  = bool ( pretend )
       do_chown = uid is not None or gid is not None
 
-      self.uid      = -1 if uid is None else uid
-      self.gid      = -1 if gid is None else gid
+      self.uid      = -1 if uid is None else int ( uid )
+      self.gid      = -1 if gid is None else int ( gid )
 
       if do_chown:
          self.chown_str = "chown {uid:d}:{gid:d} {{}}".format (
@@ -197,7 +287,7 @@ class ChownChmod ( object ):
       elif isinstance ( mode, str ):
          self.mode = get_stat_mode ( mode )
       else:
-         self.mode = mode
+         self.mode = int ( mode )
 
       if self.mode is None:
          self.chmod_str = "NO CHMOD {}"
@@ -447,7 +537,7 @@ class VirtualFsOperations ( AbstractFsOperations ):
          self.info ( ret + "\n" )
 
    def chmod_chown_recursive ( self, root ):
-      for word in self.perm_env.chmod_chown_recursive ( root ):
+      for word in self.perm_env.chown_chmod_recursive ( root ):
          if word is not None:
             self.info ( word + "\n" )
 

diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
index 6ce804b..f7a38cc 100644
--- a/roverlay/setupscript/runtime.py
+++ b/roverlay/setupscript/runtime.py
@@ -264,10 +264,10 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
 
    LOG_LEVEL         = logging.INFO
 
-   SHARED_DIR_MODE   = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
-   PRIVATE_DIR_MODE  = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
-   SHARED_FILE_MODE  = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
-   PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
+   SHARED_DIR_MODE   = "rwxrwxr-x"
+   PRIVATE_DIR_MODE  = "rwxr-x---"
+   SHARED_FILE_MODE  = "rw-rw-r--"
+   PRIVATE_FILE_MODE = "rw-r-----"
 
    def __init__ ( self, *args, **kwargs ):
       super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-17 16:40 André Erdmann
  0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-17 16:40 UTC (permalink / raw
  To: gentoo-commits

commit:     5dd5292462c176a1d1e7458ecd69fd4467ed9440
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Tue Sep 17 14:10:03 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Tue Sep 17 14:10:03 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=5dd52924

ArgumentParserProxy: use existing parser

@classmethod create_new_parser(...) can be used to get the old behavior
(ArgumentParserProxy creates the parser).

---
 roverlay/argparser.py           | 31 ++++++++++++++++++-------------
 roverlay/argutil.py             | 17 +++++++++++++----
 roverlay/runtime.py             |  4 +++-
 roverlay/setupscript/runtime.py |  6 +++---
 roverlay/status.py              |  4 +++-
 5 files changed, 40 insertions(+), 22 deletions(-)

diff --git a/roverlay/argparser.py b/roverlay/argparser.py
index 154fdce..5b28d0f 100644
--- a/roverlay/argparser.py
+++ b/roverlay/argparser.py
@@ -37,42 +37,45 @@ class RoverlayArgumentParserBase ( roverlay.argutil.ArgumentParserProxy ):
 
    DESCRIPTION_TEMPLATE = None
 
-   def __init__ (
-      self, defaults=None, description=True, formatter_class=True,
-      format_description=False, **kwargs
+   @classmethod
+   def create_new_parser ( cls,
+      defaults=None, description=True, formatter_class=True,
+      format_description=True, **kwargs
    ):
       if description is True:
-         if self.DESCRIPTION_TEMPLATE is None:
+         if cls.DESCRIPTION_TEMPLATE is None:
             desc = (
                roverlay.core.description_str + '\n'
                + roverlay.core.license_str
             )
          else:
-            desc = self.format_description()
+            desc = cls.format_description()
       elif description:
          if format_description:
-            desc = self.format_description ( description )
+            desc = cls.format_description ( description )
          else:
             desc = description
       else:
          desc = None
 
-      super ( RoverlayArgumentParserBase, self ).__init__ (
+      return super ( RoverlayArgumentParserBase, cls ).create_new_parser (
          defaults        = defaults,
          description     = desc,
          formatter_class = (
             argparse.RawDescriptionHelpFormatter
             if formatter_class is True else formatter_class
          ),
-         **kwargs
       )
+   # --- end of create_new_parser (...) ---
 
-
+   def __init__ ( self, *args, **kwargs ):
+      super ( RoverlayArgumentParserBase, self ).__init__ ( *args, **kwargs )
       self.extra_conf = None
    # --- end of __init__ (...) ---
 
-   def format_description ( self, desc=None ):
-      return ( self.DESCRIPTION_TEMPLATE if desc is None else desc ).format (
+   @classmethod
+   def format_description ( cls, desc=None ):
+      return ( cls.DESCRIPTION_TEMPLATE if desc is None else desc ).format (
          version=roverlay.core.version,
          license=roverlay.core.license_str,
       )
@@ -566,8 +569,10 @@ class RoverlayArgumentParser ( RoverlayArgumentParserBase ):
    COMMAND_DESCRIPTION = None
    DEFAULT_COMMAND     = None
 
-   def __init__ ( self, default_command=None, **kwargs ):
-      super ( RoverlayArgumentParser, self ).__init__ ( **kwargs )
+   def __init__ ( self, parser, default_command=None, defaults=None ):
+      super ( RoverlayArgumentParser, self ).__init__ (
+         parser, defaults=defaults
+      )
       self.default_command = (
          self.DEFAULT_COMMAND if default_command is None else default_command
       )

diff --git a/roverlay/argutil.py b/roverlay/argutil.py
index 90444a3..c3981e5 100644
--- a/roverlay/argutil.py
+++ b/roverlay/argutil.py
@@ -184,11 +184,20 @@ class ArgumentParserProxy ( object ):
    STR_SUPPRESS = 'keep'
 
 
-
-
-   def __init__ ( self, defaults=None, **kwargs ):
+   @classmethod
+   def create_new_parser ( cls, defaults=None, **kwargs ):
+      parser = argparse.ArgumentParser ( **kwargs )
+      return cls ( parser, defaults=defaults )
+   # --- end of create_new_parser (...) ---
+
+   @classmethod
+   def wrap ( cls, parser, defaults=None ):
+      return cls ( parser, defaults=defaults )
+   # --- end of wrap (...) ---
+
+   def __init__ ( self, parser, defaults=None ):
       super ( ArgumentParserProxy, self ).__init__()
-      self.parser = argparse.ArgumentParser (**kwargs )
+      self.parser = parser
 
       if defaults is None:
          self.defaults = dict()

diff --git a/roverlay/runtime.py b/roverlay/runtime.py
index 2c12334..43140b2 100644
--- a/roverlay/runtime.py
+++ b/roverlay/runtime.py
@@ -150,7 +150,9 @@ class RuntimeEnvironmentBase ( MinimalRuntimeEnvironment ):
 
 class RuntimeEnvironment ( RuntimeEnvironmentBase ):
 
-   ARG_PARSER_CLS = roverlay.argparser.RoverlayMainArgumentParser
+   ARG_PARSER_CLS = (
+      roverlay.argparser.RoverlayMainArgumentParser.create_new_parser
+   )
 
    def __init__ ( self, installed, *args, **kw ):
       super ( RuntimeEnvironment, self ).__init__ ( installed, *args, **kw )

diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
index 8b51973..75bfff6 100644
--- a/roverlay/setupscript/runtime.py
+++ b/roverlay/setupscript/runtime.py
@@ -47,7 +47,7 @@ def arg_stdout_or_fs ( value ):
 # --- end of arg_stdout_or_fs (...) ---
 
 
-class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
+class SetupArgumentParser ( roverlay.argparser.RoverlayArgumentParser ):
    MULTIPLE_COMMANDS = False
    COMMAND_DESCRIPTION = {
       'init'     : 'initialize roverlay\'s config and filesystem layout',
@@ -262,7 +262,7 @@ class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
       return arg
    # --- end of setup_hooks (...) ---
 
-# --- end of SetupArgParser ---
+# --- end of SetupArgumentParser ---
 
 
 class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
@@ -326,7 +326,7 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
    # --- end of get_parser_defaults (...) ---
 
    def create_argparser ( self ):
-      return SetupArgParser (
+      return SetupArgumentParser.create_new_parser (
          description = 'roverlay setup script',
          defaults    = self.get_parser_defaults(),
          epilog      = (

diff --git a/roverlay/status.py b/roverlay/status.py
index 50372b8..cb25edb 100644
--- a/roverlay/status.py
+++ b/roverlay/status.py
@@ -118,7 +118,9 @@ class SelfReferencingDict ( ReferenceableDict ):
 
 
 class StatusRuntimeEnvironment ( roverlay.runtime.RuntimeEnvironmentBase ):
-   ARG_PARSER_CLS = roverlay.argparser.RoverlayStatusArgumentParser
+   ARG_PARSER_CLS = (
+      roverlay.argparser.RoverlayStatusArgumentParser.create_new_parser
+   )
 
    TEMPLATE_ENCODING = 'utf-8'
 


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2014-02-22 14:56 André Erdmann
  0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2014-02-22 14:56 UTC (permalink / raw
  To: gentoo-commits

commit:     1587a9392d1325315e3feb1de5e59e19dd670b57
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Fri Feb 21 18:30:06 2014 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Fri Feb 21 18:30:06 2014 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=1587a939

roverlay/fsutil: walk_relpath()

Common functionality for get_fs_dict(), walk_copy_tree().

+ pwd_unexpanduser() function
* pwd_expanduser() uses roverlay.util.common.get_home_dir() now
* get_fs_dict(): don't ignore top-level files unless explicitly stated

---
 roverlay/fsutil.py              | 144 +++++++++++++++++++++++++++++-----------
 roverlay/setupscript/hookenv.py |   1 +
 2 files changed, 105 insertions(+), 40 deletions(-)

diff --git a/roverlay/fsutil.py b/roverlay/fsutil.py
index d0091c2..0e9d439 100644
--- a/roverlay/fsutil.py
+++ b/roverlay/fsutil.py
@@ -16,11 +16,57 @@ import sys
 
 import roverlay.util.common
 import roverlay.util.objects
+from roverlay.util.common import get_home_dir
 
 _OS_CHOWN = getattr ( os, 'lchown', os.chown )
 _OS_CHMOD = getattr ( os, 'lchmod', os.chmod )
 
 
+def walk_relpath (
+   root, include_root=False,
+   dirname_filter=None, filename_filter=None, prune_empty=False,
+   **os_walk_kwargs
+):
+   """See get_fs_dict().
+
+   arguments:
+   * root            --
+   * include_root    --
+   * dirname_filter  --
+   * filename_filter --
+   * prune_empty     --
+   * os_walk_kwargs  --
+   """
+   abs_root = os.path.abspath ( root )
+   if include_root:
+      relpath_begin = 1 + abs_root.rfind ( os.sep )
+   else:
+      relpath_begin = 1 + len ( abs_root )
+
+   walker = os.walk ( abs_root, **os_walk_kwargs )
+
+   if dirname_filter or filename_filter:
+      for subroot, dirnames, filenames in walker:
+         if dirname_filter:
+            dirnames[:] = [ d for d in dirnames if dirname_filter ( d ) ]
+
+         if filename_filter:
+            filenames[:] = [ f for f in filenames if filename_filter ( f ) ]
+
+         if not prune_empty or filenames or dirnames:
+            yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+
+   elif prune_empty:
+      for subroot, dirnames, filenames in walker:
+         if dirnames or filenames:
+            yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+
+   else:
+      for subroot, dirnames, filenames in walker:
+         yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+# --- end of walk_relpath (...) ---
+
+
 def walk_up ( dirpath, topdown=False, max_iter=None ):
    """Generator that yields all (partial..full) filesystem paths contained
    in dirpath.
@@ -75,7 +121,7 @@ def walk_up ( dirpath, topdown=False, max_iter=None ):
 def get_fs_dict (
    initial_root, create_item=None, dict_cls=dict,
    dirname_filter=None, filename_filter=None,
-   include_root=False, prune_empty=False, file_key=None,
+   include_root=False, toplevel_files=True, prune_empty=False, file_key=None,
 ):
    """Creates a dictionary-like object representing the filesystem structure
    starting at the given root directory.
@@ -103,6 +149,8 @@ def get_fs_dict (
                         controls whether the return value should be a dict
                         starting at initial_root or a dict containing the
                         initial_root dict. Defaults to False.
+   * toplevel_files  -- whether to include top-level files in the dict (True)
+                        or not (False). Defaults to True.
    * prune_empty     -- Whether to remove directory entries without any items.
                          Defaults to False.
    * file_key        -- Either a function that returns the dict key for a
@@ -111,41 +159,43 @@ def get_fs_dict (
    Inspired by http://code.activestate.com/recipes/577879-create-a-nested-dictionary-from-oswalk/
    """
    # TODO(could-do): max_depth=N
-   fsdict         = dict_cls()
-   my_root        = os.path.abspath ( initial_root )
+   fsdict       = dict_cls()
+   get_file_key = ( lambda x: x ) if file_key is None else file_key
 
-   get_file_key   = ( lambda x: x ) if file_key is None else file_key
 
-   dictpath_begin = (
-      1 + ( my_root.rfind ( os.sep ) if include_root else len ( my_root ) )
-   )
-
-   for root, dirnames, filenames in os.walk ( my_root ):
-      if dirname_filter:
-         dirnames[:] = [ d for d in dirnames if dirname_filter ( d ) ]
+   for root, dict_relpath, dirnames, filenames in walk_relpath (
+      initial_root, include_root=include_root, prune_empty=prune_empty,
+      dirname_filter=dirname_filter, filename_filter=filename_filter
+   ):
+      if dict_relpath:
+         dictpath = dict_relpath.split ( os.sep )
+         parent   = functools.reduce ( dict_cls.get, dictpath[:-1], fsdict )
 
-      if filename_filter:
-         filenames[:] = [ f for f in filenames if filename_filter ( f ) ]
+         if create_item is None:
+            parent [dictpath[-1]] = dict_cls.fromkeys (
+               map ( get_file_key, filenames )
+            )
+         else:
+            parent [dictpath[-1]] = dict_cls (
+               (
+                  get_file_key ( fname ),
+                  create_item ( ( root + os.sep + fname ), fname, root )
+               )
+               for fname in filenames
+            )
 
-      if not prune_empty or filenames or dirnames:
-         dict_relpath = root[dictpath_begin:]
+      elif not toplevel_files:
+         pass
 
-         if dict_relpath:
-            dictpath = dict_relpath.split ( os.sep )
-            parent   = functools.reduce ( dict_cls.get, dictpath[:-1], fsdict )
+      elif create_item is None:
+         for fname in filenames:
+            fsdict [get_file_key(fname)] = None
 
-            if create_item is None:
-               parent [dictpath[-1]] = dict_cls.fromkeys (
-                  map ( get_file_key, filenames )
-               )
-            else:
-               parent [dictpath[-1]] = dict_cls (
-                  (
-                     get_file_key ( fname ),
-                     create_item ( ( root + os.sep + fname ), fname, root )
-                  )
-                  for fname in filenames
-               )
+      else:
+         for fname in filenames:
+            fsdict [get_file_key(fname)] = create_item (
+               ( root + os.sep + fname ), fname, root
+            )
    # -- end for
 
    return fsdict
@@ -203,13 +253,29 @@ def pwd_expanduser ( fspath, uid ):
    if not fspath or fspath[0] != '~':
       return fspath
    elif len ( fspath ) < 2:
-      return pwd.getpwuid ( uid ).pw_dir
+      return get_home_dir ( uid )
    elif fspath[1] == os.sep:
-      return pwd.getpwuid ( uid ).pw_dir + fspath[1:]
+      return get_home_dir ( uid ) + fspath[1:]
    else:
       return fspath
 # --- end of pwd_expanduser (...) ---
 
+def pwd_unexpanduser ( fspath, uid ):
+   """Replaces a user's home directory with "~" in the given filesystem path.
+
+   arguments:
+   * fspath --
+   * uid    --
+   """
+   home_dir = get_home_dir ( uid )
+   if not fspath.startswith ( home_dir ):
+      return fspath
+   elif len ( fspath ) == len ( home_dir ):
+      return '~'
+   else:
+      return '~' + fspath[len(home_dir):]
+# --- end of pwd_unexpanduser (...) ---
+
 def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
    """Generator that iterates over the content of a filesystem tree starting
    at source and compares it to the filesystem tree starting at dest (which
@@ -235,13 +301,10 @@ def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
    * dest          -- absolute path to the dest root
    * subdir_root   -- whether source should be a subdir of dest root or not
                       Defaults to False.
-   * **walk_kwargs -- additional keyword arguments for os.walk()
+   * **walk_kwargs -- additional keyword arguments for walk_relpath()
    """
-   source_path   = os.path.abspath ( source )
-   dest_path     = os.path.abspath ( dest )
-   relpath_begin = 1 + (
-      source_path.rfind ( os.sep ) if subdir_root else len ( source_path )
-   )
+   source_path = os.path.abspath ( source )
+   dest_path   = os.path.abspath ( dest )
 
    get_entry = lambda path: (
       path, os.lstat ( path ) if os.path.lexists ( path ) else None
@@ -250,8 +313,9 @@ def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
       [ ( get_entry ( s + name ), get_entry ( d + name ) ) for name in names ]
    )
 
-   for root, dirnames, filenames in os.walk ( source, **walk_kwargs ):
-      root_rel  = root[relpath_begin:]
+   for root, root_rel, dirnames, filenames in walk_relpath (
+      source_path, include_root=subdir_root, **walk_kwargs
+   ):
       root_dest = ( dest + os.sep + root_rel if root_rel else dest )
 
       dirs  = get_stat_list ( root + os.sep, root_dest + os.sep, dirnames )

diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py
index 23b1c68..46a2344 100644
--- a/roverlay/setupscript/hookenv.py
+++ b/roverlay/setupscript/hookenv.py
@@ -639,6 +639,7 @@ class NestedHookScriptDirBase ( HookScriptDirBase ):
          include_root    = False,
          prune_empty     = prune_empty,
          file_key        = get_script_name,
+         toplevel_files  = False,
       )
       self.scripts.update ( new_scripts )
       self.scan_scripts()


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-02-22 14:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-11 11:14 [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/ André Erdmann
  -- strict thread matches above, loose matches on Subject: below --
2013-09-16 13:43 André Erdmann
2013-09-17 16:40 André Erdmann
2014-02-22 14:56 André Erdmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox