Skip to content
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

Enable showing DeprecationWarning in debug_on and add unit test #1554

Merged
merged 8 commits into from Mar 4, 2021

Conversation

gerritholl
Copy link
Collaborator

Extend the functionality of debug_on to switch on the visibility of DeprecationWarning using the default filter (as opposed to only showing them in __main__, which is the default Python behaviour in current and recent Python versions). Also add a unit test for debug_on.

Add a unit test for debug_on, including the new functionality to add
deprecation warnings.
Linter says docstring should be on one line.  Let it be so.
@ghost
Copy link

ghost commented Feb 19, 2021

Congratulations 🎉. DeepCode analyzed your code in 2.471 seconds and we found no issues. Enjoy a moment of no bugs ☀️.

👉 View analysis in DeepCode’s Dashboard | Configure the bot

Copy link
Member

@mraspaud mraspaud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for taking the time. Some tests are failing though.

satpy/tests/test_utils.py Show resolved Hide resolved
@gerritholl
Copy link
Collaborator Author

Hmm, I thought the tests were passing when I ran them locally :-/ I well check this.

Add a debug_off function that switches debugging off.  Use this function
when testing debug_on so it doesn't affect the behaviour of other unit
tests.  The GAC reader ones are still failing for reasons I don't
understand yet.
satpy/tests/test_utils.py Show resolved Hide resolved
satpy/utils.py Show resolved Hide resolved
satpy/utils.py Show resolved Hide resolved
@gerritholl
Copy link
Collaborator Author

I am deeply confused how this PR can cause the GACLAC reader tests to fail in the way they do.

@gerritholl
Copy link
Collaborator Author

I have no clue why, but with this PR, these import statements all return in mocks, causing a test failure:

def test_init(self):
"""Test GACLACFile initialization."""
from pygac.gac_klm import GACKLMReader
from pygac.gac_pod import GACPODReader
from pygac.lac_klm import LACKLMReader
from pygac.lac_pod import LACPODReader

Any clue why that would happen with my PR, which is just doing stuff with warning filters and loggers?

@codecov
Copy link

codecov bot commented Feb 24, 2021

Codecov Report

Merging #1554 (aaeffae) into master (d556c80) will increase coverage by 0.03%.
The diff coverage is 91.46%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1554      +/-   ##
==========================================
+ Coverage   92.54%   92.58%   +0.03%     
==========================================
  Files         251      251              
  Lines       36761    36828      +67     
