import errno import json import os from pathlib import Path from typing import Union from httpie import __version__ from httpie.compat import is_windows DEFAULT_CONFIG_DIR = Path(os.environ.get( 'HTTPIE_CONFIG_DIR', os.path.expanduser('~/.httpie') if not is_windows else os.path.expandvars(r'%APPDATA%\\httpie') )) class BaseConfigDict(dict): name = None helpurl = None about = None def _get_path(self) -> Path: """Return the config file path without side-effects.""" raise NotImplementedError() def path(self) -> Path: """Return the config file path creating basedir, if needed.""" path = self._get_path() try: path.parent.mkdir(mode=0o700, parents=True) except OSError as e: if e.errno != errno.EEXIST: raise return path def is_new(self) -> bool: return not self._get_path().exists() def load(self): try: with self.path().open('rt') as f: try: data = json.load(f) except ValueError as e: raise ValueError( 'Invalid %s JSON: %s [%s]' % (type(self).__name__, str(e), self.path()) ) self.update(data) except IOError as e: if e.errno != errno.ENOENT: raise def save(self, fail_silently=False): self['__meta__'] = { 'httpie': __version__ } if self.helpurl: self['__meta__']['help'] = self.helpurl if self.about: self['__meta__']['about'] = self.about try: with self.path().open('w') as f: json.dump(self, f, indent=4, sort_keys=True, ensure_ascii=True) f.write('\n') except IOError: if not fail_silently: raise def delete(self): try: self.path().unlink() except OSError as e: if e.errno != errno.ENOENT: raise class Config(BaseConfigDict): name = 'config' helpurl = 'https://httpie.org/doc#config' about = 'HTTPie configuration file' DEFAULTS = { 'default_options': [] } def __init__(self, directory: Union[str, Path] = DEFAULT_CONFIG_DIR): super().__init__() self.update(self.DEFAULTS) self.directory = Path(directory) def _get_path(self) -> Path: return self.directory / (self.name + '.json') @property def default_options(self) -> list: return self['default_options']