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

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

Closed
wants to merge 10 commits into from
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ env*
.coverage
cover
build
.DS_Store
.Python
/bin
/include
/lib
2 changes: 1 addition & 1 deletion logbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@


# install a default global handler
default_handler = StderrHandler()
default_handler = StderrHandler(level=ERROR)
default_handler.push_application()
18 changes: 14 additions & 4 deletions logbook/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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):
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion logbook/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
20 changes: 11 additions & 9 deletions logbook/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'``
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 6 additions & 16 deletions logbook/testsuite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 changes: 33 additions & 18 deletions logbook/testsuite/test_contextmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import sys
import pickle
import shutil
import unittest
import tempfile
import socket
from datetime import datetime, timedelta
Expand All @@ -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

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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'))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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')
Loading