diff options
author | Jacek Antonelli | 2008-08-15 23:45:34 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:34 -0500 |
commit | cd17687f01420952712a500107e0f93e7ab8d5f8 (patch) | |
tree | ce48c2b706f2c1176290e39fb555fbdf6648ce01 /linden/indra/lib | |
parent | Second Life viewer sources 1.19.0.5 (diff) | |
download | meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.zip meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.gz meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.bz2 meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.xz |
Second Life viewer sources 1.19.1.0
Diffstat (limited to 'linden/indra/lib')
-rw-r--r-- | linden/indra/lib/python/indra/base/config.py | 25 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/base/lllog.py | 72 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/base/lluuid.py | 21 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/base/metrics.py | 23 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/ipc/mysql_pool.py | 5 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/ipc/russ.py | 10 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/util/llsubprocess.py | 106 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/util/named_query.py | 48 | ||||
-rw-r--r-- | linden/indra/lib/python/indra/util/shutil2.py | 84 |
9 files changed, 379 insertions, 15 deletions
diff --git a/linden/indra/lib/python/indra/base/config.py b/linden/indra/lib/python/indra/base/config.py index eea733a..3865f5e 100644 --- a/linden/indra/lib/python/indra/base/config.py +++ b/linden/indra/lib/python/indra/base/config.py | |||
@@ -47,6 +47,25 @@ def load(indra_xml_file=None): | |||
47 | config_file.close() | 47 | config_file.close() |
48 | #print "loaded config from",indra_xml_file,"into",_g_config_dict | 48 | #print "loaded config from",indra_xml_file,"into",_g_config_dict |
49 | 49 | ||
50 | def dump(indra_xml_file, indra_cfg={}, update_in_mem=False): | ||
51 | ''' | ||
52 | Dump config contents into a file | ||
53 | Kindof reverse of load. | ||
54 | Optionally takes a new config to dump. | ||
55 | Does NOT update global config unless requested. | ||
56 | ''' | ||
57 | global _g_config_dict | ||
58 | if not indra_cfg: | ||
59 | indra_cfg = _g_config_dict | ||
60 | if not indra_cfg: | ||
61 | return | ||
62 | config_file = open(indra_xml_file, 'w') | ||
63 | _config_xml = llsd.format_xml(indra_cfg) | ||
64 | config_file.write(_config_xml) | ||
65 | config_file.close() | ||
66 | if update_in_mem: | ||
67 | update(indra_cfg) | ||
68 | |||
50 | def update(new_conf): | 69 | def update(new_conf): |
51 | """Load an XML file and apply its map as overrides or additions | 70 | """Load an XML file and apply its map as overrides or additions |
52 | to the existing config. The dataserver does this with indra.xml | 71 | to the existing config. The dataserver does this with indra.xml |
@@ -69,6 +88,12 @@ def get(key, default = None): | |||
69 | load() | 88 | load() |
70 | return _g_config_dict.get(key, default) | 89 | return _g_config_dict.get(key, default) |
71 | 90 | ||
91 | def set(key, newval): | ||
92 | global _g_config_dict | ||
93 | if _g_config_dict == None: | ||
94 | load() | ||
95 | _g_config_dict[key] = newval | ||
96 | |||
72 | def as_dict(): | 97 | def as_dict(): |
73 | global _g_config_dict | 98 | global _g_config_dict |
74 | return _g_config_dict | 99 | return _g_config_dict |
diff --git a/linden/indra/lib/python/indra/base/lllog.py b/linden/indra/lib/python/indra/base/lllog.py new file mode 100644 index 0000000..99c50ef --- /dev/null +++ b/linden/indra/lib/python/indra/base/lllog.py | |||
@@ -0,0 +1,72 @@ | |||
1 | """\ | ||
2 | @file lllog.py | ||
3 | @brief Logging for event processing | ||
4 | |||
5 | $LicenseInfo:firstyear=2008&license=mit$ | ||
6 | |||
7 | Copyright (c) 2008, Linden Research, Inc. | ||
8 | |||
9 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
10 | of this software and associated documentation files (the "Software"), to deal | ||
11 | in the Software without restriction, including without limitation the rights | ||
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
13 | copies of the Software, and to permit persons to whom the Software is | ||
14 | furnished to do so, subject to the following conditions: | ||
15 | |||
16 | The above copyright notice and this permission notice shall be included in | ||
17 | all copies or substantial portions of the Software. | ||
18 | |||
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
25 | THE SOFTWARE. | ||
26 | $/LicenseInfo$ | ||
27 | """ | ||
28 | |||
29 | from indra.base.llsd import format_notation | ||
30 | |||
31 | try: | ||
32 | import syslog | ||
33 | except ImportError: | ||
34 | # Windows | ||
35 | import sys | ||
36 | |||
37 | class syslog(object): | ||
38 | _logfp = sys.stderr | ||
39 | |||
40 | def syslog(msg): | ||
41 | _logfp.write(msg) | ||
42 | if not msg.endswith('\n'): | ||
43 | _logfp.write('\n') | ||
44 | |||
45 | syslog = staticmethod(syslog) | ||
46 | |||
47 | class Logger(object): | ||
48 | def __init__(self, name='indra'): | ||
49 | self._sequence = 0 | ||
50 | try: | ||
51 | syslog.openlog(name, syslog.LOG_CONS | syslog.LOG_PID, | ||
52 | syslog.LOG_LOCAL0) | ||
53 | except AttributeError: | ||
54 | # No syslog module on Windows | ||
55 | pass | ||
56 | |||
57 | def next(self): | ||
58 | self._sequence += 1 | ||
59 | return self._sequence | ||
60 | |||
61 | def log(self, msg, llsd): | ||
62 | payload = 'LLLOGMESSAGE (%d) %s %s' % (self.next(), msg, | ||
63 | format_notation(llsd)) | ||
64 | syslog.syslog(payload) | ||
65 | |||
66 | _logger = None | ||
67 | |||
68 | def log(msg, llsd): | ||
69 | global _logger | ||
70 | if _logger is None: | ||
71 | _logger = Logger() | ||
72 | _logger.log(msg, llsd) | ||
diff --git a/linden/indra/lib/python/indra/base/lluuid.py b/linden/indra/lib/python/indra/base/lluuid.py index f173310..dd336f0 100644 --- a/linden/indra/lib/python/indra/base/lluuid.py +++ b/linden/indra/lib/python/indra/base/lluuid.py | |||
@@ -227,16 +227,31 @@ def printTranslatedMemory(four_hex_uints): | |||
227 | uuid.setFromMemoryDump(four_hex_uints) | 227 | uuid.setFromMemoryDump(four_hex_uints) |
228 | print uuid.toString() | 228 | print uuid.toString() |
229 | 229 | ||
230 | def isPossiblyID(id_str): | 230 | def isUUID(id_str): |
231 | """ | 231 | """ |
232 | This function returns 1 if the string passed has some uuid-like | 232 | This function returns: |
233 | characteristics. Otherwise returns 0. | 233 | - 1 if the string passed is a UUID |
234 | - 0 is the string passed is not a UUID | ||
235 | - None if it neither of the if's below is satisfied | ||
234 | """ | 236 | """ |
235 | if not id_str or len(id_str) < 5 or len(id_str) > 36: | 237 | if not id_str or len(id_str) < 5 or len(id_str) > 36: |
236 | return 0 | 238 | return 0 |
237 | 239 | ||
238 | if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): | 240 | if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): |
239 | return 1 | 241 | return 1 |
242 | |||
243 | return None | ||
244 | |||
245 | def isPossiblyID(id_str): | ||
246 | """ | ||
247 | This function returns 1 if the string passed has some uuid-like | ||
248 | characteristics. Otherwise returns 0. | ||
249 | """ | ||
250 | |||
251 | is_uuid = isUUID(id_str) | ||
252 | if is_uuid is not None: | ||
253 | return is_uuid | ||
254 | |||
240 | # build a string which matches every character. | 255 | # build a string which matches every character. |
241 | hex_wildcard = r"[0-9a-fA-F]" | 256 | hex_wildcard = r"[0-9a-fA-F]" |
242 | chars = len(id_str) | 257 | chars = len(id_str) |
diff --git a/linden/indra/lib/python/indra/base/metrics.py b/linden/indra/lib/python/indra/base/metrics.py index e640c45..04e6286 100644 --- a/linden/indra/lib/python/indra/base/metrics.py +++ b/linden/indra/lib/python/indra/base/metrics.py | |||
@@ -31,12 +31,23 @@ $/LicenseInfo$ | |||
31 | import sys | 31 | import sys |
32 | from indra.base import llsd | 32 | from indra.base import llsd |
33 | 33 | ||
34 | def log(location, stats, file=None): | 34 | _sequence_id = 0 |
35 | "Write a standard llmetrics log" | 35 | |
36 | metrics = {'location':location, 'stats':stats} | 36 | def record_metrics(table, stats, dest=None): |
37 | if file is None: | 37 | "Write a standard metrics log" |
38 | _log("LLMETRICS", table, stats, dest) | ||
39 | |||
40 | def record_event(table, data, dest=None): | ||
41 | "Write a standard logmessage log" | ||
42 | _log("LLLOGMESSAGE", table, data, dest) | ||
43 | |||
44 | def _log(header, table, data, dest): | ||
45 | if dest is None: | ||
38 | # do this check here in case sys.stdout changes at some | 46 | # do this check here in case sys.stdout changes at some |
39 | # point. as a default parameter, it will never be | 47 | # point. as a default parameter, it will never be |
40 | # re-evaluated. | 48 | # re-evaluated. |
41 | file = sys.stdout | 49 | dest = sys.stdout |
42 | print >>file, "LLMETRICS:", llsd.format_notation(metrics) | 50 | global _sequence_id |
51 | print >>dest, header, "(" + str(_sequence_id) + ")", | ||
52 | print >>dest, table, llsd.format_notation(data) | ||
53 | _sequence_id += 1 | ||
diff --git a/linden/indra/lib/python/indra/ipc/mysql_pool.py b/linden/indra/lib/python/indra/ipc/mysql_pool.py index 0a06cdd..01e31bb 100644 --- a/linden/indra/lib/python/indra/ipc/mysql_pool.py +++ b/linden/indra/lib/python/indra/ipc/mysql_pool.py | |||
@@ -64,12 +64,13 @@ connection pools keyed on host,databasename""" | |||
64 | else: | 64 | else: |
65 | return self._credentials.get('default', None) | 65 | return self._credentials.get('default', None) |
66 | 66 | ||
67 | def get(self, host, dbname): | 67 | def get(self, host, dbname, port=3306): |
68 | key = (host, dbname) | 68 | key = (host, dbname, port) |
69 | if key not in self._databases: | 69 | if key not in self._databases: |
70 | new_kwargs = self._kwargs.copy() | 70 | new_kwargs = self._kwargs.copy() |
71 | new_kwargs['db'] = dbname | 71 | new_kwargs['db'] = dbname |
72 | new_kwargs['host'] = host | 72 | new_kwargs['host'] = host |
73 | new_kwargs['port'] = port | ||
73 | new_kwargs.update(self.credentials_for(host)) | 74 | new_kwargs.update(self.credentials_for(host)) |
74 | dbpool = ConnectionPool(self._min_size, self._max_size, *self._args, **new_kwargs) | 75 | dbpool = ConnectionPool(self._min_size, self._max_size, *self._args, **new_kwargs) |
75 | self._databases[key] = dbpool | 76 | self._databases[key] = dbpool |
diff --git a/linden/indra/lib/python/indra/ipc/russ.py b/linden/indra/lib/python/indra/ipc/russ.py index a198acf..fbb1777 100644 --- a/linden/indra/lib/python/indra/ipc/russ.py +++ b/linden/indra/lib/python/indra/ipc/russ.py | |||
@@ -136,7 +136,15 @@ def _build_query_string(query_dict): | |||
136 | @returns Returns an urlencoded query string including leading '?'. | 136 | @returns Returns an urlencoded query string including leading '?'. |
137 | """ | 137 | """ |
138 | if query_dict: | 138 | if query_dict: |
139 | return '?' + urllib.urlencode(query_dict) | 139 | keys = query_dict.keys() |
140 | keys.sort() | ||
141 | def stringize(value): | ||
142 | if type(value) in (str,unicode): | ||
143 | return value | ||
144 | else: | ||
145 | return str(value) | ||
146 | query_list = [urllib.quote(str(key)) + '=' + urllib.quote(stringize(query_dict[key])) for key in keys] | ||
147 | return '?' + '&'.join(query_list) | ||
140 | else: | 148 | else: |
141 | return '' | 149 | return '' |
142 | 150 | ||
diff --git a/linden/indra/lib/python/indra/util/llsubprocess.py b/linden/indra/lib/python/indra/util/llsubprocess.py new file mode 100644 index 0000000..1c107d9 --- /dev/null +++ b/linden/indra/lib/python/indra/util/llsubprocess.py | |||
@@ -0,0 +1,106 @@ | |||
1 | """\ | ||
2 | @file llsubprocess.py | ||
3 | @author Phoenix | ||
4 | @date 2008-01-18 | ||
5 | @brief The simplest possible wrapper for a common sub-process paradigm. | ||
6 | |||
7 | $LicenseInfo:firstyear=2007&license=mit$ | ||
8 | |||
9 | Copyright (c) 2007-2008, Linden Research, Inc. | ||
10 | |||
11 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
12 | of this software and associated documentation files (the "Software"), to deal | ||
13 | in the Software without restriction, including without limitation the rights | ||
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
15 | copies of the Software, and to permit persons to whom the Software is | ||
16 | furnished to do so, subject to the following conditions: | ||
17 | |||
18 | The above copyright notice and this permission notice shall be included in | ||
19 | all copies or substantial portions of the Software. | ||
20 | |||
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
27 | THE SOFTWARE. | ||
28 | $/LicenseInfo$ | ||
29 | """ | ||
30 | |||
31 | import os | ||
32 | import popen2 | ||
33 | import time | ||
34 | import select | ||
35 | |||
36 | class Timeout(RuntimeError): | ||
37 | "Exception raised when a subprocess times out." | ||
38 | pass | ||
39 | |||
40 | def run(command, args=None, data=None, timeout=None): | ||
41 | """\ | ||
42 | @brief Run command with arguments | ||
43 | |||
44 | This is it. This is the function I want to run all the time when doing | ||
45 | subprocces, but end up copying the code everywhere. none of the | ||
46 | standard commands are secure and provide a way to specify input, get | ||
47 | all the output, and get the result. | ||
48 | @param command A string specifying a process to launch. | ||
49 | @param args Arguments to be passed to command. Must be list, tuple or None. | ||
50 | @param data input to feed to the command. | ||
51 | @param timeout Maximum number of many seconds to run. | ||
52 | @return Returns (result, stdout, stderr) from process. | ||
53 | """ | ||
54 | cmd = [command] | ||
55 | if args: | ||
56 | cmd.extend([str(arg) for arg in args]) | ||
57 | #print "cmd: ","' '".join(cmd) | ||
58 | child = popen2.Popen3(cmd, True) | ||
59 | #print child.pid | ||
60 | out = [] | ||
61 | err = [] | ||
62 | result = -1 | ||
63 | time_left = timeout | ||
64 | tochild = [child.tochild.fileno()] | ||
65 | while True: | ||
66 | time_start = time.time() | ||
67 | #print "time:",time_left | ||
68 | p_in, p_out, p_err = select.select( | ||
69 | [child.fromchild.fileno(), child.childerr.fileno()], | ||
70 | tochild, | ||
71 | [], | ||
72 | time_left) | ||
73 | if p_in: | ||
74 | new_line = os.read(child.fromchild.fileno(), 32 * 1024) | ||
75 | if new_line: | ||
76 | #print "line:",new_line | ||
77 | out.append(new_line) | ||
78 | new_line = os.read(child.childerr.fileno(), 32 * 1024) | ||
79 | if new_line: | ||
80 | #print "error:", new_line | ||
81 | err.append(new_line) | ||
82 | if p_out: | ||
83 | if data: | ||
84 | #print "p_out" | ||
85 | bytes = os.write(child.tochild.fileno(), data) | ||
86 | data = data[bytes:] | ||
87 | if len(data) == 0: | ||
88 | data = None | ||
89 | tochild = [] | ||
90 | child.tochild.close() | ||
91 | result = child.poll() | ||
92 | if result != -1: | ||
93 | child.tochild.close() | ||
94 | child.fromchild.close() | ||
95 | child.childerr.close() | ||
96 | break | ||
97 | if time_left is not None: | ||
98 | time_left -= (time.time() - time_start) | ||
99 | if time_left < 0: | ||
100 | raise Timeout | ||
101 | #print "result:",result | ||
102 | out = ''.join(out) | ||
103 | #print "stdout:", out | ||
104 | err = ''.join(err) | ||
105 | #print "stderr:", err | ||
106 | return result, out, err | ||
diff --git a/linden/indra/lib/python/indra/util/named_query.py b/linden/indra/lib/python/indra/util/named_query.py index c462d9f..c52bfde 100644 --- a/linden/indra/lib/python/indra/util/named_query.py +++ b/linden/indra/lib/python/indra/util/named_query.py | |||
@@ -495,9 +495,51 @@ class _RewriteQueryForArray(object): | |||
495 | if type(value) in (list,tuple): | 495 | if type(value) in (list,tuple): |
496 | rv = [] | 496 | rv = [] |
497 | for idx in range(len(value)): | 497 | for idx in range(len(value)): |
498 | new_key = "_" + key + "_" + str(idx) | 498 | # if the value@idx is array-like, we are |
499 | self.new_params[new_key] = value[idx] | 499 | # probably dealing with a VALUES |
500 | rv.append("%(" + new_key + ")s") | 500 | new_key = "_%s_%s"%(key, str(idx)) |
501 | val_item = value[idx] | ||
502 | if type(val_item) in (list, tuple, dict): | ||
503 | if type(val_item) is dict: | ||
504 | # this is because in Python, the order of | ||
505 | # key, value retrieval from the dict is not | ||
506 | # guaranteed to match what the input intended | ||
507 | # and for VALUES, order is important. | ||
508 | # TODO: Implemented ordered dict in LLSD parser? | ||
509 | raise ExpectationFailed('Only lists/tuples allowed,\ | ||
510 | received dict') | ||
511 | values_keys = [] | ||
512 | for value_idx, item in enumerate(val_item): | ||
513 | # we want a key of the format : | ||
514 | # key_#replacement_#value_row_#value_col | ||
515 | # ugh... so if we are replacing 10 rows in user_note, | ||
516 | # the first values clause would read (for @:user_notes) :- | ||
517 | # ( :_user_notes_0_1_1, :_user_notes_0_1_2, :_user_notes_0_1_3 ) | ||
518 | # the input LLSD for VALUES will look like: | ||
519 | # <llsd>... | ||
520 | # <map> | ||
521 | # <key>user_notes</key> | ||
522 | # <array> | ||
523 | # <array> <!-- row 1 for VALUES --> | ||
524 | # <string>...</string> | ||
525 | # <string>...</string> | ||
526 | # <string>...</string> | ||
527 | # </array> | ||
528 | # ... | ||
529 | # </array> | ||
530 | # </map> | ||
531 | # ... </llsd> | ||
532 | values_key = "%s_%s"%(new_key, value_idx) | ||
533 | self.new_params[values_key] = item | ||
534 | values_keys.append("%%(%s)s"%values_key) | ||
535 | # now collapse all these new place holders enclosed in () | ||
536 | # from [':_key_0_1_1', ':_key_0_1_2', ':_key_0_1_3,...] | ||
537 | # rv will have [ '(:_key_0_1_1, :_key_0_1_2, :_key_0_1_3)', ] | ||
538 | # which is flattened a few lines below join(rv) | ||
539 | rv.append('(%s)' % ','.join(values_keys)) | ||
540 | else: | ||
541 | self.new_params[new_key] = val_item | ||
542 | rv.append("%%(%s)s"%new_key) | ||
501 | return ','.join(rv) | 543 | return ','.join(rv) |
502 | else: | 544 | else: |
503 | # not something that can be expanded, so just drop the | 545 | # not something that can be expanded, so just drop the |
diff --git a/linden/indra/lib/python/indra/util/shutil2.py b/linden/indra/lib/python/indra/util/shutil2.py new file mode 100644 index 0000000..2c504c2 --- /dev/null +++ b/linden/indra/lib/python/indra/util/shutil2.py | |||
@@ -0,0 +1,84 @@ | |||
1 | ''' | ||
2 | @file shutil2.py | ||
3 | @brief a better shutil.copytree replacement | ||
4 | |||
5 | $LicenseInfo:firstyear=2007&license=mit$ | ||
6 | |||
7 | Copyright (c) 2007-2008, Linden Research, Inc. | ||
8 | |||
9 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
10 | of this software and associated documentation files (the "Software"), to deal | ||
11 | in the Software without restriction, including without limitation the rights | ||
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
13 | copies of the Software, and to permit persons to whom the Software is | ||
14 | furnished to do so, subject to the following conditions: | ||
15 | |||
16 | The above copyright notice and this permission notice shall be included in | ||
17 | all copies or substantial portions of the Software. | ||
18 | |||
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
25 | THE SOFTWARE. | ||
26 | $/LicenseInfo$ | ||
27 | ''' | ||
28 | |||
29 | # | ||
30 | # shutil2.py | ||
31 | # Taken from http://www.scons.org/wiki/AccumulateBuilder | ||
32 | # the stock copytree sucks because it insists that the | ||
33 | # target dir not exist | ||
34 | # | ||
35 | |||
36 | import os.path | ||
37 | import shutil | ||
38 | |||
39 | def copytree(src, dest, symlinks=False): | ||
40 | """My own copyTree which does not fail if the directory exists. | ||
41 | |||
42 | Recursively copy a directory tree using copy2(). | ||
43 | |||
44 | If the optional symlinks flag is true, symbolic links in the | ||
45 | source tree result in symbolic links in the destination tree; if | ||
46 | it is false, the contents of the files pointed to by symbolic | ||
47 | links are copied. | ||
48 | |||
49 | Behavior is meant to be identical to GNU 'cp -R'. | ||
50 | """ | ||
51 | def copyItems(src, dest, symlinks=False): | ||
52 | """Function that does all the work. | ||
53 | |||
54 | It is necessary to handle the two 'cp' cases: | ||
55 | - destination does exist | ||
56 | - destination does not exist | ||
57 | |||
58 | See 'cp -R' documentation for more details | ||
59 | """ | ||
60 | for item in os.listdir(src): | ||
61 | srcPath = os.path.join(src, item) | ||
62 | if os.path.isdir(srcPath): | ||
63 | srcBasename = os.path.basename(srcPath) | ||
64 | destDirPath = os.path.join(dest, srcBasename) | ||
65 | if not os.path.exists(destDirPath): | ||
66 | os.makedirs(destDirPath) | ||
67 | copyItems(srcPath, destDirPath) | ||
68 | elif os.path.islink(item) and symlinks: | ||
69 | linkto = os.readlink(item) | ||
70 | os.symlink(linkto, dest) | ||
71 | else: | ||
72 | shutil.copy2(srcPath, dest) | ||
73 | |||
74 | # case 'cp -R src/ dest/' where dest/ already exists | ||
75 | if os.path.exists(dest): | ||
76 | destPath = os.path.join(dest, os.path.basename(src)) | ||
77 | if not os.path.exists(destPath): | ||
78 | os.makedirs(destPath) | ||
79 | # case 'cp -R src/ dest/' where dest/ does not exist | ||
80 | else: | ||
81 | os.makedirs(dest) | ||
82 | destPath = dest | ||
83 | # actually copy the files | ||
84 | copyItems(src, destPath) | ||