Repository: obspy/obspy

Issue:
trace.Trace should not change the header
Basic info
  -  ObsPy version 1.0.3
  - Python version: 3.6
  - Platform: Windows 10

I found a problem when I was using trace.Trace to initialize a trace object. Codes are listed bellow

```python
    from obspy.core import trace
    import numpy as np
    data = [0] * 20
    header = {
        'station': 'MS',
        'starttime': None
    }
    header['starttime'] = 1
    my_trace1 = trace.Trace(data=np.array(data), header=header)
    print(my_trace1)
    data = [1] * 60
    my_trace2= trace.Trace(data=np.array(data), header=header)
    print(my_trace2)
    print(header)
```
Then I got:

```
.MS.. | 1970-01-01T00:00:01.000000Z - 1970-01-01T00:00:20.000000Z | 1.0 Hz, 20 samples
.MS.. | 1970-01-01T00:00:01.000000Z - 1970-01-01T00:00:20.000000Z | 1.0 Hz, 20 samples
{'station': 'MS', 'starttime': 1, 'npts': 20}
```

I used this script to initialize two sets of traces. What I expected of `my_trace2` is a trace of 60 samples, but I got 20. I browsed the source code of trace and found a possible reason. When Trace is initialized, the `header` would be set a default key `npts`, therefore, the sample number of `my_trace2` is not correct when I use `header` the second time.

```python
    def __init__(self, data=np.array([]), header=None):
        # make sure Trace gets initialized with suitable ndarray as self.data
        # otherwise we could end up with e.g. a list object in self.data
        _data_sanity_checks(data)
        # set some defaults if not set yet
        if header is None:
            header = {}
        header.setdefault('npts', len(data))
        self.stats = Stats(header)
        # set data without changing npts in stats object (for headonly option)
        super(Trace, self).__setattr__('data', data)
```

In my opinion, an appropriate solution is to insert a line `header = header.copy()` before `header.setdefault('npts', len(data))`.
trace.Trace should not change the header
Basic info
  -  ObsPy version 1.0.3
  - Python version: 3.6
  - Platform: Windows 10

I found a problem when I was using trace.Trace to initialize a trace object. Codes are listed bellow

```python
    from obspy.core import trace
    import numpy as np
    data = [0] * 20
    header = {
        'station': 'MS',
        'starttime': None
    }
    header['starttime'] = 1
    my_trace1 = trace.Trace(data=np.array(data), header=header)
    print(my_trace1)
    data = [1] * 60
    my_trace2= trace.Trace(data=np.array(data), header=header)
    print(my_trace2)
    print(header)
```
Then I got:

```
.MS.. | 1970-01-01T00:00:01.000000Z - 1970-01-01T00:00:20.000000Z | 1.0 Hz, 20 samples
.MS.. | 1970-01-01T00:00:01.000000Z - 1970-01-01T00:00:20.000000Z | 1.0 Hz, 20 samples
{'station': 'MS', 'starttime': 1, 'npts': 20}
```

I used this script to initialize two sets of traces. What I expected of `my_trace2` is a trace of 60 samples, but I got 20. I browsed the source code of trace and found a possible reason. When Trace is initialized, the `header` would be set a default key `npts`, therefore, the sample number of `my_trace2` is not correct when I use `header` the second time.

```python
    def __init__(self, data=np.array([]), header=None):
        # make sure Trace gets initialized with suitable ndarray as self.data
        # otherwise we could end up with e.g. a list object in self.data
        _data_sanity_checks(data)
        # set some defaults if not set yet
        if header is None:
            header = {}
        header.setdefault('npts', len(data))
        self.stats = Stats(header)
        # set data without changing npts in stats object (for headonly option)
        super(Trace, self).__setattr__('data', data)
```

In my opinion, an appropriate solution is to insert a line `header = header.copy()` before `header.setdefault('npts', len(data))`.


Discussion:

Resolving patch:
diff --git a/obspy/core/trace.py b/obspy/core/trace.py
--- a/obspy/core/trace.py
+++ b/obspy/core/trace.py
@@ -284,6 +284,7 @@ def __init__(self, data=np.array([]), header=None):
         # set some defaults if not set yet
         if header is None:
             header = {}
+        header = deepcopy(header)
         header.setdefault('npts', len(data))
         self.stats = Stats(header)
         # set data without changing npts in stats object (for headonly option)


Test patch:
diff --git a/obspy/core/tests/test_trace.py b/obspy/core/tests/test_trace.py
--- a/obspy/core/tests/test_trace.py
+++ b/obspy/core/tests/test_trace.py
@@ -2694,6 +2694,25 @@ def test_trace_contiguous(self):
         self.assertFalse(tr_opt_out.data.flags['C_CONTIGUOUS'])
         self.assertFalse(tr_opt_out.data.flags['F_CONTIGUOUS'])
 
+    def test_header_dict_copied(self):
+        """
+        Regression test for #1934 (collisions when using  the same header
+        dictionary for multiple Trace inits)
+        """
+        header = {'station': 'MS', 'starttime': 1}
+        original_header = deepcopy(header)
+
+        # Init two traces and make sure the original header did not change.
+        tr1 = Trace(data=np.ones(2), header=header)
+        self.assertEqual(header, original_header)
+        tr2 = Trace(data=np.zeros(5), header=header)
+        self.assertEqual(header, original_header)
+
+        self.assertEqual(len(tr1), 2)
+        self.assertEqual(len(tr2), 5)
+        self.assertEqual(tr1.stats.npts, 2)
+        self.assertEqual(tr2.stats.npts, 5)
+
 
 def suite():
     return unittest.makeSuite(TraceTestCase, 'test')