We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Explicit `fd.close` inpelican_quickstart.py needed?
Is there any reason we have that many explicit fd.close in `pelican/tools/pelican_quickstart.py`?

Shouldn't the close be handled by the context manager we use to open the fd in the first place? Can they be removed?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pelican/tools/pelican_quickstart.py`
Content:
```
1 #!/usr/bin/env python
2 
3 import argparse
4 import locale
5 import os
6 
7 from jinja2 import Environment, FileSystemLoader
8 
9 import pytz
10 
11 try:
12     import readline  # NOQA
13 except ImportError:
14     pass
15 
16 try:
17     import tzlocal
18     _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone
19 except ImportError:
20     _DEFAULT_TIMEZONE = 'Europe/Rome'
21 
22 from pelican import __version__
23 
24 locale.setlocale(locale.LC_ALL, '')
25 try:
26     _DEFAULT_LANGUAGE = locale.getlocale()[0]
27 except ValueError:
28     # Don't fail on macosx: "unknown locale: UTF-8"
29     _DEFAULT_LANGUAGE = None
30 if _DEFAULT_LANGUAGE is None:
31     _DEFAULT_LANGUAGE = 'en'
32 else:
33     _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split('_')[0]
34 
35 _TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
36                               "templates")
37 _jinja_env = Environment(
38     loader=FileSystemLoader(_TEMPLATES_DIR),
39     trim_blocks=True,
40 )
41 
42 
43 _GITHUB_PAGES_BRANCHES = {
44     'personal': 'main',
45     'project': 'gh-pages'
46 }
47 
48 CONF = {
49     'pelican': 'pelican',
50     'pelicanopts': '',
51     'basedir': os.curdir,
52     'ftp_host': 'localhost',
53     'ftp_user': 'anonymous',
54     'ftp_target_dir': '/',
55     'ssh_host': 'localhost',
56     'ssh_port': 22,
57     'ssh_user': 'root',
58     'ssh_target_dir': '/var/www',
59     's3_bucket': 'my_s3_bucket',
60     'cloudfiles_username': 'my_rackspace_username',
61     'cloudfiles_api_key': 'my_rackspace_api_key',
62     'cloudfiles_container': 'my_cloudfiles_container',
63     'dropbox_dir': '~/Dropbox/Public/',
64     'github_pages_branch': _GITHUB_PAGES_BRANCHES['project'],
65     'default_pagination': 10,
66     'siteurl': '',
67     'lang': _DEFAULT_LANGUAGE,
68     'timezone': _DEFAULT_TIMEZONE
69 }
70 
71 # url for list of valid timezones
72 _TZ_URL = 'https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'
73 
74 
75 # Create a 'marked' default path, to determine if someone has supplied
76 # a path on the command-line.
77 class _DEFAULT_PATH_TYPE(str):
78     is_default_path = True
79 
80 
81 _DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir)
82 
83 
84 def ask(question, answer=str, default=None, length=None):
85     if answer == str:
86         r = ''
87         while True:
88             if default:
89                 r = input('> {} [{}] '.format(question, default))
90             else:
91                 r = input('> {} '.format(question))
92 
93             r = r.strip()
94 
95             if len(r) <= 0:
96                 if default:
97                     r = default
98                     break
99                 else:
100                     print('You must enter something')
101             else:
102                 if length and len(r) != length:
103                     print('Entry must be {} characters long'.format(length))
104                 else:
105                     break
106 
107         return r
108 
109     elif answer == bool:
110         r = None
111         while True:
112             if default is True:
113                 r = input('> {} (Y/n) '.format(question))
114             elif default is False:
115                 r = input('> {} (y/N) '.format(question))
116             else:
117                 r = input('> {} (y/n) '.format(question))
118 
119             r = r.strip().lower()
120 
121             if r in ('y', 'yes'):
122                 r = True
123                 break
124             elif r in ('n', 'no'):
125                 r = False
126                 break
127             elif not r:
128                 r = default
129                 break
130             else:
131                 print("You must answer 'yes' or 'no'")
132         return r
133     elif answer == int:
134         r = None
135         while True:
136             if default:
137                 r = input('> {} [{}] '.format(question, default))
138             else:
139                 r = input('> {} '.format(question))
140 
141             r = r.strip()
142 
143             if not r:
144                 r = default
145                 break
146 
147             try:
148                 r = int(r)
149                 break
150             except ValueError:
151                 print('You must enter an integer')
152         return r
153     else:
154         raise NotImplementedError(
155             'Argument `answer` must be str, bool, or integer')
156 
157 
158 def ask_timezone(question, default, tzurl):
159     """Prompt for time zone and validate input"""
160     lower_tz = [tz.lower() for tz in pytz.all_timezones]
161     while True:
162         r = ask(question, str, default)
163         r = r.strip().replace(' ', '_').lower()
164         if r in lower_tz:
165             r = pytz.all_timezones[lower_tz.index(r)]
166             break
167         else:
168             print('Please enter a valid time zone:\n'
169                   ' (check [{}])'.format(tzurl))
170     return r
171 
172 
173 def main():
174     parser = argparse.ArgumentParser(
175         description="A kickstarter for Pelican",
176         formatter_class=argparse.ArgumentDefaultsHelpFormatter)
177     parser.add_argument('-p', '--path', default=_DEFAULT_PATH,
178                         help="The path to generate the blog into")
179     parser.add_argument('-t', '--title', metavar="title",
180                         help='Set the title of the website')
181     parser.add_argument('-a', '--author', metavar="author",
182                         help='Set the author name of the website')
183     parser.add_argument('-l', '--lang', metavar="lang",
184                         help='Set the default web site language')
185 
186     args = parser.parse_args()
187 
188     print('''Welcome to pelican-quickstart v{v}.
189 
190 This script will help you create a new Pelican-based website.
191 
192 Please answer the following questions so this script can generate the files
193 needed by Pelican.
194 
195     '''.format(v=__version__))
196 
197     project = os.path.join(
198         os.environ.get('VIRTUAL_ENV', os.curdir), '.project')
199     no_path_was_specified = hasattr(args.path, 'is_default_path')
200     if os.path.isfile(project) and no_path_was_specified:
201         CONF['basedir'] = open(project).read().rstrip("\n")
202         print('Using project associated with current virtual environment. '
203               'Will save to:\n%s\n' % CONF['basedir'])
204     else:
205         CONF['basedir'] = os.path.abspath(os.path.expanduser(
206             ask('Where do you want to create your new web site?',
207                 answer=str, default=args.path)))
208 
209     CONF['sitename'] = ask('What will be the title of this web site?',
210                            answer=str, default=args.title)
211     CONF['author'] = ask('Who will be the author of this web site?',
212                          answer=str, default=args.author)
213     CONF['lang'] = ask('What will be the default language of this web site?',
214                        str, args.lang or CONF['lang'], 2)
215 
216     if ask('Do you want to specify a URL prefix? e.g., https://example.com  ',
217            answer=bool, default=True):
218         CONF['siteurl'] = ask('What is your URL prefix? (see '
219                               'above example; no trailing slash)',
220                               str, CONF['siteurl'])
221 
222     CONF['with_pagination'] = ask('Do you want to enable article pagination?',
223                                   bool, bool(CONF['default_pagination']))
224 
225     if CONF['with_pagination']:
226         CONF['default_pagination'] = ask('How many articles per page '
227                                          'do you want?',
228                                          int, CONF['default_pagination'])
229     else:
230         CONF['default_pagination'] = False
231 
232     CONF['timezone'] = ask_timezone('What is your time zone?',
233                                     CONF['timezone'], _TZ_URL)
234 
235     automation = ask('Do you want to generate a tasks.py/Makefile '
236                      'to automate generation and publishing?', bool, True)
237 
238     if automation:
239         if ask('Do you want to upload your website using FTP?',
240                answer=bool, default=False):
241             CONF['ftp'] = True,
242             CONF['ftp_host'] = ask('What is the hostname of your FTP server?',
243                                    str, CONF['ftp_host'])
244             CONF['ftp_user'] = ask('What is your username on that server?',
245                                    str, CONF['ftp_user'])
246             CONF['ftp_target_dir'] = ask('Where do you want to put your '
247                                          'web site on that server?',
248                                          str, CONF['ftp_target_dir'])
249         if ask('Do you want to upload your website using SSH?',
250                answer=bool, default=False):
251             CONF['ssh'] = True,
252             CONF['ssh_host'] = ask('What is the hostname of your SSH server?',
253                                    str, CONF['ssh_host'])
254             CONF['ssh_port'] = ask('What is the port of your SSH server?',
255                                    int, CONF['ssh_port'])
256             CONF['ssh_user'] = ask('What is your username on that server?',
257                                    str, CONF['ssh_user'])
258             CONF['ssh_target_dir'] = ask('Where do you want to put your '
259                                          'web site on that server?',
260                                          str, CONF['ssh_target_dir'])
261 
262         if ask('Do you want to upload your website using Dropbox?',
263                answer=bool, default=False):
264             CONF['dropbox'] = True,
265             CONF['dropbox_dir'] = ask('Where is your Dropbox directory?',
266                                       str, CONF['dropbox_dir'])
267 
268         if ask('Do you want to upload your website using S3?',
269                answer=bool, default=False):
270             CONF['s3'] = True,
271             CONF['s3_bucket'] = ask('What is the name of your S3 bucket?',
272                                     str, CONF['s3_bucket'])
273 
274         if ask('Do you want to upload your website using '
275                'Rackspace Cloud Files?', answer=bool, default=False):
276             CONF['cloudfiles'] = True,
277             CONF['cloudfiles_username'] = ask('What is your Rackspace '
278                                               'Cloud username?', str,
279                                               CONF['cloudfiles_username'])
280             CONF['cloudfiles_api_key'] = ask('What is your Rackspace '
281                                              'Cloud API key?', str,
282                                              CONF['cloudfiles_api_key'])
283             CONF['cloudfiles_container'] = ask('What is the name of your '
284                                                'Cloud Files container?',
285                                                str,
286                                                CONF['cloudfiles_container'])
287 
288         if ask('Do you want to upload your website using GitHub Pages?',
289                answer=bool, default=False):
290             CONF['github'] = True,
291             if ask('Is this your personal page (username.github.io)?',
292                    answer=bool, default=False):
293                 CONF['github_pages_branch'] = \
294                     _GITHUB_PAGES_BRANCHES['personal']
295             else:
296                 CONF['github_pages_branch'] = \
297                     _GITHUB_PAGES_BRANCHES['project']
298 
299     try:
300         os.makedirs(os.path.join(CONF['basedir'], 'content'))
301     except OSError as e:
302         print('Error: {}'.format(e))
303 
304     try:
305         os.makedirs(os.path.join(CONF['basedir'], 'output'))
306     except OSError as e:
307         print('Error: {}'.format(e))
308 
309     try:
310         with open(os.path.join(CONF['basedir'], 'pelicanconf.py'),
311                   'w', encoding='utf-8') as fd:
312             conf_python = dict()
313             for key, value in CONF.items():
314                 conf_python[key] = repr(value)
315 
316             _template = _jinja_env.get_template('pelicanconf.py.jinja2')
317             fd.write(_template.render(**conf_python))
318             fd.close()
319     except OSError as e:
320         print('Error: {}'.format(e))
321 
322     try:
323         with open(os.path.join(CONF['basedir'], 'publishconf.py'),
324                   'w', encoding='utf-8') as fd:
325             _template = _jinja_env.get_template('publishconf.py.jinja2')
326             fd.write(_template.render(**CONF))
327             fd.close()
328     except OSError as e:
329         print('Error: {}'.format(e))
330 
331     if automation:
332         try:
333             with open(os.path.join(CONF['basedir'], 'tasks.py'),
334                       'w', encoding='utf-8') as fd:
335                 _template = _jinja_env.get_template('tasks.py.jinja2')
336                 fd.write(_template.render(**CONF))
337                 fd.close()
338         except OSError as e:
339             print('Error: {}'.format(e))
340         try:
341             with open(os.path.join(CONF['basedir'], 'Makefile'),
342                       'w', encoding='utf-8') as fd:
343                 py_v = 'python3'
344                 _template = _jinja_env.get_template('Makefile.jinja2')
345                 fd.write(_template.render(py_v=py_v, **CONF))
346                 fd.close()
347         except OSError as e:
348             print('Error: {}'.format(e))
349 
350     print('Done. Your new project is available at %s' % CONF['basedir'])
351 
352 
353 if __name__ == "__main__":
354     main()
355
```
--- 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/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py
--- a/pelican/tools/pelican_quickstart.py
+++ b/pelican/tools/pelican_quickstart.py
@@ -3,6 +3,7 @@
 import argparse
 import locale
 import os
