Browse Source

cleanup: Remove support for EOL versions of Python

Fixes #905
rdb 5 years ago
parent
commit
d799a09002
100 changed files with 243 additions and 1352 deletions
  1. 1 43
      .github/workflows/ci.yml
  2. 2 2
      .travis.yml
  3. 6 8
      direct/src/cluster/ClusterMsgs.py
  4. 0 8
      direct/src/dcparser/dcClass_ext.cxx
  5. 0 12
      direct/src/dcparser/dcField_ext.cxx
  6. 0 61
      direct/src/dcparser/dcPacker_ext.cxx
  7. 3 8
      direct/src/directscripts/extract_docs.py
  8. 1 5
      direct/src/directtools/DirectSession.py
  9. 0 1
      direct/src/dist/FreezeTool.py
  10. 1 9
      direct/src/dist/commands.py
  11. 6 10
      direct/src/dist/pefile.py
  12. 1 6
      direct/src/distributed/NetMessenger.py
  13. 1 16
      direct/src/distributed/cConnectionRepository.cxx
  14. 1 4
      direct/src/doc/howto.adjust
  15. 1 5
      direct/src/extensions_native/extension_native_helpers.py
  16. 3 11
      direct/src/gui/DirectEntry.py
  17. 5 11
      direct/src/gui/DirectFrame.py
  18. 3 9
      direct/src/gui/DirectGuiBase.py
  19. 15 21
      direct/src/gui/DirectScrolledList.py
  20. 1 7
      direct/src/gui/DirectWaitBar.py
  21. 1 6
      direct/src/gui/OnscreenGeom.py
  22. 1 8
      direct/src/gui/OnscreenImage.py
  23. 5 25
      direct/src/gui/OnscreenText.py
  24. 2 5
      direct/src/interval/Interval.py
  25. 0 6
      direct/src/particles/ParticleEffect.py
  26. 22 35
      direct/src/showbase/ContainerLeakDetector.py
  27. 23 26
      direct/src/showbase/GarbageReport.py
  28. 2 6
      direct/src/showbase/LeakDetectors.py
  29. 2 6
      direct/src/showbase/MessengerLeakDetector.py
  30. 1 4
      direct/src/showbase/ObjectReport.py
  31. 1 6
      direct/src/showbase/ProfileSession.py
  32. 18 60
      direct/src/showbase/PythonUtil.py
  33. 1 4
      direct/src/showbase/ShowBase.py
  34. 1 6
      direct/src/showbase/ShowBaseGlobal.py
  35. 1 4
      direct/src/showbase/TkGlobal.py
  36. 5 19
      direct/src/showbase/VFSImporter.py
  37. 6 44
      direct/src/stdpy/file.py
  38. 2 5
      direct/src/stdpy/glob.py
  39. 1 4
      direct/src/stdpy/pickle.py
  40. 1 5
      direct/src/stdpy/thread.py
  41. 2 5
      direct/src/task/Task.py
  42. 3 8
      direct/src/tkpanels/AnimPanel.py
  43. 2 6
      direct/src/tkpanels/FSMInspector.py
  44. 2 6
      direct/src/tkpanels/MopathRecorder.py
  45. 3 9
      direct/src/tkpanels/ParticlePanel.py
  46. 3 8
      direct/src/tkpanels/TaskManagerPanel.py
  47. 1 5
      direct/src/tkwidgets/AppShell.py
  48. 3 8
      direct/src/tkwidgets/EntryScale.py
  49. 0 4
      direct/src/tkwidgets/Tree.py
  50. 1 6
      direct/src/tkwidgets/Valuator.py
  51. 1 6
      direct/src/tkwidgets/VectorWidgets.py
  52. 3 7
      direct/src/tkwidgets/WidgetPropertiesDialog.py
  53. 0 47
      dtool/src/dtoolutil/filename_ext.cxx
  54. 0 16
      dtool/src/dtoolutil/iostream_ext.cxx
  55. 0 47
      dtool/src/dtoolutil/textEncoder_ext.cxx
  56. 0 8
      dtool/src/prc/streamReader_ext.cxx
  57. 0 12
      dtool/src/prc/streamWriter.I
  58. 0 6
      dtool/src/prc/streamWriter.h
  59. 1 4
      makepanda/installer.nsi
  60. 5 22
      makepanda/makepackage.py
  61. 19 26
      makepanda/makepanda.py
  62. 22 46
      makepanda/makepandacore.py
  63. 10 17
      makepanda/makewheel.py
  64. 1 1
      makepanda/selfdestruct.py
  65. 2 8
      panda/CMakeLists.txt
  66. 0 2
      panda/src/android/python_main.cxx
  67. 1 4
      panda/src/android/site.py
  68. 0 5
      panda/src/display/graphicsWindow_ext.cxx
  69. 0 4
      panda/src/display/windowProperties_ext.cxx
  70. 0 2
      panda/src/event/asyncFuture_ext.cxx
  71. 0 52
      panda/src/event/pythonTask.cxx
  72. 0 5
      panda/src/express/datagram_ext.I
  73. 0 92
      panda/src/express/pointerToArray_ext.I
  74. 0 16
      panda/src/express/ramfile_ext.cxx
  75. 0 21
      panda/src/express/stringStream_ext.cxx
  76. 0 14
      panda/src/express/virtualFileSystem_ext.cxx
  77. 0 14
      panda/src/express/virtualFile_ext.cxx
  78. 0 28
      panda/src/gobj/geomVertexArrayData_ext.cxx
  79. 0 26
      panda/src/gobj/internalName_ext.cxx
  80. 0 27
      panda/src/gobj/texture_ext.cxx
  81. 0 5
      panda/src/linmath/lvecBase2_ext_src.I
  82. 0 5
      panda/src/linmath/lvecBase3_ext_src.I
  83. 0 5
      panda/src/linmath/lvecBase4_ext_src.I
  84. 0 6
      panda/src/pgraph/loaderFileTypeRegistry_ext.cxx
  85. 0 4
      panda/src/pgraph/nodePath_ext.cxx
  86. 1 32
      panda/src/pgraph/pythonLoaderFileType.cxx
  87. 0 4
      panda/src/pgraph/shaderAttrib_ext.cxx
  88. 0 8
      panda/src/pgraph/shaderInput_ext.cxx
  89. 0 4
      panda/src/pnmimage/pfmFile_ext.cxx
  90. 0 12
      panda/src/putil/bitArray_ext.cxx
  91. 0 12
      panda/src/putil/doubleBitMask_ext.I
  92. 0 2
      panda/src/putil/pythonCallbackObject.cxx
  93. 0 2
      setup.cfg
  94. 0 1
      tests/dtoolutil/test_filename.py
  95. 8 12
      tests/dtoolutil/test_textencoder.py
  96. 1 7
      tests/event/test_futures.py
  97. 0 8
      tests/gui/test_DirectEntry.py
  98. 1 17
      tests/interrogate/test_property.py
  99. 0 8
      tests/linmath/test_lvector2.py
  100. 0 7
      tests/linmath/test_lvector3.py

+ 1 - 43
.github/workflows/ci.yml

@@ -109,6 +109,7 @@ jobs:
         libeigen3-dev libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev
         libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev
         libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev
+        python3-setuptools
 
         # Workaround for CMake 3.12 finding this first:
 
@@ -180,37 +181,6 @@ jobs:
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
 
-    - name: Setup Python (Python 2.7)
-      if: contains(matrix.python, 'YES')
-      uses: actions/setup-python@v1
-      with:
-        python-version: 2.7
-    - name: Configure (Python 2.7)
-      if: contains(matrix.python, 'YES')
-      working-directory: build
-      shell: bash
-      run: >
-        cmake -DWANT_PYTHON_VERSION=2.7
-        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT=$pythonLocation .
-    - name: Build (Python 2.7)
-      if: contains(matrix.python, 'YES')
-      # BEGIN A
-      working-directory: build
-      run: cmake --build . --config ${{ matrix.config }} --parallel 4
-      # END A
-    - name: Test (Python 2.7)
-      # BEGIN B
-      if: contains(matrix.python, 'YES')
-      working-directory: build
-      shell: bash
-      env:
-        PYTHONPATH: ${{ matrix.config }}
-      run: |
-        PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
-        $PYTHON_EXECUTABLE -m pip install pytest
-        $PYTHON_EXECUTABLE -m pytest ../tests
-      # END B
-
     - name: Setup Python (Python 3.5)
       if: contains(matrix.python, 'YES')
       uses: actions/setup-python@v1
@@ -407,18 +377,6 @@ jobs:
       run: |
         python -m pip install pytest
         PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
-    - name: Set up Python 2.7
-      uses: actions/setup-python@v1
-      with:
-        python-version: 2.7
-    - name: Build Python 2.7
-      run: |
-        python makepanda/makepanda.py --no-copy-python --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir=$pythonLocation/include --python-libdir=$pythonLocation/lib --verbose --threads=4
-    - name: Test Python 2.7
-      shell: bash
-      run: |
-        python -m pip install pytest
-        PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
     - name: Make installer
       run: |
         python makepanda/makepackage.py --verbose --lzma

+ 2 - 2
.travis.yml

@@ -5,9 +5,9 @@ matrix:
     - compiler: clang
       env: PYTHONV=python3 FLAGS=--installer
     - compiler: clang
-      env: PYTHONV=python2.7 FLAGS=--override=STDFLOAT_DOUBLE=1
+      env: PYTHONV=python3 FLAGS=--override=STDFLOAT_DOUBLE=1
     - compiler: gcc
-      env: PYTHONV=python2.7 FLAGS=--optimize=4
+      env: PYTHONV=python3 FLAGS=--optimize=4
       before_install:
         - export CC=gcc-4.7
         - export CXX=g++-4.7

+ 6 - 8
direct/src/cluster/ClusterMsgs.py

@@ -31,18 +31,16 @@ CLUSTER_DAEMON_PORT = 8001
 CLUSTER_SERVER_PORT = 1970
 
 # Precede command string with ! to tell server to execute command string
-# NOTE: Had to stick with the import __builtin__ scheme, at startup,
-# __builtins__ is a module, not a dictionary, like it is inside of a module
 # Note, this startup string obviates the need to set any cluster related
 # config variables in the client Configrc files
 SERVER_STARTUP_STRING = (
     '!bash ppython -c ' +
-    '"import __builtin__; ' +
-    '__builtin__.clusterMode = \'server\';' +
-    '__builtin__.clusterServerPort = %s;' +
-    '__builtin__.clusterSyncFlag = %d;' +
-    '__builtin__.clusterDaemonClient = \'%s\';' +
-    '__builtin__.clusterDaemonPort = %d;'
+    '"import builtins; ' +
+    'builtins.clusterMode = \'server\';' +
+    'builtins.clusterServerPort = %s;' +
+    'builtins.clusterSyncFlag = %d;' +
+    'builtins.clusterDaemonClient = \'%s\';' +
+    'builtins.clusterDaemonPort = %d;'
     'from direct.directbase.DirectStart import *; run()"')
 
 class ClusterMsgHandler:

+ 0 - 8
direct/src/dcparser/dcClass_ext.cxx

@@ -540,11 +540,7 @@ client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
 
   for (int i = 0; i < num_optional_fields; i++) {
     PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
     std::string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-    std::string field_name = PyString_AsString(py_field_name);
-#endif
     Py_XDECREF(py_field_name);
 
     DCField *field = _this->get_field_by_name(field_name);
@@ -621,11 +617,7 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
 
     for (int i = 0; i < num_optional_fields; ++i) {
       PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
       std::string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-      std::string field_name = PyString_AsString(py_field_name);
-#endif
       Py_XDECREF(py_field_name);
 
       DCField *field = _this->get_field_by_name(field_name);

+ 0 - 12
direct/src/dcparser/dcField_ext.cxx

@@ -266,22 +266,14 @@ get_pystr(PyObject *value) {
 
   PyObject *str = PyObject_Str(value);
   if (str != nullptr) {
-#if PY_MAJOR_VERSION >= 3
     std::string result = PyUnicode_AsUTF8(str);
-#else
-    std::string result = PyString_AsString(str);
-#endif
     Py_DECREF(str);
     return result;
   }
 
   PyObject *repr = PyObject_Repr(value);
   if (repr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
     std::string result = PyUnicode_AsUTF8(repr);
-#else
-    std::string result = PyString_AsString(repr);
-#endif
     Py_DECREF(repr);
     return result;
   }
@@ -289,11 +281,7 @@ get_pystr(PyObject *value) {
   if (value->ob_type != nullptr) {
     PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
     if (typestr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
       std::string result = PyUnicode_AsUTF8(typestr);
-#else
-      std::string result = PyString_AsString(typestr);
-#endif
       Py_DECREF(typestr);
       return result;
     }

+ 0 - 61
direct/src/dcparser/dcPacker_ext.cxx

@@ -40,12 +40,6 @@ pack_object(PyObject *object) {
       _this->pack_int64(PyLong_AsLongLong(object));
       return;
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(object)) {
-      _this->pack_int64(PyInt_AsLong(object));
-      return;
-    }
-#endif
     break;
 
   case PT_uint64:
@@ -53,14 +47,6 @@ pack_object(PyObject *object) {
       _this->pack_uint64(PyLong_AsUnsignedLongLong(object));
       return;
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(object)) {
-      PyObject *obj1 = PyNumber_Long(object);
-      _this->pack_int(PyLong_AsUnsignedLongLong(obj1));
-      Py_DECREF(obj1);
-      return;
-    }
-#endif
     break;
 
   case PT_int:
@@ -68,12 +54,6 @@ pack_object(PyObject *object) {
       _this->pack_int(PyLong_AsLong(object));
       return;
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(object)) {
-      _this->pack_int(PyInt_AsLong(object));
-      return;
-    }
-#endif
     break;
 
   case PT_uint:
@@ -81,14 +61,6 @@ pack_object(PyObject *object) {
       _this->pack_uint(PyLong_AsUnsignedLong(object));
       return;
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyInt_Check(object)) {
-      PyObject *obj1 = PyNumber_Long(object);
-      _this->pack_uint(PyLong_AsUnsignedLong(obj1));
-      Py_DECREF(obj1);
-      return;
-    }
-#endif
     break;
 
   default:
@@ -97,15 +69,10 @@ pack_object(PyObject *object) {
 
   if (PyLong_Check(object)) {
     _this->pack_int(PyLong_AsLong(object));
-#if PY_MAJOR_VERSION < 3
-  } else if (PyInt_Check(object)) {
-    _this->pack_int(PyInt_AS_LONG(object));
-#endif
   } else if (PyFloat_Check(object)) {
     _this->pack_double(PyFloat_AS_DOUBLE(object));
   } else if (PyLong_Check(object)) {
     _this->pack_int64(PyLong_AsLongLong(object));
-#if PY_MAJOR_VERSION >= 3
   } else if (PyUnicode_Check(object)) {
     const char *buffer;
     Py_ssize_t length;
@@ -120,15 +87,6 @@ pack_object(PyObject *object) {
     if (buffer) {
       _this->pack_blob(vector_uchar(buffer, buffer + length));
     }
-#else
-  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
-    char *buffer;
-    Py_ssize_t length;
-    PyString_AsStringAndSize(object, &buffer, &length);
-    if (buffer) {
-      _this->pack_string(std::string(buffer, length));
-    }
-#endif
   } else {
     // For some reason, PySequence_Check() is incorrectly reporting that a
     // class instance is a sequence, even if it doesn't provide __len__, so we
@@ -230,26 +188,14 @@ unpack_object() {
   case PT_int:
     {
       int value = _this->unpack_int();
-#if PY_MAJOR_VERSION >= 3
       object = PyLong_FromLong(value);
-#else
-      object = PyInt_FromLong(value);
-#endif
     }
     break;
 
   case PT_uint:
     {
       unsigned int value = _this->unpack_uint();
-#if PY_MAJOR_VERSION >= 3
       object = PyLong_FromLong(value);
-#else
-      if (value & 0x80000000) {
-        object = PyLong_FromUnsignedLong(value);
-      } else {
-        object = PyInt_FromLong(value);
-      }
-#endif
     }
     break;
 
@@ -268,25 +214,18 @@ unpack_object() {
     break;
 
   case PT_blob:
-#if PY_MAJOR_VERSION >= 3
     {
       std::string str;
       _this->unpack_string(str);
       object = PyBytes_FromStringAndSize(str.data(), str.size());
     }
     break;
-#endif
-    // On Python 2, fall through to below.
 
   case PT_string:
     {
       std::string str;
       _this->unpack_string(str);
-#if PY_MAJOR_VERSION >= 3
       object = PyUnicode_FromStringAndSize(str.data(), str.size());
-#else
-      object = PyString_FromStringAndSize(str.data(), str.size());
-#endif
     }
     break;
 

+ 3 - 8
direct/src/directscripts/extract_docs.py

@@ -9,7 +9,7 @@ from __future__ import print_function
 
 __all__ = []
 
-import os, sys
+import os
 from distutils import sysconfig
 import panda3d, pandac
 from panda3d.interrogatedb import *
@@ -309,13 +309,8 @@ if __name__ == "__main__":
     processModule(handle, "core")
 
     # Determine the suffix for the extension modules.
-    if sys.version_info >= (3, 0):
-        import _imp
-        ext_suffix = _imp.extension_suffixes()[0]
-    elif sys.platform == "win32":
-        ext_suffix = ".pyd"
-    else:
-        ext_suffix = ".so"
+    import _imp
+    ext_suffix = _imp.extension_suffixes()[0]
 
     for lib in os.listdir(os.path.dirname(panda3d.__file__)):
         if lib.endswith(ext_suffix) and not lib.startswith('core.'):

+ 1 - 5
direct/src/directtools/DirectSession.py

@@ -1,5 +1,4 @@
 import math
-import sys
 
 from panda3d.core import *
 from .DirectUtil import *
@@ -942,10 +941,7 @@ class DirectSession(DirectObject):
 
     def getAndSetName(self, nodePath):
         """ Prompt user for new node path name """
-        if sys.version_info >= (3, 0):
-            from tkinter.simpledialog import askstring
-        else:
-            from tkSimpleDialog import askstring
+        from tkinter.simpledialog import askstring
         newName = askstring('Node Path: ' + nodePath.getName(),
                             'Enter new name:')
         if newName:

+ 0 - 1
direct/src/dist/FreezeTool.py

@@ -50,7 +50,6 @@ else:
 # dummy entry should be written to the inittab.
 builtinInitFuncs = {
     'builtins': None,
-    '__builtin__': None,
     'sys': None,
     'exceptions': None,
     '_warnings': '_PyWarnings_Init',

+ 1 - 9
direct/src/dist/commands.py

@@ -30,10 +30,6 @@ from .icon import Icon
 import panda3d.core as p3d
 
 
-if 'basestring' not in globals():
-    basestring = str
-
-
 if sys.version_info < (3, 0):
     # Python 3 defines these subtypes of IOError, but Python 2 doesn't.
     FileNotFoundError = IOError
@@ -48,7 +44,7 @@ if sys.version_info < (3, 0):
 
 
 def _parse_list(input):
-    if isinstance(input, basestring):
+    if isinstance(input, str):
         input = input.strip().replace(',', '\n')
         if input:
             return [item.strip() for item in input.split('\n') if item.strip()]
@@ -407,10 +403,6 @@ class build_apps(setuptools.Command):
         if sys.version_info < (3, 8):
             abi_tag += 'm'
 
-        # For these distributions, we need to append 'u' on Linux
-        if abi_tag in ('cp26m', 'cp27m', 'cp32m') and not platform.startswith('win') and not platform.startswith('macosx'):
-            abi_tag += 'u'
-
         whldir = os.path.join(whlcache, '_'.join((platform, abi_tag)))
         os.makedirs(whldir, exist_ok=True)
 

+ 6 - 10
direct/src/dist/pefile.py

@@ -10,11 +10,7 @@ from collections import namedtuple
 from array import array
 import time
 from io import BytesIO
-import sys
 
-if sys.version_info >= (3, 0):
-    unicode = str
-    unichr = chr
 
 # Define some internally used structures.
 RVASize = namedtuple('RVASize', ('addr', 'size'))
@@ -38,7 +34,7 @@ def _unpack_wstring(mem, offs=0):
     name = ""
     for i in range(name_len):
         offs += 2
-        name += unichr(*unpack('<H', mem[offs:offs+2]))
+        name += chr(*unpack('<H', mem[offs:offs+2]))
     return name
 
 def _padded(n, boundary):
@@ -208,7 +204,7 @@ class VersionInfoResource(object):
         if isinstance(value, dict):
             type = 1
             value_length = 0
-        elif isinstance(value, bytes) or isinstance(value, unicode):
+        elif isinstance(value, bytes) or isinstance(value, str):
             type = 1
             value_length = len(value) * 2 + 2
         else:
@@ -227,7 +223,7 @@ class VersionInfoResource(object):
         if isinstance(value, dict):
             for key2, value2 in sorted(value.items(), key=lambda x:x[0]):
                 self._pack_info(data, key2, value2)
-        elif isinstance(value, bytes) or isinstance(value, unicode):
+        elif isinstance(value, bytes) or isinstance(value, str):
             for c in value:
                 data += pack('<H', ord(c))
             data += b'\x00\x00'
@@ -294,7 +290,7 @@ class VersionInfoResource(object):
         c, = unpack('<H', data[offset:offset+2])
         offset += 2
         while c:
-            key += unichr(c)
+            key += chr(c)
             c, = unpack('<H', data[offset:offset+2])
             offset += 2
 
@@ -309,7 +305,7 @@ class VersionInfoResource(object):
                 c, = unpack('<H', data[offset:offset+2])
                 offset += 2
                 while c:
-                    value += unichr(c)
+                    value += chr(c)
                     c, = unpack('<H', data[offset:offset+2])
                     offset += 2
             else:
@@ -705,7 +701,7 @@ class PEFile(object):
 
         Returns the newly created Section object. """
 
-        if isinstance(name, unicode):
+        if isinstance(name, str):
             name = name.encode('ascii')
 
         section = Section()

+ 1 - 6
direct/src/distributed/NetMessenger.py

@@ -2,12 +2,7 @@
 from direct.directnotify import DirectNotifyGlobal
 from direct.distributed.PyDatagram import PyDatagram
 from direct.showbase.Messenger import Messenger
-
-import sys
-if sys.version_info >= (3, 0):
-    from pickle import dumps, loads
-else:
-    from cPickle import dumps, loads
+from pickle import dumps, loads
 
 
 # Messages do not need to be in the MESSAGE_TYPES list.

+ 1 - 16
direct/src/distributed/cConnectionRepository.cxx

@@ -411,19 +411,12 @@ send_datagram(const Datagram &dg) {
     if (!result && _bdc.IsConnected()) {
 #ifdef HAVE_PYTHON
       std::ostringstream s;
-
-#if PY_VERSION_HEX >= 0x03030000
-      PyObject *exc_type = PyExc_ConnectionError;
-#else
-      PyObject *exc_type = PyExc_OSError;
-#endif
-
       s << endl << "Error sending message: " << endl;
       dg.dump_hex(s);
       s << "Message data: " << dg.get_data() << endl;
 
       string message = s.str();
-      PyErr_SetString(exc_type, message.c_str());
+      PyErr_SetString(PyExc_ConnectionError, message.c_str());
 #endif
     }
     return result;
@@ -922,22 +915,14 @@ describe_message(std::ostream &out, const string &prefix,
     if (_python_repository != nullptr) {
       PyObject *msgId = PyLong_FromLong(msg_type);
       nassertv(msgId != nullptr);
-#if PY_MAJOR_VERSION >= 3
       PyObject *methodName = PyUnicode_FromString("_getMsgName");
-#else
-      PyObject *methodName = PyString_FromString("_getMsgName");
-#endif
       nassertv(methodName != nullptr);
 
       PyObject *result = PyObject_CallMethodObjArgs(_python_repository, methodName,
                                                     msgId, nullptr);
       nassertv(result != nullptr);
 
-#if PY_MAJOR_VERSION >= 3
       msgName += string(PyUnicode_AsUTF8(result));
-#else
-      msgName += string(PyString_AsString(result));
-#endif
 
       Py_DECREF(methodName);
       Py_DECREF(msgId);

+ 1 - 4
direct/src/doc/howto.adjust

@@ -52,10 +52,7 @@ of the slider to change settings.  Click on:
 
 You can pack multiple sliders into a single panel:
 
-if sys.version_info >= (3, 0):
-    from tkinter import *
-else:
-    from Tkinter import *
+from tkinter import *
 
 def func1(x):
    print '1:', x

+ 1 - 5
direct/src/extensions_native/extension_native_helpers.py

@@ -1,17 +1,13 @@
 __all__ = ["Dtool_ObjectToDict", "Dtool_funcToMethod"]
 
-import sys
 
 def Dtool_ObjectToDict(cls, name, obj):
     cls.DtoolClassDict[name] = obj
 
+
 def Dtool_funcToMethod(func, cls, method_name=None):
     """Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
     The new method is accessible to any instance immediately."""
-    if sys.version_info < (3, 0):
-        func.im_class = cls
-        func.im_func = func
-        func.im_self = None
     func.__func__ = func
     func.__self__ = None
     if not method_name:

+ 3 - 11
direct/src/gui/DirectEntry.py

@@ -12,7 +12,6 @@ from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
 from .OnscreenText import OnscreenText
-import sys
 # import this to make sure it gets pulled into the publish
 import encodings.utf_8
 from direct.showbase.DirectObject import DirectObject
@@ -274,16 +273,9 @@ class DirectEntry(DirectFrame):
         does not change the current cursor position.  Also see
         enterText(). """
 
-        if sys.version_info >= (3, 0):
-            assert not isinstance(text, bytes)
-            self.unicodeText = True
-            self.guiItem.setWtext(text)
-        else:
-            self.unicodeText = isinstance(text, unicode)
-            if self.unicodeText:
-                self.guiItem.setWtext(text)
-            else:
-                self.guiItem.setText(text)
+        assert not isinstance(text, bytes)
+        self.unicodeText = True
+        self.guiItem.setWtext(text)
 
     def get(self, plain = False):
         """ Returns the text currently showing in the typable region.

+ 5 - 11
direct/src/gui/DirectFrame.py

@@ -24,12 +24,6 @@ from .DirectGuiBase import *
 from .OnscreenImage import OnscreenImage
 from .OnscreenGeom import OnscreenGeom
 from .OnscreenText import OnscreenText
-import sys
-
-if sys.version_info >= (3, 0):
-    stringType = str
-else:
-    stringType = basestring
 
 
 class DirectFrame(DirectGuiWidget):
@@ -105,7 +99,7 @@ class DirectFrame(DirectGuiWidget):
             self["text"] = text
 
         text = self["text"]
-        if text is None or isinstance(text, stringType):
+        if text is None or isinstance(text, str):
             text_list = (text,) * self['numStates']
         else:
             text_list = text
@@ -126,7 +120,7 @@ class DirectFrame(DirectGuiWidget):
         geom = self["geom"]
         if geom is None or \
            isinstance(geom, NodePath) or \
-           isinstance(geom, stringType):
+           isinstance(geom, str):
             geom_list = (geom,) * self['numStates']
         else:
             geom_list = geom
@@ -147,11 +141,11 @@ class DirectFrame(DirectGuiWidget):
         if image is None or \
            isinstance(image, NodePath) or \
            isinstance(image, Texture) or \
-           isinstance(image, stringType) or \
+           isinstance(image, str) or \
            isinstance(image, Filename) or \
            (len(image) == 2 and \
-            isinstance(image[0], stringType) and \
-            isinstance(image[1], stringType)):
+            isinstance(image[0], str) and \
+            isinstance(image[1], str)):
             image_list = (image,) * self['numStates']
         else:
             image_list = image

+ 3 - 9
direct/src/gui/DirectGuiBase.py

@@ -97,12 +97,6 @@ from .OnscreenImage import *
 from direct.directtools.DirectUtil import ROUND_TO
 from direct.showbase import DirectObject
 from direct.task import Task
-import sys
-
-if sys.version_info >= (3, 0):
-    stringType = str
-else:
-    stringType = basestring
 
 guiObjectCollector = PStatCollector("Client::GuiObjects")
 
@@ -960,7 +954,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         # Convert None, and string arguments
         if relief == None:
             relief = PGFrameStyle.TNone
-        elif isinstance(relief, stringType):
+        elif isinstance(relief, str):
             # Convert string to frame style int
             relief = DGG.FrameStyleDict[relief]
         # Set style
@@ -1001,14 +995,14 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         textures = self['frameTexture']
         if textures == None or \
            isinstance(textures, Texture) or \
-           isinstance(textures, stringType):
+           isinstance(textures, str):
             textures = (textures,) * self['numStates']
         for i in range(self['numStates']):
             if i >= len(textures):
                 texture = textures[-1]
             else:
                 texture = textures[i]
-            if isinstance(texture, stringType):
+            if isinstance(texture, str):
                 texture = loader.loadTexture(texture)
             if texture:
                 self.frameStyle[i].setTexture(texture)

+ 15 - 21
direct/src/gui/DirectScrolledList.py

@@ -13,12 +13,6 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.task.Task import Task
 from .DirectFrame import *
 from .DirectButton import *
-import sys
-
-if sys.version_info >= (3,0):
-    stringType = str
-else:
-    stringType = basestring
 
 
 class DirectScrolledListItem(DirectButton):
@@ -71,7 +65,7 @@ class DirectScrolledList(DirectFrame):
         # so we can modify it without mangling the user's list
         if 'items' in kw:
             for item in kw['items']:
-                if not isinstance(item, stringType):
+                if not isinstance(item, str):
                     break
             else:
                 # we get here if every item in 'items' is a string
@@ -116,7 +110,7 @@ class DirectScrolledList(DirectFrame):
                                               DirectFrame, (self,),
                                               )
         for item in self["items"]:
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 item.reparentTo(self.itemFrame)
 
         self.initialiseoptions(DirectScrolledList)
@@ -134,7 +128,7 @@ class DirectScrolledList(DirectFrame):
         else:
             self.maxHeight = 0.0
             for item in self["items"]:
-                if not isinstance(item, stringType):
+                if not isinstance(item, str):
                     self.maxHeight = max(self.maxHeight, item.getHeight())
 
     def setScrollSpeed(self):
@@ -182,7 +176,7 @@ class DirectScrolledList(DirectFrame):
         if len(self["items"]) == 0:
             return 0
 
-        if isinstance(self["items"][0], stringType):
+        if isinstance(self["items"][0], str):
             self.notify.warning("getItemIndexForItemID: cant find itemID for non-class list items!")
             return 0
 
@@ -248,7 +242,7 @@ class DirectScrolledList(DirectFrame):
 
         # Hide them all
         for item in self["items"]:
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 item.hide()
 
         # Then show the ones in range, and stack their positions
@@ -258,7 +252,7 @@ class DirectScrolledList(DirectFrame):
             #print "stacking buttontext[", i,"]", self["items"][i]["text"]
             # If the item is a 'str', then it has not been created (scrolled list is 'as needed')
             #  Therefore, use the the function given to make it or just make it a frame
-            if isinstance(item, stringType):
+            if isinstance(item, str):
                 if self['itemMakeFunction']:
                     # If there is a function to create the item
                     item = self['itemMakeFunction'](item, i, self['itemMakeExtraArgs'])
@@ -290,7 +284,7 @@ class DirectScrolledList(DirectFrame):
             # Therefore, use the the function given to make it or
             # just make it a frame
             #print "Making " + str(item)
-            if isinstance(item, stringType):
+            if isinstance(item, str):
                 if self['itemMakeFunction']:
                     # If there is a function to create the item
                     item = self['itemMakeFunction'](item, i, self['itemMakeExtraArgs'])
@@ -355,16 +349,16 @@ class DirectScrolledList(DirectFrame):
         Add this string and extraArg to the list
         """
         assert self.notify.debugStateCall(self)
-        if not isinstance(item, stringType):
+        if not isinstance(item, str):
             # cant add attribs to non-classes (like strings & ints)
             item.itemID = self.nextItemID
             self.nextItemID += 1
         self['items'].append(item)
-        if not isinstance(item, stringType):
+        if not isinstance(item, str):
             item.reparentTo(self.itemFrame)
         if refresh:
             self.refresh()
-        if not isinstance(item, stringType):
+        if not isinstance(item, str):
             return item.itemID  # to pass to scrollToItemID
 
     def removeItem(self, item, refresh=1):
@@ -379,7 +373,7 @@ class DirectScrolledList(DirectFrame):
             if hasattr(self, "currentSelected") and self.currentSelected is item:
                 del self.currentSelected
             self["items"].remove(item)
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             return 1
@@ -397,7 +391,7 @@ class DirectScrolledList(DirectFrame):
             if (hasattr(item, 'destroy') and hasattr(item.destroy, '__call__')):
                 item.destroy()
             self["items"].remove(item)
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             return 1
@@ -419,7 +413,7 @@ class DirectScrolledList(DirectFrame):
             if hasattr(self, "currentSelected") and self.currentSelected is item:
                 del self.currentSelected
             self["items"].remove(item)
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 #RAU possible leak here, let's try to do the right thing
                 #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
@@ -444,7 +438,7 @@ class DirectScrolledList(DirectFrame):
             if (hasattr(item, 'destroy') and hasattr(item.destroy, '__call__')):
                 item.destroy()
             self["items"].remove(item)
-            if not isinstance(item, stringType):
+            if not isinstance(item, str):
                 #RAU possible leak here, let's try to do the right thing
                 #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
@@ -469,7 +463,7 @@ class DirectScrolledList(DirectFrame):
 
     def getSelectedText(self):
         assert self.notify.debugStateCall(self)
-        if isinstance(self['items'][self.index], stringType):
+        if isinstance(self['items'][self.index], str):
           return self['items'][self.index]
         else:
           return self['items'][self.index]['text']

+ 1 - 7
direct/src/gui/DirectWaitBar.py

@@ -9,12 +9,6 @@ __all__ = ['DirectWaitBar']
 from panda3d.core import *
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
-import sys
-
-if sys.version_info >= (3, 0):
-    stringType = str
-else:
-    stringType = basestring
 
 """
 import DirectWaitBar
@@ -102,7 +96,7 @@ class DirectWaitBar(DirectFrame):
         """Updates the bar texture, which you can set using bar['barTexture']."""
         # this must be a single texture (or a string).
         texture = self['barTexture']
-        if isinstance(texture, stringType):
+        if isinstance(texture, str):
             texture = loader.loadTexture(texture)
         if texture:
             self.barStyle.setTexture(texture)

+ 1 - 6
direct/src/gui/OnscreenGeom.py

@@ -4,12 +4,7 @@ __all__ = ['OnscreenGeom']
 
 from panda3d.core import *
 from direct.showbase.DirectObject import DirectObject
-import sys
 
-if sys.version_info >= (3, 0):
-    stringType = str
-else:
-    stringType = basestring
 
 class OnscreenGeom(DirectObject, NodePath):
     def __init__(self, geom = None,
@@ -98,7 +93,7 @@ class OnscreenGeom(DirectObject, NodePath):
         # Assign geometry
         if isinstance(geom, NodePath):
             self.assign(geom.copyTo(parent, sort))
-        elif isinstance(geom, stringType):
+        elif isinstance(geom, str):
             self.assign(loader.loadModel(geom))
             self.reparentTo(parent, sort)
 

+ 1 - 8
direct/src/gui/OnscreenImage.py

@@ -8,12 +8,6 @@ __all__ = ['OnscreenImage']
 
 from panda3d.core import *
 from direct.showbase.DirectObject import DirectObject
-import sys
-
-if sys.version_info >= (3, 0):
-    stringType = str
-else:
-    stringType = basestring
 
 
 class OnscreenImage(DirectObject, NodePath):
@@ -106,8 +100,7 @@ class OnscreenImage(DirectObject, NodePath):
         # Assign geometry
         if isinstance(image, NodePath):
             self.assign(image.copyTo(parent, sort))
-        elif isinstance(image, stringType) or \
-             isinstance(image, Texture):
+        elif isinstance(image, str) or isinstance(image, Texture):
             if isinstance(image, Texture):
                 # It's a Texture
                 tex = image

+ 5 - 25
direct/src/gui/OnscreenText.py

@@ -8,7 +8,6 @@ __all__ = ['OnscreenText', 'Plain', 'ScreenTitle', 'ScreenPrompt', 'NameConfirm'
 
 from panda3d.core import *
 from . import DirectGuiGlobals as DGG
-import sys
 
 ## These are the styles of text we might commonly see.  They set the
 ## overall appearance of the text according to one of a number of
@@ -281,34 +280,15 @@ class OnscreenText(NodePath):
         self.textNode.clearText()
 
     def setText(self, text):
-        if sys.version_info >= (3, 0):
-            assert not isinstance(text, bytes)
-            self.unicodeText = True
-        else:
-            self.unicodeText = isinstance(text, unicode)
-
-        if self.unicodeText:
-            self.textNode.setWtext(text)
-        else:
-            self.textNode.setText(text)
+        assert not isinstance(text, bytes)
+        self.textNode.setWtext(text)
 
     def appendText(self, text):
-        if sys.version_info >= (3, 0):
-            assert not isinstance(text, bytes)
-            self.unicodeText = True
-        else:
-            self.unicodeText = isinstance(text, unicode)
-
-        if self.unicodeText:
-            self.textNode.appendWtext(text)
-        else:
-            self.textNode.appendText(text)
+        assert not isinstance(text, bytes)
+        self.textNode.appendWtext(text)
 
     def getText(self):
-        if self.unicodeText:
-            return self.textNode.getWtext()
-        else:
-            return self.textNode.getText()
+        return self.textNode.getWtext()
 
     text = property(getText, setText)
 

+ 2 - 5
direct/src/interval/Interval.py

@@ -454,12 +454,9 @@ class Interval(DirectObject):
         """
         # Don't use a regular import, to prevent ModuleFinder from picking
         # it up as a dependency when building a .p3d package.
-        import importlib, sys
+        import importlib
         EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
-        if sys.version_info >= (3, 0):
-            tkinter = importlib.import_module('tkinter')
-        else:
-            tkinter = importlib.import_module('Tkinter')
+        tkinter = importlib.import_module('tkinter')
 
         if tl == None:
             tl = tkinter.Toplevel()

+ 0 - 6
direct/src/particles/ParticleEffect.py

@@ -7,12 +7,6 @@ from . import Particles
 from . import ForceGroup
 
 from direct.directnotify import DirectNotifyGlobal
-import sys
-
-
-if sys.version_info < (3, 0):
-    FileNotFoundError = IOError
-
 
 
 class ParticleEffect(NodePath):

+ 22 - 35
direct/src/showbase/ContainerLeakDetector.py

@@ -3,27 +3,14 @@ from direct.showbase.PythonUtil import makeFlywheelGen
 from direct.showbase.PythonUtil import itype, serialNum, safeRepr, fastRepr
 from direct.showbase.Job import Job
 import types, weakref, random, sys
+import builtins
 
-if sys.version_info >= (3, 0):
-    import builtins as __builtin__
-
-    intTypes = (int,)
-    deadEndTypes = (bool, types.BuiltinFunctionType,
-                    types.BuiltinMethodType, complex,
-                    float, int,
-                    type(None), type(NotImplemented),
-                    type, types.CodeType, types.FunctionType,
-                    bytes, str, tuple)
-else:
-    import __builtin__
-
-    intTypes = (int, long)
-    deadEndTypes = (types.BooleanType, types.BuiltinFunctionType,
-                    types.BuiltinMethodType, types.ComplexType,
-                    types.FloatType, types.IntType, types.LongType,
-                    types.NoneType, types.NotImplementedType,
-                    types.TypeType, types.CodeType, types.FunctionType,
-                    types.StringType, types.UnicodeType, types.TupleType)
+deadEndTypes = (bool, types.BuiltinFunctionType,
+                types.BuiltinMethodType, complex,
+                float, int,
+                type(None), type(NotImplemented),
+                type, types.CodeType, types.FunctionType,
+                bytes, str, tuple)
 
 
 def _createContainerLeak():
@@ -136,7 +123,7 @@ class Indirection:
     def dereferenceDictKey(self, parentDict):
         # look ourselves up in parentDict
         key = self._getNonWeakDictKey()
-        # objects in __builtin__ will have parentDict==None
+        # objects in builtins will have parentDict==None
         if parentDict is None:
             return key
         return parentDict[key]
@@ -185,7 +172,7 @@ class ObjectRef:
 
         # make sure we're not storing a reference to the actual object,
         # that could cause a memory leak
-        assert type(objId) in intTypes
+        assert type(objId) is int
         # prevent cycles (i.e. base.loader.base.loader)
         assert not self.goesThrough(objId=objId)
 
@@ -206,7 +193,7 @@ class ObjectRef:
 
     def goesThroughGen(self, obj=None, objId=None):
         if obj is None:
-            assert type(objId) in intTypes
+            assert type(objId) is int
         else:
             objId = id(obj)
         o = None
@@ -253,9 +240,9 @@ class ObjectRef:
             # eval('curObj.foo.bar.someDict')
             evalStr = 'curObj%s' % evalStr
         else:
-            # this eval is not based off of curObj, use the global__builtin__ namespace
-            # put __builtin__ at the start if it's not already there
-            bis = '__builtin__'
+            # this eval is not based off of curObj, use the globalbuiltins namespace
+            # put builtins at the start if it's not already there
+            bis = 'builtins'
             if evalStr[:len(bis)] != bis:
                 evalStr = '%s.%s' % (bis, evalStr)
         try:
@@ -369,17 +356,17 @@ class FindContainers(Job):
         ContainerLeakDetector.addPrivateObj(self.__dict__)
 
         # set up the base containers, the ones that hold most objects
-        ref = ObjectRef(Indirection(evalStr='__builtin__.__dict__'), id(__builtin__.__dict__))
-        self._id2baseStartRef[id(__builtin__.__dict__)] = ref
+        ref = ObjectRef(Indirection(evalStr='builtins.__dict__'), id(builtins.__dict__))
+        self._id2baseStartRef[id(builtins.__dict__)] = ref
         # container for objects that want to make sure they are found by
         # the object exploration algorithm, including objects that exist
         # just to measure things such as C++ memory usage, scene graph size,
         # framerate, etc. See LeakDetectors.py
-        if not hasattr(__builtin__, "leakDetectors"):
-            __builtin__.leakDetectors = {}
+        if not hasattr(builtins, "leakDetectors"):
+            builtins.leakDetectors = {}
         ref = ObjectRef(Indirection(evalStr='leakDetectors'), id(leakDetectors))
         self._id2baseStartRef[id(leakDetectors)] = ref
-        for i in self._addContainerGen(__builtin__.__dict__, ref):
+        for i in self._addContainerGen(builtins.__dict__, ref):
             pass
         try:
             base
@@ -457,7 +444,7 @@ class FindContainers(Job):
         objId = id(obj)
         if objId in self._id2discoveredStartRef:
             existingRef = self._id2discoveredStartRef[objId]
-            if type(existingRef) not in intTypes:
+            if type(existingRef) is not int:
                 if (existingRef.getNumIndirections() >=
                     ref.getNumIndirections()):
                     # the ref that we already have is more concise than the new ref
@@ -522,11 +509,11 @@ class FindContainers(Job):
                             startRefWorkingList.refGen = fw
                     if curObjRef is None:
                         # this ref set is empty, choose another
-                        # the base set should never be empty (__builtin__ etc.)
+                        # the base set should never be empty (builtins etc.)
                         continue
                     # do we need to go look up the object in _id2ref? sometimes we do that
                     # to avoid storing multiple redundant refs to a single item
-                    if type(curObjRef) in intTypes:
+                    if type(curObjRef) is int:
                         startId = curObjRef
                         curObjRef = None
                         try:
@@ -605,7 +592,7 @@ class FindContainers(Job):
                                 # don't yield, container might lose this element
                                 pass
                             if not goesThrough:
-                                if curObj is __builtin__.__dict__:
+                                if curObj is builtins.__dict__:
                                     objRef = ObjectRef(Indirection(evalStr='%s' % key),
                                                        id(curObj[key]))
                                 else:

+ 23 - 26
direct/src/showbase/GarbageReport.py

@@ -8,12 +8,9 @@ from direct.showbase.PythonUtil import AlphabetCounter
 from direct.showbase.Job import Job
 import gc
 import types
-import sys
 
 GarbageCycleCountAnnounceEvent = 'announceGarbageCycleDesc2num'
 
-if sys.version_info >= (3, 0):
-    xrange = range
 
 class FakeObject:
     pass
@@ -23,7 +20,7 @@ class FakeDelObject:
         pass
 
 def _createGarbage(num=1):
-    for i in xrange(num):
+    for i in range(num):
         a = FakeObject()
         b = FakeObject()
         a.other = b
@@ -84,7 +81,7 @@ class GarbageReport(Job):
             self.numGarbageInstances = len(garbageInstances)
             # grab the ids of the garbage instances (objects with __del__)
             self.garbageInstanceIds = set()
-            for i in xrange(len(garbageInstances)):
+            for i in range(len(garbageInstances)):
                 self.garbageInstanceIds.add(id(garbageInstances[i]))
                 if not (i % 20):
                     yield None
@@ -144,7 +141,7 @@ class GarbageReport(Job):
         self.cycleIds = set()
 
         # make the id->index table to speed up the next steps
-        for i in xrange(self.numGarbage):
+        for i in range(self.numGarbage):
             self._id2index[id(self.garbage[i])] = i
             if not (i % 20):
                 yield None
@@ -153,7 +150,7 @@ class GarbageReport(Job):
         if self._args.fullReport and (self.numGarbage != 0):
             if self._args.verbose:
                 self.notify.info('getting referrers...')
-            for i in xrange(self.numGarbage):
+            for i in range(self.numGarbage):
                 yield None
                 for result in self._getReferrers(self.garbage[i]):
                     yield None
@@ -165,7 +162,7 @@ class GarbageReport(Job):
         if self.numGarbage > 0:
             if self._args.verbose:
                 self.notify.info('getting referents...')
-            for i in xrange(self.numGarbage):
+            for i in range(self.numGarbage):
                 yield None
                 for result in self._getReferents(self.garbage[i]):
                     yield None
@@ -173,7 +170,7 @@ class GarbageReport(Job):
                 self.referentsByNumber[i] = byNum
                 self.referentsByReference[i] = byRef
 
-        for i in xrange(self.numGarbage):
+        for i in range(self.numGarbage):
             if hasattr(self.garbage[i], '_garbageInfo') and callable(self.garbage[i]._garbageInfo):
                 try:
                     info = self.garbage[i]._garbageInfo()
@@ -189,7 +186,7 @@ class GarbageReport(Job):
         if self._args.findCycles and self.numGarbage > 0:
             if self._args.verbose:
                 self.notify.info('calculating cycles...')
-            for i in xrange(self.numGarbage):
+            for i in range(self.numGarbage):
                 yield None
                 for newCycles in self._getCycles(i, self.uniqueCycleSets):
                     yield None
@@ -219,7 +216,7 @@ class GarbageReport(Job):
                         startIndex -= 1
                         endIndex -= 1
 
-                    for index in xrange(startIndex, endIndex):
+                    for index in range(startIndex, endIndex):
                         if numToSkip:
                             numToSkip -= 1
                             continue
@@ -259,7 +256,7 @@ class GarbageReport(Job):
                             # get object being referenced by container
                             nextObj = objs[index+1]
                             cycleBySyntax += brackets[0]
-                            for index in xrange(len(obj)):
+                            for index in range(len(obj)):
                                 if obj[index] is nextObj:
                                     index = str(index)
                                     break
@@ -313,7 +310,7 @@ class GarbageReport(Job):
             digits = digits
             format = '%0' + '%s' % digits + 'i:%s \t%s'
 
-            for i in xrange(numGarbage):
+            for i in range(numGarbage):
                 yield None
                 idx = garbageIndices[i]
                 if self._args.safeMode:
@@ -329,7 +326,7 @@ class GarbageReport(Job):
 
             # also log the types of the objects
             s.append('===== Garbage Item Types %s=====' % abbrev)
-            for i in xrange(numGarbage):
+            for i in range(numGarbage):
                 yield None
                 idx = garbageIndices[i]
                 objStr = str(deeptype(self.garbage[idx]))
@@ -342,21 +339,21 @@ class GarbageReport(Job):
             if self._args.findCycles:
                 s.append('===== Garbage Cycles (Garbage Item Numbers) =====')
                 ac = AlphabetCounter()
-                for i in xrange(self.numCycles):
+                for i in range(self.numCycles):
                     yield None
                     s.append('%s:%s' % (ac.next(), self.cycles[i]))
 
             if self._args.findCycles:
                 s.append('===== Garbage Cycles (Python Syntax) =====')
                 ac = AlphabetCounter()
-                for i in xrange(len(self.cyclesBySyntax)):
+                for i in range(len(self.cyclesBySyntax)):
                     yield None
                     s.append('%s:%s' % (ac.next(), self.cyclesBySyntax[i]))
 
             if len(self._id2garbageInfo):
                 s.append('===== Garbage Custom Info =====')
                 ac = AlphabetCounter()
-                for i in xrange(len(self.cyclesBySyntax)):
+                for i in range(len(self.cyclesBySyntax)):
                     yield None
                     counter = ac.next()
                     _id = id(self.garbage[i])
@@ -366,19 +363,19 @@ class GarbageReport(Job):
             if self._args.fullReport:
                 format = '%0' + '%s' % digits + 'i:%s'
                 s.append('===== Referrers By Number (what is referring to garbage item?) =====')
-                for i in xrange(numGarbage):
+                for i in range(numGarbage):
                     yield None
                     s.append(format % (i, self.referrersByNumber[i]))
                 s.append('===== Referents By Number (what is garbage item referring to?) =====')
-                for i in xrange(numGarbage):
+                for i in range(numGarbage):
                     yield None
                     s.append(format % (i, self.referentsByNumber[i]))
                 s.append('===== Referrers (what is referring to garbage item?) =====')
-                for i in xrange(numGarbage):
+                for i in range(numGarbage):
                     yield None
                     s.append(format % (i, self.referrersByReference[i]))
                 s.append('===== Referents (what is garbage item referring to?) =====')
-                for i in xrange(numGarbage):
+                for i in range(numGarbage):
                     yield None
                     s.append(format % (i, self.referentsByReference[i]))
 
@@ -386,7 +383,7 @@ class GarbageReport(Job):
 
         if self._args.log:
             self.printingBegin()
-            for i in xrange(len(self._report)):
+            for i in range(len(self._report)):
                 if self.numGarbage > 0:
                     yield None
                 self.notify.info(self._report[i])
@@ -450,7 +447,7 @@ class GarbageReport(Job):
         yield None
         # look to see if each referrer is another garbage item
         byNum = []
-        for i in xrange(len(byRef)):
+        for i in range(len(byRef)):
             if not (i % 20):
                 yield None
             referrer = byRef[i]
@@ -467,7 +464,7 @@ class GarbageReport(Job):
         yield None
         # look to see if each referent is another garbage item
         byNum = []
-        for i in xrange(len(byRef)):
+        for i in range(len(byRef)):
             if not (i % 20):
                 yield None
             referent = byRef[i]
@@ -484,7 +481,7 @@ class GarbageReport(Job):
             return cycle
         min = 1<<30
         minIndex = None
-        for i in xrange(len(cycle)):
+        for i in range(len(cycle)):
             elem = cycle[i]
             if elem < min:
                 min = elem
@@ -517,7 +514,7 @@ class GarbageReport(Job):
                 else:
                     print('restart: %s root=%s cur=%s resume=%s' % (
                         candidateCycle, rootId, curId, resumeIndex))
-            for index in xrange(resumeIndex, len(self.referentsByNumber[curId])):
+            for index in range(resumeIndex, len(self.referentsByNumber[curId])):
                 yield None
                 refId = self.referentsByNumber[curId][index]
                 if self.notify.getDebug():

+ 2 - 6
direct/src/showbase/LeakDetectors.py

@@ -5,12 +5,8 @@ ContainerLeakDetector.
 from panda3d.core import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.Job import Job
-import gc, sys
-
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
+import gc
+import builtins
 
 
 class LeakDetector:

+ 2 - 6
direct/src/showbase/MessengerLeakDetector.py

@@ -1,12 +1,8 @@
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.Job import Job
-import gc, sys
-
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
+import gc
+import builtins
 
 
 class MessengerLeakObject(DirectObject):

+ 1 - 4
direct/src/showbase/ObjectReport.py

@@ -16,11 +16,8 @@ from direct.showbase import DirectObject, ObjectPool, GarbageReport
 from direct.showbase.PythonUtil import makeList, Sync
 import gc
 import sys
+import builtins
 
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
 
 class ExclusiveObjectPool(DirectObject.DirectObject):
     # ObjectPool specialization that excludes particular objects

+ 1 - 6
direct/src/showbase/ProfileSession.py

@@ -6,12 +6,7 @@ from direct.showbase.PythonUtil import (
     _getProfileResultFileInfo, _setProfileResultsFileInfo)
 import profile
 import pstats
-import sys
-
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
+import builtins
 
 
 class PercentStats(pstats.Stats):

+ 18 - 60
direct/src/showbase/PythonUtil.py

@@ -37,17 +37,13 @@ import os
 import sys
 import random
 import time
+import builtins
+import importlib
 
 __report_indent = 3
 
 from panda3d.core import ConfigVariableBool
 
-if sys.version_info >= (3, 0):
-    import builtins
-    xrange = range
-else:
-    import __builtin__ as builtins
-
 
 """
 # with one integer positional arg, this uses about 4/5 of the memory of the Functor class below
@@ -59,41 +55,6 @@ def Functor(function, *args, **kArgs):
     return functor
 """
 
-try:
-    import importlib
-except ImportError:
-    # Backward compatibility for Python 2.6.
-    def _resolve_name(name, package, level):
-        if not hasattr(package, 'rindex'):
-            raise ValueError("'package' not set to a string")
-        dot = len(package)
-        for x in xrange(level, 1, -1):
-            try:
-                dot = package.rindex('.', 0, dot)
-            except ValueError:
-                raise ValueError("attempted relative import beyond top-level "
-                                  "package")
-        return "%s.%s" % (package[:dot], name)
-
-    def import_module(name, package=None):
-        if name.startswith('.'):
-            if not package:
-                raise TypeError("relative imports require the 'package' argument")
-            level = 0
-            for character in name:
-                if character != '.':
-                    break
-                level += 1
-            name = _resolve_name(name[level:], package, level)
-        __import__(name)
-        return sys.modules[name]
-
-    imp = import_module('imp')
-    importlib = imp.new_module("importlib")
-    importlib._resolve_name = _resolve_name
-    importlib.import_module = import_module
-    sys.modules['importlib'] = importlib
-
 
 class Functor:
     def __init__(self, function, *args, **kargs):
@@ -506,7 +467,7 @@ def replace(list, old, new, all=0):
         return 1
     else:
         numReplaced = 0
-        for i in xrange(len(list)):
+        for i in range(len(list)):
             if list[i] == old:
                 numReplaced += 1
                 list[i] = new
@@ -1659,16 +1620,13 @@ def itype(obj):
     # version of type that gives more complete information about instance types
     global dtoolSuperBase
     t = type(obj)
-    if sys.version_info < (3, 0) and t is types.InstanceType:
-        return "<type 'instance' of <class %s>>" % (obj.__class__)
-    else:
-        # C++ object instances appear to be types via type()
-        # check if this is a C++ object
-        if dtoolSuperBase is None:
-            _getDtoolSuperBase()
-        if isinstance(obj, dtoolSuperBase):
-            return "<type 'instance' of %s>" % (obj.__class__)
-        return t
+    # C++ object instances appear to be types via type()
+    # check if this is a C++ object
+    if dtoolSuperBase is None:
+        _getDtoolSuperBase()
+    if isinstance(obj, dtoolSuperBase):
+        return "<type 'instance' of %s>" % (obj.__class__)
+    return t
 
 def deeptype(obj, maxLen=100, _visitedIds=None):
     if _visitedIds is None:
@@ -1728,7 +1686,7 @@ def getNumberedTypedString(items, maxLen=5000, numPrefix=''):
     first = True
     s = ''
     snip = '<SNIP>'
-    for i in xrange(len(items)):
+    for i in range(len(items)):
         if not first:
             s += '\n'
         first = False
@@ -1759,7 +1717,7 @@ def getNumberedTypedSortedString(items, maxLen=5000, numPrefix=''):
     first = True
     s = ''
     strs.sort()
-    for i in xrange(len(strs)):
+    for i in range(len(strs)):
         if not first:
             s += '\n'
         first = False
@@ -1777,7 +1735,7 @@ def printNumberedTyped(items, maxLen=5000):
         n //= 10
     digits = digits
     format = '%0' + '%s' % digits + 'i:%s \t%s'
-    for i in xrange(len(items)):
+    for i in range(len(items)):
         objStr = fastRepr(items[i])
         if len(objStr) > maxLen:
             snip = '<SNIP>'
@@ -1792,7 +1750,7 @@ def printNumberedTypesGen(items, maxLen=5000):
         n //= 10
     digits = digits
     format = '%0' + '%s' % digits + 'i:%s'
-    for i in xrange(len(items)):
+    for i in range(len(items)):
         print(format % (i, itype(items[i])))
         yield None
 
@@ -2287,14 +2245,14 @@ def makeFlywheelGen(objects, countList=None, countFunc=None, scale=None):
             countList.append(countFunc(object))
     if scale is not None:
         # scale the counts if we've got a scale factor
-        for i in xrange(len(countList)):
+        for i in range(len(countList)):
             yield None
             if countList[i] > 0:
                 countList[i] = max(1, int(countList[i] * scale))
     # create a dict for the flywheel to use during its iteration to efficiently select
     # the objects for the sequence
     index2objectAndCount = {}
-    for i in xrange(len(countList)):
+    for i in range(len(countList)):
         yield None
         index2objectAndCount[i] = [objects[i], countList[i]]
     # create the flywheel generator
@@ -2540,7 +2498,7 @@ if __debug__ and __name__ == '__main__':
     def testAlphabetCounter():
         tempList = []
         ac = AlphabetCounter()
-        for i in xrange(26*3):
+        for i in range(26*3):
             tempList.append(ac.next())
         assert tempList == [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                             'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ',
@@ -2551,7 +2509,7 @@ if __debug__ and __name__ == '__main__':
         num += 26 # AAZ
         num += 1 # ABA
         num += 2 # ABC
-        for i in xrange(num):
+        for i in range(num):
             x = ac.next()
         assert x == 'ABC'
     testAlphabetCounter()

+ 1 - 4
direct/src/showbase/ShowBase.py

@@ -46,10 +46,7 @@ from direct.extensions_native import NodePath_extensions
 
 # This needs to be available early for DirectGUI imports
 import sys
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
+import builtins
 builtins.config = DConfig
 
 from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify

+ 1 - 6
direct/src/showbase/ShowBaseGlobal.py

@@ -72,13 +72,8 @@ def inspect(anObject):
     return Inspector.inspect(anObject)
 
 
-import sys
-if sys.version_info >= (3, 0):
-    import builtins
-else:
-    import __builtin__ as builtins
+import builtins
 builtins.inspect = inspect
-del sys
 
 # this also appears in AIBaseGlobal
 if (not __debug__) and __dev__:

+ 1 - 4
direct/src/showbase/TkGlobal.py

@@ -1,11 +1,8 @@
 """ This module is now vestigial.  """
 
 import sys, Pmw
+from tkinter import *
 
-if sys.version_info >= (3, 0):
-    from tkinter import *
-else:
-    from Tkinter import *
 
 # This is required by the ihooks.py module used by Squeeze (used by
 # pandaSqueezer.py) so that Pmw initializes properly

+ 5 - 19
direct/src/showbase/VFSImporter.py

@@ -230,10 +230,7 @@ class VFSLoader:
         #print >>sys.stderr, "importing frozen %s" % (fullname)
         module = imp.load_module(fullname, None, fullname,
                                  ('', '', imp.PY_FROZEN))
-
-        # Workaround for bug in Python 2.
-        if getattr(module, '__path__', None) == fullname:
-            module.__path__ = []
+        module.__path__ = []
         return module
 
     def _read_code(self):
@@ -288,13 +285,8 @@ class VFSLoader:
         if data[:4] != imp.get_magic():
             raise ValueError("Bad magic number in %s" % (vfile))
 
-        if sys.version_info >= (3, 0):
-            t = int.from_bytes(data[4:8], 'little')
-            data = data[12:]
-        else:
-            t = ord(data[4]) + (ord(data[5]) << 8) + \
-               (ord(data[6]) << 16) + (ord(data[7]) << 24)
-            data = data[8:]
+        t = int.from_bytes(data[4:8], 'little')
+        data = data[12:]
 
         if not timestamp or t == timestamp:
             return marshal.loads(data)
@@ -320,14 +312,8 @@ class VFSLoader:
             pass
         else:
             f.write(imp.get_magic())
-            if sys.version_info >= (3, 0):
-                f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little'))
-                f.write(b'\0\0\0\0')
-            else:
-                f.write(chr(self.timestamp & 0xff) +
-                        chr((self.timestamp >> 8) & 0xff) +
-                        chr((self.timestamp >> 16) & 0xff) +
-                        chr((self.timestamp >> 24) & 0xff))
+            f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little'))
+            f.write(b'\0\0\0\0')
             f.write(marshal.dumps(code))
             f.close()
 

+ 6 - 44
direct/src/stdpy/file.py

@@ -11,7 +11,6 @@ __all__ = [
     ]
 
 from panda3d import core
-import sys
 import os
 import io
 import encodings
@@ -19,19 +18,6 @@ from posixpath import join
 
 _vfs = core.VirtualFileSystem.getGlobalPtr()
 
-if sys.version_info < (3, 0):
-    # Python 3 defines these subtypes of IOError, but Python 2 doesn't.
-    FileNotFoundError = IOError
-    IsADirectoryError = IOError
-    FileExistsError = IOError
-    PermissionError = IOError
-
-    unicodeType = unicode
-    strType = str
-else:
-    unicodeType = str
-    strType = ()
-
 
 def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
     """This function emulates the built-in Python open() function, additionally
@@ -39,12 +25,9 @@ def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
     arguments as Python's built-in open() function.
     """
 
-    if sys.version_info >= (3, 0):
-        # Python 3 is much stricter than Python 2, which lets
-        # unknown flags fall through.
-        for ch in mode:
-            if ch not in 'rwxabt+U':
-                raise ValueError("invalid mode: '%s'" % (mode))
+    for ch in mode:
+        if ch not in 'rwxabt+U':
+            raise ValueError("invalid mode: '%s'" % (mode))
 
     creating = 'x' in mode
     writing = 'w' in mode
@@ -81,21 +64,16 @@ def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
             # We can also "open" a VirtualFile object for reading.
             vfile = file
             filename = vfile.getFilename()
-        elif isinstance(file, unicodeType):
+        elif isinstance(file, str):
             # If a raw string is given, assume it's an os-specific
             # filename.
             filename = core.Filename.fromOsSpecificW(file)
-        elif isinstance(file, strType):
-            filename = core.Filename.fromOsSpecific(file)
         else:
             # It's either a Filename object or an os.PathLike.
             # If a Filename is given, make a writable copy anyway.
             filename = core.Filename(file)
 
-        if binary or sys.version_info >= (3, 0):
-            filename.setBinary()
-        else:
-            filename.setText()
+        filename.setBinary()
 
         if not vfile:
             vfile = _vfs.getFile(filename)
@@ -156,10 +134,6 @@ def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
     if binary:
         return raw
 
-    # If we're in Python 2, we don't decode unicode strings by default.
-    if not encoding and sys.version_info < (3, 0):
-        return raw
-
     line_buffering = False
     if buffering == 1:
         line_buffering = True
@@ -172,12 +146,6 @@ def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
     return wrapper
 
 
-if sys.version_info < (3, 0):
-    # Python 2 had an alias for open() called file().
-    __all__.append('file')
-    file = open
-
-
 class StreamIOWrapper(io.IOBase):
     """ This is a file-like object that wraps around a C++ istream and/or
     ostream object.  It only deals with binary data; to work with text I/O,
@@ -197,13 +165,7 @@ class StreamIOWrapper(io.IOBase):
         if isinstance(stream, core.Ostream):
             self.__writer = core.StreamWriter(stream, False)
             self.__lastWrite = True
-            if sys.version_info >= (3, 0):
-                # In Python 3, we use appendData, which only accepts bytes.
-                self.__write = self.__writer.appendData
-            else:
-                # In Python 2.7, we also accept unicode objects, which are
-                # implicitly converted to C++ strings.
-                self.__write = self.__writer.write
+            self.__write = self.__writer.appendData
 
     def __repr__(self):
         s = "<direct.stdpy.file.StreamIOWrapper"

+ 2 - 5
direct/src/stdpy/glob.py

@@ -2,7 +2,6 @@
 vfs constructs.  This enables Python to interface more easily with Panda's
 virtual file system. """
 
-import sys
 import os
 import fnmatch
 
@@ -52,9 +51,6 @@ def iglob(pathname):
 def glob1(dirname, pattern):
     if not dirname:
         dirname = os.curdir
-    if sys.version_info < (3, 0) and isinstance(pattern, unicode) and not isinstance(dirname, unicode):
-        dirname = unicode(dirname, sys.getfilesystemencoding() or
-                                   sys.getdefaultencoding())
     try:
         names = os.listdir(dirname)
     except os.error:
@@ -81,9 +77,10 @@ def has_magic(s):
     else:
         return '*' in s or '?' in s or '[' in s
 
+
 def escape(pathname):
     drive, pathname = os.path.splitdrive(pathname)
-    if sys.version_info >= (3, 0) and isinstance(pathname, bytes):
+    if isinstance(pathname, bytes):
         newpath = bytearray(drive)
         for c in pathname:
             if c == 42 or c == 63 or c == 91:

+ 1 - 4
direct/src/stdpy/pickle.py

@@ -23,11 +23,8 @@ support extensions of this nature. """
 
 import sys
 from panda3d.core import BamWriter, BamReader
+from copyreg import dispatch_table
 
-if sys.version_info >= (3, 0):
-    from copyreg import dispatch_table
-else:
-    from copy_reg import dispatch_table
 
 # A funny replacement for "import pickle" so we don't get confused
 # with the local pickle.py.

+ 1 - 5
direct/src/stdpy/thread.py

@@ -31,12 +31,8 @@ consider_yield = core.Thread.consider_yield
 
 forceYield = force_yield
 considerYield = consider_yield
+error = RuntimeError
 
-if sys.version_info >= (3, 3):
-    error = RuntimeError
-else:
-    class error(Exception):
-        pass
 
 class LockType:
     """ Implements a mutex lock.  Instead of directly subclassing

+ 2 - 5
direct/src/task/Task.py

@@ -17,19 +17,16 @@ from direct.showbase.MessengerGlobal import messenger
 import types
 import random
 import importlib
-import sys
 
 try:
-    if sys.version_info >= (3, 0):
-        import _signal as signal
-    else:
-        import signal
+    import _signal as signal
 except ImportError:
     signal = None
 
 from panda3d.core import *
 from direct.extensions_native import HTTPChannel_extensions
 
+
 def print_exc_plus():
     """
     Print the usual traceback information, followed by a listing of all the

+ 3 - 8
direct/src/tkpanels/AnimPanel.py

@@ -9,16 +9,11 @@ __all__ = ['AnimPanel', 'ActorControl']
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-import Pmw, sys, os
+import Pmw, os
 from direct.task import Task
 from panda3d.core import Filename, getModelPath
-
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askfloat
-    from tkinter.filedialog import askopenfilename
-else:
-    from tkSimpleDialog import askfloat
-    from tkFileDialog import askopenfilename
+from tkinter.simpledialog import askfloat
+from tkinter.filedialog import askopenfilename
 
 
 FRAMES = 0

+ 2 - 6
direct/src/tkpanels/FSMInspector.py

@@ -106,12 +106,8 @@ __all__ = ['FSMInspector', 'StateInspector']
 
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-import Pmw, math, operator, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askstring
-else:
-    from tkSimpleDialog import askstring
+import Pmw, math, operator
+from tkinter.simpledialog import askstring
 
 
 DELTA = (5.0 / 360.) * 2.0 * math.pi

+ 2 - 6
direct/src/tkpanels/MopathRecorder.py

@@ -12,17 +12,13 @@ from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectUtil import *
 from direct.directtools.DirectGeometry import *
 from direct.directtools.DirectSelection import *
-import Pmw, os, sys
+import Pmw, os
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
 from direct.tkwidgets import EntryScale
 from direct.tkwidgets import VectorWidgets
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import *
-else:
-    from tkFileDialog import *
+from tkinter.filedialog import *
 
 
 PRF_UTILITIES = [

+ 3 - 9
direct/src/tkpanels/ParticlePanel.py

@@ -13,15 +13,9 @@ from direct.tkpanels import Placer
 from direct.particles import ForceGroup
 from direct.particles import Particles
 from direct.particles import ParticleEffect
-import Pmw, os, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import *
-    from tkinter.simpledialog import askstring
-else:
-    from tkFileDialog import *
-    from tkSimpleDialog import askstring
-
+import Pmw, os
+from tkinter.filedialog import *
+from tkinter.simpledialog import askstring
 from panda3d.core import *
 from panda3d.physics import *
 from panda3d.direct import getParticlePath

+ 3 - 8
direct/src/tkpanels/TaskManagerPanel.py

@@ -4,14 +4,9 @@ __all__ = ['TaskManagerPanel', 'TaskManagerWidget']
 
 from direct.tkwidgets.AppShell import *
 from direct.showbase.DirectObject import DirectObject
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter import *
-    from tkinter.messagebox import askokcancel
-else:
-    from Tkinter import *
-    from tkMessageBox import askokcancel
+import Pmw
+from tkinter import *
+from tkinter.messagebox import askokcancel
 
 
 class TaskManagerPanel(AppShell):

+ 1 - 5
direct/src/tkwidgets/AppShell.py

@@ -16,11 +16,7 @@ from . import Slider
 from . import EntryScale
 from . import VectorWidgets
 from . import ProgressBar
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import *
-else:
-    from tkFileDialog import *
+from tkinter.filedialog import *
 
 
 """

+ 3 - 8
direct/src/tkwidgets/EntryScale.py

@@ -5,14 +5,9 @@ EntryScale Class: Scale with a label, and a linked and validated entry
 __all__ = ['EntryScale', 'EntryScaleGroup']
 
 from direct.showbase.TkGlobal import *
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import *
-    from tkinter.colorchooser import askcolor
-else:
-    from tkSimpleDialog import *
-    from tkColorChooser import askcolor
+import Pmw
+from tkinter.simpledialog import *
+from tkinter.colorchooser import askcolor
 
 """
 Change Min/Max buttons to labels, add highlight binding

+ 0 - 4
direct/src/tkwidgets/Tree.py

@@ -24,10 +24,6 @@ from direct.showbase.TkGlobal import *
 from panda3d.core import *
 
 
-if sys.version_info < (3, 0):
-    FileNotFoundError = IOError
-
-
 class TreeNode:
 
     def __init__(self, canvas, parent, item, menuList = []):

+ 1 - 6
direct/src/tkwidgets/Valuator.py

@@ -8,12 +8,7 @@ from . import WidgetPropertiesDialog
 import Pmw
 from direct.directtools.DirectUtil import getTkColorString
 from panda3d.core import Vec4
-import sys
-
-if sys.version_info >= (3, 0):
-    from tkinter.colorchooser import askcolor
-else:
-    from tkColorChooser import askcolor
+from tkinter.colorchooser import askcolor
 
 VALUATOR_MINI = 'mini'
 VALUATOR_FULL = 'full'

+ 1 - 6
direct/src/tkwidgets/VectorWidgets.py

@@ -5,12 +5,7 @@ __all__ = ['VectorEntry', 'Vector2Entry', 'Vector3Entry', 'Vector4Entry', 'Color
 from direct.showbase.TkGlobal import *
 from . import Valuator
 import Pmw
-import sys
-
-if sys.version_info >= (3, 0):
-    from tkinter.colorchooser import askcolor
-else:
-    from tkColorChooser import askcolor
+from tkinter.colorchooser import askcolor
 
 
 class VectorEntry(Pmw.MegaWidget):

+ 3 - 7
direct/src/tkwidgets/WidgetPropertiesDialog.py

@@ -3,7 +3,7 @@
 __all__ = ['WidgetPropertiesDialog']
 
 from direct.showbase.TkGlobal import *
-import Pmw, sys
+import Pmw
 
 """
 TODO:
@@ -31,12 +31,8 @@ class WidgetPropertiesDialog(Toplevel):
             self.propertyList.sort()
         # Use default parent if none specified
         if not parent:
-            if sys.version_info >= (3, 0):
-                import tkinter
-                parent = tkinter._default_root
-            else:
-                import Tkinter
-                parent = Tkinter._default_root
+            import tkinter
+            parent = tkinter._default_root
         # Create toplevel window
         Toplevel.__init__(self, parent)
         self.transient(parent)

+ 0 - 47
dtool/src/dtoolutil/filename_ext.cxx

@@ -34,18 +34,9 @@ __init__(PyObject *path) {
 
   if (PyUnicode_CheckExact(path)) {
     wchar_t *data;
-#if PY_VERSION_HEX >= 0x03020000
     data = PyUnicode_AsWideCharString(path, &length);
-#else
-    length = PyUnicode_GET_SIZE(path);
-    data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
-    PyUnicode_AsWideChar((PyUnicodeObject *)path, data, length);
-#endif
     (*_this) = wstring(data, length);
-
-#if PY_VERSION_HEX >= 0x03020000
     PyMem_Free(data);
-#endif
     return;
   }
 
@@ -81,13 +72,7 @@ __init__(PyObject *path) {
   if (PyObject_HasAttrString(path, "_format_parsed_parts")) {
     path_str = PyObject_Str(path);
   } else {
-#if PY_VERSION_HEX >= 0x03040000
     PyErr_Format(PyExc_TypeError, "expected str, bytes, Path or Filename object, not %s", Py_TYPE(path)->tp_name);
-#elif PY_MAJOR_VERSION >= 3
-    PyErr_Format(PyExc_TypeError, "expected str, bytes or Filename object, not %s", Py_TYPE(path)->tp_name);
-#else
-    PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path)->tp_name);
-#endif
     return;
   }
 #endif
@@ -98,18 +83,9 @@ __init__(PyObject *path) {
 
   if (PyUnicode_CheckExact(path_str)) {
     wchar_t *data;
-#if PY_VERSION_HEX >= 0x03020000
     data = PyUnicode_AsWideCharString(path_str, &length);
-#else
-    length = PyUnicode_GET_SIZE(path_str);
-    data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1));
-    PyUnicode_AsWideChar((PyUnicodeObject *)path_str, data, length);
-#endif
     (*_this) = Filename::from_os_specific_w(wstring(data, length));
-
-#if PY_VERSION_HEX >= 0x03020000
     PyMem_Free(data);
-#endif
 
   } else if (PyBytes_CheckExact(path_str)) {
     char *data;
@@ -117,11 +93,7 @@ __init__(PyObject *path) {
     (*_this) = Filename::from_os_specific(string(data, length));
 
   } else {
-#if PY_MAJOR_VERSION >= 3
     PyErr_Format(PyExc_TypeError, "expected str or bytes object, not %s", Py_TYPE(path_str)->tp_name);
-#else
-    PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path_str)->tp_name);
-#endif
   }
   Py_DECREF(path_str);
 }
@@ -150,25 +122,10 @@ __reduce__(PyObject *self) const {
  */
 PyObject *Extension<Filename>::
 __repr__() const {
-#if PY_MAJOR_VERSION >= 3
-  // Python 3 case: return a unicode object.
   wstring filename = _this->get_fullpath_w();
   PyObject *str = PyUnicode_FromWideChar(filename.data(), (Py_ssize_t)filename.size());
 
-#if PY_VERSION_HEX >= 0x03040000
   PyObject *result = PyUnicode_FromFormat("Filename(%R)", str);
-#else
-  static PyObject *format = PyUnicode_FromString("Filename(%r)");
-  PyObject *result = PyUnicode_Format(format, str);
-#endif
-
-#else
-  // Python 2 case: return a regular string.
-  string filename = _this->get_fullpath();
-  PyObject *str = PyString_FromStringAndSize(filename.data(), (Py_ssize_t)filename.size());
-  static PyObject *format = PyString_FromString("Filename(%r)");
-  PyObject *result = PyString_Format(format, str);
-#endif
 
   Py_DECREF(str);
   return result;
@@ -199,12 +156,8 @@ scan_directory() const {
   PyObject *result = PyList_New(contents.size());
   for (size_t i = 0; i < contents.size(); ++i) {
     const string &filename = contents[i];
-#if PY_MAJOR_VERSION >= 3
     // This function expects UTF-8.
     PyObject *str = PyUnicode_FromStringAndSize(filename.data(), filename.size());
-#else
-    PyObject *str = PyString_FromStringAndSize(filename.data(), filename.size());
-#endif
     PyList_SET_ITEM(result, i, str);
   }
 

+ 0 - 16
dtool/src/dtoolutil/iostream_ext.cxx

@@ -49,11 +49,7 @@ read(Py_ssize_t size) {
 #endif
   }
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(buffer, read_bytes);
-#else
-  return PyString_FromStringAndSize(buffer, read_bytes);
-#endif
 }
 
 /**
@@ -92,11 +88,7 @@ read1(Py_ssize_t size) {
   Py_BLOCK_THREADS
 #endif
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(buffer, read_bytes);
-#else
-  return PyString_FromStringAndSize(buffer, read_bytes);
-#endif
 }
 
 /**
@@ -128,11 +120,7 @@ readall() {
   Py_BLOCK_THREADS
 #endif
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)result.data(), result.size());
-#else
-  return PyString_FromStringAndSize((char *)result.data(), result.size());
-#endif
 }
 
 /**
@@ -195,11 +183,7 @@ readline(Py_ssize_t size) {
   Py_BLOCK_THREADS
 #endif
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(line.data(), line.size());
-#else
-  return PyString_FromStringAndSize(line.data(), line.size());
-#endif
 }
 
 /**

+ 0 - 47
dtool/src/dtoolutil/textEncoder_ext.cxx

@@ -22,26 +22,11 @@
 void Extension<TextEncoder>::
 set_text(PyObject *text) {
   if (PyUnicode_Check(text)) {
-#if PY_VERSION_HEX >= 0x03030000
     Py_ssize_t len;
     const char *str = PyUnicode_AsUTF8AndSize(text, &len);
     _this->set_text(std::string(str, len), TextEncoder::E_utf8);
-#else
-    Py_ssize_t len = PyUnicode_GET_SIZE(text);
-    wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1));
-    PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len);
-    _this->set_wtext(std::wstring(str, len));
-#endif
   } else {
-#if PY_MAJOR_VERSION >= 3
     Dtool_Raise_TypeError("expected string");
-#else
-    char *str;
-    Py_ssize_t len;
-    if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) {
-      _this->set_text(std::string(str, len));
-    }
-#endif
   }
 }
 
@@ -63,13 +48,8 @@ set_text(PyObject *text, TextEncoder::Encoding encoding) {
  */
 PyObject *Extension<TextEncoder>::
 get_text() const {
-#if PY_MAJOR_VERSION >= 3
   std::wstring text = _this->get_wtext();
   return PyUnicode_FromWideChar(text.data(), (Py_ssize_t)text.size());
-#else
-  std::string text = _this->get_text();
-  return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
-#endif
 }
 
 /**
@@ -78,11 +58,7 @@ get_text() const {
 PyObject *Extension<TextEncoder>::
 get_text(TextEncoder::Encoding encoding) const {
   std::string text = _this->get_text(encoding);
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
-#else
-  return PyString_FromStringAndSize((char *)text.data(), (Py_ssize_t)text.size());
-#endif
 }
 
 /**
@@ -91,7 +67,6 @@ get_text(TextEncoder::Encoding encoding) const {
 void Extension<TextEncoder>::
 append_text(PyObject *text) {
   if (PyUnicode_Check(text)) {
-#if PY_VERSION_HEX >= 0x03030000
     Py_ssize_t len;
     const char *str = PyUnicode_AsUTF8AndSize(text, &len);
     std::string text_str(str, len);
@@ -100,22 +75,8 @@ append_text(PyObject *text) {
     } else {
       _this->append_wtext(TextEncoder::decode_text(text_str, TextEncoder::E_utf8));
     }
-#else
-    Py_ssize_t len = PyUnicode_GET_SIZE(text);
-    wchar_t *str = (wchar_t *)alloca(sizeof(wchar_t) * (len + 1));
-    PyUnicode_AsWideChar((PyUnicodeObject *)text, str, len);
-    _this->append_wtext(std::wstring(str, len));
-#endif
   } else {
-#if PY_MAJOR_VERSION >= 3
     Dtool_Raise_TypeError("expected string");
-#else
-    char *str;
-    Py_ssize_t len;
-    if (PyString_AsStringAndSize(text, (char **)&str, &len) != -1) {
-      _this->append_text(std::string(str, len));
-    }
-#endif
   }
 }
 
@@ -125,11 +86,7 @@ append_text(PyObject *text) {
 PyObject *Extension<TextEncoder>::
 encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
   std::string value = TextEncoder::encode_wchar(ch, encoding);
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
-#else
-  return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
-#endif
 }
 
 /**
@@ -139,11 +96,7 @@ encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
 PyObject *Extension<TextEncoder>::
 encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
   std::string value = TextEncoder::encode_wtext(wtext, encoding);
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
-#else
-  return PyString_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
-#endif
 }
 
 /**

+ 0 - 8
dtool/src/prc/streamReader_ext.cxx

@@ -60,11 +60,7 @@ readline() {
     ch = in->get();
   }
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(line.data(), line.size());
-#else
-  return PyString_FromStringAndSize(line.data(), line.size());
-#endif
 }
 
 /**
@@ -80,11 +76,7 @@ readlines() {
 
   PyObject *py_line = readline();
 
-#if PY_MAJOR_VERSION >= 3
   while (PyBytes_GET_SIZE(py_line) > 0) {
-#else
-  while (PyString_GET_SIZE(py_line) > 0) {
-#endif
     PyList_Append(lst, py_line);
     Py_DECREF(py_line);
 

+ 0 - 12
dtool/src/prc/streamWriter.I

@@ -16,9 +16,6 @@
  */
 INLINE StreamWriter::
 StreamWriter(std::ostream &out) :
-#ifdef HAVE_PYTHON
-  softspace(0),
-#endif
   _out(&out),
   _owns_stream(false)
 {
@@ -29,9 +26,6 @@ StreamWriter(std::ostream &out) :
  */
 INLINE StreamWriter::
 StreamWriter(std::ostream *out, bool owns_stream) :
-#ifdef HAVE_PYTHON
-  softspace(0),
-#endif
   _out(out),
   _owns_stream(owns_stream)
 {
@@ -42,9 +36,6 @@ StreamWriter(std::ostream *out, bool owns_stream) :
  */
 INLINE StreamWriter::
 StreamWriter(const StreamWriter &copy) :
-#ifdef HAVE_PYTHON
-  softspace(0),
-#endif
   _out(copy._out),
   _owns_stream(false)
 {
@@ -55,9 +46,6 @@ StreamWriter(const StreamWriter &copy) :
  */
 INLINE StreamWriter::
 StreamWriter(StreamWriter &&from) noexcept :
-#ifdef HAVE_PYTHON
-  softspace(0),
-#endif
   _out(from._out),
   _owns_stream(from._owns_stream)
 {

+ 0 - 6
dtool/src/prc/streamWriter.h

@@ -83,12 +83,6 @@ public:
 private:
   std::ostream *_out;
   bool _owns_stream;
-
-#ifdef HAVE_PYTHON
-PUBLISHED:
-  // Python 2 needs this for printing to work correctly.
-  int softspace;
-#endif
 };
 
 #include "streamWriter.I"

+ 1 - 4
makepanda/installer.nsi

@@ -14,7 +14,7 @@
 ;
 ;   BUILT         - location of panda install tree.
 ;   SOURCE        - location of the panda source-tree if available, OR location of panda install tree.
-;   INCLUDE_PYVER - version of Python that Panda was built with (eg. "2.7", "3.5-32")
+;   INCLUDE_PYVER - version of Python that Panda was built with (eg. "3.8", "3.7-32")
 ;   REGVIEW       - either 32 or 64, depending on the build architecture.
 ;
 
@@ -367,7 +367,6 @@ SectionGroup "Python modules" SecGroupPython
         File /r "${BUILT}\panda3d\*.py"
     SectionEnd
 
-    !insertmacro PyBindingSection 2.7 .pyd
     !if "${REGVIEW}" == "32"
         !insertmacro PyBindingSection 3.5-32 .cp35-win32.pyd
         !insertmacro PyBindingSection 3.6-32 .cp36-win32.pyd
@@ -479,7 +478,6 @@ Function .onInit
     SetRegView ${REGVIEW}
     !endif
 
-    ; We never check for 2.7; it is always disabled in Auto mode
     !if "${REGVIEW}" == "32"
         !insertmacro MaybeEnablePyBindingSection 3.5-32
         !insertmacro MaybeEnablePyBindingSection 3.6-32
@@ -872,7 +870,6 @@ SectionEnd
   !insertmacro MUI_DESCRIPTION_TEXT ${SecTools} $(DESC_SecTools)
   !insertmacro MUI_DESCRIPTION_TEXT ${SecGroupPython} $(DESC_SecGroupPython)
   !insertmacro MUI_DESCRIPTION_TEXT ${SecPyShared} $(DESC_SecPyShared)
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings2.7} $(DESC_SecPyBindings2.7)
   !if "${REGVIEW}" == "32"
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5-32} $(DESC_SecPyBindings3.5-32)
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6-32} $(DESC_SecPyBindings3.6-32)

+ 5 - 22
makepanda/makepackage.py

@@ -209,8 +209,7 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1,
                        python_versions=[], **kwargs):
     outputdir = GetOutputDir()
 
-    # We pack Python 2 and Python 3, if we built with support for it.
-    python2_ver = None
+    # We pack the default Python 3 version that ships with Ubuntu.
     python3_ver = None
     install_python_versions = []
 
@@ -218,12 +217,9 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1,
     oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir))
     sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2])
 
-    # Check that we built with support for these.
+    # Check that we built with support for it.
     for version_info in python_versions:
-        if version_info["version"] == "2.7":
-            python2_ver = "2.7"
-            install_python_versions.append(version_info)
-        elif version_info["version"] == sys_python3_ver:
+        if version_info["version"] == sys_python3_ver:
             python3_ver = sys_python3_ver
             install_python_versions.append(version_info)
 
@@ -301,18 +297,9 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1,
         recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip()
         provides = "panda3d"
 
-        if python2_ver or python3_ver:
-            recommends += ", python-pmw"
-
-        if python2_ver:
-            depends += ", python%s" % (python2_ver)
-            recommends += ", python-wxversion"
-            recommends += ", python-tk (>= %s)" % (python2_ver)
-            provides += ", python2-panda3d"
-
         if python3_ver:
             depends += ", python%s" % (python3_ver)
-            recommends += ", python3-tk (>= %s)" % (python3_ver)
+            recommends += ", python-pmw, python3-tk (>= %s)" % (python3_ver)
             provides += ", python3-panda3d"
 
         if not PkgSkip("NVIDIACG"):
@@ -654,11 +641,7 @@ def MakeInstallerOSX(version, python_versions=[], installdir=None, **kwargs):
 
     for version_info in python_versions:
         pyver = version_info["version"]
-        if pyver in ("2.7", "3.4"):
-            # Don't install these EOL versions of Python by default.
-            cond = "false"
-        else:
-            cond = "isPythonVersionInstalled('%s')" % (pyver)
+        cond = "isPythonVersionInstalled('%s')" % (pyver)
         dist.write('    <choice id="pybindings%s" start_selected="%s" title="Python %s Bindings" tooltip="Python bindings for the Panda3D libraries" description="Support for Python %s.">\n' % (pyver, cond, pyver, pyver))
         dist.write('        <pkg-ref id="org.panda3d.panda3d.pybindings%s.pkg"/>\n' % (pyver))
         dist.write('    </choice>\n')

+ 19 - 26
makepanda/makepanda.py

@@ -7,14 +7,16 @@
 # panda3d.
 #
 ########################################################################
+
+import sys
+if sys.version_info < (3, 5):
+    print("This version of Python is not supported, use version 3.5 or higher.")
+    exit(1)
+
 try:
-    import sys, os, platform, time, stat, re, getopt, threading, signal, shutil
-    if sys.platform == "darwin" or sys.version_info >= (2, 6):
-        import plistlib
-    if sys.version_info >= (3, 0):
-        import queue
-    else:
-        import Queue as queue
+    import os, platform, time, stat, re, getopt, threading, signal, shutil
+    import plistlib
+    import queue
 except KeyboardInterrupt:
     raise
 except:
@@ -484,15 +486,13 @@ SdkAutoDisableMax()
 SdkAutoDisableSpeedTree()
 
 if not PkgSkip("PYTHON") and SDK["PYTHONVERSION"] == "python2.7":
-    warn_prefix = "%sWARNING:%s " % (GetColor("red"), GetColor())
-    print("==========================================================================")
-    print(warn_prefix + "Python 2.7 has reached EOL as of January 1, 2020 and is no longer")
-    print(warn_prefix + "maintained.  Panda3D will soon cease to work with this version.")
-    print(warn_prefix + "Please upgrade to Python 3 now.")
-    print("==========================================================================")
+    pref = "%sERROR:%s " % (GetColor("red"), GetColor())
+    print("========================================================================")
+    print(pref + "Python 2.7 has reached EOL as of January 1, 2020 and is no longer")
+    print(pref + "supported.  Please upgrade to Python 3.5 or later.")
+    print("========================================================================")
     sys.stdout.flush()
-    # Give the user some time to contemplate their sins
-    time.sleep(6.0)
+    sys.exit(1)
 
 ########################################################################
 ##
@@ -1969,8 +1969,7 @@ def FreezePy(target, inputs, opts):
     assert len(inputs) > 0
 
     cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"].replace('\\', '/')) + " "
-    if sys.version_info >= (2, 6):
-        cmdstr += "-B "
+    cmdstr += "-B "
 
     cmdstr += os.path.join(GetOutputDir(), "direct", "dist", "pfreeze.py")
 
@@ -2584,12 +2583,8 @@ p3d_init = """"Python bindings for the Panda3D libraries"
 __version__ = '%s'
 
 if __debug__:
-    import sys
-    if sys.version_info < (3, 0):
-        sys.stderr.write("WARNING: Python 2.7 has reached EOL as of January 1, 2020.\\n")
-        sys.stderr.write("To suppress this warning, upgrade to Python 3.\\n")
-        sys.stderr.flush()
-    del sys
+    if 1 / 2 == 0:
+        raise ImportError("Python 2 is not supported.")
 """ % (WHLVERSION)
 
 if GetTarget() == 'windows':
@@ -6034,9 +6029,7 @@ finally:
 # Run the test suite.
 if RUNTESTS:
     cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"].replace('\\', '/'))
-    if sys.version_info >= (2, 6):
-        cmdstr += " -B"
-    cmdstr += " -m pytest tests"
+    cmdstr += " -B -m pytest tests"
     if GetVerbose():
         cmdstr += " --verbose"
     oscmd(cmdstr)

+ 22 - 46
makepanda/makepandacore.py

@@ -8,15 +8,9 @@
 import sys,os,time,stat,string,re,getopt,fnmatch,threading,signal,shutil,platform,glob,getpass,signal
 import subprocess
 from distutils import sysconfig
-
-if sys.version_info >= (3, 0):
-    import pickle
-    import _thread as thread
-    import configparser
-else:
-    import cPickle as pickle
-    import thread
-    import ConfigParser as configparser
+import pickle
+import _thread as thread
+import configparser
 
 SUFFIX_INC = [".cxx",".cpp",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"]
 SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"]
@@ -49,10 +43,7 @@ if sys.platform == 'darwin':
     # On OSX, platform.architecture reports '64bit' even if it is
     # currently running in 32-bit mode.  But sys.maxint is a reliable
     # indicator.
-    if sys.version_info >= (3, 0):
-        host_64 = (sys.maxsize > 0x100000000)
-    else:
-        host_64 = (sys.maxint > 0x100000000)
+    host_64 = (sys.maxsize > 0x100000000)
 else:
     # On Windows (and Linux?) sys.maxint reports 0x7fffffff even on a
     # 64-bit build.  So we stick with platform.architecture in that
@@ -215,10 +206,7 @@ def GetColor(color = None):
     else:
         token = curses.tparm(curses.tigetstr("sgr0"))
 
-    if sys.version_info >= (3, 0):
-        return token.decode('ascii')
-    else:
-        return token
+    return token.decode('ascii')
 
 def ColorText(color, text, reset=True):
     if reset is True:
@@ -1000,10 +988,7 @@ def JavaCalcDependencies(srcfile, clspath):
 
 if sys.platform == "win32":
     # Note: not supported on cygwin.
-    if sys.version_info >= (3, 0):
-        import winreg
-    else:
-        import _winreg as winreg
+    import winreg
 
 def TryRegistryKey(path):
     try:
@@ -1148,10 +1133,7 @@ def WriteFile(wfile, data, newline=None):
         data = data.replace('\n', newline)
 
     try:
-        if sys.version_info >= (3, 0):
-            dsthandle = open(wfile, "w", newline='')
-        else:
-            dsthandle = open(wfile, "w")
+        dsthandle = open(wfile, "w", newline='')
         dsthandle.write(data)
         dsthandle.close()
     except:
@@ -2057,10 +2039,7 @@ def SdkLocatePython(prefer_thirdparty_python=False):
 
     if GetTarget() == 'windows':
         sdkdir = GetThirdpartyBase() + "/win-python"
-
-        if sys.version_info >= (3, 0):
-            # Python 3 build...
-            sdkdir += "%d.%d" % sys.version_info[:2]
+        sdkdir += "%d.%d" % sys.version_info[:2]
 
         if GetOptimize() <= 2:
             sdkdir += "-dbg"
@@ -3239,15 +3218,8 @@ def SetOrigExt(x, v):
     ORIG_EXT[x] = v
 
 def GetExtensionSuffix():
-    if sys.version_info >= (3, 0):
-        import _imp
-        return _imp.extension_suffixes()[0]
-
-    target = GetTarget()
-    if target == 'windows':
-        return '.pyd'
-    else:
-        return '.so'
+    import _imp
+    return _imp.extension_suffixes()[0]
 
 def GetPythonABI():
     soabi = sysconfig.get_config_var('SOABI')
@@ -3267,11 +3239,6 @@ def GetPythonABI():
     if malloc_flag is None or malloc_flag:
         soabi += 'm'
 
-    if sys.version_info < (3, 3):
-        usize = sysconfig.get_config_var('Py_UNICODE_SIZE')
-        if (usize is None and sys.maxunicode == 0x10ffff) or usize == 4:
-            soabi += 'u'
-
     return soabi
 
 def CalcLocation(fn, ipath):
@@ -3406,12 +3373,14 @@ def UpdatePythonVersionInfoFile(new_info):
             json_data = []
 
         # Prune the list by removing the entries that conflict with our build,
-        # plus the entries that no longer exist
+        # plus the entries that no longer exist, and the EOL Python versions
         for version_info in json_data[:]:
             core_pyd = os.path.join(GetOutputDir(), "panda3d", "core" + version_info["ext_suffix"])
             if version_info["ext_suffix"] == new_info["ext_suffix"] or \
                version_info["soabi"] == new_info["soabi"] or \
-               not os.path.isfile(core_pyd):
+               not os.path.isfile(core_pyd) or \
+               version_info["version"].split(".", 1)[0] == "2" or \
+               version_info["version"] in ("3.0", "3.1", "3.2", "3.3", "3.4"):
                 json_data.remove(version_info)
 
     if not PkgSkip("PYTHON"):
@@ -3428,10 +3397,17 @@ def ReadPythonVersionInfoFile():
     json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json")
     if os.path.isfile(json_file):
         try:
-            return json.load(open(json_file, 'r'))
+            json_data = json.load(open(json_file, 'r'))
         except:
             pass
 
+        # Don't include unsupported versions of Python.
+        for version_info in json_data[:]:
+            if version_info["version"] in ("2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4"):
+                json_data.remove(version_info)
+
+        return json_data
+
     return []
 
 

+ 10 - 17
makepanda/makewheel.py

@@ -20,12 +20,11 @@ from base64 import urlsafe_b64encode
 
 
 def get_abi_tag():
-    if sys.version_info >= (3, 0):
-        soabi = get_config_var('SOABI')
-        if soabi and soabi.startswith('cpython-'):
-            return 'cp' + soabi.split('-')[1]
-        elif soabi:
-            return soabi.replace('.', '_').replace('-', '_')
+    soabi = get_config_var('SOABI')
+    if soabi and soabi.startswith('cpython-'):
+        return 'cp' + soabi.split('-')[1]
+    elif soabi:
+        return soabi.replace('.', '_').replace('-', '_')
 
     soabi = 'cp%d%d' % (sys.version_info[:2])
 
@@ -40,11 +39,6 @@ def get_abi_tag():
     if malloc_flag is None or malloc_flag:
         soabi += 'm'
 
-    if sys.version_info < (3, 3):
-        usize = get_config_var('Py_UNICODE_SIZE')
-        if (usize is None and sys.maxunicode == 0x10ffff) or usize == 4:
-            soabi += 'u'
-
     return soabi
 
 
@@ -537,6 +531,9 @@ def makewheel(version, output_dir, platform=None):
         if not LocateBinary("patchelf"):
             raise Exception("patchelf is required when building a Linux wheel.")
 
+    if sys.version_info < (3, 5):
+        raise Exception("Python 3.5 is required to produce a wheel.")
+
     if platform is None:
         # Determine the platform from the build.
         platform_dat = os.path.join(output_dir, 'tmp', 'platform.dat')
@@ -625,12 +622,8 @@ __version__ = '{0}'
     if '27' in ABI_TAG:
         p3d_init += """
 if __debug__:
-    import sys
-    if sys.version_info < (3, 0):
-        sys.stderr.write("WARNING: Python 2.7 will reach EOL after December 31, 2019.\\n")
-        sys.stderr.write("To suppress this warning, upgrade to Python 3.\\n")
-        sys.stderr.flush()
-    del sys
+    if 1 / 2 == 0:
+        raise ImportError(\"Python 2 is not supported.\")
 """
 
     whl.write_file_data('panda3d/__init__.py', p3d_init)

+ 1 - 1
makepanda/selfdestruct.py

@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python
 
 import os
 import re

+ 2 - 8
panda/CMakeLists.txt

@@ -118,14 +118,8 @@ if(INTERROGATE_PYTHON_INTERFACE)
 __version__ = '${PROJECT_VERSION}'
 
 if __debug__:
-    import sys
-    if sys.version_info < (3, 0):
-        sys.stderr.write('''\\
-WARNING: Python 2.7 has reached EOL as of January 1, 2020.
-To suppress this warning, upgrade to Python 3.
-''')
-        sys.stdout.flush()
-    del sys
+    if 1 / 2 == 0:
+        raise ImportError(\"Python 2 is not supported.\")
 
 ${win32_init}")
 

+ 0 - 2
panda/src/android/python_main.cxx

@@ -18,9 +18,7 @@
 #undef _POSIX_C_SOURCE
 #undef _XOPEN_SOURCE
 #include <Python.h>
-#if PY_MAJOR_VERSION >= 3
 #include <wchar.h>
-#endif
 
 #include <dlfcn.h>
 

+ 1 - 4
panda/src/android/site.py

@@ -4,10 +4,7 @@ import os
 from importlib.abc import Loader, MetaPathFinder
 from importlib.machinery import ModuleSpec
 
-if sys.version_info >= (3, 5):
-    from importlib import _bootstrap_external
-else:
-    from importlib import _bootstrap as _bootstrap_external
+from importlib import _bootstrap_external
 
 sys.platform = "android"
 

+ 0 - 5
panda/src/display/graphicsWindow_ext.cxx

@@ -37,11 +37,6 @@ remove_python_event_handler(PyObject* name){
     if (PyObject_RichCompareBool(pgwp->get_name(), name, Py_EQ) == 1) {
       toRemove.push_back(pgwp);
     }
-#if PY_MAJOR_VERSION < 3
-    else if (PyObject_Compare(pgwp->get_name(), name) == 0) {
-      toRemove.push_back(pgwp);
-    }
-#endif
   }
   std::list<PythonGraphicsWindowProc*>::iterator iter2;
   for (iter2 = toRemove.begin(); iter2 != toRemove.end(); ++iter2) {

+ 0 - 4
panda/src/display/windowProperties_ext.cxx

@@ -66,11 +66,7 @@ __init__(PyObject *self, PyObject *args, PyObject *kwds) {
         PyObject *key_repr = PyObject_Repr(key);
         PyErr_Format(PyExc_TypeError,
                      "%.100s is an invalid keyword argument for WindowProperties()",
-#if PY_MAJOR_VERSION >= 3
                      PyUnicode_AsUTF8(key_repr)
-#else
-                     PyString_AsString(key_repr)
-#endif
                     );
         Py_DECREF(key_repr);
         return;

+ 0 - 2
panda/src/event/asyncFuture_ext.cxx

@@ -285,13 +285,11 @@ gather(PyObject *args) {
         futures.push_back(fut);
         continue;
       }
-#if PY_VERSION_HEX >= 0x03050000
     } else if (PyCoro_CheckExact(item)) {
       // We allow passing in a coroutine instead of a future.  This causes it
       // to be scheduled as a task.
       futures.push_back(new PythonTask(item));
       continue;
-#endif
     }
     return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future");
   }

+ 0 - 52
panda/src/event/pythonTask.cxx

@@ -52,12 +52,10 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
   if (func_or_coro == Py_None || PyCallable_Check(func_or_coro)) {
     _function = func_or_coro;
     Py_INCREF(_function);
-#if PY_VERSION_HEX >= 0x03050000
   } else if (PyCoro_CheckExact(func_or_coro)) {
     // We also allow passing in a coroutine, because why not.
     _generator = func_or_coro;
     Py_INCREF(_generator);
-#endif
   } else if (PyGen_CheckExact(func_or_coro)) {
     // Something emulating a coroutine.
     _generator = func_or_coro;
@@ -283,11 +281,7 @@ exception() const {
  */
 int PythonTask::
 __setattr__(PyObject *self, PyObject *attr, PyObject *v) {
-#if PY_MAJOR_VERSION >= 3
   if (!PyUnicode_Check(attr)) {
-#else
-  if (!PyString_Check(attr)) {
-#endif
     PyErr_Format(PyExc_TypeError,
                  "attribute name must be string, not '%.200s'",
                  attr->ob_type->tp_name);
@@ -307,13 +301,8 @@ __setattr__(PyObject *self, PyObject *attr, PyObject *v) {
     PyObject *str = PyObject_Repr(v);
     task_cat.debug()
       << *this << ": task."
-#if PY_MAJOR_VERSION >= 3
       << PyUnicode_AsUTF8(attr) << " = "
       << PyUnicode_AsUTF8(str) << "\n";
-#else
-      << PyString_AsString(attr) << " = "
-      << PyString_AsString(str) << "\n";
-#endif
     Py_DECREF(str);
   }
 
@@ -340,15 +329,9 @@ __delattr__(PyObject *self, PyObject *attr) {
 
   if (PyDict_DelItem(__dict__, attr) == -1) {
     // PyDict_DelItem does not raise an exception.
-#if PY_MAJOR_VERSION < 3
-    PyErr_Format(PyExc_AttributeError,
-                 "'PythonTask' object has no attribute '%.400s'",
-                 PyString_AS_STRING(attr));
-#else
     PyErr_Format(PyExc_AttributeError,
                  "'PythonTask' object has no attribute '%U'",
                  attr);
-#endif
     return -1;
   }
 
@@ -475,23 +458,15 @@ do_python_task() {
       // The function has yielded a generator.  We will call into that
       // henceforth, instead of calling the function from the top again.
       if (task_cat.is_debug()) {
-#if PY_MAJOR_VERSION >= 3
         PyObject *str = PyObject_ASCII(_function);
         task_cat.debug()
           << PyUnicode_AsUTF8(str) << " in " << *this
           << " yielded a generator.\n";
-#else
-        PyObject *str = PyObject_Repr(_function);
-        task_cat.debug()
-          << PyString_AsString(str) << " in " << *this
-          << " yielded a generator.\n";
-#endif
         Py_DECREF(str);
       }
       _generator = result;
       result = nullptr;
 
-#if PY_VERSION_HEX >= 0x03050000
     } else if (result != nullptr && Py_TYPE(result)->tp_as_async != nullptr) {
       // The function yielded a coroutine, or something of the sort.
       if (task_cat.is_debug()) {
@@ -513,7 +488,6 @@ do_python_task() {
         Py_DECREF(result);
       }
       result = nullptr;
-#endif
     }
   }
 
@@ -532,13 +506,7 @@ do_python_task() {
       Py_DECREF(_generator);
       _generator = nullptr;
 
-#if PY_VERSION_HEX >= 0x03030000
       if (_PyGen_FetchStopIterationValue(&result) == 0) {
-#else
-      if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
-        result = Py_None;
-        Py_INCREF(result);
-#endif
         PyErr_Clear();
 
         // If we passed a coroutine into the task, eg. something like:
@@ -635,14 +603,12 @@ do_python_task() {
             << "future.done is not callable\n";
           return DS_interrupt;
         }
-#if PY_MAJOR_VERSION >= 3
         if (task_cat.is_debug()) {
           PyObject *str = PyObject_ASCII(result);
           task_cat.debug()
             << *this << " is now polling " << PyUnicode_AsUTF8(str) << ".done()\n";
           Py_DECREF(str);
         }
-#endif
         Py_DECREF(result);
         return DS_cont;
       }
@@ -671,13 +637,8 @@ do_python_task() {
     return DS_done;
   }
 
-#if PY_MAJOR_VERSION >= 3
   if (PyLong_Check(result)) {
     long retval = PyLong_AS_LONG(result);
-#else
-  if (PyInt_Check(result)) {
-    long retval = PyInt_AS_LONG(result);
-#endif
 
     switch (retval) {
     case DS_again:
@@ -710,11 +671,7 @@ do_python_task() {
   PyMethodDef *meth = nullptr;
   if (PyCFunction_Check(result)) {
     meth = ((PyCFunctionObject *)result)->m_ml;
-#if PY_MAJOR_VERSION >= 3
   } else if (Py_TYPE(result) == &PyMethodDescr_Type) {
-#else
-  } else if (strcmp(Py_TYPE(result)->tp_name, "method_descriptor") == 0) {
-#endif
     meth = ((PyMethodDescrObject *)result)->d_method;
   }
 
@@ -724,21 +681,12 @@ do_python_task() {
   }
 
   std::ostringstream strm;
-#if PY_MAJOR_VERSION >= 3
   PyObject *str = PyObject_ASCII(result);
   if (str == nullptr) {
     str = PyUnicode_FromString("<repr error>");
   }
   strm
     << *this << " returned " << PyUnicode_AsUTF8(str);
-#else
-  PyObject *str = PyObject_Repr(result);
-  if (str == nullptr) {
-    str = PyString_FromString("<repr error>");
-  }
-  strm
-    << *this << " returned " << PyString_AsString(str);
-#endif
   Py_DECREF(str);
   Py_DECREF(result);
   std::string message = strm.str();

+ 0 - 5
panda/src/express/datagram_ext.I

@@ -18,12 +18,7 @@ INLINE PyObject *Extension<Datagram>::
 get_message() const {
   const char *data = (const char *)_this->get_data();
   size_t size = _this->get_length();
-
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)data, size);
-#else
-  return PyString_FromStringAndSize((char *)data, size);
-#endif
 }
 
 /**

+ 0 - 92
panda/src/express/pointerToArray_ext.I

@@ -80,11 +80,7 @@ INLINE void set_matrix_view(Py_buffer &view, int flags, int length, int size, bo
 template<class Element>
 INLINE void Extension<PointerToArray<Element> >::
 __init__(PyObject *self, PyObject *source) {
-#if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(source)) {
-#else
-  if (PyString_CheckExact(source)) {
-#endif
     // It's a byte sequence, or any object that exports the buffer protocol.
     this->set_data(source);
     return;
@@ -158,11 +154,7 @@ __setitem__(size_t n, const Element &value) {
 template<class Element>
 INLINE PyObject *Extension<PointerToArray<Element> >::
 get_data() const {
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
-#else
-  return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
-#endif
 }
 
 /**
@@ -175,7 +167,6 @@ get_data() const {
 template<class Element>
 INLINE void Extension<PointerToArray<Element> >::
 set_data(PyObject *data) {
-#if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(data)) {
     // User passed a buffer object.
     Py_buffer view;
@@ -208,33 +199,6 @@ set_data(PyObject *data) {
     PyBuffer_Release(&view);
     return;
   }
-#endif
-
-  // In Python 2, there was also an older buffer protocol, supported by eg.
-  // str and array objects.
-#if PY_MAJOR_VERSION < 3
-  // The old, deprecated buffer interface, as used by eg. the array module.
-  const void *buffer;
-  Py_ssize_t buffer_len;
-  if (!PyUnicode_CheckExact(data) &&
-      PyObject_AsReadBuffer(data, &buffer, &buffer_len) == 0) {
-    if (buffer_len % sizeof(Element) != 0) {
-      PyErr_Format(PyExc_ValueError,
-                   "byte buffer is not a multiple of %zu bytes",
-                   sizeof(Element));
-      return;
-    }
-
-    if (buffer_len > 0) {
-      this->_this->resize(buffer_len / sizeof(Element));
-      memcpy(this->_this->p(), buffer, buffer_len);
-    } else {
-      this->_this->clear();
-    }
-
-    return;
-  }
-#endif
 
   Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object");
 }
@@ -252,11 +216,7 @@ get_subdata(size_t n, size_t count) const {
   n = std::min(n, this->_this->size());
   count = std::max(count, n);
   count = std::min(count, this->_this->size() - n);
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
-#else
-  return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
-#endif
 }
 
 /**
@@ -277,11 +237,7 @@ __getitem__(size_t n) const {
 template<class Element>
 INLINE PyObject *Extension<ConstPointerToArray<Element> >::
 get_data() const {
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
-#else
-  return PyString_FromStringAndSize((char *)this->_this->p(), sizeof(Element) * this->_this->size());
-#endif
 }
 
 /**
@@ -297,11 +253,7 @@ get_subdata(size_t n, size_t count) const {
   n = std::min(n, this->_this->size());
   count = std::max(count, n);
   count = std::min(count, this->_this->size() - n);
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
-#else
-  return PyString_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
-#endif
 }
 
 /**
@@ -311,7 +263,6 @@ get_subdata(size_t n, size_t count) const {
 template<class Element>
 INLINE int Extension<PointerToArray<Element> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   const char *format = get_format_code(Element);
   if (format == nullptr) {
     // Not supported.
@@ -350,9 +301,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -362,7 +310,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<>
 INLINE int Extension<PointerToArray<LMatrix3f> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   if (self != nullptr) {
     Py_INCREF(self);
   }
@@ -376,9 +323,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -388,7 +332,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<>
 INLINE int Extension<PointerToArray<LMatrix3d> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   if (self != nullptr) {
     Py_INCREF(self);
   }
@@ -402,9 +345,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -414,7 +354,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<>
 INLINE int Extension<PointerToArray<UnalignedLMatrix4f> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   if (self != nullptr) {
     Py_INCREF(self);
   }
@@ -428,9 +367,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -440,7 +376,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<>
 INLINE int Extension<PointerToArray<UnalignedLMatrix4d> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   if (self != nullptr) {
     Py_INCREF(self);
   }
@@ -454,9 +389,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -465,14 +397,12 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
 template<class Element>
 INLINE void Extension<PointerToArray<Element> >::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
-#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements view->obj.
   if (view->internal != nullptr) {
     // Oh, right, let's not forget to unref this.
     unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = nullptr;
   }
-#endif
 }
 
 /**
@@ -482,7 +412,6 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
 template<class Element>
 INLINE int Extension<ConstPointerToArray<Element> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
@@ -527,9 +456,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -538,7 +464,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<>
 INLINE int Extension<ConstPointerToArray<LMatrix3f> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
@@ -557,9 +482,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -568,7 +490,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<>
 INLINE int Extension<ConstPointerToArray<LMatrix3d> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
@@ -587,9 +508,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -598,7 +516,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<>
 INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4f> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
@@ -617,9 +534,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -628,7 +542,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<>
 INLINE int Extension<ConstPointerToArray<UnalignedLMatrix4d> >::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
     PyErr_SetString(PyExc_BufferError,
                     "Object is not writable.");
@@ -647,9 +560,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->internal = (void*) this->_this;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -658,12 +568,10 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
 template<class Element>
 INLINE void Extension<ConstPointerToArray<Element> >::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
-#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements obj->view.
   if (view->internal != nullptr) {
     // Oh, right, let's not forget to unref this.
     unref_delete((const PointerToArray<Element> *)view->internal);
     view->internal = nullptr;
   }
-#endif
 }

+ 0 - 16
panda/src/express/ramfile_ext.cxx

@@ -26,11 +26,7 @@ read(size_t length) {
   length = std::min(length, data_length - _this->_pos);
   _this->_pos = std::min(_this->_pos + length, data_length);
 
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)data, length);
-#else
-  return PyString_FromStringAndSize((char *)data, length);
-#endif
 }
 
 /**
@@ -44,11 +40,7 @@ read(size_t length) {
 PyObject *Extension<Ramfile>::
 readline() {
   std::string line = _this->readline();
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(line.data(), line.size());
-#else
-  return PyString_FromStringAndSize(line.data(), line.size());
-#endif
 }
 
 /**
@@ -64,11 +56,7 @@ readlines() {
 
   std::string line = _this->readline();
   while (!line.empty()) {
-#if PY_MAJOR_VERSION >= 3
     PyObject *py_line = PyBytes_FromStringAndSize(line.data(), line.size());
-#else
-    PyObject *py_line = PyString_FromStringAndSize(line.data(), line.size());
-#endif
 
     PyList_Append(lst, py_line);
     Py_DECREF(py_line);
@@ -83,11 +71,7 @@ readlines() {
  */
 PyObject *Extension<Ramfile>::
 get_data() const {
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize(_this->_data.data(), _this->_data.size());
-#else
-  return PyString_FromStringAndSize(_this->_data.data(), _this->_data.size());
-#endif
 }
 
 #endif

+ 0 - 21
panda/src/express/stringStream_ext.cxx

@@ -31,17 +31,9 @@ get_data() {
   _this->flush();
   const vector_uchar &data = _this->_buf.get_data();
   if (!data.empty()) {
-#if PY_MAJOR_VERSION >= 3
     return PyBytes_FromStringAndSize((char *)&data[0], data.size());
-#else
-    return PyString_FromStringAndSize((char *)&data[0], data.size());
-#endif
   }
-#if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize("", 0);
-#else
-  return PyString_FromStringAndSize("", 0);
-#endif
 }
 
 /**
@@ -55,7 +47,6 @@ set_data(PyObject *data) {
     return;
   }
 
-#if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(data)) {
     Py_buffer view;
     if (PyObject_GetBuffer(data, &view, PyBUF_CONTIG_RO) == -1) {
@@ -67,18 +58,6 @@ set_data(PyObject *data) {
     PyBuffer_Release(&view);
     return;
   }
-#endif
-
-#if PY_MAJOR_VERSION < 3
-  if (PyString_Check(data)) {
-    char *buffer;
-    Py_ssize_t length;
-    if (PyString_AsStringAndSize(data, &buffer, &length) != -1) {
-      _this->set_data((const unsigned char *)buffer, (size_t)length);
-    }
-    return;
-  }
-#endif
 
   PyErr_SetString(PyExc_TypeError,
                   "StringStream requires a bytes or buffer object");

+ 0 - 14
panda/src/express/virtualFileSystem_ext.cxx

@@ -43,19 +43,11 @@ read_file(const Filename &filename, bool auto_unwrap) const {
     return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", filename.c_str());
   }
 
-#if PY_MAJOR_VERSION >= 3
   if (pv.empty()) {
     return PyBytes_FromStringAndSize("", 0);
   } else {
     return PyBytes_FromStringAndSize((const char *)&pv[0], pv.size());
   }
-#else
-  if (pv.empty()) {
-    return PyString_FromStringAndSize("", 0);
-  } else {
-    return PyString_FromStringAndSize((const char *)&pv[0], pv.size());
-  }
-#endif
 }
 
 /**
@@ -71,15 +63,9 @@ write_file(const Filename &filename, PyObject *data, bool auto_wrap) {
   char *buffer;
   Py_ssize_t length;
 
-#if PY_MAJOR_VERSION >= 3
   if (PyBytes_AsStringAndSize(data, &buffer, &length) == -1) {
     return nullptr;
   }
-#else
-  if (PyString_AsStringAndSize(data, &buffer, &length) == -1) {
-    return nullptr;
-  }
-#endif
 
   bool result = _this->write_file(filename, (const unsigned char *)buffer, length, auto_wrap);
   return PyBool_FromLong(result);

+ 0 - 14
panda/src/express/virtualFile_ext.cxx

@@ -44,19 +44,11 @@ read_file(bool auto_unwrap) const {
     return PyErr_Format(PyExc_IOError, "Failed to read file: '%s'", fn.c_str());
   }
 
-#if PY_MAJOR_VERSION >= 3
   if (pv.empty()) {
     return PyBytes_FromStringAndSize("", 0);
   } else {
     return PyBytes_FromStringAndSize((const char *)&pv[0], pv.size());
   }
-#else
-  if (pv.empty()) {
-    return PyString_FromStringAndSize("", 0);
-  } else {
-    return PyString_FromStringAndSize((const char *)&pv[0], pv.size());
-  }
-#endif
 }
 
 /**
@@ -72,15 +64,9 @@ write_file(PyObject *data, bool auto_wrap) {
   char *buffer;
   Py_ssize_t length;
 
-#if PY_MAJOR_VERSION >= 3
   if (PyBytes_AsStringAndSize(data, &buffer, &length) == -1) {
     return nullptr;
   }
-#else
-  if (PyString_AsStringAndSize(data, &buffer, &length) == -1) {
-    return nullptr;
-  }
-#endif
 
   // Release the GIL while we do this potentially slow operation.
 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)

+ 0 - 28
panda/src/gobj/geomVertexArrayData_ext.cxx

@@ -28,7 +28,6 @@ struct InternalBufferData {
  */
 int Extension<GeomVertexArrayData>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
-#if PY_VERSION_HEX >= 0x02060000
   PT(GeomVertexArrayDataHandle) handle = _this->modify_handle();
   CPT(GeomVertexArrayFormat) format = handle->get_array_format();
 
@@ -78,9 +77,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
   view->suboffsets = nullptr;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -88,7 +84,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) {
  */
 int Extension<GeomVertexArrayData>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
       PyErr_SetString(PyExc_BufferError,
                       "Object is not writable.");
@@ -144,9 +139,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->suboffsets = nullptr;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 /**
@@ -154,7 +146,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
  */
 void Extension<GeomVertexArrayData>::
 __releasebuffer__(PyObject *self, Py_buffer *view) const {
-#if PY_VERSION_HEX >= 0x02060000
   // Note: PyBuffer_Release automatically decrements view->obj.
   InternalBufferData *data;
   data = (InternalBufferData *) view->internal;
@@ -163,7 +154,6 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
   }
   delete data;
   view->internal = nullptr;
-#endif
 }
 
 /**
@@ -172,11 +162,6 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
  */
 void Extension<GeomVertexArrayDataHandle>::
 copy_data_from(PyObject *buffer) {
-
-#if PY_VERSION_HEX < 0x02060000
-  PyErr_SetString(PyExc_TypeError, "buffer interface not supported before Python 2.6");
-
-#else
   if (!PyObject_CheckBuffer(buffer)) {
     PyErr_SetString(PyExc_TypeError, "buffer object expected");
     return;
@@ -191,7 +176,6 @@ copy_data_from(PyObject *buffer) {
   _this->copy_data_from((const unsigned char *) view.buf, view.len);
 
   PyBuffer_Release(&view);
-#endif
 }
 
 /**
@@ -201,11 +185,6 @@ copy_data_from(PyObject *buffer) {
  */
 void Extension<GeomVertexArrayDataHandle>::
 copy_subdata_from(size_t to_start, size_t to_size, PyObject *buffer) {
-
-#if PY_VERSION_HEX < 0x02060000
-  PyErr_SetString(PyExc_TypeError, "buffer interface not supported before Python 2.6");
-
-#else
   if (!PyObject_CheckBuffer(buffer)) {
     PyErr_SetString(PyExc_TypeError, "buffer object expected");
     return;
@@ -222,7 +201,6 @@ copy_subdata_from(size_t to_start, size_t to_size, PyObject *buffer) {
                            0, (size_t) view.len);
 
   PyBuffer_Release(&view);
-#endif
 }
 
 /**
@@ -234,11 +212,6 @@ void Extension<GeomVertexArrayDataHandle>::
 copy_subdata_from(size_t to_start, size_t to_size,
                   PyObject *buffer,
                   size_t from_start, size_t from_size) {
-
-#if PY_VERSION_HEX < 0x02060000
-  PyErr_SetString(PyExc_TypeError, "buffer interface not supported before Python 2.6");
-
-#else
   if (!PyObject_CheckBuffer(buffer)) {
     PyErr_SetString(PyExc_TypeError, "buffer object expected");
     return;
@@ -259,7 +232,6 @@ copy_subdata_from(size_t to_start, size_t to_size,
                            from_start, from_size);
 
   PyBuffer_Release(&view);
-#endif
 }
 
 #endif  // HAVE_PYTHON

+ 0 - 26
panda/src/gobj/internalName_ext.cxx

@@ -24,7 +24,6 @@ extern struct Dtool_PyTypedObject Dtool_InternalName;
  * to InternalName objects more efficiently by storing a mapping between
  * Python and Panda interned strings.
  */
-#if PY_MAJOR_VERSION >= 3
 PT(InternalName) Extension<InternalName>::
 make(PyObject *str) {
   if (!PyUnicode_Check(str)) {
@@ -55,31 +54,6 @@ make(PyObject *str) {
     const char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
     string name(c_str, len);
 
-#else
-PT(InternalName) Extension<InternalName>::
-make(PyObject *str) {
-  if (!PyString_Check(str)) {
-    Dtool_Raise_ArgTypeError(str, 0, "InternalName.make", "str");
-    return nullptr;
-  }
-
-  if (!PyString_CHECK_INTERNED(str)) {
-    // Not an interned string; don't bother.
-    string name(PyString_AS_STRING(str), PyString_GET_SIZE(str));
-    return InternalName::make(name);
-  }
-
-  InternalName::PyInternTable::const_iterator it;
-  it = InternalName::_py_intern_table.find((PyObject*)str);
-
-  if (it != InternalName::_py_intern_table.end()) {
-    return (*it).second;
-
-  } else {
-    string name(PyString_AS_STRING(str), PyString_GET_SIZE(str));
-
-#endif  // PY_MAJOR_VERSION
-
     PT(InternalName) iname = InternalName::make(name);
 
     // We basically leak references to both the PyObject and the InternalName.

+ 0 - 27
panda/src/gobj/texture_ext.cxx

@@ -43,7 +43,6 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression,
     }
   }
 
-#if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(image)) {
     // User passed a buffer object.
     Py_buffer view;
@@ -82,30 +81,6 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression,
     PyBuffer_Release(&view);
     return;
   }
-#endif
-
-#if PY_MAJOR_VERSION < 3
-  // The old, deprecated buffer interface, as used by eg. the array module.
-  const void *buffer;
-  Py_ssize_t buffer_len;
-  if (!PyUnicode_CheckExact(image) &&
-      PyObject_AsReadBuffer(image, &buffer, &buffer_len) == 0) {
-    if (compression == Texture::CM_off) {
-      int component_width = _this->get_component_width();
-      if (buffer_len % component_width != 0) {
-        PyErr_Format(PyExc_ValueError,
-                    "byte buffer is not a multiple of %d bytes",
-                    component_width);
-        return;
-      }
-    }
-
-    PTA_uchar data = PTA_uchar::empty_array(buffer_len, Texture::get_class_type());
-    memcpy(data.p(), buffer, buffer_len);
-    _this->set_ram_image(std::move(data), compression, page_size);
-    return;
-  }
-#endif
 
   Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image", "CPTA_uchar or buffer");
 }
@@ -129,7 +104,6 @@ set_ram_image_as(PyObject *image, const std::string &provided_format) {
     }
   }
 
-#if PY_VERSION_HEX >= 0x02060000
   if (PyObject_CheckBuffer(image)) {
     // User passed a buffer object.
     Py_buffer view;
@@ -160,7 +134,6 @@ set_ram_image_as(PyObject *image, const std::string &provided_format) {
     PyBuffer_Release(&view);
     return;
   }
-#endif
 
   Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image_as", "CPTA_uchar or buffer");
 }

+ 0 - 5
panda/src/linmath/lvecBase2_ext_src.I

@@ -12,14 +12,9 @@
  */
 
 #ifdef FLOATTYPE_IS_INT
-#if PY_MAJOR_VERSION >= 3
 #define PYNUMBER_FLOATTYPE PyNumber_Long
 #define PY_AS_FLOATTYPE PyLong_AS_LONG
 #else
-#define PYNUMBER_FLOATTYPE PyNumber_Int
-#define PY_AS_FLOATTYPE PyInt_AS_LONG
-#endif
-#else
 #define PYNUMBER_FLOATTYPE PyNumber_Float
 #define PY_AS_FLOATTYPE (FLOATTYPE)PyFloat_AsDouble
 #endif

+ 0 - 5
panda/src/linmath/lvecBase3_ext_src.I

@@ -12,14 +12,9 @@
  */
 
 #ifdef FLOATTYPE_IS_INT
-#if PY_MAJOR_VERSION >= 3
 #define PYNUMBER_FLOATTYPE PyNumber_Long
 #define PY_AS_FLOATTYPE PyLong_AS_LONG
 #else
-#define PYNUMBER_FLOATTYPE PyNumber_Int
-#define PY_AS_FLOATTYPE PyInt_AS_LONG
-#endif
-#else
 #define PYNUMBER_FLOATTYPE PyNumber_Float
 #define PY_AS_FLOATTYPE (FLOATTYPE)PyFloat_AsDouble
 #endif

+ 0 - 5
panda/src/linmath/lvecBase4_ext_src.I

@@ -12,14 +12,9 @@
  */
 
 #ifdef FLOATTYPE_IS_INT
-#if PY_MAJOR_VERSION >= 3
 #define PYNUMBER_FLOATTYPE PyNumber_Long
 #define PY_AS_FLOATTYPE PyLong_AS_LONG
 #else
-#define PYNUMBER_FLOATTYPE PyNumber_Int
-#define PY_AS_FLOATTYPE PyInt_AS_LONG
-#endif
-#else
 #define PYNUMBER_FLOATTYPE PyNumber_Float
 #define PY_AS_FLOATTYPE (FLOATTYPE)PyFloat_AsDouble
 #endif

+ 0 - 6
panda/src/pgraph/loaderFileTypeRegistry_ext.cxx

@@ -48,13 +48,7 @@ register_deferred_type(PyObject *entry_point) {
 
   const char *name_str;
   Py_ssize_t name_len;
-#if PY_MAJOR_VERSION >= 3
   name_str = PyUnicode_AsUTF8AndSize(name, &name_len);
-#else
-  if (PyString_AsStringAndSize(name, (char **)&name_str, &name_len) == -1) {
-    name_str = nullptr;
-  }
-#endif
   Py_DECREF(name);
 
   if (name_str == nullptr) {

+ 0 - 4
panda/src/pgraph/nodePath_ext.cxx

@@ -286,12 +286,8 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
   while (PyDict_Next(kwargs, &pos, &key, &value)) {
     char *buffer;
     Py_ssize_t length;
-#if PY_MAJOR_VERSION >= 3
     buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
     if (buffer == nullptr) {
-#else
-    if (PyString_AsStringAndSize(key, &buffer, &length) == -1) {
-#endif
       Dtool_Raise_TypeError("NodePath.set_shader_inputs accepts only string keywords");
       return;
     }

+ 1 - 32
panda/src/pgraph/pythonLoaderFileType.cxx

@@ -84,11 +84,7 @@ init(PyObject *loader) {
   // it must occur in the list.
   PyObject *extensions = PyObject_GetAttrString(loader, "extensions");
   if (extensions != nullptr) {
-    if (PyUnicode_Check(extensions)
-#if PY_MAJOR_VERSION < 3
-      || PyString_Check(extensions)
-#endif
-      ) {
+    if (PyUnicode_Check(extensions)) {
       Dtool_Raise_TypeError("extensions list should be a list or tuple");
       Py_DECREF(extensions);
       return false;
@@ -111,14 +107,7 @@ init(PyObject *loader) {
       PyObject *extension = items[i];
       const char *extension_str;
       Py_ssize_t extension_len;
-  #if PY_MAJOR_VERSION >= 3
       extension_str = PyUnicode_AsUTF8AndSize(extension, &extension_len);
-  #else
-      if (PyString_AsStringAndSize(extension, (char **)&extension_str, &extension_len) == -1) {
-        extension_str = nullptr;
-      }
-  #endif
-
       if (extension_str == nullptr) {
         Py_DECREF(sequence);
         return false;
@@ -146,11 +135,7 @@ init(PyObject *loader) {
       loader_cat.error()
         << "Registered extension '" << _extension
         << "' does not occur in extensions list of "
-#if PY_MAJOR_VERSION >= 3
         << PyUnicode_AsUTF8(repr) << "\n";
-#else
-        << PyString_AsString(repr) << "\n";
-#endif
       Py_DECREF(repr);
       return false;
     }
@@ -179,17 +164,9 @@ init(PyObject *loader) {
   PyErr_Clear();
 
   if (_load_func == nullptr && _save_func == nullptr) {
-#if PY_MAJOR_VERSION >= 3
     PyErr_Format(PyExc_TypeError,
                  "loader plug-in %R does not define load_file or save_file function",
                  loader);
-#else
-    PyObject *repr = PyObject_Repr(loader);
-    PyErr_Format(PyExc_TypeError,
-                 "loader plug-in %s does not define load_file or save_file function",
-                 PyString_AsString(repr));
-    Py_DECREF(repr);
-#endif
     return false;
   }
 
@@ -219,11 +196,7 @@ ensure_loaded() const {
 
     loader_cat.info()
       << "loading file type module: "
-#if PY_MAJOR_VERSION >= 3
       << PyUnicode_AsUTF8(repr) << "\n";
-#else
-      << PyString_AsString(repr) << "\n";
-#endif
     Py_DECREF(repr);
   }
 
@@ -238,11 +211,7 @@ ensure_loaded() const {
 
     loader_cat.error()
       << "unable to load "
-#if PY_MAJOR_VERSION >= 3
       << PyUnicode_AsUTF8(repr) << "\n";
-#else
-      << PyString_AsString(repr) << "\n";
-#endif
     Py_DECREF(repr);
   }
 

+ 0 - 4
panda/src/pgraph/shaderAttrib_ext.cxx

@@ -49,12 +49,8 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) const {
   while (PyDict_Next(kwargs, &pos, &key, &value)) {
     char *buffer;
     Py_ssize_t length;
-#if PY_MAJOR_VERSION >= 3
     buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
     if (buffer == nullptr) {
-#else
-    if (PyString_AsStringAndSize(key, &buffer, &length) == -1) {
-#endif
       Dtool_Raise_TypeError("ShaderAttrib.set_shader_inputs accepts only string keywords");
       delete attrib;
       return nullptr;

+ 0 - 8
panda/src/pgraph/shaderInput_ext.cxx

@@ -261,14 +261,6 @@ __init__(CPT_InternalName name, PyObject *value, int priority) {
     _this->_stored_ptr = vec;
     _this->_stored_vector = vec;
 
-#if PY_MAJOR_VERSION < 3
-  } else if (PyInt_Check(value)) {
-    LVecBase4i vec((int)PyInt_AS_LONG(value), 0, 0, 0);
-    _this->_type = ShaderInput::M_numeric;
-    _this->_stored_ptr = vec;
-    _this->_stored_vector.set((PN_stdfloat)vec[0], 0, 0, 0);
-#endif
-
   } else if (PyLong_Check(value)) {
     LVecBase4i vec((int)PyLong_AsLong(value), 0, 0, 0);
     _this->_type = ShaderInput::M_numeric;

+ 0 - 4
panda/src/pnmimage/pfmFile_ext.cxx

@@ -77,7 +77,6 @@ get_points() const {
  */
 int Extension<PfmFile>::
 __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
-#if PY_VERSION_HEX >= 0x02060000
   if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) {
       PyErr_SetString(PyExc_BufferError,
                       "Object is not writable.");
@@ -117,9 +116,6 @@ __getbuffer__(PyObject *self, Py_buffer *view, int flags) const {
   view->suboffsets = nullptr;
 
   return 0;
-#else
-  return -1;
-#endif
 }
 
 #endif  // HAVE_PYTHON

+ 0 - 12
panda/src/putil/bitArray_ext.cxx

@@ -20,18 +20,6 @@
  */
 void Extension<BitArray>::
 __init__(PyObject *init_value) {
-#if PY_MAJOR_VERSION < 3
-  if (PyInt_Check(init_value)) {
-    long value = PyInt_AS_LONG(init_value);
-    if (value >= 0) {
-      _this->set_word(0, value);
-    } else {
-      PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
-    }
-    return;
-  }
-#endif
-
   if (!PyLong_Check(init_value) || Py_SIZE(init_value) < 0) {
     PyErr_SetString(PyExc_ValueError, "BitArray constructor requires a positive integer");
     return;

+ 0 - 12
panda/src/putil/doubleBitMask_ext.I

@@ -17,18 +17,6 @@
 template<class BMType>
 INLINE void Extension<DoubleBitMask<BMType> >::
 __init__(PyObject *init_value) {
-#if PY_MAJOR_VERSION < 3
-  if (PyInt_Check(init_value)) {
-    long value = PyInt_AS_LONG(init_value);
-    if (value >= 0) {
-      this->_this->store((typename BMType::WordType)value, 0, sizeof(long) * 8 - 1);
-    } else {
-      PyErr_SetString(PyExc_ValueError, "DoubleBitMask constructor requires a positive integer");
-    }
-    return;
-  }
-#endif
-
   if (!PyLong_Check(init_value) || Py_SIZE(init_value) < 0) {
     PyErr_SetString(PyExc_ValueError, "DoubleBitMask constructor requires a positive integer");
     return;

+ 0 - 2
panda/src/putil/pythonCallbackObject.cxx

@@ -45,9 +45,7 @@ PythonCallbackObject(PyObject *function) {
   // Ensure that the Python threading system is initialized and ready to go.
 #ifdef WITH_THREAD  // This symbol defined within Python.h
 
-#if PY_VERSION_HEX >= 0x03020000
   Py_Initialize();
-#endif
 
   PyEval_InitThreads();
 #endif

+ 0 - 2
setup.cfg

@@ -13,8 +13,6 @@ classifiers =
     Operating System :: OS Independent
     Programming Language :: C++
     Programming Language :: Python
-    Programming Language :: Python :: 2
-    Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
     Programming Language :: Python :: 3.5
     Programming Language :: Python :: 3.6

+ 0 - 1
tests/dtoolutil/test_filename.py

@@ -15,7 +15,6 @@ def test_filename_open():
     open(fn, 'rb')
 
 
[email protected](sys.version_info < (3, 4), reason="Requires Python 3.4")
 def test_filename_ctor_pathlib():
     pathlib = pytest.importorskip('pathlib')
 

+ 8 - 12
tests/dtoolutil/test_textencoder.py

@@ -2,28 +2,24 @@ import sys
 import pytest
 from panda3d.core import TextEncoder
 
-if sys.version_info >= (3, 0):
-    unichr = chr
-    xrange = range
-
 
 def valid_characters():
     """Generator yielding all valid Unicode code points."""
 
-    for i in xrange(0xd800):
-        yield unichr(i)
+    for i in range(0xd800):
+        yield chr(i)
 
-    for i in xrange(0xe000, sys.maxunicode + 1):
+    for i in range(0xe000, sys.maxunicode + 1):
         if i != 0xfeff and i & 0xfffe != 0xfffe:
-            yield unichr(i)
+            yield chr(i)
 
 
 def test_text_decode_iso8859():
     encoder = TextEncoder()
     encoder.set_encoding(TextEncoder.E_iso8859)
 
-    for i in xrange(255):
-        enc = unichr(i).encode('latin-1')
+    for i in range(255):
+        enc = chr(i).encode('latin-1')
         assert len(enc) == 1
 
         dec = encoder.decode_text(enc)
@@ -60,8 +56,8 @@ def test_text_encode_iso8859():
     encoder = TextEncoder()
     encoder.set_encoding(TextEncoder.E_iso8859)
 
-    for i in xrange(255):
-        c = unichr(i)
+    for i in range(255):
+        c = chr(i)
         enc = encoder.encode_wtext(c)
         assert enc == c.encode('latin-1')
 

+ 1 - 7
tests/event/test_futures.py

@@ -1,13 +1,7 @@
 from panda3d import core
 import pytest
 import time
-import sys
-
-if sys.version_info >= (3,):
-    from concurrent.futures._base import TimeoutError, CancelledError
-else:
-    TimeoutError = Exception
-    CancelledError = Exception
+from concurrent.futures._base import TimeoutError, CancelledError
 
 
 def test_future_cancelled():

+ 0 - 8
tests/gui/test_DirectEntry.py

@@ -1,6 +1,5 @@
 # coding=utf-8
 from direct.gui.DirectEntry import DirectEntry
-import sys
 
 
 def test_entry_destroy():
@@ -34,10 +33,3 @@ def test_entry_auto_capitalize():
     assert entry.get() == u'àütò çapítalízè ţèsţ'
     entry._autoCapitalize()
     assert entry.get() == u'Àütò Çapítalízè Ţèsţ'
-
-    # Also test it with a UTF-8 encoded byte string in Python 2.
-    if sys.version_info < (3, 0):
-        entry.set(u'àütò çapítalízè ţèsţ'.encode('utf-8'))
-        assert entry.get() == u'àütò çapítalízè ţèsţ'
-        entry._autoCapitalize()
-        assert entry.get() == u'Àütò Çapítalízè Ţèsţ'

+ 1 - 17
tests/interrogate/test_property.py

@@ -2,11 +2,7 @@ import sys
 import pytest
 from panda3d import core
 from contextlib import contextmanager
-
-if sys.version_info >= (3, 3):
-    import collections.abc as collections_abc
-else:
-    import _abcoll as collections_abc
+import collections.abc as collections_abc
 
 
 @contextmanager
@@ -122,11 +118,6 @@ def test_seq_property_getitem():
     assert prop[-2] == item_b
     assert prop[-3] == item_a
 
-    # Long index
-    if sys.version_info[0] < 3:
-        assert prop[long(1)] == item_b
-        assert prop[long(-1)] == item_b
-
     # Out of bounds access
     with pytest.raises(IndexError):
         prop[-4]
@@ -176,13 +167,6 @@ def test_seq_property_setitem():
     prop[-3] = item_c
     assert tuple(prop) == (item_c, item_b, item_a)
 
-    # Long index
-    if sys.version_info[0] < 3:
-        prop[long(1)] = item_b
-        assert prop[1] == item_b
-        prop[long(-1)] = item_b
-        assert prop[-1] == item_b
-
     # Out of bounds access
     with pytest.raises(IndexError):
         prop[-4] = item_c

+ 0 - 8
tests/linmath/test_lvector2.py

@@ -1,15 +1,9 @@
 from math import floor, ceil
-import sys
 
 from panda3d.core import Vec2, Vec3, Vec4, Vec2F, Vec2D
 import pytest
 
 
-reason = '''Rounding in Python 2.7 expects to return a float value, since it returns a Vector it
-does not work. When Python 2.7 gets deprecated, remove this check.'''
-
-
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_round():
     original_vector = Vec2(2.3, -2.6)
 
@@ -18,7 +12,6 @@ def test_round():
     assert rounded_vector.y == -3
 
 
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_floor():
     original_vector = Vec2(2.3, -2.6)
 
@@ -27,7 +20,6 @@ def test_floor():
     assert rounded_vector.y == -3
 
 
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_ceil():
     original_vector = Vec2(2.3, -2.6)
 

+ 0 - 7
tests/linmath/test_lvector3.py

@@ -1,14 +1,9 @@
 from math import floor, ceil
-import sys
 
 from panda3d.core import Vec2, Vec3, Vec3F, Vec3D
 import pytest
 
 
-reason = '''Rounding in Python 2.7 expects to return a float value, since it returns a Vector it
-does not work. When Python 2.7 gets deprecated, remove this check.'''
-
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_round():
     original_vector = Vec3(2.3, -2.6, 3.5)
 
@@ -18,7 +13,6 @@ def test_round():
     assert rounded_vector.z == 4
 
 
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_floor():
     original_vector = Vec3(2.3, -2.6, 3.5)
 
@@ -28,7 +22,6 @@ def test_floor():
     assert rounded_vector.z == 3
 
 
[email protected](sys.version_info < (3, 5), reason=reason)
 def test_ceil():
     original_vector = Vec3(2.3, -2.6, 3.5)
 

Some files were not shown because too many files changed in this diff