From 8eea75dc3f4138a0b216e8d662089d2c930d5d45 Mon Sep 17 00:00:00 2001
From: Josh Bell <josh@lindenlab.com>
Date: Fri, 15 Feb 2008 18:47:45 +0000
Subject: svn merge -r 79841:79886
 svn+ssh://svn.lindenlab.com/svn/linden/branches/self-contained-unit-test -->
 release

QAR-291/QAR-284/QAR-246 - Self-Contained Unit Test
---
 indra/lib/python/indra/base/config.py      | 25 +++++++++
 indra/lib/python/indra/base/lluuid.py      | 21 ++++++--
 indra/lib/python/indra/ipc/mysql_pool.py   |  5 +-
 indra/lib/python/indra/util/named_query.py | 48 +++++++++++++++--
 indra/lib/python/indra/util/shutil2.py     | 84 ++++++++++++++++++++++++++++++
 5 files changed, 175 insertions(+), 8 deletions(-)
 create mode 100644 indra/lib/python/indra/util/shutil2.py

diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py
index c6872fa251..a28c59c702 100644
--- a/indra/lib/python/indra/base/config.py
+++ b/indra/lib/python/indra/base/config.py
@@ -47,6 +47,25 @@ def load(indra_xml_file=None):
         config_file.close()
         #print "loaded config from",indra_xml_file,"into",_g_config_dict
 
+def dump(indra_xml_file, indra_cfg={}, update_in_mem=False):
+    '''
+    Dump config contents into a file
+    Kindof reverse of load.
+    Optionally takes a new config to dump.
+    Does NOT update global config unless requested.
+    '''
+    global _g_config_dict
+    if not indra_cfg:
+        indra_cfg = _g_config_dict
+    if not indra_cfg:
+        return
+    config_file = open(indra_xml_file, 'w')
+    _config_xml = llsd.format_xml(indra_cfg)
+    config_file.write(_config_xml)
+    config_file.close()
+    if update_in_mem:
+        update(indra_cfg)
+
 def update(new_conf):
     """Load an XML file and apply its map as overrides or additions
     to the existing config.  The dataserver does this with indra.xml
@@ -69,6 +88,12 @@ def get(key, default = None):
         load()
     return _g_config_dict.get(key, default)
 
+def set(key, newval):
+    global _g_config_dict
+    if _g_config_dict == None:
+        load()
+    _g_config_dict[key] = newval
+
 def as_dict():
     global _g_config_dict
     return _g_config_dict
diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py
index 609c8cc261..019ccfc215 100644
--- a/indra/lib/python/indra/base/lluuid.py
+++ b/indra/lib/python/indra/base/lluuid.py
@@ -227,16 +227,31 @@ def printTranslatedMemory(four_hex_uints):
     uuid.setFromMemoryDump(four_hex_uints)
     print uuid.toString()
 
-def isPossiblyID(id_str):
+def isUUID(id_str):
     """
-    This function returns 1 if the string passed has some uuid-like
-    characteristics. Otherwise returns 0.
+    This function returns:
+    - 1 if the string passed is a UUID
+    - 0 is the string passed is not a UUID
+    - None if it neither of the if's below is satisfied
     """
     if not id_str or len(id_str) <  5 or len(id_str) > 36:
         return 0
 
     if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str):
         return 1
+
+    return None
+
+def isPossiblyID(id_str):
+    """
+    This function returns 1 if the string passed has some uuid-like
+    characteristics. Otherwise returns 0.
+    """
+
+    is_uuid = isUUID(id_str)
+    if is_uuid is not None:
+        return is_uuid
+
     # build a string which matches every character.
     hex_wildcard = r"[0-9a-fA-F]"
     chars = len(id_str)
diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py
index 6288a4c732..827b6d42e9 100644
--- a/indra/lib/python/indra/ipc/mysql_pool.py
+++ b/indra/lib/python/indra/ipc/mysql_pool.py
@@ -64,12 +64,13 @@ connection pools keyed on host,databasename"""
         else:
             return self._credentials.get('default', None)
 
-    def get(self, host, dbname):
-        key = (host, dbname)
+    def get(self, host, dbname, port=3306):
+        key = (host, dbname, port)
         if key not in self._databases:
             new_kwargs = self._kwargs.copy()
             new_kwargs['db'] = dbname
             new_kwargs['host'] = host
+            new_kwargs['port'] = port
             new_kwargs.update(self.credentials_for(host))
             dbpool = ConnectionPool(self._min_size, self._max_size, *self._args, **new_kwargs)
             self._databases[key] = dbpool
diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py
index c3bdd046fd..471998388d 100644
--- a/indra/lib/python/indra/util/named_query.py
+++ b/indra/lib/python/indra/util/named_query.py
@@ -495,9 +495,51 @@ class _RewriteQueryForArray(object):
         if type(value) in (list,tuple):
             rv = []
             for idx in range(len(value)):
-                new_key = "_" + key + "_" + str(idx)
-                self.new_params[new_key] = value[idx]
-                rv.append("%(" + new_key + ")s")
+                # if the value@idx is array-like, we are
+                # probably dealing with a VALUES
+                new_key = "_%s_%s"%(key, str(idx))
+                val_item = value[idx]
+                if type(val_item) in (list, tuple, dict):
+                    if type(val_item) is dict:
+                        # this is because in Python, the order of 
+                        # key, value retrieval from the dict is not
+                        # guaranteed to match what the input intended
+                        # and for VALUES, order is important.
+                        # TODO: Implemented ordered dict in LLSD parser?
+                        raise ExpectationFailed('Only lists/tuples allowed,\
+                                received dict')
+                    values_keys = []
+                    for value_idx, item in enumerate(val_item):
+                        # we want a key of the format :
+                        # key_#replacement_#value_row_#value_col
+                        # ugh... so if we are replacing 10 rows in user_note, 
+                        # the first values clause would read (for @:user_notes) :-
+                        # ( :_user_notes_0_1_1,  :_user_notes_0_1_2, :_user_notes_0_1_3 )
+                        # the input LLSD for VALUES will look like:
+                        # <llsd>...
+                        # <map>
+                        #  <key>user_notes</key>
+                        #      <array>
+                        #      <array> <!-- row 1 for VALUES -->
+                        #          <string>...</string>
+                        #          <string>...</string>
+                        #          <string>...</string>
+                        #      </array>
+                        # ...
+                        #      </array>
+                        # </map>
+                        # ... </llsd>
+                        values_key = "%s_%s"%(new_key, value_idx)
+                        self.new_params[values_key] = item
+                        values_keys.append("%%(%s)s"%values_key)
+                    # now collapse all these new place holders enclosed in ()
+                    # from [':_key_0_1_1', ':_key_0_1_2', ':_key_0_1_3,...] 
+                    # rv will have [ '(:_key_0_1_1, :_key_0_1_2, :_key_0_1_3)', ]
+                    # which is flattened a few lines below join(rv)
+                    rv.append('(%s)' % ','.join(values_keys))
+                else:
+                    self.new_params[new_key] = val_item
+                    rv.append("%%(%s)s"%new_key)
             return ','.join(rv)
         else:
             # not something that can be expanded, so just drop the
diff --git a/indra/lib/python/indra/util/shutil2.py b/indra/lib/python/indra/util/shutil2.py
new file mode 100644
index 0000000000..3acb44bf6f
--- /dev/null
+++ b/indra/lib/python/indra/util/shutil2.py
@@ -0,0 +1,84 @@
+'''
+@file shutil2.py
+@brief a better shutil.copytree replacement
+
+$LicenseInfo:firstyear=2007&license=mit$
+
+Copyright (c) 2007, Linden Research, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+$/LicenseInfo$
+'''
+
+#
+# shutil2.py
+# Taken from http://www.scons.org/wiki/AccumulateBuilder
+# the stock copytree sucks because it insists that the
+# target dir not exist
+#
+
+import os.path
+import shutil
+
+def copytree(src, dest, symlinks=False):
+    """My own copyTree which does not fail if the directory exists.
+    
+    Recursively copy a directory tree using copy2().
+
+    If the optional symlinks flag is true, symbolic links in the
+    source tree result in symbolic links in the destination tree; if
+    it is false, the contents of the files pointed to by symbolic
+    links are copied.
+    
+    Behavior is meant to be identical to GNU 'cp -R'.    
+    """
+    def copyItems(src, dest, symlinks=False):
+        """Function that does all the work.
+        
+        It is necessary to handle the two 'cp' cases:
+        - destination does exist
+        - destination does not exist
+        
+        See 'cp -R' documentation for more details
+        """
+        for item in os.listdir(src):
+           srcPath = os.path.join(src, item)
+           if os.path.isdir(srcPath):
+               srcBasename = os.path.basename(srcPath)
+               destDirPath = os.path.join(dest, srcBasename)
+               if not os.path.exists(destDirPath):
+                   os.makedirs(destDirPath)
+               copyItems(srcPath, destDirPath)
+           elif os.path.islink(item) and symlinks:
+               linkto = os.readlink(item)
+               os.symlink(linkto, dest)
+           else:
+               shutil.copy2(srcPath, dest)
+
+    # case 'cp -R src/ dest/' where dest/ already exists
+    if os.path.exists(dest):
+       destPath = os.path.join(dest, os.path.basename(src))
+       if not os.path.exists(destPath):
+           os.makedirs(destPath)
+    # case 'cp -R src/ dest/' where dest/ does not exist
+    else:
+       os.makedirs(dest)
+       destPath = dest
+    # actually copy the files
+    copyItems(src, destPath)
-- 
cgit v1.2.3