-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Address ruff's DTZ003 rule violations #15298
Address ruff's DTZ003 rule violations #15298
Conversation
Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.
|
👋 Thank you for your draft pull request! Do you know that you can use |
Thanks! I tried this as part of #14784 but I ran into timedelta math error. Perhaps you know how to fix? Please see #14784 (comment) |
Yeah, I ran into it as well. I proposed a solution, but I will leave it up for debate when the PR is ready for review. I will write some notes on this, I hope. Thanks! |
75e0621
to
ac5c880
Compare
ac5c880
to
19736be
Compare
astropy/time/tests/test_basic.py
Outdated
# Time.datetime returns a naive `datetime` object. It is made aware in order | ||
# to properly compute timedelta. | ||
dt = ( | ||
t.datetime.replace(tzinfo=datetime.timezone.utc) - now |
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 think users need to be made aware of the necessity to do this extra step, so it needs a change log?
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.
Possibly, however I am not sure if this should be the change. A better change here might be for Time.datetime
to return a "timezone-aware" datetime by default? @mhvk what do you think should happen?
Note that I would like to see these changes merged as they should fix some of the issues we are seeing in Python 3.12 in #14784.
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.
See my comment above about now
.
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 think for now we should simply also adjust the output for TimeDatetime
to be UTC by default if the scale is UTC (keeping the present behaviour of not setting the timezone for any other scale). This would make changes at
astropy/astropy/time/formats.py
Lines 1126 to 1130 in bde1b85
if timezone is not None: | |
if self._scale != "utc": | |
raise ScaleValueError( | |
f"scale is {self._scale}, must be 'utc' when timezone is supplied." | |
) |
Essentially, one would change those lines to,
if self._scale == "utc":
if timezone is None:
timezone = timezone.utc
else:
if timezone is not None:
raise ScaleValueError(
f"scale is {self._scale}, must be 'utc' when timezone is supplied."
)
With that, your change here would no longer be required.
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.
@mhvk, I think there are more subtleties to consider. This change would break the following:
from astropy.time import Time
import datetime
t = datetime.datetime.now() # not timezone aware
a_t = Time(t) # should be the same time
a_t.datetime - t # raises the error about incompatible timezones with the proposed change, works currently
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.
Folks, I believe I am missing something, but this PR is only avoiding using calls to naive utcnow
. Astropy's Time
remains naive as before, and no API change is introduced.
Time::now()
is still using an equivalent call to utcnow
and return the same naive time.
I understand of course that fetching aware times and storing them as naive is throwing information away, but shouldn't this be addressed in a dedicated PR, which will most probably change Time
's API?
I would even be willing to put some effort on it (with some guidance from someone more experienced in astropy).
Do you folks believe there is any improvement to be done in this PR? I would not mind working on additions, but it is not clear to me what should be done from this point on, given the discussions.
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.
@hfcpeixoto , sorry for the confusion! Just replacing utcnow broke one of the tests, so that is why the changes blew up. Now it is up to the maintainers on what is the best path forward...
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.
@WilliamJamieson - I think my change would actually correct a bug, since datetime.now()
would get the local time, while Time.now().datetime
gives UTC. So, the two should generally not be equal. I think I slightly prefer to fix this to the best of our abilities.
Maybe let me ping @taldcroft too. Overall, it seems options are:
- current PR, which means updating one test (if in the test we did
now.replace(tzinfo=None)
, it would remain clear thatTime
itself did not change at all). - My update, so that
Time.datetime
always gives a timezone ifscale="utc"
; - Filter warnings in TST: Test against Python 3.12 pre-prelease #14784.
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.
@mhvk my example has nothing to do with Time.now()
, instead it considers the behavior one might expect from constructing a Time
object (a_t
) from a timezone unaware datetime
object (t
). The change you propose makes it so that Time.datetime
always (in the context of construction from Python datetime
objects) is timezone aware which is a major change from the current behavior. This change will break any existing code which computes a_t.datetime - t
where the (Python datetime) t
object is not timezone aware. I think it is perfectly reasonable for users to assume that their Time
objects are not timezone aware unless they make them so, like what is done by the Python datetime
.
The replacement of now = datetime.datetime.utcnow()
with now = datetime.datetime.now(tz=timezone.utc)
in the test is what is the actual behavior change at issue, not the change in the astropy code. This is because the change causes the now
object to be timezone aware, which causes Python to then require the other time used when computing a timedelta
with it to also be timezone aware. The fix for that issue is what lead @hfcpeixoto to using .replace
to resolve. Instead the fix to preserve what is currently being tested should be for the timezone awareness to be removed from now
not d_t
.
This leaves the question of how Time
objects can preserve timezone information which maybe encoded in a datetime
object used to construct them. Currently, there is no mechanism to do so.
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.
@hfcpeixoto, sorry for creating the confusion for you. It just so happens that this PR makes most of the changes necessary to fix some of the problems identified in #14784, so it has taken on more importance than you originally intended. Thank you for attempting to resolve these seemingly small issues, it shows the utility in using more of the ruff
rules. However, as in many cases, fixing seemingly small things reveals a larger issues.
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.
Thanks for tackling this challenging problem. Python has sub-optimal time zone support.
# call `utcnow` immediately to be sure it's ASAP | ||
dtnow = datetime.utcnow() | ||
# call `now` immediately to be sure it's ASAP | ||
dtnow = datetime.now(tz=timezone.utc) |
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 think a new (optional) argument should be added to now
that allows the user to specify the timezone.
The default value should be gotten from time.tzname
, corrected for daylight savings with 'time.localtime().tm_isdst
(see https://stackoverflow.com/questions/1111056/get-time-zone-information-of-the-system-in-python and https://bugs.python.org/issue22798).
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 think time.localtime().tm_gmtoff
will be necessary to get the offset from utc
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.
daylight savings corrections might use tzinfo.dst
instead of tm_isdst
. I haven't investigated details
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 like the idea of an optional argument, but it would be a big API change to make Time.now()
return non-UTC times, so I think the default has to be timezone.utc
. Obviously, this could also be done as follow-up, since here the idea is just to avoid the utcnow()
method. Indeed, given the need to get this in for #14784, perhaps it is best to make minimal changes here.
astropy/time/tests/test_basic.py
Outdated
# Time.datetime returns a naive `datetime` object. It is made aware in order | ||
# to properly compute timedelta. | ||
dt = ( | ||
t.datetime.replace(tzinfo=datetime.timezone.utc) - now |
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.
See my comment above about now
.
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.
Good idea, but indeed somewhat tricky. Suggestions in-line.
# call `utcnow` immediately to be sure it's ASAP | ||
dtnow = datetime.utcnow() | ||
# call `now` immediately to be sure it's ASAP | ||
dtnow = datetime.now(tz=timezone.utc) |
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 like the idea of an optional argument, but it would be a big API change to make Time.now()
return non-UTC times, so I think the default has to be timezone.utc
. Obviously, this could also be done as follow-up, since here the idea is just to avoid the utcnow()
method. Indeed, given the need to get this in for #14784, perhaps it is best to make minimal changes here.
astropy/time/tests/test_basic.py
Outdated
# Time.datetime returns a naive `datetime` object. It is made aware in order | ||
# to properly compute timedelta. | ||
dt = ( | ||
t.datetime.replace(tzinfo=datetime.timezone.utc) - now |
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 think for now we should simply also adjust the output for TimeDatetime
to be UTC by default if the scale is UTC (keeping the present behaviour of not setting the timezone for any other scale). This would make changes at
astropy/astropy/time/formats.py
Lines 1126 to 1130 in bde1b85
if timezone is not None: | |
if self._scale != "utc": | |
raise ScaleValueError( | |
f"scale is {self._scale}, must be 'utc' when timezone is supplied." | |
) |
Essentially, one would change those lines to,
if self._scale == "utc":
if timezone is None:
timezone = timezone.utc
else:
if timezone is not None:
raise ScaleValueError(
f"scale is {self._scale}, must be 'utc' when timezone is supplied."
)
With that, your change here would no longer be required.
astropy/time/tests/test_basic.py
Outdated
@@ -1475,15 +1475,19 @@ def test_now(): | |||
Tests creating a Time object with the `now` class method. | |||
""" | |||
|
|||
now = datetime.datetime.utcnow() | |||
now = datetime.datetime.now(tz=datetime.timezone.utc) |
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.
now = datetime.datetime.now(tz=datetime.timezone.utc) | |
# `Time.datetime` is not timezone aware, meaning `.replace` is necessary for | |
# `now` also not be timezone aware. | |
now = datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None) |
This is my suggestion from https://github.com/astropy/astropy/pull/15298/files#r1323005153 along side changing dt
back to:
dt = t.datetime - now
While leaving astropy.time.formats
alone. This has two advantages:
- It results in no behavioral changes from
astropy.time.Time
with respect to whatTime.datetime
is (not timezone aware). - It more accurately conveys what was actually being tested prior to these changes. That is computing "now" as UTC times, which are not timezone aware.
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, good suggestion, helps to reproduce what is there and keep the PR minimal. The rest can be in follow-up!
I opened #15311 to discuss possible future changes in how we deal with timezones. |
I didn't notice #15306 when I opened my issue for discussion; that would be the right place to continue discussion, so I closed my feature request. For this PR, I think the path forward is to follow @WilliamJamieson's #15298 (comment) |
Based on suggestion from @WilliamJamieson astropy#15298 (comment)
Thanks, @mhvk. I have added a commit based on o @WilliamJamieson's suggestion #15298 (comment). Maybe I should re-request review at this point? |
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.
With the latest change, this looks all good to me! Thanks, @hfcpeixoto!
p.s. I added "refactoring" and "no-changelog-entry-needed" since those seemed appropriate. |
All `datetime.utcnow()` calls are replaced by aware calls
Co-authored-by: Nathaniel Starkman <nstarman@users.noreply.github.com>
Based on suggestion from WilliamJamieson astropy#15298 (comment)
9526009
to
2d4f16a
Compare
@hfcpeixoto I went ahead and rebased your PR and modified your last commit message to remove the Again thank you for your timely contribution, which ended up sparking a deeper conversation than was expected. It is always nice to identify and eliminate technical debt. |
This comment was marked as resolved.
This comment was marked as resolved.
Huh, weird, back in my Python 3.12 PR, I had to patch |
Ruff only operates on pure Python files. It totally ignores any non Python files like the documentation rst files. |
Then I guess it is a little dangerous to rely solely on it to check such things? For the future, please also do a recursive |
This comment was marked as duplicate.
This comment was marked as duplicate.
@meeseeksdev backport to v5.3.x |
This comment was marked as resolved.
This comment was marked as resolved.
Backport PR #15298 on branch v5.3.x (Address ruff's DTZ003 rule violations)
Description
This pull request is to address ruff's DTZ003: "Checks for usage of datetime.datetime.utcnow()".
Replace
utcnow()
usages bynow(tz=timezone.utc)
.