Coverage for hookee/conf.py: 89.09%

# Coverage for hookee/conf.py : 89.09%

## 55 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1 import os

2

3 import click

4 import confuse

5

6 from hookee.exception import HookeeConfigError

7

8 __author__ = "Alex Laird"

9 __copyright__ = "Copyright 2020, Alex Laird"

10 __version__ = "2.0.0"

11

12 template = {

13 "port": int,

14 "subdomain": confuse.String(default=None),

15 "region": confuse.Choice(["us", "eu", "ap", "au", "sa", "jp", "in"], default=None),

16 "hostname": confuse.String(default=None),

17 "auth": confuse.String(default=None),

18 "host_header": confuse.String(default=None),

19 "response": confuse.String(default=None),

20 "content_type": confuse.String(default=None),

21 "request_script": confuse.Filename(default=None),

22 "response_script": confuse.Filename(default=None),

23 "auth_token": confuse.String(default=None),

24 "plugins_dir": confuse.Filename(),

25 "plugins": list,

26 "console_width": confuse.Integer(default=80),

27 "header_color": confuse.Integer(default="green"),

28 "default_color": confuse.Integer(default="white"),

29 "request_color": confuse.Integer(default="white"),

30}

31

32

33 class Config:

34 """

35 An object with accessor methods containing ``hookee``'s configuration. Default configuration can be

36 overridden by creating a custom config at ``~/.config/hookee/config.yaml`` (when setting config

37 values from the command line, this is where updated values are stored) which in turn can be overridden by

38 passing args to the CLI.

39

40 If instantiating for a custom integration, args that would otherwise have been passed to and validated by the CLI

41 (see ``hookee --help``) can instead be passed as ``kwargs`` here to ensure the same validation is done.

42 For example:

43

44 .. code-block:: python

45

46 from hookee.conf import Config

47

48 config = Config(subdomain="my_domain",

49 region="eu")

50

51 A callback function can also be passed instead of ``response`` and ``content-type`` (or needing to use

52 plugins) when integrating with ``hookee``'s APIs:

53

54 .. code-block:: python

55

56 from hookee.conf import Config

57

58 def response_callback(request, response):

59 response.data = "<Response>Ok</Response>"

60 response.headers["Content-Type"] = "application/xml"

61 return response

62

63 config = Config(response_callback=response_callback)

64

65 :var config_obj: The templated config object.

66 :vartype config_obj: confuse.core.Configuration

67 :var config_dir: The directory of the config being used.

68 :vartype config_dir: str

69 :var config_path: The full path to the config file being used.

70 :vartype config_path: str

71 :var config_data: The parsed and validated config data. Use :func:`get`, :func:`set`, and other accessors

72 to interact with the data.

73 :vartype config_data: confuse.templates.AttrDict

74 :var click_logging: ``True`` if ``click`` should be used for log output, which enables colors and formatting when

75 logging to a console, ``False`` if a logger should be used. If not passed, ``True`` if a :class:`click.Context`

76 is found to be active. Not persisted to the config file.

77 :vartype click_logging: bool

78 :var response_callback: The response callback function, if defined. Not persisted to the config file.

79 :vartype response_callback: types.FunctionType, optional

80 """

81

82 def __init__(self, click_logging=None, **kwargs):

83 try:

84 if click_logging is None:

85 click_logging = click.get_current_context(silent=True) is not None

86

87 self.response_callback = kwargs.pop("response_callback", None)

88

89 config = confuse.Configuration("hookee", __name__)

90 print(config.sources)

91 config.set_args(kwargs)

92

93 self.config_obj = config

94 self.config_dir = self.config_obj.config_dir()

95 self.config_path = os.path.join(self.config_dir, confuse.CONFIG_FILENAME)

96

97 self.config_data = config.get(template)

98

99 self.click_logging = click_logging

100

101 if self.config_data.get("response") and self.response_callback:

102 raise HookeeConfigError("Can't define both \"response\" and \"response_callback\".")

103 elif self.response_callback and not callable(self.response_callback):

104 raise HookeeConfigError("\"response_callback\" must be a function.")

105

106 plugins_dir = os.path.expanduser(self.config_data["plugins_dir"])

107 if not os.path.exists(plugins_dir):

108 os.makedirs(plugins_dir)

109 except confuse.NotFoundError as e:

110 raise HookeeConfigError("The config file is invalid: {}.".format(str(e)))

111 except (confuse.ConfigReadError, ValueError):

112 raise HookeeConfigError("The config file is not valid YAML.")

113

114 def get(self, key):

115 """

116 Get the config value for the given key of persisted data.

117

118 :param key: The key.

119 :type key: str

120 :return: The config value.

121 :rtype: object

122 """

123 return self.config_data[key]

124

125 def set(self, key, value):

126 """

127 Update the config key to the given value, persisting to ``config.yaml``.

128

129 :param key: The key.

130 :type key: str

131 :param value: The value to set.

132 :type key: object

133 """

134 if value != self.config_data[key]:

135 self._update_config_objects(key, value)

136

137 def append(self, key, value):

138 """

139 Update the config key by appending to the list the given value, persisting to ``config.yaml``.

140

141 :param key: The key.

142 :type key: str

143 :param value: The value to append.

144 :type value: object

145 """

146 list_item = list(self.config_data[key])

147

148 if value not in list_item:

149 list_item.append(value)

150 self._update_config_objects(key, list_item)

151

152 def remove(self, key, value):

153 """

154 Update the config key by removing from the list the given value from the list for the given key, persisting to

155 ``config.yaml``.

156

157 :param key: The key.

158 :type key: str

159 :param value: The value to remove.

160 :type value: object

161 """

162 list_item = list(self.config_data[key])

163

164 if value in list_item:

165 list_item.remove(value)

166 self._update_config_objects(key, list_item)

167

168 def _update_config_objects(self, key, value):

169 self.config_data[key] = value

170 self.config_obj[key] = value

171

172 self._write_config_objects_to_file()

173

174 def _write_config_objects_to_file(self):

175 with open(self.config_path, "w") as f:

176 f.write(self.config_obj.dump())