class_docstr: The abstract base class of experiment config classes.
  
  A config class should be a type-hinted dataclass inheriting ``ConfigBase``.
  Or for a training service config class, it can inherit ``TrainingServiceConfig``.
  
  .. code-block:: python
  
      @dataclass(init=False)
      class ExperimentConfig(ConfigBase):
          name: Optional[str]
          ...
  
  Subclasses are suggested to override ``_canonicalize()`` and ``_validate_canonical()`` methods.
  
  Users can create a config object with constructor or ``ConfigBase.load()``,
  validate its legality with ``ConfigBase.validate()``,
  and finally convert it to the format accepted by NNI manager with ``ConfigBase.json()``.
  
  Example usage:
  
  .. code-block:: python
  
      # when using Python API
      config1 = ExperimentConfig(trialCommand='...', trialConcurrency=1, ...)
      config1.validate()
      print(config1.json())
  
      # when using config file
      config2 = ExperimentConfig.load('examples/config.yml')
      config2.validate()
      print(config2.json())
  
  Config objects will remember where they are loaded; therefore relative paths can be resolved smartly.
  If a config object is created with constructor, the base path will be current working directory.
  If it is loaded with ``ConfigBase.load(path)``, the base path will be ``path``'s parent.
  
  .. attention::
  
      All the classes that inherit ``ConfigBase`` are not allowed to use ``from __future__ import annotations``,
      because ``ConfigBase`` uses ``typeguard`` to perform runtime check and it does not support lazy annotations.
class_docstr_tok: ["The","abstract","base","class","of","experiment","config","classes",".","A","config","class","should","be","a","type-hinted","dataclass","inheriting","``","ConfigBase","``",".","Or","for","a","training","service","config","class",",","it","can","inherit","``","TrainingServiceConfig","``",".","..","code-block",":",":","python","@","dataclass","(","init=False",")","class","ExperimentConfig","(","ConfigBase",")",":","name",":","Optional","[","str","]","...","Subclasses","are","suggested","to","override","``","_canonicalize","(",")","``","and","``","_validate_canonical","(",")","``","methods",".","Users","can","create","a","config","object","with","constructor","or","``","ConfigBase.load","(",")","``",",","validate","its","legality","with","``","ConfigBase.validate","(",")","``",",","and","finally","convert","it","to","the","format","accepted","by","NNI","manager","with","``","ConfigBase.json","(",")","``",".","Example","usage",":","..","code-block",":",":","python","#","when","using","Python","API","config1","=","ExperimentConfig","(","trialCommand=","'","...","'",",","trialConcurrency=1",",","...",")","config1.validate","(",")","print","(","config1.json","(",")",")","#","when","using","config","file","config2","=","ExperimentConfig.load","(","'examples\/config.yml","'",")","config2.validate","(",")","print","(","config2.json","(",")",")","Config","objects","will","remember","where","they","are","loaded",";","therefore","relative","paths","can","be","resolved","smartly",".","If","a","config","object","is","created","with","constructor",",","the","base","path","will","be","current","working","directory",".","If","it","is","loaded","with","``","ConfigBase.load","(","path",")","``",",","the","base","path","will","be","``","path","``","'s","parent",".","..","attention",":",":","All","the","classes","that","inherit","``","ConfigBase","``","are","not","allowed","to","use","``","from","__future__","import","annotations","``",",","because","``","ConfigBase","``","uses","``","typeguard","``","to","perform","runtime","check","and","it","does","not","support","lazy","annotations","."]
docstr: There are two common ways to use the constructor,
  directly writing kwargs and unpacking from JSON (YAML) object:
  
  .. code-block:: python
  
      config1 = AlgorithmConfig(name='TPE', class_args={'optimize_mode': 'maximize'})
  
      json = {'name': 'TPE', 'classArgs': {'optimize_mode': 'maximize'}}
      config2 = AlgorithmConfig(**json)
  
  If the config class has fields whose type is another config class, or list of another config class,
  they will recursively load dict values.
  
  Because JSON objects can use "camelCase" for field names,
  cases and underscores in ``kwargs`` keys are ignored in this constructor.
  For example if a config class has a field ``hello_world``,
  then using ``hello_world=1``, ``helloWorld=1``, and ``_HELLOWORLD_=1`` in constructor
  will all assign to the same field.
  
  If ``kwargs`` contain extra keys, `AttributeError` will be raised.
  
  If ``kwargs`` do not have enough key, missing fields are silently set to `MISSING()`.
  You can use ``utils.is_missing()`` to check them.
