-
Notifications
You must be signed in to change notification settings - Fork 298
Iris options: NetCDF #2467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iris options: NetCDF #2467
Conversation
bjlittle
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick This is great, but I'm keen to understand how this might scale as more options are added.
That is, we know that's going to happen pretty swiftly on adoption, so there will be separate option name-spaces for netcdf and parallel (or whatever for dask) ... how are these extra options added. Can you preempt that by having a common behaviour and state that can be inherited? Or is your strategy for us to refactor/rationalise at the point when a new additional option is added?
It would also be handy to be able to do
>>> iris.optionsor
>>> print(iris.options)and see what options are available. Or is your expectation for the user to do help(iris.options) ?
I've not specifically gone through your tests, in case there is fall out from my current comments.
lib/iris/options.py
Outdated
| """ | ||
| self.__dict__['conventions_override'] = conventions_override | ||
|
|
||
| def __str__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use __repr__ instead, otherwise you get this behaviour ...
>>> iris.options.netcdf
<iris.options.NetCDF object at 0x7fdc0b9f60d0>
>>> print iris.options.netcdf
NetCDF options: conventions_override=False.
>>> There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't have much success with __repr__ either 😒
lib/iris/options.py
Outdated
|
|
||
| def __str__(self): | ||
| msg = 'NetCDF options: conventions_override={}.' | ||
| return msg.format(self.conventions_override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's potential for more option attributes to come, this pattern is handy ...
msg = 'NetCDF options: conventions_override={self.conventions_override!r}.'
return msg.format(self)
lib/iris/options.py
Outdated
| import warnings | ||
|
|
||
|
|
||
| class NetCDF(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick Is this thread safe? We'll want that for dask usage ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we though? It is interesting that dask's set_options is not thread-safe. I left this like this for precisely this reason -- so that we'd get some commenting on it.
In fact I'd go so far as to argue that we don't want options thread-safe (!) -- my reason being that if I set an option I'd like it to be available in all threads, not just the calling thread. Of course, this may indicate some mixed-up thinking about how threads work, so I'm happy to stand corrected about this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we'll be driven by the behaviour. Let's see how it plays!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say if you provide it as a context manager, then it's not a one-off switch to set-and-forget.
In that case, thread-safe is definitely preferable IMHO.
lib/iris/options.py
Outdated
|
|
||
| def __setattr__(self, name, value): | ||
| if name not in self.__dict__: | ||
| msg = "'Future' object has no attribute {!r}".format(name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cut'n paste carnage ... Future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops! Good spot 😉
lib/iris/options.py
Outdated
|
|
||
| @property | ||
| def _defaults_dict(self): | ||
| return {'conventions_override': False} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the intent, but there's a slight disconnect here, in that there is a default value in the formal argument list of the __init__.
These could contradict each other ... it would be nice to specify this in one place somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True! I'll poke it and see what I can do.
lib/iris/options.py
Outdated
| if name not in self.__dict__: | ||
| msg = "'Future' object has no attribute {!r}".format(name) | ||
| raise AttributeError(msg) | ||
| if value not in [True, False]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we restricting all option attributes to be only bool?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in a "maximise work not done" sense 😉 -- in this case I only need to care about bools, so I have. I do have a plan for making this more broad, but that will appear in #2462, where I need it.
|
I fell this doesn't follow the spirit of what you mentioned
I think I'd find it much tidier to have 'iris.netcdf.options' instead of 'iris.options.netcdf', because it scales nicely : In particular, the definition and usage of the options settings relating to a given area (e.g. netcdf) are kept together and contained in the relevant place, i.e. the sub-module. I suspect we may want a top-level 'iris.options', mostly just so we can print a summary. Then you can ... Also |
|
@pp-mo I think it is tidier to have all of the options in one place (there's another one for controlling parallel processing options), and I intend them to inherit from a superclass |
Not if they are going to divide into separate self-contained areas of interest. This is very much like iris.Future... |
|
i think i am following the intent of this the ability to control low level options in these ways seems useful I am wondering how these are described for users. I think there could be some be some explanatory docs in the refdocs for config to better handle this. I also wonder whether the sample.site.cfg should include this, and perhaps remove some of the other deprecated options whilst we are there: this file is a useful reference, under source control |
|
Rebased. |
lib/iris/config.py
Outdated
| If `False` (the default), specifies that Iris should set the | ||
| CF Conventions version when saving cubes as NetCDF files. | ||
| If `True`, specifies that the cubes being saved to NetCDF should | ||
| set the cf conventions version for the saved NetCDF files. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick Apologies for being picky, but is this cf conventions or CF Conventions ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nono, good spot! My bad, I'll change (it should be the latter).
lib/iris/config.py
Outdated
| * Specify, for the lifetime of the session, that we want all cubes | ||
| written to NetCDF to define their own CF Conventions versions:: | ||
| iris.options.netcdf.conventions_override = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick This has gone state ... should now be iris.config.netcdf.conventions_override
lib/iris/config.py
Outdated
| * Specify, with a context manager, that we want a cube written to | ||
| NetCDF to define its own CF Conventions version:: | ||
| with iris.options.netcdf.context(conventions_override=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick Same reek of staleness here, thanks
| def _defaults_dict(self): | ||
| return {'conventions_override': {'default': False, | ||
| 'options': [True, False]}, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick The _defaults_dict is private to the class, so is there a real need to make this a property? I can understand making this a read-only property if it was part of the public API, but it's not, so is there a need to be this restrictive or am I missing something? (most likely)
Why can't the self._defaults_dict be created and initialised just the once in the __init__ ?
lib/iris/config.py
Outdated
| def __setattr__(self, name, value): | ||
| if name not in self.__dict__: | ||
| # Can't add new names. | ||
| msg = "'Option' object has no attribute {!r}".format(name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick This isn't an Option instance ... or are you thinking in the generic sense? If so, would it be better to say something along the lines of "{!r} option has no attribute {!r}.".format(type(self).__name__, name) ...
lib/iris/config.py
Outdated
| # anything goes. | ||
| if value not in self._defaults_dict[name]['options']: | ||
| good_value = self._defaults_dict[name]['default'] | ||
| wmsg = ('Attempting to set bad value {!r} for attribute {!r}. ' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor suggestion, bad -> invalid
Do we want to raise a warning here, which may be silently ignored and use the default, or raise a hard exception ...
|
@dkillick You have quite a few test failures. Without delving too deeply, I think that the failures might be related to the fact that the options are not thread safe? In that, a test is setting the I'll leave you to dig and confirm whether this is the case or not ... |
|
@dkillick We still need to consider how this approach is going to scale ... at the moment we will be adding all of the options into Also, as a user of iris I want an easy way to check what possible options are available to me and what specific options are set for a group i.e. what NetCDF options are set?, what regrid options are set?, what are all the options groups? Do you have any thoughts on this ... |
lib/iris/fileformats/netcdf.py
Outdated
|
|
||
| conventions = CF_CONVENTIONS_VERSION | ||
| if iris.config.netcdf.conventions_override: | ||
| conventions = cube.attributes['Conventions'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick The cube.attributes may not necessarily have any Conventions to use ...
I guess if that is the case, it seems reasonable to use the default CF_CONVENTIONS_VERSION instead rather than raise an exception ...
| # | ||
| # You should have received a copy of the GNU Lesser General Public License | ||
| # along with Iris. If not, see <http://www.gnu.org/licenses/>. | ||
| """Unit tests for the `iris.options.Paralle` class.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dkillick cut'n paste ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And a typo!
At the moment |
|
@bjlittle we have a green light!! 🚦 |
|
@dkillick Awesome, thanks! 👍 |
Adds the
optionsmodule to master (cf. #2462). In particular, this PR adds an Option to control certain aspects of NetCDF file handling, specifically what to do with a custom conventions attribute on saving a cube to a NetCDF file.In this sense this PR is proposing a very similar change to the excellent suggestion made by @morwenna-01 in #2457, but does so in the manner suggested there by @bjlittle.