Project

General

Profile

Download (3.59 KB) Statistics
| Branch: | Tag: | Revision:

calebasse / calebasse / doc_templates.py @ a9520794

1
import os
2
import os.path
3
import re
4
import contextlib
5
import tempfile
6

    
7
__ALL__ = [ 'DocTemplateError', 'make_doc_from_template' ]
8

    
9
VARIABLE_RE = re.compile(r'@[A-Z]{2,3}[0-9]{0,2}')
10

    
11
class DocTemplateError(RuntimeError):
12
    def __str__(self):
13
        return ' '.join(map(str, self.args))
14

    
15

    
16
@contextlib.contextmanager
17
def delete_on_error(f):
18
    '''Context manager which deletes a file if an exception occur'''
19
    try:
20
        yield
21
    except:
22
        os.remove(f.name)
23
        raise
24

    
25

    
26
def replace_variables(template, variables, ignore_unused=True):
27
    '''Lookup all substring looking like @XXX9 in the string template, and
28
       replace them by the value of the key XXX9 in the dictionary variables.
29

    
30
       Returns a new :class:str
31

    
32
       :param template: string containing the variables references
33
       :param variables: dictionary containing the variables values
34

    
35
    '''
36
    needed_vars = set([v[1:] for v in re.findall(VARIABLE_RE, template)])
37
    given_vars = set(variables.keys())
38
    if given_vars != needed_vars:
39
        missing = needed_vars - given_vars
40
        if missing or not ignore_unused:
41
            unused = given_vars - needed_vars
42
            raise DocTemplateError(
43
                'Mismatch between given and needed variables: missing={0} unused={1}'
44
                .format(map(repr, missing), map(repr, unused)))
45
    def variable_replacement(match_obj):
46
        return variables[match_obj.group(0)[1:]]
47
    return re.sub(VARIABLE_RE, variable_replacement, template)
48

    
49

    
50
def char_to_rtf(c):
51
    if ord(c) < 128:
52
        return c
53
    else:
54
        return '\u%d?' % ord(c)
55

    
56

    
57
def utf8_to_rtf(s):
58
    s = ''.join([ char_to_rtf(c) for c in s])
59
    return '{\uc1{%s}}' % s
60

    
61

    
62
def unicode_to_rtf(value):
63
    try:
64
        value = unicode(value)
65
    except Exception, e:
66
        raise DocTemplateError('Unable to get a unicode value', e)
67
    return utf8_to_rtf(value)
68

    
69

    
70
def variables_to_rtf(variables):
71
    return dict(((k, unicode_to_rtf(v)) for k,v in variables.iteritems()))
72

    
73

    
74
def make_doc_from_template(from_path, to_path, variables, persistent):
75
    '''Use file from_path as a template to combine with the variables
76
       dictionary and place the result in the file to_path.
77
       Encode value of variable into encoding of UTF-8 for the RTF file format.
78

    
79
       :param from_path: the template file path
80
       :param to_path: the newly created file containing the result of the templating
81
       :param variables: the dictionary of variables to replace
82
    '''
83

    
84
    if not os.path.exists(from_path):
85
        raise DocTemplateError('Template file does not exist', repr(from_path))
86
    if os.path.exists(to_path):
87
        raise DocTemplateError('Destination file already exists', repr(to_path))
88
    variables = variables_to_rtf(variables)
89
    if persistent:
90
        with open(to_path, 'w') as to_file:
91
            with open(from_path) as from_file:
92
                with delete_on_error(to_file):
93
                    to_file.write(replace_variables(from_file.read(), variables))
94
    else:
95
        with tempfile.NamedTemporaryFile(prefix=to_path,
96
                delete=False) as to_file:
97
            with open(from_path) as from_file:
98
                with delete_on_error(to_file):
99
                    to_file.write(replace_variables(from_file.read(), variables))
100
    return to_file.name
101

    
102
if __name__ == '__main__':
103
    import sys
104
    try:
105
        variables = dict((map(lambda x: unicode(x, 'utf-8'), arg.split('=')) for arg in sys.argv[3:]))
106
        make_doc_from_template(sys.argv[1], sys.argv[2], variables)
107
    except Exception, e:
108
        raise
109
        print 'Usage: python doc_template.py <template.rtf> <output.rtf> [KEY1=value1 KEY2=value2 ...]'
(6-6/17)