跳转至

Config

Bases: NestedDict

Config is an extension of NestedDict.

The differences between Config and NestedDict lies in 3 aspects:

  1. Config has default_factory set to Config and convert_mapping set to True by default.
  2. Config has a frozen attribute, which can be toggled with freeze(lock) & defrost(unlock) or temporarily changed with locked & unlocked.
  3. Config has a ConfigParser built-in, and supports add_argument and parse.

Config also features a post method and a boot method to support lazy-initilisation. This is useful when you want to perform some post-processing on the config. For example, some values may be a combination of other values, and you may define them in post.

boot is introduced to call all post methods in the nested structure of Config object. By default, boot will be called to after Config is parsed.

You could also manually call boot if you you don’t parse command-line arguments.

Notes

Since Config has default_factory set to Config, accessing anything that does not exist will create a new empty Config sub-attribute.

A frozen Config does not have this behavior and will raises KeyError when accessing anything that does not exist.

It is recommended to call config.freeze() or config.to(NestedDict) to avoid this behavior.

Attributes:

Name Type Description
parser ConfigParser

Parser for command-line arguments.

frozen bool

If True, the config is frozen and cannot be altered.

Examples:

Python Console Session
>>> c = Config(**{"f.n": "chang"})
>>> c.i.d = 1013
>>> c.i.d
1013
>>> c.d.i
Config(<class 'chanfig.config.Config'>, )
>>> c.freeze().dict()
{'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}
>>> c.d.i = 1013
Traceback (most recent call last):
ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
>>> c.d.e
Traceback (most recent call last):
AttributeError: 'Config' object has no attribute 'e'
>>> with c.unlocked():
...     del c.d
>>> c.dict()
{'f': {'n': 'chang'}, 'i': {'d': 1013}}
Source code in chanfig/config.py
Python
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
class Config(NestedDict):
    r"""
    `Config` is an extension of `NestedDict`.

    The differences between `Config` and `NestedDict` lies in 3 aspects:

    1. `Config` has `default_factory` set to `Config` and `convert_mapping` set to `True` by default.
    2. `Config` has a `frozen` attribute, which can be toggled with `freeze`(`lock`) & `defrost`(`unlock`)
        or temporarily changed with `locked` & `unlocked`.
    3. `Config` has a `ConfigParser` built-in, and supports `add_argument` and `parse`.

    Config also features a `post` method and a `boot` method to support lazy-initilisation.
    This is useful when you want to perform some post-processing on the config.
    For example, some values may be a combination of other values, and you may define them in `post`.

    `boot` is introduced to call all `post` methods in the nested structure of `Config` object.
    By default, `boot` will be called to after `Config` is parsed.

    You could also manually call `boot` if you you don't parse command-line arguments.

    Notes:
        Since `Config` has `default_factory` set to `Config`,
        accessing anything that does not exist will create a new empty Config sub-attribute.

        A **frozen** `Config` does not have this behavior and
        will raises `KeyError` when accessing anything that does not exist.

        It is recommended to call `config.freeze()` or `config.to(NestedDict)` to avoid this behavior.

    Attributes:
        parser (ConfigParser): Parser for command-line arguments.
        frozen (bool): If `True`, the config is frozen and cannot be altered.

    Examples:
        >>> c = Config(**{"f.n": "chang"})
        >>> c.i.d = 1013
        >>> c.i.d
        1013
        >>> c.d.i
        Config(<class 'chanfig.config.Config'>, )
        >>> c.freeze().dict()
        {'f': {'n': 'chang'}, 'i': {'d': 1013}, 'd': {'i': {}}}
        >>> c.d.i = 1013
        Traceback (most recent call last):
        ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
        >>> c.d.e
        Traceback (most recent call last):
        AttributeError: 'Config' object has no attribute 'e'
        >>> with c.unlocked():
        ...     del c.d
        >>> c.dict()
        {'f': {'n': 'chang'}, 'i': {'d': 1013}}
    """

    parser: ConfigParser
    frozen: bool = False

    def __init__(self, *args, default_factory: Optional[Callable] = None, **kwargs):
        if default_factory is None:
            default_factory = Config
        super().__init__(*args, default_factory=default_factory, **kwargs)
        self.setattr("parser", ConfigParser())

    def post(self) -> Config:
        r"""
        Post process of `Config`.

        Some `Config` may need to do some post process after `Config` is initialised.
        `post` is provided for this lazy-initialisation purpose.

        By default, `post` does nothing and returns `self`.

        Note that you should always call `boot` to apply `post` rather than calling `post` directly,
        as `boot` recursively call `post` on sub-configs.

        See Also: [`chanfig.Config.boot`][chanfig.Config.boot]

        Returns:
            self:

        Examples:
            >>> class PostConfig(Config):
            ...     def post(self):
            ...         if isinstance(self.data, str):
            ...             self.data = Config(feature=self.data, label=self.data)
            ...         return self
            >>> c = PostConfig(data="path")
            >>> c.post()
            PostConfig(<class 'chanfig.config.Config'>,
              ('data'): Config(<class 'chanfig.config.Config'>,
                ('feature'): 'path'
                ('label'): 'path'
              )
            )
        """

        return self

    def boot(self) -> Config:
        r"""
        Apply `post` recursively.

        Sub-config may have their own `post` method.
        `boot` is provided to apply `post` recursively.

        By default, `boot` is called after `Config` is parsed.
        If you don't need to parse command-line arguments, you should call `boot` manually.

        See Also: [`chanfig.Config.post`][chanfig.Config.post]

        Returns:
            self:

        Examples:
            >>> class DataConfig(Config):
            ...     def post(self):
            ...         if isinstance(self.path, str):
            ...             self.path = Config(feature=self.path, label=self.path)
            ...         return self
            >>> class BootConfig(Config):
            ...     def __init__(self, *args, **kwargs):
            ...         super().__init__(*args, **kwargs)
            ...         self.dataset = DataConfig(path="path")
            ...     def post(self):
            ...         if isinstance(self.id, str):
            ...             self.id += "_id"
            ...         return self
            >>> c = BootConfig(id="boot")
            >>> c.boot()
            BootConfig(<class 'chanfig.config.Config'>,
              ('id'): 'boot_id'
              ('dataset'): DataConfig(<class 'chanfig.config.Config'>,
                ('path'): Config(<class 'chanfig.config.Config'>,
                  ('feature'): 'path'
                  ('label'): 'path'
                )
              )
            )
        """

        for value in self.values():
            if isinstance(value, Config):
                value.boot()
        self.post()
        return self

    def parse(
        self,
        args: Optional[Iterable[str]] = None,
        default_config: Optional[str] = None,
        no_default_config_action: str = "raise",
    ) -> Config:
        r"""

        Parse command-line arguments with `ConfigParser`.

        This function internally calls `Config.post`.

        See Also: [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]

        Examples:
            >>> c = Config(a=0)
            >>> c.dict()
            {'a': 0}
            >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()
            {'a': 1, 'b': 2, 'c': 3}
        """

        if not self.hasattr("parser"):
            self.setattr("parser", ConfigParser())
        self.getattr("parser").parse(args, self, default_config, no_default_config_action)
        self.boot()
        return self

    parse_config = parse

    def add_argument(self, *args, **kwargs) -> None:
        r"""
        Add an argument to `ConfigParser`.

        Note that value defined in `Config` will override the default value defined in `add_argument`.

        Examples:
            >>> c = Config(a=0, c=1)
            >>> arg = c.add_argument("--a", type=int, default=1)
            >>> arg = c.add_argument("--b", type=int, default=2)
            >>> c.parse(['--c', '4']).dict()
            {'a': 1, 'c': 4, 'b': 2}
        """

        if not self.hasattr("parser"):
            self.setattr("parser", ConfigParser())
        return self.getattr("parser").add_argument(*args, **kwargs)

    def freeze(self, recursive: bool = True) -> Config:
        r"""
        Freeze `Config`.

        Args:
            recursive:

        **Alias**:

        + `lock`

        Examples:
            >>> c = Config(**{'i.d': 1013})
            >>> c.getattr('frozen')
            False
            >>> c.freeze(recursive=False).dict()
            {'i': {'d': 1013}}
            >>> c.getattr('frozen')
            True
            >>> c.i.getattr('frozen')
            False
            >>> c.lock().dict()  # alias
            {'i': {'d': 1013}}
            >>> c.i.getattr('frozen')
            True
        """

        @wraps(self.freeze)
        def freeze(config: Config) -> None:
            if isinstance(config, Config):
                config.setattr("frozen", True)

        if recursive:
            self.apply_(freeze)
        else:
            freeze(self)
        return self

    def lock(self, recursive: bool = True) -> Config:
        r"""
        Alias of [`freeze`][chanfig.Config.freeze].
        """
        return self.freeze(recursive=recursive)

    @contextmanager
    def locked(self):
        """
        Context manager which temporarily locks `Config`.

        Examples:
            >>> c = Config()
            >>> with c.locked():
            ...     c['i.d'] = 1013
            Traceback (most recent call last):
            ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
            >>> c.i.d = 1013
            >>> c.dict()
            {'i': {'d': 1013}}
        """

        was_frozen = self.getattr("frozen", False)
        try:
            self.freeze()
            yield self
        finally:
            if not was_frozen:
                self.defrost()

    def defrost(self, recursive: bool = True) -> Config:
        r"""
        Defrost `Config`.

        Args:
            recursive:

        **Alias**:

        + `unlock`

        Examples:
            >>> c = Config(**{'i.d': 1013})
            >>> c.getattr('frozen')
            False
            >>> c.freeze().dict()
            {'i': {'d': 1013}}
            >>> c.getattr('frozen')
            True
            >>> c.defrost(recursive=False).dict()
            {'i': {'d': 1013}}
            >>> c.getattr('frozen')
            False
            >>> c.i.getattr('frozen')
            True
            >>> c.unlock().dict()  # alias
            {'i': {'d': 1013}}
            >>> c.i.getattr('frozen')
            False
        """

        @wraps(self.defrost)
        def defrost(config: Config) -> None:
            if isinstance(config, Config):
                config.setattr("frozen", False)

        if recursive:
            self.apply_(defrost)
        else:
            defrost(self)
        return self

    def unlock(self, recursive: bool = True) -> Config:
        r"""
        Alias of [`defrost`][chanfig.Config.defrost].
        """
        return self.defrost(recursive=recursive)

    @contextmanager
    def unlocked(self):
        """
        Context manager which temporarily unlocks `Config`.

        Examples:
            >>> c = Config()
            >>> c.freeze().dict()
            {}
            >>> with c.unlocked():
            ...     c['i.d'] = 1013
            >>> c.defrost().dict()
            {'i': {'d': 1013}}
        """

        was_frozen = self.getattr("frozen", False)
        try:
            self.defrost()
            yield self
        finally:
            if was_frozen:
                self.freeze()

    def get(self, name: Any, default: Any = Null) -> Any:
        r"""
        Get value from `Config`.

        Note that `default` has higher priority than `default_factory`.

        Args:
            name:
            default:

        Returns:
            value:
                If `Config` does not contain `name`, return `default`.
                If `default` is not specified, return `default_factory()`.

        Raises:
            KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.

        Examples:
            >>> d = Config(**{"i.d": 1013})
            >>> d.get('i.d')
            1013
            >>> d['i.d']
            1013
            >>> d.i.d
            1013
            >>> d.get('f', 2)
            2
            >>> d.f
            Config(<class 'chanfig.config.Config'>, )
            >>> del d.f
            >>> d.freeze()
            Config(<class 'chanfig.config.Config'>,
              ('i'): Config(<class 'chanfig.config.Config'>,
                ('d'): 1013
              )
            )
            >>> d.f
            Traceback (most recent call last):
            AttributeError: 'Config' object has no attribute 'f'
            >>> d["f.n"]
            Traceback (most recent call last):
            KeyError: 'f.n'
        """

        if not self.hasattr("default_factory"):  # did not call super().__init__() in sub-class
            self.setattr("default_factory", Config)
        if name in self or not self.getattr("frozen", False):
            return super().get(name, default)
        raise KeyError(name)

    @frozen_check
    def set(
        self,
        name: Any,
        value: Any,
        convert_mapping: Optional[bool] = None,
    ) -> None:
        r"""
        Set value of `Config`.

        Args:
            name:
            value:

        Raises:
            ValueError: If `Config` is frozen.

        Examples:
            >>> c = Config()
            >>> c['i.d'] = 1013
            >>> c.i.d
            1013
            >>> c.freeze().dict()
            {'i': {'d': 1013}}
            >>> c['i.d'] = 1013
            Traceback (most recent call last):
            ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
            >>> c.defrost().dict()
            {'i': {'d': 1013}}
            >>> c['i.d'] = 1013
            >>> c.i.d
            1013
        """

        return super().set(name, value, convert_mapping)

    @frozen_check
    def delete(self, name: Any) -> None:
        r"""
        Delete value from `Config`.

        Args:
            name:

        Examples:
            >>> d = Config(**{"i.d": 1013, "f.n": "chang"})
            >>> d.i.d
            1013
            >>> d.f.n
            'chang'
            >>> d.delete('i.d')
            >>> "i.d" in d
            False
            >>> d.i.d
            Config(<class 'chanfig.config.Config'>, )
            >>> "i.d" in d
            True
            >>> del d.f.n
            >>> d.f.n
            Config(<class 'chanfig.config.Config'>, )
            >>> del d.c
            Traceback (most recent call last):
            AttributeError: 'Config' object has no attribute 'c'
        """

        super().delete(name)

    @frozen_check
    def pop(self, name: Any, default: Any = Null) -> Any:
        r"""
        Pop value from `Config`.

        Args:
            name:
            default:

        Returns:
            value: If `Config` does not contain `name`, return `default`.

        Examples:
            >>> c = Config()
            >>> c['i.d'] = 1013
            >>> c.pop('i.d')
            1013
            >>> c.pop('i.d', True)
            True
            >>> c.freeze().dict()
            {'i': {}}
            >>> c['i.d'] = 1013
            Traceback (most recent call last):
            ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
            >>> c.defrost().dict()
            {'i': {}}
            >>> c['i.d'] = 1013
            >>> c.pop('i.d')
            1013
        """

        return super().pop(name, default)

post()

Post process of Config.

Some Config may need to do some post process after Config is initialised. post is provided for this lazy-initialisation purpose.

By default, post does nothing and returns self.

Note that you should always call boot to apply post rather than calling post directly, as boot recursively call post on sub-configs.

See Also: chanfig.Config.boot

Returns:

Name Type Description
self Config

Examples:

Python Console Session
>>> class PostConfig(Config):
...     def post(self):
...         if isinstance(self.data, str):
...             self.data = Config(feature=self.data, label=self.data)
...         return self
>>> c = PostConfig(data="path")
>>> c.post()
PostConfig(<class 'chanfig.config.Config'>,
  ('data'): Config(<class 'chanfig.config.Config'>,
    ('feature'): 'path'
    ('label'): 'path'
  )
)
Source code in chanfig/config.py
Python
def post(self) -> Config:
    r"""
    Post process of `Config`.

    Some `Config` may need to do some post process after `Config` is initialised.
    `post` is provided for this lazy-initialisation purpose.

    By default, `post` does nothing and returns `self`.

    Note that you should always call `boot` to apply `post` rather than calling `post` directly,
    as `boot` recursively call `post` on sub-configs.

    See Also: [`chanfig.Config.boot`][chanfig.Config.boot]

    Returns:
        self:

    Examples:
        >>> class PostConfig(Config):
        ...     def post(self):
        ...         if isinstance(self.data, str):
        ...             self.data = Config(feature=self.data, label=self.data)
        ...         return self
        >>> c = PostConfig(data="path")
        >>> c.post()
        PostConfig(<class 'chanfig.config.Config'>,
          ('data'): Config(<class 'chanfig.config.Config'>,
            ('feature'): 'path'
            ('label'): 'path'
          )
        )
    """

    return self

boot()

Apply post recursively.

Sub-config may have their own post method. boot is provided to apply post recursively.

By default, boot is called after Config is parsed. If you don’t need to parse command-line arguments, you should call boot manually.

See Also: chanfig.Config.post

Returns:

Name Type Description
self Config

Examples:

Python Console Session
>>> class DataConfig(Config):
...     def post(self):
...         if isinstance(self.path, str):
...             self.path = Config(feature=self.path, label=self.path)
...         return self
>>> class BootConfig(Config):
...     def __init__(self, *args, **kwargs):
...         super().__init__(*args, **kwargs)
...         self.dataset = DataConfig(path="path")
...     def post(self):
...         if isinstance(self.id, str):
...             self.id += "_id"
...         return self
>>> c = BootConfig(id="boot")
>>> c.boot()
BootConfig(<class 'chanfig.config.Config'>,
  ('id'): 'boot_id'
  ('dataset'): DataConfig(<class 'chanfig.config.Config'>,
    ('path'): Config(<class 'chanfig.config.Config'>,
      ('feature'): 'path'
      ('label'): 'path'
    )
  )
)
Source code in chanfig/config.py
Python
def boot(self) -> Config:
    r"""
    Apply `post` recursively.

    Sub-config may have their own `post` method.
    `boot` is provided to apply `post` recursively.

    By default, `boot` is called after `Config` is parsed.
    If you don't need to parse command-line arguments, you should call `boot` manually.

    See Also: [`chanfig.Config.post`][chanfig.Config.post]

    Returns:
        self:

    Examples:
        >>> class DataConfig(Config):
        ...     def post(self):
        ...         if isinstance(self.path, str):
        ...             self.path = Config(feature=self.path, label=self.path)
        ...         return self
        >>> class BootConfig(Config):
        ...     def __init__(self, *args, **kwargs):
        ...         super().__init__(*args, **kwargs)
        ...         self.dataset = DataConfig(path="path")
        ...     def post(self):
        ...         if isinstance(self.id, str):
        ...             self.id += "_id"
        ...         return self
        >>> c = BootConfig(id="boot")
        >>> c.boot()
        BootConfig(<class 'chanfig.config.Config'>,
          ('id'): 'boot_id'
          ('dataset'): DataConfig(<class 'chanfig.config.Config'>,
            ('path'): Config(<class 'chanfig.config.Config'>,
              ('feature'): 'path'
              ('label'): 'path'
            )
          )
        )
    """

    for value in self.values():
        if isinstance(value, Config):
            value.boot()
    self.post()
    return self

parse(args=None, default_config=None, no_default_config_action='raise')

Parse command-line arguments with ConfigParser.

This function internally calls Config.post.

See Also: chanfig.ConfigParser.parse

Examples:

Python Console Session
>>> c = Config(a=0)
>>> c.dict()
{'a': 0}
>>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()
{'a': 1, 'b': 2, 'c': 3}
Source code in chanfig/config.py
Python
def parse(
    self,
    args: Optional[Iterable[str]] = None,
    default_config: Optional[str] = None,
    no_default_config_action: str = "raise",
) -> Config:
    r"""

    Parse command-line arguments with `ConfigParser`.

    This function internally calls `Config.post`.

    See Also: [`chanfig.ConfigParser.parse`][chanfig.ConfigParser.parse]

    Examples:
        >>> c = Config(a=0)
        >>> c.dict()
        {'a': 0}
        >>> c.parse(['--a', '1', '--b', '2', '--c', '3']).dict()
        {'a': 1, 'b': 2, 'c': 3}
    """

    if not self.hasattr("parser"):
        self.setattr("parser", ConfigParser())
    self.getattr("parser").parse(args, self, default_config, no_default_config_action)
    self.boot()
    return self

add_argument(*args, **kwargs)

Add an argument to ConfigParser.

Note that value defined in Config will override the default value defined in add_argument.

Examples:

Python Console Session
>>> c = Config(a=0, c=1)
>>> arg = c.add_argument("--a", type=int, default=1)
>>> arg = c.add_argument("--b", type=int, default=2)
>>> c.parse(['--c', '4']).dict()
{'a': 1, 'c': 4, 'b': 2}
Source code in chanfig/config.py
Python
def add_argument(self, *args, **kwargs) -> None:
    r"""
    Add an argument to `ConfigParser`.

    Note that value defined in `Config` will override the default value defined in `add_argument`.

    Examples:
        >>> c = Config(a=0, c=1)
        >>> arg = c.add_argument("--a", type=int, default=1)
        >>> arg = c.add_argument("--b", type=int, default=2)
        >>> c.parse(['--c', '4']).dict()
        {'a': 1, 'c': 4, 'b': 2}
    """

    if not self.hasattr("parser"):
        self.setattr("parser", ConfigParser())
    return self.getattr("parser").add_argument(*args, **kwargs)

freeze(recursive=True)

Freeze Config.

Parameters:

Name Type Description Default
recursive bool True

Alias:

  • lock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})