+from typing import Mapping
 
 from jinja2 import Environment, FileSystemLoader
 
@@ -170,6 +171,16 @@
     return r
 
 
+def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str):
+    try:
+        with open(os.path.join(CONF['basedir'], target_path),
+                  'w', encoding='utf-8') as fd:
+            _template = _jinja_env.get_template(tmpl_name)
+            fd.write(_template.render(**tmpl_vars))
+    except OSError as e:
+        print('Error: {}'.format(e))
+
+
 def main():
     parser = argparse.ArgumentParser(
         description="A kickstarter for Pelican",
@@ -306,46 +317,16 @@
     except OSError as e:
         print('Error: {}'.format(e))
 
-    try:
-        with open(os.path.join(CONF['basedir'], 'pelicanconf.py'),
-                  'w', encoding='utf-8') as fd:
-            conf_python = dict()
-            for key, value in CONF.items():
-                conf_python[key] = repr(value)
+    conf_python = dict()
+    for key, value in CONF.items():
+        conf_python[key] = repr(value)
+    render_jinja_template('pelicanconf.py.jinja2', conf_python, 'pelicanconf.py')
 
-            _template = _jinja_env.get_template('pelicanconf.py.jinja2')
-            fd.write(_template.render(**conf_python))
-            fd.close()
-    except OSError as e:
-        print('Error: {}'.format(e))
-
-    try:
-        with open(os.path.join(CONF['basedir'], 'publishconf.py'),
-                  'w', encoding='utf-8') as fd:
-            _template = _jinja_env.get_template('publishconf.py.jinja2')
-            fd.write(_template.render(**CONF))
-            fd.close()
-    except OSError as e:
-        print('Error: {}'.format(e))
+    render_jinja_template('publishconf.py.jinja2', CONF, 'publishconf.py')
 
     if automation:
-        try:
-            with open(os.path.join(CONF['basedir'], 'tasks.py'),
-                      'w', encoding='utf-8') as fd:
-                _template = _jinja_env.get_template('tasks.py.jinja2')
-                fd.write(_template.render(**CONF))
-                fd.close()
-        except OSError as e:
-            print('Error: {}'.format(e))
-        try:
-            with open(os.path.join(CONF['basedir'], 'Makefile'),
-                      'w', encoding='utf-8') as fd:
-                py_v = 'python3'
-                _template = _jinja_env.get_template('Makefile.jinja2')
-                fd.write(_template.render(py_v=py_v, **CONF))
-                fd.close()
-        except OSError as e:
-            print('Error: {}'.format(e))
+        render_jinja_template('tasks.py.jinja2', CONF, 'tasks.py')
+        render_jinja_template('Makefile.jinja2', CONF, 'Makefile')
 
     print('Done. Your new project is available at %s' % CONF['basedir'])

{"golden_diff": "diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py\n--- a/pelican/tools/pelican_quickstart.py\n+++ b/pelican/tools/pelican_quickstart.py\n@@ -3,6 +3,7 @@\n import argparse\n import locale\n import os\n+from typing import Mapping\n \n from jinja2 import Environment, FileSystemLoader\n \n@@ -170,6 +171,16 @@\n     return r\n \n \n+def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str):\n+    try:\n+        with open(os.path.join(CONF['basedir'], target_path),\n+                  'w', encoding='utf-8') as fd:\n+            _template = _jinja_env.get_template(tmpl_name)\n+            fd.write(_template.render(**tmpl_vars))\n+    except OSError as e:\n+        print('Error: {}'.format(e))\n+\n+\n def main():\n     parser = argparse.ArgumentParser(\n         description=\"A kickstarter for Pelican\",\n@@ -306,46 +317,16 @@\n     except OSError as e:\n         print('Error: {}'.format(e))\n \n-    try:\n-        with open(os.path.join(CONF['basedir'], 'pelicanconf.py'),\n-                  'w', encoding='utf-8') as fd:\n-            conf_python = dict()\n-            for key, value in CONF.items():\n-                conf_python[key] = repr(value)\n+    conf_python = dict()\n+    for key, value in CONF.items():\n+        conf_python[key] = repr(value)\n+    render_jinja_template('pelicanconf.py.jinja2', conf_python, 'pelicanconf.py')\n \n-            _template = _jinja_env.get_template('pelicanconf.py.jinja2')\n-            fd.write(_template.render(**conf_python))\n-            fd.close()\n-    except OSError as e:\n-        print('Error: {}'.format(e))\n-\n-    try:\n-        with open(os.path.join(CONF['basedir'], 'publishconf.py'),\n-                  'w', encoding='utf-8') as fd:\n-            _template = _jinja_env.get_template('publishconf.py.jinja2')\n-            fd.write(_template.render(**CONF))\n-            fd.close()\n-    except OSError as e:\n-        print('Error: {}'.format(e))\n+    render_jinja_template('publishconf.py.jinja2', CONF, 'publishconf.py')\n \n     if automation:\n-        try:\n-            with open(os.path.join(CONF['basedir'], 'tasks.py'),\n-                      'w', encoding='utf-8') as fd:\n-                _template = _jinja_env.get_template('tasks.py.jinja2')\n-                fd.write(_template.render(**CONF))\n-                fd.close()\n-        except OSError as e:\n-            print('Error: {}'.format(e))\n-        try:\n-            with open(os.path.join(CONF['basedir'], 'Makefile'),\n-                      'w', encoding='utf-8') as fd:\n-                py_v = 'python3'\n-                _template = _jinja_env.get_template('Makefile.jinja2')\n-                fd.write(_template.render(py_v=py_v, **CONF))\n-                fd.close()\n-        except OSError as e:\n-            print('Error: {}'.format(e))\n+        render_jinja_template('tasks.py.jinja2', CONF, 'tasks.py')\n+        render_jinja_template('Makefile.jinja2', CONF, 'Makefile')\n \n     print('Done. Your new project is available at %s' % CONF['basedir'])\n", "issue": "Explicit `fd.close` inpelican_quickstart.py needed?\nIs there any reason we have that many explicit fd.close in `pelican/tools/pelican_quickstart.py`?\r\n\r\nShouldn't the close be handled by the context manager we use to open the fd in the first place? Can they be removed?\n", "before_files": [{"content": "#!/usr/bin/env python\n\nimport argparse\nimport locale\nimport os\n\nfrom jinja2 import Environment, FileSystemLoader\n\nimport pytz\n\ntry:\n    import readline  # NOQA\nexcept ImportError:\n    pass\n\ntry:\n    import tzlocal\n    _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone\nexcept ImportError:\n    _DEFAULT_TIMEZONE = 'Europe/Rome'\n\nfrom pelican import __version__\n\nlocale.setlocale(locale.LC_ALL, '')\ntry:\n    _DEFAULT_LANGUAGE = locale.getlocale()[0]\nexcept ValueError:\n    # Don't fail on macosx: \"unknown locale: UTF-8\"\n    _DEFAULT_LANGUAGE = None\nif _DEFAULT_LANGUAGE is None:\n    _DEFAULT_LANGUAGE = 'en'\nelse:\n    _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split('_')[0]\n\n_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),\n                              \"templates\")\n_jinja_env = Environment(\n    loader=FileSystemLoader(_TEMPLATES_DIR),\n    trim_blocks=True,\n)\n\n\n_GITHUB_PAGES_BRANCHES = {\n    'personal': 'main',\n    'project': 'gh-pages'\n}\n\nCONF = {\n    'pelican': 'pelican',\n    'pelicanopts': '',\n    'basedir': os.curdir,\n    'ftp_host': 'localhost',\n    'ftp_user': 'anonymous',\n    'ftp_target_dir': '/',\n    'ssh_host': 'localhost',\n    'ssh_port': 22,\n    'ssh_user': 'root',\n    'ssh_target_dir': '/var/www',\n    's3_bucket': 'my_s3_bucket',\n    'cloudfiles_username': 'my_rackspace_username',\n    'cloudfiles_api_key': 'my_rackspace_api_key',\n    'cloudfiles_container': 'my_cloudfiles_container',\n    'dropbox_dir': '~/Dropbox/Public/',\n    'github_pages_branch': _GITHUB_PAGES_BRANCHES['project'],\n    'default_pagination': 10,\n    'siteurl': '',\n    'lang': _DEFAULT_LANGUAGE,\n    'timezone': _DEFAULT_TIMEZONE\n}\n\n# url for list of valid timezones\n_TZ_URL = 'https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'\n\n\n# Create a 'marked' default path, to determine if someone has supplied\n# a path on the command-line.\nclass _DEFAULT_PATH_TYPE(str):\n    is_default_path = True\n\n\n_DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir)\n\n\ndef ask(question, answer=str, default=None, length=None):\n    if answer == str:\n        r = ''\n        while True:\n            if default:\n                r = input('> {} [{}] '.format(question, default))\n            else:\n                r = input('> {} '.format(question))\n\n            r = r.strip()\n\n            if len(r) <= 0:\n                if default:\n                    r = default\n                    break\n                else:\n                    print('You must enter something')\n            else:\n                if length and len(r) != length:\n                    print('Entry must be {} characters long'.format(length))\n                else:\n                    break\n\n        return r\n\n    elif answer == bool:\n        r = None\n        while True:\n            if default is True:\n                r = input('> {} (Y/n) '.format(question))\n            elif default is False:\n                r = input('> {} (y/N) '.format(question))\n            else:\n                r = input('> {} (y/n) '.format(question))\n\n            r = r.strip().lower()\n\n            if r in ('y', 'yes'):\n                r = True\n                break\n            elif r in ('n', 'no'):\n                r = False\n                break\n            elif not r:\n                r = default\n                break\n            else:\n                print(\"You must answer 'yes' or 'no'\")\n        return r\n    elif answer == int:\n        r = None\n        while True:\n            if default:\n                r = input('> {} [{}] '.format(question, default))\n            else:\n                r = input('> {} '.format(question))\n\n            r = r.strip()\n\n            if not r:\n                r = default\n                break\n\n            try:\n                r = int(r)\n                break\n            except ValueError:\n                print('You must enter an integer')\n        return r\n    else:\n        raise NotImplementedError(\n            'Argument `answer` must be str, bool, or integer')\n\n\ndef ask_timezone(question, default, tzurl):\n    \"\"\"Prompt for time zone and validate input\"\"\"\n    lower_tz = [tz.lower() for tz in pytz.all_timezones]\n    while True:\n        r = ask(question, str, default)\n        r = r.strip().replace(' ', '_').lower()\n        if r in lower_tz:\n            r = pytz.all_timezones[lower_tz.index(r)]\n            break\n        else:\n            print('Please enter a valid time zone:\\n'\n                  ' (check [{}])'.format(tzurl))\n    return r\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"A kickstarter for Pelican\",\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n    parser.add_argument('-p', '--path', default=_DEFAULT_PATH,\n                        help=\"The path to generate the blog into\")\n    parser.add_argument('-t', '--title', metavar=\"title\",\n                        help='Set the title of the website')\n    parser.add_argument('-a', '--author', metavar=\"author\",\n                        help='Set the author name of the website')\n    parser.add_argument('-l', '--lang', metavar=\"lang\",\n                        help='Set the default web site language')\n\n    args = parser.parse_args()\n\n    print('''Welcome to pelican-quickstart v{v}.\n\nThis script will help you create a new Pelican-based website.\n\nPlease answer the following questions so this script can generate the files\nneeded by Pelican.\n\n    '''.format(v=__version__))\n\n    project = os.path.join(\n        os.environ.get('VIRTUAL_ENV', os.curdir), '.project')\n    no_path_was_specified = hasattr(args.path, 'is_default_path')\n    if os.path.isfile(project) and no_path_was_specified:\n        CONF['basedir'] = open(project).read().rstrip(\"\\n\")\n        print('Using project associated with current virtual environment. '\n              'Will save to:\\n%s\\n' % CONF['basedir'])\n    else:\n        CONF['basedir'] = os.path.abspath(os.path.expanduser(\n            ask('Where do you want to create your new web site?',\n                answer=str, default=args.path)))\n\n    CONF['sitename'] = ask('What will be the title of this web site?',\n                           answer=str, default=args.title)\n    CONF['author'] = ask('Who will be the author of this web site?',\n                         answer=str, default=args.author)\n    CONF['lang'] = ask('What will be the default language of this web site?',\n                       str, args.lang or CONF['lang'], 2)\n\n    if ask('Do you want to specify a URL prefix? e.g., https://example.com  ',\n           answer=bool, default=True):\n        CONF['siteurl'] = ask('What is your URL prefix? (see '\n                              'above example; no trailing slash)',\n                              str, CONF['siteurl'])\n\n    CONF['with_pagination'] = ask('Do you want to enable article pagination?',\n                                  bool, bool(CONF['default_pagination']))\n\n    if CONF['with_pagination']:\n        CONF['default_pagination'] = ask('How many articles per page '\n                                         'do you want?',\n                                         int, CONF['default_pagination'])\n    else:\n        CONF['default_pagination'] = False\n\n    CONF['timezone'] = ask_timezone('What is your time zone?',\n                                    CONF['timezone'], _TZ_URL)\n\n    automation = ask('Do you want to generate a tasks.py/Makefile '\n                     'to automate generation and publishing?', bool, True)\n\n    if automation:\n        if ask('Do you want to upload your website using FTP?',\n               answer=bool, default=False):\n            CONF['ftp'] = True,\n            CONF['ftp_host'] = ask('What is the hostname of your FTP server?',\n                                   str, CONF['ftp_host'])\n            CONF['ftp_user'] = ask('What is your username on that server?',\n                                   str, CONF['ftp_user'])\n            CONF['ftp_target_dir'] = ask('Where do you want to put your '\n                                         'web site on that server?',\n                                         str, CONF['ftp_target_dir'])\n        if ask('Do you want to upload your website using SSH?',\n               answer=bool, default=False):\n            CONF['ssh'] = True,\n            CONF['ssh_host'] = ask('What is the hostname of your SSH server?',\n                                   str, CONF['ssh_host'])\n            CONF['ssh_port'] = ask('What is the port of your SSH server?',\n                                   int, CONF['ssh_port'])\n            CONF['ssh_user'] = ask('What is your username on that server?',\n                                   str, CONF['ssh_user'])\n            CONF['ssh_target_dir'] = ask('Where do you want to put your '\n                                         'web site on that server?',\n                                         str, CONF['ssh_target_dir'])\n\n        if ask('Do you want to upload your website using Dropbox?',\n               answer=bool, default=False):\n            CONF['dropbox'] = True,\n            CONF['dropbox_dir'] = ask('Where is your Dropbox directory?',\n                                      str, CONF['dropbox_dir'])\n\n        if ask('Do you want to upload your website using S3?',\n               answer=bool, default=False):\n            CONF['s3'] = True,\n            CONF['s3_bucket'] = ask('What is the name of your S3 bucket?',\n                                    str, CONF['s3_bucket'])\n\n        if ask('Do you want to upload your website using '\n               'Rackspace Cloud Files?', answer=bool, default=False):\n            CONF['cloudfiles'] = True,\n            CONF['cloudfiles_username'] = ask('What is your Rackspace '\n                                              'Cloud username?', str,\n                                              CONF['cloudfiles_username'])\n            CONF['cloudfiles_api_key'] = ask('What is your Rackspace '\n                                             'Cloud API key?', str,\n                                             CONF['cloudfiles_api_key'])\n            CONF['cloudfiles_container'] = ask('What is the name of your '\n                                               'Cloud Files container?',\n                                               str,\n                                               CONF['cloudfiles_container'])\n\n        if ask('Do you want to upload your website using GitHub Pages?',\n               answer=bool, default=False):\n            CONF['github'] = True,\n            if ask('Is this your personal page (username.github.io)?',\n                   answer=bool, default=False):\n                CONF['github_pages_branch'] = \\\n                    _GITHUB_PAGES_BRANCHES['personal']\n            else:\n                CONF['github_pages_branch'] = \\\n                    _GITHUB_PAGES_BRANCHES['project']\n\n    try:\n        os.makedirs(os.path.join(CONF['basedir'], 'content'))\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    try:\n        os.makedirs(os.path.join(CONF['basedir'], 'output'))\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    try:\n        with open(os.path.join(CONF['basedir'], 'pelicanconf.py'),\n                  'w', encoding='utf-8') as fd:\n            conf_python = dict()\n            for key, value in CONF.items():\n                conf_python[key] = repr(value)\n\n            _template = _jinja_env.get_template('pelicanconf.py.jinja2')\n            fd.write(_template.render(**conf_python))\n            fd.close()\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    try:\n        with open(os.path.join(CONF['basedir'], 'publishconf.py'),\n                  'w', encoding='utf-8') as fd:\n            _template = _jinja_env.get_template('publishconf.py.jinja2')\n            fd.write(_template.render(**CONF))\n            fd.close()\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    if automation:\n        try:\n            with open(os.path.join(CONF['basedir'], 'tasks.py'),\n                      'w', encoding='utf-8') as fd:\n                _template = _jinja_env.get_template('tasks.py.jinja2')\n                fd.write(_template.render(**CONF))\n                fd.close()\n        except OSError as e:\n            print('Error: {}'.format(e))\n        try:\n            with open(os.path.join(CONF['basedir'], 'Makefile'),\n                      'w', encoding='utf-8') as fd:\n                py_v = 'python3'\n                _template = _jinja_env.get_template('Makefile.jinja2')\n                fd.write(_template.render(py_v=py_v, **CONF))\n                fd.close()\n        except OSError as e:\n            print('Error: {}'.format(e))\n\n    print('Done. Your new project is available at %s' % CONF['basedir'])\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "pelican/tools/pelican_quickstart.py"}], "after_files": [{"content": "#!/usr/bin/env python\n\nimport argparse\nimport locale\nimport os\nfrom typing import Mapping\n\nfrom jinja2 import Environment, FileSystemLoader\n\nimport pytz\n\ntry:\n    import readline  # NOQA\nexcept ImportError:\n    pass\n\ntry:\n    import tzlocal\n    _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone\nexcept ImportError:\n    _DEFAULT_TIMEZONE = 'Europe/Rome'\n\nfrom pelican import __version__\n\nlocale.setlocale(locale.LC_ALL, '')\ntry:\n    _DEFAULT_LANGUAGE = locale.getlocale()[0]\nexcept ValueError:\n    # Don't fail on macosx: \"unknown locale: UTF-8\"\n    _DEFAULT_LANGUAGE = None\nif _DEFAULT_LANGUAGE is None:\n    _DEFAULT_LANGUAGE = 'en'\nelse:\n    _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split('_')[0]\n\n_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),\n                              \"templates\")\n_jinja_env = Environment(\n    loader=FileSystemLoader(_TEMPLATES_DIR),\n    trim_blocks=True,\n)\n\n\n_GITHUB_PAGES_BRANCHES = {\n    'personal': 'main',\n    'project': 'gh-pages'\n}\n\nCONF = {\n    'pelican': 'pelican',\n    'pelicanopts': '',\n    'basedir': os.curdir,\n    'ftp_host': 'localhost',\n    'ftp_user': 'anonymous',\n    'ftp_target_dir': '/',\n    'ssh_host': 'localhost',\n    'ssh_port': 22,\n    'ssh_user': 'root',\n    'ssh_target_dir': '/var/www',\n    's3_bucket': 'my_s3_bucket',\n    'cloudfiles_username': 'my_rackspace_username',\n    'cloudfiles_api_key': 'my_rackspace_api_key',\n    'cloudfiles_container': 'my_cloudfiles_container',\n    'dropbox_dir': '~/Dropbox/Public/',\n    'github_pages_branch': _GITHUB_PAGES_BRANCHES['project'],\n    'default_pagination': 10,\n    'siteurl': '',\n    'lang': _DEFAULT_LANGUAGE,\n    'timezone': _DEFAULT_TIMEZONE\n}\n\n# url for list of valid timezones\n_TZ_URL = 'https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'\n\n\n# Create a 'marked' default path, to determine if someone has supplied\n# a path on the command-line.\nclass _DEFAULT_PATH_TYPE(str):\n    is_default_path = True\n\n\n_DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir)\n\n\ndef ask(question, answer=str, default=None, length=None):\n    if answer == str:\n        r = ''\n        while True:\n            if default:\n                r = input('> {} [{}] '.format(question, default))\n            else:\n                r = input('> {} '.format(question))\n\n            r = r.strip()\n\n            if len(r) <= 0:\n                if default:\n                    r = default\n                    break\n                else:\n                    print('You must enter something')\n            else:\n                if length and len(r) != length:\n                    print('Entry must be {} characters long'.format(length))\n                else:\n                    break\n\n        return r\n\n    elif answer == bool:\n        r = None\n        while True:\n            if default is True:\n                r = input('> {} (Y/n) '.format(question))\n            elif default is False:\n                r = input('> {} (y/N) '.format(question))\n            else:\n                r = input('> {} (y/n) '.format(question))\n\n            r = r.strip().lower()\n\n            if r in ('y', 'yes'):\n                r = True\n                break\n            elif r in ('n', 'no'):\n                r = False\n                break\n            elif not r:\n                r = default\n                break\n            else:\n                print(\"You must answer 'yes' or 'no'\")\n        return r\n    elif answer == int:\n        r = None\n        while True:\n            if default:\n                r = input('> {} [{}] '.format(question, default))\n            else:\n                r = input('> {} '.format(question))\n\n            r = r.strip()\n\n            if not r:\n                r = default\n                break\n\n            try:\n                r = int(r)\n                break\n            except ValueError:\n                print('You must enter an integer')\n        return r\n    else:\n        raise NotImplementedError(\n            'Argument `answer` must be str, bool, or integer')\n\n\ndef ask_timezone(question, default, tzurl):\n    \"\"\"Prompt for time zone and validate input\"\"\"\n    lower_tz = [tz.lower() for tz in pytz.all_timezones]\n    while True:\n        r = ask(question, str, default)\n        r = r.strip().replace(' ', '_').lower()\n        if r in lower_tz:\n            r = pytz.all_timezones[lower_tz.index(r)]\n            break\n        else:\n            print('Please enter a valid time zone:\\n'\n                  ' (check [{}])'.format(tzurl))\n    return r\n\n\ndef render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str):\n    try:\n        with open(os.path.join(CONF['basedir'], target_path),\n                  'w', encoding='utf-8') as fd:\n            _template = _jinja_env.get_template(tmpl_name)\n            fd.write(_template.render(**tmpl_vars))\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"A kickstarter for Pelican\",\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n    parser.add_argument('-p', '--path', default=_DEFAULT_PATH,\n                        help=\"The path to generate the blog into\")\n    parser.add_argument('-t', '--title', metavar=\"title\",\n                        help='Set the title of the website')\n    parser.add_argument('-a', '--author', metavar=\"author\",\n                        help='Set the author name of the website')\n    parser.add_argument('-l', '--lang', metavar=\"lang\",\n                        help='Set the default web site language')\n\n    args = parser.parse_args()\n\n    print('''Welcome to pelican-quickstart v{v}.\n\nThis script will help you create a new Pelican-based website.\n\nPlease answer the following questions so this script can generate the files\nneeded by Pelican.\n\n    '''.format(v=__version__))\n\n    project = os.path.join(\n        os.environ.get('VIRTUAL_ENV', os.curdir), '.project')\n    no_path_was_specified = hasattr(args.path, 'is_default_path')\n    if os.path.isfile(project) and no_path_was_specified:\n        CONF['basedir'] = open(project).read().rstrip(\"\\n\")\n        print('Using project associated with current virtual environment. '\n              'Will save to:\\n%s\\n' % CONF['basedir'])\n    else:\n        CONF['basedir'] = os.path.abspath(os.path.expanduser(\n            ask('Where do you want to create your new web site?',\n                answer=str, default=args.path)))\n\n    CONF['sitename'] = ask('What will be the title of this web site?',\n                           answer=str, default=args.title)\n    CONF['author'] = ask('Who will be the author of this web site?',\n                         answer=str, default=args.author)\n    CONF['lang'] = ask('What will be the default language of this web site?',\n                       str, args.lang or CONF['lang'], 2)\n\n    if ask('Do you want to specify a URL prefix? e.g., https://example.com  ',\n           answer=bool, default=True):\n        CONF['siteurl'] = ask('What is your URL prefix? (see '\n                              'above example; no trailing slash)',\n                              str, CONF['siteurl'])\n\n    CONF['with_pagination'] = ask('Do you want to enable article pagination?',\n                                  bool, bool(CONF['default_pagination']))\n\n    if CONF['with_pagination']:\n        CONF['default_pagination'] = ask('How many articles per page '\n                                         'do you want?',\n                                         int, CONF['default_pagination'])\n    else:\n        CONF['default_pagination'] = False\n\n    CONF['timezone'] = ask_timezone('What is your time zone?',\n                                    CONF['timezone'], _TZ_URL)\n\n    automation = ask('Do you want to generate a tasks.py/Makefile '\n                     'to automate generation and publishing?', bool, True)\n\n    if automation:\n        if ask('Do you want to upload your website using FTP?',\n               answer=bool, default=False):\n            CONF['ftp'] = True,\n            CONF['ftp_host'] = ask('What is the hostname of your FTP server?',\n                                   str, CONF['ftp_host'])\n            CONF['ftp_user'] = ask('What is your username on that server?',\n                                   str, CONF['ftp_user'])\n            CONF['ftp_target_dir'] = ask('Where do you want to put your '\n                                         'web site on that server?',\n                                         str, CONF['ftp_target_dir'])\n        if ask('Do you want to upload your website using SSH?',\n               answer=bool, default=False):\n            CONF['ssh'] = True,\n            CONF['ssh_host'] = ask('What is the hostname of your SSH server?',\n                                   str, CONF['ssh_host'])\n            CONF['ssh_port'] = ask('What is the port of your SSH server?',\n                                   int, CONF['ssh_port'])\n            CONF['ssh_user'] = ask('What is your username on that server?',\n                                   str, CONF['ssh_user'])\n            CONF['ssh_target_dir'] = ask('Where do you want to put your '\n                                         'web site on that server?',\n                                         str, CONF['ssh_target_dir'])\n\n        if ask('Do you want to upload your website using Dropbox?',\n               answer=bool, default=False):\n            CONF['dropbox'] = True,\n            CONF['dropbox_dir'] = ask('Where is your Dropbox directory?',\n                                      str, CONF['dropbox_dir'])\n\n        if ask('Do you want to upload your website using S3?',\n               answer=bool, default=False):\n            CONF['s3'] = True,\n            CONF['s3_bucket'] = ask('What is the name of your S3 bucket?',\n                                    str, CONF['s3_bucket'])\n\n        if ask('Do you want to upload your website using '\n               'Rackspace Cloud Files?', answer=bool, default=False):\n            CONF['cloudfiles'] = True,\n            CONF['cloudfiles_username'] = ask('What is your Rackspace '\n                                              'Cloud username?', str,\n                                              CONF['cloudfiles_username'])\n            CONF['cloudfiles_api_key'] = ask('What is your Rackspace '\n                                             'Cloud API key?', str,\n                                             CONF['cloudfiles_api_key'])\n            CONF['cloudfiles_container'] = ask('What is the name of your '\n                                               'Cloud Files container?',\n                                               str,\n                                               CONF['cloudfiles_container'])\n\n        if ask('Do you want to upload your website using GitHub Pages?',\n               answer=bool, default=False):\n            CONF['github'] = True,\n            if ask('Is this your personal page (username.github.io)?',\n                   answer=bool, default=False):\n                CONF['github_pages_branch'] = \\\n                    _GITHUB_PAGES_BRANCHES['personal']\n            else:\n                CONF['github_pages_branch'] = \\\n                    _GITHUB_PAGES_BRANCHES['project']\n\n    try:\n        os.makedirs(os.path.join(CONF['basedir'], 'content'))\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    try:\n        os.makedirs(os.path.join(CONF['basedir'], 'output'))\n    except OSError as e:\n        print('Error: {}'.format(e))\n\n    conf_python = dict()\n    for key, value in CONF.items():\n        conf_python[key] = repr(value)\n    render_jinja_template('pelicanconf.py.jinja2', conf_python, 'pelicanconf.py')\n\n    render_jinja_template('publishconf.py.jinja2', CONF, 'publishconf.py')\n\n    if automation:\n        render_jinja_template('tasks.py.jinja2', CONF, 'tasks.py')\n        render_jinja_template('Makefile.jinja2', CONF, 'Makefile')\n\n    print('Done. Your new project is available at %s' % CONF['basedir'])\n\n\nif __name__ == \"__main__\":\n    main()\n", "path": "pelican/tools/pelican_quickstart.py"}]}