Replace deepcopy with constructor (i.e. type call)
Can now "clone" with configuration changes by passing object
of the same type to it's constructor, e.g.
dn1 = DN(('cn', 'foo')
dn2 = DN(dn1)
dn2 = DN(dn1, first_key_match=False)

Remove pairwise grouping for RDN's. Had previously removed it
for DN's, left it in for RDN's because it seemed to make sense
because of the way RDN's work but consistency is a higher goal.

Add keyword constructor parameters to pass configuration options.

Make first_key_match a configuration keyword.

Updated documentation.

Updated unit test.

FWIW, I noticed the unittest is now running 2x faster, not sure why,
removal of deepcopy? Anyway, hard to argue with performance doubling.

--
John Dennis <jden...@redhat.com>

Looking to carve out IT costs?
www.redhat.com/carveoutcosts/
From 5ae2c8d7372b02a64d1b01df838b0478e59d168c Mon Sep 17 00:00:00 2001
From: John Dennis <jden...@redhat.com>
Date: Tue, 26 Jul 2011 16:55:12 -0400
Subject: [PATCH 32/32] Make AVA, RDN & DN comparison case insensitive. No
 need for lowercase normalization.
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

Replace deepcopy with constructor (i.e. type call)
Can now "clone" with configuration changes by passing object
of the same type to it's constructor, e.g.
dn1 = DN(('cn', 'foo')
dn2 = DN(dn1)
dn2 = DN(dn1, first_key_match=False)

Remove pairwise grouping for RDN's. Had previously removed it
for DN's, left it in for RDN's because it seemed to make sense
because of the way RDN's work but consistency is a higher goal.

Add keyword constructor parameters to pass configuration options.

Make first_key_match a configuration keyword.

Updated documentation.

Updated unit test.

FWIW, I noticed the unittest is now running 2x faster, not sure why,
removal of deepcopy? Anyway, hard to argue with performance doubling.
---
 ipalib/dn.py                 |  262 +++++++++++++++++++++++++-----------------
 tests/test_ipalib/test_dn.py |   89 +++++++++------
 2 files changed, 211 insertions(+), 140 deletions(-)

diff --git a/ipalib/dn.py b/ipalib/dn.py
index 47c6619..1311b6a 100644
--- a/ipalib/dn.py
+++ b/ipalib/dn.py
@@ -19,7 +19,6 @@
 
 from ldap.dn import str2dn, dn2str
 from ldap import DECODING_ERROR
-from copy import deepcopy
 import sys
 
 __all__ = ['AVA', 'RDN', 'DN']
@@ -392,7 +391,7 @@ if container_dn in dn:
 # (e.g. cmp()). The general rule is for objects supporting multiple
 # values first their lengths are compared, then if the lengths match
 # the respective components of each are pair-wise compared until one
-# is discovered to be  non-equal
+# is discovered to be  non-equal. The comparision is case insensitive.
 
 Concatenation and In-Place Addition:
 
@@ -412,13 +411,35 @@ manipulate these objects.
 
 '''
 
+def _adjust_indices(start, end, length):
+    'helper to fixup start/end slice values'
+
+    if end > length:
+        end = length
+    elif end < 0:
+        end += length
+        if end < 0:
+            end = 0
+
+    if start < 0:
+        start += length
+        if start < 0:
+            start = 0
+
+    return start, end
+
 class AVA(object):
     '''
+    AVA(arg0, ...)
+
     An AVA is an LDAP Attribute Value Assertion. It is convenient to think of
     AVA's as a <attr,value> pair. AVA's are members of RDN's (Relative
     Distinguished Name).
 
-    The AVA constructor may be invoked with any of the following methods:
+    The AVA constructor is passed a sequence of args and a set of
+    keyword parameters used for configuration.
+
+    The arg sequence may be:
 
     1) With 2 string (or unicode) arguments, the first argument will be the
     attr, the 2nd the value.
@@ -454,14 +475,16 @@ class AVA(object):
 
     AVA objects support equality testing and comparsion (e.g. cmp()). When they
     are compared the attr is compared first, if the 2 attr's are equal then the
