diff --git a/python/obitools3/apps/__init__.py b/python/obitools3/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/obitools3/apps/arguments.pxd b/python/obitools3/apps/arguments.pxd new file mode 100644 index 0000000..b4713b7 --- /dev/null +++ b/python/obitools3/apps/arguments.pxd @@ -0,0 +1,3 @@ +#cython: language_level=3 + +cpdef buildArgumentParser(str configname, str softname) \ No newline at end of file diff --git a/python/obitools3/apps/arguments.pyx b/python/obitools3/apps/arguments.pyx new file mode 100644 index 0000000..c521fac --- /dev/null +++ b/python/obitools3/apps/arguments.pyx @@ -0,0 +1,61 @@ +#cython: language_level=3 + +''' +Created on 27 mars 2016 + +@author: coissac +''' + +import argparse +import sys + + +from .command import getCommandsList + +class ObiParser(argparse.ArgumentParser): + def error(self, message): + sys.stderr.write('error: %s\n' % message) + self.print_help() + sys.exit(2) + +cpdef buildArgumentParser(str configname, + str softname): + parser = ObiParser() + + parser.add_argument('--version', dest='%s:version' % configname, + action='store_true', + default=False, + help='Print the version of %s' % softname) + + parser.add_argument('--log', dest='%s:log' % configname, + action='store', + type=str, + default=None, + help='Create a logfile') + + parser.add_argument('--no-progress', dest='%s:progress' % configname, + action='store_false', + default=None, + help='Do not print the progress bar during analyzes') + + subparsers = parser.add_subparsers(title='subcommands', + description='valid subcommands', + help='additional help') + + commands = getCommandsList() + + for c in commands: + module = commands[c] + + if hasattr(module, "run"): + if hasattr(module, "__title__"): + sub = subparsers.add_parser(c,help=module.__title__) + else: + sub = subparsers.add_parser(c) + + if hasattr(module, "addOptions"): + module.addOptions(sub) + + sub.set_defaults(**{'%s:module' % configname : module}) + + return parser diff --git a/python/obitools3/apps/command.pxd b/python/obitools3/apps/command.pxd new file mode 100644 index 0000000..8c034a4 --- /dev/null +++ b/python/obitools3/apps/command.pxd @@ -0,0 +1,3 @@ +#cython: language_level=3 + +cdef object loadCommand(str name,loader) diff --git a/python/obitools3/apps/command.pyx b/python/obitools3/apps/command.pyx new file mode 100644 index 0000000..1234188 --- /dev/null +++ b/python/obitools3/apps/command.pyx @@ -0,0 +1,44 @@ +#cython: language_level=3 + +''' +Created on 27 mars 2016 + +@author: coissac +''' + +import pkgutil + +from obitools3 import commands + +cdef object loadCommand(str name,loader): + ''' + Load a command module from its name and an ImpLoader + + This function is for internal use + + @param name: name of the module + @type name: str + @param loader: the module loader + @type loader: ImpLoader + + @return the loaded module + @rtype: module + ''' + + module = loader.find_module(name).load_module(name) + return module + +def getCommandsList(): + ''' + Returns the list of sub-commands available to the main `obi` command + + @return: a dict instance with key corresponding to each command and + value corresponding to the module + + @rtype: dict + ''' + + cdef dict cmds = dict((x[1],loadCommand(x[1],x[0])) + for x in pkgutil.iter_modules(commands.__path__) + if not x[2]) + return cmds diff --git a/python/obitools3/apps/config.pxd b/python/obitools3/apps/config.pxd new file mode 100644 index 0000000..d055eef --- /dev/null +++ b/python/obitools3/apps/config.pxd @@ -0,0 +1,10 @@ +#cython: language_level=3 + +cpdef str setRootConfigName(str rootname) +cpdef str getRootConfigName() + +cdef dict buildDefaultConfiguration(str root_config_name, + dict config) + +cpdef dict getConfiguration(str root_config_name, + dict config) \ No newline at end of file diff --git a/python/obitools3/apps/config.pyx b/python/obitools3/apps/config.pyx new file mode 100644 index 0000000..55178ec --- /dev/null +++ b/python/obitools3/apps/config.pyx @@ -0,0 +1,96 @@ +#cython: language_level=3 + +''' +Created on 27 mars 2016 + +@author: coissac +''' + +import sys + +from .command import getCommandsList +from .logging cimport getLogger +from .arguments cimport buildArgumentParser + +from obitools3.version import version +from _curses import version + +cdef dict __default_config__ = {} + + +cpdef str setRootConfigName(str rootname): + global __default_config__ + if '__root_config__' in __default_config__: + if __default_config__["__root_config__"] in __default_config__: + __default_config__[rootname]=__default_config__[__default_config__["__root_config__"]] + del __default_config__[__default_config__["__root_config__"]] + __default_config__['__root_config__']=rootname + return rootname + +cpdef str getRootConfigName(): + global __default_config__ + return __default_config__.get('__root_config__',None) + +cdef dict buildDefaultConfiguration(str root_config_name, + dict config): + global __default_config__ + + __default_config__.clear() + setRootConfigName(root_config_name) + + __default_config__[root_config_name]=config + + config['version']=version + + commands = getCommandsList() + + for c in commands: + module = commands[c] + + assert hasattr(module, "run") + + if hasattr(module, 'default_config'): + __default_config__[c]=module.default_config + else: + __default_config__[c]={} + + return __default_config__ + + +cpdef dict getConfiguration(str root_config_name, + dict config): + global __default_config__ + + if '__done__' in __default_config__: + return __default_config__ + + + config = buildDefaultConfiguration(root_config_name, + config) + + parser = buildArgumentParser(root_config_name, + config[root_config_name]['software']) + + options = vars(parser.parse_args()) + + if options['%s:version' % root_config_name]: + print("%s - Version %s" % (config[root_config_name]['software'], + config[root_config_name]['version'])) + sys.exit(0) + + for k in options: + section,key = k.split(':') + s = config[section] + if options[k] is not None: + s[key]=options[k] + + if not 'module' in config[root_config_name]: + print('\nError: No command specified',file=sys.stderr) + parser.print_help() + sys.exit(2) + + getLogger(config) + + config['__done__']=True + + return config diff --git a/python/obitools3/apps/logging.pxd b/python/obitools3/apps/logging.pxd new file mode 100644 index 0000000..5ead8bb --- /dev/null +++ b/python/obitools3/apps/logging.pxd @@ -0,0 +1,3 @@ +#cython: language_level=3 + +cpdef getLogger(dict config) diff --git a/python/obitools3/apps/logging.pyx b/python/obitools3/apps/logging.pyx new file mode 100644 index 0000000..559977d --- /dev/null +++ b/python/obitools3/apps/logging.pyx @@ -0,0 +1,46 @@ +#cython: language_level=3 + +''' +Created on 27 mars 2016 + +@author: coissac +''' + +import logging +import sys + +cpdef getLogger(dict config): + ''' + Returns the logger as defined by the command line option + or by the config file + :param config: + ''' + + root = config["__root_config__"] + + level = config[root]['loglevel'] + logfile= config[root]['log'] + + rootlogger = logging.getLogger() + logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s") + + stderrHandler = logging.StreamHandler(sys.stderr) + stderrHandler.setFormatter(logFormatter) + + rootlogger.addHandler(stderrHandler) + + if logfile: + fileHandler = logging.FileHandler(logfile) + fileHandler.setFormatter(logFormatter) + rootlogger.addHandler(fileHandler) + + try: + loglevel = getattr(logging, level) + except: + loglevel = logging.INFO + + rootlogger.setLevel(loglevel) + + config[root]['logger']=rootlogger + + return rootlogger diff --git a/python/obitools3/apps/progress.pxd b/python/obitools3/apps/progress.pxd new file mode 100644 index 0000000..979ab80 --- /dev/null +++ b/python/obitools3/apps/progress.pxd @@ -0,0 +1,65 @@ +#cython: language_level=3 + +from ..utils cimport str2bytes + +cdef extern from "stdio.h": + struct FILE + int fprintf(FILE *stream, char *format, ...) + FILE* stderr + ctypedef unsigned int off_t "unsigned long long" + +cdef extern from "unistd.h": + int fsync(int fd); + +cdef extern from "time.h": + struct tm : + int tm_yday + int tm_hour + int tm_min + int tm_sec + + enum: CLOCKS_PER_SEC + + ctypedef int time_t + ctypedef int clock_t + ctypedef int suseconds_t + + struct timeval: + time_t tv_sec # seconds */ + suseconds_t tv_usec # microseconds */ + + + struct timezone : + int tz_minuteswest; # minutes west of Greenwich + int tz_dsttime; # type of DST correction + + + int gettimeofday(timeval *tv, timezone *tz) + + + tm *gmtime_r(time_t *clock, tm *result) + time_t time(time_t *tloc) + clock_t clock() + +cdef class ProgressBar: + cdef off_t maxi + cdef clock_t starttime + cdef clock_t lasttime + cdef clock_t tickcount + cdef int freq + cdef int cycle + cdef int arrow + cdef int lastlog + cdef bint ontty + cdef int fd + + cdef bytes head + cdef char *chead + + cdef object logger + + cdef char *wheel + cdef char *spaces + cdef char* diese + + cdef clock_t clock(self) diff --git a/python/obitools3/apps/progress.pyx b/python/obitools3/apps/progress.pyx new file mode 100644 index 0000000..48bffe4 --- /dev/null +++ b/python/obitools3/apps/progress.pyx @@ -0,0 +1,138 @@ +#cython: language_level=3 + +''' +Created on 27 mars 2016 + +@author: coissac +''' + +import sys +from ..utils cimport bytes2str + +cdef class ProgressBar: + cdef clock_t clock(self): + cdef clock_t t + cdef timeval tp + cdef clock_t s + + gettimeofday(&tp,NULL) + s = ( tp.tv_usec * 1.e-6 * CLOCKS_PER_SEC) + t = tp.tv_sec * CLOCKS_PER_SEC + s + + return t + + def __init__(self, + off_t maxi, + dict config, + str head="", + double seconde=0.1): + self.starttime = self.clock() + self.lasttime = self.starttime + self.tickcount = (seconde * CLOCKS_PER_SEC) + self.freq = 1 + self.cycle = 0 + self.arrow = 0 + self.lastlog = 0 + + self.ontty = sys.stderr.isatty() + + if (maxi<=0): + maxi=1 + + self.maxi = maxi + self.head = str2bytes(head) + self.chead= self.head + + + self.logger=config[config["__root_config__"]]["logger"] + self.wheel = '|/-\\' + self.spaces=' ' \ + ' ' \ + ' ' \ + ' ' \ + ' ' + self.diese ='##########' \ + '##########' \ + '##########' \ + '##########' \ + '##########' + + def __call__(self,object pos): + cdef off_t ipos + cdef clock_t elapsed + cdef clock_t newtime + cdef clock_t delta + cdef clock_t more + cdef double percent + cdef tm remain + cdef int days,hour,minu,sec + cdef off_t fraction + cdef int twentyth + + self.cycle+=1 + + if self.cycle % self.freq == 0: + self.cycle=1 + newtime = self.clock() + delta = newtime - self.lasttime + self.lasttime = newtime + elapsed = newtime - self.starttime +# print(" ",delta,elapsed,elapsed/CLOCKS_PER_SEC,self.tickcount) + + if delta < self.tickcount / 5 : + self.freq*=2 + elif delta > self.tickcount * 5 and self.freq>1: + self.freq/=2 + + + if callable(pos): + ipos=pos() + else: + ipos=pos + + if ipos==0: + ipos=1 + + percent = ipos/self.maxi + more = ((elapsed / percent * (1. - percent))/CLOCKS_PER_SEC) + gmtime_r(&more, &remain) + days = remain.tm_yday + hour = remain.tm_hour + minu = remain.tm_min + sec = remain.tm_sec + + if self.ontty: + fraction=(percent * 50.) + self.arrow=(self.arrow+1) % 4 + self.diese[fraction]=0 + self.spaces[50 - fraction]=0 + + if days: + fprintf(stderr,b'\r%s %5.1f %% |%s%c%s] remain : %d days %02d:%02d:%02d', + self.chead, + percent*100, + self.diese,self.wheel[self.arrow],self.spaces, + days,hour,minu,sec) + else: + fprintf(stderr,b'\r%s %5.1f %% |%s%c%s] remain : %02d:%02d:%02d', + self.chead, + percent*100., + self.diese,self.wheel[self.arrow],self.spaces, + hour,minu,sec) + self.diese[fraction]=b'#' + self.spaces[50 - fraction]=b' ' + + twentyth = int(percent * 20) + if twentyth != self.lastlog: + + if self.ontty: + fprintf(stderr,b'\n') + + self.logger.info('%s %5.1f %% remain : %02d:%02d:%02d' % ( + bytes2str(self.head), + percent*100., + hour,minu,sec)) + self.lastlog=twentyth + else: + self.cycle+=1 +