summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Brox <tobias@redpill-linpro.com>2021-02-05 12:33:39 +0100
committerTobias Brox <tobias@redpill-linpro.com>2021-02-05 12:33:39 +0100
commit60ec379725b3ffedf57f33e869b15a4abf09464d (patch)
tree1633ef05f5c7fba224a137b30f722c072772646c
parente5968d0faa6852440f27ea23778a96814bef95fd (diff)
downloadpython-caldav-60ec379725b3ffedf57f33e869b15a4abf09464d.zip
logging and assert handling should be configurable through environment option
-rw-r--r--.gitignore1
-rw-r--r--caldav/__init__.py2
-rw-r--r--caldav/davclient.py28
-rw-r--r--caldav/lib/error.py27
-rw-r--r--caldav/objects.py13
-rwxr-xr-xsetup.py2
6 files changed, 49 insertions, 24 deletions
diff --git a/.gitignore b/.gitignore
index 0e410a4..6a52676 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@ bin
build
docs/build
dist
-lib
include
.Python
.*.egg-info
diff --git a/caldav/__init__.py b/caldav/__init__.py
index e2a6582..433fa5e 100644
--- a/caldav/__init__.py
+++ b/caldav/__init__.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
from .davclient import DAVClient
+## TODO: I don't think I like the practice of "import *" here. Should be revisited prior to launching 1.0 at least.
from .objects import *
# possibly a bug in the tBaxter fork of vobject, this one has to be
@@ -12,7 +13,6 @@ import logging
# Silence notification of no default logging handler
log = logging.getLogger("caldav")
-
class NullHandler(logging.Handler):
def emit(self, record):
pass
diff --git a/caldav/davclient.py b/caldav/davclient.py
index 140d015..aae27da 100644
--- a/caldav/davclient.py
+++ b/caldav/davclient.py
@@ -45,12 +45,12 @@ class DAVResponse:
if content_length == 0:
self._raw = ''
self.tree = None
- logging.debug("No content delivered")
+ log.debug("No content delivered")
else:
#self.tree = etree.parse(response.raw, parser=etree.XMLParser(remove_blank_text=True))
self.tree = etree.XML(response.content, parser=etree.XMLParser(remove_blank_text=True))
if log.level <= logging.DEBUG:
- logging.debug(etree.tostring(self.tree, pretty_print=True))
+ log.debug(etree.tostring(self.tree, pretty_print=True))
elif (self.headers.get('Content-Type', '').startswith('text/calendar') or
self.headers.get('Content-Type', '').startswith('text/plain')):
## text/plain is typically for errors, we shouldn't see it on 200/207 responses.
@@ -67,7 +67,7 @@ class DAVResponse:
pass
if hasattr(self, '_raw'):
- logging.debug(self._raw)
+ log.debug(self._raw)
# ref https://github.com/python-caldav/caldav/issues/112 stray CRs may cause problems
if type(self._raw) == bytes:
self._raw = self._raw.replace(b'\r\n', b'\n')
@@ -137,12 +137,12 @@ class DAVResponse:
status = None
href = None
propstats = []
- assert(response.tag == dav.Response.tag)
+ error.assert_(response.tag == dav.Response.tag)
for elem in response:
if elem.tag == dav.Status.tag:
- assert(not status)
+ error.assert_(not status)
status = elem.text
- assert(status)
+ error.assert_(status)
self.validate_status(status)
elif elem.tag == dav.Href.tag:
assert not href
@@ -150,8 +150,8 @@ class DAVResponse:
elif elem.tag == dav.PropStat.tag:
propstats.append(elem)
else:
- assert(False)
- assert(href)
+ error.assert_(False)
+ error.assert_(href)
return (href, propstats, status)
def find_objects_and_props(self, compatibility_mode=False):
@@ -171,10 +171,10 @@ class DAVResponse:
if r.tag == dav.SyncToken.tag:
self.sync_token = r.text
continue
- assert(r.tag == dav.Response.tag)
+ error.assert_(r.tag == dav.Response.tag)
(href, propstats, status) = self._parse_response(r)
- assert not href in self.objects
+ error.assert_(not href in self.objects)
self.objects[href] = {}
## The properties may be delivered either in one
@@ -183,9 +183,9 @@ class DAVResponse:
for propstat in propstats:
cnt = 0
status = propstat.find(dav.Status.tag)
- assert(status is not None)
+ error.assert_(status is not None)
if (status is not None):
- assert(len(status) == 0)
+ error.assert_(len(status) == 0)
cnt += 1
self.validate_status(status.text)
if not compatibility_mode:
@@ -198,7 +198,7 @@ class DAVResponse:
self.objects[href][theprop.tag] = theprop
## there shouldn't be any more elements except for status and prop
- assert(cnt == len(propstat))
+ error.assert_(cnt == len(propstat))
return self.objects
@@ -227,7 +227,7 @@ class DAVResponse:
else:
if not values:
return None
- assert(len(values)==1)
+ error.assert_(len(values)==1)
return values[0]
def expand_simple_props(self, props=[], multi_value_props=[], xpath=None):
diff --git a/caldav/lib/error.py b/caldav/lib/error.py
index 08afe54..2c38434 100644
--- a/caldav/lib/error.py
+++ b/caldav/lib/error.py
@@ -2,6 +2,33 @@
# -*- encoding: utf-8 -*-
from collections import defaultdict
+import logging
+
+try:
+ import os
+ ## one of DEBUG_PDB, DEBUG, DEVELOPMENT, PRODUCTION
+ debugmode = os.environ['PYTHON_CALDAV_DEBUGMODE']
+except:
+ ## TODO: the default value here should be set to PRODUCTION prior to release
+ debugmode = 'DEVELOPMENT'
+
+log = logging.getLogger('caldav')
+if debugmode.startswith('DEBUG'):
+ log.setLevel(logging.DEBUG)
+else:
+ log.setLevel(logging.WARNING)
+
+def assert_(condition):
+ try:
+ assert(condition)
+ except AssertionError:
+ if debugmode == 'PRODUCTION':
+ log.error("Deviation from expectations found. %s" % ERR_FRAGMENT, exc_info=True)
+ elif debugmode == 'DEBUG_PDB':
+ log.error("Deviation from expectations found. Dropping into debugger")
+ import pdb; pdb.set_trace()
+ else:
+ raise
ERR_FRAGMENT="Please raise an issue at https://github.com/python-caldav/caldav/issues or reach out to t-caldav@tobixen.no, include this error and the traceback and tell what server you are using"
diff --git a/caldav/objects.py b/caldav/objects.py
index 5cc9bd2..0a8f07b 100644
--- a/caldav/objects.py
+++ b/caldav/objects.py
@@ -30,15 +30,12 @@ from caldav.elements import dav, cdav, ical
from caldav.lib.python_utilities import to_unicode
import logging
-
log = logging.getLogger('caldav')
-log.setLevel(logging.ERROR)
def errmsg(r):
"""Utility for formatting a response xml tree to an error string"""
return "%s %s\n\n%s" % (r.status, r.reason, r.raw)
-
class DAVObject(object):
"""
@@ -223,7 +220,7 @@ class DAVObject(object):
properties = self._handle_xml_response(response, props)
- assert(properties)
+ error.assert_(properties)
path = unquote(self.url.path)
if path.endswith('/'):
@@ -234,17 +231,17 @@ class DAVObject(object):
if path in properties:
rc = properties[path]
elif exchange_path in properties:
- logging.error("potential path handling problem with ending slashes. Path given: %s, path found: %s. %s" % (path, exchange_path, error.ERR_FRAGMENT))
+ log.error("potential path handling problem with ending slashes. Path given: %s, path found: %s. %s" % (path, exchange_path, error.ERR_FRAGMENT))
rc = properties[exchange_path]
elif self.url in properties:
rc = properties[self.url]
elif '/principal/' in properties and path.endswith('/principal/'):
- logging.error("Bypassing a known iCloud bug - path expected in response: %s, path found: /principal/" % (path, error.ERR_FRAGMENT))
+ log.error("Bypassing a known iCloud bug - path expected in response: %s, path found: /principal/" % (path, error.ERR_FRAGMENT))
## The strange thing is that we apparently didn't encounter this problem in bc589093a34f0ed0ef489ad5e9cba048750c9837 or 3ee4e42e2fa8f78b71e5ffd1ef322e4007df7a60 - TODO: check this up
rc = properties['/principal/']
else:
- logging.error("Possibly the server has a path handling problem. Path expected: %s, path found: %s %s" % (path, str(list(properties.keys)), error.ERR_FRAGMENT))
- import pdb; pdb.set_trace()
+ log.error("Possibly the server has a path handling problem. Path expected: %s, path found: %s %s" % (path, str(list(properties.keys)), error.ERR_FRAGMENT))
+ error.assert_(False)
return rc
diff --git a/setup.py b/setup.py
index a12a6af..4241e9b 100755
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,9 @@
from setuptools import setup, find_packages
import sys
+## ATTENTION! when doing releases, the default debugmode in lib/error.py should be set to PRODUCTION.
version = '0.8.0pre'
+## (TODO: any nicer ways than doing this manually? Make a "releases" branch, maybe?)
if __name__ == '__main__':
## For python 2.7 and 3.5 we depend on pytz and tzlocal. For 3.6 and up, batteries are included. Same with mock.