-    values are compared.
+    values are compared. The comparision is case insensitive (because attr's map
+    to numeric OID's and their values derive from from the 'name' atribute type
+    (OID 2.5.4.41) whose EQUALITY MATCH RULE is caseIgnoreMatch.
 
     The str method of an AVA returns the string representation in RFC 4514 DN
     syntax with proper escaping.
     '''
     flags = 0
 
-    def __init__(self, *args):
+    def __init__(self, *args, **kwds):
         if len(args) == 1:
             arg = args[0]
             if isinstance(arg, basestring):
@@ -496,42 +519,84 @@ class AVA(object):
         if not isinstance(value, basestring):
             raise TypeError("value must be basestring, got %s instead" % value.__class__.__name__)
 
-        self.attr  = attr.decode('utf-8')
-        self.value = value.decode('utf-8')
+        attr  = attr.decode('utf-8')
+        value = value.decode('utf-8')
+
+        self._attr  = attr
+        self._value = value
+
+    def _get_attr(self):
+        return self._attr
+
+    def _set_attr(self, new_attr):
+        if not isinstance(new_attr, basestring):
+            raise TypeError("attr must be basestring, got %s instead" % new_attr.__class__.__name__)
+
+        self._attr  = new_attr
 
+    attr  = property(_get_attr, _set_attr)
+
+    def _get_value(self):
+        return self._value
+
+    def _set_value(self, new_value):
+        if not isinstance(new_value, basestring):
+            raise TypeError("value must be basestring, got %s instead" % new_value.__class__.__name__)
+
+        self._value  = new_value
+
+    value = property(_get_value, _set_value)
 
     def _to_openldap(self):
-        return [[(self.attr.encode('utf-8'), self.value.encode('utf-8'), self.flags)]]
+        return [[(self._attr.encode('utf-8'), self._value.encode('utf-8'), self.flags)]]
 
     def __str__(self):
         return dn2str(self._to_openldap())
 
     def __getitem__(self, key):
         if isinstance(key, basestring):
-            if key == self.attr:
-                return self.value
+            if key == self._attr:
+                return self._value
             raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
         else:
             raise TypeError("unsupported type for AVA indexing, must be basestring; not %s" % \
                                 (key.__class__.__name__))
 
     def __eq__(self, other):
+        '''
+        The attr comparison is case insensitive because attr is
+        really an LDAP attribute type which means it's specified with
+        an OID (dotted number) and not a string. Since OID's are
+        numeric the human readable name which maps to the OID is not
+        significant in case.
+
+        The value comparison is also case insensitive because the all
+        attribute types used in a DN are derived from the 'name'
+        atribute type (OID 2.5.4.41) whose EQUALITY MATCH RULE is
+        caseIgnoreMatch.
+        '''
         if not isinstance(other, self.__class__):
             raise TypeError("expected AVA but got %s" % (other.__class__.__name__))
 
-        return self.attr == other.attr and self.value == other.value
+        return self._attr.lower() == other.attr.lower() and \
+            self._value.lower() == other.value.lower()
 
     def __cmp__(self, other):
+        'comparision is case insensitive, see __eq__ doc for explanation'
+
         if not isinstance(other, self.__class__):
             raise TypeError("expected AVA but got %s" % (other.__class__.__name__))
 
-        result = cmp(self.attr, other.attr)
-        if result != 0: return result
-        result = cmp(self.value, other.value)
+        result = cmp(self._attr.lower(), other.attr.lower())
+        if result != 0:
+            return result
+        result = cmp(self._value.lower(), other.value.lower())
         return result
 
 class RDN(object):
     '''
+    RDN(arg0, ..., first_key_match=True)
+
     An RDN is a LDAP Relative Distinguished Name. RDN's are members of DN's
     (Distinguished Name). An RDN contains 1 or more AVA's. If the RDN contains
     more than one AVA it is said to be a multi-valued RDN. When an RDN is
@@ -542,13 +607,12 @@ class RDN(object):
     within an RDN). Single valued RDN's are the norm and thus the RDN
     constructor has simple syntax for them.
 
-    The RDN constructor may be invoked in a variety of different ways.
+    The RDN constructor is passed a sequence of args and a set of
+    keyword parameters used for configuration.
+
+    The constructor iterates though the sequence and adds AVA's to the RDN.
 
-    * When two adjacent string (or unicode) argument appear together in the
-    argument list they are taken to be the <attr,value> pair of an AVA. An AVA
-    object is constructed and inserted into the RDN. Multiple pairs of strings
-    arguments may appear in the argument list, each pair adds one additional AVA
-    to the RDN.
+    The arg sequence may be:
 
     * A 2-valued tuple or list denotes the <attr,value> pair of an AVA. The
     first member is the attr and the second member is the value, both members
@@ -562,28 +626,26 @@ class RDN(object):
     more AVA <attr,value> pairs. The parsing recognizes the DN syntax escaping
     rules.
 
-    Note, a DN syntax argument is distguished from AVA string pairs by testing
-    to see if two strings appear adjacent in the argument list, if so those two
-    strings are interpretted as an <attr,value> AVA pair and consumed.
+    * A AVA object, the AVA will be copied into the new RDN respecting
+      the constructors keyword configuration parameters.
 
-    * A AVA object. Each AVA object in the argument list will be added to the RDN.
+    * A RDN object, the AVA's in the RDN are copied into the new RDN
+      respecting the constructors keyword configuration parameters.
 
     Single AVA Examples:
 
-    RDN('cn', 'Bob')                    # 2 adjacent strings yield 1 AVA
     RDN(('cn', 'Bob'))                  # tuple yields 1 AVA
     RDN('cn=Bob')                       # DN syntax with 1 AVA
     RDN(AVA('cn', 'Bob'))               # AVA object adds 1 AVA
 
     Multiple AVA Examples:
 
-    RDN('cn', 'Bob', 'ou', 'people')	# 2 strings pairs yield 2 AVA's
     RDN(('cn', 'Bob'),('ou', 'people')) # 2 tuples yields 2 AVA's
     RDN('cn=Bob+ou=people')             # DN syntax with 2 AVA's
     RDN(AVA('cn', 'Bob'),AVA('ou', 'people')) # 2 AVA objects adds 2 AVA's
-    RDN('cn', 'Bob', "ou=people')       # 3 strings, 1st two strings form 1 AVA
-                                        # 3rd string DN syntax for 1 AVA,
-                                        # adds 2 AVA's in total
+    RDN(('cn', 'Bob'), 'ou=people')     # 2 args, 1st tuple forms 1 AVA,
+                                        # 2nd DN syntax string adds 1 AVA,
+                                        # 2 AVA's in total
 
     Note: The RHS of a slice assignment is interpreted exactly in the
     same manner as the constructor argument list (see above examples).
@@ -613,7 +675,7 @@ class RDN(object):
     and value properties return the attr and value properties of the first AVA
     in the RDN, for example:
 
-    rdn = RDN('cn', 'Bob') # rdn has 1 AVA whose attr == 'cn' and value == 'Bob'
+    rdn = RDN(('cn', 'Bob')) # rdn has 1 AVA whose attr == 'cn' and value == 'Bob'
     len(rdn) -> 1
     rdn.attr -> u'cn'      # exactly equivalent to rdn[0].attr
     rdn.value -> u'Bob'    # exactly equivalent to rdn[0].value
@@ -644,14 +706,22 @@ class RDN(object):
 
     flags = 0
 
-    def __init__(self, *args):
-        self.first_key_match = True
+    def __init__(self, *args, **kwds):
+        self.first_key_match = kwds.get('first_key_match', True)
         self.avas = self._avas_from_sequence(args)
         self.avas.sort()
 
     def _ava_from_value(self, value):
         if isinstance(value, AVA):
-            return deepcopy(value)
+            return AVA(value.attr, value.value)
+        elif isinstance(value, RDN):
+            avas = []
+            for ava in value.avas:
+                avas.append(AVA(ava.attr, ava.value))
+            if len(avas) == 1:
+                return avas[0]
+            else:
+                return avas
         elif isinstance(value, basestring):
             try:
                 rdns = str2dn(value.encode('utf-8'))
@@ -679,22 +749,12 @@ class RDN(object):
     def _avas_from_sequence(self, seq):
         avas = []
 
-        i = 0
-        while i < len(seq):
-            if i+1 < len(seq)                   and \
-               isinstance(seq[i],   basestring) and \
-               isinstance(seq[i+1], basestring):
-                ava = AVA(seq[i], seq[i+1])
-                avas.append(ava)
-                i += 2
+        for item in seq:
+            ava = self._ava_from_value(item)
+            if isinstance(ava, list):
+                avas.extend(ava)
             else:
-                arg = seq[i]
-                ava = self._ava_from_value(arg)
-                if isinstance(ava, list):
-                    avas.extend(ava)
-                else:
-                    avas.append(ava)
-                i += 1
+                avas.append(ava)
         return avas
 
     def _to_openldap(self):
@@ -753,7 +813,8 @@ class RDN(object):
                 if key == self.avas[i].attr:
                     found = True
                     self.avas[i] = new_ava
-                    if self.first_key_match: break
+                    if self.first_key_match:
+                        break
                 i += 1
             if not found:
                 raise KeyError("\"%s\" not found in %s" % (key, self.__str__()))
@@ -805,25 +866,27 @@ class RDN(object):
             raise TypeError("expected RDN but got %s" % (other.__class__.__name__))
 
         result = cmp(len(self), len(other))
-        if result != 0: return result
+        if result != 0:
+            return result
         i = 0
         while i < len(self):
             result = cmp(self[i], other[i])
-            if result != 0: return result
+            if result != 0:
+                return result
             i += 1
         return 0
 
     def __add__(self, other):
-        result = deepcopy(self)
-        if isinstance(other, self.__class__):
+        result = RDN(self, first_key_match=self.first_key_match)
+        if isinstance(other, RDN):
             for ava in other.avas:
-                result.avas.append(deepcopy(ava))
+                result.avas.append(AVA(ava.attr, ava.value))
         elif isinstance(other, AVA):
-            result.avas.append(deepcopy(other))
+            result.avas.append(AVA(other.attr, other.value))
         elif isinstance(other, basestring):
             rdn = RDN(other)
             for ava in rdn.avas:
-                result.avas.append(deepcopy(ava))
+                result.avas.append(AVA(ava.attr, ava.value))
         else:
             raise TypeError("expected RDN, AVA or basestring but got %s" % (other.__class__.__name__))
 
@@ -831,15 +894,15 @@ class RDN(object):
         return result
 
     def __iadd__(self, other):
-        if isinstance(other, self.__class__):
+        if isinstance(other, RDN):
             for ava in other.avas:
-                self.avas.append(deepcopy(ava))
+                self.avas.append(AVA(ava.attr, ava.value))
         elif isinstance(other, AVA):
-            self.avas.append(deepcopy(other))
+            self.avas.append(AVA(other.attr, other.value))
         elif isinstance(other, basestring):
             rdn = RDN(other)
             for ava in rdn.avas:
-                self.avas.append(deepcopy(ava))
+                self.avas.append(AVA(ava.attr, ava.value))
         else:
             raise TypeError("expected RDN, AVA or basestring but got %s" % (other.__class__.__name__))
 
@@ -848,11 +911,17 @@ class RDN(object):
 
 class DN(object):
     '''
+    DN(arg0, ..., first_key_match=True)
+
     A DN is a LDAP Distinguished Name. A DN is an ordered sequence of RDN's.
 
-    The DN constructor accepts a sequence. The constructor iterates
-    through the sequence and adds the RDN's it finds in order to the
-    DN object. Each item in the sequence may be:
+    The DN constructor is passed a sequence of args and a set of
+    keyword parameters used for configuration. normalize means the
+    attr and value will be converted to lower case.
+
+    The constructor iterates through the sequence and adds the RDN's
+    it finds in order to the DN object. Each item in the sequence may
+    be:
 
     * A 2-valued tuple or list. The first member is the attr and the
       second member is the value of an RDN, both members must be
@@ -866,11 +935,12 @@ class DN(object):
       to yield one or more RDN's which will be appended in order to
       the DN. The parsing recognizes the DN syntax escaping rules.
 
-    * A RDN object. Each RDN object in the argument list will be
-      appended to the DN in order.
+    * A RDN object, the RDN will copied respecting the constructors
+      keyword configuration parameters and appended in order.
 
-    * A DN object. Each DN object in the argument list will append in order
-      it's RDN's to the DN.
+    * A DN object, the RDN's in the DN are copied respecting the
+      constructors keyword configuration parameters and appended in
+      order.
 
     Single DN Examples:
 
@@ -972,17 +1042,18 @@ class DN(object):
 
     flags = 0
 
-    def __init__(self, *args):
+    def __init__(self, *args, **kwds):
+        self.first_key_match = kwds.get('first_key_match', True)
         self.first_key_match = True
         self.rdns = self._rdns_from_sequence(args)
 
     def _rdn_from_value(self, value):
         if isinstance(value, RDN):
-            return deepcopy(value)
+            return RDN(value, first_key_match=self.first_key_match)
         elif isinstance(value, DN):
             rdns = []
             for rdn in value.rdns:
-                rdns.append(deepcopy(rdn))
+                rdns.append(RDN(rdn, first_key_match=self.first_key_match))
             if len(rdns) == 1:
                 return rdns[0]
             else:
@@ -995,7 +1066,7 @@ class DN(object):
                     avas = []
                     for ava_tuple in rdn_list:
                         avas.append(AVA(ava_tuple[0], ava_tuple[1]))
-                    rdn = RDN(*avas)
+                    rdn = RDN(*avas, first_key_match=self.first_key_match)
                     rdns.append(rdn)
             except DECODING_ERROR:
                 raise ValueError("malformed RDN string = \"%s\"" % value)
@@ -1006,7 +1077,7 @@ class DN(object):
         elif isinstance(value, (tuple, list)):
             if len(value) != 2:
                 raise ValueError("tuple or list must be 2-valued, not \"%s\"" % (rdn))
-            rdn = RDN(value)
+            rdn = RDN(value, first_key_match=self.first_key_match)
             return rdn
         else:
             raise TypeError("must be str,unicode,tuple, or RDN, got %s instead" % \
@@ -1098,7 +1169,8 @@ class DN(object):
             raise TypeError("expected DN but got %s" % (other.__class__.__name__))
 
         result = cmp(len(self), len(other))
-        if result != 0: return result
+        if result != 0:
+            return result
         return self._cmp_sequence(other, 0, len(self))
 
     def _cmp_sequence(self, pattern, self_start, pat_len):
@@ -1106,35 +1178,36 @@ class DN(object):
         pat_idx = 0
         while pat_idx < pat_len:
             result = cmp(self[self_idx], pattern[pat_idx])
-            if result != 0: return result
+            if result != 0:
+                return result
             self_idx += 1
             pat_idx += 1
         return 0
 
     def __add__(self, other):
-        result = deepcopy(self)
+        result = DN(self, first_key_match=self.first_key_match)
         if isinstance(other, self.__class__):
             for rdn in other.rdns:
-                result.rdns.append(deepcopy(rdn))
+                result.rdns.append(RDN(rdn, first_key_match=self.first_key_match))
         elif isinstance(other, RDN):
-            result.rdns.append(deepcopy(other))
+            result.rdns.append(RDN(other, first_key_match=self.first_key_match))
         elif isinstance(other, basestring):
-            dn = DN(other)
+            dn = DN(other, first_key_match=self.first_key_match)
             for rdn in dn.rdns:
-                result.rdns.append(deepcopy(rdn))
+                result.rdns.append(rdn)
         else:
             raise TypeError("expected DN, RDN or basestring but got %s" % (other.__class__.__name__))
 
         return result
 
     def __iadd__(self, other):
-        if isinstance(other, self.__class__):
+        if isinstance(other, DN):
             for rdn in other.rdns:
-                self.rdns.append(deepcopy(rdn))
+                self.rdns.append(RDN(rdn, first_key_match=self.first_key_match))
         elif isinstance(other, RDN):
-            self.rdns.append(deepcopy(other))
+            self.rdns.append(RDN(other, first_key_match=self.first_key_match))
         elif isinstance(other, basestring):
-            dn = DN(other)
+            dn = DN(other, first_key_match=self.first_key_match)
             self.__iadd__(dn)
         else:
             raise TypeError("expected DN, RDN or basestring but got %s" % (other.__class__.__name__))
@@ -1174,23 +1247,6 @@ class DN(object):
 
         return self._tailmatch(suffix, start, end, +1)
 
-    def _adjust_indices(self, start, end, length):
-        'helper to fixup start/end slice values'
-
-        if end > length:
-            end = length
-        elif end < 0:
-            end += length
-            if end < 0:
-                end = 0
-
-        if start < 0:
-            start += length
-            if start < 0:
-                start = 0
-
-        return start, end
-
     def _tailmatch(self, pattern, start, end, direction):
         '''
         Matches the end (direction >= 0) or start (direction < 0) of self
@@ -1207,11 +1263,11 @@ class DN(object):
 
         self_len = len(self)
 
-        start, end = self._adjust_indices(start, end, self_len)
+        start, end = _adjust_indices(start, end, self_len)
 
         if direction < 0:       # starswith
             if start+pat_len > self_len:
-                return 0;
+                return 0
         else:                   # endswith
             if end-start < pat_len or start > self_len:
                 return 0
@@ -1222,7 +1278,7 @@ class DN(object):
         if isinstance(pattern, DN):
             if end-start >= pat_len:
                 return not self._cmp_sequence(pattern, start, pat_len)
-            return 0;
+            return 0
         else:
             return self.rdns[start] == pattern
 
diff --git a/tests/test_ipalib/test_dn.py b/tests/test_ipalib/test_dn.py
index 029297f..c647460 100644
--- a/tests/test_ipalib/test_dn.py
+++ b/tests/test_ipalib/test_dn.py
@@ -37,7 +37,7 @@ def make_rdn_args(low, high, kind, attr=None, value=None):
         elif kind == 'list':
             result.append([new_attr, new_value])
         elif kind == 'RDN':
-            result.append(RDN(new_attr, new_value))
+            result.append(RDN((new_attr, new_value)))
         else:
             raise ValueError("Unknown kind = %s" % kind)
 
@@ -162,6 +162,28 @@ class TestAVA(unittest.TestCase):
         result = cmp(ava1, self.ava1)
         self.assertEqual(result, 0)
 
+        # Upper case attr should still be equal
+        ava1 = AVA(self.attr1.upper(), self.value1)
+
+        self.assertFalse(ava1.attr == self.attr1)
+        self.assertTrue(ava1.value == self.value1)
+        self.assertTrue(ava1 == self.ava1)
+        self.assertFalse(ava1 != self.ava1)
+
+        result = cmp(ava1, self.ava1)
+        self.assertEqual(result, 0)
+
+        # Upper case value should still be equal
+        ava1 = AVA(self.attr1, self.value1.upper())
+
+        self.assertTrue(ava1.attr == self.attr1)
+        self.assertFalse(ava1.value == self.value1)
+        self.assertTrue(ava1 == self.ava1)
+        self.assertFalse(ava1 != self.ava1)
+
+        result = cmp(ava1, self.ava1)
+        self.assertEqual(result, 0)
+
         # Make ava1's attr greater
         ava1.attr = self.attr1 + "1"
 
@@ -199,7 +221,7 @@ class TestRDN(unittest.TestCase):
         self.ava1     = AVA(self.attr1, self.value1)
 
         self.str_rdn1 = '%s=%s' % (self.attr1, self.value1)
-        self.rdn1 = RDN(self.attr1, self.value1)
+        self.rdn1 = RDN((self.attr1, self.value1))
 
         self.attr2    = 'ou'
         self.value2   = 'people'
@@ -207,7 +229,7 @@ class TestRDN(unittest.TestCase):
         self.ava2     = AVA(self.attr2, self.value2)
 
         self.str_rdn2 = '%s=%s' % (self.attr2, self.value2)
-        self.rdn2     = RDN(self.attr2, self.value2)
+        self.rdn2     = RDN((self.attr2, self.value2))
 
         self.str_ava3 = '%s=%s+%s=%s' % (self.attr1, self.value1, self.attr2, self.value2)
 
@@ -216,13 +238,6 @@ class TestRDN(unittest.TestCase):
 
     def test_create(self):
         # Create with single attr,value pair
-        rdn1 = RDN(self.attr1, self.value1)
-        self.assertEqual(len(rdn1), 1)
-        self.assertEqual(rdn1, self.rdn1)
-        self.assertIsInstance(rdn1[0], AVA)
-        self.assertEqual(rdn1[0], self.ava1)
-
-        # Create with single attr,value pair passed as a tuple
         rdn1 = RDN((self.attr1, self.value1))
         self.assertEqual(len(rdn1), 1)
         self.assertEqual(rdn1, self.rdn1)
@@ -230,7 +245,7 @@ class TestRDN(unittest.TestCase):
         self.assertEqual(rdn1[0], self.ava1)
 
         # Create with multiple attr,value pairs
-        rdn3 = RDN(self.attr1, self.value1, self.attr2, self.value2)
+        rdn3 = RDN((self.attr1, self.value1), (self.attr2, self.value2))
         self.assertEqual(len(rdn3), 2)
         self.assertEqual(rdn3, self.rdn3)
         self.assertIsInstance(rdn3[0], AVA)
@@ -250,7 +265,7 @@ class TestRDN(unittest.TestCase):
         # Create with multiple attr,value pairs but reverse
         # constructor parameter ordering. RDN canonical ordering
         # should remain the same
-        rdn3 = RDN(self.attr2, self.value2, self.attr1, self.value1)
+        rdn3 = RDN((self.attr2, self.value2), (self.attr1, self.value1))
         self.assertEqual(len(rdn3), 2)
         self.assertEqual(rdn3, self.rdn3)
         self.assertIsInstance(rdn3[0], AVA)
@@ -333,7 +348,7 @@ class TestRDN(unittest.TestCase):
 
     def test_cmp(self):
         # Equality
-        rdn1 = RDN(self.attr1, self.value1)
+        rdn1 = RDN((self.attr1, self.value1))
 
         self.assertTrue(rdn1 == self.rdn1)
         self.assertFalse(rdn1 != self.rdn1)
@@ -404,50 +419,50 @@ class TestRDN(unittest.TestCase):
         self.assertEqual(self.rdn3[:],   [self.ava1, self.ava2])
 
     def test_assignments(self):
-        rdn = RDN(self.attr1, self.value1)
+        rdn = RDN((self.attr1, self.value1))
         rdn[0] = self.ava2
         self.assertEqual(rdn, self.rdn2)
 
-        rdn = RDN(self.attr1, self.value1)
+        rdn = RDN((self.attr1, self.value1))
         rdn[0] = (self.attr2, self.value2)
         self.assertEqual(rdn, self.rdn2)
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         rdn[self.attr1] = self.str_ava2
         self.assertEqual(rdn[0], self.ava2)
 
         # Can't assign multiples to single entry
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         with self.assertRaises(TypeError):
             rdn[self.attr1] = self.str_ava3
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         with self.assertRaises(TypeError):
             rdn[self.attr1] = (self.attr1, self.value1, self.attr2, self.value2)
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         with self.assertRaises(TypeError):
             rdn[self.attr1] = [(self.attr1, self.value1), (self.attr2, self.value2)]
 
         # Slices
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         self.assertEqual(rdn, self.rdn1)
         rdn[0:1] = [self.ava2]
         self.assertEqual(rdn, self.rdn2)
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         self.assertEqual(rdn, self.rdn1)
         rdn[:] = [(self.attr2, self.value2)]
         self.assertEqual(rdn, self.rdn2)
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         self.assertEqual(rdn, self.rdn1)
         rdn[:] = [(self.attr1, self.value1),(self.attr2, self.value2)]
         self.assertEqual(rdn, self.rdn3)
 
-        rdn  = RDN(self.attr1, self.value1)
+        rdn  = RDN((self.attr1, self.value1))
         self.assertEqual(rdn, self.rdn1)
-        rdn[0:1] = [self.attr1, self.value1, self.attr2, self.value2]
+        rdn[0:1] = [(self.attr1, self.value1), (self.attr2, self.value2)]
         self.assertEqual(rdn, self.rdn3)
 
 
@@ -480,23 +495,23 @@ class TestRDN(unittest.TestCase):
 
 
     def test_concat(self):
-        rdn1 = RDN(self.attr1, self.value1)
-        rdn2 = RDN(self.attr2, self.value2)
+        rdn1 = RDN((self.attr1, self.value1))
+        rdn2 = RDN((self.attr2, self.value2))
 
         # in-place addtion
         rdn1 += rdn2
         self.assertEqual(rdn1, self.rdn3)
 
-        rdn1 = RDN(self.attr1, self.value1)
+        rdn1 = RDN((self.attr1, self.value1))
         rdn1 += self.ava2
         self.assertEqual(rdn1, self.rdn3)
 
-        rdn1 = RDN(self.attr1, self.value1)
+        rdn1 = RDN((self.attr1, self.value1))
         rdn1 += self.str_ava2
         self.assertEqual(rdn1, self.rdn3)
 
         # concatenation
-        rdn1 = RDN(self.attr1, self.value1)
+        rdn1 = RDN((self.attr1, self.value1))
         rdn3 = rdn1 + rdn2
         self.assertEqual(rdn3, self.rdn3)
 
@@ -516,7 +531,7 @@ class TestDN(unittest.TestCase):
         self.ava1     = AVA(self.attr1, self.value1)
 
         self.str_rdn1 = '%s=%s' % (self.attr1, self.value1)
-        self.rdn1     = RDN(self.attr1, self.value1)
+        self.rdn1     = RDN((self.attr1, self.value1))
 
         self.attr2    = 'ou'
         self.value2   = 'people'
@@ -524,7 +539,7 @@ class TestDN(unittest.TestCase):
         self.ava2     = AVA(self.attr2, self.value2)
 
         self.str_rdn2 = '%s=%s' % (self.attr2, self.value2)
-        self.rdn2     = RDN(self.attr2, self.value2)
+        self.rdn2     = RDN((self.attr2, self.value2))
 
         self.str_dn1 = self.str_rdn1
         self.dn1 = DN(self.rdn1)
@@ -535,12 +550,12 @@ class TestDN(unittest.TestCase):
         self.str_dn3 = '%s,%s' % (self.str_rdn1, self.str_rdn2)
         self.dn3 = DN(self.rdn1, self.rdn2)
 
-        self.base_rdn1 = RDN('dc', 'redhat')
-        self.base_rdn2 = RDN('dc', 'com')
+        self.base_rdn1 = RDN(('dc', 'redhat'))
+        self.base_rdn2 = RDN(('dc', 'com'))
         self.base_dn = DN(self.base_rdn1, self.base_rdn2)
 
-        self.container_rdn1 = RDN('cn', 'sudorules')
-        self.container_rdn2 = RDN('cn', 'sudo')
+        self.container_rdn1 = RDN(('cn', 'sudorules'))
+        self.container_rdn2 = RDN(('cn', 'sudo'))
         self.container_dn = DN(self.container_rdn1, self.container_rdn2)
 
         self.base_container_dn = DN((self.attr1, self.value1),
@@ -581,7 +596,7 @@ class TestDN(unittest.TestCase):
         self.assertEqual(dn1[1], self.rdn2)
 
         # Create with multiple attr,value pairs passed as tuple and RDN
-        dn1 = DN((self.attr1, self.value1), RDN(self.attr2, self.value2))
+        dn1 = DN((self.attr1, self.value1), RDN((self.attr2, self.value2)))
         self.assertEqual(len(dn1), 2)
         self.assertIsInstance(dn1[0], RDN)
         self.assertIsInstance(dn1[0].attr, unicode)
@@ -809,7 +824,7 @@ class TestDN(unittest.TestCase):
                 value = alt_rdn_value_arg(i)
                 dn1[i] = attr, value
                 dn2[orig_attr] = (attr, value)
-                dn3[i] = RDN(attr, value)
+                dn3[i] = RDN((attr, value))
 
         self.assertEqual(dn1, dn2)
         self.assertEqual(dn1, dn3)
-- 
1.7.4.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to