From f729cfc33f258781c5fd85a3d8773bf6149d12db Mon Sep 17 00:00:00 2001
From: Bennett Goble <signal@lindenlab.com>
Date: Sat, 5 Jun 2021 22:02:54 -0700
Subject: SL-15742: Convert build scripts to Python 3

This changeset makes it possible to build the Second Life viewer using
Python 3. It is designed to be used with an equivalent Autobuild branch
so that a developer can compile without needing Python 2 on their
machine.

Breaking change: Python 2 support ending

Rather than supporting two versions of Python, including one that was
discontinued at the beginning of the year, this branch focuses on
pouring future effort into Python 3 only. As a result, scripts do not
need to be backwards compatible. This means that build environments,
be they on personal computers and on build agents, need to have a
compatible interpreter.

Notes

- SLVersionChecker will still use Python 2 on macOS
- Fixed the message template url used by template_verifier.py
---
 indra/llcommon/tests/llleap_test.cpp        | 41 +++++++++++++++--------------
 indra/llcommon/tests/llprocess_test.cpp     | 30 ++++++++++++---------
 indra/llcommon/tests/llsdserialize_test.cpp | 15 +++++------
 3 files changed, 45 insertions(+), 41 deletions(-)

(limited to 'indra/llcommon/tests')

diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index 9d71e327d8..9754353ab0 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -145,13 +145,13 @@ namespace tut
                    "    data = ''.join(parts)\n"
                    "    assert len(data) == length\n"
                    "    try:\n"
-                   "        return llsd.parse(data)\n"
+                   "        return llsd.parse(data.encode())\n"
                    //   Seems the old indra.base.llsd module didn't properly
                    //   convert IndexError (from running off end of string) to
                    //   LLSDParseError.
-                   "    except (IndexError, llsd.LLSDParseError), e:\n"
+                   "    except (IndexError, llsd.LLSDParseError) as e:\n"
                    "        msg = 'Bad received packet (%s)' % e\n"
-                   "        print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n"
+                   "        print('%s, %s bytes:' % (msg, len(data)), file=sys.stderr)\n"
                    "        showmax = 40\n"
                    //       We've observed failures with very large packets;
                    //       dumping the entire packet wastes time and space.
@@ -167,12 +167,12 @@ namespace tut
                    "            data = data[:trunc]\n"
                    "            ellipsis = '... (%s more)' % (length - trunc)\n"
                    "        offset = -showmax\n"
-                   "        for offset in xrange(0, len(data)-showmax, showmax):\n"
-                   "            print >>sys.stderr, '%04d: %r +' % \\\n"
-                   "                  (offset, data[offset:offset+showmax])\n"
+                   "        for offset in range(0, len(data)-showmax, showmax):\n"
+                   "            print('%04d: %r +' % \\\n"
+                   "                  (offset, data[offset:offset+showmax]), file=sys.stderr)\n"
                    "        offset += showmax\n"
-                   "        print >>sys.stderr, '%04d: %r%s' % \\\n"
-                   "              (offset, data[offset:], ellipsis)\n"
+                   "        print('%04d: %r%s' % \\\n"
+                   "              (offset, data[offset:], ellipsis), file=sys.stderr)\n"
                    "        raise ParseError(msg, data)\n"
                    "\n"
                    "# deal with initial stdin message\n"
@@ -189,7 +189,7 @@ namespace tut
                    "    sys.stdout.flush()\n"
                    "\n"
                    "def send(pump, data):\n"
-                   "    put(llsd.format_notation(dict(pump=pump, data=data)))\n"
+                   "    put(llsd.format_notation(dict(pump=pump, data=data)).decode())\n"
                    "\n"
                    "def request(pump, data):\n"
                    "    # we expect 'data' is a dict\n"
@@ -253,7 +253,7 @@ namespace tut
     {
         set_test_name("bad stdout protocol");
         NamedTempFile script("py",
-                             "print 'Hello from Python!'\n");
+                             "print('Hello from Python!')\n");
         CaptureLog log(LLError::LEVEL_WARN);
         waitfor(LLLeap::create(get_test_name(),
                                sv(list_of(PYTHON)(script.getName()))));
@@ -438,8 +438,8 @@ namespace tut
                              // guess how many messages it will take to
                              // accumulate BUFFERED_LENGTH
                              "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
-                             "print >>sys.stderr, 'Sending %s requests' % count\n"
-                             "for i in xrange(count):\n"
+                             "print('Sending %s requests' % count, file=sys.stderr)\n"
+                             "for i in range(count):\n"
                              "    request('" << api.getName() << "', dict(reqid=i))\n"
                              // The assumption in this specific test that
                              // replies will arrive in the same order as
@@ -450,7 +450,7 @@ namespace tut
                              // arbitrary order, and we'd have to tick them
                              // off from a set.
                              "result = ''\n"
-                             "for i in xrange(count):\n"
+                             "for i in range(count):\n"
                              "    resp = get()\n"
                              "    if resp['data']['reqid'] != i:\n"
                              "        result = 'expected reqid=%s in %s' % (i, resp)\n"
@@ -476,13 +476,13 @@ namespace tut
                              "desired = int(sys.argv[1])\n"
                              // 7 chars per item: 6 digits, 1 comma
                              "count = int((desired - 50)/7)\n"
-                             "large = ''.join('%06d,' % i for i in xrange(count))\n"
+                             "large = ''.join('%06d,' % i for i in range(count))\n"
                              // Pass 'large' as reqid because we know the API
                              // will echo reqid, and we want to receive it back.
                              "request('" << api.getName() << "', dict(reqid=large))\n"
                              "try:\n"
                              "    resp = get()\n"
-                             "except ParseError, e:\n"
+                             "except ParseError as e:\n"
                              "    # try to find where e.data diverges from expectation\n"
                              // Normally we'd expect a 'pump' key in there,
                              // too, with value replypump(). But Python
@@ -493,17 +493,18 @@ namespace tut
                              // strange.
                              "    expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
                              "    chunk = 40\n"
-                             "    for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n"
+                             "    for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
                              "        if e.data[offset:offset+chunk] != \\\n"
                              "           expect[offset:offset+chunk]:\n"
-                             "            print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n"
+                             "            print('Offset %06d: expect %r,\\n'\\\n"
                              "                                '                  get %r' %\\\n"
                              "                                (offset,\n"
                              "                                 expect[offset:offset+chunk],\n"
-                             "                                 e.data[offset:offset+chunk])\n"
+                             "                                 e.data[offset:offset+chunk]),\n"
+                             "                                 file=sys.stderr)\n"
                              "            break\n"
                              "    else:\n"
-                             "        print >>sys.stderr, 'incoming data matches expect?!'\n"
+                             "        print('incoming data matches expect?!', file=sys.stderr)\n"
                              "    send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
                              "    sys.exit(1)\n"
                              "\n"
@@ -512,7 +513,7 @@ namespace tut
                              "    send('" << result.getName() << "', '')\n"
                              "    sys.exit(0)\n"
                              // Here we know echoed did NOT match; try to find where
-                             "for i in xrange(count):\n"
+                             "for i in range(count):\n"
                              "    start = 7*i\n"
                              "    end   = 7*(i+1)\n"
                              "    if end > len(echoed)\\\n"
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index f0eafa8201..e530975e86 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -360,10 +360,10 @@ namespace tut
             "import time" EOL
             EOL
             "time.sleep(2)" EOL
-            "print >>sys.stdout, 'stdout after wait'" EOL
+            "print('stdout after wait', file=sys.stdout)" EOL
             "sys.stdout.flush()" EOL
             "time.sleep(2)" EOL
-            "print >>sys.stderr, 'stderr after wait'" EOL
+            "print('stderr after wait', file=sys.stderr)" EOL
             "sys.stderr.flush()" EOL
             );
 
@@ -381,7 +381,11 @@ namespace tut
 
         std::vector<const char*> argv;
         apr_proc_t child;
+#if defined(LL_WINDOWS)
         argv.push_back("python");
+#else
+        argv.push_back("python3");
+#endif
         // Have to have a named copy of this std::string so its c_str() value
         // will persist.
         std::string scriptname(script.getName());
@@ -573,7 +577,7 @@ namespace tut
                                  // note nonstandard output-file arg!
                                  "with open(sys.argv[3], 'w') as f:\n"
                                  "    for arg in sys.argv[1:]:\n"
-                                 "        print >>f, arg\n");
+                                 "        print(arg, file=f)\n");
         // We expect that PythonProcessLauncher has already appended
         // its own NamedTempFile to mParams.args (sys.argv[0]).
         py.mParams.args.add("first arg");          // sys.argv[1]
@@ -742,7 +746,7 @@ namespace tut
                                      "with open(sys.argv[1], 'w') as f:\n"
                                      "    f.write('ok')\n"
                                      "# wait for 'go' from test program\n"
-                                     "for i in xrange(60):\n"
+                                     "for i in range(60):\n"
                                      "    time.sleep(1)\n"
                                      "    with open(sys.argv[2]) as f:\n"
                                      "        go = f.read()\n"
@@ -804,7 +808,7 @@ namespace tut
                                      "with open(sys.argv[1], 'w') as f:\n"
                                      "    f.write('ok')\n"
                                      "# wait for 'go' from test program\n"
-                                     "for i in xrange(60):\n"
+                                     "for i in range(60):\n"
                                      "    time.sleep(1)\n"
                                      "    with open(sys.argv[2]) as f:\n"
                                      "        go = f.read()\n"
