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

Drop pytz as a dependency #716

Closed
pganssle opened this issue Jun 7, 2020 · 8 comments · Fixed by #940
Closed

Drop pytz as a dependency #716

pganssle opened this issue Jun 7, 2020 · 8 comments · Fixed by #940
Assignees

Comments

@pganssle
Copy link

pganssle commented Jun 7, 2020

Since the introduction of PEP 495, pytz's unusual API has become more of a liability than an asset. With the introduction of PEP 615 (the standard library zoneinfo module), there is really no good reason to continue using pytz at this point, and it is probably a good time to transition away from it. This is one of the more popular projects using pytz, and I think it would be a good idea to start transitioning away.

I will note that Stuart Bishop, author and maintainer of pytz, is planning to make pytz a thin wrapper around zoneinfo at some point, and so it is probably a good idea to get ahead of the curve on this.

I took a quick look to see how easy it would be to do, and I found that the pytz-specific integration is a bit deeper than I had hoped, and a straightforward adoption of zoneinfo.ZoneInfo would run into the following problems:

  1. This project still supports Python 2.7, and backports.zoneinfo only supports Python 3.6+.
  2. The get_timezone function explicitly returns pytz zones, which (as we noted), have a different API than standard tzinfo zones.
  3. The get_next_timezone_transition function relies on private implementation details of pytz, and it is not possible to write analogous code for zoneinfo, which stores transition information in opaque C structs. Presumably this will become a problem in the future when pytz becomes a thin wrapper around zoneinfo anyway.

I think that we can solve problem 1 easily enough by adding a compatibility function that uses dateutil.tz in Python 2.7 only — dateutil.tz has similar functionality to zoneinfo.ZoneInfo.

Depending on how you feel about breaking changes and how much you intend to explicitly support the pytz interface, I think you can solve problem 2 with a thin wrapper class deprecating the pytz interface, like this:

class TzWrapper(tzinfo):
    def __init__(base_tzinfo):
        self._base_tzinfo = base_tzinfo

    def utcoffset(self, dt):
        return self._base_tzinfo.utcoffset(dt)

    def dst(self, dt):
        return self._base_tzinfo.dst(dt)

    def tzname(self, dt):
        return self._base_tzinfo.tzname(dt)

    def fromutc(self, dt):
        dt_base = dt.replace(tzinfo=self._base_tzinfo)
        dt_out = self._base_tzinfo.fromutc(dt_base)
        return dt_out.replace(tzinfo=self)

    def localize(self, dt, is_dst=False):
        raise_pytz_warning()
        # Some more involved code to map is_dst to fold goes here
        ...
        return dt.replace(tzinfo=self, fold=fold)

    def normalize(self, dt):
        raise_pytz_warning()
        return dt.astimezone(self)

Where raise_pytz_warning() would raise a warning telling people to stop calling localize and normalize, as the wrapper class will be dropped in a future version.

Problem 3 is the hardest to solve and also is likely to affect you regardless of whether or not you choose to adopt backports.zoneinfo. There's no public interface on any time zone provider that allows for this, so I think your options are:

  1. Deprecate and then remove this function entirely.
  2. Make the case for why this functionality is important, in which case we can add it to zoneinfo for Python 3.10, and add it to backports.zoneinfo now.
  3. Write a TZif parser (this is actually easier than it sounds), and try to find the corresponding TZif file (this is fragile, unfortunately) to get the next transition.
  4. Don't use any existing time zone implementations and write your own that exposes this.
@pganssle
Copy link
Author

pganssle commented Jun 7, 2020

I'll note that in a search on github, the only references to get_next_timezone_transition I find are forks or vendored versions of this repository and its documentation, and this function, which is in a project that hasn't had any commits in 7 years, so there's a good chance that no one is actually using this function any longer.

@mitsuhiko
Copy link
Member

I would say that removing get_next_timezone_transition is fine. Knowing when transitions are happening is very useful but this function did not expose a sane enough interface to be useful here.

@homeworkprod
Copy link

  1. This project still supports Python 2.7, and backports.zoneinfo only supports Python 3.6+.

[…]

I think that we can solve problem 1 easily enough by adding a compatibility function that uses dateutil.tz in Python 2.7 only — dateutil.tz has similar functionality to zoneinfo.ZoneInfo.

As of 58de834, master requires at least Python 3.6, so the first problem is solved for upcoming releases.

akx added a commit to akx/babel that referenced this issue Apr 8, 2022
In preparation of removing the hard dependency on pytz

Refs python-babel#716
@akx akx self-assigned this Apr 8, 2022
@akx
Copy link
Member

akx commented Apr 8, 2022

@pganssle @mitsuhiko Following #852 (to be merged for Babel 2.10), get_next_timezone_transition() is marked deprecated with no replacement and we can 🚮 it altogether for the following version of Babel.

akx added a commit that referenced this issue Apr 8, 2022
In preparation of removing the hard dependency on pytz

Refs #716
@ds-cbo
Copy link
Contributor

ds-cbo commented Jan 3, 2023

May I ask for an update on this issue? With Django 4.x deprecating pytz in favour of zoneinfo and with Django 3.x losing LTS this year, we are looking to remove the pytz dependency altogether to avoid (accidental) incompatibilities when passing datetimes between libraries. It currently seems that Babel would be blocking that.

@mitsuhiko
Copy link
Member

I don't have an update here but I would be curious if there is an actual risk of this happening. In theory the tzinfo interface did not change (at least from my understanding) so the objects should work interchangeably.

@ds-cbo
Copy link
Contributor

ds-cbo commented Jan 4, 2023

In theory, it should indeed not matter if everyone adheres to the tzinfo interface and doesn't accidentally call .normalize or .localize anywhere, but we've already found some dependencies that do/did this. Babel seems to be in the clear here on closer inspection, though I have not tested it yet.

However, there are already some incompatibilities in timezone data, since the America/Ciudad_Juarez timezone does exist in zoneinfo but not in pytz on our systems. While we're not extremely likely to hit it in practice, Babel's get_timezone would miss it when that's passed by name from others using zoneinfo.

Would it be okay if I send in a PR to remove the hard pytz dependency (or only depend on it under python 3.9) and have Babel's get_timezone(str) default to zoneinfo and fall back to pytz to look up with? If only for the peace of mind that we don't have to actively keep track of any more future incompatibilities between these libraries.

@akx
Copy link
Member

akx commented Jan 4, 2023

@ds-cbo A pull request is very welcome.

@akx akx closed this as completed in #940 Jan 11, 2023
thephez added a commit to dashpay/docs that referenced this issue Apr 5, 2023
The latest version of Babel has an issue that causes an error when "make html" is run in some cases. Installing pytz seems to fix it, but this issue makes it seem like that dependency is undesirable: python-babel/babel#716.

Pinning to Babel 2.11.0 for now avoids local build issues.
thephez added a commit to dashpay/docs that referenced this issue Apr 5, 2023
The latest version of Babel has an issue that causes an error when "make html" is run in some cases. Installing pytz seems to fix it, but this issue makes it seem like that dependency is undesirable: python-babel/babel#716.

Pinning to Babel 2.11.0 for now avoids local build issues.
thephez added a commit to dashpay/docs that referenced this issue Apr 11, 2023
The latest version of Babel has an issue that causes an error when "make html" is run in some cases. Installing pytz seems to fix it, but this issue makes it seem like that dependency is undesirable: python-babel/babel#716.

Pinning to Babel 2.11.0 for now avoids local build issues.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants