cctool

A tool for managing contacts and calendars.
git clone https://git.ce9e.org/cctool.git

commit
b329a7613a15efa3cbe9c6f8538b639e801ea889
parent
7ec7ea7190d2fde6f8ddbc1f8d8eeb60d63d711d
Author
Tobias Bengfort <tobias.bengfort@gmx.net>
Date
2015-05-08 19:42
implement own ldif parser

incompatible but actually works better on my data than the 3rd party
one.

Should be turned into a full implementation of rfc2849.

Diffstat

M cctool.py 58 ++++++++++++++++++++++++++++++----------------------------
M setup.py 1 -
M tests.py 8 +++++---

3 files changed, 35 insertions, 32 deletions


diff --git a/cctool.py b/cctool.py

@@ -40,9 +40,9 @@ from datetime import date
   40    40 from datetime import datetime
   41    41 from io import BytesIO
   42    42 import argparse
   -1    43 import base64
   43    44 import codecs
   44    45 import json
   45    -1 import logging as log
   46    46 import os
   47    47 import pickle
   48    48 import re
@@ -54,11 +54,6 @@ except ImportError:
   54    54 	from configparser import RawConfigParser as ConfigParser
   55    55 
   56    56 try:
   57    -1 	import ldif
   58    -1 except ImportError as err:
   59    -1 	ldif = err
   60    -1 
   61    -1 try:
   62    57 	import icalendar
   63    58 except ImportError as err:
   64    59 	icalendar = err
@@ -87,6 +82,7 @@ def formats():
   87    82 		'abook': ABook,
   88    83 		'json': JSON,
   89    84 		'pickle': Pickle,
   -1    85 		'ldif': LDIF,
   90    86 	}
   91    87 	outformats = {
   92    88 		'bsdcal': BSDCal,
@@ -97,8 +93,6 @@ def formats():
   97    93 	if not isinstance(icalendar, Exception):
   98    94 		informats['ics'] = ICal
   99    95 		outformats['ics'] = ICal
  100    -1 	if not isinstance(ldif, Exception):
  101    -1 		informats['ldif'] = LDIF
  102    96 	if not isinstance(yaml, Exception):
  103    97 		informats['yml'] = YAML
  104    98 		outformats['yml'] = YAML
@@ -402,16 +396,6 @@ class ABook(Format):
  402   396 		cp.write(_fh)
  403   397 
  404   398 
  405    -1 if not isinstance(ldif, Exception):
  406    -1 	class LDIFParser(ldif.LDIFParser):
  407    -1 		def __init__(self, fh):
  408    -1 			ldif.LDIFParser.__init__(self, fh)
  409    -1 			self.entries = {}
  410    -1 
  411    -1 		def handle(self, dn, entry):
  412    -1 			self.entries[dn] = entry
  413    -1 
  414    -1 
  415   399 class LDIF(Format):
  416   400 	fields = {
  417   401 		'cn': 'name',
@@ -419,17 +403,35 @@ class LDIF(Format):
  419   403 	}
  420   404 
  421   405 	@classmethod
   -1   406 	def get_blocks(cls, fh):
   -1   407 		block = []
   -1   408 		for _line in fh:
   -1   409 			line = _line.rstrip()
   -1   410 			if not line:
   -1   411 				yield block
   -1   412 				block = []
   -1   413 			elif line.startswith(b'#'):
   -1   414 				continue
   -1   415 			elif line.startswith(b' '):
   -1   416 				block[-1] += line[1:]
   -1   417 			else:
   -1   418 				block.append(line)
   -1   419 		if block:
   -1   420 			yield block
   -1   421 
   -1   422 	@classmethod
  422   423 	def load(cls, fh):
  423    -1 		if isinstance(ldif, Exception):
  424    -1 			raise ldif
  425    -1 		parser = LDIFParser(fh)
  426    -1 		try:
  427    -1 			parser.parse()
  428    -1 		except ValueError as err:
  429    -1 			log.warning("ValueError after reading %i records: %s",
  430    -1 				parser.records_read, err)
  431    -1 		for entry in parser.entries.values():
  432    -1 			yield map_keys(MultiDict(entry), cls.fields)
   -1   424 		for block in cls.get_blocks(fh):
   -1   425 			item = MultiDict()
   -1   426 			for line in block:
   -1   427 				m = re.match(b'([^:]*):(:?) *(.*)', line)
   -1   428 				if m:
   -1   429 					key, b64, value = m.groups()
   -1   430 					if b64 == b':':
   -1   431 						value = base64.decodestring(value)
   -1   432 					item.append(key.decode('utf8'), [value.decode('utf8')])
   -1   433 			if item:
   -1   434 				yield map_keys(item, cls.fields)
  433   435 
  434   436 
  435   437 class DateTimeJSONEncoder(json.JSONEncoder):

diff --git a/setup.py b/setup.py

@@ -11,7 +11,6 @@ setup(
   11    11     platforms='any',
   12    12     py_modules=['cctool'],
   13    13     extras_require={
   14    -1         'ldif': ['python-ldap'],
   15    14         'ical': ['icaledar'],
   16    15         'yaml': ['PyYAML'],
   17    16     },

diff --git a/tests.py b/tests.py

@@ -112,12 +112,14 @@ class TestABook(_TestFormat):
  112   112 		self.text = b'[0]\nname = foo\nbday = 1970-01-01\n\n'
  113   113 
  114   114 
  115    -1 @unittest.skipIf(isinstance(cctool.ldif, Exception), 'ldif not available')
  116   115 class TestLDIF(_TestFormat):
  117   116 	def setUp(self):
  118   117 		self.format = cctool.LDIF()
  119    -1 		self.data = [cctool.MultiDict({'dn': ['foo']})]
  120    -1 		self.text = b'[0]\ndn = foo\n\n'
   -1   118 		self.data = [cctool.MultiDict([
   -1   119 			('name', ['foo']),
   -1   120 			('email', ['foo@example.com']),
   -1   121 		])]
   -1   122 		self.text = b'cn: foo\nmail:: Zm9vQGV4YW1wbGUuY29t'
  121   123 
  122   124 	def test_dump(self):
  123   125 		pass