20012022 by vinay sajip permission to use copy modify and distribute this software and its documentation for any purpose and without fee is hereby granted provided that the above notice appear in all copies and that both that notice and this permission notice appear in supporting documentation and that the name of vinay sajip not be used in advertising or publicity pertaining to distribution of the software without specific written prior permission vinay sajip disclaims all warranties with regard to this software including all implied warranties of merchantability and fitness in no event shall vinay sajip be liable for any special indirect or consequential damages or any damages whatsoever resulting from loss of use data or profits whether in an action of contract negligence or other tortious action arising out of or in connection with the use or performance of this software configuration functions for the logging package for python the core package is based on pep 282 and comments thereto in comp lang python and influenced by apache s log4j system c 20012022 vinay sajip to use simply import logging and log away the following code implements a socket listener for onthefly reconfiguration of logging listener holds the server object doing the listening read the logging configuration from a configparserformat file this can be called several times from an application allowing an end user the ability to select from various precanned configurations if the developer provides a mechanism to present the choices and load the chosen configuration critical section handlers add themselves to logging handlers resolve a dotted name to a global object name name split used name pop0 found importused for n in name used used n try found getattrfound n except attributeerror importused found getattrfound n return found def stripspacesalist return mapstr strip alist def createformatterscp install and return handlers hlist cphandlerskeys if not lenhlist return hlist hlist split hlist stripspaceshlist handlers fixups for interhandler references for hand in hlist section cphandlers hand klass sectionclass fmt section getformatter try klass evalklass varslogging except attributeerror nameerror klass resolveklass args section getargs args evalargs varslogging kwargs section getkwargs kwargs evalkwargs varslogging h klassargs kwargs h name hand if level in section level sectionlevel h setlevellevel if lenfmt h setformatterformattersfmt if issubclassklass logging handlers memoryhandler target section gettarget if lentarget the target handler may not be loaded yet so keep for later fixups appendh target handlershand h now all handlers are loaded fixup interhandler references for h t in fixups h settargethandlerst return handlers def handleexistingloggersexisting childloggers disableexisting root logging root for log in existing logger root manager loggerdictlog if log in childloggers if not isinstancelogger logging placeholder logger setlevellogging notset logger handlers logger propagate true else logger disabled disableexisting def installloggerscp handlers disableexisting configure the root first and now the others we don t want to lose the existing loggers since other threads may have pointers to them existing is set to contain all existing loggers and as we go through the new configuration we remove any which are configured at the end what s left in existing is the set of loggers which were in the previous configuration but which are not in the new configuration the list needs to be sorted so that we can avoid disabling child loggers of explicitly named loggers with a sorted list it is easier to find the child loggers we ll keep the list of existing loggers which are children of named loggers here now set up the new ones disable any old loggers there s no point deleting them as other threads may continue to hold references and by disabling them you stop them doing any logging however don t disable children of named loggers as that s probably not what was intended by the user for log in existing logger root manager loggerdictlog if log in childloggers logger level logging notset logger handlers logger propagate 1 elif disableexistingloggers logger disabled 1 clear and close existing handlers logging handlers clear logging shutdownlogging handlerlist del logging handlerlist identifier re compile azaz09 re i def valididents m identifier matchs if not m raise valueerror not a valid python identifier r s return true class convertingmixinobject if the converted value is different save for next time the convertingxxx classes are wrappers around standard python containers and they serve to convert any suitable values in the container the conversion converts base dicts lists and tuples to their wrapped equivalents whereas strings which match a conversion format are converted appropriately each wrapper should have a configurator attribute holding the actual configurator to use for conversion a converting dictionary wrapper def getitemself key value dict getitemself key return self convertwithkeykey value def getself key defaultnone value dict getself key default return self convertwithkeykey value def popself key defaultnone value dict popself key default return self convertwithkeykey value replacefalse class convertinglistlist convertingmixin a converting tuple wrapper def getitemself key value tuple getitemself key can t replace a tuple entry return self convertwithkeykey value replacefalse class baseconfiguratorobject convertpattern re compiler pprefixaz psuffix wordpattern re compiler sws dotpattern re compiler sws indexpattern re compiler s digitpattern re compiler d valueconverters ext extconvert cfg cfgconvert we might want to use a different one e g importlib importer staticmethodimport def initself config self config convertingdictconfig self config configurator self def resolveself s name s split used name pop0 try found self importerused for frag in name used frag try found getattrfound frag except attributeerror self importerused found getattrfound frag return found except importerror as e v valueerror cannot resolve r s s e raise v from e def extconvertself value default converter for the cfg protocol rest value m self wordpattern matchrest if m is none raise valueerrorunable to convert r value else rest restm end d self configm groups0 print d rest while rest m self dotpattern matchrest if m d dm groups0 else m self indexpattern matchrest if m idx m groups0 if not self digitpattern matchidx d didx else try n intidx try as number first most likely d dn except typeerror d didx if m rest restm end else raise valueerror unable to convert r at r value rest rest should be empty return d def convertself value if not isinstancevalue convertingdict and isinstancevalue dict value convertingdictvalue value configurator self elif not isinstancevalue convertinglist and isinstancevalue list value convertinglistvalue value configurator self elif not isinstancevalue convertingtuple and isinstancevalue tuple and not hasattrvalue fields value convertingtuplevalue value configurator self elif isinstancevalue str str for py3k m self convertpattern matchvalue if m d m groupdict prefix d prefix converter self valueconverters getprefix none if converter suffix d suffix converter getattrself converter value convertersuffix return value def configurecustomself config check for valid identifiers utility function which converts lists to tuples if isinstancevalue list value tuplevalue return value class dictconfiguratorbaseconfigurator def configureself do formatters first they don t refer to anything else next do filters they don t refer to anything else either next do handlers they refer to formatters and filters as handlers can refer to other handlers sort the keys to allow a deterministic order of configuration now do any that were deferred next do loggers they refer to handlers and filters we don t want to lose the existing loggers since other threads may have pointers to them existing is set to contain all existing loggers and as we go through the new configuration we remove any which are configured at the end what s left in existing is the set of loggers which were in the previous configuration but which are not in the new configuration the list needs to be sorted so that we can avoid disabling child loggers of explicitly named loggers with a sorted list it is easier to find the child loggers we ll keep the list of existing loggers which are children of named loggers here now set up the new ones disable any old loggers there s no point deleting them as other threads may continue to hold references and by disabling them you stop them doing any logging however don t disable children of named loggers as that s probably not what was intended by the user for log in existing logger root manager loggerdictlog if log in childloggers logger level logging notset logger handlers logger propagate true elif disableexisting logger disabled true and finally do the root logger configure a formatter from a dictionary if in config factory config for use in exception handler try result self configurecustomconfig except typeerror as te if format not in strte raise name of parameter changed from fmt to format retry with old name this is so that code can be used with older python versions e g by django config fmt config pop format config factory result self configurecustomconfig else fmt config get format none dfmt config get datefmt none style config get style cname config get class none defaults config get defaults none if not cname c logging formatter else c resolvecname kwargs add defaults only if it exists prevents typeerror in custom formatter callables that do not accept it if defaults is not none kwargs defaults defaults a typeerror would be raised if validate key is passed in with a formatter callable that does not accept validate as a parameter if validate in config if user hasn t mentioned it the default will be fine result cfmt dfmt style config validate kwargs else result cfmt dfmt style kwargs return result def configurefilterself config add filters to a filterer from a list of names for f in filters try if callablef or callablegetattrf filter none filter f else filter self config filters f filterer addfilterfilter except exception as e raise valueerror unable to add filter r f from e def configurequeuehandlerself klass kwargs if queue in kwargs q kwargs queue else q queue queue unbounded rhl kwargs get respecthandlerlevel false if listener in kwargs lklass kwargs listener else lklass logging handlers queuelistener listener lklassq kwargs handlers respecthandlerlevelrhl handler klassq handler listener listener return handler def configurehandlerself config special case for handler which refers to another handler another special case for handler which refers to other handlers the argument name changed from strm to stream retry with old name this is so that code can be used with older python versions e g by django add handlers to a logger from a list of names for h in handlers try logger addhandlerself config handlers h except exception as e raise valueerror unable to add handler r h from e def commonloggerconfigself logger config incrementalfalse level config get level none if level is not none logger setlevellogging checklevellevel if not incremental remove any existing handlers for h in logger handlers logger removehandlerh handlers config get handlers none if handlers self addhandlerslogger handlers filters config get filters none if filters self addfilterslogger filters def configureloggerself name config incrementalfalse configure a root logger from a dictionary root logging getlogger self commonloggerconfigroot config incremental dictconfigclass dictconfigurator def dictconfigconfig start up a socket server on the specified port and listen for new configurations these will be sent as a file suitable for processing by fileconfig returns a thread object on which you can call start to start the server and which you can join when appropriate to stop the server call stoplistening use the verify argument to verify any bytes received across the wire from a client if specified it should be a callable which receives a single argument the bytes of configuration data received across the network and it should return either none to indicate that the passed in bytes could not be verified and should be discarded or a byte string which is then passed to the configuration machinery as normal note that you can return transformed bytes e g by decrypting the bytes passed in handler for a logging configuration request it expects a completely new logging configuration and uses fileconfig to install it handle a request each request is expected to be a 4byte length packed using struct packl n followed by the config file uses fileconfig to do the grunt work apply new configuration a simple tcp socketbased logging config receiver stop the listening server which was created with a call to listen 2001 2022 by vinay sajip permission to use copy modify and distribute this software and its documentation for any purpose and without fee is hereby granted provided that the above notice appear in all copies and that both that notice and this permission notice appear in supporting documentation and that the name of vinay sajip not be used in advertising or publicity pertaining to distribution of the software without specific written prior permission vinay sajip disclaims all warranties with regard to this software including all implied warranties of merchantability and fitness in no event shall vinay sajip be liable for any special indirect or consequential damages or any damages whatsoever resulting from loss of use data or profits whether in an action of contract negligence or other tortious action arising out of or in connection with the use or performance of this software configuration functions for the logging package for python the core package is based on pep 282 and comments thereto in comp lang python and influenced by apache s log4j system c 2001 2022 vinay sajip to use simply import logging and log away the following code implements a socket listener for on the fly reconfiguration of logging _listener holds the server object doing the listening read the logging configuration from a configparser format file this can be called several times from an application allowing an end user the ability to select from various pre canned configurations if the developer provides a mechanism to present the choices and load the chosen configuration critical section handlers add themselves to logging _handlers resolve a dotted name to a global object create and return formatters install and return handlers for inter handler references the target handler may not be loaded yet so keep for later now all handlers are loaded fixup inter handler references when re configuring logging handle loggers which were in the previous configuration but are not in the new configuration there s no point deleting them as other threads may continue to hold references to them and by disabling them you stop them doing any logging however don t disable children of named loggers as that s probably not what was intended by the user also allow existing loggers to not be disabled if disable_existing is false create and install loggers configure the root first and now the others we don t want to lose the existing loggers since other threads may have pointers to them existing is set to contain all existing loggers and as we go through the new configuration we remove any which are configured at the end what s left in existing is the set of loggers which were in the previous configuration but which are not in the new configuration the list needs to be sorted so that we can avoid disabling child loggers of explicitly named loggers with a sorted list it is easier to find the child loggers we ll keep the list of existing loggers which are children of named loggers here now set up the new ones start with the entry after qn disable any old loggers there s no point deleting them as other threads may continue to hold references and by disabling them you stop them doing any logging however don t disable children of named loggers as that s probably not what was intended by the user for log in existing logger root manager loggerdict log if log in child_loggers logger level logging notset logger handlers logger propagate 1 elif disable_existing_loggers logger disabled 1 clear and close existing handlers for convertingxxx s this mixin class provides common functions if the converted value is different save for next time the convertingxxx classes are wrappers around standard python containers and they serve to convert any suitable values in the container the conversion converts base dicts lists and tuples to their wrapped equivalents whereas strings which match a conversion format are converted appropriately each wrapper should have a configurator attribute holding the actual configurator to use for conversion a converting dictionary wrapper a converting list wrapper a converting tuple wrapper can t replace a tuple entry the configurator base class which defines some useful defaults we might want to use a different one e g importlib resolve strings to objects using standard import and attribute syntax default converter for the ext protocol default converter for the cfg protocol print d rest try as number first most likely rest should be empty convert values to an appropriate type dicts lists and tuples are replaced by their converting alternatives strings are checked to see if they have a conversion format and are converted if they do str for py3k configure an object with a user supplied factory check for valid identifiers utility function which converts lists to tuples configure logging using a dictionary like object to describe the configuration do the configuration do formatters first they don t refer to anything else next do filters they don t refer to anything else either next do handlers they refer to formatters and filters as handlers can refer to other handlers sort the keys to allow a deterministic order of configuration now do any that were deferred next do loggers they refer to handlers and filters we don t want to lose the existing loggers since other threads may have pointers to them existing is set to contain all existing loggers and as we go through the new configuration we remove any which are configured at the end what s left in existing is the set of loggers which were in the previous configuration but which are not in the new configuration the list needs to be sorted so that we can avoid disabling child loggers of explicitly named loggers with a sorted list it is easier to find the child loggers we ll keep the list of existing loggers which are children of named loggers here now set up the new ones look after name disable any old loggers there s no point deleting them as other threads may continue to hold references and by disabling them you stop them doing any logging however don t disable children of named loggers as that s probably not what was intended by the user for log in existing logger root manager loggerdict log if log in child_loggers logger level logging notset logger handlers logger propagate true elif disable_existing logger disabled true and finally do the root logger configure a formatter from a dictionary for use in exception handler name of parameter changed from fmt to format retry with old name this is so that code can be used with older python versions e g by django add defaults only if it exists prevents typeerror in custom formatter callables that do not accept it a typeerror would be raised if validate key is passed in with a formatter callable that does not accept validate as a parameter if user hasn t mentioned it the default will be fine configure a filter from a dictionary add filters to a filterer from a list of names unbounded configure a handler from a dictionary for restoring in case of error special case for handler which refers to another handler restore for deferred cfg another special case for handler which refers to other handlers restore for deferred cfg the argument name changed from strm to stream retry with old name this is so that code can be used with older python versions e g by django add handlers to a logger from a list of names perform configuration which is common to root and non root loggers remove any existing handlers configure a non root logger from a dictionary configure a root logger from a dictionary configure logging using a dictionary start up a socket server on the specified port and listen for new configurations these will be sent as a file suitable for processing by fileconfig returns a thread object on which you can call start to start the server and which you can join when appropriate to stop the server call stoplistening use the verify argument to verify any bytes received across the wire from a client if specified it should be a callable which receives a single argument the bytes of configuration data received across the network and it should return either none to indicate that the passed in bytes could not be verified and should be discarded or a byte string which is then passed to the configuration machinery as normal note that you can return transformed bytes e g by decrypting the bytes passed in handler for a logging configuration request it expects a completely new logging configuration and uses fileconfig to install it handle a request each request is expected to be a 4 byte length packed using struct pack l n followed by the config file uses fileconfig to do the grunt work verified can process apply new configuration a simple tcp socket based logging config receiver stop the listening server which was created with a call to listen
import errno
import functools
import io
import logging
import logging.handlers
import os
import queue
import re
import struct
import threading
import traceback

from socketserver import ThreadingTCPServer, StreamRequestHandler


DEFAULT_LOGGING_CONFIG_PORT = 9030

RESET_ERROR = errno.ECONNRESET






_listener = None

def fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None):
    
    import configparser

    if isinstance(fname, str):
        if not os.path.exists(fname):
            raise FileNotFoundError(f"{fname} doesn't exist")
        elif not os.path.getsize(fname):
            raise RuntimeError(f'{fname} is an empty file')

    if isinstance(fname, configparser.RawConfigParser):
        cp = fname
    else:
        try:
            cp = configparser.ConfigParser(defaults)
            if hasattr(fname, 'readline'):
                cp.read_file(fname)
            else:
                encoding = io.text_encoding(encoding)
                cp.read(fname, encoding=encoding)
        except configparser.ParsingError as e:
            raise RuntimeError(f'{fname} is invalid: {e}')

    formatters = _create_formatters(cp)

    
    with logging._lock:
        _clearExistingHandlers()

        
        handlers = _install_handlers(cp, formatters)
        _install_loggers(cp, handlers, disable_existing_loggers)


def _resolve(name):
    
    name = name.split('.')
    used = name.pop(0)
    found = __import__(used)
    for n in name:
        used = used + '.' + n
        try:
            found = getattr(found, n)
        except AttributeError:
            __import__(used)
            found = getattr(found, n)
    return found

