regebro / tzlocal

A Python module that tries to figure out what your local timezone is

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

5.0.1 causues circular import with rfc5424logging.Rfc5424SysLogHandler

BalazsBago opened this issue · comments

Hi,

I am using rfc5424logging package 1.4.3 (https://rfc5424-logging-handler.readthedocs.io/en/latest/) and since 5.0.1 version of tzlocal i get the following error:

partially initialized module 'rfc5424logging' has no attribute 'Rfc5424SysLogHandler' (most likely due to a circular import)

Is there a traceback?

Thank you for your fast reaction, here is the traceback:

  import rfc5424logging
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./venv2/lib/python3.9/site-packages/rfc5424logging/__init__.py", line 1, in <module>
    from .adapter import Rfc5424SysLogAdapter, EMERGENCY, ALERT, NOTICE
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/./venv2/lib/python3.9/site-packages/rfc5424logging/adapter.py", line 2, in <module>
    from .handler import NOTICE, EMERGENCY, ALERT
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./venv2/lib/python3.9/site-packages/rfc5424logging/handler.py", line 10, in <module>
    from tzlocal import get_localzone
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./venv2/lib/python3.9/site-packages/tzlocal/__init__.py", line 10, in <module>
    from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./venv2/lib/python3.9/site-packages/tzlocal/unix.py", line 8, in <module>
    from tzlocal import utils
  File "<frozen importlib._bootstrap>", line 1058, in _handle_fromlist
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "./venv2/lib/python3.9/site-packages/tzlocal/utils.py", line 15, in <module>
    log = logging.getLogger("tzlocal")
  File "/usr/lib/python3.9/logging/__init__.py", line 2042, in getLogger
    return Logger.manager.getLogger(name)

OK, so you have to revert to 4.x for the time being. I'll make a PR for rfc5424logging to fix this.

Thank you, are you sure I need 4.x, because based on my test 5.0.0 worked correctly.

Right, but 5.0.0 has other bugs, nothing serious though, so it works for you, you can use it.

rfc5424logging seems abandoned though, so I guess you will in the end have to fork it.

I'm experiencing this same issue on 5.0.1 with some custom logging that I implemented.

Here's a simplification of the logic that's experiencing the problem:

import logging
import tzlocal
def logging_Formatting_converter( self: Any, seconds: int ) -> datetime:
	try:
		tzinfo: Any = tzlocal.get_localzone()
	except ValueError:
		tzinfo = tz.utc
	return tz.fromtimestamp( seconds, tzinfo )

setattr( logging.Formatter, 'converter', logging_Formatting_converter )

The above logic combined with the following line from tzlocal is triggering a max recursion error:

File "C:\Python310-64\lib\site-packages\tzlocal\win32.py", line 58, in _get_localzone_name
    log.debug("Looking up time zone info from registry")

I can certainly patch my code, but I will lose functionality in doing so (I like having timezones on timestamps in log files - it really helps when tracking down issues). I'm rolling back to 4.x for now but hopeful you guys will remove this log.debug statement from your code.

No, I will not remove logging from tzlocal, it's necessary for debugging configuration problems. rfc5424-logging-handler should instead be fixed. This is unlikely to happen at it seem to have been abandoned.

I understand. I'm not using rfc5424-logging-handler. How can I use tzlocal from inside my own logging handler without causing an infinite loop?

Well, first of all I have to ask why you are using tzlocal in this case? Do you need to know the name of your local time zone?

There have been numerous times where I've been comparing logs files from multiple programs, some my own but not all, trying to determine what happened that caused a customer complaint. It's not always obvious which time zone each log file is using so seeing the time zone suffix on timestamps helps eliminate that confusion. Basically I have standardized that timestamps in my file look like this: 2023-05-31 06:16:00 CDT and I'm getting the "CDT" part by applying the local timezone to the log entry's timestamp using your module. I could certainly log in UTC but that would be significantly less convenient and I would get customer complaints.

Ah, you get the CDT by doing .strftime("%Z") ? Those short codes are also available in the time module as time.tzname[time.daylight].

You can also import tzlocal from inside the logging handler, that also works. Neither is maybe very neat, but they are faster and don't need tzlocal.

time.tzname[time.daylight] gives me 'Central Daylight Time' on Windows 10, not the abbreviated name.

How would importing tzlocal from inside the logging handler avoid the infinite loop? My apologies for not understanding what you're relaying.

I did figure out I can do the following which would be acceptable, but not quite as readable:

time.strftime('%z', time.localtime())

The infinite loop comes from you trying to import the logging module, and the logging module trying to import tzlocal, and tzlocal then trying to set up logging, which causes an import of your logging module, which means the logging module imports tzlocal, etc.

By not importing it on the module level, there's no loop.

That's actually not what's causing my infinite loop. It's being caused by my having a monkey-patch on logging.Formatting.converter(). I'm calling tz.get_localzone() and that results in a call to log.debug() from tzlocal\win32.py line 58, which then leads to my monkey-patch calling tz.get_localzone() again.

Oh. You don't have have a logging handler class, like Rfc5424SysLogHandler does?
Maybe just don't call get_localzone() from inside the handler? It's not likely to change during runtime, and in fact, tzlocal caches the result anyway, so it won't change unless you tell it to forget it, which is a function only there to make the unit tests easier.

Or call it once first, forcing tzlocal to cache the result, so no more logging. :-)

Thank you for the suggestions. Unfortunately, both of them are unpalatable. In my experience, caching the local zone has caused problems in the past when DST transitions. Calling get_localzone() before setting up logging may fix the issue today, but you might make changes tomorrow that break it again. The reason for using your module inside of my logging handler was to be able to get CDT-0500 in my timestamps instead of just -0500. I have decided to remove use of your module and lose that functionality in order to prioritize stability over what is really a QoL feature. Thank you for your time and consideration and thank you for your awesome library.

The local zone does not change over DST, so you don't need to worry about that. It only changes if you change the system configuration.

But since you just want the abbreviation, I think it's better to get it from the time module. tzlocal is really for when you want the IANA name, or you need a zoneinfo object, which you only really need for calendar applications, ie converting between timezones for datetimes in the future and in the past.