>>> c.getattr('frozen')
False
>>> c.freeze(recursive=False).dict()
{'i': {'d': 1013}}
>>> c.getattr('frozen')
True
>>> c.i.getattr('frozen')
False
>>> c.lock().dict()  # alias
{'i': {'d': 1013}}
>>> c.i.getattr('frozen')
True
Source code in chanfig/config.py
Python
def freeze(self, recursive: bool = True) -> Config:
    r"""
    Freeze `Config`.

    Args:
        recursive:

    **Alias**:

    + `lock`

    Examples:
        >>> c = Config(**{'i.d': 1013})
        >>> c.getattr('frozen')
        False
        >>> c.freeze(recursive=False).dict()
        {'i': {'d': 1013}}
        >>> c.getattr('frozen')
        True
        >>> c.i.getattr('frozen')
        False
        >>> c.lock().dict()  # alias
        {'i': {'d': 1013}}
        >>> c.i.getattr('frozen')
        True
    """

    @wraps(self.freeze)
    def freeze(config: Config) -> None:
        if isinstance(config, Config):
            config.setattr("frozen", True)

    if recursive:
        self.apply_(freeze)
    else:
        freeze(self)
    return self

lock(recursive=True)

Alias of freeze.

Source code in chanfig/config.py
Python
def lock(self, recursive: bool = True) -> Config:
    r"""
    Alias of [`freeze`][chanfig.Config.freeze].
    """
    return self.freeze(recursive=recursive)

locked()

Context manager which temporarily locks Config.

Examples:

Python Console Session
>>> c = Config()
>>> with c.locked():
...     c['i.d'] = 1013
Traceback (most recent call last):
ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
>>> c.i.d = 1013
>>> c.dict()
{'i': {'d': 1013}}
Source code in chanfig/config.py
Python
@contextmanager
def locked(self):
    """
    Context manager which temporarily locks `Config`.

    Examples:
        >>> c = Config()
        >>> with c.locked():
        ...     c['i.d'] = 1013
        Traceback (most recent call last):
        ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
        >>> c.i.d = 1013
        >>> c.dict()
        {'i': {'d': 1013}}
    """

    was_frozen = self.getattr("frozen", False)
    try:
        self.freeze()
        yield self
    finally:
        if not was_frozen:
            self.defrost()

defrost(recursive=True)

Defrost Config.

Parameters:

Name Type Description Default
recursive bool True

Alias:

  • unlock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})
>>> c.getattr('frozen')
False
>>> c.freeze().dict()
{'i': {'d': 1013}}
>>> c.getattr('frozen')
True
>>> c.defrost(recursive=False).dict()
{'i': {'d': 1013}}
>>> c.getattr('frozen')
False
>>> c.i.getattr('frozen')
True
>>> c.unlock().dict()  # alias
{'i': {'d': 1013}}
>>> c.i.getattr('frozen')
False
Source code in chanfig/config.py
Python
def defrost(self, recursive: bool = True) -> Config:
    r"""
    Defrost `Config`.

    Args:
        recursive:

    **Alias**:

    + `unlock`

    Examples:
        >>> c = Config(**{'i.d': 1013})
        >>> c.getattr('frozen')
        False
        >>> c.freeze().dict()
        {'i': {'d': 1013}}
        >>> c.getattr('frozen')
        True
        >>> c.defrost(recursive=False).dict()
        {'i': {'d': 1013}}
        >>> c.getattr('frozen')
        False
        >>> c.i.getattr('frozen')
        True
        >>> c.unlock().dict()  # alias
        {'i': {'d': 1013}}
        >>> c.i.getattr('frozen')
        False
    """

    @wraps(self.defrost)
    def defrost(config: Config) -> None:
        if isinstance(config, Config):
            config.setattr("frozen", False)

    if recursive:
        self.apply_(defrost)
    else:
        defrost(self)
    return self

unlock(recursive=True)

Alias of defrost.

Source code in chanfig/config.py
Python
def unlock(self, recursive: bool = True) -> Config:
    r"""
    Alias of [`defrost`][chanfig.Config.defrost].
    """
    return self.defrost(recursive=recursive)

unlocked()

Context manager which temporarily unlocks Config.

Examples:

Python Console Session
>>> c = Config()
>>> c.freeze().dict()
{}
>>> with c.unlocked():
...     c['i.d'] = 1013
>>> c.defrost().dict()
{'i': {'d': 1013}}
Source code in chanfig/config.py
Python
@contextmanager
def unlocked(self):
    """
    Context manager which temporarily unlocks `Config`.

    Examples:
        >>> c = Config()
        >>> c.freeze().dict()
        {}
        >>> with c.unlocked():
        ...     c['i.d'] = 1013
        >>> c.defrost().dict()
        {'i': {'d': 1013}}
    """

    was_frozen = self.getattr("frozen", False)
    try:
        self.defrost()
        yield self
    finally:
        if was_frozen:
            self.freeze()

get(name, default=Null)

Get value from Config.

Note that default has higher priority than default_factory.

Parameters:

Name Type Description Default
name Any required
default Any Null

Returns:

Name Type Description
value Any

If Config does not contain name, return default. If default is not specified, return default_factory().