def _strip_spaces(alist):
    return map(str.strip, alist)

def _create_formatters(cp):
    
    flist = cp["formatters"]["keys"]
    if not len(flist):
        return {}
    flist = flist.split(",")
    flist = _strip_spaces(flist)
    formatters = {}
    for form in flist:
        sectname = "formatter_%s" % form
        fs = cp.get(sectname, "format", raw=True, fallback=None)
        dfs = cp.get(sectname, "datefmt", raw=True, fallback=None)
        stl = cp.get(sectname, "style", raw=True, fallback='%')
        defaults = cp.get(sectname, "defaults", raw=True, fallback=None)

        c = logging.Formatter
        class_name = cp[sectname].get("class")
        if class_name:
            c = _resolve(class_name)

        if defaults is not None:
            defaults = eval(defaults, vars(logging))
            f = c(fs, dfs, stl, defaults=defaults)
        else:
            f = c(fs, dfs, stl)
        formatters[form] = f
    return formatters


def _install_handlers(cp, formatters):
    
    hlist = cp["handlers"]["keys"]
    if not len(hlist):
        return {}
    hlist = hlist.split(",")
    hlist = _strip_spaces(hlist)
    handlers = {}
    fixups = [] 
    for hand in hlist:
        section = cp["handler_%s" % hand]
        klass = section["class"]
        fmt = section.get("formatter", "")
        try:
            klass = eval(klass, vars(logging))
        except (AttributeError, NameError):
            klass = _resolve(klass)
        args = section.get("args", '()')
        args = eval(args, vars(logging))
        kwargs = section.get("kwargs", '{}')
        kwargs = eval(kwargs, vars(logging))
        h = klass(*args, **kwargs)
        h.name = hand
        if "level" in section:
            level = section["level"]
            h.setLevel(level)
        if len(fmt):
            h.setFormatter(formatters[fmt])
        if issubclass(klass, logging.handlers.MemoryHandler):
            target = section.get("target", "")
            if len(target): 
                fixups.append((h, target))
        handlers[hand] = h
    
    for h, t in fixups:
        h.setTarget(handlers[t])
    return handlers

