diff options
Diffstat (limited to 'indra/lib/python')
32 files changed, 171 insertions, 161 deletions
diff --git a/indra/lib/python/indra/__init__.py b/indra/lib/python/indra/__init__.py index 0c5053cf49..0c5053cf49 100644..100755 --- a/indra/lib/python/indra/__init__.py +++ b/indra/lib/python/indra/__init__.py diff --git a/indra/lib/python/indra/base/__init__.py b/indra/lib/python/indra/base/__init__.py index 2904fd3380..2904fd3380 100644..100755 --- a/indra/lib/python/indra/base/__init__.py +++ b/indra/lib/python/indra/base/__init__.py diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py index 1f06898ffd..1f06898ffd 100644..100755 --- a/indra/lib/python/indra/base/cllsd_test.py +++ b/indra/lib/python/indra/base/cllsd_test.py diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py index adafa29b51..adafa29b51 100644..100755 --- a/indra/lib/python/indra/base/config.py +++ b/indra/lib/python/indra/base/config.py diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index 4527b115f9..4527b115f9 100644..100755 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py index 369ae4e92f..369ae4e92f 100644..100755 --- a/indra/lib/python/indra/base/lluuid.py +++ b/indra/lib/python/indra/base/lluuid.py diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py index ff8380265f..ff8380265f 100644..100755 --- a/indra/lib/python/indra/base/metrics.py +++ b/indra/lib/python/indra/base/metrics.py diff --git a/indra/lib/python/indra/ipc/__init__.py b/indra/lib/python/indra/ipc/__init__.py index 302bbf4a03..302bbf4a03 100644..100755 --- a/indra/lib/python/indra/ipc/__init__.py +++ b/indra/lib/python/indra/ipc/__init__.py diff --git a/indra/lib/python/indra/ipc/compatibility.py b/indra/lib/python/indra/ipc/compatibility.py index b9045c22f3..b9045c22f3 100644..100755 --- a/indra/lib/python/indra/ipc/compatibility.py +++ b/indra/lib/python/indra/ipc/compatibility.py diff --git a/indra/lib/python/indra/ipc/httputil.py b/indra/lib/python/indra/ipc/httputil.py index d53f34a771..d53f34a771 100644..100755 --- a/indra/lib/python/indra/ipc/httputil.py +++ b/indra/lib/python/indra/ipc/httputil.py diff --git a/indra/lib/python/indra/ipc/llmessage.py b/indra/lib/python/indra/ipc/llmessage.py index 91fb36b72c..91fb36b72c 100644..100755 --- a/indra/lib/python/indra/ipc/llmessage.py +++ b/indra/lib/python/indra/ipc/llmessage.py diff --git a/indra/lib/python/indra/ipc/llsdhttp.py b/indra/lib/python/indra/ipc/llsdhttp.py index cbe8ee1eca..cbe8ee1eca 100644..100755 --- a/indra/lib/python/indra/ipc/llsdhttp.py +++ b/indra/lib/python/indra/ipc/llsdhttp.py diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py index e5855a3091..e5855a3091 100644..100755 --- a/indra/lib/python/indra/ipc/mysql_pool.py +++ b/indra/lib/python/indra/ipc/mysql_pool.py diff --git a/indra/lib/python/indra/ipc/russ.py b/indra/lib/python/indra/ipc/russ.py index ac780f128b..ac780f128b 100644..100755 --- a/indra/lib/python/indra/ipc/russ.py +++ b/indra/lib/python/indra/ipc/russ.py diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py index 0a0ce2b4e2..0a0ce2b4e2 100644..100755 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ b/indra/lib/python/indra/ipc/servicebuilder.py diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py index d867e71537..d867e71537 100644..100755 --- a/indra/lib/python/indra/ipc/siesta.py +++ b/indra/lib/python/indra/ipc/siesta.py diff --git a/indra/lib/python/indra/ipc/siesta_test.py b/indra/lib/python/indra/ipc/siesta_test.py index a35eed2460..a35eed2460 100644..100755 --- a/indra/lib/python/indra/ipc/siesta_test.py +++ b/indra/lib/python/indra/ipc/siesta_test.py diff --git a/indra/lib/python/indra/ipc/tokenstream.py b/indra/lib/python/indra/ipc/tokenstream.py index b96f26d3ff..b96f26d3ff 100644..100755 --- a/indra/lib/python/indra/ipc/tokenstream.py +++ b/indra/lib/python/indra/ipc/tokenstream.py diff --git a/indra/lib/python/indra/ipc/webdav.py b/indra/lib/python/indra/ipc/webdav.py index 98b8499b6a..98b8499b6a 100644..100755 --- a/indra/lib/python/indra/ipc/webdav.py +++ b/indra/lib/python/indra/ipc/webdav.py diff --git a/indra/lib/python/indra/ipc/xml_rpc.py b/indra/lib/python/indra/ipc/xml_rpc.py index 47536c10c3..47536c10c3 100644..100755 --- a/indra/lib/python/indra/ipc/xml_rpc.py +++ b/indra/lib/python/indra/ipc/xml_rpc.py diff --git a/indra/lib/python/indra/util/__init__.py b/indra/lib/python/indra/util/__init__.py index b004e5804f..b004e5804f 100644..100755 --- a/indra/lib/python/indra/util/__init__.py +++ b/indra/lib/python/indra/util/__init__.py diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py index 4fcf662dd9..4fcf662dd9 100644..100755 --- a/indra/lib/python/indra/util/fastest_elementtree.py +++ b/indra/lib/python/indra/util/fastest_elementtree.py diff --git a/indra/lib/python/indra/util/helpformatter.py b/indra/lib/python/indra/util/helpformatter.py index ba5c9b67d1..ba5c9b67d1 100644..100755 --- a/indra/lib/python/indra/util/helpformatter.py +++ b/indra/lib/python/indra/util/helpformatter.py diff --git a/indra/lib/python/indra/util/iterators.py b/indra/lib/python/indra/util/iterators.py index 9013fa6303..9013fa6303 100644..100755 --- a/indra/lib/python/indra/util/iterators.py +++ b/indra/lib/python/indra/util/iterators.py diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index c33a03034a..52b4acbc94 100644..100755 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -41,6 +41,14 @@ import tarfile import errno import subprocess +class ManifestError(RuntimeError): + """Use an exception more specific than generic Python RuntimeError""" + pass + +class MissingError(ManifestError): + """You specified a file that doesn't exist""" + pass + def path_ancestors(path): drive, path = os.path.splitdrive(os.path.normpath(path)) result = [] @@ -76,30 +84,8 @@ def get_default_platform(dummy): 'darwin':'darwin' }[sys.platform] -def get_default_version(srctree): - # look up llversion.h and parse out the version info - paths = [os.path.join(srctree, x, 'llversionviewer.h') for x in ['llcommon', '../llcommon', '../../indra/llcommon.h']] - for p in paths: - if os.path.exists(p): - contents = open(p, 'r').read() - major = re.search("LL_VERSION_MAJOR\s=\s([0-9]+)", contents).group(1) - minor = re.search("LL_VERSION_MINOR\s=\s([0-9]+)", contents).group(1) - patch = re.search("LL_VERSION_PATCH\s=\s([0-9]+)", contents).group(1) - build = re.search("LL_VERSION_BUILD\s=\s([0-9]+)", contents).group(1) - return major, minor, patch, build - -def get_channel(srctree): - # look up llversionserver.h and parse out the version info - paths = [os.path.join(srctree, x, 'llversionviewer.h') for x in ['llcommon', '../llcommon', '../../indra/llcommon.h']] - for p in paths: - if os.path.exists(p): - contents = open(p, 'r').read() - channel = re.search("LL_CHANNEL\s=\s\"(.+)\";\s*$", contents, flags = re.M).group(1) - return channel - - DEFAULT_SRCTREE = os.path.dirname(sys.argv[0]) -DEFAULT_CHANNEL = 'Second Life Release' +RELEASE_CHANNEL = 'Second Life Release' ARGUMENTS=[ dict(name='actions', @@ -132,10 +118,7 @@ ARGUMENTS=[ default=""), dict(name='channel', description="""The channel to use for updates, packaging, settings name, etc.""", - default=get_channel), - dict(name='login_channel', - description="""The channel to use for login handshake/updates only.""", - default=None), + default='CHANNEL UNSET'), dict(name='installer_name', description=""" The name of the file that the installer should be packaged up into. Only used on Linux at the moment.""", @@ -156,10 +139,13 @@ ARGUMENTS=[ contain the name of the final package in a form suitable for use by a .bat file.""", default=None), - dict(name='version', - description="""This specifies the version of Second Life that is - being packaged up.""", - default=get_default_version) + dict(name='versionfile', + description="""The name of a file containing the full version number."""), + dict(name='signature', + description="""This specifies an identity to sign the viewer with, if any. + If no value is supplied, the default signature will be used, if any. Currently + only used on Mac OS X.""", + default=None) ] def usage(srctree=""): @@ -180,6 +166,9 @@ def usage(srctree=""): arg['description'] % nd) def main(): +## import itertools +## print ' '.join((("'%s'" % item) if ' ' in item else item) +## for item in itertools.chain([sys.executable], sys.argv)) option_names = [arg['name'] + '=' for arg in ARGUMENTS] option_names.append('help') options, remainder = getopt.getopt(sys.argv[1:], "", option_names) @@ -216,9 +205,14 @@ def main(): args[arg['name']] = default # fix up version - if isinstance(args.get('version'), str): - args['version'] = args['version'].split('.') - + if isinstance(args.get('versionfile'), str): + try: # read in the version string + vf = open(args['versionfile'], 'r') + args['version'] = vf.read().strip().split('.') + except: + print "Unable to read versionfile '%s'" % args['versionfile'] + raise + # default and agni are default if args['grid'] in ['default', 'agni']: args['grid'] = '' @@ -230,15 +224,98 @@ def main(): for opt in args: print "Option:", opt, "=", args[opt] + # pass in sourceid as an argument now instead of an environment variable + try: + args['sourceid'] = os.environ["sourceid"] + except KeyError: + args['sourceid'] = "" + + # Build base package. + touch = args.get('touch') + if touch: + print 'Creating base package' + args['package_id'] = "" # base package has no package ID wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) - + # Store package file for later if making touched file. + base_package_file = "" + if touch: + print 'Created base package ', wm.package_file + base_package_file = "" + wm.package_file + + # handle multiple packages if set + try: + additional_packages = os.environ["additional_packages"] + except KeyError: + additional_packages = "" + if additional_packages: + # Determine destination prefix / suffix for additional packages. + base_dest_postfix = args['dest'] + base_dest_prefix = "" + base_dest_parts = args['dest'].split(os.sep) + if len(base_dest_parts) > 1: + base_dest_postfix = base_dest_parts[len(base_dest_parts) - 1] + base_dest_prefix = base_dest_parts[0] + i = 1 + while i < len(base_dest_parts) - 1: + base_dest_prefix = base_dest_prefix + os.sep + base_dest_parts[i] + i = i + 1 + # Determine touched prefix / suffix for additional packages. + base_touch_postfix = "" + base_touch_prefix = "" + if touch: + base_touch_postfix = touch + base_touch_parts = touch.split('/') + if "arwin" in args['platform']: + if len(base_touch_parts) > 1: + base_touch_postfix = base_touch_parts[len(base_touch_parts) - 1] + base_touch_prefix = base_touch_parts[0] + i = 1 + while i < len(base_touch_parts) - 1: + base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] + i = i + 1 + else: + if len(base_touch_parts) > 2: + base_touch_postfix = base_touch_parts[len(base_touch_parts) - 2] + '/' + base_touch_parts[len(base_touch_parts) - 1] + base_touch_prefix = base_touch_parts[0] + i = 1 + while i < len(base_touch_parts) - 2: + base_touch_prefix = base_touch_prefix + '/' + base_touch_parts[i] + i = i + 1 + # Store base channel name. + base_channel_name = args['channel'] + # Build each additional package. + package_id_list = additional_packages.split(" ") + for package_id in package_id_list: + try: + args['package_id'] = package_id + args['channel'] = base_channel_name + os.environ[package_id + "_viewer_channel_suffix"] + if package_id + "_sourceid" in os.environ: + args['sourceid'] = os.environ[package_id + "_sourceid"] + else: + args['sourceid'] = "" + args['dest'] = base_dest_prefix + os.sep + package_id + os.sep + base_dest_postfix + except KeyError: + sys.stderr.write("Failed to create package for package_id: %s" % package_id) + sys.stderr.flush() + continue + if touch: + print 'Creating additional package for ', package_id, ' in ', args['dest'] + wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) + wm.do(*args['actions']) + if touch: + print 'Created additional package ', wm.package_file, ' for ', package_id + faketouch = base_touch_prefix + '/' + package_id + '/' + base_touch_postfix + fp = open(faketouch, 'w') + fp.write('set package_file=%s\n' % wm.package_file) + fp.close() + # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... touch = args.get('touch') if touch: fp = open(touch, 'w') - fp.write('set package_file=%s\n' % wm.package_file) + fp.write('set package_file=%s\n' % base_package_file) fp.close() print 'touched', touch return 0 @@ -275,7 +352,7 @@ class LLManifest(object): def default_grid(self): return self.args.get('grid', None) == '' def default_channel(self): - return self.args.get('channel', None) == DEFAULT_CHANNEL + return self.args.get('channel', None) == RELEASE_CHANNEL def construct(self): """ Meant to be overriden by LLManifest implementors with code that @@ -385,7 +462,7 @@ class LLManifest(object): child.stdout.close() status = child.wait() if status: - raise RuntimeError( + raise ManifestError( "Command %s returned non-zero status (%s) \noutput:\n%s" % (command, status, output) ) return output @@ -395,14 +472,24 @@ class LLManifest(object): a) verify that you really have created it b) schedule it for cleanup""" if not os.path.exists(path): - raise RuntimeError, "Should be something at path " + path + raise ManifestError, "Should be something at path " + path self.created_paths.append(path) - def put_in_file(self, contents, dst): + def put_in_file(self, contents, dst, src=None): # write contents as dst - f = open(self.dst_path_of(dst), "wb") - f.write(contents) - f.close() + dst_path = self.dst_path_of(dst) + f = open(dst_path, "wb") + try: + f.write(contents) + finally: + f.close() + + # Why would we create a file in the destination tree if not to include + # it in the installer? The default src=None (plus the fact that the + # src param is last) is to preserve backwards compatibility. + if src: + self.file_list.append([src, dst_path]) + return dst_path def replace_in(self, src, dst=None, searchdict={}): if dst == None: @@ -550,7 +637,7 @@ class LLManifest(object): except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: - raise RuntimeError, errors + raise ManifestError, errors def cmakedirs(self, path): @@ -598,11 +685,10 @@ class LLManifest(object): def check_file_exists(self, path): if not os.path.exists(path) and not os.path.islink(path): - raise RuntimeError("Path %s doesn't exist" % ( - os.path.normpath(os.path.join(os.getcwd(), path)),)) + raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),)) - wildcard_pattern = re.compile('\*') + wildcard_pattern = re.compile(r'\*') def expand_globs(self, src, dst): src_list = glob.glob(src) src_re, d_template = self.wildcard_regex(src.replace('\\', '/'), @@ -611,11 +697,28 @@ class LLManifest(object): d = src_re.sub(d_template, s.replace('\\', '/')) yield os.path.normpath(s), os.path.normpath(d) + def path2basename(self, path, file): + """ + It is a common idiom to write: + self.path(os.path.join(somedir, somefile), somefile) + + So instead you can write: + self.path2basename(somedir, somefile) + + Note that this is NOT the same as: + self.path(os.path.join(somedir, somefile)) + + which is the same as: + temppath = os.path.join(somedir, somefile) + self.path(temppath, temppath) + """ + return self.path(os.path.join(path, file), file) + def path(self, src, dst=None): sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() if src == None: - raise RuntimeError("No source file, dst is " + dst) + raise ManifestError("No source file, dst is " + dst) if dst == None: dst = src dst = os.path.join(self.get_dst_prefix(), dst) @@ -637,15 +740,29 @@ class LLManifest(object): else: count += self.process_file(src, dst) return count - try: - count = try_path(os.path.join(self.get_src_prefix(), src)) - except RuntimeError: + + for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix(): try: - count = try_path(os.path.join(self.get_artwork_prefix(), src)) - except RuntimeError: - count = try_path(os.path.join(self.get_build_prefix(), src)) + count = try_path(os.path.join(pfx, src)) + except MissingError: + # If src isn't a wildcard, and if that file doesn't exist in + # this pfx, try next pfx. + count = 0 + continue + + # Here try_path() didn't raise MissingError. Did it process any files? + if count: + break + # Even though try_path() didn't raise MissingError, it returned 0 + # files. src is probably a wildcard meant for some other pfx. Loop + # back to try the next. + print "%d files" % count + # Let caller check whether we processed as many files as expected. In + # particular, let caller notice 0. + return count + def do(self, *actions): self.actions = actions self.construct() diff --git a/indra/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py index 7e0e115d14..7e0e115d14 100644..100755 --- a/indra/lib/python/indra/util/llsubprocess.py +++ b/indra/lib/python/indra/util/llsubprocess.py diff --git a/indra/lib/python/indra/util/llversion.py b/indra/lib/python/indra/util/llversion.py deleted file mode 100644 index ba6f567b60..0000000000 --- a/indra/lib/python/indra/util/llversion.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -"""\ -@file llversion.py -@brief Parses llcommon/llversionserver.h and llcommon/llversionviewer.h - for the version string and channel string. - Parses hg info for branch and revision. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, 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$ -""" - -import re, sys, os, subprocess - -# Methods for gathering version information from -# llversionviewer.h and llversionserver.h - -def get_src_root(): - indra_lib_python_indra_path = os.path.dirname(__file__) - return os.path.abspath(os.path.realpath(indra_lib_python_indra_path + "/../../../../../")) - -def get_version_file_contents(version_type): - filepath = get_src_root() + '/indra/llcommon/llversion%s.h' % version_type - file = open(filepath,"r") - file_str = file.read() - file.close() - return file_str - -def get_version(version_type): - file_str = get_version_file_contents(version_type) - m = re.search('const S32 LL_VERSION_MAJOR = (\d+);', file_str) - VER_MAJOR = m.group(1) - m = re.search('const S32 LL_VERSION_MINOR = (\d+);', file_str) - VER_MINOR = m.group(1) - m = re.search('const S32 LL_VERSION_PATCH = (\d+);', file_str) - VER_PATCH = m.group(1) - m = re.search('const S32 LL_VERSION_BUILD = (\d+);', file_str) - VER_BUILD = m.group(1) - version = "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s" % locals() - return version - -def get_channel(version_type): - file_str = get_version_file_contents(version_type) - m = re.search('const char \* const LL_CHANNEL = "(.+)";', file_str) - return m.group(1) - -def get_viewer_version(): - return get_version('viewer') - -def get_server_version(): - return get_version('server') - -def get_viewer_channel(): - return get_channel('viewer') - -def get_server_channel(): - return get_channel('server') - -# Methods for gathering hg information -def get_hg_repo(): - child = subprocess.Popen(["hg","showconfig","paths.default"], stdout=subprocess.PIPE) - output, error = child.communicate() - status = child.returncode - if status: - print >> sys.stderr, error - sys.exit(1) - if not output: - print >> sys.stderr, 'ERROR: cannot find repo we cloned from' - sys.exit(1) - return output - -def get_hg_changeset(): - # The right thing to do would be to use the *global* revision id: - # "hg id -i" - # For the moment though, we use the parent revision: - child = subprocess.Popen(["hg","parents","--template","{rev}"], stdout=subprocess.PIPE) - output, error = child.communicate() - status = child.returncode - if status: - print >> sys.stderr, error - sys.exit(1) - lines = output.splitlines() - if len(lines) > 1: - print >> sys.stderr, 'ERROR: working directory has %d parents' % len(lines) - return lines[0] - -def using_hg(): - return os.path.isdir(os.path.join(get_src_root(), '.hg')) diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py index 6bf956107d..6bf956107d 100644..100755 --- a/indra/lib/python/indra/util/named_query.py +++ b/indra/lib/python/indra/util/named_query.py diff --git a/indra/lib/python/indra/util/shutil2.py b/indra/lib/python/indra/util/shutil2.py index 9e2e7a6ded..9e2e7a6ded 100644..100755 --- a/indra/lib/python/indra/util/shutil2.py +++ b/indra/lib/python/indra/util/shutil2.py diff --git a/indra/lib/python/indra/util/term.py b/indra/lib/python/indra/util/term.py index 8c316a1f12..8c316a1f12 100644..100755 --- a/indra/lib/python/indra/util/term.py +++ b/indra/lib/python/indra/util/term.py diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py index 0532cb0065..0532cb0065 100644..100755 --- a/indra/lib/python/indra/util/test_win32_manifest.py +++ b/indra/lib/python/indra/util/test_win32_manifest.py diff --git a/indra/lib/python/uuid.py b/indra/lib/python/uuid.py index e956383cca..e956383cca 100644..100755 --- a/indra/lib/python/uuid.py +++ b/indra/lib/python/uuid.py |