@@ -857,7 +861,7 @@ namespace tut
         set_test_name("'bogus' test");
         CaptureLog recorder;
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'Hello world'\n");
+                                 "print('Hello world')\n");
         py.mParams.files.add(LLProcess::FileParam("bogus"));
         py.mPy = LLProcess::create(py.mParams);
         ensure("should have rejected 'bogus'", ! py.mPy);
@@ -872,7 +876,7 @@ namespace tut
         // Replace this test with one or more real 'file' tests when we
         // implement 'file' support
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'Hello world'\n");
+                                 "print('Hello world')\n");
         py.mParams.files.add(LLProcess::FileParam());
         py.mParams.files.add(LLProcess::FileParam("file"));
         py.mPy = LLProcess::create(py.mParams);
@@ -887,7 +891,7 @@ namespace tut
         // implement 'tpipe' support
         CaptureLog recorder;
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'Hello world'\n");
+                                 "print('Hello world')\n");
         py.mParams.files.add(LLProcess::FileParam());
         py.mParams.files.add(LLProcess::FileParam("tpipe"));
         py.mPy = LLProcess::create(py.mParams);
@@ -904,7 +908,7 @@ namespace tut
         // implement 'npipe' support
         CaptureLog recorder;
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'Hello world'\n");
+                                 "print('Hello world')\n");
         py.mParams.files.add(LLProcess::FileParam());
         py.mParams.files.add(LLProcess::FileParam());
         py.mParams.files.add(LLProcess::FileParam("npipe"));
@@ -980,7 +984,7 @@ namespace tut
     {
         set_test_name("get*Pipe() validation");
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'this output is expected'\n");
+                                 "print('this output is expected)'\n");
         py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for  stdin
         py.mParams.files.add(LLProcess::FileParam());       // inherit stdout
         py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
@@ -1001,13 +1005,13 @@ namespace tut
         set_test_name("talk to stdin/stdout");
         PythonProcessLauncher py(get_test_name(),
                                  "import sys, time\n"
-                                 "print 'ok'\n"
+                                 "print('ok')\n"
                                  "sys.stdout.flush()\n"
                                  "# wait for 'go' from test program\n"
                                  "go = sys.stdin.readline()\n"
                                  "if go != 'go\\n':\n"
                                  "    sys.exit('expected \"go\", saw %r' % go)\n"
-                                 "print 'ack'\n");
+                                 "print('ack')\n");
         py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
         py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
         py.launch();
@@ -1118,7 +1122,7 @@ namespace tut
     {
         set_test_name("ReadPipe \"eof\" event");
         PythonProcessLauncher py(get_test_name(),
-                                 "print 'Hello from Python!'\n");
+                                 "print('Hello from Python!')\n");
         py.mParams.files.add(LLProcess::FileParam()); // stdin
         py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
         py.launch();
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 642c1c3879..c246f5ee56 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -1795,7 +1795,7 @@ namespace tut
         set_test_name("verify NamedTempFile");
         python("platform",
                "import sys\n"
-               "print 'Running on', sys.platform\n");
+               "print('Running on', sys.platform)\n");
     }
 
     // helper for test<3>
@@ -1825,14 +1825,14 @@ namespace tut
         const char pydata[] =
             "def verify(iterable):\n"
             "    it = iter(iterable)\n"
-            "    assert it.next() == 17\n"
-            "    assert abs(it.next() - 3.14) < 0.01\n"
-            "    assert it.next() == '''\\\n"
+            "    assert next(it) == 17\n"
+            "    assert abs(next(it) - 3.14) < 0.01\n"
+            "    assert next(it) == '''\\\n"
             "This string\n"
             "has several\n"
             "lines.'''\n"
             "    try:\n"
-            "        it.next()\n"
+            "        next(it)\n"
             "    except StopIteration:\n"
             "        pass\n"
             "    else:\n"
@@ -1855,7 +1855,7 @@ namespace tut
                "        yield llsd.parse(item)\n" <<
                pydata <<
                // Don't forget raw-string syntax for Windows pathnames.
-               "verify(parse_each(open(r'" << file.getName() << "')))\n");
+               "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n");
     }
 
     template<> template<>
@@ -1870,7 +1870,6 @@ namespace tut
 
         python("write Python notation",
                placeholders::arg1 <<
-               "from __future__ import with_statement\n" <<
                import_llsd <<
                "DATA = [\n"
                "    17,\n"
@@ -1884,7 +1883,7 @@ namespace tut
                // N.B. Using 'print' implicitly adds newlines.
                "with open(r'" << file.getName() << "', 'w') as f:\n"
                "    for item in DATA:\n"
-               "        print >>f, llsd.format_notation(item)\n");
+               "        print(llsd.format_notation(item).decode(), file=f)\n");
 
         std::ifstream inf(file.getName().c_str());
         LLSD item;
-- 
cgit v1.2.3