==========================================
+ Hits        34022    34097      +75     
+ Misses       2739     2731       -8     
Flag Coverage Δ
behaviourtests 4.52% <19.51%> (+0.02%) ⬆️
unittests 92.72% <91.46%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
satpy/resample.py 89.50% <ø> (ø)
satpy/scene.py 91.65% <50.00%> (-0.61%) ⬇️
satpy/composites/__init__.py 88.28% <100.00%> (+0.01%) ⬆️
satpy/tests/test_composites.py 99.87% <100.00%> (+<0.01%) ⬆️
satpy/tests/test_regressions.py 100.00% <100.00%> (ø)
satpy/tests/test_scene.py 99.72% <100.00%> (ø)
satpy/tests/test_utils.py 100.00% <100.00%> (ø)
satpy/utils.py 88.88% <100.00%> (+6.93%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d556c80...fbeed6e. Read the comment docs.

@pnuu
Copy link
Member

pnuu commented Feb 24, 2021

The only failing build seems to be, as far as I know, the unstable build that uses bleeding edge versions from dask, xarray, etc. master branches. And that one is allowed to fail.

@gerritholl
Copy link
Collaborator Author

I have no clue why, but with this PR, these import statements all return in mocks, causing a test failure:

def test_init(self):
"""Test GACLACFile initialization."""
from pygac.gac_klm import GACKLMReader
from pygac.gac_pod import GACPODReader
from pygac.lac_klm import LACKLMReader
from pygac.lac_pod import LACPODReader

Any clue why that would happen with my PR, which is just doing stuff with warning filters and loggers?

These failures happen on my local machine if and only if local pygac is installed, and I run the new tests:

pytest test_utils.py reader_tests/test_avhrr_l1b_gaclac.py (or a generic pytest) while pygac is installed gives me:

================================================================================================================== short test summary info ===================================================================================================================
FAILED reader_tests/test_avhrr_l1b_gaclac.py::TestGACLACFile::test__slice - ValueError: Given start line 5 exceeds scanline range 2                                                                                                                          
FAILED reader_tests/test_avhrr_l1b_gaclac.py::TestGACLACFile::test_init - AssertionError: <class 'pygac.gac_pod.GACPODReader'> is not <MagicMock name='mock.gac_pod.GACPODReader' id='140567597024208'> : Wrong reader class assigned to NSS.GHRR.NA.D79184...
FAILED reader_tests/test_avhrr_l1b_gaclac.py::TestGACLACFile::test_strip_invalid_lat - TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''  
========================================================================================================= 3 failed, 21 passed, 41 warnings in 6.24s ==========================================================================================================

pytest reader_test/test_avhrr_l1b_gaclac.py — all tests pass.

@mraspaud
Copy link
Member

I think this deserves it's own issue. Creating now.

Copy link
Member

@mraspaud mraspaud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments for clarity, otherwise it looks good.

satpy/utils.py Outdated
_warning_manager = _WarningManager()


def dep_warnings_on():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dep can be missleading (I was thinking dependency, as in warnings from other libraries), maybe just rename to deprecation_warnings_on for clarity?

satpy/utils.py Outdated
_warning_manager.filt = warnings.filters[0]


def dep_warnings_off():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too.

Comment on lines +103 to +109
class _WarningManager:
"""Class to handle switching warnings on and off."""

filt = None


_warning_manager = _WarningManager()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need a class here? would it work with just a module variable, eg _warning_filter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would have to be a global variable. I personally hate global variables. I find them confusing, error-prone, and hard to test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a way, isn't _warning_manager also a global variable? Yes, it's a class instance, but everything in Python is a class instance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a way yes, and in a way no. There is no global keyword, and I'm not changing somebody else's namespace; rather, I'm just changing the state of a mutable object that happens to be in the module namespace. The difference is subtle and in this case largely semantic, but if somebody did have the idea to use their own reference to _warning_manager (hopefully for testing purposes) it would be affected by changes, unlike a global variable which would be a name bound to a new object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I get your point.

Now, looking at the bigger picture, why use a class at all and not just a constant?
When doing warnings.filterwarnings("default", category=DeprecationWarning), the last filter becomes in principle ('default', None, DeprecationWarning, None, 0) (I say in principle because there could be a race condition, or are warning filters thread-safe?).
So we could have _deprecation_warning_filter = ('default', None, DeprecationWarning, None, 0) at the module level and use that in deprecation_warnings_off.
Or are you worried that the filter syntax will change in certain circumstances?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it's thread safe, let's just hope people don't rely on debug_on()...debug_off() to work reliably in threaded code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about thread-safety:

The catch_warnings manager works by replacing and then later restoring the module’s showwarning() function and internal list of filter specifications. This means the context manager is modifying global state and therefore is not thread-safe.

(last lines in https://docs.python.org/3/library/warnings.html)

Copy link
Member

@mraspaud mraspaud Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'm not thrilled with the current solution, but for lack of a better option, I say we leave it as it is.
@pnuu @djhoese any more comments on this?
Otherwise I'll merge this tonight.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No further comments from me 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not thrilled with the Python 'warnings' module, so there is no solution that I would be thrilled with.

satpy/utils.py Outdated
Comment on lines 80 to 95
@contextlib.contextmanager
def debug(dep_warnings=True):
"""Context manager to temporarily set debugging on.

Example::

>>> with satpy.utils.debug():
... code_here()

Args:
dep_warnings (Optional[bool]): Switch on deprecation warnings.
Defaults to True.
"""
debug_on()
yield
debug_off()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's really nice, great addition!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename dep_warnings to deprecation_warnings?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed it throughout now (and passed the relevant parameter in line 93...).

@mraspaud mraspaud added the enhancement code enhancements, features, improvements label Feb 25, 2021
@mraspaud mraspaud added this to the v0.26.0 milestone Feb 25, 2021
@mraspaud mraspaud merged commit fffc4c3 into pytroll:master Mar 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement code enhancements, features, improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants