Skip to content

Commit

Permalink
Load settings from a configuration file
Browse files Browse the repository at this point in the history
This commit allows settings to be loaded from a configuration
file. If one exists at `$XDG_CONFIG_HOME/screenplain/screenplain.cfg`
(by default), it will be loaded.

A new `-c`/`--config` command line flag allows loading a configuration
file explicitly. Command line options take precedence over the settings
loaded from the configuration file.

The format of the file is INI, with no interpolations, options without
values are allowed, and double-bracketed "sub-sections" (aesthetics
only, the hierarchy of such sections in relation with "root-level"
sections is not enforced).

An example of a configuration file would be:

```ini
    [export]
        format: pdf

        [[pdf]]
            strong: no

        [[html]]
            base: no
            css
```
  • Loading branch information
lenormf committed May 14, 2020
1 parent 4a0020c commit b9055e5
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 4 deletions.
61 changes: 61 additions & 0 deletions screenplain/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import os
import re
import json
import configparser


class Defaults:
FILENAME_CONFIG = 'screenplain.cfg'
PATH_CONFIG = os.path.join(
os.getenv('XDG_CONFIG_HOME')
or os.path.join(os.getenv('HOME'), '.config'),
'screenplain',
FILENAME_CONFIG,
)


class ConfigurationFileError(Exception): pass


class ConfigurationFile(configparser.ConfigParser):
def __init__(self, path=Defaults.PATH_CONFIG):
super().__init__(interpolation=None,
allow_no_value=True,
converters={
'list': self.__getlist,
})

# Allow brackets in section names
self.SECTCRE = re.compile(r'^[ \t\r\f\v]*\[(?P<header>.+?)\][ \t\r\f\v]*$')

# Initialize sections and their expected values
self.read_string("""
[export]
format
[[pdf]]
strong: no
font
[[html]]
base: no
css
[font]
""")

try:
self.read(path)
except configparser.Error as e:
raise ConfigurationFileError('unable to load configuration file: %s' % e)

def __getlist(self, v):
try:
v = json.loads(v)
except json.JSONDecodeError as e:
raise ConfigurationFileError('unable to decode JSON value: %s' % e)

if not isinstance(v, list):
raise ConfigurationFileError('value is not a list: %s' % type(v))

return v
41 changes: 37 additions & 4 deletions screenplain/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php

import os
import sys
import codecs
from optparse import OptionParser

from screenplain.parsers import fountain
from screenplain.config import ConfigurationFile, ConfigurationFileError

output_formats = (
'fdx', 'html', 'pdf'
Expand All @@ -32,6 +34,14 @@ def invalid_format(parser, message):

def main(args):
parser = OptionParser(usage=usage)
parser.add_option(
'-c', '--config',
metavar='CONFIG',
help=(
'Path to the configuration file to load (superseeded by command '
'line options)'
)
)
parser.add_option(
'-f', '--format', dest='output_format',
metavar='FORMAT',
Expand All @@ -43,6 +53,7 @@ def main(args):
parser.add_option(
'--bare',
action='store_true',
default=False,
dest='bare',
help=(
'For HTML output, only output the actual screenplay, '
Expand All @@ -60,6 +71,7 @@ def main(args):
parser.add_option(
'--strong',
action='store_true',
default=False,
dest='strong',
help=(
'For PDF output, scene headings will appear '
Expand All @@ -72,8 +84,27 @@ def main(args):
input_file = (len(args) > 0 and args[0] != '-') and args[0] or None
output_file = (len(args) > 1 and args[1] != '-') and args[1] or None

format = options.output_format
if format is None and output_file:
try:
if options.config:
if not os.path.isfile(options.config):
sys.stderr.write('no such file: %s' % options.config)
return
config = ConfigurationFile(options.config)
else:
config = ConfigurationFile()
except ConfigurationFileError as e:
sys.stderr.write('error: %s' % e)
return

if options.output_format:
config['export']['format'] = options.output_format
if options.css:
config['[html]']['css'] = options.css
config['[html]']['bare'] = str(options.bare)
config['[pdf]']['strong'] = str(options.strong)

format = config['export']['format']
if not format and output_file:
if output_file.endswith('.fdx'):
format = 'fdx'
elif output_file.endswith('.html'):
Expand Down Expand Up @@ -121,14 +152,16 @@ def main(args):
from screenplain.export.fdx import to_fdx
to_fdx(screenplay, output)
elif format == 'html':
html_options = config['[html]']
from screenplain.export.html import convert
convert(
screenplay, output,
css_file=options.css, bare=options.bare
css_file=html_options['css'], bare=html_options.getboolean('bare')
)
elif format == 'pdf':
pdf_options = config['[pdf]']
from screenplain.export.pdf import to_pdf
to_pdf(screenplay, output, is_strong=options.strong)
to_pdf(config, screenplay, output, is_strong=pdf_options.getboolean('strong'))
finally:
if output_file:
output.close()
Expand Down

0 comments on commit b9055e5

Please sign in to comment.