Repository: getnikola/nikola

Issue:
[7.8.14] Timezone input "Berlin" crashes `nikola init`?
Hi!

### Environment (if reporting a bug)

**Python Version:**
2.7.14, 3.5.4 (same error)

**Nikola Version:**
7.8.14 (but also 7.8.13)

**Operating System:**
Linux

### Description:
I figured by now that "Europe/Berlin" wouldhave been the value to use. I wonder though, if this crash is a packaging issue at distro level or a bug in Nikola. I'd be happy to help fix it:
```
# nikola init
[..]
Time zone [UTC]: Berlin
[2018-04-01T17:00:31Z] WARNING: UserWarning: /usr/lib64/python3.5/site-packages/dateutil/zoneinfo/__init__.py:26: I/O error(2): No such file or directory
    WARNING: Time zone not found.  Searching list of time zones for a match.
Traceback (most recent call last):
  File "/usr/lib64/python3.5/site-packages/doit/doit_cmd.py", line 168, in run
    return command.parse_execute(args)
  File "/usr/lib64/python3.5/site-packages/doit/cmd_base.py", line 122, in parse_execute
    return self.execute(params, args)
  File "/usr/lib64/python3.5/site-packages/nikola/plugin_categories.py", line 134, in execute
    return self._execute(options, args)
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 495, in _execute
    st = self.ask_questions(target=target, demo=options.get('demo'))
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 467, in ask_questions
    query(default, toconf)
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 390, in tzhandler
    zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream())
  File "/usr/lib64/python3.5/tarfile.py", line 1548, in open
    raise ValueError("nothing to open")
ValueError: nothing to open
```
PS: dateutil is at version 2.7.2, doit at 0.29.0.
[7.8.14] Timezone input "Berlin" crashes `nikola init`?
Hi!

### Environment (if reporting a bug)

**Python Version:**
2.7.14, 3.5.4 (same error)

**Nikola Version:**
7.8.14 (but also 7.8.13)

**Operating System:**
Linux

### Description:
I figured by now that "Europe/Berlin" wouldhave been the value to use. I wonder though, if this crash is a packaging issue at distro level or a bug in Nikola. I'd be happy to help fix it:
```
# nikola init
[..]
Time zone [UTC]: Berlin
[2018-04-01T17:00:31Z] WARNING: UserWarning: /usr/lib64/python3.5/site-packages/dateutil/zoneinfo/__init__.py:26: I/O error(2): No such file or directory
    WARNING: Time zone not found.  Searching list of time zones for a match.
Traceback (most recent call last):
  File "/usr/lib64/python3.5/site-packages/doit/doit_cmd.py", line 168, in run
    return command.parse_execute(args)
  File "/usr/lib64/python3.5/site-packages/doit/cmd_base.py", line 122, in parse_execute
    return self.execute(params, args)
  File "/usr/lib64/python3.5/site-packages/nikola/plugin_categories.py", line 134, in execute
    return self._execute(options, args)
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 495, in _execute
    st = self.ask_questions(target=target, demo=options.get('demo'))
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 467, in ask_questions
    query(default, toconf)
  File "/usr/lib64/python3.5/site-packages/nikola/plugins/command/init.py", line 390, in tzhandler
    zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream())
  File "/usr/lib64/python3.5/tarfile.py", line 1548, in open
    raise ValueError("nothing to open")
ValueError: nothing to open
```
PS: dateutil is at version 2.7.2, doit at 0.29.0.


Discussion:
Works for me:

```
Please choose the correct time zone for your blog. Nikola uses the tz database.
You can find your time zone here:
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Time zone [Europe/Warsaw]: Berlin
    WARNING: Time zone not found.  Searching list of time zones for a match.
    Picking 'Europe/Berlin'.
    Current time in Europe/Berlin: 19:04:06
Use this time zone? [Y/n]
```

