Commits

Anonymous committed 79aded1

1)major reorganisation to add config files, launchers 2) logger moved to util 3) some items were turned invisible

Comments (0)

Files changed (18)

config/__init__.py

Empty file removed.

config/conffile.py

-#import ConfigParser
-from configobj import ConfigObj
-
-def get_configuration(filename):
-
-    # TODO: find out how to mix configuration file and opt/argparse
-    
-    return ConfigObj(filename)
-
-    # old code
-    ## get the conf into a dict in a standard way
-    #conf = {}
-    #parser = ConfigParser.SafeConfigParser()
-    #parser.read([filename])
-    #for section in parser.sections():
-    #    for option in parser.options(section):
-    #        value = parser.get(section, option)
-    #        conf[(section, option)] = value
- 

config/logger.py

-import logging
-import time
-import os
-import inspect
-
-from limma import APPLICATION_NAME
-
-LOG_FILENAME_TIME_FORMAT = '%Y_%m_%d-%H_%M'
-LOG_FILENAME = '%s-%s.log' % (APPLICATION_NAME, 
-                              time.strftime(LOG_FILENAME_TIME_FORMAT))
-
-def get_module_logger():
-    # return simply the root logger (while the issue with the original code is
-    # not fixed)
-    return logging.getLogger()
-
-def get_module_logger_original():
-    # not working with current plantrisk + py2exe hack
-    """get the logger given the name of the root module (e.g. ui, 
-    structure, etc)"""
-    caller = inspect.stack()[1]
-    module = inspect.getmodule(caller[0])
-    # XXX: is there another way (without __name__)?
-    modules = module.__name__.split('.')
-    if len(modules) < 2 or modules[0] != APPLICATION_NAME:
-        raise ValueError("unexpected module %s" % module)
-    name = modules[1]
-    logger = logging.getLogger(name)
-    return logger
-
-def configure_logging(conf):
-    # pylint: disable=C0103
-    # C0103 - Invalid name (allow 'fh' and 'sh')
-
-    # configure root logger
-    root_logger = logging.getLogger()
-    if root_logger.handlers:
-        # avoid configuring more than once (test cases do this!)
-        return
-    root_logger.setLevel(logging.DEBUG)
-    formatter = logging.Formatter('[%(levelname)s][%(name)s] %(message)s')
-
-    # XXX: with the console, we get the exception twice, one to the stderr and
-    # another when copying the traceback to the logger
-    if not conf.get(('main', 'no_console_logging')):
-        sh = logging.StreamHandler()
-        sh.setLevel(logging.WARNING)  # XXX: good for console ui, bad for qt ui
-        sh.setFormatter(formatter)
-        root_logger.addHandler(sh)
-
-    # TODO: delete old logs (get code from PlantRisk's run manager)
-    logdir = conf.get(('main', 'log_directory'))
-    if logdir:
-        if not os.path.exists(logdir):
-            os.makedirs(logdir)
-        filename = os.path.join(logdir, LOG_FILENAME)
-        fh = logging.FileHandler(filename)
-        fh.setLevel(logging.DEBUG)
-        fh.setFormatter(formatter)
-        root_logger.addHandler(fh)
-
-    # XX: not working
-    ## print a starting message using already the logger of the module
-    #logger = get_module_logger()
-    #logger.warning("start logging")

launch_console.py

+import sys
+import os
+from configobj import ConfigObj
+
+script_path = os.path.abspath(sys.argv[0])
+sys.path.insert(0, os.path.dirname(os.path.dirname(script_path)))
+#print 'sys.path =', sys.path
+
+from limma.main import main
+from limma.launch_defaults import SHAREDDRIVE_DIR, DEFAULT_CONFIG
+
+LOCAL_SUBDIR = 'Limma'
+CONFIG_FNAME = 'limma.ini'
+
+def launch_console():
+
+    # TODO: test receipt for "-u" in http://stackoverflow.com/questions/881696
+    # - perhaps better: http://stackoverflow.com/questions/107705
+
+    # get default config and tell that the console is to be used
+    config = DEFAULT_CONFIG
+    config['interface'] = 'console'
+
+    # look for local config file
+    config_found = False
+    appdata_dir = os.environ.get('APPDATA')
+    if appdata_dir:
+        config_fname = os.path.join(appdata_dir, LOCAL_SUBDIR, CONFIG_FNAME)
+        if os.path.exists(config_fname):
+            config_found = True
+
+    # look for config file in the shared drive (if no local config file)
+    if not config_found:
+        config_fname = os.path.join(SHAREDDRIVE_DIR, CONFIG_FNAME)
+        if os.path.exists(config_fname):
+            config_found = True
+
+    # override default config with config from file
+    if config_found:
+        # using print, since no logger activated at this point
+        print("Found configuration file in %s" % config_fname)
+        config.update(ConfigObj(config_fname))
+        
+    args = []
+    main(config, args)
+
+if __name__ == '__main__':
+    # no command-line parameters allowed
+    launch_console()

launch_defaults.py

+import os
+
+USER = os.environ.get('USER').lower()
+SHAREDDRIVE_DIR = r"X:\02_0024\10_PlantRisk\01_Source\Limma"
+DATADIR_DEFAULT = os.path.join(SHAREDDRIVE_DIR, 'users', USER, 'data')
+LOGDIR_DEFAULT = os.path.join(SHAREDDRIVE_DIR, 'users', USER, 'logs')
+DEFAULT_CONFIG = {
+    'data_directory': DATADIR_DEFAULT,
+    'log_directory': LOGDIR_DEFAULT,
+    }
 import optparse
 
 from limma.ui.console.console_ui import ConsoleUI
-from limma.config.conffile import get_configuration
-from limma.config.logger import configure_logging
+from limma.ui.gui.qt.qt_ui import QtUI
+from limma.util.logger import configure_logging
 
-CONF_FILENAME = 'limma.conf'
+def main(config, args):
 
-def main(args):
+    config_dict = get_options_as_dict(args, config)
+    config.update(config_dict)
 
-    opt = get_options(args)
-
-    print 'opt =', opt
-
-    if opt.config_file:
-        config = get_configuration(opt.config_file)
-    else:
-        config = {}
-    print 'config =', config
     configure_logging(config)
 
-    if opt.remove_data:
+    if config.get('remove_data'):
         data_dir = config['data_directory']
         if os.path.exists(data_dir):
             shutil.rmtree(data_dir)
     #   http://stackoverflow.com/a/9929970/1153614
     logger = logging.getLogger()
     try:
-        ui = ConsoleUI(config)
+        ui = get_interface(config)
         ui.loop()
     except:
         logger.exception("Got exception on main handler")
         raise
 
-def get_options(args):
+def get_interface(config):
+    """Return the ui interface requested by the user."""
+    interface_name = config['interface']
+    if interface_name == 'console':
+        return ConsoleUI(config)
+    elif interface_name == 'qt':
+        return QtUI(config)
+
+def get_options_as_dict(args, config):
+
+    # XX: still hesitation on whether to use optparse object (with attributes)
+    # or a ConfigObj (a dict) for the parameters... For now the command-line
+    # arguments are translated into a dict. And the dict is used here for
+    # the default values (ugly hack...)
 
     parser = optparse.OptionParser()
     parser.add_option('-r', '--print-type',
                       dest='remove_data',
                       action='store_true',
-                      default=False,
+                      default=config.get('print-type'),
                       help="Remove persisted data")
-    parser.add_option('-c', '--config-file',
-                      dest='config_file',
+    parser.add_option('-i', '--interface',
+                      dest='interface',
                       type=str,
-                      help="Configuration file name")
+                      default=config.get('interface'), 
+                      help="Interface (console, qt)")
     options, _ = parser.parse_args(args)
 
-    return options
+    cmdline_dict = vars(options)
+    return cmdline_dict
 
 if __name__ == '__main__':
-    main(sys.argv)
+    # no default option here: the configuration has to come from the
+    # command line
+    main({}, sys.argv)

persister/flexible_persister.py

 import cPickle
 import yaml
 
-from limma.config.logger import get_module_logger
+from limma.util.logger import get_module_logger
 from limma.persister.persister import Persister
 
 def write_to_yaml_file(value, path):

persister/persister.py

 import shutil
 import cPickle
 
-from limma.config.logger import get_module_logger
+from limma.util.logger import get_module_logger
 
 class Persister(object):
     # persister API

persister/yaml_persister.py

-"""
-Principles
-- We have 3 types of persisters:
-  - dir (is_dir == True): a directory
-  - file (if_file == True): a yaml file
-  - default: 
-
-- the parent of a file persister is a dir persister.
-- the parent of a default persister is a default persister or a file persister.
-- EMPTY PERSISTER: the empty persister is represented by:
-  - for dir persister: an empty directory
-  - for the file persister: an empty file
-  - for the default persister: an empty dict belonging to the parent dict
-- the attributes can be in a file or in the yaml structure
-  - for the dir persister: a file must be used (pickle)
-  - for the file persister:
-    - option 1: yaml structure
-    - option 2: file name in the yaml structure + pickle file in the directory
-      of the first ancestror which is a dir persister
-- *default value*: the attribute is at its default value iff
-  - for the dir persister; the file does not exist
-  - for other persisters: the entry does not exist in the persister
-  (the idea is that at the creation of the persister all attributes are at the
-  default value, without any manipulation needed for the attributes)
-
-"""
-import os
-import shutil
-import cPickle
-
-from limma.config.logger import get_module_logger
-from limma.persister.persister import Persister
-
-class YamlPersister(Persister):
-    def __init__(self, name, parent=None, data_dir=None, create=False, 
-                 reset=False, is_dir=None):
-        "check comments in FileSystemPersister"
-        Persister.__init__(self)
-        self.name = name
-        self.parent = parent
-        self.data_dir = data_dir
-
-        # decide for the moment which persister is dir or not (without
-        # forcing the caller to say that)
-        # - for the moment: 
-        #   - the book tree is a dir persister
-        #   - the book is a yaml_file persiter
-        if is_dir is None
-
-
-
-
-
-        self.logger = get_module_logger()
-
-        # important flags for the yaml persister
-        # - for the moment put into files only the attributes of the root
-        if parent is None:
-            self.is_dir = True
-            self.is_file = False
-        elif parent.parent is None:
-            self.is_dir = False
-            self.is_file = True
-        else:
-            self.is_dir = False
-            self.is_file = False
-
-        # compute self.dir
-        if self.is_dir:
-            if data_dir is None:
-                raise ValueError("data_dir must be given when there is no "
-                                 "parent container")
-            self.logger.info("Creating root persister %s in directory %s"
-                             % (name, self.data_dir))
-            self.dir = data_dir
-        else:
-            self.dir = None
-
-        # compute self.path
-        if self.is_file:
-            if not parent.is_dir:
-                raise ValueError("file persister must be children of directory "
-                                 "persisters (%s)" % self.name)
-            self.path = os.path.join(parent.dir, self.name)
-
-        # reset
-        if reset:
-            self.remove_data()
-
-        # create the directory if requested
-        # - 3 types
-        #   - is_root: nothing to do (only checks). No dir iff empty dict
-        #   - is_file: nothing to do (only checks). No file iff empty dict
-        #   - other: nothing to do (only checks). No yaml entry iff empty dict
-        if create:
-            if self.is_dir:
-                if os.path.exists(self.data_dir):
-                    raise ValueError("persister %s (directory %s) "
-                                     "already exists"
-                                     % (self.name, self.data_dir))
-            elif self.is_file:
-                if os.path.exists(self.path):
-                    raise ValueError("persister %s (file %s) "
-                                     "already exists"
-                                     % (self.name, self.path))
-            else:
-                if name in parent.dict:
-                    raise ValueError("persister %s already exists" % self.name)
-
-        # load dict
-        self._dict = {}
-
-    def get_subpersister(self, name, create=False):
-        return YamlPersister(name, self, create=create)
-
-    def load(self, name, default_value):
-
-        if self.is_dir:
-            raise ValueError("can't get an attribute %s from a yaml directory "
-                             "persister (named %s)" % (name, self.name))
-
-        if name in self._dict:
-            return self._dict[name]
-        else:
-            #return default_value
-            # XXX: paranoid attempt to avoid initialising to objects with the
-            # same mutable structure (needed?, good approach?)
-            if default_value == []:
-                default_value = []
-            elif default_value == {}:
-                default_value = {}
-            obj = default_value
-            return obj
-
-    def save(self, name, obj, default_value):
-        "persist an attribute of the persister"
-
-        if self.is_dir:
-            raise ValueError("can't save an attribute %s into a yaml directory "
-                             "persister (named %s)" % (name, self.name))
-
-        # put into the dict and write the dict into the disk
-        self._dict[name] = obj
-        self.write()
-
-    def write(self):
-        "persist the persiter itself"
-
-        if self.is_dir:
-            raise ValueError("can't write a yaml directory persister (named %s)" 
-                             % self.name)
-        elif self.is_file:
-            #XXX: yaml stuff
-            pass
-        else:
-            self.parent.write()
-            
-    def remove_data(self):
-        "remove from the persistence"
-        # don't put this in __delete__, which runs each time the code stops!
-        if self.is_dir:
-            shutil.rmtree(self.dir)
-        elif self.is_file:
-            os.remove(self.path)
-        else:
-            del self.parent.dict[self.name]
-            self.parent.write()

plugins/plantrisk_change.py

         raise NotImplementedError()
 
 # trick to register the class of the plugin
-ITEM_TYPES.append(PlantriskChange)
+#ITEM_TYPES.append(PlantriskChange) make it invisible for the moment