def _handle_existing_loggers(existing, child_loggers, disable_existing):
    
    root = logging.root
    for log in existing:
        logger = root.manager.loggerDict[log]
        if log in child_loggers:
            if not isinstance(logger, logging.PlaceHolder):
                logger.setLevel(logging.NOTSET)
                logger.handlers = []
                logger.propagate = True
        else:
            logger.disabled = disable_existing

def _install_loggers(cp, handlers, disable_existing):
    

    
    llist = cp["loggers"]["keys"]
    llist = llist.split(",")
    llist = list(_strip_spaces(llist))
    llist.remove("root")
    section = cp["logger_root"]
    root = logging.root
    log = root
    if "level" in section:
        level = section["level"]
        log.setLevel(level)
    for h in root.handlers[:]:
        root.removeHandler(h)
    hlist = section["handlers"]
    if len(hlist):
        hlist = hlist.split(",")
        hlist = _strip_spaces(hlist)
        for hand in hlist:
            log.addHandler(handlers[hand])

    
    
    
    
    
    
    
    
    
    existing = list(root.manager.loggerDict.keys())
    
    
    
    
    existing.sort()
    
    
    child_loggers = []
    
    for log in llist:
        section = cp["logger_%s" % log]
        qn = section["qualname"]
        propagate = section.getint("propagate", fallback=1)
        logger = logging.getLogger(qn)
        if qn in existing:
            i = existing.index(qn) + 1 
            prefixed = qn + "."
            pflen = len(prefixed)
            num_existing = len(existing)
            while i < num_existing:
                if existing[i][:pflen] == prefixed:
                    child_loggers.append(existing[i])
                i += 1
            existing.remove(qn)
        if "level" in section:
            level = section["level"]
            logger.setLevel(level)
        for h in logger.handlers[:]:
            logger.removeHandler(h)
        logger.propagate = propagate
        logger.disabled = 0
        hlist = section["handlers"]
        if len(hlist):
            hlist = hlist.split(",")
            hlist = _strip_spaces(hlist)
            for hand in hlist:
                logger.addHandler(handlers[hand])

    
    
    
    
    
    
    
    
    
    
    
    
    
    _handle_existing_loggers(existing, child_loggers, disable_existing)


def _clearExistingHandlers():
    
    logging._handlers.clear()
    logging.shutdown(logging._handlerList[:])
    del logging._handlerList[:]


IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I)


def valid_ident(s):
    m = IDENTIFIER.match(s)
    if not m:
        raise ValueError('Not a valid Python identifier: %r' % s)
    return True


class ConvertingMixin(object):
    

    def convert_with_key(self, key, value, replace=True):
        result = self.configurator.convert(value)
        
        if value is not result:
            if replace:
                self[key] = result
            if type(result) in (ConvertingDict, ConvertingList,
                               ConvertingTuple):
                result.parent = self
                result.key = key
        return result

    def convert(self, value):
        result = self.configurator.convert(value)
        if value is not result:
            if type(result) in (ConvertingDict, ConvertingList,
                               ConvertingTuple):
                result.parent = self
        return result











class ConvertingDict(dict, ConvertingMixin):
    

    def __getitem__(self, key):
        value = dict.__getitem__(self, key)
        return self.convert_with_key(key, value)

    def get(self, key, default=None):
        value = dict.get(self, key, default)
        return self.convert_with_key(key, value)

    def pop(self, key, default=None):
        value = dict.pop(self, key, default)
        return self.convert_with_key(key, value, replace=False)

class ConvertingList(list, ConvertingMixin):
    
    def __getitem__(self, key):
        value = list.__getitem__(self, key)
        return self.convert_with_key(key, value)

    def pop(self, idx=-1):
        value = list.pop(self, idx)
        return self.convert(value)

class ConvertingTuple(tuple, ConvertingMixin):
    
    def __getitem__(self, key):
        value = tuple.__getitem__(self, key)
        
        return self.convert_with_key(key, value, replace=False)

class BaseConfigurator(object):
    

    CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$')

    WORD_PATTERN = re.compile(r'^\s*(\w+)\s*')
    DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*')
    INDEX_PATTERN = re.compile(r'^\[([^\[\]]*)\]\s*')
    DIGIT_PATTERN = re.compile(r'^\d+$')

    value_converters = {
        'ext' : 'ext_convert',
        'cfg' : 'cfg_convert',
    }

    
    importer = staticmethod(__import__)

    def __init__(self, config):
        self.config = ConvertingDict(config)
        self.config.configurator = self

    def resolve(self, s):
        
        name = s.split('.')
        used = name.pop(0)
        try:
            found = self.importer(used)
            for frag in name:
                used += '.' + frag
                try:
                    found = getattr(found, frag)
                except AttributeError:
                    self.importer(used)
                    found = getattr(found, frag)
            return found
        except ImportError as e:
            v = ValueError('Cannot resolve %r: %s' % (s, e))
            raise v from e

    def ext_convert(self, value):
        
        return self.resolve(value)

    def cfg_convert(self, value):
        
        rest = value
        m = self.WORD_PATTERN.match(rest)
        if m is None:
            raise ValueError("Unable to convert %r" % value)
        else:
            rest = rest[m.end():]
            d = self.config[m.groups()[0]]
            
            while rest:
                m = self.DOT_PATTERN.match(rest)
                if m:
                    d = d[m.groups()[0]]
                else:
                    m = self.INDEX_PATTERN.match(rest)
                    if m:
                        idx = m.groups()[0]
                        if not self.DIGIT_PATTERN.match(idx):
                            d = d[idx]
                        else:
                            try:
                                n = int(idx) 
                                d = d[n]
                            except TypeError:
                                d = d[idx]
                if m:
                    rest = rest[m.end():]
                else:
                    raise ValueError('Unable to convert '
                                     '%r at %r' % (value, rest))
        
        return d

    def convert(self, value):
        
        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
            value = ConvertingDict(value)
            value.configurator = self
        elif not isinstance(value, ConvertingList) and isinstance(value, list):
            value = ConvertingList(value)
            value.configurator = self
        elif not isinstance(value, ConvertingTuple) and\
                 isinstance(value, tuple) and not hasattr(value, '_fields'):
            value = ConvertingTuple(value)
            value.configurator = self
        elif isinstance(value, str): 
            m = self.CONVERT_PATTERN.match(value)
            if m:
                d = m.groupdict()
                prefix = d['prefix']
                converter = self.value_converters.get(prefix, None)
                if converter:
                    suffix = d['suffix']
                    converter = getattr(self, converter)
                    value = converter(suffix)
        return value

    def configure_custom(self, config):
        
        c = config.pop('()')
        if not callable(c):
            c = self.resolve(c)
        
        kwargs = {k: config[k] for k in config if (k != '.' and valid_ident(k))}
        result = c(**kwargs)
        props = config.pop('.', None)
        if props:
            for name, value in props.items():
                setattr(result, name, value)
        return result

    def as_tuple(self, value):
        
        if isinstance(value, list):
            value = tuple(value)
        return value

class DictConfigurator(BaseConfigurator):
    

    def configure(self):
        

        config = self.config
        if 'version' not in config:
            raise ValueError("dictionary doesn't specify a version")
        if config['version'] != 1:
            raise ValueError("Unsupported version: %s" % config['version'])
        incremental = config.pop('incremental', False)
        EMPTY_DICT = {}
        with logging._lock:
            if incremental:
                handlers = config.get('handlers', EMPTY_DICT)
                for name in handlers:
                    if name not in logging._handlers:
                        raise ValueError('No handler found with '
                                         'name %r'  % name)
                    else:
                        try:
                            handler = logging._handlers[name]
                            handler_config = handlers[name]
                            level = handler_config.get('level', None)
                            if level:
                                handler.setLevel(logging._checkLevel(level))
                        except Exception as e:
                            raise ValueError('Unable to configure handler '
                                             '%r' % name) from e
                loggers = config.get('loggers', EMPTY_DICT)
                for name in loggers:
                    try:
                        self.configure_logger(name, loggers[name], True)
                    except Exception as e:
                        raise ValueError('Unable to configure logger '
                                         '%r' % name) from e
                root = config.get('root', None)
                if root:
                    try:
                        self.configure_root(root, True)
                    except Exception as e:
                        raise ValueError('Unable to configure root '
                                         'logger') from e
            else:
                disable_existing = config.pop('disable_existing_loggers', True)

                _clearExistingHandlers()

                
                formatters = config.get('formatters', EMPTY_DICT)
                for name in formatters:
                    try:
                        formatters[name] = self.configure_formatter(
                                                            formatters[name])
                    except Exception as e:
                        raise ValueError('Unable to configure '
                                         'formatter %r' % name) from e
                
                filters = config.get('filters', EMPTY_DICT)
                for name in filters:
                    try:
                        filters[name] = self.configure_filter(filters[name])
                    except Exception as e:
                        raise ValueError('Unable to configure '
                                         'filter %r' % name) from e

                
                
                
                handlers = config.get('handlers', EMPTY_DICT)
                deferred = []
                for name in sorted(handlers):
                    try:
                        handler = self.configure_handler(handlers[name])
                        handler.name = name
                        handlers[name] = handler
                    except Exception as e:
                        if ' not configured yet' in str(e.__cause__):
                            deferred.append(name)
                        else:
                            raise ValueError('Unable to configure handler '
                                             '%r' % name) from e

                
                for name in deferred:
                    try:
                        handler = self.configure_handler(handlers[name])
                        handler.name = name
                        handlers[name] = handler
                    except Exception as e:
                        raise ValueError('Unable to configure handler '
                                         '%r' % name) from e

                

                
                
                
                
                
                
                
                
                root = logging.root
                existing = list(root.manager.loggerDict.keys())
                
                
                
                
                existing.sort()
                
                
                child_loggers = []
                
                loggers = config.get('loggers', EMPTY_DICT)
                for name in loggers:
                    if name in existing:
                        i = existing.index(name) + 1 
                        prefixed = name + "."
                        pflen = len(prefixed)
                        num_existing = len(existing)
                        while i < num_existing:
                            if existing[i][:pflen] == prefixed:
                                child_loggers.append(existing[i])
                            i += 1
                        existing.remove(name)
                    try:
                        self.configure_logger(name, loggers[name])
                    except Exception as e:
                        raise ValueError('Unable to configure logger '
                                         '%r' % name) from e

                
                
                
                
                
                
                
                
                
                
                
                
                
                _handle_existing_loggers(existing, child_loggers,
                                         disable_existing)

                
                root = config.get('root', None)
                if root:
                    try:
                        self.configure_root(root)
                    except Exception as e:
                        raise ValueError('Unable to configure root '
                                         'logger') from e

    def configure_formatter(self, config):
        
        if '()' in config:
            factory = config['()'] 
            try:
                result = self.configure_custom(config)
            except TypeError as te:
                if "'format'" not in str(te):
                    raise
                
                
                
                
                config['fmt'] = config.pop('format')
                config['()'] = factory
                result = self.configure_custom(config)
        else:
            fmt = config.get('format', None)
            dfmt = config.get('datefmt', None)
            style = config.get('style', '%')
            cname = config.get('class', None)
            defaults = config.get('defaults', None)

            if not cname:
                c = logging.Formatter
            else:
                c = _resolve(cname)

            kwargs  = {}

            
            
            
            if defaults is not None:
                kwargs['defaults'] = defaults

            
            
            if 'validate' in config:  
                result = c(fmt, dfmt, style, config['validate'], **kwargs)
            else:
                result = c(fmt, dfmt, style, **kwargs)

        return result

    def configure_filter(self, config):
        
        if '()' in config:
            result = self.configure_custom(config)
        else:
            name = config.get('name', '')
            result = logging.Filter(name)
        return result

    def add_filters(self, filterer, filters):
        
        for f in filters:
            try:
                if callable(f) or callable(getattr(f, 'filter', None)):
                    filter_ = f
                else:
                    filter_ = self.config['filters'][f]
                filterer.addFilter(filter_)
            except Exception as e:
                raise ValueError('Unable to add filter %r' % f) from e

    def _configure_queue_handler(self, klass, **kwargs):
        if 'queue' in kwargs:
            q = kwargs['queue']
        else:
            q = queue.Queue()  
        rhl = kwargs.get('respect_handler_level', False)
        if 'listener' in kwargs:
            lklass = kwargs['listener']
        else:
            lklass = logging.handlers.QueueListener
        listener = lklass(q, *kwargs['handlers'], respect_handler_level=rhl)
        handler = klass(q)
        handler.listener = listener
        return handler

    def configure_handler(self, config):
        
        config_copy = dict(config)  
        formatter = config.pop('formatter', None)
        if formatter:
            try:
                formatter = self.config['formatters'][formatter]
            except Exception as e:
                raise ValueError('Unable to set formatter '
                                 '%r' % formatter) from e
        level = config.pop('level', None)
        filters = config.pop('filters', None)
        if '()' in config:
            c = config.pop('()')
            if not callable(c):
                c = self.resolve(c)
            factory = c
        else:
            cname = config.pop('class')
            if callable(cname):
                klass = cname
            else:
                klass = self.resolve(cname)
            if issubclass(klass, logging.handlers.MemoryHandler) and\
                'target' in config:
                
                try:
                    tn = config['target']
                    th = self.config['handlers'][tn]
                    if not isinstance(th, logging.Handler):
                        config.update(config_copy)  
                        raise TypeError('target not configured yet')
                    config['target'] = th
                except Exception as e:
                    raise ValueError('Unable to set target handler %r' % tn) from e
            elif issubclass(klass, logging.handlers.QueueHandler):
                
                if 'handlers' not in config:
                    raise ValueError('No handlers specified for a QueueHandler')
                if 'queue' in config:
                    qspec = config['queue']
                    if not isinstance(qspec, queue.Queue):
                        if isinstance(qspec, str):
                            q = self.resolve(qspec)
                            if not callable(q):
                                raise TypeError('Invalid queue specifier %r' % qspec)
                            q = q()
                        elif isinstance(qspec, dict):
                            if '()' not in qspec:
                                raise TypeError('Invalid queue specifier %r' % qspec)
                            q = self.configure_custom(dict(qspec))
                        else:
                            raise TypeError('Invalid queue specifier %r' % qspec)
                        config['queue'] = q
                if 'listener' in config:
                    lspec = config['listener']
                    if isinstance(lspec, type):
                        if not issubclass(lspec, logging.handlers.QueueListener):
                            raise TypeError('Invalid listener specifier %r' % lspec)
                    else:
                        if isinstance(lspec, str):
                            listener = self.resolve(lspec)
                            if isinstance(listener, type) and\
                                not issubclass(listener, logging.handlers.QueueListener):
                                raise TypeError('Invalid listener specifier %r' % lspec)
                        elif isinstance(lspec, dict):
                            if '()' not in lspec:
                                raise TypeError('Invalid listener specifier %r' % lspec)
                            listener = self.configure_custom(dict(lspec))
                        else:
                            raise TypeError('Invalid listener specifier %r' % lspec)
                        if not callable(listener):
                            raise TypeError('Invalid listener specifier %r' % lspec)
                        config['listener'] = listener
                hlist = []
                try:
                    for hn in config['handlers']:
                        h = self.config['handlers'][hn]
                        if not isinstance(h, logging.Handler):
                            config.update(config_copy)  
                            raise TypeError('Required handler %r '
                                            'is not configured yet' % hn)
                        hlist.append(h)
                except Exception as e:
                    raise ValueError('Unable to set required handler %r' % hn) from e
                config['handlers'] = hlist
            elif issubclass(klass, logging.handlers.SMTPHandler) and\
                'mailhost' in config:
                config['mailhost'] = self.as_tuple(config['mailhost'])
            elif issubclass(klass, logging.handlers.SysLogHandler) and\
                'address' in config:
                config['address'] = self.as_tuple(config['address'])
            if issubclass(klass, logging.handlers.QueueHandler):
                factory = functools.partial(self._configure_queue_handler, klass)
            else:
                factory = klass
        kwargs = {k: config[k] for k in config if (k != '.' and valid_ident(k))}
        try:
            result = factory(**kwargs)
        except TypeError as te:
            if "'stream'" not in str(te):
                raise
            
            
            
            
            kwargs['strm'] = kwargs.pop('stream')
            result = factory(**kwargs)
        if formatter:
            result.setFormatter(formatter)
        if level is not None:
            result.setLevel(logging._checkLevel(level))
        if filters:
            self.add_filters(result, filters)
        props = config.pop('.', None)
        if props:
            for name, value in props.items():
                setattr(result, name, value)
        return result

    def add_handlers(self, logger, handlers):
        
        for h in handlers:
            try:
                logger.addHandler(self.config['handlers'][h])
            except Exception as e:
                raise ValueError('Unable to add handler %r' % h) from e

    def common_logger_config(self, logger, config, incremental=False):
        
        level = config.get('level', None)
        if level is not None:
            logger.setLevel(logging._checkLevel(level))
        if not incremental:
            
            for h in logger.handlers[:]:
                logger.removeHandler(h)
            handlers = config.get('handlers', None)
            if handlers:
                self.add_handlers(logger, handlers)
            filters = config.get('filters', None)
            if filters:
                self.add_filters(logger, filters)

    def configure_logger(self, name, config, incremental=False):
        
        logger = logging.getLogger(name)
        self.common_logger_config(logger, config, incremental)
        logger.disabled = False
        propagate = config.get('propagate', None)
        if propagate is not None:
            logger.propagate = propagate

    def configure_root(self, config, incremental=False):
        
        root = logging.getLogger()
        self.common_logger_config(root, config, incremental)

dictConfigClass = DictConfigurator

def dictConfig(config):
    
    dictConfigClass(config).configure()


def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
    

    class ConfigStreamHandler(StreamRequestHandler):
        
        def handle(self):
            
            try:
                conn = self.connection
                chunk = conn.recv(4)
                if len(chunk) == 4:
                    slen = struct.unpack(">L", chunk)[0]
                    chunk = self.connection.recv(slen)
                    while len(chunk) < slen:
                        chunk = chunk + conn.recv(slen - len(chunk))
                    if self.server.verify is not None:
                        chunk = self.server.verify(chunk)
                    if chunk is not None:   
                        chunk = chunk.decode("utf-8")
                        try:
                            import json
                            d =json.loads(chunk)
                            assert isinstance(d, dict)
                            dictConfig(d)
                        except Exception:
                            

                            file = io.StringIO(chunk)
                            try:
                                fileConfig(file)
                            except Exception:
                                traceback.print_exc()
                    if self.server.ready:
                        self.server.ready.set()
            except OSError as e:
                if e.errno != RESET_ERROR:
                    raise

    class ConfigSocketReceiver(ThreadingTCPServer):
        

        allow_reuse_address = 1

        def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
                     handler=None, ready=None, verify=None):
            ThreadingTCPServer.__init__(self, (host, port), handler)
            with logging._lock:
                self.abort = 0
            self.timeout = 1
            self.ready = ready
            self.verify = verify

        def serve_until_stopped(self):
            import select
            abort = 0
            while not abort:
                rd, wr, ex = select.select([self.socket.fileno()],
                                           [], [],
                                           self.timeout)
                if rd:
                    self.handle_request()
                with logging._lock:
                    abort = self.abort
            self.server_close()

    class Server(threading.Thread):

        def __init__(self, rcvr, hdlr, port, verify):
            super(Server, self).__init__()
            self.rcvr = rcvr
            self.hdlr = hdlr
            self.port = port
            self.verify = verify
            self.ready = threading.Event()

        def run(self):
            server = self.rcvr(port=self.port, handler=self.hdlr,
                               ready=self.ready,
                               verify=self.verify)
            if self.port == 0:
                self.port = server.server_address[1]
            self.ready.set()
            global _listener
            with logging._lock:
                _listener = server
            server.serve_until_stopped()

    return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)

def stopListening():
    
    global _listener
    with logging._lock:
        if _listener:
            _listener.abort = 1
            _listener = None