collections.OrderedDict = _OrderedDict
+class ConfigError (Exception):
+ """Errors with config file usage
+ """
+ pass
+
+
def read_config(path=None, encoding=None):
"Read config from json file"
if not encoding:
encoding = _ENCODING
if path:
- fp = open(path)
+ try:
+ with open(path, 'rb') as f:
+ config_bytes = f.read()
+ except IOError as e:
+ raise ConfigError('Could not read config from {}'.format(path))
else:
nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
+ branch = 'config'
+ filename = 'status-config.json'
# read only the first line from the pipe
sha1_bytes = subprocess.Popen(
- ['git', '--git-dir', nmbhome, 'show-ref', '-s', 'config'],
+ ['git', '--git-dir', nmbhome, 'show-ref', '-s', '--heads', branch],
stdout=subprocess.PIPE).stdout.readline()
sha1 = sha1_bytes.decode(encoding).rstrip()
+ if not sha1:
+ raise ConfigError(
+ ("No local branch '{branch}' in {nmbgit}. "
+ 'Checkout a local {branch} branch or explicitly set --config.'
+ ).format(branch=branch, nmbgit=nmbhome))
- fp_byte_stream = subprocess.Popen(
+ p = subprocess.Popen(
['git', '--git-dir', nmbhome, 'cat-file', 'blob',
- sha1+':status-config.json'],
- stdout=subprocess.PIPE).stdout
- fp = codecs.getreader(encoding=encoding)(stream=fp_byte_stream)
-
- return json.load(fp)
+ '{}:{}'.format(sha1, filename)],
+ stdout=subprocess.PIPE)
+ config_bytes, err = p.communicate()
+ status = p.wait()
+ if status != 0:
+ raise ConfigError(
+ ("Missing {filename} in branch '{branch}' of {nmbgit}. "
+ 'Add the file or explicitly set --config.'
+ ).format(filename=filename, branch=branch, nmbgit=nmbhome))
+
+ config_json = config_bytes.decode(encoding)
+ try:
+ return json.loads(config_json)
+ except ValueError as e:
+ if not path:
+ path = "{} in branch '{}' of {}".format(
+ filename, branch, nmbhome)
+ raise ConfigError(
+ 'Could not parse JSON from the config file {}:\n{}'.format(
+ path, e))
class Thread (list):
stream.write(self.footer)
def _write_view(self, database, view, stream):
+ # sort order, default to oldest-first
+ sort_key = view.get('sort', 'oldest-first')
+ # dynamically accept all values in Query.SORT
+ sort_attribute = sort_key.upper().replace('-', '_')
+ try:
+ sort = getattr(notmuch.Query.SORT, sort_attribute)
+ except AttributeError:
+ raise ConfigError('Invalid sort setting for {}: {!r}'.format(
+ view['title'], sort_key))
if 'query-string' not in view:
query = view['query']
view['query-string'] = ' and '.join(query)
q = notmuch.Query(database, view['query-string'])
- q.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
+ q.set_sort(sort)
threads = self._get_threads(messages=q.search_messages())
self._write_view_header(view=view, stream=stream)
self._write_threads(threads=threads, stream=stream)
args = parser.parse_args()
-config = read_config(path=args.config)
+try:
+ config = read_config(path=args.config)
+except ConfigError as e:
+ print(e, file=sys.stderr)
+ sys.exit(1)
header_template = config['meta'].get('header', '''<!DOCTYPE html>
<html lang="en">
<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
<title>{title}</title>
<style media="screen" type="text/css">
+ h1 {{
+ font-size: 1.5em;
+ }}
+ h2 {{
+ font-size: 1.17em;
+ }}
+ h3 {{
+ font-size: 100%;
+ }}
table {{
border-spacing: 0;
}}
</style>
</head>
<body>
-<h2>{title}</h2>
+<h1>{title}</h1>
{blurb}
</p>
-<h3>Views</h3>
+<h2>Views</h2>
''')
footer_template = config['meta'].get('footer', '''