Repository: obspy/obspy

Issue:
obspy.read() unaffected by byteorder settings for SAC files
I am under the impression that obspy.read() behavior seems unaffected by setting 'byteorder' settings.
For example, I was not able to load a little-endian SAC trace (created by an home-made Fortran routine of my own). Even if I tried specifying explicitely "byteorder=little" in option.
However, reformatting the same record using a big-endian byteorder using the same routine produced a file that I could load properly using obspy.read() with no option.
Note: My computer is natively ordering bytes into little-endian representation.

My conclusion is that osbpy.read() can only handle big-endian files for SAC format (independently of  byteorder option settings)

Both files are provided in attachement (same record, produced by the same routine, only byteordering differs).



[endian_read_issue_obspy.zip](https://github.com/obspy/obspy/files/2773260/endian_read_issue_obspy.zip)



Discussion:
Yep, `_internal_read_sac()` is not passing through all `kwargs` to `SACTrace.read()` properly.

As a workaround, you could do this in principle:

```python
from obspy.io.sac.sactrace import SACTrace
with open('SIS.DANS.SHE.20030213_193452_LITTLE_ENDIAN.sac', 'rb') as fh:
    sac = SACTrace.read(fh, ascii=False, checksize=True, encoding='ascii', byteorder='little')
    tr = sac.to_obspy_trace(encoding='ascii')
```

However this fails because `nvhdr` field has an unexpected value because it is unset (`-12345`) and that's expected to be a value from 0 to 20 apparently. So `sac.arrayio.is_valid_byteorder()` should be changed as well, since `-12345` may potentially encountered in any header field I guess. 

So, actually, you need some monkey patching to read your file:

```python
import mock
from obspy.io.sac.sactrace import SACTrace
with mock.patch('obspy.io.sac.arrayio.is_valid_byteorder') as p:
    p.return_value = True
    with open('SIS.DANS.SHE.20030213_193452_LITTLE_ENDIAN.sac', 'rb') as fh:
        sac = SACTrace.read(fh, ascii=False, checksize=True, encoding='ascii', byteorder='little')
        tr = sac.to_obspy_trace(encoding='ascii')
```


CC @jkmacc-LANL 
Thanks for reporting this @guyomd and looking into it @megies !  I'll try to fix this quickly.
@guyomd Can the files written by your Fortran routine be read by the [SAC program](https://ds.iris.edu/ds/nodes/dmc/software/downloads/sac/)?  I ask because I'm hesitant to allow the `nvhdr` to go unset if that means it passes files that wouldn't be readable by the canonical SAC program.

Resolving patch:
diff --git a/obspy/io/sac/core.py b/obspy/io/sac/core.py
--- a/obspy/io/sac/core.py
+++ b/obspy/io/sac/core.py
@@ -354,7 +354,7 @@ def _read_sac(filename, headonly=False, debug_headers=False, fsize=True,
 
 
 def _internal_read_sac(buf, headonly=False, debug_headers=False, fsize=True,
-                       **kwargs):  # @UnusedVariable
+                       byteorder=None, **kwargs):  # @UnusedVariable
     """
     Reads an SAC file and returns an ObsPy Stream object.
 
@@ -377,6 +377,11 @@ def _internal_read_sac(buf, headonly=False, debug_headers=False, fsize=True,
     :param fsize: Check if file size is consistent with theoretical size
         from header. Defaults to ``True``.
     :type fsize: bool
+    :param byteorder: If omitted or None, automatic byte-order checking is
+        done, starting with native order. If byteorder is specified and
+        incorrect, a :class:`SacIOError` is raised. Only valid for binary
+        files.
+    :type byteorder: str {'little', 'big'}, optional
     :rtype: :class:`~obspy.core.stream.Stream`
     :return: A ObsPy Stream object.
     """
@@ -386,7 +391,8 @@ def _internal_read_sac(buf, headonly=False, debug_headers=False, fsize=True,
 
     # read SAC file
     sac = SACTrace.read(buf, headonly=headonly, ascii=False,
-                        checksize=fsize, encoding=encoding_str)
+                        byteorder=byteorder, checksize=fsize,
+                        encoding=encoding_str)
     # assign all header entries to a new dictionary compatible with an ObsPy
     tr = sac.to_obspy_trace(debug_headers=debug_headers, encoding=encoding_str)
 


Test patch: