Refactor options manager - #39
Conversation
|
@JHopeCollins this is only a rough first attempt but I want to check with you on the approach. The main thing I'm doing is moving the options logic into the |
0b9e714 to
a0fd39a
Compare
|
Here's an example demonstrating what I believe to be quite unintuitive behaviour. I totally get why this isn't recommended practice, but I also think that we should always try and do the intuitive thing. @pytest.mark.skipnopetsc4py
@pytest.mark.parametrize("options_prefix", (None, "", "custom_"))
def test_commandline_options_change(caplog, options_prefix):
from petsc4py import PETSc
options = PETSc.Options()
om = petsctools.OptionsManager(
{"opt1": "value1"}, options_prefix=options_prefix
)
with om.inserted_options():
assert options[f"{om.options_prefix}opt1"] == "value1"
# Put some options in the database after the options manager is created
options[f"{om.options_prefix}opt1"] = "value2"
with om.inserted_options():
# NOTE: not 'value2' because we use the frozen value from when we first
# made the options manager
assert options[f"{om.options_prefix}opt1"] == "value1"
# the original option should be put back (currently it's dropped)
assert options[f"{om.options_prefix}opt1"] == "value2" |
| # Start building parameters from the defaults so | ||
| # that they will overwritten by any other source. | ||
| parameters = default_options | parameters | ||
|
|
||
| # The parameters to drop from the global options when we leave the | ||
| # inserted_options context. This is everything except for options | ||
| # passed on the command line. | ||
| to_delete = set(parameters.keys()) |
There was a problem hiding this comment.
Your refactor using dict piping made a mistake I made with the default options sets obvious.
The default_options were grabbed from the global options so we shouldn't delete these. For these options we even know for sure that they will be wanted elsewhere!
| # Start building parameters from the defaults so | |
| # that they will overwritten by any other source. | |
| parameters = default_options | parameters | |
| # The parameters to drop from the global options when we leave the | |
| # inserted_options context. This is everything except for options | |
| # passed on the command line. | |
| to_delete = set(parameters.keys()) | |
| # The parameters to drop from the global options when we leave the | |
| # inserted_options context. This is everything except for options | |
| # passed on the command line. | |
| to_delete = set(parameters.keys()) | |
| # Start building parameters from the defaults so | |
| # that they will overwritten by any other source. | |
| parameters = default_options | parameters |
|
|
||
| # TODO | ||
| # make sure we warn on usage if prefix is None | ||
| # and the appctx too | ||
|
|
There was a problem hiding this comment.
| # TODO | |
| # make sure we warn on usage if prefix is None | |
| # and the appctx too |
| if unsafe_prefix and not warned: | ||
| petsctools.log.warning( | ||
| "Setting options using an autogenerated prefix " | ||
| f"({options_prefix}) is unsafe" | ||
| ) | ||
| warned = True # only warn once |
There was a problem hiding this comment.
Can we collect all of the unsafe options that were passed and raise a single warning at the end that lists all of them?
| om = petsctools.OptionsManager( | ||
| default_params, options_prefix=options_prefix | ||
| ) | ||
| assert om.options_prefix == true_prefix |
There was a problem hiding this comment.
| assert om.options_prefix == true_prefix | |
| assert om.options_prefix == true_prefix, "The later tests are invalid if these prefixes do not match." |
|
|
||
| @pytest.mark.skipnopetsc4py | ||
| @pytest.mark.parametrize("options_prefix", (None, "", "custom_")) | ||
| def test_commandline_options(caplog, options_prefix): |
There was a problem hiding this comment.
Please can you add an error message to each assertion describing what failure means?
| The prefix to look up items in the global options database | ||
| (may be ``None``, in which case only entries from ``parameters`` | ||
| will be considered. |
There was a problem hiding this comment.
| The prefix to look up items in the global options database | |
| (may be ``None``, in which case the prefix is generated from default_prefix). |
| form "{default_prefix}_{n}", where n is a unique integer. Note that | ||
| because the unique integer is not stable any options passed via the | ||
| command line with a matching prefix will be ignored. |
There was a problem hiding this comment.
| form "{default_prefix}_{n}", where n is a unique integer. Note that | |
| because the unique integer is not stable the auto-generated prefix | |
| for a particular solver may change between python invocations due | |
| to changes elsewhere in the code. |
JHopeCollins
left a comment
There was a problem hiding this comment.
Generally good, this looks like it deals with the original problem and also provides more information to users.
- We will no longer interfere with the global options dictionary or attempt to hide any entries from the auto-prefixed solver.
- We will warn you if you do use the auto-generated prefix that this is unstable, where previously we would confusingly just silently ignore some options.
I had some small requests then I'm happy to approve. The biggest one is actually fixing a bug from my DefaultOptionsSet PR!
Key changes:
Closes #37
The MFE in the issue is fixed by this: