fat_config 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41cf78034335f9fd05433c7058f26fff973575eb0b6becf4f501a15027905c9c
4
- data.tar.gz: 6987287fa5d625c0c97c1427add4d7777dea525100d220b8f28241720b85b340
3
+ metadata.gz: 3d987b854bd4105f55ead3efbc73a77a1e168b4c5bcfc6caa487e248463df95e
4
+ data.tar.gz: 8d65e3589a6402eb4f4fa374a815ffad56902162d4f0e107c0ef6f676ed39252
5
5
  SHA512:
6
- metadata.gz: b55c2816b7c7402a751242af54e6e6cf54c4b67965e1b276f743c8a4ae991c6e7752c62ca5ff27a69e64eab2b2eb6bb1c8573c4ec3e3a57d5b4eae072e2813aa
7
- data.tar.gz: 5d8c20611e05bd4af1745fa00dd1e83295c066fa53d40d3e955df032aa71cf144c5331298f42740d0e26b06f94e5a2a2838a875614a1b8685405d427f269023c
6
+ metadata.gz: f19a80712d34593aa39ebf5f5763a48aa5cce085b721b90a6a591417da9ce878c466800938f482aa88b5c6dac4c2ebdfb2250c7d798e8a0a8cbf415867e8b353
7
+ data.tar.gz: 0c8575cf4ffc410eba8c6d14e194e802f0e8505f266e0e08e4a63c7905c2cd5c2c184c6114b870bcec8b86dc56758035a4a8c8b2c5d48fbb871e17423224e073
data/README.md CHANGED
@@ -1,26 +1,26 @@
1
- - [Introduction](#org1f0d9bd)
2
- - [Installation](#org3f8a5fd)
3
- - [Usage:](#org5c5fe0f)
4
- - [Following XDG Standards](#org7e6640a)
5
- - [Following Classic UNIX Standards](#orgbc610e8)
6
- - [Available Config File Styles](#orgacf5ac1)
7
- - [Hash Keys](#orgf87b1a5)
8
- - [Hash Values](#org451482b)
9
- - [YAML](#orgef8563a)
10
- - [TOML](#orgcf8804e)
11
- - [JSON](#org549e838)
12
- - [INI](#org12513cc)
13
- - [Creating a Reader](#org7a0f2a9)
14
- - [Calling the `read` method on a `Reader`](#org7e439f9)
1
+ - [Introduction](#org6ea1400)
2
+ - [Installation](#org0bfc9c4)
3
+ - [Usage:](#orgb03325f)
4
+ - [Following XDG Standards](#orgcc49e5b)
5
+ - [Following Classic UNIX Standards](#orgb094a10)
6
+ - [Available Config File Styles](#org77efd19)
7
+ - [Hash Keys](#org2489659)
8
+ - [Hash Values](#org3c327fd)
9
+ - [YAML](#org47246cf)
10
+ - [TOML](#orgcd934bb)
11
+ - [JSON](#orge3c0568)
12
+ - [INI](#org479cf72)
13
+ - [Creating a Reader](#org195ed64)
14
+ - [Calling the `read` method on a `Reader`](#org0c26613)
15
15
  - [Parsing Environment and Command Line Strings](#parsing-environment-and-command-line-strings)
16
- - [Development](#org1fdfa48)
17
- - [Contributing](#org4454a76)
18
- - [License](#org8a2eaa6)
16
+ - [Development](#orgadf9605)
17
+ - [Contributing](#org00f8770)
18
+ - [License](#orgbb61eac)
19
19
 
20
20
  [![img](https://github.com/ddoherty03/fat_config/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/ddoherty03/fat_config/actions/workflows/main.yml)
21
21
 
22
22
 
23
- <a id="org1f0d9bd"></a>
23
+ <a id="org6ea1400"></a>
24
24
 
25
25
  # Introduction
26
26
 
@@ -29,7 +29,7 @@ Allowing a user to configure an application to change its behavior at runtime ca
29
29
  `FatConfig` eliminates the tedium of reading configuration files and the environment to populate a Hash of configuration settings. You need only define a `FatConfig::Reader` and call its `#read` method to look for, read, translate, and merge any config files into a single Hash that encapsulates all the files in the proper priority. It can be set to read `YAML`, `TOML`, `JSON`, or `INI` config files.
30
30
 
31
31
 
32
- <a id="org3f8a5fd"></a>
32
+ <a id="org0bfc9c4"></a>
33
33
 
34
34
  # Installation
35
35
 
@@ -46,7 +46,7 @@ gem install fat_config
46
46
  ```
47
47
 
48
48
 
49
- <a id="org5c5fe0f"></a>
49
+ <a id="orgb03325f"></a>
50
50
 
51
51
  # Usage:
52
52
 
@@ -64,7 +64,7 @@ config = reader.read
64
64
  The `reader.read` method will parse the config files (by default assumed to be YAML files), config environment variable, and optional command-line parameters and return the composite config as a Hash.
65
65
 
66
66
 
67
- <a id="org7e6640a"></a>
67
+ <a id="orgcc49e5b"></a>
68
68
 
69
69
  ## Following XDG Standards
70
70
 
@@ -78,7 +78,7 @@ By default, `FatConfig::Reader#read` follows the [XDG Desktop Standards](https:/
78
78
  6. Finally, it will merge in any options given in the optional `command_line:` named parameter to the `#read` method. That parameter can either be a `Hash` or a `String`. If it is a `String`, it is interpreted the same way as the environment variable `MYAPP_OPTIONS` as explained below in [Parsing Environment and Command Line Strings](#parsing-environment-and-command-line-strings); if it is a `Hash`, it is used directly and merged into the hash returned from the prior methods.
79
79
 
80
80
 
81
- <a id="orgbc610e8"></a>
81
+ <a id="orgb094a10"></a>
82
82
 
83
83
  ## Following Classic UNIX Standards
84
84
 
@@ -101,7 +101,7 @@ With the optional `:xdg` keyword parameter to `FatConfig::Reader#read` set to `f
101
101
  6. Finally, it will merge in any options given in the optional `command_line:` named parameter to the `#read` method. That parameter can either be a `Hash` or a `String`. If it is a `String`, it will interpret the string as explained below in [Parsing Environment and Command Line Strings](#parsing-environment-and-command-line-strings); if it is a `Hash`, it is used directly and merged into the hash returned from the prior methods.
102
102
 
103
103
 
104
- <a id="orgacf5ac1"></a>
104
+ <a id="org77efd19"></a>
105
105
 
106
106
  ## Available Config File Styles
107
107
 
@@ -115,24 +115,28 @@ With the optional `:xdg` keyword parameter to `FatConfig::Reader#read` set to `f
115
115
  By default, the style is `yaml`. Note that the style only pertains to the syntax of on-disk configuration files. Configuration can also be set by an environment variable, `MYAPP_OPTIONS` and by a command-line string optionally provided to the `#read` method. Those are simple parsers that parse strings of option settings as explained below. See, [Parsing Environment and Command Line Strings](#parsing-environment-and-command-line-strings).
116
116
 
117
117
 
118
- <a id="orgf87b1a5"></a>
118
+ <a id="org2489659"></a>
119
119
 
120
120
  ## Hash Keys
121
121
 
122
- The returned Hash will have symbols as keys, using the names given in the config files, except that they will have any hyphens converted to the underscore. Thus the config setting "page-width: 6.5in" in a config file will result in a Hash entry of `{ page_width: '6.5in' }`.
122
+ Any keys that are Strings will be converted to a symbol, using the names given in the config files, except that they will have any hyphens converted to the underscore so they are suitable for use as a method call. Thus the config setting "page-width: 6.5in" in a config file will result in a Hash entry of `{ page_width: '6.5in' }`.
123
123
 
124
+ Keys that are not Strings will be left alone, so that, for example, you might have Integer keys, which may be useful below the top level.
124
125
 
125
- <a id="org451482b"></a>
126
+
127
+ <a id="org3c327fd"></a>
126
128
 
127
129
  ## Hash Values
128
130
 
129
- Whether the values of the returned Hash will be 'deserialized' into a Ruby object is controlled by the style of the configuration files. For example, the `:yaml` style deserializes the following types:
131
+ Whether the values of the returned Hash will be 'deserialized' into a Ruby object is controlled by the style of the configuration files.
130
132
 
131
133
 
132
- <a id="orgef8563a"></a>
134
+ <a id="org47246cf"></a>
133
135
 
134
136
  ### YAML
135
137
 
138
+ The `:yaml` style deserializes the following types:
139
+
136
140
  - TrueClass (the string 'true' of whatever case)
137
141
  - FalseClass (the string 'false' of whatever case)
138
142
  - NilClass (when no value given)
@@ -144,10 +148,12 @@ Whether the values of the returned Hash will be 'deserialized' into a Ruby objec
144
148
  - Date, DateTime, and Time, which FatConfig adds to the foregoing default types deserialized by the default YAML library.
145
149
 
146
150
 
147
- <a id="orgcf8804e"></a>
151
+ <a id="orgcd934bb"></a>
148
152
 
149
153
  ### TOML
150
154
 
155
+ The `:toml` style deserializes the following types:
156
+
151
157
  - TrueClass (exactly the string 'true')
152
158
  - FalseClass (exactly the string 'false')
153
159
  - Integer (when it looks like an whole number or 0x&#x2026; or 0o&#x2026; hex or octal)
@@ -158,10 +164,12 @@ Whether the values of the returned Hash will be 'deserialized' into a Ruby objec
158
164
  - Date and Time, when given in ISO form YYYY-MM-DD or YYYY-MM-DDThh:mm:ss
159
165
 
160
166
 
161
- <a id="org549e838"></a>
167
+ <a id="orge3c0568"></a>
162
168
 
163
169
  ### JSON
164
170
 
171
+ The `:json` style deserializes the following types:
172
+
165
173
  - TrueClass (exactly the string 'true')
166
174
  - FalseClass (exactly the string 'false')
167
175
  - Integer (when it looks like an decimal whole number, but NO provision hex or octal)
@@ -172,10 +180,12 @@ Whether the values of the returned Hash will be 'deserialized' into a Ruby objec
172
180
  - Date and Time, NOT deserialized, returns a parse error
173
181
 
174
182
 
175
- <a id="org12513cc"></a>
183
+ <a id="org479cf72"></a>
176
184
 
177
185
  ### INI
178
186
 
187
+ The `:ini` style deserializes the following types:
188
+
179
189
  - TrueClass (exactly the string 'true')
180
190
  - FalseClass (exactly the string 'false')
181
191
  - Integer (when it looks like an whole number or 0x&#x2026; or 0o&#x2026; hex or octal)
@@ -186,7 +196,7 @@ Whether the values of the returned Hash will be 'deserialized' into a Ruby objec
186
196
  - Date and Time, NOT deserialized, returned as a String
187
197
 
188
198
 
189
- <a id="org7a0f2a9"></a>
199
+ <a id="org195ed64"></a>
190
200
 
191
201
  ## Creating a Reader
192
202
 
@@ -203,7 +213,7 @@ reader3 = FatConfig.new('labrat', style: 'ini', xdg: false) # Use classic UNIX
203
213
  ```
204
214
 
205
215
 
206
- <a id="org7e439f9"></a>
216
+ <a id="org0c26613"></a>
207
217
 
208
218
  ## Calling the `read` method on a `Reader`
209
219
 
@@ -266,7 +276,7 @@ Here are the parsing rules:
266
276
  4. These rules apply regardless of style being used for config files.
267
277
 
268
278
 
269
- <a id="org1fdfa48"></a>
279
+ <a id="orgadf9605"></a>
270
280
 
271
281
  # Development
272
282
 
@@ -275,14 +285,14 @@ After checking out the repo, run \`bin/setup\` to install dependencies. Then, ru
275
285
  To install this gem onto your local machine, run \`bundle exec rake install\`. To release a new version, update the version number in \`version.rb\`, and then run \`bundle exec rake release\`, which will create a git tag for the version, push git commits and the created tag, and push the \`.gem\` file to [rubygems.org](https://rubygems.org).
276
286
 
277
287
 
278
- <a id="org4454a76"></a>
288
+ <a id="org00f8770"></a>
279
289
 
280
290
  # Contributing
281
291
 
282
292
  Bug reports and pull requests are welcome on GitHub at <https://github.com/ddoherty03/fat_config>.
283
293
 
284
294
 
285
- <a id="org8a2eaa6"></a>
295
+ <a id="orgbb61eac"></a>
286
296
 
287
297
  # License
288
298
 
@@ -1,5 +1,78 @@
1
- # frozen_string_literal: true
2
-
1
+ # in fat_config/parse_error.rb (or wherever ParseError lives)
3
2
  module FatConfig
4
- class ParseError < StandardError; end
3
+ class ParseError < StandardError
4
+ attr_reader :file, :format, :line, :column, :problem, :context, :snippet
5
+
6
+ def initialize(file:, format:, problem:, line: nil, column: nil, context: nil, snippet: nil)
7
+ @file = file
8
+ @format = format
9
+ @problem = problem
10
+ @line = line
11
+ @column = column
12
+ @context = context
13
+ @snippet = snippet
14
+ super(build_message)
15
+ end
16
+
17
+ def self.snippet_from_string(str, line:, column:)
18
+ snippet = nil
19
+
20
+ if str && line && line.to_i > 0
21
+ lines = str.lines
22
+ idx = line.to_i - 1
23
+ if idx >= 0 && idx < lines.length
24
+ src = lines[idx].chomp
25
+ caret =
26
+ if column
27
+ (" " * column.to_i) + "^"
28
+ end
29
+ snippet = [src, caret].compact.join("\n")
30
+ end
31
+ end
32
+
33
+ snippet
34
+ end
35
+
36
+ def self.snippet_from_file(file_name, line:, column:)
37
+ text = nil
38
+
39
+ begin
40
+ text = File.read(file_name, encoding: "UTF-8")
41
+ rescue StandardError
42
+ text = nil
43
+ end
44
+
45
+ snippet = nil
46
+ if text
47
+ snippet = snippet_from_string(text, line: line, column: column)
48
+ end
49
+
50
+ snippet
51
+ end
52
+
53
+ private
54
+
55
+ def build_message
56
+ loc =
57
+ if line && column
58
+ " at line #{line}, column #{column}"
59
+ elsif line
60
+ " at line #{line}"
61
+ else
62
+ ""
63
+ end
64
+
65
+ msg = +"#{format.to_s.upcase} parse error in #{file}#{loc}: #{problem}"
66
+
67
+ if snippet && !snippet.empty?
68
+ msg << "\n\n#{snippet}"
69
+ end
70
+
71
+ if context && !context.empty?
72
+ msg << "\n\n#{context}"
73
+ end
74
+
75
+ msg
76
+ end
77
+ end
5
78
  end
@@ -5,31 +5,80 @@ module FatConfig
5
5
  def load_string(str)
6
6
  # Since INIFile does not have a method for parsing strings, we have to
7
7
  # create a file with the string as content.
8
- tmp_path = Tempfile.create
9
- File.write(tmp_path, str)
10
- load_file(tmp_path)
8
+ tmp = Tempfile.create("fat_config_ini")
9
+ tmp.write(str)
10
+ tmp.flush
11
+ load_file(tmp.path)
11
12
  rescue IniFile::Error => ex
12
- raise FatConfig::ParseError, ex.to_s
13
+ snippet = snippet_from_inifile_error(ex)
14
+ raise(
15
+ ParseError.new(
16
+ file: "string",
17
+ format: :ini,
18
+ problem: ex.message,
19
+ line: nil,
20
+ column: nil,
21
+ context: nil,
22
+ snippet: snippet,
23
+ ),
24
+ cause: ex,
25
+ )
13
26
  end
14
27
 
15
28
  def load_file(file_name)
16
29
  ini = IniFile.load(file_name)
17
30
  config = {}
18
31
  ini.each_section do |sec|
19
- case ini[sec]
20
- when Hash
21
- config[sec.to_sym] = ini[sec].methodize
22
- else
23
- config[sec.to_sym] = ini[sec]
24
- end
32
+ config[sec.to_sym] =
33
+ case ini[sec]
34
+ when Hash
35
+ ini[sec].methodize
36
+ else
37
+ ini[sec]
38
+ end
25
39
  end
26
40
  config
27
41
  rescue IniFile::Error => ex
28
- raise FatConfig::ParseError, ex.to_s
42
+ snippet = snippet_from_inifile_error(ex)
43
+ raise(
44
+ ParseError.new(
45
+ file: file_name,
46
+ format: :ini,
47
+ problem: ex.message,
48
+ line: nil,
49
+ column: nil,
50
+ context: nil,
51
+ snippet: snippet,
52
+ ),
53
+ cause: ex,
54
+ )
29
55
  end
30
56
 
31
57
  def possible_extensions
32
58
  super + ['ini']
33
59
  end
60
+
61
+ private
62
+
63
+ # IniFile::Error messages commonly end with the offending line via inspect,
64
+ # e.g. 'expected "=": "bad line"'. We can at least surface that line.
65
+ def snippet_from_inifile_error(ex)
66
+ line_text = nil
67
+ if ex&.message
68
+ m = ex.message.match(/:\s*(\".*\"|\'.*\')\s*\z/)
69
+ if m
70
+ begin
71
+ line_text = eval(m[1])
72
+ rescue StandardError
73
+ line_text = m[1]
74
+ end
75
+ end
76
+ end
77
+ snippet = nil
78
+ if line_text && !line_text.empty?
79
+ snippet = +"#{line_text}\n^\n"
80
+ end
81
+ snippet
82
+ end
34
83
  end
35
84
  end
@@ -2,18 +2,58 @@
2
2
 
3
3
  module FatConfig
4
4
  class JSONStyle < Style
5
- def load_string(str)
6
- JSON.parse(str, symbolize_name: true).methodize
5
+ def load_string(str, file: 'string')
6
+ JSON.parse(str).methodize
7
7
  rescue JSON::ParserError => ex
8
- raise FatConfig::ParseError, ex.to_s
8
+ line, column = extract_line_column(ex.message)
9
+ snippet =
10
+ if line
11
+ ParseError.snippet_from_string(str, line: line, column: column)
12
+ end
13
+ raise(
14
+ ParseError.new(
15
+ file: file,
16
+ format: :json,
17
+ problem: ex.message,
18
+ line: line,
19
+ column: column,
20
+ context: nil,
21
+ snippet: snippet,
22
+ ),
23
+ cause: ex,
24
+ )
9
25
  end
10
26
 
11
27
  def load_file(file_name)
12
- load_string(File.read(file_name))
28
+ str = File.read(file_name, encoding: "UTF-8")
29
+ load_string(str, file: file_name)
13
30
  end
14
31
 
15
32
  def possible_extensions
16
33
  super + ['json']
17
34
  end
35
+
36
+ private
37
+
38
+ # Best-effort: Ruby JSON error messages commonly include
39
+ # "... at line 3, column 12"
40
+ def extract_line_column(message)
41
+ line = nil
42
+ column = nil
43
+
44
+ if message
45
+ m = message.match(/line\s+(\d+)\s+.*column\s+(\d+)/i)
46
+ if m
47
+ line = m[1].to_i
48
+ column = m[2].to_i
49
+ else
50
+ m2 = message.match(/line\s+(\d+)/i)
51
+ if m2
52
+ line = m2[1].to_i
53
+ end
54
+ end
55
+ end
56
+ [line, column]
57
+ end
18
58
  end
19
59
  end
@@ -2,18 +2,83 @@
2
2
 
3
3
  module FatConfig
4
4
  class TOMLStyle < Style
5
- def load_string(str)
6
- Tomlib.load(str)&.methodize
5
+ def load_string(str, file: "string")
6
+ data = Tomlib.load(str)
7
+ data ||= {}
8
+ data.methodize
7
9
  rescue Tomlib::ParseError => ex
8
- raise FatConfig::ParseError, ex.to_s
10
+ line, column, problem, context = extract_error(ex)
11
+ snippet =
12
+ if line
13
+ ParseError.snippet_from_string(str, line: line, column: column)
14
+ end
15
+ raise(
16
+ ParseError.new(
17
+ file: file,
18
+ format: :toml,
19
+ problem: problem || ex.message,
20
+ line: line,
21
+ column: column,
22
+ context: context,
23
+ snippet: snippet,
24
+ ),
25
+ cause: ex,
26
+ )
9
27
  end
10
28
 
11
29
  def load_file(file_name)
12
- load_string(File.read(file_name))
30
+ str = File.read(file_name, encoding: "UTF-8")
31
+ data = load_string(str, file: file_name)
32
+ data
13
33
  end
14
34
 
15
35
  def possible_extensions
16
36
  super + ['toml']
17
37
  end
38
+
39
+ private
40
+
41
+ def extract_error(ex)
42
+ line = nil
43
+ column = nil
44
+ context = nil
45
+
46
+ if ex.respond_to?(:line)
47
+ v = ex.line
48
+ line = v.to_i if v
49
+ elsif ex.respond_to?(:lineno)
50
+ v = ex.lineno
51
+ line = v.to_i if v
52
+ end
53
+
54
+ if ex.respond_to?(:column)
55
+ v = ex.column
56
+ column = v.to_i if v
57
+ elsif ex.respond_to?(:colno)
58
+ v = ex.colno
59
+ column = v.to_i if v
60
+ end
61
+
62
+ problem =
63
+ if ex.respond_to?(:problem)
64
+ ex.problem
65
+ else
66
+ ex.message
67
+ end
68
+
69
+ context = ex.context if ex.respond_to?(:context)
70
+
71
+ if (line.nil? || column.nil?) && ex.message
72
+ m = ex.message.match(/line\s+(\d+)\s*,?\s*(?:col(?:umn)?|column)\s+(\d+)/i)
73
+ if m
74
+ line ||= m[1].to_i
75
+ column ||= m[2].to_i
76
+ else
77
+ m2 = ex.message.match(/line\s+(\d+)/i)
78
+ line ||= m2[1].to_i if m2
79
+ end
80
+ end
81
+ [line, column, problem, context]
82
+ end
18
83
  end
19
84
  end
@@ -41,7 +41,19 @@ module FatConfig
41
41
  {}
42
42
  end
43
43
  rescue Psych::SyntaxError => ex
44
- raise FatConfig::ParseError, ex.to_s
44
+ snippet = ParseError.snippet_from_string(str, line: ex.line, column: ex.column)
45
+ raise(
46
+ ParseError.new(
47
+ file: "string",
48
+ format: :yaml,
49
+ line: ex.line,
50
+ column: ex.column,
51
+ problem: ex.problem,
52
+ context: ex.context,
53
+ snippet: snippet,
54
+ ),
55
+ cause: ex,
56
+ )
45
57
  end
46
58
 
47
59
  def load_file(file_name)
@@ -60,7 +72,19 @@ module FatConfig
60
72
  {}
61
73
  end
62
74
  rescue Psych::SyntaxError => ex
63
- raise FatConfig::ParseError, ex.to_s
75
+ snippet = ParseError.snippet_from_file(file_name, line: ex.line, column: ex.column)
76
+ raise(
77
+ ParseError.new(
78
+ file: file_name,
79
+ format: :yaml,
80
+ line: ex.line,
81
+ column: ex.column,
82
+ problem: ex.problem,
83
+ context: ex.context,
84
+ snippet: snippet,
85
+ ),
86
+ cause: ex,
87
+ )
64
88
  end
65
89
 
66
90
  def possible_extensions
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FatConfig
4
- VERSION = "0.6.1"
4
+ VERSION = "0.7.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fat_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel E. Doherty
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2026-02-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubygems_version: 4.0.0
126
+ rubygems_version: 3.6.2
127
127
  specification_version: 4
128
128
  summary: Library to read config from standard XDG or classic locations.
129
129
  test_files: []