# Path: valohai_cli/api.py
# def request(self, method, url, **kwargs) -> Response:  # type: ignore
#     api_error_class = kwargs.pop('api_error_class', APIError)
#     handle_errors = bool(kwargs.pop('handle_errors', True))
#     try:
#         resp = super().request(method, url, **kwargs)
#     except requests.ConnectionError as ce:
#         host = urlparse(ce.request.url).netloc
#         if 'Connection refused' in str(ce):
#             raise CLIException(
#                 f'Unable to connect to {host} (connection refused); try again soon.'
#             ) from ce
#         raise
# 
#     if handle_errors and resp.status_code >= 400:
#         cls = (APINotFoundError if resp.status_code == 404 else api_error_class)
#         raise cls(resp)
#     return resp
# 
# Path: valohai_cli/exceptions.py
# class APIError(CLIException):
#     kind = 'API Error'
# 
#     def __init__(self, response: Response) -> None:
#         if '<!DOCTYPE html>' in response.text:
#             # Don't shower the user with a blob of HTML
#             text = 'Internal error'
#         else:
#             text = response.text
#         super().__init__(text)
#         self.response = response
#         self.request = response.request
# 
#     @property
#     def error_json(self) -> Union[None, dict, list]:
#         try:
#             error_json = self.response.json()
#             if isinstance(error_json, (dict, list)):
#                 return error_json
#         except Exception:
#             pass
#         return None
# 
#     @property
#     def code(self) -> Optional[Any]:
#         """
#         Attempt to retrieve a top-level error code from the response.
#         """
#         error_json = self.error_json
#         if isinstance(error_json, dict):
#             return error_json.get('code')
#         return None
# 
#     def format_message(self) -> str:
#         try:
#             error_json = self.error_json
#             if error_json:
#                 from .utils.error_fmt import format_error_data
#                 try:
#                     return format_error_data(error_json)
#                 except Exception:
#                     return pformat(error_json, indent=2)  # This should be more readable than raw JSON
#         except Exception:
#             pass
#         return super().format_message()
# 
# class InvalidConfig(CLIException):
#     pass
# 
# class NoExecution(CLIException):
#     color = 'yellow'
# 
# Path: valohai_cli/git.py
# def get_file_at_commit(directory: str, commit: str, path: str) -> bytes:
#     """
#     Get the contents of repository `path` at commit `commit` given the
#     Git working directory `directory`.
# 
#     :param directory: Git working directory.
#     :param commit: Commit ID
#     :param path: In-repository path
#     :return: File contents as bytes
#     """
#     args = ['git', 'show', f'{commit}:{path}']
#     return check_git_output(args, directory)


import io
import os
import valohai_yaml
from typing import List, Optional, TextIO, Union
from click import BadParameter
from valohai_yaml.objs.config import Config
from valohai_cli.api import request
from valohai_cli.exceptions import APIError, InvalidConfig, NoExecution
from valohai_cli.git import get_file_at_commit

    @property
    def name(self) -> str:
        return str(self.data['name'])

    def get_config(self, commit_identifier: Optional[str] = None) -> Config:
        """
        Get the `valohai_yaml.Config` object from the current working directory,
        or a given commit.

        :param commit_identifier: Hexadecimal commit identifier; optional.
        :return: valohai_yaml.Config
        """
        if not commit_identifier:  # Current working directory
            filename = self.get_config_filename()
            with open(filename) as infp:
                return self._parse_config(infp, filename)
        else:  # Arbitrary commit
            filename = f'{commit_identifier}:valohai.yaml'
            directory = self.directory
            config_bytes = get_file_at_commit(directory, commit_identifier, 'valohai.yaml')
            config_sio = io.StringIO(config_bytes.decode('utf-8'))
            return self._parse_config(config_sio, filename)

    def _parse_config(self, config_fp: TextIO, filename: str = '<config file>') -> Config:
        try:
            return valohai_yaml.parse(config_fp)
        except OSError as err:
            raise InvalidConfig(f'Could not read {filename}') from err
        except valohai_yaml.ValidationErrors as ves:

# Path: valohai_cli/api.py
# def request(self, method, url, **kwargs) -> Response:  # type: ignore
#     api_error_class = kwargs.pop('api_error_class', APIError)
#     handle_errors = bool(kwargs.pop('handle_errors', True))
#     try:
#         resp = super().request(method, url, **kwargs)
#     except requests.ConnectionError as ce:
#         host = urlparse(ce.request.url).netloc
#         if 'Connection refused' in str(ce):
#             raise CLIException(
#                 f'Unable to connect to {host} (connection refused); try again soon.'
#             ) from ce
#         raise
# 
#     if handle_errors and resp.status_code >= 400:
#         cls = (APINotFoundError if resp.status_code == 404 else api_error_class)
#         raise cls(resp)
#     return resp
# 
# Path: valohai_cli/exceptions.py
# class APIError(CLIException):
#     kind = 'API Error'
# 
#     def __init__(self, response: Response) -> None:
#         if '<!DOCTYPE html>' in response.text:
#             # Don't shower the user with a blob of HTML
#             text = 'Internal error'
#         else:
#             text = response.text
#         super().__init__(text)
#         self.response = response
#         self.request = response.request
# 
#     @property
#     def error_json(self) -> Union[None, dict, list]:
#         try:
#             error_json = self.response.json()
#             if isinstance(error_json, (dict, list)):
#                 return error_json
#         except Exception:
#             pass
#         return None
# 
#     @property
#     def code(self) -> Optional[Any]:
#         """
#         Attempt to retrieve a top-level error code from the response.
#         """
#         error_json = self.error_json
#         if isinstance(error_json, dict):
#             return error_json.get('code')
#         return None
# 
#     def format_message(self) -> str:
#         try:
#             error_json = self.error_json
#             if error_json:
#                 from .utils.error_fmt import format_error_data
#                 try:
#                     return format_error_data(error_json)
#                 except Exception:
#                     return pformat(error_json, indent=2)  # This should be more readable than raw JSON
#         except Exception:
#             pass
#         return super().format_message()
# 
# class InvalidConfig(CLIException):
#     pass
# 
# class NoExecution(CLIException):
#     color = 'yellow'
# 
# Path: valohai_cli/git.py
# def get_file_at_commit(directory: str, commit: str, path: str) -> bytes:
#     """
#     Get the contents of repository `path` at commit `commit` given the
#     Git working directory `directory`.
# 
#     :param directory: Git working directory.
#     :param commit: Commit ID
#     :param path: In-repository path
#     :return: File contents as bytes
#     """
#     args = ['git', 'show', f'{commit}:{path}']
#     return check_git_output(args, directory)


# Path: valohai_cli/models/project.py
import io
import os
import valohai_yaml
from typing import List, Optional, TextIO, Union
from click import BadParameter
from valohai_yaml.objs.config import Config
from valohai_cli.api import request
from valohai_cli.exceptions import APIError, InvalidConfig, NoExecution
from valohai_cli.git import get_file_at_commit


    @property
    def name(self) -> str:
        return str(self.data['name'])

    def get_config(self, commit_identifier: Optional[str] = None) -> Config:
        """
        Get the `valohai_yaml.Config` object from the current working directory,
        or a given commit.

        :param commit_identifier: Hexadecimal commit identifier; optional.
        :return: valohai_yaml.Config
        """
        if not commit_identifier:  # Current working directory
            filename = self.get_config_filename()
            with open(filename) as infp:
                return self._parse_config(infp, filename)
        else:  # Arbitrary commit
            filename = f'{commit_identifier}:valohai.yaml'
            directory = self.directory
            config_bytes = get_file_at_commit(directory, commit_identifier, 'valohai.yaml')
            config_sio = io.StringIO(config_bytes.decode('utf-8'))
            return self._parse_config(config_sio, filename)

    def _parse_config(self, config_fp: TextIO, filename: str = '<config file>') -> Config:
        try:
            return valohai_yaml.parse(config_fp)
        except OSError as err:
            raise InvalidConfig(f'Could not read {filename}') from err
        except valohai_yaml.ValidationErrors as ves:

            raise InvalidConfig('{filename} is invalid ({n} errors); see `vh lint`'.format(