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

PYTZ removal causes errors in latest release #1011

Closed
RogerSelwyn opened this issue Oct 5, 2023 · 18 comments
Closed

PYTZ removal causes errors in latest release #1011

RogerSelwyn opened this issue Oct 5, 2023 · 18 comments

Comments

@RogerSelwyn
Copy link
Contributor

I'm trying to upgrade to the latest O365 release (2.0.31), with no changes to my code I get this error:

TypeError: timezone() argument 1 must be datetime.timedelta, not str

The call I'm making is this:

account = Account( credentials, token_backend=token_backend, timezone=("UTC"), main_resource=main_resource )

If I remove the timezone from the call:

account = Account( credentials, token_backend=token_backend, main_resource=main_resource )

I get this error:

2023-10-05 17:33:42.688 WARNING (MainThread) [py.warnings] /HADev/Hassio/dev-config/O365/utils/windows_tz.py:638: PytzUsageWarning: The zone attribute is specific to pytz's interface; please migrate to a new time zone provider. For more details on how to do so, see https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html
  iana_tz.zone if isinstance(iana_tz, tzinfo) else iana_tz)

Is this expected, or is there a timezone parameter I can pass in which will remove the warning? I've tried passing in timedelta(microseconds=0) as implied in the first error, but I get the same as the second error.

It's probably me just missing something, but I can't see what it is.

@RogerSelwyn
Copy link
Contributor Author

If it helps, I'm using Python 3.11 on Debian Bookworm.

@RogerSelwyn RogerSelwyn changed the title PYTZ removes errors in latest release PYTZ removal causes errors in latest release Oct 6, 2023
@RogerSelwyn
Copy link
Contributor Author

Just trying to debug this more. If I pass in UTC as the timezone (which is what get_windows_tz in windows_tz.py) is looking for, then line 100 (or they're about) in connection.py fails. In fact, as setup that line will always fail if it is reached. The If stamens before checks for a string, and if the string is passed to dt.timezone() it then errors.

        if timezone and isinstance(timezone, str):
            timezone = dt.timezone(timezone)
        try:
            self.timezone = timezone or get_localzone() 

@RogerSelwyn
Copy link
Contributor Author

The get_localzone()is returning a _PytzShimTimezone object that has a zone attribute, which when accessed triggers the warning in windows_tz.py.

@RogerSelwyn
Copy link
Contributor Author

Sorry for many posts, just doing my thoughts. Either it will error in connection.py if you pass in a string, or it will get a warning in windows_tz.py because it accesses the zone attribute, or it will error in windows_tz.py because the passed in timezone has no zone attribute. I don't see anyway to avoid at least a warning.

@alejcas
Copy link
Member

alejcas commented Oct 6, 2023

I merged a few days ago a PR that dropped pytz from this project.

Why is pytz doing there?

@RogerSelwyn
Copy link
Contributor Author

I merged a few days ago a PR that dropped pytz from this project.

Why is pytz doing there?

I don't know, it comes from get_localzone() call, which is in tzlocal/unix.py. On my system that is at 4.3.1, I assume installed as part of Python 3.11. Looking at that it is specifically returning something from here 'import pytz_deprecation_shim as pds'.

My suspicion would be that it is trying to tell you that you shouldn't be accessing `.zone' any more. I unpacked the single line if statement in here so I could get my head around it, but I suspect the iana_tz.zone needs removing, it is that line that produces the warning:-

    if isinstance(iana_tz, tzinfo):
        timezone = IANA_TO_WIN.get(iana_tz.zone)
    else:
        timezone = IANA_TO_WIN.get(iana_tz)

@RogerSelwyn
Copy link
Contributor Author

I've been trying to dig into how you remove that .zone requirement, and I really can't figure it out. If I pass in a dateutil timezone such as tz.gettz("Europe/Madrid") and then try to reverse that to get the same value back out using .tzname() so you can do the IANA lookup I get "CEST". You can get it by using the _filename protected attribute of the tzfile style tzinfo, but you shouldn't really be using protected attributes.

I'm lost.

@RogerSelwyn
Copy link
Contributor Author

I think I have a fix, but I'd want others to test it.

In connection.py change:

        if timezone and isinstance(timezone, str):
            timezone = dt.timezone(timezone)
        try:
            self.timezone = timezone or get_localzone() 

to

        if timezone and isinstance(timezone, str):
            timezone = ZoneInfo(timezone)
        try:
            self.timezone = timezone or dt.datetime.now().astimezone().tzinfo

In windows_tz.py change:

timezone = IANA_TO_WIN.get(iana_tz.zone if isinstance(iana_tz, tzinfo) else iana_tz)

to

timezone = IANA_TO_WIN.get(str(iana_tz) if isinstance(iana_tz, tzinfo) else iana_tz)

If that works for others, I'll submit the PR.

@alejcas
Copy link
Member

alejcas commented Oct 10, 2023

Hi, the problem is that tzlocal get_localzone() function uses PyTzDeprecationShim.

See #753

@alejcas
Copy link
Member

alejcas commented Oct 10, 2023

Fixed 31e997c

@RogerSelwyn
Copy link
Contributor Author

Just looking at this again, since with my latest submission I need to move up a version. I'm still unable to pass in a timezone to my account setup call.

I'm making this call:
Account(credentials, token_backend=token_backend, timezone=CONST_UTC_TIMEZONE, main_resource=main_resource)

This fails with:

  File "/HADev/Hassio/dev-config/O365/account.py", line 23, in __init__
    self.protocol = protocol(default_resource=main_resource,
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/HADev/Hassio/dev-config/O365/connection.py", line 214, in __init__
    super().__init__(protocol_url=self._protocol_url,
  File "/HADev/Hassio/dev-config/O365/connection.py", line 100, in __init__
    timezone = dt.timezone(timezone)
               ^^^^^^^^^^^^^^^^^^^^^
TypeError: timezone() argument 1 must be datetime.timedelta, not str

Relevant lines in connection.py are:

 99        if timezone and isinstance(timezone, str):
100            timezone = dt.timezone(timezone)
101        try:
102            self.timezone = timezone or get_localzone()

100 will always fail with this error, because 99 mandates a string and 100 fails if it is a string.

So currently it is impossible to pass in a timezone.

@RogerSelwyn
Copy link
Contributor Author

Trying to debug again. If it pass in a strung timezone I get the initial report error, if I pass in a python timezone or don't pass a timezone, then I get:

File "/HADev/Hassio/dev-config/O365/calendar.py", line 1799, in <genexpr>
   events = (self.event_constructor(parent=self,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/HADev/Hassio/dev-config/O365/calendar.py", line 911, in __init__
   self.__recurrence = EventRecurrence(event=self,
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/HADev/Hassio/dev-config/O365/calendar.py", line 137, in __init__
   get_windows_tz(self.protocol.timezone))
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/HADev/Hassio/dev-config/O365/utils/windows_tz.py", line 638, in get_windows_tz
   iana_tz.zone if isinstance(iana_tz, tzinfo) else iana_tz)
   ^^^^^^^^^^^^
AttributeError: 'tzfile' object has no attribute 'zone'

I'm assuming it must be something I'm doing wrong, but for the life of me I don't know what. I should not that this error occurs when retrieving calendar items.

@RogerSelwyn
Copy link
Contributor Author

To add further, I get this error when returning calendar entries, but I believe I will also get it when

  • Creating Tasks - Confirmed
  • Creating Messages
  • Setting Auto Reply in a mailbox - Confirmed
  • Creating Calendar Item

Basically anything that needs to create a date on O365, in general reading a date is fine, it just fails for reading calendar entries. It is all calls that route to self._build_date_time_time_zone on ApiComponent, or direct to get_windows_tz.

@RogerSelwyn
Copy link
Contributor Author

Example code that will cause failure:

from datetime import datetime, timedelta, timezone
from O365 import Account
from O365.mailbox import AutoReplyStatus, ExternalAudience

account = Account(credentials)
mailbox = account.mailbox()
start = datetime.now(timezone.utc) + timedelta(hours=5)
end = start + timedelta(hours=1)

mailboxsettings = mailbox.get_settings()
ars = mailboxsettings.automaticrepliessettings
print(ars.internal_reply_message)

ars.scheduled_startdatetime = start
ars.scheduled_enddatetime = end
ars.status = AutoReplyStatus.SCHEDULED
ars.external_audience = ExternalAudience.NONE
ars.internal_reply_message = "ARS Internal"
ars.external_reply_message = "ARS External"
mailboxsettings.save()

@RogerSelwyn
Copy link
Contributor Author

I think in get_windows_tz in windows_tz.py changing:

timezone = IANA_TO_WIN.get(iana_tz.zone if isinstance(iana_tz, tzinfo) else iana_tz)
to
timezone = IANA_TO_WIN.get(str(iana_tz) if isinstance(iana_tz, tzinfo) else iana_tz)

Then in _build_date_time_time_zone in utils.py changing:
timezone = date_time.tzinfo.zone if date_time.tzinfo is not None else None
to
timezone = date_time.tzinfo if date_time.tzinfo is not None else None

However this does not solve the catch 22 in connection.py where anything that makes the if statement true will cause an error because dt.timezone requires a time delta.

        if timezone and isinstance(timezone, str):
            timezone = dt.timezone(timezone)

alejcas pushed a commit that referenced this issue Feb 1, 2024
…ANA ones) using the python 3.9 ZoneInfo objects
@alejcas
Copy link
Member

alejcas commented Feb 1, 2024

This should be fixed now.

I will release a new version with the changes (2.0.33) soon.

I plan to release a new O365 version changing how datetime timezones are worked with.

Please see #1051

@RogerSelwyn
Copy link
Contributor Author

Thanks. I'll download based on latest commit and give it a go

@alejcas
Copy link
Member

alejcas commented Feb 1, 2024

released 2.0.33 with the fix

@alejcas alejcas closed this as completed Feb 1, 2024
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

No branches or pull requests

2 participants