Repository: mne-tools/mne-python

Issue:
"dateutil" package requirement issue
1. Package "dateutil" is required to install mne but is not in requirements.txt

> ---------------------------------------------------------------------------
> ModuleNotFoundError                       Traceback (most recent call last)
> <ipython-input-1-d70f97b8aea9> in <module>
> ----> 1 import mne
> 
> /home/kmaksyme/dev/mne-python/mne/__init__.py in <module>
>      23                     get_config, get_config_path, set_cache_dir,
>      24                     set_memmap_min_size, grand_average, sys_info, open_docs)
> ---> 25 from .io.pick import (pick_types, pick_channels,
>      26                       pick_channels_regexp, pick_channels_forward,
>      27                       pick_types_forward, pick_channels_cov,
> 
> /home/kmaksyme/dev/mne-python/mne/io/__init__.py in <module>
>      23 from . import constants
>      24 from . import edf
> ---> 25 from . import egi
>      26 from . import fiff
>      27 from . import kit
> 
> /home/kmaksyme/dev/mne-python/mne/io/egi/__init__.py in <module>
>       3 # Author: Denis A. Engemann <denis.engemann@gmail.com>
>       4 
> ----> 5 from .egi import read_raw_egi
> 
> /home/kmaksyme/dev/mne-python/mne/io/egi/egi.py in <module>
>       9 import numpy as np
>      10 
> ---> 11 from .egimff import _read_raw_egi_mff
>      12 from .events import _combine_triggers
>      13 from ..base import BaseRaw, _check_update_montage
> 
> /home/kmaksyme/dev/mne-python/mne/io/egi/egimff.py in <module>
>       5 import time
>       6 from xml.dom.minidom import parse
> ----> 7 import dateutil.parser
>       8 
>       9 import numpy as np
> 
> ModuleNotFoundError: No module named 'dateutil'

2. Package "dateutil" requires  Python 2 while:

> Note that MNE-Python 0.17 will be the last release to support Python 2. From MNE-Python 0.18, only Python 3 will be supported.


Discussion:
I have no idea how this slipped through the cracks.

how about using datetime to avoid this issue?

It looks like the dep is from `xml.dom.minidom.parse` so we'd actually need to replace that
+1

Oh no I'm wrong, it is just `dateutil`. Yes we should just try to replace it

Resolving patch:
diff --git a/mne/io/egi/egimff.py b/mne/io/egi/egimff.py
--- a/mne/io/egi/egimff.py
+++ b/mne/io/egi/egimff.py
@@ -4,7 +4,6 @@
 import os.path as op
 import time
 from xml.dom.minidom import parse
-import dateutil.parser
 
 import numpy as np
 
@@ -119,6 +118,25 @@ def _read_mff_header(filepath):
     return summaryinfo
 
 
+class _FixedOffset(datetime.tzinfo):
+    """Fixed offset in minutes east from UTC.
+
+    Adapted from the official Python documentation.
+    """
+
+    def __init__(self, offset):
+        self._offset = datetime.timedelta(minutes=offset)
+
+    def utcoffset(self, dt):
+        return self._offset
+
+    def tzname(self, dt):
+        return 'MFF'
+
+    def dst(self, dt):
+        return datetime.timedelta(0)
+
+
 def _read_header(input_fname):
     """Obtain the headers from the file package mff.
 
@@ -135,7 +153,15 @@ def _read_header(input_fname):
     mff_hdr = _read_mff_header(input_fname)
     with open(input_fname + '/signal1.bin', 'rb') as fid:
         version = np.fromfile(fid, np.int32, 1)[0]
-    time_n = dateutil.parser.parse(mff_hdr['date'])
+    # This should be equivalent to the following, but no need for external dep:
+    # import dateutil.parser
+    # time_n = dateutil.parser.parse(mff_hdr['date'])
+    dt = mff_hdr['date'][:26]
+    assert mff_hdr['date'][-6] in ('+', '-')
+    sn = -1 if mff_hdr['date'][-6] == '-' else 1  # +
+    tz = [sn * int(t) for t in (mff_hdr['date'][-5:-3], mff_hdr['date'][-2:])]
+    time_n = datetime.datetime.strptime(dt, '%Y-%m-%dT%H:%M:%S.%f')
+    time_n = time_n.replace(tzinfo=_FixedOffset(60 * tz[0] + tz[1]))
     info = dict(
         version=version,
         year=int(time_n.strftime('%Y')),


Test patch: