docstr: Validate and read the docker-compose.yml
docstr_tok: ["Validate","and","read","the","docker-compose.yml"]
code: def _read_config(self, config_path, compose_bin):
      """
      Validate and read the docker-compose.yml
      """
      yaml = YAML()
      with config_path.open() as fp:
          self.raw_config = yaml.load(fp)
  
      services = self.raw_config["services"].keys()
      self.hierarchy = dict(
          flatten(self.raw_config.get("x-hierarchy", {}))
      )
      self.limit_presets = self.raw_config.get("x-limit-presets", {})
      self.with_gpus = self.raw_config.get("x-with-gpus", [])
      nodes = self.hierarchy.keys()
      errors = []
  
      for name in self.with_gpus:
          if name not in services:
              errors.append(
                  "Service `{}` defined in `x-with-gpus` bot not in "
                  "`services`".format(name)
              )
      for name in nodes - services:
          errors.append(
              "Service `{}` is defined in `x-hierarchy` bot not in "
              "`services`".format(name)
          )
      for name in services - nodes:
          errors.append(
              "Service `{}` is defined in `services` but not in "
              "`x-hierarchy`".format(name)
          )
  
      # trigger docker-compose's own validation
      compose = Command("docker-compose")
      args = ["--file", str(config_path), "config"]
      result = compose.run(
          *args,
          env=self.env,
          check=False,
          stderr=subprocess.PIPE,
          stdout=subprocess.PIPE,
      )
  
      if result.returncode != 0:
          # strip the intro line of docker-compose errors
          errors += result.stderr.decode().splitlines()
  
      if errors:
          msg = "\n".join([" - {}".format(msg) for msg in errors])
          raise ValueError(
              "Found errors with docker-compose:\n{}".format(msg)
          )
  
      rendered_config = StringIO(result.stdout.decode())
      self.path = config_path
      self.config = yaml.load(rendered_config)
  
imports: import os
  import shlex
  import subprocess
  from io import StringIO
  from dotenv import dotenv_values
  from ruamel.yaml import YAML
  from ..utils.command import Command, default_bin
  from ..utils.source import arrow_path
  from ..compat import _ensure_path
label_desc: Use image node_id 3 for calling the ComposeConfig obj's underlying member method code with example usage: obj._read_config(config_path, compose_bin) without return types