prefpicker 2.17.0__py3-none-any.whl

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.
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "description": "prefpicker template schema",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {
7
+ "pref": {
8
+ "type": "object",
9
+ "additionalProperties": false,
10
+ "patternProperties": {
11
+ "^.+$": {
12
+ "additionalProperties": false,
13
+ "type": "object",
14
+ "properties": {
15
+ "variants": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "patternProperties": {
19
+ "^.+$": {
20
+ "type": "array",
21
+ "minItems": 1,
22
+ "items": {
23
+ "type": ["boolean", "integer", "null", "string"]
24
+ }
25
+ }
26
+ },
27
+ "required": [
28
+ "default"
29
+ ]
30
+ },
31
+ "review_on_close": {
32
+ "type": "array",
33
+ "minItems": 1,
34
+ "items": {
35
+ "type": "integer"
36
+ }
37
+ }
38
+ },
39
+ "required": [
40
+ "variants"
41
+ ]
42
+ }
43
+ }
44
+ },
45
+ "variant": {
46
+ "type": "array",
47
+ "items": {
48
+ "type": "string"
49
+ }
50
+ }
51
+ },
52
+ "required": [
53
+ "pref",
54
+ "variant"
55
+ ]
56
+ }
@@ -0,0 +1,95 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ """main.py tests"""
5
+
6
+ from pytest import raises
7
+
8
+ from .main import main
9
+ from .prefpicker import PrefPicker
10
+
11
+
12
+ def test_main_01(tmp_path):
13
+ """test main()"""
14
+ prefs_js = tmp_path / "prefs.js"
15
+ yml = tmp_path / "test.yml"
16
+ yml.write_text(
17
+ """
18
+ variant: []
19
+ pref:
20
+ test.a:
21
+ variants:
22
+ default: [1]"""
23
+ )
24
+ assert main([str(yml), str(prefs_js), "--variant", "default"]) == 0
25
+ assert prefs_js.is_file()
26
+
27
+
28
+ def test_main_02(capsys):
29
+ """test main() with missing input"""
30
+ with raises(SystemExit):
31
+ main(["missing.yml", "prefs.js"])
32
+ assert "Cannot find input file 'missing.yml'" in capsys.readouterr()[1]
33
+
34
+
35
+ def test_main_03(tmp_path):
36
+ """test main() with built-in input"""
37
+ prefs_js = tmp_path / "prefs.js"
38
+ templates = tuple(x.name for x in PrefPicker.templates())
39
+ assert templates
40
+ assert main([templates[0], str(prefs_js)]) == 0
41
+ assert prefs_js.is_file()
42
+
43
+
44
+ def test_main_04(tmp_path):
45
+ """test main() with invalid variant"""
46
+ prefs_js = tmp_path / "prefs.js"
47
+ yml = tmp_path / "test.yml"
48
+ yml.write_text(
49
+ """
50
+ variant: []
51
+ pref:
52
+ test.a:
53
+ variants:
54
+ default: [1]"""
55
+ )
56
+ assert main([str(yml), str(prefs_js), "--variant", "x"]) == 1
57
+ assert not prefs_js.is_file()
58
+
59
+
60
+ def test_main_05(tmp_path):
61
+ """test main() with check results"""
62
+ prefs_js = tmp_path / "prefs.js"
63
+ yml = tmp_path / "test.yml"
64
+ yml.write_text(
65
+ """
66
+ variant: [extra]
67
+ pref:
68
+ test.a:
69
+ variants:
70
+ default: [1, 1]
71
+ extra: [1, 1]"""
72
+ )
73
+ assert main([str(yml), str(prefs_js), "--check", "--variant", "extra"]) == 0
74
+ assert prefs_js.is_file()
75
+
76
+
77
+ def test_main_06(capsys, tmp_path):
78
+ """test main() with invalid output path"""
79
+ yml = tmp_path / "test.yml"
80
+ yml.touch()
81
+ # output is a directory
82
+ with raises(SystemExit):
83
+ main([str(yml), str(tmp_path)])
84
+ assert "is a directory." in capsys.readouterr()[1]
85
+ # output to missing directory
86
+ with raises(SystemExit):
87
+ main([str(yml), str(tmp_path / "missing" / "prefs.js")])
88
+ assert "directory does not exist." in capsys.readouterr()[1]
89
+
90
+
91
+ def test_main_07(tmp_path):
92
+ """test main() with invalid input"""
93
+ yml = tmp_path / "test.yml"
94
+ yml.write_text("{test{")
95
+ assert main([str(yml), str(tmp_path / "prefs.js")]) == 1
@@ -0,0 +1,281 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ """prefpicker.py tests"""
5
+
6
+ from pytest import mark, raises
7
+
8
+ from .prefpicker import PrefPicker, SourceDataError
9
+
10
+
11
+ def test_prefpicker_01(tmp_path):
12
+ """test simple PrefPicker"""
13
+ yml = tmp_path / "test.yml"
14
+ yml.write_text(
15
+ """
16
+ variant: []
17
+ pref:
18
+ test.a:
19
+ variants:
20
+ default: [1]"""
21
+ )
22
+ picker = PrefPicker.load_template(yml)
23
+ assert len(picker.variants) == 1
24
+ assert "default" in picker.variants
25
+ assert len(picker.prefs) == 1
26
+ assert "test.a" in picker.prefs
27
+
28
+
29
+ @mark.parametrize(
30
+ "data, msg",
31
+ [
32
+ # invalid template
33
+ ([], "invalid template"),
34
+ # variant list missing
35
+ ({"pref": {"a.b": {"variants": {"default": [1]}}}}, "variant list is missing"),
36
+ # variant definition is invalid type
37
+ ({"variant": [{"bad": 1}]}, "variant definition must be a string"),
38
+ # variant is invalid type
39
+ ({"variant": ""}, "variant is not a list"),
40
+ # pref dict missing
41
+ ({"variant": []}, "pref group is missing"),
42
+ # pref is invalid type
43
+ ({"pref": [], "variant": []}, "pref is not a dict"),
44
+ # pref entry is invalid
45
+ (
46
+ {"pref": {"a.b": None}, "variant": []},
47
+ r"'a\.b' entry must contain a dict",
48
+ ),
49
+ # pref variants is invalid
50
+ (
51
+ {"pref": {"a.b": {"variants": None}}, "variant": []},
52
+ r"'a\.b' is missing 'variants' dict",
53
+ ),
54
+ # pref missing default variant
55
+ (
56
+ {"variant": [], "pref": {"test.a": {"variants": {}}}},
57
+ r"'test\.a' is missing 'default' variant",
58
+ ),
59
+ # template with undefined variant
60
+ (
61
+ {"variant": [], "pref": {"a.b": {"variants": {"default": [1], "x": [2]}}}},
62
+ r"'x' in 'a\.b' is not a defined variant",
63
+ ),
64
+ # template with unused variant
65
+ (
66
+ {"variant": ["unused"], "pref": {"a.b": {"variants": {"default": [1]}}}},
67
+ "Unused variants 'unused'",
68
+ ),
69
+ # pref with empty variant list
70
+ (
71
+ {"variant": [], "pref": {"a.b": {"variants": {"default": []}}}},
72
+ r"'default' in 'a\.b' is empty",
73
+ ),
74
+ # pref with invalid variant type
75
+ (
76
+ {"variant": [], "pref": {"a.b": {"variants": {"default": "x"}}}},
77
+ r"variant 'default' in 'a\.b' is not a list",
78
+ ),
79
+ # pref variant with invalid type
80
+ (
81
+ {"variant": [], "pref": {"a.b": {"variants": {"default": [1.11]}}}},
82
+ r"unsupported datatype 'float' \(a\.b\)",
83
+ ),
84
+ ],
85
+ )
86
+ def test_prefpicker_02(data, msg):
87
+ """test PrefPicker.verify_data()"""
88
+ with raises(SourceDataError, match=msg):
89
+ PrefPicker.verify_data(data)
90
+
91
+
92
+ def test_prefpicker_03():
93
+ """test PrefPicker.check_overwrites()"""
94
+ raw_data = {
95
+ "variant": ["fail", "safe"],
96
+ "pref": {
97
+ "test.a": {"variants": {"default": [1], "fail": [1, 2], "safe": [3]}},
98
+ "test.b": {"variants": {"default": [9], "safe": [None]}},
99
+ },
100
+ }
101
+ # use verify_data just to sanity check test data
102
+ PrefPicker.verify_data(raw_data)
103
+ ppick = PrefPicker()
104
+ ppick.variants = set(raw_data["variant"] + ["default"])
105
+ ppick.prefs = raw_data["pref"]
106
+ results = tuple(ppick.check_overwrites())
107
+ assert results
108
+ assert results[0][0] == "test.a"
109
+ assert results[0][1] == "fail"
110
+ assert results[0][2] == 1
111
+
112
+
113
+ def test_prefpicker_04():
114
+ """test PrefPicker.check_duplicates()"""
115
+ raw_data = {
116
+ "variant": ["fail", "safe"],
117
+ "pref": {
118
+ "test.a": {"variants": {"default": [1], "fail": [1, 2, 3, 1], "safe": [3]}},
119
+ "test.b": {"variants": {"default": [9], "safe": [None]}},
120
+ },
121
+ }
122
+ # use verify_data just to sanity check test data
123
+ PrefPicker.verify_data(raw_data)
124
+ ppick = PrefPicker()
125
+ ppick.variants = set(raw_data["variant"] + ["default"])
126
+ ppick.prefs = raw_data["pref"]
127
+ results = tuple(ppick.check_duplicates())
128
+ assert results
129
+ assert results[0][0] == "test.a"
130
+ assert results[0][1] == "fail"
131
+
132
+
133
+ def test_prefpicker_05():
134
+ """test PrefPicker.check_combinations()"""
135
+ raw_data = {
136
+ "variant": ["v1", "v2"],
137
+ "pref": {
138
+ "test.a": {"variants": {"default": [1], "v1": [1, 2, 3, 4], "v2": [3]}},
139
+ "test.b": {"variants": {"default": [2], "v1": [1, 2], "v2": [3]}},
140
+ "test.c": {"variants": {"default": [1, 3], "v2": [None]}},
141
+ },
142
+ }
143
+ # use verify_data just to sanity check test data
144
+ PrefPicker.verify_data(raw_data)
145
+ ppick = PrefPicker()
146
+ ppick.variants = set(raw_data["variant"] + ["default"])
147
+ ppick.prefs = raw_data["pref"]
148
+ results = tuple(ppick.check_combinations())
149
+ assert len(results) == 2
150
+ assert results[0][0] == "default"
151
+ assert results[0][1] == 2
152
+ assert results[1][0] == "v1"
153
+ assert results[1][1] == 16
154
+
155
+
156
+ def test_prefpicker_06(tmp_path):
157
+ """test simple PrefPicker.create_prefsjs()"""
158
+ ppick = PrefPicker()
159
+ prefs = tmp_path / "prefs.js"
160
+ ppick.create_prefsjs(prefs)
161
+ assert prefs.is_file()
162
+ # check only comments were written to the document
163
+ with prefs.open("r") as in_fp:
164
+ for line in in_fp:
165
+ assert line.startswith("//")
166
+ assert in_fp.tell() > 0
167
+
168
+
169
+ def test_prefpicker_07(tmp_path):
170
+ """test PrefPicker.create_prefsjs() with variants"""
171
+ raw_data = {
172
+ "variant": ["test", "skip"],
173
+ "pref": {
174
+ "test.a": {"variants": {"default": [0], "test": [1], "skip": [2]}},
175
+ "test.b": {"variants": {"default": [True]}},
176
+ },
177
+ }
178
+ PrefPicker.verify_data(raw_data)
179
+ ppick = PrefPicker()
180
+ ppick.variants = set(raw_data["variant"] + ["default"])
181
+ ppick.prefs = raw_data["pref"]
182
+ prefs = tmp_path / "prefs.js"
183
+ # test with 'default' variant
184
+ ppick.create_prefsjs(prefs)
185
+ assert prefs.is_file()
186
+ prefs_data = prefs.read_text()
187
+ assert "defined by variant 'default'" not in prefs_data
188
+ assert 'user_pref("test.a", 0);\n' in prefs_data
189
+ assert 'user_pref("test.b", true);\n' in prefs_data
190
+ # test with 'test' variant
191
+ ppick.create_prefsjs(prefs, variant="test")
192
+ assert prefs.is_file()
193
+ prefs_data = prefs.read_text()
194
+ assert 'user_pref("test.a", 1);\n' in prefs_data
195
+ assert 'user_pref("test.b", true);\n' in prefs_data
196
+ assert "// 'test.a' defined by variant 'test'" in prefs_data
197
+
198
+
199
+ def test_prefpicker_08(tmp_path):
200
+ """test PrefPicker.create_prefsjs() with different values"""
201
+ raw_data = {
202
+ "variant": [],
203
+ "pref": {
204
+ # type int
205
+ "test.a": {
206
+ "variants": {
207
+ "default": [0, 1],
208
+ }
209
+ },
210
+ # type None (skip)
211
+ "test.b": {
212
+ "variants": {
213
+ "default": [None],
214
+ }
215
+ },
216
+ # type string
217
+ "test.c": {
218
+ "variants": {
219
+ "default": ["test string"],
220
+ }
221
+ },
222
+ # type string (with quotes)
223
+ "test.d": {
224
+ "variants": {
225
+ "default": ["'test' \"string\""],
226
+ }
227
+ },
228
+ # type bool
229
+ "test.e": {
230
+ "variants": {
231
+ "default": [True, False],
232
+ }
233
+ },
234
+ # add None twice to trigger the 'skip' code path and write out comment
235
+ "test.f": {
236
+ "variants": {
237
+ "default": [None, None],
238
+ }
239
+ },
240
+ # mixed types
241
+ "test.g": {
242
+ "variants": {
243
+ "default": ["foo", True, 0],
244
+ }
245
+ },
246
+ },
247
+ }
248
+ PrefPicker.verify_data(raw_data)
249
+ ppick = PrefPicker()
250
+ ppick.variants = set(raw_data["variant"] + ["default"])
251
+ ppick.prefs = raw_data["pref"]
252
+ prefs = tmp_path / "prefs.js"
253
+ ppick.create_prefsjs(prefs)
254
+ assert prefs.is_file()
255
+ prefs_data = prefs.read_text()
256
+ assert 'user_pref("test.b",' not in prefs_data
257
+ assert "user_pref(\"test.c\", 'test string');" in prefs_data
258
+ # test with unsupported value datatype
259
+ raw_data = {"variant": [], "pref": {"boom.": {"variants": {"default": [1.01]}}}}
260
+ ppick.variants = set(raw_data["variant"] + ["default"])
261
+ ppick.prefs = raw_data["pref"]
262
+ with raises(SourceDataError, match="Unsupported datatype"):
263
+ ppick.create_prefsjs(prefs)
264
+
265
+
266
+ def test_prefpicker_09(tmp_path):
267
+ """test PrefPicker.load_template() with invalid YAML"""
268
+ yml = tmp_path / "test.yml"
269
+ yml.write_text("{-{-{-{-:::")
270
+ with raises(SourceDataError, match=r"invalid YAML"):
271
+ PrefPicker.load_template(yml)
272
+
273
+
274
+ def test_prefpicker_10():
275
+ """test PrefPicker.lookup_template()"""
276
+ # unknown template
277
+ assert PrefPicker.lookup_template("missing") is None
278
+ # existing template
279
+ template = next(PrefPicker.templates(), None)
280
+ assert template is not None
281
+ assert PrefPicker.lookup_template(template.name)
@@ -0,0 +1,44 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ """template.py tests"""
5
+
6
+ from difflib import unified_diff
7
+
8
+ from yaml import safe_dump, safe_load
9
+
10
+ from .main import main
11
+ from .prefpicker import PrefPicker
12
+
13
+
14
+ def test_templates_01(tmp_path):
15
+ """sanity check template YAML files"""
16
+ prefs_js = tmp_path / "prefs.js"
17
+ checked = []
18
+ for template in PrefPicker.templates():
19
+ assert main([str(template), str(prefs_js), "--check"]) == 0
20
+ assert prefs_js.is_file()
21
+ prefs_js.unlink()
22
+ checked.append(template.name)
23
+ assert "browser-fuzzing.yml" in checked
24
+
25
+
26
+ def test_templates_02():
27
+ """check formatting of template YAML files"""
28
+ for template in PrefPicker.templates():
29
+ with template.open() as in_fp:
30
+ # remove comments
31
+ input_yml = "".join(x for x in in_fp if not x.lstrip().startswith("#"))
32
+ formatted_yml = safe_dump(safe_load(input_yml), indent=2, width=100)
33
+ diff = tuple(
34
+ unified_diff(
35
+ input_yml.splitlines(),
36
+ formatted_yml.splitlines(),
37
+ fromfile=str(template),
38
+ tofile="formatting fixes",
39
+ lineterm="",
40
+ )
41
+ )
42
+ if diff:
43
+ formatted = "\n".join(diff)
44
+ raise AssertionError(f"Formatting changes required:\n{formatted}")
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: prefpicker
3
+ Version: 2.17.0
4
+ Summary: PrefPicker - Manage & generate prefs.js files
5
+ Home-page: https://github.com/MozillaSecurity/prefpicker
6
+ Author: Tyson Smith
7
+ Author-email: twsmith@mozilla.com
8
+ Maintainer: Mozilla Fuzzing Team
9
+ Maintainer-email: fuzzing@mozilla.com
10
+ License: MPL 2.0
11
+ Keywords: firefox fuzz fuzzing test testing
12
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: PyYAML
19
+ Provides-Extra: dev
20
+ Requires-Dist: pre-commit; extra == "dev"
21
+ Requires-Dist: tox; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ PrefPicker
25
+ ==========
26
+ [![CI](https://github.com/MozillaSecurity/prefpicker/actions/workflows/ci.yml/badge.svg)](https://github.com/MozillaSecurity/prefpicker/actions/workflows/ci.yml)
27
+ [![codecov](https://codecov.io/gh/MozillaSecurity/prefpicker/branch/master/graph/badge.svg)](https://codecov.io/gh/MozillaSecurity/prefpicker)
28
+ [![Matrix](https://img.shields.io/badge/chat-%23fuzzing-green?logo=matrix)](https://matrix.to/#/#fuzzing:mozilla.org)
29
+ [![PyPI](https://img.shields.io/pypi/v/prefpicker)](https://pypi.org/project/prefpicker)
30
+
31
+
32
+ Manage & generate prefs.js files for use when testing Firefox. This tool is intended to simplify the use and tracking of prefs used by
33
+ our fuzzing tools. The template files can be modified to allow the creation of custom prefs.js files without
34
+ the need to maintain a separate mostly duplicate version of a prefs file.
35
+
36
+ YAML Template Structure
37
+ -----------------------
38
+
39
+ The template document is made up of variants, prefs and values.
40
+
41
+ _**pref**_ is the name of the preference that will be added to the prefs.js file. This is an unquoted string.
42
+ Valid prefs can be found in [all.js](https://hg.mozilla.org/mozilla-central/file/tip/modules/libpref/init/all.js) or in [StaticPrefList.yml](https://hg.mozilla.org/mozilla-central/file/tip/modules/libpref/init/StaticPrefList.yaml).
43
+
44
+ __**review_on_close**__ is optional. It is a list of relevant Bugzilla IDs used to help avoid obsolete entries. When all bugs in the list are closed the entry will be reviewed and removed if appropriate.
45
+
46
+ _**value**_ can be a `bool`, `int`, `string` or `null`. Adding multiple potential values is possible.
47
+ When multiple values are present one is chosen at random when generating the output.
48
+ Using a value of `null` will exclude the pref from the prefs.js file (acts as browser default).
49
+
50
+ _**variant**_ is a subset of values to be used in place of the default values.
51
+ The default variant is used unless a variant is specified.
52
+
53
+ There are a few mechanisms in place to help keep the file in order:
54
+ - All prefs must have a default variant
55
+ - All variants must be defined in the variant list
56
+ - All variants in the variant list must be used
57
+ - All variants must be a list and contain values
58
+
59
+ ```yml
60
+ # example.yml
61
+ variant: # list of extra variants, default is implied
62
+ - alt # name of variant
63
+ pref:
64
+ pref.name: # unquoted name of the pref used in prefs.js
65
+ review_on_close: # optional
66
+ - 123456
67
+ variants:
68
+ default: # variant definition, default is required
69
+ - 0 # potential value
70
+ alt: # extra optional variant
71
+ - 1 # if multiple values are defined one is chosen randomly
72
+ - null # null is a special case meaning exclude the pref
73
+ ```
74
+
75
+ Updating Templates and Adding Prefs
76
+ -----------------------------------
77
+ Prefs are found in the `.yml` files in the [template](/src/prefpicker/templates) directory.
78
+ Only prefs that are ready to be tested should be added.
79
+ When adding a pref to a template it is encouraged to add a comment that provides justification and points to a bug in Bugzilla for additional context.
80
+ If a pref does not already exist and is only used with non-default variants a `null` entry must be added to the default variant.
81
+
82
+ Quick Setup
83
+ -----------
84
+
85
+ Use pip to install prefpicker.
86
+
87
+ ```bash
88
+ pip install prefpicker
89
+ ```
90
+
91
+ Examples
92
+ --------
93
+
94
+ Use a built-in [template](/src/prefpicker/templates) to generate an up-to-date `prefs.js` file.
95
+
96
+ ```bash
97
+ prefpicker browser-fuzzing.yml prefs.js
98
+ ```
99
+
100
+ Or generate a `prefs.js` file from a custom template using the `webrender` variant:
101
+
102
+ ```bash
103
+ user@machine:~/prefpicker$ prefpicker custom/template.yml ~/Desktop/prefs.js --variant webrender
104
+ Loading 'template.yml'...
105
+ Loaded 255 prefs and 5 variants
106
+ Generating 'prefs.js' using variant 'webrender'...
107
+ Done.
108
+ ```
109
+
110
+ The resulting `prefs.js` file is ready to be used with Firefox. It will look something like this:
111
+
112
+ ```js
113
+ // Generated with PrefPicker @ 2020-02-08 00:50:29 UTC
114
+ // Variant 'webrender'
115
+ /// ... snip
116
+ user_pref("fuzzing.enabled", true);
117
+ /// ... snip
118
+ // 'gfx.webrender.all' defined by variant 'webrender'
119
+ user_pref("gfx.webrender.all", true);
120
+ /// ... snip
121
+ ```
@@ -0,0 +1,16 @@
1
+ prefpicker/__init__.py,sha256=XWsqb0fPEcXI12OFAGU0ftCAiUAnNTyyAzADC62x1-g,348
2
+ prefpicker/__main__.py,sha256=8-D6LzRgVF2PwY7XXs1Fs-Nzy0NPOl04DKMmk18SqsU,282
3
+ prefpicker/main.py,sha256=V10iJ6UiNc5aACflJSHjJl59kc6f1uJhuSfRFkrsJqw,3842
4
+ prefpicker/prefpicker.py,sha256=tKnwVdO0bwmOv1XjNntojAiCPwHsAGNx3X6SMxLpaJQ,9817
5
+ prefpicker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ prefpicker/test_main.py,sha256=6K4eKDvXwy7ogdj-tOKIfjCJjr0CnQwfFyobjpj4hgk,2644
7
+ prefpicker/test_prefpicker.py,sha256=ojoYid7rC57dpWqjpnXnp7_E1F84OBa6iBKrAwl1CyU,9330
8
+ prefpicker/test_templates.py,sha256=jlu3QTWzn1iE_7ljQs3byBAvUaGjeFZPNuBySSvSUoM,1496
9
+ prefpicker/templates/browser-fuzzing.yml,sha256=wAuOaGPHoLQY6TnuvE0b67YOvRBWMVfMj6C-Hd64BXs,23756
10
+ prefpicker/templates/schema.json,sha256=D14picYNGMXV0WObBxeZFb7l_U_OJkZIi29Rv25M2nA,1293
11
+ prefpicker-2.17.0.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
12
+ prefpicker-2.17.0.dist-info/METADATA,sha256=LHgAJLnJzhqWKYK9bc7IVzQijRarWvx8ExS7VfMab54,4949
13
+ prefpicker-2.17.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ prefpicker-2.17.0.dist-info/entry_points.txt,sha256=og57vebqGVtgg_OZGO39WUb13H8wMzcnNBh4h-t26K0,52
15
+ prefpicker-2.17.0.dist-info/top_level.txt,sha256=H-QqR-VZXNwG4ya7wnqx5NA-yua6D9uQuBquP2zCRbc,11
16
+ prefpicker-2.17.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ prefpicker = prefpicker.main:main