We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
jinjify: convert a single mako template file to jinja?
It looks like jinjify requires a full theme to work. For example, how do you synchronize the two templates in the [projectpages plugin](https://github.com/getnikola/plugins/tree/master/v7/projectpages/templates) with jinjify?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/jinjify.py`
Content:
```
1 #!/usr/bin/env python
2 import io
3 import glob
4 import sys
5 import os
6 import re
7 import json
8 import shutil
9 
10 import colorama
11 import jinja2
12 
13 dumb_replacements = [
14     ["{% if any(post.is_mathjax for post in posts) %}", '{% if posts|selectattr("is_mathjax")|list %}'],
15     ["json.dumps(title)", "title|tojson"],
16     ["{{ parent.extra_head() }}", "{{ super() }}"],
17     ["{{ parent.content() }}", "{{ super() }}"],
18     ["prefix='\\", "prefix='"],
19     ["og: http://ogp.me/ns# \\", "og: http://ogp.me/ns#"],
20     ["article: http://ogp.me/ns/article# \\", "article: http://ogp.me/ns/article#"],
21     ["fb: http://ogp.me/ns/fb# \\", "fb: http://ogp.me/ns/fb#"],
22     ['dir="rtl" \\', 'dir="rtl"'],
23     ['sorted(translations)', 'translations|sort'],
24 ]
25 
26 dumber_replacements = [
27     ['<%! import json %>\n\n', ''],
28     ["<html\n\\", "<html\n"],
29     ["\n'\\\n", "\n'\n"],
30     ["{% endif %}\n\\", "{% endif %}\n"]
31 ]
32 
33 
34 def jinjify(in_theme, out_theme):
35     """Convert in_theme into a jinja version and put it in out_theme"""
36 
37     in_templates_path = os.path.join(in_theme, "templates")
38     out_templates_path = os.path.join(out_theme, "templates")
39     try:
40         os.makedirs(out_templates_path)
41     except:
42         pass
43     lookup = jinja2.Environment()
44     lookup.filters['tojson'] = json.dumps
45     lookup.loader = jinja2.FileSystemLoader([out_templates_path], encoding='utf-8')
46     for template in glob.glob(os.path.join(in_templates_path, "*.tmpl")):
47         out_template = os.path.join(out_templates_path, os.path.basename(template))
48         with io.open(template, "r", encoding="utf-8") as inf:
49             data = mako2jinja(inf)
50 
51         lines = []
52         for line in data.splitlines():
53             for repl in dumb_replacements:
54                 line = line.replace(*repl)
55             lines.append(line)
56         data = '\n'.join(lines)
57 
58         for repl in dumber_replacements:
59             data = data.replace(*repl)
60 
61         with io.open(out_template, "w+", encoding="utf-8") as outf:
62             outf.write(data + '\n')
63 
64         # Syntax check output
65         source, filename = lookup.loader.get_source(lookup, os.path.basename(template))[:2]
66         try:
67             lookup.parse(source)
68         except Exception as e:
69             error("Syntax error in {0}:{1}".format(out_template, e.lineno))
70 
71     parent = os.path.basename(in_theme.rstrip('/'))
72     child = os.path.basename(out_theme.rstrip('/'))
73     mappings = {
74         'base-jinja': 'base',
75         'bootstrap3-jinja': 'base-jinja',
76     }
77 
78     if child in mappings:
79         parent = mappings[child]
80 
81     with io.open(os.path.join(out_theme, "parent"), "w+", encoding='utf-8') as outf:
82         outf.write(u'{0}\n'.format(parent))
83 
84     with io.open(os.path.join(out_theme, "engine"), "w+", encoding='utf-8') as outf:
85         outf.write(u"jinja\n")
86 
87     # Copy assets in bootstrap/bootstrap3
88     if child == 'bootstrap3-jinja':
89         shutil.rmtree(os.path.join(out_theme, "assets"))
90         shutil.copytree(
91             os.path.join(in_theme, "assets"), os.path.join(out_theme, "assets"),
92             symlinks=True)
93 
94     # Copy bundles
95     # shutil.copy(os.path.join(in_theme, "bundles"), os.path.join(out_theme, "bundles"))
96 
97     # Copy README
98     if os.path.isfile(os.path.join(in_theme, "README.md")):
99         shutil.copy(os.path.join(in_theme, "README.md"), os.path.join(out_theme, "README.md"))
100 
101 
102 def error(msg):
103     print(colorama.Fore.RED + "ERROR:" + msg)
104 
105 
106 def mako2jinja(input_file):
107 
108     output = ''
109 
110     # TODO: OMG, this code is so horrible. Look at it; just look at it:
111 
112     macro_start = re.compile(r'(.*)<%.*def name="(.*?)".*>(.*)', re.IGNORECASE)
113     macro_end = re.compile(r'(.*)</%def>(.*)', re.IGNORECASE)
114 
115     if_start = re.compile(r'(.*)% *if (.*):(.*)', re.IGNORECASE)
116     if_else = re.compile(r'(.*)% *else.*:(.*)', re.IGNORECASE)
117     if_elif = re.compile(r'(.*)% *elif (.*):(.*)', re.IGNORECASE)
118     if_end = re.compile(r'(.*)% *endif(.*)', re.IGNORECASE)
119 
120     for_start = re.compile(r'(.*)% *for (.*):(.*)', re.IGNORECASE)
121     for_end = re.compile(r'(.*)% *endfor(.*)', re.IGNORECASE)
122 
123     namespace = re.compile(r'(.*)<% *namespace name="(.*?)".* file="(.*?)".*/>(.*)', re.IGNORECASE)
124     inherit = re.compile(r'(.*)<% *inherit file="(.*?)".*/>(.*)', re.IGNORECASE)
125 
126     block_single_line = re.compile(r'(.*)<% *block.*name="(.*?)".*>(.*)</% *block>(.*)', re.IGNORECASE)
127     block_start = re.compile(r'(.*)<% *block.*name="(.*?)".*>(.*)', re.IGNORECASE)
128     block_end = re.compile(r'(.*)</%block>(.*)', re.IGNORECASE)
129 
130     val = re.compile(r'\$\{(.*?)\}', re.IGNORECASE)
131     func_len = re.compile(r'len\((.*?)\)', re.IGNORECASE)
132     filter_h = re.compile(r'\|h', re.IGNORECASE)
133     filter_striphtml = re.compile(r'\|striphtml', re.IGNORECASE)
134     filter_u = re.compile(r'\|u', re.IGNORECASE)
135 
136     comment_single_line = re.compile(r'^.*##(.*?)$', re.IGNORECASE)
137 
138     for line in input_file:
139 
140         # Process line for repeated inline replacements
141         m_val = val.search(line)
142         m_func_len = func_len.search(line)
143         m_filter_h = filter_h.search(line)
144         m_filter_striphtml = filter_striphtml.search(line)
145         m_filter_u = filter_u.search(line)
146 
147         if m_val:
148             line = val.sub(r'{{ \1 }}', line)
149 
150         if m_filter_h:
151             line = filter_h.sub(r'|e', line)
152 
153         if m_filter_striphtml:
154             line = filter_striphtml.sub(r'|e', line)
155 
156         if m_filter_u:
157             line = filter_u.sub(r'|urlencode', line)
158 
159         if m_func_len:
160             line = func_len.sub(r'\1|length', line)
161 
162         # Process line for single 'whole line' replacements
163         m_macro_start = macro_start.search(line)
164         m_macro_end = macro_end.search(line)
165         m_if_start = if_start.search(line)
166         m_if_else = if_else.search(line)
167         m_if_elif = if_elif.search(line)
168         m_if_end = if_end.search(line)
169         m_for_start = for_start.search(line)
170         m_for_end = for_end.search(line)
171         m_namspace = namespace.search(line)
172         m_inherit = inherit.search(line)
173         m_block_single_line = block_single_line.search(line)
174         m_block_start = block_start.search(line)
175         m_block_end = block_end.search(line)
176 
177         m_comment_single_line = comment_single_line.search(line)
178 
179         if m_comment_single_line:
180             output += m_comment_single_line.expand(r'{# \1 #}') + '\n'
181 
182         elif m_macro_start:
183             output += m_macro_start.expand(r'\1{% macro \2 %}\3') + '\n'
184         elif m_macro_end:
185             output += m_macro_end.expand(r'\1{% endmacro %}\1') + '\n'
186 
187         elif m_if_start:
188             output += m_if_start.expand(r'\1{% if \2 %}\3') + '\n'
189         elif m_if_else:
190             output += m_if_else.expand(r'\1{% else %}\2') + '\n'
191         elif m_if_elif:
192             output += m_if_elif.expand(r'\1{% elif \2 %}\3') + '\n'
193         elif m_if_end:
194             output += m_if_end.expand(r'\1{% endif %}\2') + '\n'
195 
196         elif m_for_start:
197             output += m_for_start.expand(r'\1{% for \2 %}\3') + '\n'
198         elif m_for_end:
199             output += m_for_end.expand(r'\1{% endfor %}\2') + '\n'
200 
201         elif m_namspace:
202             output += m_namspace.expand(r"\1{% import '\3' as \2 with context %}\4") + '\n'
203         elif m_inherit:
204             output += m_inherit.expand(r"{% extends '\2' %}\3") + '\n'
205 
206         elif m_block_single_line:
207             output += m_block_single_line.expand(r'\1{% block \2 %}\3{% endblock %}\4') + '\n'
208         elif m_block_start:
209             output += m_block_start.expand(r'\1{% block \2 %}\3') + '\n'
210         elif m_block_end:
211             output += m_block_end.expand(r'\1{% endblock %}\2') + '\n'
212 
213         else:
214             # Doesn't match anything we're going to process, pass though
215             output += line
216 
217     return output
218 
219 
220 def jinjify_shortcodes(in_dir, out_dir):
221     for fname in os.listdir(in_dir):
222         if not fname.endswith('.tmpl'):
223             continue
224         in_file = os.path.join(in_dir, fname)
225         out_file = os.path.join(out_dir, fname)
226         with open(in_file) as inf:
227             data = mako2jinja(inf)
228         with open(out_file, 'w') as outf:
229             outf.write(data)
230             
231 
232 if __name__ == "__main__":
233     if len(sys.argv) == 1:
234         print('Performing standard conversions:')
235         for m, j in (
236             ('nikola/data/themes/base', 'nikola/data/themes/base-jinja'),
237             ('nikola/data/themes/bootstrap3', 'nikola/data/themes/bootstrap3-jinja')
238         ):
239             print('    {0} -> {1}'.format(m, j))
240             jinjify(m, j)
241         jinjify_shortcodes('nikola/data/shortcodes/mako', 'nikola/data/shortcodes/jinja')
242     elif len(sys.argv) != 3:
243         print('ERROR: needs input and output directory, or no arguments for default conversions.')
244     else:
245         jinjify(sys.argv[1], sys.argv[2])
246
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scripts/jinjify.py b/scripts/jinjify.py
--- a/scripts/jinjify.py
+++ b/scripts/jinjify.py
@@ -6,6 +6,7 @@
 import re
 import json
 import shutil
+import tempfile
 
 import colorama
 import jinja2
@@ -229,6 +230,11 @@
             outf.write(data)
             
 
+def usage():
+    print("Usage: python {} [in-dir] [out-dir]".format(sys.argv[0]))
+    print("OR")
+    print("Usage: python {} [in-file] [out-file]".format(sys.argv[0]))
+
 if __name__ == "__main__":
     if len(sys.argv) == 1:
         print('Performing standard conversions:')
@@ -240,6 +246,18 @@
             jinjify(m, j)
         jinjify_shortcodes('nikola/data/shortcodes/mako', 'nikola/data/shortcodes/jinja')
     elif len(sys.argv) != 3:
-        print('ERROR: needs input and output directory, or no arguments for default conversions.')
-    else:
+        print('ERROR: needs input and output directory (file), or no arguments for default conversions.')
+        usage()
+    elif os.path.isdir(sys.argv[1]) and (os.path.isdir(sys.argv[2]) or not os.path.exists(sys.argv[2])):
         jinjify(sys.argv[1], sys.argv[2])
+    elif os.path.isfile(sys.argv[1]) and (os.path.isfile(sys.argv[2]) or not os.path.exists(sys.argv[2])):
+        tmpdir = tempfile.mkdtemp()
+        indir = os.path.sep.join((tmpdir, 'in', 'templates'))
+        outdir = os.path.sep.join((tmpdir, 'out', 'templates'))
+        os.makedirs(indir)
+        shutil.copy(sys.argv[1], indir)
+        jinjify(os.path.dirname(indir), os.path.dirname(outdir))
+        shutil.move(os.path.sep.join((outdir, os.path.basename(sys.argv[1]))), sys.argv[2])
+    else:
+        print('ERROR: the two arguments must be both directories or files')
+        usage()

{"golden_diff": "diff --git a/scripts/jinjify.py b/scripts/jinjify.py\n--- a/scripts/jinjify.py\n+++ b/scripts/jinjify.py\n@@ -6,6 +6,7 @@\n import re\n import json\n import shutil\n+import tempfile\n \n import colorama\n import jinja2\n@@ -229,6 +230,11 @@\n             outf.write(data)\n             \n \n+def usage():\n+    print(\"Usage: python {} [in-dir] [out-dir]\".format(sys.argv[0]))\n+    print(\"OR\")\n+    print(\"Usage: python {} [in-file] [out-file]\".format(sys.argv[0]))\n+\n if __name__ == \"__main__\":\n     if len(sys.argv) == 1:\n         print('Performing standard conversions:')\n@@ -240,6 +246,18 @@\n             jinjify(m, j)\n         jinjify_shortcodes('nikola/data/shortcodes/mako', 'nikola/data/shortcodes/jinja')\n     elif len(sys.argv) != 3:\n-        print('ERROR: needs input and output directory, or no arguments for default conversions.')\n-    else:\n+        print('ERROR: needs input and output directory (file), or no arguments for default conversions.')\n+        usage()\n+    elif os.path.isdir(sys.argv[1]) and (os.path.isdir(sys.argv[2]) or not os.path.exists(sys.argv[2])):\n         jinjify(sys.argv[1], sys.argv[2])\n+    elif os.path.isfile(sys.argv[1]) and (os.path.isfile(sys.argv[2]) or not os.path.exists(sys.argv[2])):\n+        tmpdir = tempfile.mkdtemp()\n+        indir = os.path.sep.join((tmpdir, 'in', 'templates'))\n+        outdir = os.path.sep.join((tmpdir, 'out', 'templates'))\n+        os.makedirs(indir)\n+        shutil.copy(sys.argv[1], indir)\n+        jinjify(os.path.dirname(indir), os.path.dirname(outdir))\n+        shutil.move(os.path.sep.join((outdir, os.path.basename(sys.argv[1]))), sys.argv[2])\n+    else:\n+        print('ERROR: the two arguments must be both directories or files')\n+        usage()\n", "issue": "jinjify: convert a single mako template file to jinja?\nIt looks like jinjify requires a full theme to work. For example, how do you synchronize the two templates in the [projectpages plugin](https://github.com/getnikola/plugins/tree/master/v7/projectpages/templates) with jinjify?\n\n", "before_files": [{"content": "#!/usr/bin/env python\nimport io\nimport glob\nimport sys\nimport os\nimport re\nimport json\nimport shutil\n\nimport colorama\nimport jinja2\n\ndumb_replacements = [\n    [\"{% if any(post.is_mathjax for post in posts) %}\", '{% if posts|selectattr(\"is_mathjax\")|list %}'],\n    [\"json.dumps(title)\", \"title|tojson\"],\n    [\"{{ parent.extra_head() }}\", \"{{ super() }}\"],\n    [\"{{ parent.content() }}\", \"{{ super() }}\"],\n    [\"prefix='\\\\\", \"prefix='\"],\n    [\"og: http://ogp.me/ns# \\\\\", \"og: http://ogp.me/ns#\"],\n    [\"article: http://ogp.me/ns/article# \\\\\", \"article: http://ogp.me/ns/article#\"],\n    [\"fb: http://ogp.me/ns/fb# \\\\\", \"fb: http://ogp.me/ns/fb#\"],\n    ['dir=\"rtl\" \\\\', 'dir=\"rtl\"'],\n    ['sorted(translations)', 'translations|sort'],\n]\n\ndumber_replacements = [\n    ['<%! import json %>\\n\\n', ''],\n    [\"<html\\n\\\\\", \"<html\\n\"],\n    [\"\\n'\\\\\\n\", \"\\n'\\n\"],\n    [\"{% endif %}\\n\\\\\", \"{% endif %}\\n\"]\n]\n\n\ndef jinjify(in_theme, out_theme):\n    \"\"\"Convert in_theme into a jinja version and put it in out_theme\"\"\"\n\n    in_templates_path = os.path.join(in_theme, \"templates\")\n    out_templates_path = os.path.join(out_theme, \"templates\")\n    try:\n        os.makedirs(out_templates_path)\n    except:\n        pass\n    lookup = jinja2.Environment()\n    lookup.filters['tojson'] = json.dumps\n    lookup.loader = jinja2.FileSystemLoader([out_templates_path], encoding='utf-8')\n    for template in glob.glob(os.path.join(in_templates_path, \"*.tmpl\")):\n        out_template = os.path.join(out_templates_path, os.path.basename(template))\n        with io.open(template, \"r\", encoding=\"utf-8\") as inf:\n            data = mako2jinja(inf)\n\n        lines = []\n        for line in data.splitlines():\n            for repl in dumb_replacements:\n                line = line.replace(*repl)\n            lines.append(line)\n        data = '\\n'.join(lines)\n\n        for repl in dumber_replacements:\n            data = data.replace(*repl)\n\n        with io.open(out_template, \"w+\", encoding=\"utf-8\") as outf:\n            outf.write(data + '\\n')\n\n        # Syntax check output\n        source, filename = lookup.loader.get_source(lookup, os.path.basename(template))[:2]\n        try:\n            lookup.parse(source)\n        except Exception as e:\n            error(\"Syntax error in {0}:{1}\".format(out_template, e.lineno))\n\n    parent = os.path.basename(in_theme.rstrip('/'))\n    child = os.path.basename(out_theme.rstrip('/'))\n    mappings = {\n        'base-jinja': 'base',\n        'bootstrap3-jinja': 'base-jinja',\n    }\n\n    if child in mappings:\n        parent = mappings[child]\n\n    with io.open(os.path.join(out_theme, \"parent\"), \"w+\", encoding='utf-8') as outf:\n        outf.write(u'{0}\\n'.format(parent))\n\n    with io.open(os.path.join(out_theme, \"engine\"), \"w+\", encoding='utf-8') as outf:\n        outf.write(u\"jinja\\n\")\n\n    # Copy assets in bootstrap/bootstrap3\n    if child == 'bootstrap3-jinja':\n        shutil.rmtree(os.path.join(out_theme, \"assets\"))\n        shutil.copytree(\n            os.path.join(in_theme, \"assets\"), os.path.join(out_theme, \"assets\"),\n            symlinks=True)\n\n    # Copy bundles\n    # shutil.copy(os.path.join(in_theme, \"bundles\"), os.path.join(out_theme, \"bundles\"))\n\n    # Copy README\n    if os.path.isfile(os.path.join(in_theme, \"README.md\")):\n        shutil.copy(os.path.join(in_theme, \"README.md\"), os.path.join(out_theme, \"README.md\"))\n\n\ndef error(msg):\n    print(colorama.Fore.RED + \"ERROR:\" + msg)\n\n\ndef mako2jinja(input_file):\n\n    output = ''\n\n    # TODO: OMG, this code is so horrible. Look at it; just look at it:\n\n    macro_start = re.compile(r'(.*)<%.*def name=\"(.*?)\".*>(.*)', re.IGNORECASE)\n    macro_end = re.compile(r'(.*)</%def>(.*)', re.IGNORECASE)\n\n    if_start = re.compile(r'(.*)% *if (.*):(.*)', re.IGNORECASE)\n    if_else = re.compile(r'(.*)% *else.*:(.*)', re.IGNORECASE)\n    if_elif = re.compile(r'(.*)% *elif (.*):(.*)', re.IGNORECASE)\n    if_end = re.compile(r'(.*)% *endif(.*)', re.IGNORECASE)\n\n    for_start = re.compile(r'(.*)% *for (.*):(.*)', re.IGNORECASE)\n    for_end = re.compile(r'(.*)% *endfor(.*)', re.IGNORECASE)\n\n    namespace = re.compile(r'(.*)<% *namespace name=\"(.*?)\".* file=\"(.*?)\".*/>(.*)', re.IGNORECASE)\n    inherit = re.compile(r'(.*)<% *inherit file=\"(.*?)\".*/>(.*)', re.IGNORECASE)\n\n    block_single_line = re.compile(r'(.*)<% *block.*name=\"(.*?)\".*>(.*)</% *block>(.*)', re.IGNORECASE)\n    block_start = re.compile(r'(.*)<% *block.*name=\"(.*?)\".*>(.*)', re.IGNORECASE)\n    block_end = re.compile(r'(.*)</%block>(.*)', re.IGNORECASE)\n\n    val = re.compile(r'\\$\\{(.*?)\\}', re.IGNORECASE)\n    func_len = re.compile(r'len\\((.*?)\\)', re.IGNORECASE)\n    filter_h = re.compile(r'\\|h', re.IGNORECASE)\n    filter_striphtml = re.compile(r'\\|striphtml', re.IGNORECASE)\n    filter_u = re.compile(r'\\|u', re.IGNORECASE)\n\n    comment_single_line = re.compile(r'^.*##(.*?)$', re.IGNORECASE)\n\n    for line in input_file:\n\n        # Process line for repeated inline replacements\n        m_val = val.search(line)\n        m_func_len = func_len.search(line)\n        m_filter_h = filter_h.search(line)\n        m_filter_striphtml = filter_striphtml.search(line)\n        m_filter_u = filter_u.search(line)\n\n        if m_val:\n            line = val.sub(r'{{ \\1 }}', line)\n\n        if m_filter_h:\n            line = filter_h.sub(r'|e', line)\n\n        if m_filter_striphtml:\n            line = filter_striphtml.sub(r'|e', line)\n\n        if m_filter_u:\n            line = filter_u.sub(r'|urlencode', line)\n\n        if m_func_len:\n            line = func_len.sub(r'\\1|length', line)\n\n        # Process line for single 'whole line' replacements\n        m_macro_start = macro_start.search(line)\n        m_macro_end = macro_end.search(line)\n        m_if_start = if_start.search(line)\n        m_if_else = if_else.search(line)\n        m_if_elif = if_elif.search(line)\n        m_if_end = if_end.search(line)\n        m_for_start = for_start.search(line)\n        m_for_end = for_end.search(line)\n        m_namspace = namespace.search(line)\n        m_inherit = inherit.search(line)\n        m_block_single_line = block_single_line.search(line)\n        m_block_start = block_start.search(line)\n        m_block_end = block_end.search(line)\n\n        m_comment_single_line = comment_single_line.search(line)\n\n        if m_comment_single_line:\n            output += m_comment_single_line.expand(r'{# \\1 #}') + '\\n'\n\n        elif m_macro_start:\n            output += m_macro_start.expand(r'\\1{% macro \\2 %}\\3') + '\\n'\n        elif m_macro_end:\n            output += m_macro_end.expand(r'\\1{% endmacro %}\\1') + '\\n'\n\n        elif m_if_start:\n            output += m_if_start.expand(r'\\1{% if \\2 %}\\3') + '\\n'\n        elif m_if_else:\n            output += m_if_else.expand(r'\\1{% else %}\\2') + '\\n'\n        elif m_if_elif:\n            output += m_if_elif.expand(r'\\1{% elif \\2 %}\\3') + '\\n'\n        elif m_if_end:\n            output += m_if_end.expand(r'\\1{% endif %}\\2') + '\\n'\n\n        elif m_for_start:\n            output += m_for_start.expand(r'\\1{% for \\2 %}\\3') + '\\n'\n        elif m_for_end:\n            output += m_for_end.expand(r'\\1{% endfor %}\\2') + '\\n'\n\n        elif m_namspace:\n            output += m_namspace.expand(r\"\\1{% import '\\3' as \\2 with context %}\\4\") + '\\n'\n        elif m_inherit:\n            output += m_inherit.expand(r\"{% extends '\\2' %}\\3\") + '\\n'\n\n        elif m_block_single_line:\n            output += m_block_single_line.expand(r'\\1{% block \\2 %}\\3{% endblock %}\\4') + '\\n'\n        elif m_block_start:\n            output += m_block_start.expand(r'\\1{% block \\2 %}\\3') + '\\n'\n        elif m_block_end:\n            output += m_block_end.expand(r'\\1{% endblock %}\\2') + '\\n'\n\n        else:\n            # Doesn't match anything we're going to process, pass though\n            output += line\n\n    return output\n\n\ndef jinjify_shortcodes(in_dir, out_dir):\n    for fname in os.listdir(in_dir):\n        if not fname.endswith('.tmpl'):\n            continue\n        in_file = os.path.join(in_dir, fname)\n        out_file = os.path.join(out_dir, fname)\n        with open(in_file) as inf:\n            data = mako2jinja(inf)\n        with open(out_file, 'w') as outf:\n            outf.write(data)\n            \n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 1:\n        print('Performing standard conversions:')\n        for m, j in (\n            ('nikola/data/themes/base', 'nikola/data/themes/base-jinja'),\n            ('nikola/data/themes/bootstrap3', 'nikola/data/themes/bootstrap3-jinja')\n        ):\n            print('    {0} -> {1}'.format(m, j))\n            jinjify(m, j)\n        jinjify_shortcodes('nikola/data/shortcodes/mako', 'nikola/data/shortcodes/jinja')\n    elif len(sys.argv) != 3:\n        print('ERROR: needs input and output directory, or no arguments for default conversions.')\n    else:\n        jinjify(sys.argv[1], sys.argv[2])\n", "path": "scripts/jinjify.py"}], "after_files": [{"content": "#!/usr/bin/env python\nimport io\nimport glob\nimport sys\nimport os\nimport re\nimport json\nimport shutil\nimport tempfile\n\nimport colorama\nimport jinja2\n\ndumb_replacements = [\n    [\"{% if any(post.is_mathjax for post in posts) %}\", '{% if posts|selectattr(\"is_mathjax\")|list %}'],\n    [\"json.dumps(title)\", \"title|tojson\"],\n    [\"{{ parent.extra_head() }}\", \"{{ super() }}\"],\n    [\"{{ parent.content() }}\", \"{{ super() }}\"],\n    [\"prefix='\\\\\", \"prefix='\"],\n    [\"og: http://ogp.me/ns# \\\\\", \"og: http://ogp.me/ns#\"],\n    [\"article: http://ogp.me/ns/article# \\\\\", \"article: http://ogp.me/ns/article#\"],\n    [\"fb: http://ogp.me/ns/fb# \\\\\", \"fb: http://ogp.me/ns/fb#\"],\n    ['dir=\"rtl\" \\\\', 'dir=\"rtl\"'],\n    ['sorted(translations)', 'translations|sort'],\n]\n\ndumber_replacements = [\n    ['<%! import json %>\\n\\n', ''],\n    [\"<html\\n\\\\\", \"<html\\n\"],\n    [\"\\n'\\\\\\n\", \"\\n'\\n\"],\n    [\"{% endif %}\\n\\\\\", \"{% endif %}\\n\"]\n]\n\n\ndef jinjify(in_theme, out_theme):\n    \"\"\"Convert in_theme into a jinja version and put it in out_theme\"\"\"\n\n    in_templates_path = os.path.join(in_theme, \"templates\")\n    out_templates_path = os.path.join(out_theme, \"templates\")\n    try:\n        os.makedirs(out_templates_path)\n    except:\n        pass\n    lookup = jinja2.Environment()\n    lookup.filters['tojson'] = json.dumps\n    lookup.loader = jinja2.FileSystemLoader([out_templates_path], encoding='utf-8')\n    for template in glob.glob(os.path.join(in_templates_path, \"*.tmpl\")):\n        out_template = os.path.join(out_templates_path, os.path.basename(template))\n        with io.open(template, \"r\", encoding=\"utf-8\") as inf:\n            data = mako2jinja(inf)\n\n        lines = []\n        for line in data.splitlines():\n            for repl in dumb_replacements:\n                line = line.replace(*repl)\n            lines.append(line)\n        data = '\\n'.join(lines)\n\n        for repl in dumber_replacements:\n            data = data.replace(*repl)\n\n        with io.open(out_template, \"w+\", encoding=\"utf-8\") as outf:\n            outf.write(data + '\\n')\n\n        # Syntax check output\n        source, filename = lookup.loader.get_source(lookup, os.path.basename(template))[:2]\n        try:\n            lookup.parse(source)\n        except Exception as e:\n            error(\"Syntax error in {0}:{1}\".format(out_template, e.lineno))\n\n    parent = os.path.basename(in_theme.rstrip('/'))\n    child = os.path.basename(out_theme.rstrip('/'))\n    mappings = {\n        'base-jinja': 'base',\n        'bootstrap3-jinja': 'base-jinja',\n    }\n\n    if child in mappings:\n        parent = mappings[child]\n\n    with io.open(os.path.join(out_theme, \"parent\"), \"w+\", encoding='utf-8') as outf:\n        outf.write(u'{0}\\n'.format(parent))\n\n    with io.open(os.path.join(out_theme, \"engine\"), \"w+\", encoding='utf-8') as outf:\n        outf.write(u\"jinja\\n\")\n\n    # Copy assets in bootstrap/bootstrap3\n    if child == 'bootstrap3-jinja':\n        shutil.rmtree(os.path.join(out_theme, \"assets\"))\n        shutil.copytree(\n            os.path.join(in_theme, \"assets\"), os.path.join(out_theme, \"assets\"),\n            symlinks=True)\n\n    # Copy bundles\n    # shutil.copy(os.path.join(in_theme, \"bundles\"), os.path.join(out_theme, \"bundles\"))\n\n    # Copy README\n    if os.path.isfile(os.path.join(in_theme, \"README.md\")):\n        shutil.copy(os.path.join(in_theme, \"README.md\"), os.path.join(out_theme, \"README.md\"))\n\n\ndef error(msg):\n    print(colorama.Fore.RED + \"ERROR:\" + msg)\n\n\ndef mako2jinja(input_file):\n\n    output = ''\n\n    # TODO: OMG, this code is so horrible. Look at it; just look at it:\n\n    macro_start = re.compile(r'(.*)<%.*def name=\"(.*?)\".*>(.*)', re.IGNORECASE)\n    macro_end = re.compile(r'(.*)</%def>(.*)', re.IGNORECASE)\n\n    if_start = re.compile(r'(.*)% *if (.*):(.*)', re.IGNORECASE)\n    if_else = re.compile(r'(.*)% *else.*:(.*)', re.IGNORECASE)\n    if_elif = re.compile(r'(.*)% *elif (.*):(.*)', re.IGNORECASE)\n    if_end = re.compile(r'(.*)% *endif(.*)', re.IGNORECASE)\n\n    for_start = re.compile(r'(.*)% *for (.*):(.*)', re.IGNORECASE)\n    for_end = re.compile(r'(.*)% *endfor(.*)', re.IGNORECASE)\n\n    namespace = re.compile(r'(.*)<% *namespace name=\"(.*?)\".* file=\"(.*?)\".*/>(.*)', re.IGNORECASE)\n    inherit = re.compile(r'(.*)<% *inherit file=\"(.*?)\".*/>(.*)', re.IGNORECASE)\n\n    block_single_line = re.compile(r'(.*)<% *block.*name=\"(.*?)\".*>(.*)</% *block>(.*)', re.IGNORECASE)\n    block_start = re.compile(r'(.*)<% *block.*name=\"(.*?)\".*>(.*)', re.IGNORECASE)\n    block_end = re.compile(r'(.*)</%block>(.*)', re.IGNORECASE)\n\n    val = re.compile(r'\\$\\{(.*?)\\}', re.IGNORECASE)\n    func_len = re.compile(r'len\\((.*?)\\)', re.IGNORECASE)\n    filter_h = re.compile(r'\\|h', re.IGNORECASE)\n    filter_striphtml = re.compile(r'\\|striphtml', re.IGNORECASE)\n    filter_u = re.compile(r'\\|u', re.IGNORECASE)\n\n    comment_single_line = re.compile(r'^.*##(.*?)$', re.IGNORECASE)\n\n    for line in input_file:\n\n        # Process line for repeated inline replacements\n        m_val = val.search(line)\n        m_func_len = func_len.search(line)\n        m_filter_h = filter_h.search(line)\n        m_filter_striphtml = filter_striphtml.search(line)\n        m_filter_u = filter_u.search(line)\n\n        if m_val:\n            line = val.sub(r'{{ \\1 }}', line)\n\n        if m_filter_h:\n            line = filter_h.sub(r'|e', line)\n\n        if m_filter_striphtml:\n            line = filter_striphtml.sub(r'|e', line)\n\n        if m_filter_u:\n            line = filter_u.sub(r'|urlencode', line)\n\n        if m_func_len:\n            line = func_len.sub(r'\\1|length', line)\n\n        # Process line for single 'whole line' replacements\n        m_macro_start = macro_start.search(line)\n        m_macro_end = macro_end.search(line)\n        m_if_start = if_start.search(line)\n        m_if_else = if_else.search(line)\n        m_if_elif = if_elif.search(line)\n        m_if_end = if_end.search(line)\n        m_for_start = for_start.search(line)\n        m_for_end = for_end.search(line)\n        m_namspace = namespace.search(line)\n        m_inherit = inherit.search(line)\n        m_block_single_line = block_single_line.search(line)\n        m_block_start = block_start.search(line)\n        m_block_end = block_end.search(line)\n\n        m_comment_single_line = comment_single_line.search(line)\n\n        if m_comment_single_line:\n            output += m_comment_single_line.expand(r'{# \\1 #}') + '\\n'\n\n        elif m_macro_start:\n            output += m_macro_start.expand(r'\\1{% macro \\2 %}\\3') + '\\n'\n        elif m_macro_end:\n            output += m_macro_end.expand(r'\\1{% endmacro %}\\1') + '\\n'\n\n        elif m_if_start:\n            output += m_if_start.expand(r'\\1{% if \\2 %}\\3') + '\\n'\n        elif m_if_else:\n            output += m_if_else.expand(r'\\1{% else %}\\2') + '\\n'\n        elif m_if_elif:\n            output += m_if_elif.expand(r'\\1{% elif \\2 %}\\3') + '\\n'\n        elif m_if_end:\n            output += m_if_end.expand(r'\\1{% endif %}\\2') + '\\n'\n\n        elif m_for_start:\n            output += m_for_start.expand(r'\\1{% for \\2 %}\\3') + '\\n'\n        elif m_for_end:\n            output += m_for_end.expand(r'\\1{% endfor %}\\2') + '\\n'\n\n        elif m_namspace:\n            output += m_namspace.expand(r\"\\1{% import '\\3' as \\2 with context %}\\4\") + '\\n'\n        elif m_inherit:\n            output += m_inherit.expand(r\"{% extends '\\2' %}\\3\") + '\\n'\n\n        elif m_block_single_line:\n            output += m_block_single_line.expand(r'\\1{% block \\2 %}\\3{% endblock %}\\4') + '\\n'\n        elif m_block_start:\n            output += m_block_start.expand(r'\\1{% block \\2 %}\\3') + '\\n'\n        elif m_block_end:\n            output += m_block_end.expand(r'\\1{% endblock %}\\2') + '\\n'\n\n        else:\n            # Doesn't match anything we're going to process, pass though\n            output += line\n\n    return output\n\n\ndef jinjify_shortcodes(in_dir, out_dir):\n    for fname in os.listdir(in_dir):\n        if not fname.endswith('.tmpl'):\n            continue\n        in_file = os.path.join(in_dir, fname)\n        out_file = os.path.join(out_dir, fname)\n        with open(in_file) as inf:\n            data = mako2jinja(inf)\n        with open(out_file, 'w') as outf:\n            outf.write(data)\n            \n\ndef usage():\n    print(\"Usage: python {} [in-dir] [out-dir]\".format(sys.argv[0]))\n    print(\"OR\")\n    print(\"Usage: python {} [in-file] [out-file]\".format(sys.argv[0]))\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 1:\n        print('Performing standard conversions:')\n        for m, j in (\n            ('nikola/data/themes/base', 'nikola/data/themes/base-jinja'),\n            ('nikola/data/themes/bootstrap3', 'nikola/data/themes/bootstrap3-jinja')\n        ):\n            print('    {0} -> {1}'.format(m, j))\n            jinjify(m, j)\n        jinjify_shortcodes('nikola/data/shortcodes/mako', 'nikola/data/shortcodes/jinja')\n    elif len(sys.argv) != 3:\n        print('ERROR: needs input and output directory (file), or no arguments for default conversions.')\n        usage()\n    elif os.path.isdir(sys.argv[1]) and (os.path.isdir(sys.argv[2]) or not os.path.exists(sys.argv[2])):\n        jinjify(sys.argv[1], sys.argv[2])\n    elif os.path.isfile(sys.argv[1]) and (os.path.isfile(sys.argv[2]) or not os.path.exists(sys.argv[2])):\n        tmpdir = tempfile.mkdtemp()\n        indir = os.path.sep.join((tmpdir, 'in', 'templates'))\n        outdir = os.path.sep.join((tmpdir, 'out', 'templates'))\n        os.makedirs(indir)\n        shutil.copy(sys.argv[1], indir)\n        jinjify(os.path.dirname(indir), os.path.dirname(outdir))\n        shutil.move(os.path.sep.join((outdir, os.path.basename(sys.argv[1]))), sys.argv[2])\n    else:\n        print('ERROR: the two arguments must be both directories or files')\n        usage()\n", "path": "scripts/jinjify.py"}]}