What distro is this? There might be an issue with their `dateutil` package. They probably removed `dateutil`’s timezone database in favor of the system-wide `/usr/share/tzinfo` database.
We're calling `dateutil.zoneinfo.getzoneinfofile_stream()` and using the return value without checking whether it's `None` (I cannot even find documentation on this function, but its code says it can return `None`). It should only return `None` if something is broken (i.e. when pkgutil cannot find or open dateutil's `dateutil-zoneinfo.tar.gz`), if I understand the code correctly, but we could still handle this case more gracefully.
Thanks for your kind reply.
It turns out my distro does not install that tarball on purpose, not sure yet, why. I have opened a [bug about it downstream](https://bugs.gentoo.org/652174) now.
> (I cannot even find documentation on this function [...])

And that's because it's **private API** and you are not supposed to use it. So fix your program to use public API instead of relying on implementation details.
@hartwork, @mgorny: does the following work on Gentoo?

    python -c 'import pprint, dateutil.zoneinfo; pprint.pprint(dateutil.zoneinfo.get_zonefile_instance().zones)'

([public, documented API](https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.get_zonefile_instance) — one of the entries should be `'Europe/Berlin': tzfile('Europe/Berlin')`)
It gives an empty list and that's something we actually need to fix.
Hm, having a quick look into the dateutil ebuild it seems that the
"dateutil-zoneinfo.tar.gz" is not installed on purpose?

Regards


Am 02.04.2018 um 11:57 schrieb Michał Górny:
>
> It gives an empty list and that's something we actually need to fix.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/getnikola/nikola/issues/3006#issuecomment-377898328>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ADF4ggxJgUXsMv6ErC_0SWSaHtBCyC8sks5tkfYUgaJpZM4TC5Ye>.
>

-- 
Michael Brakemeier
michael@brakemeier.de


Works for me:

```
Please choose the correct time zone for your blog. Nikola uses the tz database.
You can find your time zone here:
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Time zone [Europe/Warsaw]: Berlin
    WARNING: Time zone not found.  Searching list of time zones for a match.
    Picking 'Europe/Berlin'.
    Current time in Europe/Berlin: 19:04:06
Use this time zone? [Y/n]
```

What distro is this? There might be an issue with their `dateutil` package. They probably removed `dateutil`’s timezone database in favor of the system-wide `/usr/share/tzinfo` database.
We're calling `dateutil.zoneinfo.getzoneinfofile_stream()` and using the return value without checking whether it's `None` (I cannot even find documentation on this function, but its code says it can return `None`). It should only return `None` if something is broken (i.e. when pkgutil cannot find or open dateutil's `dateutil-zoneinfo.tar.gz`), if I understand the code correctly, but we could still handle this case more gracefully.
Thanks for your kind reply.
It turns out my distro does not install that tarball on purpose, not sure yet, why. I have opened a [bug about it downstream](https://bugs.gentoo.org/652174) now.
> (I cannot even find documentation on this function [...])

And that's because it's **private API** and you are not supposed to use it. So fix your program to use public API instead of relying on implementation details.
@hartwork, @mgorny: does the following work on Gentoo?

    python -c 'import pprint, dateutil.zoneinfo; pprint.pprint(dateutil.zoneinfo.get_zonefile_instance().zones)'

([public, documented API](https://dateutil.readthedocs.io/en/stable/zoneinfo.html#dateutil.zoneinfo.get_zonefile_instance) — one of the entries should be `'Europe/Berlin': tzfile('Europe/Berlin')`)
It gives an empty list and that's something we actually need to fix.
Hm, having a quick look into the dateutil ebuild it seems that the
"dateutil-zoneinfo.tar.gz" is not installed on purpose?

Regards


Am 02.04.2018 um 11:57 schrieb Michał Górny:
>
> It gives an empty list and that's something we actually need to fix.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/getnikola/nikola/issues/3006#issuecomment-377898328>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ADF4ggxJgUXsMv6ErC_0SWSaHtBCyC8sks5tkfYUgaJpZM4TC5Ye>.
>

-- 
Michael Brakemeier
michael@brakemeier.de



Resolving patch:
diff --git a/nikola/plugins/command/init.py b/nikola/plugins/command/init.py
--- a/nikola/plugins/command/init.py
+++ b/nikola/plugins/command/init.py
@@ -37,7 +37,6 @@
 import dateutil.zoneinfo
 from mako.template import Template
 from pkg_resources import resource_filename
-import tarfile
 
 import nikola
 from nikola.nikola import DEFAULT_INDEX_READ_MORE_LINK, DEFAULT_FEED_READ_MORE_LINK, LEGAL_VALUES, urlsplit, urlunsplit
@@ -384,15 +383,15 @@ def tzhandler(default, toconf):
 
                 if tz is None:
                     print("    WARNING: Time zone not found.  Searching list of time zones for a match.")
-                    zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream())
-                    zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()]
-                    if len(zonenames) == 1:
-                        tz = dateutil.tz.gettz(zonenames[0])
-                        answer = zonenames[0]
+                    all_zones = dateutil.zoneinfo.get_zonefile_instance().zones
+                    matching_zones = [zone for zone in all_zones if answer.lower() in zone.lower()]
+                    if len(matching_zones) == 1:
+                        tz = dateutil.tz.gettz(matching_zones[0])
+                        answer = matching_zones[0]
                         print("    Picking '{0}'.".format(answer))
-                    elif len(zonenames) > 1:
+                    elif len(matching_zones) > 1:
                         print("    The following time zones match your query:")
-                        print('        ' + '\n        '.join(zonenames))
+                        print('        ' + '\n        '.join(matching_zones))
                         continue
 
                 if tz is not None:


Test patch: