Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Updated MailHandler to allow using TLS without having a keyfile/certfile #47

Closed
wants to merge 10 commits into from

2 participants

Remco Wendt Roman Valls Guimerà
Remco Wendt

Hey guys,

Thanks for logbook! After dealing with the pain of python's stdlib logging for years, this library is such a relief! Never want to go back ;)

Attached is a small patch to allow using TLS without explicitly defining keyfile/certfile. I didn't find any unit tests testing this behavior. Also fixed a small typo.

Cheers,
Remco

Remco Wendt

Hmm this is of course not really backwards compatible with people using tls with smtp in the old way. Let me know what you think and I could fix backward compatibility.

Roman Valls Guimerà
Collaborator

Hello @shanx, I find your patch interesting, could you update it a little bit so that it merges the current codebase and guarantee backwards compatibility?

Thanks!

Roman Valls Guimerà
Collaborator

Unmantained, closing.

Roman Valls Guimerà brainstorm closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 3, 2011
  1. Remco Wendt

    Fixed typo

    shanx authored
Commits on Dec 5, 2011
  1. Remco Wendt

    Added venv files to gitignore

    shanx authored
  2. Remco Wendt
Commits on Dec 8, 2011
  1. Remco Wendt
  2. Remco Wendt
  3. Remco Wendt
Commits on Dec 14, 2011
  1. Remco Wendt

    Changed level of default_handler to error. IMHO we only want the defa…

    shanx authored
    …ult_handler to log to stderr when an error occurs. If I want to see more information, then I will register my own handler.
  2. Remco Wendt

    Added python 3.2 to tox

    shanx authored
  3. Remco Wendt
  4. Remco Wendt
This page is out of date. Refresh to see the latest.
5 .gitignore
View
@@ -11,3 +11,8 @@ env*
.coverage
cover
build
+.DS_Store
+.Python
+/bin
+/include
+/lib
2  logbook/__init__.py
View
@@ -42,5 +42,5 @@
# install a default global handler
-default_handler = StderrHandler()
+default_handler = StderrHandler(level=ERROR)
default_handler.push_application()
18 logbook/base.py
View
@@ -152,7 +152,11 @@ def push_thread(self):
def pop_thread(self):
"""Pops the context object from the stack."""
popped = self.stack_manager.pop_thread()
- assert popped is self, 'popped unexpected object'
+ assert popped is self, \
+ 'Popped unexpected object. Expected: %s, got: %s' % (
+ repr(self),
+ repr(popped)
+ )
def push_application(self):
"""Pushes the context object to the application stack."""
@@ -161,7 +165,11 @@ def push_application(self):
def pop_application(self):
"""Pops the context object from the stack."""
popped = self.stack_manager.pop_application()
- assert popped is self, 'popped unexpected object'
+ assert popped is self, \
+ 'Popped unexpected object. Expected: %s, got: %s' % (
+ repr(self),
+ repr(popped)
+ )
class NestedSetup(StackedObject):
@@ -666,7 +674,9 @@ def log(self, level, *args, **kwargs):
def catch_exceptions(self, *args, **kwargs):
"""A context manager that catches exceptions and calls
- :meth:`exception` for exceptions caught that way. Example::
+ :meth:`exception` for exceptions caught that way.
+
+ Example::
with logger.catch_exceptions():
execute_code_that_might_fail()
@@ -848,7 +858,7 @@ class LoggerGroup(object):
def __init__(self, loggers=None, level=NOTSET, processor=None):
#: a list of all loggers on the logger group. Use the
#: :meth:`add_logger` and :meth:`remove_logger` methods to add
- #: or remove loggers from this list.
+ #: or remove loggers from this list.
self.loggers = []
if loggers is not None:
for logger in loggers:
3  logbook/compat.py
View
@@ -196,7 +196,8 @@ def emit(self, record):
def redirect_warnings():
"""Like :func:`redirected_warnings` but will redirect all warnings
- to the shutdown of the interpreter::
+ to the shutdown of the interpreter
+ ::
from logbook.compat import redirect_warnings
redirect_warnings()
20 logbook/handlers.py
View
@@ -239,7 +239,7 @@ def emit_batch(self, records, reason):
is intended to be used by other handlers which are already protected
against internal breakage.
- `reason` is a string that specifies the rason why :meth:`emit_batch`
+ `reason` is a string that specifies the reason why :meth:`emit_batch`
was called, and not :meth:`emit`. The following are valid values:
``'buffer'``
@@ -1021,10 +1021,10 @@ class MailHandler(Handler, StringFormatterHandlerMixin,
record_cache_prune = 0.333
def __init__(self, from_addr, recipients, subject=None,
- server_addr=None, credentials=None, secure=None,
- record_limit=None, record_delta=None, level=NOTSET,
- format_string=None, related_format_string=None,
- filter=None, bubble=False):
+ server_addr=None, credentials=None, use_tls=False,
+ keyfile=None, certfile=None,record_limit=None,
+ record_delta=None, level=NOTSET, format_string=None,
+ related_format_string=None, filter=None, bubble=False):
Handler.__init__(self, level, filter, bubble)
StringFormatterHandlerMixin.__init__(self, format_string)
LimitingHandlerMixin.__init__(self, record_limit, record_delta)
@@ -1035,7 +1035,9 @@ def __init__(self, from_addr, recipients, subject=None,
self.subject = subject
self.server_addr = server_addr
self.credentials = credentials
- self.secure = secure
+ self.use_tls = use_tls
+ self.keyfile=keyfile
+ self.certfile=certfile
if related_format_string is None:
related_format_string = self.default_related_format_string
self.related_format_string = related_format_string
@@ -1123,15 +1125,15 @@ def get_connection(self):
from smtplib import SMTP, SMTP_PORT, SMTP_SSL_PORT
if self.server_addr is None:
host = 'localhost'
- port = self.secure and SMTP_SSL_PORT or SMTP_PORT
+ port = self.use_tls and SMTP_SSL_PORT or SMTP_PORT
else:
host, port = self.server_addr
con = SMTP()
con.connect(host, port)
if self.credentials is not None:
- if self.secure is not None:
+ if self.use_tls:
con.ehlo()
- con.starttls(*self.secure)
+ con.starttls(self.keyfile, self.certfile)
con.ehlo()
con.login(*self.credentials)
return con
22 logbook/testsuite/__init__.py
View
@@ -9,9 +9,13 @@
:license: BSD, see LICENSE for more details.
"""
import sys
-import unittest
-import logbook
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+import logbook
_skipped_modules = []
_missing = object()
@@ -86,17 +90,3 @@ def wrapper(*args, **kwargs):
sys.modules[name] = old
return wrapper
return decorate
-
-
-def suite():
- loader = unittest.TestLoader()
- suite = LogbookTestSuite()
- suite.addTests(loader.loadTestsFromName('logbook.testsuite.test_regular'))
- if sys.version_info >= (2, 5):
- suite.addTests(loader.loadTestsFromName
- ('logbook.testsuite.test_contextmanager'))
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
51 logbook/testsuite/test_contextmanager.py
View
@@ -15,7 +15,6 @@
import sys
import pickle
import shutil
-import unittest
import tempfile
import socket
from datetime import datetime, timedelta
@@ -24,6 +23,11 @@
from contextlib import contextmanager
from cStringIO import StringIO
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
import logbook
from logbook.testsuite import LogbookTestCase, make_fake_mail_handler
@@ -278,21 +282,21 @@ def test_mail_handler(self):
else:
ascii_subject = False
subject = u'\xf8nicode'
- handler = make_fake_mail_handler(subject=subject)
+ handler = make_fake_mail_handler(subject=subject, level=logbook.CRITICAL)
with capture_stderr() as fallback:
with handler:
- self.log.warn('This is not mailed')
+ self.log.error('This is not mailed')
try:
1 / 0
- except Exception:
- self.log.exception('This is unfortunate')
+ except Exception, e:
+ self.log.critical('This is unfortunate', exc_info=sys.exc_info())
self.assertEqual(len(handler.mails), 1)
sender, receivers, mail = handler.mails[0]
self.assertEqual(sender, handler.from_addr)
if not ascii_subject:
self.assert_('=?utf-8?q?=C3=B8nicode?=' in mail)
- self.assert_(re.search('Message type:\s+ERROR', mail))
+ self.assert_(re.search('Message type:\s+CRITICAL', mail))
self.assert_(re.search('Location:.*%s' % test_file, mail))
self.assert_(re.search('Module:\s+%s' % __name__, mail))
self.assert_(re.search('Function:\s+test_mail_handler', mail))
@@ -400,9 +404,9 @@ def process_record(self, record):
handler = MyTestHandler()
with capture_stderr() as captured:
with handler:
- log.warn('From my logger')
- self.log.warn('From another logger')
- self.assert_(handler.has_warning('From my logger'))
+ log.error('From my logger')
+ self.log.error('From another logger')
+ self.assert_(handler.has_error('From my logger'))
self.assert_('From another logger' in captured.getvalue())
def test_custom_handling_tester(self):
@@ -445,7 +449,7 @@ def new_heavy_init(self):
null_handler.bubble = True
with capture_stderr() as captured:
- logbook.warning('Not a blockhole')
+ logbook.error('Not a blockhole')
self.assertNotEqual(captured.getvalue(), '')
def test_calling_frame(self):
@@ -471,7 +475,7 @@ def test_nested_setups(self):
logger.error('This is also a mail')
with logger.catch_exceptions():
1 / 0
- logger.warn('And here we go straight back to stderr')
+ logger.error('And here we go straight back to stderr')
self.assert_(test_handler.has_warning('This is a warning'))
self.assert_(test_handler.has_error('This is also a mail'))
@@ -692,12 +696,12 @@ def test_error_flag(self):
with logbook.Flags(errors='silent'):
with logbook.Flags(errors='print'):
- self.log.warn('Foo {42}', 'aha')
+ self.log.error('Foo {42}', 'aha')
self.assertNotEqual(captured.getvalue(), '')
try:
with logbook.Flags(errors='raise'):
- self.log.warn('Foo {42}', 'aha')
+ self.log.error('Foo {42}', 'aha')
except Exception, e:
self.assert_('Could not format message with provided '
'arguments' in str(e))
@@ -733,10 +737,13 @@ def inject_extra(record):
class DefaultConfigurationTestCase(LogbookTestCase):
def test_default_handlers(self):
+ # Logbook should log level ERROR and up by default
+ self.assertEqual(logbook.ERROR, logbook.default_handler.level)
+
with capture_stderr() as stream:
- self.log.warn('Aha!')
+ self.log.error('Aha!')
captured = stream.getvalue()
- self.assert_('WARNING: testlogger: Aha!' in captured)
+ self.assert_('ERROR: testlogger: Aha!' in captured)
class LoggingCompatTestCase(LogbookTestCase):
@@ -754,7 +761,7 @@ def test_basic_compat(self):
logger.warn('This is from the old system')
logger.error('This is from the old system')
logger.critical('This is from the old system')
- self.assert_(('WARNING: %s: This is from the old system' % name)
+ self.assert_(('ERROR: %s: This is from the old system' % name)
in captured.getvalue())
def test_redirect_logbook(self):
@@ -829,5 +836,13 @@ def test_tagged(self):
self.assert_('cmd message' in stringio)
-if __name__ == '__main__':
- unittest.main()
+class PlatformSpecificTestCase(LogbookTestCase):
+ @unittest.skipUnless(sys.platform.startswith("win"), "Requires Windows")
+ def test_win32_logger(self):
+ from logbook import NTEventLogHandler, Logger
+
+ logger = Logger('MyLogger')
+ handler = NTEventLogHandler('My Application')
+
+ with handler.applicationbound():
+ logger.error('Testing')
80 logbook/testsuite/test_regular.py
View
@@ -17,7 +17,6 @@
import thread
import pickle
import shutil
-import unittest
import tempfile
import socket
from datetime import datetime, timedelta
@@ -25,6 +24,11 @@
from itertools import izip
from cStringIO import StringIO
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
import logbook
from logbook.testsuite import LogbookTestCase, skip_if, require, missing, \
make_fake_mail_handler
@@ -147,6 +151,35 @@ def make_record():
self.assert_(re.search('Happened in file .*%s, line \d+' % test_file,
errormsg))
+ def test_context_exception(self):
+ # NOTE: Since no actual logging is done we only put this
+ # test in test_regular
+
+ # Push a certain handler onto the application stack
+ handler = logbook.TestHandler()
+ handler.push_application()
+
+ # Creating another handler and trying to pop it from that stack
+ # should raise an AssertionError
+ another_handler = logbook.StderrHandler()
+
+ # This is for compatibility reasons, we would rather use
+ # assertRaisesRegexp here.
+ expected_msg = (
+ r"Popped unexpected object. "
+ r"Expected\: \<logbook\.handlers\.StderrHandler object at .*\>, "
+ r"got\: \<logbook\.handlers\.TestHandler object at .*\>"
+ )
+ self.assertRaisesRegexp(expected_msg, another_handler.pop_application)
+
+ handler.pop_application()
+
+ # Now repeat for thread context
+ handler.push_thread()
+ self.assertRaisesRegexp(expected_msg, another_handler.pop_thread)
+ handler.pop_thread()
+
+
def test_exception_catching(self):
logger = logbook.Logger('Test')
handler = logbook.TestHandler()
@@ -372,16 +405,16 @@ def test_mail_handler(self):
else:
ascii_subject = False
subject = u'\xf8nicode'
- handler = make_fake_mail_handler(subject=subject)
+ handler = make_fake_mail_handler(subject=subject, level=logbook.CRITICAL)
fallback = capture_stderr.start()
try:
handler.push_thread()
try:
- self.log.warn('This is not mailed')
+ self.log.error('This is not mailed')
try:
1 / 0
except Exception:
- self.log.exception('This is unfortunate')
+ self.log.critical('This is unfortunate', exc_info=sys.exc_info())
finally:
handler.pop_thread()
@@ -390,7 +423,7 @@ def test_mail_handler(self):
self.assertEqual(sender, handler.from_addr)
if not ascii_subject:
self.assert_('=?utf-8?q?=C3=B8nicode?=' in mail)
- self.assert_(re.search('Message type:\s+ERROR', mail))
+ self.assert_(re.search('Message type:\s+CRITICAL', mail))
self.assert_(re.search('Location:.*%s' % test_file, mail))
self.assert_(re.search('Module:\s+%s' % __name__, mail))
self.assert_(re.search('Function:\s+test_mail_handler', mail))
@@ -580,7 +613,7 @@ def process_record(self, record):
handler.push_thread()
try:
log.warn('From my logger')
- self.log.warn('From another logger')
+ self.log.error('From another logger')
finally:
handler.pop_thread()
self.assert_(handler.has_warning('From my logger'))
@@ -649,7 +682,7 @@ def new_heavy_init(self):
null_handler.bubble = True
captured = capture_stderr.start()
try:
- logbook.warning('Not a blockhole')
+ logbook.error('Not a blockhole')
self.assertNotEqual(captured.getvalue(), '')
finally:
capture_stderr.end()
@@ -686,7 +719,7 @@ def test_nested_setups(self):
logger.exception()
finally:
handlers.pop_thread()
- logger.warn('And here we go straight back to stderr')
+ logger.error('And here we go straight back to stderr')
self.assert_(test_handler.has_warning('This is a warning'))
self.assert_(test_handler.has_error('This is also a mail'))
@@ -946,7 +979,7 @@ def test_error_flag(self):
print_flag = logbook.Flags(errors='print')
print_flag.push_thread()
try:
- self.log.warn('Foo {42}', 'aha')
+ self.log.error('Foo {42}', 'aha')
finally:
print_flag.pop_thread()
finally:
@@ -957,7 +990,7 @@ def test_error_flag(self):
raise_flag = logbook.Flags(errors='raise')
raise_flag.push_thread()
try:
- self.log.warn('Foo {42}', 'aha')
+ self.log.error('Foo {42}', 'aha')
finally:
raise_flag.pop_thread()
except Exception, e:
@@ -1008,13 +1041,16 @@ def inject_extra(record):
class DefaultConfigurationTestCase(LogbookTestCase):
def test_default_handlers(self):
+ # Logbook should log level ERROR and up by default
+ self.assertEqual(logbook.ERROR, logbook.default_handler.level)
+
stream = capture_stderr.start()
try:
- self.log.warn('Aha!')
+ self.log.error('Aha!')
captured = stream.getvalue()
finally:
capture_stderr.end()
- self.assert_('WARNING: testlogger: Aha!' in captured)
+ self.assert_('ERROR: testlogger: Aha!' in captured)
class LoggingCompatTestCase(LogbookTestCase):
@@ -1037,7 +1073,7 @@ def test_basic_compat(self):
logger.critical('This is from the old system')
finally:
redirector.end()
- self.assert_(('WARNING: %s: This is from the old system' % name)
+ self.assert_(('ERROR: %s: This is from the old system' % name)
in captured.getvalue())
finally:
capture_stderr.end()
@@ -1265,7 +1301,6 @@ def test_exception_handler_specific_level(self):
self.assert_('this is irrelevant' in test_handler.records[0].message)
-
class QueuesTestCase(LogbookTestCase):
def _get_zeromq(self):
@@ -1519,20 +1554,3 @@ def test_datehelpers(self):
self.assertEqual(v.hour, 11)
v = parse_iso8601('2000-01-01T12:00:00-01:00')
self.assertEqual(v.hour, 13)
-
-
-def suite():
- loader = unittest.TestLoader()
- suite = LogbookTestSuite()
- suite.addTests(loader.loadTestsFromName(__name__))
- try:
- suite.addTests(loader.loadTestsFromName
- ('logbook.testsuite.test_contextmanager'))
- except SyntaxError:
- # Python 2.4 does not support the 'with' statement
- pass
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
7 testwin32log.py
View
@@ -1,7 +0,0 @@
-from logbook import NTEventLogHandler, Logger
-
-logger = Logger('MyLogger')
-handler = NTEventLogHandler('My Application')
-
-with handler.applicationbound():
- logger.error('Testing')
39 tox.ini
View
@@ -1,26 +1,42 @@
[tox]
-envlist=py24,py25,py26,py27,py31,docs
+envlist=py25,py26,py27,py31,py32,docs
[testenv]
+
+[testenv:py24]
+deps=
+ SQLAlchemy
+ pysqlite
+ simplejson
+ multiprocessing
+ execnet
+ Jinja2
+ unittest2==0.5.1
+commands=unit2 discover []
+
+[testenv:py25]
deps=
SQLAlchemy
+ simplejson
+ multiprocessing
pyzmq
execnet
Jinja2
-commands=python -c __import__('unittest').main('logbook.testsuite','suite')
-changedir={toxworkdir}
+ unittest2==0.5.1
+commands=unit2 discover []
-[testenv:py24]
+[testenv:py26]
deps=
SQLAlchemy
- pysqlite
simplejson
multiprocessing
pyzmq
execnet
Jinja2
+ unittest2==0.5.1
+commands=unit2 discover []
-[testenv:py25]
+[testenv:py27]
deps=
SQLAlchemy
simplejson
@@ -28,12 +44,23 @@ deps=
pyzmq
execnet
Jinja2
+commands=python -m unittest discover []
[testenv:py31]
deps=
SQLAlchemy
execnet
Jinja2
+ discover
+# Test discovery missed python 3.1 release :(
+commands=python -m discover []
+
+[testenv:py32]
+deps=
+ SQLAlchemy
+ execnet
+ Jinja2
+commands=python -m unittest discover []
[testenv:docs]
deps=
Something went wrong with that request. Please try again.