toolsutils.py
changeset 0 b97547f5f1fa
child 541 0d75cfe50f83
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """some utilities for cubicweb tools
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 import os, sys
       
    10 from os import listdir, makedirs, symlink, environ, chmod, walk, remove
       
    11 from os.path import exists, join, abspath, normpath
       
    12 
       
    13 from logilab.common.clcommands import Command as BaseCommand, \
       
    14      main_run as base_main_run, register_commands, pop_arg, cmd_run
       
    15 from logilab.common.compat import any
       
    16 
       
    17 from cubicweb import warning
       
    18 from cubicweb import ConfigurationError, ExecutionError
       
    19 
       
    20 def iter_dir(directory, condition_file=None, ignore=()):
       
    21     """iterate on a directory"""
       
    22     for sub in listdir(directory):
       
    23         if sub in ('CVS', '.svn', '.hg'):
       
    24             continue
       
    25         if condition_file is not None and \
       
    26                not exists(join(directory, sub, condition_file)):
       
    27             continue
       
    28         if sub in ignore:
       
    29             continue
       
    30         yield sub
       
    31 
       
    32 def create_dir(directory):
       
    33     """create a directory if it doesn't exist yet"""
       
    34     try:
       
    35         makedirs(directory)
       
    36         print 'created directory', directory
       
    37     except OSError, ex:
       
    38         import errno
       
    39         if ex.errno != errno.EEXIST:
       
    40             raise
       
    41         print 'directory %s already exists' % directory
       
    42                 
       
    43 def create_symlink(source, target):
       
    44     """create a symbolic link"""
       
    45     if exists(target):
       
    46         remove(target)
       
    47     symlink(source, target)
       
    48     print '[symlink] %s <-- %s' % (target, source)
       
    49 
       
    50 def create_copy(source, target):
       
    51     import shutil
       
    52     print '[copy] %s <-- %s' % (target, source)
       
    53     shutil.copy2(source, target)
       
    54     
       
    55 def rm(whatever):
       
    56     import shutil
       
    57     shutil.rmtree(whatever)
       
    58     print 'removed %s' % whatever
       
    59 
       
    60 def show_diffs(appl_file, ref_file, askconfirm=True):
       
    61     """interactivly replace the old file with the new file according to
       
    62     user decision
       
    63     """
       
    64     import shutil
       
    65     p_output = os.popen('diff -u %s %s' % (appl_file, ref_file), 'r')
       
    66     diffs = p_output.read()
       
    67     if diffs:
       
    68         if askconfirm:
       
    69             print 
       
    70             print diffs
       
    71             action = raw_input('replace (N/y/q) ? ').lower()
       
    72         else:
       
    73             action = 'y'
       
    74         if action == 'y':
       
    75             try:
       
    76                 shutil.copyfile(ref_file, appl_file)
       
    77             except IOError:
       
    78                 os.system('chmod a+w %s' % appl_file)
       
    79                 shutil.copyfile(ref_file, appl_file)
       
    80             print 'replaced'
       
    81         elif action == 'q':
       
    82             sys.exit(0)
       
    83         else:
       
    84             copy_file = appl_file + '.default'
       
    85             copy = file(copy_file, 'w')
       
    86             copy.write(open(ref_file).read())
       
    87             copy.close()
       
    88             print 'keep current version, the new file has been written to', copy_file
       
    89     else:
       
    90         print 'no diff between %s and %s' % (appl_file, ref_file)
       
    91 
       
    92 
       
    93 def copy_skeleton(skeldir, targetdir, context,
       
    94                   exclude=('*.py[co]', '*.orig', '*~', '*_flymake.py'),
       
    95                   askconfirm=False):
       
    96     import shutil
       
    97     from fnmatch import fnmatch
       
    98     skeldir = normpath(skeldir)
       
    99     targetdir = normpath(targetdir)
       
   100     for dirpath, dirnames, filenames in walk(skeldir):
       
   101         tdirpath = dirpath.replace(skeldir, targetdir)
       
   102         create_dir(tdirpath)
       
   103         for fname in filenames:
       
   104             if any(fnmatch(fname, pat) for pat in exclude):
       
   105                 continue
       
   106             fpath = join(dirpath, fname)
       
   107             if 'CUBENAME' in fname:
       
   108                 tfpath = join(tdirpath, fname.replace('CUBENAME', context['cubename']))
       
   109             elif 'DISTNAME' in fname:
       
   110                 tfpath = join(tdirpath, fname.replace('DISTNAME', context['distname']))
       
   111             else:
       
   112                 tfpath = join(tdirpath, fname)
       
   113             if fname.endswith('.tmpl'):
       
   114                 tfpath = tfpath[:-5]
       
   115                 if not askconfirm or not exists(tfpath) or \
       
   116                        confirm('%s exists, overwrite?' % tfpath):
       
   117                     fname = fill_templated_file(fpath, tfpath, context)
       
   118                     print '[generate] %s <-- %s' % (tfpath, fpath)
       
   119             elif exists(tfpath):
       
   120                 show_diffs(tfpath, fpath, askconfirm)
       
   121             else:
       
   122                 shutil.copyfile(fpath, tfpath)
       
   123                 
       
   124 def fill_templated_file(fpath, tfpath, context):
       
   125     fobj = file(tfpath, 'w')
       
   126     templated = file(fpath).read()
       
   127     fobj.write(templated % context)
       
   128     fobj.close()
       
   129 
       
   130 def restrict_perms_to_user(filepath, log=None):
       
   131     """set -rw------- permission on the given file"""
       
   132     if log:
       
   133         log('set %s permissions to 0600', filepath)
       
   134     else:
       
   135         print 'set %s permissions to 0600' % filepath
       
   136     chmod(filepath, 0600)
       
   137 
       
   138 def confirm(question, default_is_yes=True):
       
   139     """ask for confirmation and return true on positive answer"""
       
   140     if default_is_yes:
       
   141         input_str = '%s [Y/n]: '
       
   142     else:
       
   143         input_str = '%s [y/N]: '
       
   144     answer = raw_input(input_str % (question)).strip().lower()
       
   145     if default_is_yes:
       
   146         if answer in ('n', 'no'):
       
   147             return False
       
   148         return True
       
   149     if answer in ('y', 'yes'):
       
   150         return True
       
   151     return False
       
   152 
       
   153 def read_config(config_file):
       
   154     """read the application configuration from a file and return it as a
       
   155     dictionnary
       
   156 
       
   157     :type config_file: str
       
   158     :param config_file: path to the configuration file
       
   159 
       
   160     :rtype: dict
       
   161     :return: a dictionary with specified values associated to option names 
       
   162     """
       
   163     from logilab.common.fileutils import lines
       
   164     config = current = {}
       
   165     try:
       
   166         for line in lines(config_file, comments='#'):
       
   167             try:
       
   168                 option, value = line.split('=', 1)
       
   169             except ValueError:
       
   170                 option = line.strip().lower()
       
   171                 if option[0] == '[':
       
   172                     # start a section
       
   173                     section = option[1:-1]
       
   174                     assert not config.has_key(section), \
       
   175                            'Section %s is defined more than once' % section
       
   176                     config[section] = current = {}
       
   177                     continue
       
   178                 print >> sys.stderr, 'ignoring malformed line\n%r' % line
       
   179                 continue
       
   180             option = option.strip().replace(' ', '_')
       
   181             value = value.strip()
       
   182             current[option] = value or None
       
   183     except IOError, ex:
       
   184         warning('missing or non readable configuration file %s (%s)',
       
   185                 config_file, ex)
       
   186     return config
       
   187 
       
   188 def env_path(env_var, default, name):
       
   189     """get a path specified in a variable or using the default value and return
       
   190     it.
       
   191 
       
   192     :type env_var: str
       
   193     :param env_var: name of an environment variable
       
   194 
       
   195     :type default: str
       
   196     :param default: default value if the environment variable is not defined
       
   197     
       
   198     :type name: str
       
   199     :param name: the informal name of the path, used for error message
       
   200     
       
   201     :rtype: str
       
   202     :return: the value of the environment variable or the default value
       
   203 
       
   204     :raise `ConfigurationError`: if the returned path does not exist
       
   205     """
       
   206     path = environ.get(env_var, default)
       
   207     if not exists(path):
       
   208         raise ConfigurationError('%s path %s doesn\'t exist' % (name, path))
       
   209     return abspath(path)
       
   210 
       
   211 
       
   212 
       
   213 _HDLRS = {}
       
   214 
       
   215 class metacmdhandler(type):
       
   216     def __new__(mcs, name, bases, classdict):
       
   217         cls = super(metacmdhandler, mcs).__new__(mcs, name, bases, classdict)
       
   218         if getattr(cls, 'cfgname', None) and getattr(cls, 'cmdname', None):
       
   219             _HDLRS.setdefault(cls.cmdname, []).append(cls)
       
   220         return cls
       
   221 
       
   222 
       
   223 class CommandHandler(object):
       
   224     """configuration specific helper for cubicweb-ctl commands"""
       
   225     __metaclass__ = metacmdhandler
       
   226     def __init__(self, config):
       
   227         self.config = config
       
   228 
       
   229 class Command(BaseCommand):
       
   230     """base class for cubicweb-ctl commands"""
       
   231 
       
   232     def config_helper(self, config, required=True, cmdname=None):
       
   233         if cmdname is None:
       
   234             cmdname = self.name
       
   235         for helpercls in _HDLRS.get(cmdname, ()):
       
   236             if helpercls.cfgname == config.name:
       
   237                 return helpercls(config)
       
   238         if config.name == 'all-in-one':
       
   239             for helpercls in _HDLRS.get(cmdname, ()):
       
   240                 if helpercls.cfgname == 'repository':
       
   241                     return helpercls(config)
       
   242         if required:
       
   243             msg = 'No helper for command %s using %s configuration' % (
       
   244                 cmdname, config.name)
       
   245             raise ConfigurationError(msg)
       
   246         
       
   247     def fail(self, reason):
       
   248         print "command failed:", reason
       
   249         sys.exit(1)
       
   250     
       
   251                     
       
   252 def main_run(args, doc):
       
   253     """command line tool"""
       
   254     try:
       
   255         base_main_run(args, doc)
       
   256     except ConfigurationError, err:
       
   257         print 'ERROR: ', err
       
   258         sys.exit(1)
       
   259     except ExecutionError, err:
       
   260         print err
       
   261         sys.exit(2)
       
   262 
       
   263 CONNECT_OPTIONS = (
       
   264     ("user",
       
   265      {'short': 'u', 'type' : 'string', 'metavar': '<user>',
       
   266       'help': 'connect as <user> instead of being prompted to give it.',
       
   267       }
       
   268      ),
       
   269     ("password",
       
   270      {'short': 'p', 'type' : 'password', 'metavar': '<password>',
       
   271       'help': 'automatically give <password> for authentication instead of \
       
   272 being prompted to give it.',
       
   273       }),
       
   274     ("host",
       
   275      {'short': 'H', 'type' : 'string', 'metavar': '<hostname>',
       
   276       'default': 'all-in-one',
       
   277       'help': 'specify the name server\'s host name. Will be detected by \
       
   278 broadcast if not provided.',
       
   279       }),
       
   280     )
       
   281 
       
   282 def config_connect(appid, optconfig):
       
   283     from cubicweb.dbapi import connect
       
   284     from getpass import getpass
       
   285     user = optconfig.user
       
   286     if not user:
       
   287         user = raw_input('login: ')
       
   288     password = optconfig.password
       
   289     if not password:
       
   290         password = getpass('password: ')
       
   291     return connect(user=user, password=password, host=optconfig.host, database=appid)
       
   292