plugins/plantrisk_master.py

         raise NotImplementedError()
 
 # trick to register the class of the plugin
-ITEM_TYPES.append(PlantriskMaster)
+#ITEM_TYPES.append(PlantriskMaster) make it invisible for the moment
+

structure/book.py

 from limma.persister.container import Container, PersistentObject
-from limma.config.logger import get_module_logger
+from limma.util.logger import get_module_logger
 from limma.structure.sheet import Sheet
 
 # for the moment there is only one sheet

structure/book_tree.py

 from limma.persister.container import Container, PersistentList
 from limma.persister.flexible_persister import FlexiblePersister
 from limma.util.compute_random_name import compute_random_name
-from limma.config.logger import get_module_logger
+from limma.util.logger import get_module_logger
 from limma.structure.book import Book
 
 BOOK_NAME_PREFIX = 'book_'

structure/sheet.py

 from limma.persister.container import (
     Container, PersistentObject, PersistentList, PersistentDict)
 from limma.util.compute_random_name import compute_random_name
-from limma.config.logger import get_module_logger
+from limma.util.logger import get_module_logger
 
 # trick to feed the global dict with classes from the plugins
 from limma.items.global_item_type_list import ITEM_TYPES

structure/structure.py

 import os
 
 from limma.structure.book_tree import BookTree
+from limma.util.logger import get_module_logger
 
 class Structure(object):
     """Will be replaced by book and book tree later"""
     def __init__(self, config):
+        
+        logger = get_module_logger()
+
         data_dir = config['data_directory']
+        if not os.path.exists(data_dir):
+            os.makedirs(data_dir)
+        logger.info("Data directory is %s" % data_dir)
 
         # normally the code knows whether it expects to find data or not. This 
         # the only exception: the top level data can be there or not.
 import os
 import shutil
 
-from limma.config.logger import configure_logging, get_module_logger
+from limma.util.logger import configure_logging, get_module_logger
 
 TEST_DIR = os.path.dirname(os.path.realpath(__file__))
 ALL_TESTS_DATA_DIR = os.path.join(TEST_DIR, 'data')

ui/gui/__init__.py

Empty file added.
+import logging
+import time
+import os
+import inspect
+
+from limma import APPLICATION_NAME
+
+LOG_FILENAME_TIME_FORMAT = '%Y_%m_%d-%H_%M'
+LOG_FILENAME = '%s-%s.log' % (APPLICATION_NAME, 
+                              time.strftime(LOG_FILENAME_TIME_FORMAT))
+
+def get_module_logger():
+    # return simply the root logger (while the issue with the original code is
+    # not fixed)
+    return logging.getLogger()
+
+def get_module_logger_original():
+    # not working with current plantrisk + py2exe hack
+    """get the logger given the name of the root module (e.g. ui, 
+    structure, etc)"""
+    caller = inspect.stack()[1]
+    module = inspect.getmodule(caller[0])
+    # XXX: is there another way (without __name__)?
+    modules = module.__name__.split('.')
+    if len(modules) < 2 or modules[0] != APPLICATION_NAME:
+        raise ValueError("unexpected module %s" % module)
+    name = modules[1]
+    logger = logging.getLogger(name)
+    return logger
+
+def configure_logging(config):
+    # pylint: disable=C0103
+    # C0103 - Invalid name (allow 'fh' and 'sh')
+
+    # configure root logger
+    root_logger = logging.getLogger()
+    if root_logger.handlers:
+        # avoid configuring more than once (test cases do this!)
+        return
+    root_logger.setLevel(logging.DEBUG)
+    #formatter = logging.Formatter('[%(levelname)s][%(name)s] %(message)s')
+    formatter = logging.Formatter('[%(levelname)s] %(message)s')
+
+    # XXX: with the console, we get the exception twice, one to the stderr and
+    # another when copying the traceback to the logger
+    if not config.get('no_console_logging'):
+        sh = logging.StreamHandler()
+        sh.setLevel(logging.INFO)  # good for console ui or for qt ?
+        sh.setFormatter(formatter)
+        root_logger.addHandler(sh)
+
+    # TODO: delete old logs (get code from PlantRisk's run manager)
+    logdir = config.get('log_directory')
+    if logdir:
+        if not os.path.exists(logdir):
+            os.makedirs(logdir)
+        filename = os.path.join(logdir, LOG_FILENAME)
+        fh = logging.FileHandler(filename)
+        fh.setLevel(logging.DEBUG)
+        fh.setFormatter(formatter)
+        root_logger.addHandler(fh)
+
+    logger = get_module_logger()
+    logger.info("Start logging")
+    logger.info("Log directory is %s" % logdir)