# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/.
# The beginning of this script is both valid shell and valid python, # such that the script starts with the shell and is reexecuted python '''which' mach > /dev/null 2>&1 && exec mach python "$0""$@" ||
echo "mach not found, either add it to your \$PATH or run this script via ./mach python testing/profiles/profile"; exit# noqa '''
"""This script can be used to:
1) Show all preferences for a given suite
2) Diff preferences between two suites or profiles
3) Sort preference files alphabetically for a given profile
To use, either make sure that `mach` is on your $PATH, or run:
$ ./mach python testing/profiles/profile <args>
For more details run:
$ ./profile -- --help """
import json
import os
import sys
from argparse import ArgumentParser
from itertools import chain
from mozprofile import Profile
from mozprofile.prefs import Preferences
def read_prefs(profile, pref_files=None): """Read and return all preferences set in the given profile.
:param profile: Profile name relative to this `here`.
:returns: A dictionary of preferences set in the profile. """
pref_files = pref_files or Profile.preference_file_names
prefs = {} for name in pref_files:
path = os.path.join(here, profile, name) ifnot os.path.isfile(path): continue
def get_profiles(key): """Return a list of profile names for key."""
with open(os.path.join(here, 'profiles.json'), 'r') as fh:
profiles = json.load(fh)
if'+' in key:
keys = key.split('+') else:
keys = [key]
names = set() for key in keys: if key in profiles:
names.update(profiles[key])
elif os.path.isdir(os.path.join(here, key)):
names.add(key)
ifnot names:
raise ValueError('{} is not a recognized suite or profile'.format(key)) return names
def read(key): """Read preferences relevant to either a profile or suite.
:param key: Can either be the name of a profile, or the name of
a suite as defined in suites.json. """
prefs = {} for profile in get_profiles(key):
prefs.update(read_prefs(profile)) return prefs
def format_diff(diff, fmt, limit_key): """Format a diff."""
indent = ' ' if limit_key:
diff = {limit_key: diff[limit_key]}
indent = ''
if fmt == 'json':
print(json.dumps(diff, sort_keys=True, indent=2)) return 0
lines = [] for key, prefs in sorted(diff.items()): ifnot limit_key:
lines.append("{}:".format(key))
for pref, value in sorted(prefs.items()):
context = {'pref': pref, 'value': repr(value)}
if isinstance(value, list):
context['value_a'] = repr(value[0])
context['value_b'] = repr(value[1])
text = FORMAT_STRINGS[fmt][1].format(**context) else:
text = FORMAT_STRINGS[fmt][0].format(**context)
def diff(a, b, fmt, limit_key): """Diff two profiles or suites.
:param a: The first profile or suite name.
:param b: The second profile or suite name. """
prefs_a = read(a)
prefs_b = read(b)
res = jsondiff.diff(prefs_a, prefs_b, syntax='symmetric') ifnot res: return 0
if isinstance(res, list) and len(res) == 2:
res = {
jsondiff.Symbol('delete'): res[0],
jsondiff.Symbol('insert'): res[1],
}
# Post process results to make them JSON compatible and a # bit more clear. Also calculate identical prefs.
results = {}
results['change'] = {k: v for k, v in res.items() ifnot isinstance(k, jsondiff.Symbol)}
symbols = [(k, v) for k, v in res.items() if isinstance(k, jsondiff.Symbol)]
results['insert'] = {k: v for sym, pref in symbols for k, v in pref.items() if sym.label == 'insert'}
results['delete'] = {k: v for sym, pref in symbols for k, v in pref.items() if sym.label == 'delete'}
same = set(prefs_a.keys()) - set(chain(*results.values()))
results['same'] = {k: v for k, v in prefs_a.items() if k in same} return format_diff(results, fmt, limit_key)
def read_with_comments(path):
with open(path, 'r') as fh:
lines = fh.readlines()
result = []
buf = [] for line in lines:
line = line.strip() ifnot line: continue
if line.startswith('//'):
buf.append(line) continue
if buf:
result.append(buf + [line])
buf = [] continue
result.append([line]) return result
def sort_file(path): """Sort the given pref file alphabetically, preserving preceding comments
that start with '//'.
:param path: Path to the preference file to sort. """
result = read_with_comments(path)
result = sorted(result, key=lambda x: x[-1])
result = chain(*result)
with open(path, 'w') as fh:
fh.write('\n'.join(result) + '\n')
def sort(profile): """Sort all prefs in the given profile alphabetically. This will preserve
comments on preceding lines.
:param profile: The name of the profile to sort. """
pref_files = Profile.preference_file_names
for name in pref_files:
path = os.path.join(here, profile, name) if os.path.isfile(path):
sort_file(path)
def show(suite): """Display all prefs set in profiles used by the given suite.
:param suite: The name of the suite to show preferences for. This must
be a key in suites.json. """ for k, v in sorted(read(suite).items()):
print("{}: {}".format(k, repr(v)))
def rm(profile, pref_file): if pref_file == '-':
lines = sys.stdin.readlines() else:
with open(pref_file, 'r') as fh:
lines = fh.readlines()
lines = [l.strip() for l in lines if l.strip()] ifnot lines: return
def filter_line(content): returnnot any(line in content[-1] for line in lines)
diff_parser = subparsers.add_parser('diff')
diff_parser.add_argument('a', metavar='A',
help="Path to the first profile or suite name to diff.")
diff_parser.add_argument('b', metavar='B',
help="Path to the second profile or suite name to diff.")
diff_parser.add_argument('-f', '--format', dest='fmt', default='pretty',
choices=['pretty', 'json', 'names'],
help="Format to dump diff in (default: pretty)")
diff_parser.add_argument('-k', '--limit-key', default=None,
choices=['change', 'delete', 'insert', 'same'],
help="Restrict diff to the specified key.")
diff_parser.set_defaults(func=diff)
sort_parser = subparsers.add_parser('sort')
sort_parser.add_argument('profile', help="Path to profile to sort preferences.")
sort_parser.set_defaults(func=sort)
show_parser = subparsers.add_parser('show')
show_parser.add_argument('suite', help="Name of suite to show arguments for.")
show_parser.set_defaults(func=show)
rm_parser = subparsers.add_parser('rm')
rm_parser.add_argument('profile', help="Name of the profile to remove prefs from.")
rm_parser.add_argument('--pref-file', default='-', help="File containing a list of pref " "substrings to delete (default: stdin)")
rm_parser.set_defaults(func=rm)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.