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: Create a "canonical" copy of the config, and validate it.
  
  This function is mainly used internally by NNI.
  
  Term explanation:
  The config schema for end users is more flexible than the format NNI manager accepts,
  so config classes have to deal with the conversion.
  Here we call the converted format "canonical".
  
  Returns
  -------
  type(self)
      A deep copy.
docstr_tok: ["Create","a","``","canonical","''","copy","of","the","config",",","and","validate","it",".","This","function","is","mainly","used","internally","by","NNI",".","Term","explanation",":","The","config","schema","for","end","users","is","more","flexible","than","the","format","NNI","manager","accepts",",","so","config","classes","have","to","deal","with","the","conversion",".","Here","we","call","the","converted","format","``","canonical","''",".","Returns","--","--","--","-","type","(","self",")","A","deep","copy","."]
code: def canonical_copy(self):
      """
      Create a "canonical" copy of the config, and validate it.
  
      This function is mainly used internally by NNI.
  
      Term explanation:
      The config schema for end users is more flexible than the format NNI manager accepts,
      so config classes have to deal with the conversion.
      Here we call the converted format "canonical".
  
      Returns
      -------
      type(self)
          A deep copy.
      """
      canon = copy.deepcopy(self)
      canon._canonicalize([])
      canon._validate_canonical()
      return canon
  
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 3 for calling the ConfigBase obj's underlying member method code with example usage: obj.canonical_copy() and returns: canon