docstr_tok: ["There","are","two","common","ways","to","use","the","constructor",",","directly","writing","kwargs","and","unpacking","from","JSON","(","YAML",")","object",":","..","code-block",":",":","python","config1","=","AlgorithmConfig","(","name='TPE","'",",","class_args=","{","'optimize_mode","'",":","'maximize","'","}",")","json","=","{","'name","'",":","'TPE","'",",","'classArgs","'",":","{","'optimize_mode","'",":","'maximize","'","}","}","config2","=","AlgorithmConfig","(","*","*","json",")","If","the","config","class","has","fields","whose","type","is","another","config","class",",","or","list","of","another","config","class",",","they","will","recursively","load","dict","values",".","Because","JSON","objects","can","use","``","camelCase","''","for","field","names",",","cases","and","underscores","in","``","kwargs","``","keys","are","ignored","in","this","constructor",".","For","example","if","a","config","class","has","a","field","``","hello_world","``",",","then","using","``","hello_world=1","``",",","``","helloWorld=1","``",",","and","``","_HELLOWORLD_=1","``","in","constructor","will","all","assign","to","the","same","field",".","If","``","kwargs","``","contain","extra","keys",",","`","AttributeError","`","will","be","raised",".","If","``","kwargs","``","do","not","have","enough","key",",","missing","fields","are","silently","set","to","`","MISSING","(",")","`",".","You","can","use","``","utils.is_missing","(",")","``","to","check","them","."]
code: def __init__(self, **kwargs):
      """
      There are two common ways to use the constructor,
      directly writing kwargs and unpacking from JSON (YAML) object:
  
      .. code-block:: python
  
          config1 = AlgorithmConfig(name='TPE', class_args={'optimize_mode': 'maximize'})
  
          json = {'name': 'TPE', 'classArgs': {'optimize_mode': 'maximize'}}
          config2 = AlgorithmConfig(**json)
  
      If the config class has fields whose type is another config class, or list of another config class,
      they will recursively load dict values.
  
      Because JSON objects can use "camelCase" for field names,
      cases and underscores in ``kwargs`` keys are ignored in this constructor.
      For example if a config class has a field ``hello_world``,
      then using ``hello_world=1``, ``helloWorld=1``, and ``_HELLOWORLD_=1`` in constructor
      will all assign to the same field.
  
      If ``kwargs`` contain extra keys, `AttributeError` will be raised.
  
      If ``kwargs`` do not have enough key, missing fields are silently set to `MISSING()`.
      You can use ``utils.is_missing()`` to check them.
      """
      self._base_path = utils.get_base_path()
      args = {
          utils.case_insensitive(key): value
          for key, value in kwargs.items()
      }
      for field in utils.fields(self):
          value = args.pop(
              utils.case_insensitive(field.name), field.default
          )
          setattr(self, field.name, value)
      if args:  # maybe a key is misspelled
          class_name = type(self).__name__
          fields = ", ".join(args.keys())
          raise AttributeError(
              f"{class_name} does not have field(s) {fields}"
          )
  
      # try to unpack nested config
      for field in utils.fields(self):
          value = getattr(self, field.name)
          if utils.is_instance(value, field.type):
              continue  # already accepted by subclass, don't touch it
          if isinstance(value, dict):
              config = utils.guess_config_type(value, field.type)
              if config is not None:
                  setattr(self, field.name, config)
          elif (
              isinstance(value, list)
              and value
              and isinstance(value[0], dict)
          ):
              configs = utils.guess_list_config_type(value, field.type)
              if configs:
                  setattr(self, field.name, configs)
  
file_path: nni/nni/experiment/config/base.py
imports: import copy
  import dataclasses
  from pathlib import Path
  import yaml
  from .None import utils
label_desc: Use image node_id 1 to create a new ConfigBase object  with example: obj = ConfigBase()