0

I have some code which should log errors using syslog. When logging to syslog I want the script name and pid to show in the log itself.

from syslog import syslog, openlog, LOG_PID
from sys import version_info

openlog(logoption=LOG_PID)
syslog('Some message')

However, python 2.6 does not support openlog, logoption and LOG_PID, and I know that this script will run on machines using python 2.7 and python 2.6. I solved it this way.

from syslog import syslog
from sys import version_info

(major, minor, dummy1, dummy2, dummy3) = version_info
if major == 2 and minor == 7:
    from syslog import openlog, LOG_PID
    openlog(logoption=LOG_PID)

syslog('some message')

Doing it this way feels strange. Is this sound? Also is there anyway I can get the same functionality in 2.6?

Mogget
  • 834
  • 2
  • 11
  • 21
  • You're asking at least questions here. It's much better to ask a specific question. If you need to ask multiple questions, ask them as separate questions. – abarnert Nov 21 '13 at 00:53
  • Where did you get the idea that "python 2.6 does not support openlog, logoption and LOG_PID"? – abarnert Nov 21 '13 at 01:10
  • I went into the python [documentation](http://docs.python.org/2.6/library/syslog.html) for 2.6 and couldn't fint it. Also when trying to import openlog and LOG_PID from syslog it raises an error. – Mogget Nov 21 '13 at 08:18
  • 1
    `openlog` is the second function on the page that you just linked. It's in the exact same location in the 2.6 docs as in the 2.7 docs. How could you not find it? And the docs for `LOG_PID` (which are identical on both versions) explain why importing `LOG_PID` failed: it the platform doesn't support it (that is, it's not in ``), Python can't support it either. – abarnert Nov 21 '13 at 18:22

2 Answers2

3

First:

Importing from the same library twice

I'm not sure what you think the problem is with this, because you never even hinted at what it is. If you import a library multiple times, the library only actually gets imported once, but any name bindings that happen as part of your import happen each time.

That "but" isn't usually a problem, but if you're trying to use from MODULE import NAME syntax, and one of the names has the same name as the module itself, it is easy to confuse yourself.

The best solution to that is to just not do it. For example:

import syslog
# …
syslog.openlog(logoption=syslog.LOG_PID)

If you really need the from imports, make sure you only do the from foo import foo after all other from foo import bar imports, and that will avoid any confusion.


Next, your basic premise is wrong:

However, python 2.6 does not support openlog, logoption and LOG_PID

Yes it does. The openlog function has been in the module since the beginning. SO has LOG_PID. Here are the 2.6 docs. And here's me using it:

$ python2.6
Python 2.6.7 (r267:88850, Oct 11 2012, 20:15:00)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from syslog import openlog, LOG_PID
>>> openlog('syslog', LOG_PID)

If you're getting an error on import, most likely it has nothing to do with the Python version, but with your platform. As the docs say, all of the log options are only available "if defined in <syslog.h>." So, if your platform doesn't have LOG_PID, then Python—whether 2.6 or 2.7—won't have syslog.LOG_PID on that platform.

It is true that 2.6 doesn't guarantee that openlog takes keyword arguments, while 2.7 does. But that difference can't possibly result in an ImportError. And the way to guard against that is not to just skip calling the function in 2.6; it's to call the function without keyword arguments, so it works equally in both versions.


Meanwhile, you're right that this is strange:

(major, minor, dummy1, dummy2, dummy3) = version_info
if major == 2 and minor == 7:

… but only because it's overly verbose, and it depends on the fact that version_info tuples will always have exactly 5 components in this and all future versions. The idiomatic way to do this is:

if version_info >= (2, 7):

If you don't understand why this works, read the documentation on Comparisons, and play around with comparing different tuples until you get it.

However, it's even better to check for names instead of checking for versions. Just try, and handle the relevant ImportError or NameError with an except and fallback code. Mateo's answer shows how to do this:

try:
    from syslog import syslog, openlog, LOG_PID
    openlog('syslog', LOG_PID)
except ImportError:
    from syslog import syslog

Among other things, this means you don't have to guess or figure out which version something was added in and run the risk of guessing wrong—as you did in this case.


Next:

Also is there anyway I can get the same functionality in 2.6?

Sure, but not by using the stdlib syslog module. It's not like the feature was there in 2.6 but just hidden from users, it's not there.

If you search PyPI for syslog you will find a variety of third-party modules that you can use instead.

But if your platform's syslog doesn't have LOG_PID, nothing you do is going to make syslog accept that option.

You may want to use logging with a SysLogHandler, and configure it at the Python level instead of at the syslog level.

Alternatively, you can stick with syslog and tack the pid onto the message manually—a horrible hack, but what else are you going to do if the platform's syslog doesn't log pids and you refuse to use anything but syslog?

abarnert
  • 354,177
  • 51
  • 601
  • 671
1

I think you can do it with try:except. Something like:

try:
    from syslog import openlog, LOG_PID
    openlog(logoption=LOG_PID)
except ImportError:
    from syslog import syslog

That makes it not so version specific.

Full disclosure: I got this answer from Best way to import version-specific python modules when I trying to do something similar.

Community
  • 1
  • 1
Mateo
  • 1,781
  • 1
  • 16
  • 21
  • He also wants to import `syslog` in the first case, so you want to add `, syslog` to the first line But otherwise, yes, if this is what you want to do, this is the Pythonic way to do it. – abarnert Nov 21 '13 at 18:23