B t `@sdZddlZddlZddlmZddlZddlZddlZddlm Z ddZ yddl Z dZ Wne k rtdZ YnXy ddlZWne k rddlZYnXd Zed Zd&d d Zed ZddZddZddZGdddeZddZie_GdddeZGdddeZGdddeZ GdddeZ!Gd d!d!eZ"Gd"d#d#eZ#e#Z$ej%r|e#j&e#_'e#j(e#_)e#j*e#_+Gd$d%d%eZ,dS)'ad5================================= The PyUtilib Configuration System ================================= The PyUtilib config system provides a set of three classes (:py:class:`ConfigDict`, :py:class:`ConfigList`, and :py:class:`ConfigValue`) for managing and documenting structured configuration information and user input. The system is based around the ConfigValue class, which provides storage for a single configuration entry. ConfigValue objects can be grouped using two containers (ConfigDict and ConfigList), which provide functionality analogous to Python's dict and list classes, respectively. At its simplest, the Config system allows for developers to specify a dictionary of documented configuration entries, allow users to provide values for those entries, and retrieve the current values: .. doctest:: :hide: >>> import argparse >>> from pyutilib.misc.config import ( ... ConfigDict, ConfigList, ConfigValue, In, ... ) .. doctest:: >>> config = ConfigDict() >>> config.declare('filename', ConfigValue( ... default=None, ... domain=str, ... description="Input file name", ... )) >>> config.declare("bound tolerance", ConfigValue( ... default=1E-5, ... domain=float, ... description="Bound tolerance", ... doc="Relative tolerance for bound feasibility checks" ... )) >>> config.declare("iteration limit", ConfigValue( ... default=30, ... domain=int, ... description="Iteration limit", ... doc="Number of maximum iterations in the decomposition methods" ... )) >>> config['filename'] = 'tmp.txt' >>> print(config['filename']) tmp.txt >>> print(config['iteration limit']) 30 For convenience, ConfigDict objects support read/write access via attributes (with spaces in the declaration names replaced by underscores): .. doctest:: >>> print(config.filename) tmp.txt >>> print(config.iteration_limit) 30 >>> config.iteration_limit = 20 >>> print(config.iteration_limit) 20 All Config objects support a ``domain`` keyword that accepts a callable object (type, function, or callable instance). The domain callable should take data and map it onto the desired domain, optionally performing domain validation (see :py:class:`ConfigValue`, :py:class:`ConfigDict`, and :py:class:`ConfigList` for more information). This allows client code to accept a very flexible set of inputs without "cluttering" the code with input validation: .. doctest:: >>> config.iteration_limit = 35.5 >>> print(config.iteration_limit) 35 >>> print(type(config.iteration_limit).__name__) int Configuring class hierarchies ============================= A feature of the Config system is that the core classes all implement ``__call__``, and can themselves be used as ``domain`` values. Beyond providing domain verification for complex hierarchical structures, this feature allows ConfigDicts to cleanly support the configuration of derived objects. Consider the following example: .. doctest:: >>> class Base(object): ... CONFIG = ConfigDict() ... CONFIG.declare('filename', ConfigValue( ... default='input.txt', ... domain=str, ... )) ... def __init__(self, **kwds): ... c = self.CONFIG(kwds) ... c.display() ... >>> class Derived(Base): ... CONFIG = Base.CONFIG() ... CONFIG.declare('pattern', ConfigValue( ... default=None, ... domain=str, ... )) ... >>> tmp = Base(filename='foo.txt') filename: foo.txt >>> tmp = Derived(pattern='.*warning') filename: input.txt pattern: .*warning Here, the base class ``Base`` declares a class-level attribute CONFIG as a ConfigDict containing a single entry (``filename``). The derived class (``Derived``) then starts by making a copy of the base class' ``CONFIG``, and then defines an additional entry (`pattern`). Instances of the base class will still create ``c`` instances that only have the single ``filename`` entry, whereas instances of the derived class will have ``c`` instances with two entries: the ``pattern`` entry declared by the derived class, and the ``filename`` entry "inherited" from the base class. An extension of this design pattern provides a clean approach for handling "ephemeral" instance options. Consider an interface to an external "solver". Our class implements a ``solve()`` method that takes a problem and sends it to the solver along with some solver configuration options. We would like to be able to set those options "persistently" on instances of the interface class, but still override them "temporarily" for individual calls to ``solve()``. We implement this by creating copies of the class's configuration for both specific instances and for use by each ``solve()`` call: .. doctest:: >>> class Solver(object): ... CONFIG = ConfigDict() ... CONFIG.declare('iterlim', ConfigValue( ... default=10, ... domain=int, ... )) ... def __init__(self, **kwds): ... self.config = self.CONFIG(kwds) ... def solve(self, model, **options): ... config = self.config(options) ... # Solve the model with the specified iterlim ... config.display() ... >>> solver = Solver() >>> solver.solve(None) iterlim: 10 >>> solver.config.iterlim = 20 >>> solver.solve(None) iterlim: 20 >>> solver.solve(None, iterlim=50) iterlim: 50 >>> solver.solve(None) iterlim: 20 Interacting with argparse ========================= In addition to basic storage and retrieval, the Config system provides hooks to the argparse command-line argument parsing system. Individual Config entries can be declared as argparse arguments. To make declaration simpler, the :py:meth:`declare` method returns the declared Config object so that the argument declaration can be done inline: .. doctest:: >>> config = ConfigDict() >>> config.declare('iterlim', ConfigValue( ... domain=int, ... default=100, ... description="iteration limit", ... )).declare_as_argument() >>> config.declare('lbfgs', ConfigValue( ... domain=bool, ... description="use limited memory BFGS update", ... )).declare_as_argument() >>> config.declare('linesearch', ConfigValue( ... domain=bool, ... default=True, ... description="use line search", ... )).declare_as_argument() >>> config.declare('relative tolerance', ConfigValue( ... domain=float, ... description="relative convergence tolerance", ... )).declare_as_argument('--reltol', '-r', group='Tolerances') >>> config.declare('absolute tolerance', ConfigValue( ... domain=float, ... description="absolute convergence tolerance", ... )).declare_as_argument('--abstol', '-a', group='Tolerances') The ConfigDict can then be used to initialize (or augment) an argparse ArgumentParser object: .. doctest:: >>> parser = argparse.ArgumentParser("tester") >>> config.initialize_argparse(parser) Key information from the ConfigDict is automatically transferred over to the ArgumentParser object: .. doctest:: >>> print(parser.format_help()) usage: tester [-h] [--iterlim INT] [--lbfgs] [--disable-linesearch] [--reltol FLOAT] [--abstol FLOAT] optional arguments: -h, --help show this help message and exit --iterlim INT iteration limit --lbfgs use limited memory BFGS update --disable-linesearch [DON'T] use line search Tolerances: --reltol FLOAT, -r FLOAT relative convergence tolerance --abstol FLOAT, -a FLOAT absolute convergence tolerance Parsed arguments can then be imported back into the ConfigDict: .. doctest:: >>> args=parser.parse_args(['--lbfgs', '--reltol', '0.1', '-a', '0.2']) >>> args = config.import_argparse(args) >>> config.display() iterlim: 100 lbfgs: true linesearch: true relative tolerance: 0.1 absolute tolerance: 0.2 Accessing user-specified values =============================== It is frequently useful to know which values a user explicitly set, and which values a user explicitly set, but have never been retrieved. The configuration system provides two gemerator methods to return the items that a user explicitly set (:py:meth:`user_values`) and the items that were set but never retrieved (:py:meth:`unused_user_values`): .. doctest:: >>> print([val.name() for val in config.user_values()]) ['lbfgs', 'relative tolerance', 'absolute tolerance'] >>> print(config.relative_tolerance) 0.1 >>> print([val.name() for val in config.unused_user_values()]) ['lbfgs', 'absolute tolerance'] Generating output & documentation ================================= Configuration objects support three methods for generating output and documentation: :py:meth:`display()`, :py:meth:`generate_yaml_template()`, and :py:meth:`generate_documentation()`. The simplest is :py:meth:`display()`, which prints out the current values of the configuration object (and if it is a container type, all of it's children). :py:meth:`generate_yaml_template` is simular to :py:meth:`display`, but also includes the description fields as formatted comments. .. doctest:: >>> solver_config = config >>> config = ConfigDict() >>> config.declare('output', ConfigValue( ... default='results.yml', ... domain=str, ... description='output results filename' ... )) >>> config.declare('verbose', ConfigValue( ... default=0, ... domain=int, ... description='output verbosity', ... doc='This sets the system verbosity. The default (0) only logs ' ... 'warnings and errors. Larger integer values will produce ' ... 'additional log messages.', ... )) >>> config.declare('solvers', ConfigList( ... domain=solver_config, ... description='list of solvers to apply', ... )) >>> config.display() output: results.yml verbose: 0 solvers: [] >>> print(config.generate_yaml_template()) output: results.yml # output results filename verbose: 0 # output verbosity solvers: [] # list of solvers to apply It is important to note that both methods document the current state of the configuration object. So, in the example above, since the `solvers` list is empty, you will not get any information on the elements in the list. Of course, if you add a value to the list, then the data will be output: .. doctest:: >>> tmp = config() >>> tmp.solvers.append({}) >>> tmp.display() output: results.yml verbose: 0 solvers: - iterlim: 100 lbfgs: true linesearch: true relative tolerance: 0.1 absolute tolerance: 0.2 >>> print(tmp.generate_yaml_template()) output: results.yml # output results filename verbose: 0 # output verbosity solvers: # list of solvers to apply - iterlim: 100 # iteration limit lbfgs: true # use limited memory BFGS update linesearch: true # use line search relative tolerance: 0.1 # relative convergence tolerance absolute tolerance: 0.2 # absolute convergence tolerance The third method (:py:meth:`generate_documentation`) behaves differently. This method is designed to generate reference documentation. For each configuration item, the `doc` field is output. If the item has no `doc`, then the `description` field is used. List containers have their *domain* documented and not their current values. The documentation can be configured through optional arguments. The defaults generate LaTeX documentation: .. doctest:: >>> print(config.generate_documentation()) \begin{description}[topsep=0pt,parsep=0.5em,itemsep=-0.4em] \item[{output}]\hfill \\output results filename \item[{verbose}]\hfill \\This sets the system verbosity. The default (0) only logs warnings and errors. Larger integer values will produce additional log messages. \item[{solvers}]\hfill \\list of solvers to apply \begin{description}[topsep=0pt,parsep=0.5em,itemsep=-0.4em] \item[{iterlim}]\hfill \\iteration limit \item[{lbfgs}]\hfill \\use limited memory BFGS update \item[{linesearch}]\hfill \\use line search \item[{relative tolerance}]\hfill \\relative convergence tolerance \item[{absolute tolerance}]\hfill \\absolute convergence tolerance \end{description} \end{description} N)wrap)xrangecOsPyddlm}Wntk r,dd}YnXdtks.dump_dump)Zyamlr ImportErrorglobalsAssertionError)r kwdsrr r r r s r TF) ConfigDict ConfigBlock ConfigList ConfigValuezpyutilib.misc.configcCs.|rtdd|}tdd|}tdd|S)Nz\s-_z[^a-zA-Z0-9-_])resub)nameZ space_to_dashr r r _munge_namesrz ^([ ]*)cCs|s|S|}x|r.|ds.|dqWt|dkrD|Stdd|ddD}tt|dd|kr|d|d<n|d|d |d<x4t |ddD] \}}||d ||d<qWd |S)Nrcss"|]}tt|dVqdS)rN)len _leadingSpacematchgroup).0lr r r sz%_strip_indentation.. ) splitlinesstrippoprlstripminrrr rstrip enumeratejoin)doclinesZ minIndentir"r r r _strip_indentations r0cCs|}|dk ryf||kr|jn|}tt|jjddk rd|t|dd7}|drp|dd}n |t|7}Wn|tt |7}YnX|S)NT)default_flow_stylez...) _datagetattr _builtins __class____name__r r*endswithrr)prefixvalueobj_strr3r r r _value2strings r=cCsv|}|dk rnyD||kr|jn|}|t|dd7}|drN|dd}Wn|tt|7}YnX|S)NT)r1z...r2)r3r r*r8rr)r9r:r;r<r3r r r _value2yamls r>c@seZdZddZddZdS)_UnpickleableDomaincCst|j|_|d|_dS)NT)rr7_typer_name)selfr;r r r __init__s z_UnpickleableDomain.__init__cCstd|j|jf|S)Nz%s %s was pickled with an unpicklable domain. The domain was stripped and lost during the pickle process. Setting new values on the restored object cannot be mapped into the correct domain. )loggingerrorr@rA)rBargr r r __call__sz_UnpickleableDomain.__call__N)r7 __module__ __qualname__rCrGr r r r r?sr?cCsrt|}|tjkr(tj|r |St|Syt|dtj|<|Sttdt r\dtj|<t|SdS)NTrF) r _picklableknownr?pickledumps isinstancesysexc_info RuntimeError)fieldr;Zftyper r r rJs    rJc seZdZdZGdddeZd0ddZfdd Zd d Zeeeeeeeed f d dZ d1ddZ ddZ ddZ ddZ ddZddZddZddZd2d d!Zd3d#d$Zd4d*d+Zd,d-Zd.d/ZZS)5 ConfigBase) _parentrA_userSet _userAccessedr3_default_domain _description_doc _visibility _argparsec@s eZdZdS)zConfigBase.NoArgumentN)r7rHrIr r r r NoArgumentsr]NrcCsNd|_d|_d|_d|_d|_||_||_t||_t||_ ||_ d|_ dS)NF) rTrArUrVr3rWrXr0rYrZr[r\)rBdefaultdomain descriptionr- visibilityr r r rCs  zConfigBase.__init__cs\jjdtkri}ntt}|fddtjDt|d|d<d|d<|S)Nc3s|]}|t|fVqdS)N)r4)r!key)rBr r r#Dsz*ConfigBase.__getstate__..rXrT)r6__mro__rSsuper __getstate__update __slots__rJ)rBstate)r6)rBr rf0szConfigBase.__getstate__cCs*x$t|D]\}}t|||q WdS)N)six iteritemsobject __setattr__)rBrircvalr r r __setstate__IszConfigBase.__setstate__Fc Csi} |tjkr|jn|| d<|tjkr,|jn|| d<|tjkrD|jn|| d<t|tr|tjkrf|jn|| d<|tjkr~|jn|| d<|tjk rt dt |f|tjk rt dt |fnr|tjkr| n|| d<|tjkr|j n|| d <|tjk rt d t |f|tjk r6t d t |f|jf| } t|trx||jD]h} | sl| |jkrV|j| } | | d | j| <}| j| | |jkr| j| | |_| j|_qVWn| |tjk r| || S) Nr`r-raimplicitimplicit_domainz3domain ignored by __call__(): class is a ConfigDictz4default ignored by __call__(): class is a ConfigDictr^r_z._get_subparser_or_groupcs}t|}d|kr|d}t|trxx\t|D]@\}}||\}}|s2|t|dkr2td|dfq2Wn||\}}d|krdd|d<d|kr|dd d krj dk rj j t krj j |d<nt d |d<|j|d tji|dS) Nr rz9Could not find argparse subparser '%s' for Config item %sTdestz CONFIGBLOCK.metavarrr)rrFr^)dictr'rNtupler+rrQrgetrXr6rr7upperr add_argumentrSUPPRESS)_args_kwdsr_groupZ_idxrZ_issub)rr;parserrBr r _process_argparse_defs,      z=ConfigBase.initialize_argparse.._process_argparse_defr)argparse_is_availabler_data_collectorr\)rBrrlevelr9r:rrr )rr;rrBr initialize_argparses  zConfigBase.initialize_argparsec Csx|ddD]\}}}}|jdkr&qxj|jD]`\}}d|kr`|d}||kr||j|q.d|d}||kr.||j||j|=q.WqW|S)Nrrz CONFIGBLOCK.T)rr\r|__dict__r) rBZ parsed_argsrr9r:r;rrZ_destr r r import_argparse4s zConfigBase.import_argparserc Cs|tjkrtd|tjfg}|dkr.tj}x|dd|D]v\}}}} |dkrZ| jsZq>t||| } d||| dg||d<x.t|D]"\} } | dk r| | d|| <qWq>WdS)Nz0unknown content filter '%s'; valid values are %srruserdata r$) rcontent_filtersrrOstdoutrrUr=r+write) rBZcontent_filterindent_spacingZostreamraZ_blockslvlr9r:r;r<r/rr r r displayDs   zConfigBase.displayNc s>d}d}t|dd|}i}xj|D]b\}} } } t| | | } ||krVgdddd||<||d| ddt| t| jp~dfq$Wxt|D]}||} | t||tfd d ||dD| t|7t fd d ||dD}t||d|dkr>tfd d ||dD}n}||d <|||d<|||d<qWd}d}d}xpt|D]d}||d }||d|}||kr||kr|}nP||krP||kr|}|}qWt }|jr| | |jdx"|D]\}} } } t| | | } | jsX| d||| dq||krp|t|}n||d t|}| d||t| |dkr| d|||| n| | dd|| |t|t||}| dd||t| j|dd| dqW|S)Nz # rr)dataofflineoverr:rc3s$|]\}}}|kr|n|VqdS)Nr )r!rcrnr-)_okr r r#isz4ConfigBase.generate_yaml_template..c3s$|]\}}}|krdVqdS)rNr )r!rcrnr-)offsetwidthr r r#lsc3s(|] \}}}|kr|VqdS)Nr )r!rcrnr-)rrr r r#osrrrr$rz%%-%dsz )subsequent_indent)listrr>ryfindrrYsortedmaxsumrjStringIOrr(r,rgetvalue)rBrrraZ minDocWidthcommentrZ level_inforprernr;r<indentrrZmaxLvlZmaxDocpadZ_padrZosrRZtxtArear )rrrr generate_yaml_templateZsz  (           z!ConfigBase.generate_yaml_template<\begin{description}[topsep=0pt,parsep=0.5em,itemsep=-0.4em] \end{description} \item[{%s}]\hfill \\%src Cst} g} |} d} x|dd|dD]\} }}}t| | krxt| | dkr`| dqDW| | d|kr| | || n|r| | || d|7} xdt| | kr| }|dk r| dd|} d|kr| | ||q|r| | |qW|} d|kr6| | ||n|rJ| | ||jp\|j p\d}|rd|k}d|kr||f}n |r|}|rt ||| d|| d|d}n|f}| d ||d d s| d d|kr| | ||q(|r(| | |q(Wx^| rz| }|dk r| dd|} d|krh| | ||n| | |qW| S) NrrTz%srz )initial_indentrr$)rjrrrryrrr'rZrYr writelinesr,r8r)rBZ block_startZ block_endZ item_startZ item_bodyZitem_endrrrarrZlastObjrrrrnr;_lastrZZ _wrapLinesZ doc_linesr r r generate_documentationsp "           z!ConfigBase.generate_documentationccs:|jr |Vx(|ddD]\}}}}|jr|VqWdS)Nrr)rUr)rBrr9r:r;r r r user_valuess zConfigBase.user_valuesccsF|jr|js|Vx.|ddD]\}}}}|jr |js |Vq WdS)Nrr)rUrVr)rBrr9r:r;r r r unused_user_valuess   zConfigBase.unused_user_values)NNNNr)F)NrNN)rrr)rrrrrrrr)r7rHrIrhrlr]rCrfrorGrrrrr{rrrrrrrr __classcell__r r )r6r rSsD   A %B  H @rSc@s4eZdZdZddZd ddZddZdd d Zd S)raStore and manipulate a single configuration value. Parameters ---------- default: optional The default value that this ConfigValue will take if no value is provided. domain: callable, optional The domain can be any callable that accepts a candidate value and returns the value converted to the desired type, optionally performing any data validation. The result will be stored into the ConfigValue. Examples include type constructors like `int` or `float`. More complex domain examples include callable objects; for example, the :py:class:`In` class that ensures that the value falls into an acceptable set or even a complete :py:class:`ConfigDict` instance. description: str, optional The short description of this value doc: str, optional The long documentation string for this value visibility: int, optional The visibility of this ConfigValue when generating templates and documentation. Visibility supports specification of "advanced" or "developer" options. ConfigValues with visibility=0 (the default) will always be printed / included. ConfigValues with higher visibility values will only be included when the generation method specifies a visibility greater than or equal to the visibility of this object. cOstj|f|||dS)N)rSrCr{)rBr rr r r rCszConfigValue.__init__TcCs|r d|_|jS)NT)rVr3)rBrr r r r: szConfigValue.valuecCs|||_d|_dS)NT)rr3rU)rBr:r r r r|%s zConfigValue.set_valueNFccs(|dk r||jkrdS||||fVdS)N)r[)rBrr9radocModer r r r)szConfigValue._data_collector)T)NF)r7rHrI__doc__rCr:r|rr r r r rs " rcs(eZdZfddZfddZZS)ImmutableConfigValuecs4|||jkr tt|dtt||dS)Nz is currently immutable)rr3rQrrerr|)rBr:)r6r r r|0szImmutableConfigValue.set_valuecsXytt||jWn0t|jdr@tt||nYnXd|_d|_dS)NrGF)rerr|rWrrVrU)rB)r6r r r{5s zImmutableConfigValue.reset)r7rHrIr|r{rr r )r6r r/s rc@s eZdZdZddZddZdS) MarkImmutableag Mark instances of ConfigValue as immutable. Parameters ---------- config_value: ConfigValue The ConfigValue instances that should be marked immutable. Note that multiple instances of ConfigValue can be passed. Examples -------- >>> config = ConfigBlock() >>> config.declare('a', ConfigValue(default=1, domain=int)) >>> config.declare('b', ConfigValue(default=1, domain=int)) >>> locker = MarkImmutable(config.get('a'), config.get('b')) Now, config.a and config.b cannot be changed. To make them mutable again, >>> locker.release_lock() cGs\t|_y8x2|D]*}t|tk r(tdt|_|j|qWWn|YnXdS)Nz3Only ConfigValue instances can be marked immutable.) r_lockedrrrrr6ry release_lock)rBr rFr r r rCVs  zMarkImmutable.__init__cCs"x|jD] }t|_qWt|_dS)N)rrr6r)rBrFr r r rbs  zMarkImmutable.release_lockN)r7rHrIrrCrr r r r rAs rcseZdZdZddZfddZddZejfdd Z d d Z d d Z ddZ dddZ ddZddZejfddZejfddZd ddZZS)!ramStore and manipulate a list of configuration values. Parameters ---------- default: optional The default value that this ConfigList will take if no value is provided. If default is a list or ConfigList, then each member is cast to the ConfigList's domain to build the default value, otherwise the default is cast to the domain and forms a default list with a single element. domain: callable, optional The domain can be any callable that accepts a candidate value and returns the value converted to the desired type, optionally performing any data validation. The result will be stored / added to the ConfigList. Examples include type constructors like `int` or `float`. More complex domain examples include callable objects; for example, the :py:class:`In` class that ensures that the value falls into an acceptable set or even a complete :py:class:`ConfigDict` instance. description: str, optional The short description of this list doc: str, optional The long documentation string for this list visibility: int, optional The visibility of this ConfigList when generating templates and documentation. Visibility supports specification of "advanced" or "developer" options. ConfigLists with visibility=0 (the default) will always be printed / included. ConfigLists with higher visibility values will only be included when the generation method specifies a visibility greater than or equal to the visibility of this object. cOsPtj|f|||jdkr&t|_nt|jtr4ntd|jd|_|dS)N)r_)rSrCrXrrNr{)rBr rr r r rCs   zConfigList.__init__cs*tt||}x|jD] }||_qWdS)N)rerror3rT)rBrir )r6r r ros zConfigList.__setstate__cCs*|j|}d|_t|tr"|S|SdS)NT)r3rVrNrr:)rBrcrnr r r __getitem__s   zConfigList.__getitem__cCsPy|j|}d|_|SYnX|tjkr0dS|jdk rD||St|SdS)NT)r3rVrSr]rXr)rBrcr^rnr r r rs    zConfigList.getcCs|j||dS)N)r3r|)rBrcrnr r r __setitem__szConfigList.__setitem__cCs |jS)N)r3__len__)rBr r r rszConfigList.__len__cs&d_tfddttjDS)NTc3s|]}|VqdS)Nr )r!r/)rBr r r#sz&ConfigList.__iter__..)rViterrrr3)rBr )rBr __iter__szConfigList.__iter__Tcsr d|_fdd|jDS)NTcsg|]}|qSr )r:)r!config)rr r sz$ConfigList.value..)rVr3)rBrr )rr r:szConfigList.valuecCsj|j}g|_y>t|tks$t|tr>x"|D]}||q*Wn ||Wn||_YnXd|_dS)NT)r3rrrNrryrU)rBr:Z_oldrnr r r r|s   zConfigList.set_valuecCs&t|x|D] }d|_qWdS)NF)rSr{rrU)rBrnr r r r{s zConfigList.resetcCs`||}|dkrdS|j|||jd_dt|jdf|jd_d|jd_d|_dS)Nrz[%s]rT)rr3ryrTrrArU)rBr:rnr r r rys    zConfigList.appendcCstd||S)Nz?DEPRECATED: ConfigList.add() has been deprecated. Use append())ruwarningry)rBr:r r r rzszConfigList.addNFccs|dk r||jkrdS|r`||d|fV|j|dd||}t|x|D] }|VqNWdS|r|jsz||g|fVn||d|fV|dk r|d7}x.|jD]$}x||d||D] }|VqWqWdS)Nrz- )r[rXrrjnextr3)rBrr9rarZ subDomainrr:r r r rs&    zConfigList._data_collector)T)NF)r7rHrIrrCrorrSr]rrrrr:r|r{ryrzrrr r )r6r rhs%    rcs eZdZdZdZdZeejZd>ddZfd d Z fd d Z d dZ ej fddZ ej fddZddZddZddZddZddZddZfdd Zd!d"Zd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zd/d0Zd?d1d2Zd3d4Zd@d6d7Z dAd8d9Z!d:d;Z"dBdtt}|fddtjDt|d|d<|S)Nc3s|]}|t|fVqdS)N)r4)r!rc)rBr r r#Osz*ConfigDict.__getstate__..rt)rerrfrgrhrJ)rBri)r6)rBr rfMszConfigDict.__getstate__cs0tt||}xt|jD] }||_qWdS)N)rerrorj itervaluesr3rT)rBrir )r6r r roSszConfigDict.__setstate__cCs:d|_t|}t|j|tr,|j|S|j|SdS)NT)rVrrNr3rr:)rBrcr r r rXs zConfigDict.__getitem__cCsPd|_t|}||jkr"|j|S|tjkr0dS|jdk rD||St|SdS)NT)rVrr3rSr]rtr)rBrcr^r r r r`s     zConfigDict.getcCsHd|_t|}||jkr"|j|S|tjkr8||dS|||SdS)NT)rVrr3rSr]rz)rBrcr^r r r setdefaultls    zConfigDict.setdefaultcCs4t|}||jkr |||n|j||dS)N)rr3rzr|)rBrcrnr r r rvs zConfigDict.__setitem__cCs$|j|=|j||j|dS)N)r3rwremoverxdiscard)rBrcr r r __delitem__~s zConfigDict.__delitem__cCst|}||jkS)N)rr3)rBrcr r r __contains__szConfigDict.__contains__cCs |jS)N)rwr)rBr r r rszConfigDict.__len__cCs |jS)N)rwr)rBr r r rszConfigDict.__iter__cCs<||jkr0|dd}||jkr,td||}t||S)NrrzUnknown attribute '%s')r3replacerrr)rBrrAr r r __getattr__s     zConfigDict.__getattr__csF|tjkrtt|||n$||jkr4|dd}t|||dS)Nrr)r _all_slotsrermr3rr)rBrr:)r6r r rms    zConfigDict.__setattr__cCs |jS)N)rwr)rBr r r iterkeysszConfigDict.iterkeysccs$d|_x|jD]}||VqWdS)NT)rVrw)rBrcr r r rs zConfigDict.itervaluesccs(d|_x|jD]}|||fVqWdS)NT)rVrw)rBrcr r r rks zConfigDict.iteritemscCs t|S)N)rr)rBr r r keysszConfigDict.keyscCs t|S)N)rr)rBr r r valuesszConfigDict.valuescCs t|S)N)rrk)rBr r r itemsszConfigDict.itemscCst|}|jdk r2td||jd|df||jkrRtd||dfd|ksjd|ksjd|krtd||df||j|<|j|||_||_|S)NzMconfig '%s' is already assigned to Config Block '%s'; cannot reassign to '%s'Tz3duplicate config '%s' defined for Config Block '%s'rr]zNIllegal character in config '%s' for config Block '%s': '.[]' are not allowed.)rrTrrr3rwryrA)rBrrr r r _adds$    zConfigDict._addcCs|||}|j||S)N)rrxrz)rBrrr}r r r declares  zConfigDict.declarecCs`t|tstdxH|D]<}|r.||kr.q||krDtd|f|||j|qWdS)Nz8ConfigDict.declare_from() only accepts other ConfigDictszAConfigDict.declare_from passed a block with a duplicate field, %s)rNrrrrr3)rBotherskiprcr r r declare_froms   zConfigDict.declare_fromcCsl|jstd||df|jdkrPt|tr>|||}qb||t|}n||||}d|_|S)NzNKey '%s' not defined in Config Block '%s' and Block disallows implicit entriesT) rsrrrtrNrSrrrU)rBrrr}r r r rzs  zConfigDict.addTcs(r d|_tfddt|jDS)NTc3s |]\}}||fVqdS)N)r:)r!rr)rr r r#sz#ConfigDict.value..)rVrrjrkr3)rBrr )rr r:s zConfigDict.valuecCsP|dkr |St|tk r>t|ts>td|dt|jf|sF|Sg}i}xz|D]r}t|}||jkrt|||<qT| dd}||jkr||t|<qT|rqT|j r| |qTtd||dfqTW| d}yPx&|j D]}||kr|||||<qWx"t|D]}||||qWWn |||YnXd|_|S)Nz.Expected dict value for %s.set_value, found %sTrrzXkey '%s' not defined for Config Block '%s' and implicit (undefined) keys are not allowedF)rrrNrrrr7rr3rrsryr:rwrrzr{r|rU)rBr:Z skip_implicitZ _implicitZ _decl_maprc_keyZ _old_datar r r r|sJ           zConfigDict.set_valuecs8ddfddjDjdd<d_d_dS)NcSs*||jk}|r|j|n|j|=|S)N)rxr3r{)rBrcZkeepr r r _keep3s  zConfigDict.reset.._keepcsg|]}|r|qSr r )r!r )rrBr r r;sz$ConfigDict.reset..F)rwrVrU)rBr )rrBr r{0s zConfigDict.resetccsv|dk r||jkrdS|r8||d|fV|dk r8|d7}x8|jD].}x(|j|||d||D] }|Vq`Wq@WdS)Nrz: )r[rwr3r)rBrr9rarrcrr r r r?s  zConfigDict._data_collector)NNFNr)N)T)F)NF)%r7rHrIrrrhrSrrCrfrorr]rrrrrrrrrmrrrkrrrrrrrzr:r|r{rrr r )r6r rsF         7rc@s"eZdZdZdddZddZdS)InaA ConfigValue domain validator that checks values against a set Instances of In map incoming values to the desired type (if domain is specified) and check that the resulting value is in the specified set. Examples -------- >>> c = ConfigValue(domain=In(['foo', 'bar', '0'], domain=str)) >>> c.set_value('foo') >>> c.display foo >>> c.set_value(3) ValueError: invalid value for configuration '': Failed casting 3 to Error: value 3 not in domain ['foo', 'bar'] >>> c.display foo >>> c.set_value(0) >>> c.display '0' NcCs||_||_dS)N) _allowablerX)rBZ allowabler_r r r rCpsz In.__init__cCs>|jdk r||}n|}||jkr(|Std||jfdS)Nzvalue %s not in domain %s)rXrr)rBr:rr r r rGts    z In.__call__)N)r7rHrIrrCrGr r r r rVs r)T)-rrrOtextwraprrDrLrjZ six.movesrr rrrbuiltinsr5 __builtin____all__ getLoggerrurcompilerr0r=r>rlr?rJrKrSrrrrrrPY3rrrrrkrrr r r r sZ        i7'07