Repository: obspy/obspy

Issue:
stream deepcopy issue
taken from http://lists.obspy.org/pipermail/obspy-users/2013-April/000451.html
## 

hello all,

i have some issues with deep copies of stream objects.

it looks like format-specific dicts are "linked" in deep copy operations instead of being duplicated.

this happened after some obspy upgrades, since i'm pretty sure my code using deep copy functions worked correctly (and was not changed) until some months ago.

here's a sample session.

``` python
$ ipython
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
Type "copyright", "credits" or "license" for more information.

IPython 0.10 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: from obspy import read
In [2]: from obspy.arclink.client import Client
In [3]: from obspy.core import UTCDateTime
In [4]: client = Client(user="test@obspy.de")
In [5]: t = UTCDateTime("2009-08-24 00:20:03")

# let's grab some data first:
In [6]: st = client.getWaveform('BW', 'RJOB', '', 'EHZ', t, t + 30,
route=False)
In [7]: st[0].stats
Out[7]: Stats({'network': 'BW', '_format': 'MSEED', 'mseed': AttribDict({'dataquality': 'D', 'record_length': 512, 'byteorder': '>', 'encoding': 'STEIM1'}), 'sampling_rate': 200.0, 'station': 'RJOB', 'location': '', 'starttime': UTCDateTime(2009, 8, 24, 0, 20, 3), 'delta': 0.0050000000000000001, 'calib': 1.0, 'npts': 6001, 'endtime': UTCDateTime(2009, 8, 24, 0, 20, 33), 'channel': 'EHZ'})

# here comes the weird stuff:
In [8]: from copy import deepcopy as copy_deepcopy
In [9]: ct = copy_deepcopy(st)
In [10]: st[0].stats.mseed.dataquality = 'X'
In [11]: st[0].stats.mseed.dataquality
Out[11]: 'X'
In [12]: ct[0].stats.mseed.dataquality
Out[12]: 'X'

# BUT, dict data in common header is deep-copied as expected:
In [13]: st[0].stats.network = 'XX'
In [14]: st[0].stats.network
Out[14]: 'XX'
In [15]: ct[0].stats.network
Out[15]: 'BW'
```



Discussion:

Resolving patch:
diff --git a/obspy/core/util/attribdict.py b/obspy/core/util/attribdict.py
--- a/obspy/core/util/attribdict.py
+++ b/obspy/core/util/attribdict.py
@@ -10,6 +10,7 @@
 """
 
 import collections
+import copy
 
 
 class AttribDict(collections.MutableMapping):
@@ -95,11 +96,11 @@ def __setstate__(self, adict):
     __delattr__ = __delitem__
 
     def copy(self):
-        return self.__class__(self.__dict__.copy())
+        return copy.deepcopy(self)
 
     def __deepcopy__(self, *args, **kwargs):  # @UnusedVariable
         ad = self.__class__()
-        ad.update(self.__dict__)
+        ad.update(copy.deepcopy(self.__dict__))
         return ad
 
     def update(self, adict={}):


Test patch:
diff --git a/obspy/core/tests/test_stream.py b/obspy/core/tests/test_stream.py
--- a/obspy/core/tests/test_stream.py
+++ b/obspy/core/tests/test_stream.py
@@ -2,10 +2,10 @@
 from copy import deepcopy
 from obspy import UTCDateTime, Stream, Trace, read
 from obspy.core.stream import writePickle, readPickle, isPickle
+from obspy.core.util.attribdict import AttribDict
 from obspy.core.util.base import NamedTemporaryFile
 import cPickle
 import numpy as np
-import os
 import pickle
 import unittest
 import warnings
@@ -1717,7 +1717,7 @@ def test_plot(self):
         Tests plot method if matplotlib is installed
         """
         try:
-            import matplotlib
+            import matplotlib  # @UnusedImport
         except ImportError:
             return
         self.mseed_stream.plot(show=False)
@@ -1727,11 +1727,34 @@ def test_spectrogram(self):
         Tests spectrogram method if matplotlib is installed
         """
         try:
-            import matplotlib
+            import matplotlib  # @UnusedImport
         except ImportError:
             return
         self.mseed_stream.spectrogram(show=False)
 
+    def test_deepcopy(self):
+        """
+        Tests __deepcopy__ method.
+
+        http://lists.obspy.org/pipermail/obspy-users/2013-April/000451.html
+        """
+        # example stream
+        st = read()
+        # set a common header
+        st[0].stats.network = 'AA'
+        # set format specific header
+        st[0].stats.mseed = AttribDict(dataquality='A')
+        ct = deepcopy(st)
+        # common header
+        st[0].stats.network = 'XX'
+        self.assertEquals(st[0].stats.network, 'XX')
+        self.assertEquals(ct[0].stats.network, 'AA')
+        # format specific headers
+        st[0].stats.mseed.dataquality = 'X'
+        self.assertEquals(st[0].stats.mseed.dataquality, 'X')
+        # XXX: this fails atm
+        self.assertEquals(ct[0].stats.mseed.dataquality, 'A')
+
 
 def suite():
     return unittest.makeSuite(StreamTestCase, 'test')
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
@@ -1099,7 +1099,7 @@ def test_plot(self):
         Tests plot method if matplotlib is installed
         """
         try:
-            import matplotlib
+            import matplotlib  # @UnusedImport
         except ImportError:
             return
         tr = Trace(data=np.arange(25))
@@ -1110,7 +1110,7 @@ def test_spectrogram(self):
         Tests spectrogram method if matplotlib is installed
         """
         try:
-            import matplotlib
+            import matplotlib  # @UnusedImport
         except ImportError:
             return
         tr = Trace(data=np.arange(25))