Raises:

Type Description
KeyError

If Config does not contain name and default/default_factory is not specified.

Examples:

Python Console Session
>>> d = Config(**{"i.d": 1013})
>>> d.get('i.d')
1013
>>> d['i.d']
1013
>>> d.i.d
1013
>>> d.get('f', 2)
2
>>> d.f
Config(<class 'chanfig.config.Config'>, )
>>> del d.f
>>> d.freeze()
Config(<class 'chanfig.config.Config'>,
  ('i'): Config(<class 'chanfig.config.Config'>,
    ('d'): 1013
  )
)
>>> d.f
Traceback (most recent call last):
AttributeError: 'Config' object has no attribute 'f'
>>> d["f.n"]
Traceback (most recent call last):
KeyError: 'f.n'
Source code in chanfig/config.py
Python
def get(self, name: Any, default: Any = Null) -> Any:
    r"""
    Get value from `Config`.

    Note that `default` has higher priority than `default_factory`.

    Args:
        name:
        default:

    Returns:
        value:
            If `Config` does not contain `name`, return `default`.
            If `default` is not specified, return `default_factory()`.

    Raises:
        KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.

    Examples:
        >>> d = Config(**{"i.d": 1013})
        >>> d.get('i.d')
        1013
        >>> d['i.d']
        1013
        >>> d.i.d
        1013
        >>> d.get('f', 2)
        2
        >>> d.f
        Config(<class 'chanfig.config.Config'>, )
        >>> del d.f
        >>> d.freeze()
        Config(<class 'chanfig.config.Config'>,
          ('i'): Config(<class 'chanfig.config.Config'>,
            ('d'): 1013
          )
        )
        >>> d.f
        Traceback (most recent call last):
        AttributeError: 'Config' object has no attribute 'f'
        >>> d["f.n"]
        Traceback (most recent call last):
        KeyError: 'f.n'
    """

    if not self.hasattr("default_factory"):  # did not call super().__init__() in sub-class
        self.setattr("default_factory", Config)
    if name in self or not self.getattr("frozen", False):
        return super().get(name, default)
    raise KeyError(name)

set(name, value, convert_mapping=None)

Set value of Config.

Parameters:

Name Type Description Default
name Any required
value Any required

Raises:

Type Description
ValueError

If Config is frozen.

Examples:

Python Console Session
>>> c = Config()
>>> c['i.d'] = 1013
>>> c.i.d
1013
>>> c.freeze().dict()
{'i': {'d': 1013}}
>>> c['i.d'] = 1013
Traceback (most recent call last):
ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
>>> c.defrost().dict()
{'i': {'d': 1013}}
>>> c['i.d'] = 1013
>>> c.i.d
1013
Source code in chanfig/config.py
Python
@frozen_check
def set(
    self,
    name: Any,
    value: Any,
    convert_mapping: Optional[bool] = None,
) -> None:
    r"""
    Set value of `Config`.

    Args:
        name:
        value:

    Raises:
        ValueError: If `Config` is frozen.

    Examples:
        >>> c = Config()
        >>> c['i.d'] = 1013
        >>> c.i.d
        1013
        >>> c.freeze().dict()
        {'i': {'d': 1013}}
        >>> c['i.d'] = 1013
        Traceback (most recent call last):
        ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
        >>> c.defrost().dict()
        {'i': {'d': 1013}}
        >>> c['i.d'] = 1013
        >>> c.i.d
        1013
    """

    return super().set(name, value, convert_mapping)

delete(name)

Delete value from Config.

Parameters:

Name Type Description Default
name Any required

Examples:

Python Console Session
>>> d = Config(**{"i.d": 1013, "f.n": "chang"})
>>> d.i.d
1013
>>> d.f.n
'chang'
>>> d.delete('i.d')
>>> "i.d" in d
False
>>> d.i.d
Config(<class 'chanfig.config.Config'>, )
>>> "i.d" in d
True
>>> del d.f.n
>>> d.f.n
Config(<class 'chanfig.config.Config'>, )
>>> del d.c
Traceback (most recent call last):
AttributeError: 'Config' object has no attribute 'c'
Source code in chanfig/config.py
Python
@frozen_check
def delete(self, name: Any) -> None:
    r"""
    Delete value from `Config`.

    Args:
        name:

    Examples:
        >>> d = Config(**{"i.d": 1013, "f.n": "chang"})
        >>> d.i.d
        1013
        >>> d.f.n
        'chang'
        >>> d.delete('i.d')
        >>> "i.d" in d
        False
        >>> d.i.d
        Config(<class 'chanfig.config.Config'>, )
        >>> "i.d" in d
        True
        >>> del d.f.n
        >>> d.f.n
        Config(<class 'chanfig.config.Config'>, )
        >>> del d.c
        Traceback (most recent call last):
        AttributeError: 'Config' object has no attribute 'c'
    """

    super().delete(name)

pop(name, default=Null)

Pop value from Config.

Parameters:

Name Type Description Default
name Any required
default Any Null

Returns:

Name Type Description
value Any

If Config does not contain name, return default.

Examples:

Python Console Session
>>> c = Config()
>>> c['i.d'] = 1013
>>> c.pop('i.d')
1013
>>> c.pop('i.d', True)
True
>>> c.freeze().dict()
{'i': {}}
>>> c['i.d'] = 1013
Traceback (most recent call last):
ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
>>> c.defrost().dict()
{'i': {}}
>>> c['i.d'] = 1013
>>> c.pop('i.d')
1013
Source code in chanfig/config.py
Python
@frozen_check
def pop(self, name: Any, default: Any = Null) -> Any:
    r"""
    Pop value from `Config`.

    Args:
        name:
        default:

    Returns:
        value: If `Config` does not contain `name`, return `default`.

    Examples:
        >>> c = Config()
        >>> c['i.d'] = 1013
        >>> c.pop('i.d')
        1013
        >>> c.pop('i.d', True)
        True
        >>> c.freeze().dict()
        {'i': {}}
        >>> c['i.d'] = 1013
        Traceback (most recent call last):
        ValueError: Attempting to alter a frozen config. Run config.defrost() to defrost first.
        >>> c.defrost().dict()
        {'i': {}}
        >>> c['i.d'] = 1013
        >>> c.pop('i.d')
        1013
    """

    return super().pop(name, default)

最后更新: 2023-05-20 15:08:43