Ignore:
Timestamp:
Mar 19, 2014, 11:31:01 PM (11 years ago)
Author:
dmik
Message:

python: Merge vendor 2.7.6 to trunk.

Location:
python/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • python/trunk

  • python/trunk/Lib/logging/config.py

    r2 r391  
    1 # Copyright 2001-2007 by Vinay Sajip. All Rights Reserved.
     1# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
    22#
    33# Permission to use, copy, modify, and distribute this software and its
     
    2020by Apache's log4j system.
    2121
    22 Should work under Python versions >= 1.5.2, except that source line
    23 information is not available unless 'sys._getframe()' is.
    24 
    25 Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
     22Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
    2623
    2724To use, simply 'import logging' and log away!
    2825"""
    2926
    30 import sys, logging, logging.handlers, string, socket, struct, os, traceback, types
     27import sys, logging, logging.handlers, socket, struct, os, traceback, re
     28import types, cStringIO
    3129
    3230try:
     
    5351_listener = None
    5452
    55 def fileConfig(fname, defaults=None, disable_existing_loggers=1):
     53def fileConfig(fname, defaults=None, disable_existing_loggers=True):
    5654    """
    5755    Read the logging configuration from a ConfigParser-format file.
     
    6159    developer provides a mechanism to present the choices and load the chosen
    6260    configuration).
    63     In versions of ConfigParser which have the readfp method [typically
    64     shipped in 2.x versions of Python], you can pass in a file-like object
    65     rather than a filename, in which case the file-like object will be read
    66     using readfp.
    6761    """
    6862    import ConfigParser
    6963
    7064    cp = ConfigParser.ConfigParser(defaults)
    71     if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
     65    if hasattr(fname, 'readline'):
    7266        cp.readfp(fname)
    7367    else:
     
    9084def _resolve(name):
    9185    """Resolve a dotted name to a global object."""
    92     name = string.split(name, '.')
     86    name = name.split('.')
    9387    used = name.pop(0)
    9488    found = __import__(used)
     
    10397
    10498def _strip_spaces(alist):
    105     return map(lambda x: string.strip(x), alist)
     99    return map(lambda x: x.strip(), alist)
     100
     101def _encoded(s):
     102    return s if isinstance(s, str) else s.encode('utf-8')
    106103
    107104def _create_formatters(cp):
     
    110107    if not len(flist):
    111108        return {}
    112     flist = string.split(flist, ",")
     109    flist = flist.split(",")
    113110    flist = _strip_spaces(flist)
    114111    formatters = {}
     
    139136    if not len(hlist):
    140137        return {}
    141     hlist = string.split(hlist, ",")
     138    hlist = hlist.split(",")
    142139    hlist = _strip_spaces(hlist)
    143140    handlers = {}
     
    182179    # configure the root first
    183180    llist = cp.get("loggers", "keys")
    184     llist = string.split(llist, ",")
    185     llist = map(lambda x: string.strip(x), llist)
     181    llist = llist.split(",")
     182    llist = list(map(lambda x: x.strip(), llist))
    186183    llist.remove("root")
    187184    sectname = "logger_root"
     
    196193    hlist = cp.get(sectname, "handlers")
    197194    if len(hlist):
    198         hlist = string.split(hlist, ",")
     195        hlist = hlist.split(",")
    199196        hlist = _strip_spaces(hlist)
    200197        for hand in hlist:
     
    210207    #which were in the previous configuration but
    211208    #which are not in the new configuration.
    212     existing = root.manager.loggerDict.keys()
     209    existing = list(root.manager.loggerDict.keys())
    213210    #The list needs to be sorted so that we can
    214211    #avoid disabling child loggers of explicitly
     
    230227        logger = logging.getLogger(qn)
    231228        if qn in existing:
    232             i = existing.index(qn)
     229            i = existing.index(qn) + 1 # start with the entry after qn
    233230            prefixed = qn + "."
    234231            pflen = len(prefixed)
    235232            num_existing = len(existing)
    236             i = i + 1 # look at the entry after qn
    237             while (i < num_existing) and (existing[i][:pflen] == prefixed):
    238                 child_loggers.append(existing[i])
    239                 i = i + 1
     233            while i < num_existing:
     234                if existing[i][:pflen] == prefixed:
     235                    child_loggers.append(existing[i])
     236                i += 1
    240237            existing.remove(qn)
    241238        if "level" in opts:
     
    248245        hlist = cp.get(sectname, "handlers")
    249246        if len(hlist):
    250             hlist = string.split(hlist, ",")
     247            hlist = hlist.split(",")
    251248            hlist = _strip_spaces(hlist)
    252249            for hand in hlist:
     
    264261            logger.handlers = []
    265262            logger.propagate = 1
    266         elif disable_existing_loggers:
    267             logger.disabled = 1
     263        else:
     264            logger.disabled = disable_existing_loggers
     265
     266
     267
     268IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)
     269
     270
     271def valid_ident(s):
     272    m = IDENTIFIER.match(s)
     273    if not m:
     274        raise ValueError('Not a valid Python identifier: %r' % s)
     275    return True
     276
     277
     278# The ConvertingXXX classes are wrappers around standard Python containers,
     279# and they serve to convert any suitable values in the container. The
     280# conversion converts base dicts, lists and tuples to their wrapped
     281# equivalents, whereas strings which match a conversion format are converted
     282# appropriately.
     283#
     284# Each wrapper should have a configurator attribute holding the actual
     285# configurator to use for conversion.
     286
     287class ConvertingDict(dict):
     288    """A converting dictionary wrapper."""
     289
     290    def __getitem__(self, key):
     291        value = dict.__getitem__(self, key)
     292        result = self.configurator.convert(value)
     293        #If the converted value is different, save for next time
     294        if value is not result:
     295            self[key] = result
     296            if type(result) in (ConvertingDict, ConvertingList,
     297                                ConvertingTuple):
     298                result.parent = self
     299                result.key = key
     300        return result
     301
     302    def get(self, key, default=None):
     303        value = dict.get(self, key, default)
     304        result = self.configurator.convert(value)
     305        #If the converted value is different, save for next time
     306        if value is not result:
     307            self[key] = result
     308            if type(result) in (ConvertingDict, ConvertingList,
     309                                ConvertingTuple):
     310                result.parent = self
     311                result.key = key
     312        return result
     313
     314    def pop(self, key, default=None):
     315        value = dict.pop(self, key, default)
     316        result = self.configurator.convert(value)
     317        if value is not result:
     318            if type(result) in (ConvertingDict, ConvertingList,
     319                                ConvertingTuple):
     320                result.parent = self
     321                result.key = key
     322        return result
     323
     324class ConvertingList(list):
     325    """A converting list wrapper."""
     326    def __getitem__(self, key):
     327        value = list.__getitem__(self, key)
     328        result = self.configurator.convert(value)
     329        #If the converted value is different, save for next time
     330        if value is not result:
     331            self[key] = result
     332            if type(result) in (ConvertingDict, ConvertingList,
     333                                ConvertingTuple):
     334                result.parent = self
     335                result.key = key
     336        return result
     337
     338    def pop(self, idx=-1):
     339        value = list.pop(self, idx)
     340        result = self.configurator.convert(value)
     341        if value is not result:
     342            if type(result) in (ConvertingDict, ConvertingList,
     343                                ConvertingTuple):
     344                result.parent = self
     345        return result
     346
     347class ConvertingTuple(tuple):
     348    """A converting tuple wrapper."""
     349    def __getitem__(self, key):
     350        value = tuple.__getitem__(self, key)
     351        result = self.configurator.convert(value)
     352        if value is not result:
     353            if type(result) in (ConvertingDict, ConvertingList,
     354                                ConvertingTuple):
     355                result.parent = self
     356                result.key = key
     357        return result
     358
     359class BaseConfigurator(object):
     360    """
     361    The configurator base class which defines some useful defaults.
     362    """
     363
     364    CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')
     365
     366    WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
     367    DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
     368    INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*')
     369    DIGIT_PATTERN = re.compile(r'^\d+$')
     370
     371    value_converters = {
     372        'ext' : 'ext_convert',
     373        'cfg' : 'cfg_convert',
     374    }
     375
     376    # We might want to use a different one, e.g. importlib
     377    importer = __import__
     378
     379    def __init__(self, config):
     380        self.config = ConvertingDict(config)
     381        self.config.configurator = self
     382        # Issue 12718: winpdb replaces __import__ with a Python function, which
     383        # ends up being treated as a bound method. To avoid problems, we
     384        # set the importer on the instance, but leave it defined in the class
     385        # so existing code doesn't break
     386        if type(__import__) == types.FunctionType:
     387            self.importer = __import__
     388
     389    def resolve(self, s):
     390        """
     391        Resolve strings to objects using standard import and attribute
     392        syntax.
     393        """
     394        name = s.split('.')
     395        used = name.pop(0)
     396        try:
     397            found = self.importer(used)
     398            for frag in name:
     399                used += '.' + frag
     400                try:
     401                    found = getattr(found, frag)
     402                except AttributeError:
     403                    self.importer(used)
     404                    found = getattr(found, frag)
     405            return found
     406        except ImportError:
     407            e, tb = sys.exc_info()[1:]
     408            v = ValueError('Cannot resolve %r: %s' % (s, e))
     409            v.__cause__, v.__traceback__ = e, tb
     410            raise v
     411
     412    def ext_convert(self, value):
     413        """Default converter for the ext:// protocol."""
     414        return self.resolve(value)
     415
     416    def cfg_convert(self, value):
     417        """Default converter for the cfg:// protocol."""
     418        rest = value
     419        m = self.WORD_PATTERN.match(rest)
     420        if m is None:
     421            raise ValueError("Unable to convert %r" % value)
     422        else:
     423            rest = rest[m.end():]
     424            d = self.config[m.groups()[0]]
     425            #print d, rest
     426            while rest:
     427                m = self.DOT_PATTERN.match(rest)
     428                if m:
     429                    d = d[m.groups()[0]]
     430                else:
     431                    m = self.INDEX_PATTERN.match(rest)
     432                    if m:
     433                        idx = m.groups()[0]
     434                        if not self.DIGIT_PATTERN.match(idx):
     435                            d = d[idx]
     436                        else:
     437                            try:
     438                                n = int(idx) # try as number first (most likely)
     439                                d = d[n]
     440                            except TypeError:
     441                                d = d[idx]
     442                if m:
     443                    rest = rest[m.end():]
     444                else:
     445                    raise ValueError('Unable to convert '
     446                                     '%r at %r' % (value, rest))
     447        #rest should be empty
     448        return d
     449
     450    def convert(self, value):
     451        """
     452        Convert values to an appropriate type. dicts, lists and tuples are
     453        replaced by their converting alternatives. Strings are checked to
     454        see if they have a conversion format and are converted if they do.
     455        """
     456        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
     457            value = ConvertingDict(value)
     458            value.configurator = self
     459        elif not isinstance(value, ConvertingList) and isinstance(value, list):
     460            value = ConvertingList(value)
     461            value.configurator = self
     462        elif not isinstance(value, ConvertingTuple) and\
     463                 isinstance(value, tuple):
     464            value = ConvertingTuple(value)
     465            value.configurator = self
     466        elif isinstance(value, basestring): # str for py3k
     467            m = self.CONVERT_PATTERN.match(value)
     468            if m:
     469                d = m.groupdict()
     470                prefix = d['prefix']
     471                converter = self.value_converters.get(prefix, None)
     472                if converter:
     473                    suffix = d['suffix']
     474                    converter = getattr(self, converter)
     475                    value = converter(suffix)
     476        return value
     477
     478    def configure_custom(self, config):
     479        """Configure an object with a user-supplied factory."""
     480        c = config.pop('()')
     481        if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
     482            c = self.resolve(c)
     483        props = config.pop('.', None)
     484        # Check for valid identifiers
     485        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
     486        result = c(**kwargs)
     487        if props:
     488            for name, value in props.items():
     489                setattr(result, name, value)
     490        return result
     491
     492    def as_tuple(self, value):
     493        """Utility function which converts lists to tuples."""
     494        if isinstance(value, list):
     495            value = tuple(value)
     496        return value
     497
     498class DictConfigurator(BaseConfigurator):
     499    """
     500    Configure logging using a dictionary-like object to describe the
     501    configuration.
     502    """
     503
     504    def configure(self):
     505        """Do the configuration."""
     506
     507        config = self.config
     508        if 'version' not in config:
     509            raise ValueError("dictionary doesn't specify a version")
     510        if config['version'] != 1:
     511            raise ValueError("Unsupported version: %s" % config['version'])
     512        incremental = config.pop('incremental', False)
     513        EMPTY_DICT = {}
     514        logging._acquireLock()
     515        try:
     516            if incremental:
     517                handlers = config.get('handlers', EMPTY_DICT)
     518                for name in handlers:
     519                    if name not in logging._handlers:
     520                        raise ValueError('No handler found with '
     521                                         'name %r'  % name)
     522                    else:
     523                        try:
     524                            handler = logging._handlers[name]
     525                            handler_config = handlers[name]
     526                            level = handler_config.get('level', None)
     527                            if level:
     528                                handler.setLevel(logging._checkLevel(level))
     529                        except StandardError, e:
     530                            raise ValueError('Unable to configure handler '
     531                                             '%r: %s' % (name, e))
     532                loggers = config.get('loggers', EMPTY_DICT)
     533                for name in loggers:
     534                    try:
     535                        self.configure_logger(name, loggers[name], True)
     536                    except StandardError, e:
     537                        raise ValueError('Unable to configure logger '
     538                                         '%r: %s' % (name, e))
     539                root = config.get('root', None)
     540                if root:
     541                    try:
     542                        self.configure_root(root, True)
     543                    except StandardError, e:
     544                        raise ValueError('Unable to configure root '
     545                                         'logger: %s' % e)
     546            else:
     547                disable_existing = config.pop('disable_existing_loggers', True)
     548
     549                logging._handlers.clear()
     550                del logging._handlerList[:]
     551
     552                # Do formatters first - they don't refer to anything else
     553                formatters = config.get('formatters', EMPTY_DICT)
     554                for name in formatters:
     555                    try:
     556                        formatters[name] = self.configure_formatter(
     557                                                            formatters[name])
     558                    except StandardError, e:
     559                        raise ValueError('Unable to configure '
     560                                         'formatter %r: %s' % (name, e))
     561                # Next, do filters - they don't refer to anything else, either
     562                filters = config.get('filters', EMPTY_DICT)
     563                for name in filters:
     564                    try:
     565                        filters[name] = self.configure_filter(filters[name])
     566                    except StandardError, e:
     567                        raise ValueError('Unable to configure '
     568                                         'filter %r: %s' % (name, e))
     569
     570                # Next, do handlers - they refer to formatters and filters
     571                # As handlers can refer to other handlers, sort the keys
     572                # to allow a deterministic order of configuration
     573                handlers = config.get('handlers', EMPTY_DICT)
     574                deferred = []
     575                for name in sorted(handlers):
     576                    try:
     577                        handler = self.configure_handler(handlers[name])
     578                        handler.name = name
     579                        handlers[name] = handler
     580                    except StandardError, e:
     581                        if 'target not configured yet' in str(e):
     582                            deferred.append(name)
     583                        else:
     584                            raise ValueError('Unable to configure handler '
     585                                             '%r: %s' % (name, e))
     586
     587                # Now do any that were deferred
     588                for name in deferred:
     589                    try:
     590                        handler = self.configure_handler(handlers[name])
     591                        handler.name = name
     592                        handlers[name] = handler
     593                    except StandardError, e:
     594                        raise ValueError('Unable to configure handler '
     595                                         '%r: %s' % (name, e))
     596
     597                # Next, do loggers - they refer to handlers and filters
     598
     599                #we don't want to lose the existing loggers,
     600                #since other threads may have pointers to them.
     601                #existing is set to contain all existing loggers,
     602                #and as we go through the new configuration we
     603                #remove any which are configured. At the end,
     604                #what's left in existing is the set of loggers
     605                #which were in the previous configuration but
     606                #which are not in the new configuration.
     607                root = logging.root
     608                existing = root.manager.loggerDict.keys()
     609                #The list needs to be sorted so that we can
     610                #avoid disabling child loggers of explicitly
     611                #named loggers. With a sorted list it is easier
     612                #to find the child loggers.
     613                existing.sort()
     614                #We'll keep the list of existing loggers
     615                #which are children of named loggers here...
     616                child_loggers = []
     617                #now set up the new ones...
     618                loggers = config.get('loggers', EMPTY_DICT)
     619                for name in loggers:
     620                    name = _encoded(name)
     621                    if name in existing:
     622                        i = existing.index(name)
     623                        prefixed = name + "."
     624                        pflen = len(prefixed)
     625                        num_existing = len(existing)
     626                        i = i + 1 # look at the entry after name
     627                        while (i < num_existing) and\
     628                              (existing[i][:pflen] == prefixed):
     629                            child_loggers.append(existing[i])
     630                            i = i + 1
     631                        existing.remove(name)
     632                    try:
     633                        self.configure_logger(name, loggers[name])
     634                    except StandardError, e:
     635                        raise ValueError('Unable to configure logger '
     636                                         '%r: %s' % (name, e))
     637
     638                #Disable any old loggers. There's no point deleting
     639                #them as other threads may continue to hold references
     640                #and by disabling them, you stop them doing any logging.
     641                #However, don't disable children of named loggers, as that's
     642                #probably not what was intended by the user.
     643                for log in existing:
     644                    logger = root.manager.loggerDict[log]
     645                    if log in child_loggers:
     646                        logger.level = logging.NOTSET
     647                        logger.handlers = []
     648                        logger.propagate = True
     649                    elif disable_existing:
     650                        logger.disabled = True
     651
     652                # And finally, do the root logger
     653                root = config.get('root', None)
     654                if root:
     655                    try:
     656                        self.configure_root(root)
     657                    except StandardError, e:
     658                        raise ValueError('Unable to configure root '
     659                                         'logger: %s' % e)
     660        finally:
     661            logging._releaseLock()
     662
     663    def configure_formatter(self, config):
     664        """Configure a formatter from a dictionary."""
     665        if '()' in config:
     666            factory = config['()'] # for use in exception handler
     667            try:
     668                result = self.configure_custom(config)
     669            except TypeError, te:
     670                if "'format'" not in str(te):
     671                    raise
     672                #Name of parameter changed from fmt to format.
     673                #Retry with old name.
     674                #This is so that code can be used with older Python versions
     675                #(e.g. by Django)
     676                config['fmt'] = config.pop('format')
     677                config['()'] = factory
     678                result = self.configure_custom(config)
     679        else:
     680            fmt = config.get('format', None)
     681            dfmt = config.get('datefmt', None)
     682            result = logging.Formatter(fmt, dfmt)
     683        return result
     684
     685    def configure_filter(self, config):
     686        """Configure a filter from a dictionary."""
     687        if '()' in config:
     688            result = self.configure_custom(config)
     689        else:
     690            name = config.get('name', '')
     691            result = logging.Filter(name)
     692        return result
     693
     694    def add_filters(self, filterer, filters):
     695        """Add filters to a filterer from a list of names."""
     696        for f in filters:
     697            try:
     698                filterer.addFilter(self.config['filters'][f])
     699            except StandardError, e:
     700                raise ValueError('Unable to add filter %r: %s' % (f, e))
     701
     702    def configure_handler(self, config):
     703        """Configure a handler from a dictionary."""
     704        formatter = config.pop('formatter', None)
     705        if formatter:
     706            try:
     707                formatter = self.config['formatters'][formatter]
     708            except StandardError, e:
     709                raise ValueError('Unable to set formatter '
     710                                 '%r: %s' % (formatter, e))
     711        level = config.pop('level', None)
     712        filters = config.pop('filters', None)
     713        if '()' in config:
     714            c = config.pop('()')
     715            if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType:
     716                c = self.resolve(c)
     717            factory = c
     718        else:
     719            cname = config.pop('class')
     720            klass = self.resolve(cname)
     721            #Special case for handler which refers to another handler
     722            if issubclass(klass, logging.handlers.MemoryHandler) and\
     723                'target' in config:
     724                try:
     725                    th = self.config['handlers'][config['target']]
     726                    if not isinstance(th, logging.Handler):
     727                        config['class'] = cname # restore for deferred configuration
     728                        raise StandardError('target not configured yet')
     729                    config['target'] = th
     730                except StandardError, e:
     731                    raise ValueError('Unable to set target handler '
     732                                     '%r: %s' % (config['target'], e))
     733            elif issubclass(klass, logging.handlers.SMTPHandler) and\
     734                'mailhost' in config:
     735                config['mailhost'] = self.as_tuple(config['mailhost'])
     736            elif issubclass(klass, logging.handlers.SysLogHandler) and\
     737                'address' in config:
     738                config['address'] = self.as_tuple(config['address'])
     739            factory = klass
     740        kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
     741        try:
     742            result = factory(**kwargs)
     743        except TypeError, te:
     744            if "'stream'" not in str(te):
     745                raise
     746            #The argument name changed from strm to stream
     747            #Retry with old name.
     748            #This is so that code can be used with older Python versions
     749            #(e.g. by Django)
     750            kwargs['strm'] = kwargs.pop('stream')
     751            result = factory(**kwargs)
     752        if formatter:
     753            result.setFormatter(formatter)
     754        if level is not None:
     755            result.setLevel(logging._checkLevel(level))
     756        if filters:
     757            self.add_filters(result, filters)
     758        return result
     759
     760    def add_handlers(self, logger, handlers):
     761        """Add handlers to a logger from a list of names."""
     762        for h in handlers:
     763            try:
     764                logger.addHandler(self.config['handlers'][h])
     765            except StandardError, e:
     766                raise ValueError('Unable to add handler %r: %s' % (h, e))
     767
     768    def common_logger_config(self, logger, config, incremental=False):
     769        """
     770        Perform configuration which is common to root and non-root loggers.
     771        """
     772        level = config.get('level', None)
     773        if level is not None:
     774            logger.setLevel(logging._checkLevel(level))
     775        if not incremental:
     776            #Remove any existing handlers
     777            for h in logger.handlers[:]:
     778                logger.removeHandler(h)
     779            handlers = config.get('handlers', None)
     780            if handlers:
     781                self.add_handlers(logger, handlers)
     782            filters = config.get('filters', None)
     783            if filters:
     784                self.add_filters(logger, filters)
     785
     786    def configure_logger(self, name, config, incremental=False):
     787        """Configure a non-root logger from a dictionary."""
     788        logger = logging.getLogger(name)
     789        self.common_logger_config(logger, config, incremental)
     790        propagate = config.get('propagate', None)
     791        if propagate is not None:
     792            logger.propagate = propagate
     793
     794    def configure_root(self, config, incremental=False):
     795        """Configure a root logger from a dictionary."""
     796        root = logging.getLogger()
     797        self.common_logger_config(root, config, incremental)
     798
     799dictConfigClass = DictConfigurator
     800
     801def dictConfig(config):
     802    """Configure logging using a dictionary."""
     803    dictConfigClass(config).configure()
    268804
    269805
     
    279815    """
    280816    if not thread:
    281         raise NotImplementedError, "listen() needs threading to work"
     817        raise NotImplementedError("listen() needs threading to work")
    282818
    283819    class ConfigStreamHandler(StreamRequestHandler):
     
    305841                    while len(chunk) < slen:
    306842                        chunk = chunk + conn.recv(slen - len(chunk))
    307                     #Apply new configuration. We'd like to be able to
    308                     #create a StringIO and pass that in, but unfortunately
    309                     #1.5.2 ConfigParser does not support reading file
    310                     #objects, only actual files. So we create a temporary
    311                     #file and remove it later.
    312                     file = tempfile.mktemp(".ini")
    313                     f = open(file, "w")
    314                     f.write(chunk)
    315                     f.close()
    316843                    try:
    317                         fileConfig(file)
    318                     except (KeyboardInterrupt, SystemExit):
    319                         raise
     844                        import json
     845                        d =json.loads(chunk)
     846                        assert isinstance(d, dict)
     847                        dictConfig(d)
    320848                    except:
    321                         traceback.print_exc()
    322                     os.remove(file)
     849                        #Apply new configuration.
     850
     851                        file = cStringIO.StringIO(chunk)
     852                        try:
     853                            fileConfig(file)
     854                        except (KeyboardInterrupt, SystemExit):
     855                            raise
     856                        except:
     857                            traceback.print_exc()
     858                    if self.server.ready:
     859                        self.server.ready.set()
    323860            except socket.error, e:
    324                 if type(e.args) != types.TupleType:
     861                if not isinstance(e.args, tuple):
    325862                    raise
    326863                else:
     
    337874
    338875        def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
    339                      handler=None):
     876                     handler=None, ready=None):
    340877            ThreadingTCPServer.__init__(self, (host, port), handler)
    341878            logging._acquireLock()
     
    343880            logging._releaseLock()
    344881            self.timeout = 1
     882            self.ready = ready
    345883
    346884        def serve_until_stopped(self):
     
    356894                abort = self.abort
    357895                logging._releaseLock()
    358 
    359     def serve(rcvr, hdlr, port):
    360         server = rcvr(port=port, handler=hdlr)
    361         global _listener
    362         logging._acquireLock()
    363         _listener = server
    364         logging._releaseLock()
    365         server.serve_until_stopped()
    366 
    367     return threading.Thread(target=serve,
    368                             args=(ConfigSocketReceiver,
    369                                   ConfigStreamHandler, port))
     896            self.socket.close()
     897
     898    class Server(threading.Thread):
     899
     900        def __init__(self, rcvr, hdlr, port):
     901            super(Server, self).__init__()
     902            self.rcvr = rcvr
     903            self.hdlr = hdlr
     904            self.port = port
     905            self.ready = threading.Event()
     906
     907        def run(self):
     908            server = self.rcvr(port=self.port, handler=self.hdlr,
     909                               ready=self.ready)
     910            if self.port == 0:
     911                self.port = server.server_address[1]
     912            self.ready.set()
     913            global _listener
     914            logging._acquireLock()
     915            _listener = server
     916            logging._releaseLock()
     917            server.serve_until_stopped()
     918
     919    return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
    370920
    371921def stopListening():
     
    374924    """
    375925    global _listener
    376     if _listener:
    377         logging._acquireLock()
    378         _listener.abort = 1
    379         _listener = None
     926    logging._acquireLock()
     927    try:
     928        if _listener:
     929            _listener.abort = 1
     930            _listener = None
     931    finally:
    380932        logging._releaseLock()
Note: See TracChangeset for help on using the changeset viewer.