Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Attr dict type check #1997
What does this PR do?
This PR enables obspy.core.util.AttribDict and subclasses to perform basic type checks when assigning values to names defined in the _types dict, which stores the name of the attribute as a key and its type as the value (tuples of types also work). If the correct type was not passed a warning will be issued and the value will be cast into the stated type (or first type if tuple is used).
It is then applied on obspy.core.trace.Stats to ensure a string or None is passed to the network, station, location, and channel codes.
from obspy.core.trace import Stats stats = Stats() stats.network = 'UU' # this works fine just as before stats.station = 1 # this issues a warning that station must be a str and casts value to '1' stats.location = None # also works fine
Why was it initiated? Any relevant Issues?
Additionally, I suggest the following changes to the AttribDict class:
This would effect
from obspy.core.util import AttribDict ad = AttribDict() ad.readonly = True # this breaks the core functionality of the AttribDict ad.some_value = 13 # raises TypeError because bool is not iterable
if, however, the user had to do this:
ad._readonly = True
it would be clear that non-public attrs were being messed with. Should I open another issue or should we discuss here?
edit: I just remembered in python sets are not immutable, updated comment accordingly.
Looks mostly good to me. Thanks!
The only things which I'm currently worried about a bit is how it deals with the different string types in Python 2 and 3. Can you please add a test that uses the
native_bytes types + also the
unicode type and test what happens? This is such a core piece of ObsPy and we have to get it right.
I wrote a test that ensures the behavior I was after in the first place: ensuring the nslc values in Stats objects do not prevent a stream from being saved as mseed. It goes something like this:
def test_casted_stats_nscl_still_writes(self): """ Ensure a Stream object that has had its nslc types cast to str can still be written as mseed. """ st = Stream(traces=read()) # set new stats with warnings.catch_warnings(record=True) as w: warnings.simplefilter('default') st.stats.network = 1 st.stats.station = 1.1 st.stats.location = b'23' st.stats.channel = None # try writing stream to io bio = io.BytesIO() st.write(bio, 'mseed')
However, st.write fails because None (stats.channel) has not attribute encode. Since the default value for the nslc codes is a blank string I don't think None should be an option (not sure why I added it). I will change the none test and remove None from the supported types dict. The way I was getting at NoneType was a bit kludgey anyhow.
I see and also did not consider this in my first review round. I second removing
Otherwise this now looks fine to me once CI passes.
@megies, personally I dislike the double underscore name mangling thing python does. A single underscore should be enough to tell users "don't mess with this unless you know what you are doing".
It would be easy to make a set of forbidden attrs, say (_defaults, _readonly, _warn_on_non_default_key, _do_not_warn_on) then ensure an exception is raised when a user tries to set one of these attributes.
So I tried renaming some of these attributes to start with a leading underscore but so many things broke when I ran the tests (>50). I am starting to think it may not be feasible at this point to change them. Even if I get all the tests passing who knows how many user code this would break...
I agree - if its too hard or invasive best don't change it in this PR here.