Use Python 3
authorPascal Merz <pascal.merz@colorado.edu>
Thu, 16 Aug 2018 00:29:38 +0000 (18:29 -0600)
committerPascal Merz <pascal.merz@colorado.edu>
Thu, 16 May 2019 02:39:32 +0000 (20:39 -0600)
This updates the Python scripts shipped with GROMACS to use Python 3.5+.
Most changes done are either due to `__cmp__` not being available in py3
(replaced by `functools.total_ordering`), py2-iterators being replaced by
py3-iterators, or print statements being replaced by functions.
Additionaly, some changes in the Cmake files were made to select the
different Python version.

All python scripts having an explicit `#!/usr/bin/env python2` as a first
line were checked and updated. Larger changes were only needed for the
documentation scripts, other scripts did not require any changes. Scripts
not explicitly using shebangs were also checked, but no changes were
needed. The following scripts were not touched:
- all scripts under tests/physicalvalidation/ (already py3 compatible)
- all scripts under python_packaging/
- scripts/make_gromos_rtp.py - this file is not valid py2.7

Refs #2615

Change-Id: I2edbe213bc6401563934ce56f5fd6a39886d3095

14 files changed:
admin/copyright.py
cmake/FindPythonModule.cmake
cmake/FindSphinx.cmake
docs/CMakeLists.txt
docs/conf.py
docs/doxygen/CMakeLists.txt
docs/doxygen/check-source.py
docs/doxygen/doxygenxml.py
docs/doxygen/gmxtree.py
docs/doxygen/graphbuilder.py
docs/doxygen/includesorter.py
docs/doxygen/reporter.py
src/gromacs/nbnxm/kernel_file_generator/make_verlet_simd_kernel_files.py
src/gromacs/selection/tests/gensphere.py

index 7e38ed8ab122de90634d4ca555a35b0e1bbc5e72..0b349861cca53091cf8f7b41a3d3f7fe84e39832 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013,2014,2015,2016,2018, by the GROMACS development team, led by
+# Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
index 1b2dd8a51c640ca1fb74746f5fa83127263251e6..4d000a7e784d4f8c4cddda59344555836f848641 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015, by the GROMACS development team, led by
+# Copyright (c) 2015,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -34,7 +34,7 @@
 
 # Adapted from code posted on cmake-users by Mark Moll (the execute_process()
 # call remains, but other things have been rewritten for nicer behavior).
-find_package(PythonInterp 2.7)
+find_package(PythonInterp 3.5)
 
 function (find_python_module module)
     string(TOUPPER ${module} _module_upper)
@@ -60,7 +60,7 @@ function (find_python_module module)
             # A module's location is usually a directory, but for binary modules
             # it's a .so file.
             execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
-                "import re, ${module}; print re.compile('/__init__.py.*').sub('',${module}.__file__)"
+                "import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))"
                 RESULT_VARIABLE _status
                 OUTPUT_VARIABLE _location
                 ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
index c4df707f6f748af0ab70da8f2591bb4b54fd939d..5d078495dcf9d8a66f72f8bc967b3c2c98f1a7af 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2018, by the GROMACS development team, led by
+# Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -69,9 +69,11 @@ set(_find_deps_options)
 if (Sphinx_FIND_QUIETLY)
     set(_find_deps_options QUIET)
 endif()
-include(FindPythonModule)
-find_python_module(pygments ${_find_deps_options})
-if (PYTHONMODULE_PYGMENTS)
+execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+                "import pygments"
+                RESULT_VARIABLE _pygments_status
+                ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
+if (_pygments_status)
     set(Sphinx_pygments_FOUND 1)
 endif()
 
index 331e2bacc3a70fdf3f00a1da51039b174b335fe8..348185dcc4927fd967a072ea4beee403ad17bde4 100644 (file)
@@ -64,14 +64,8 @@ if (DEFINED PYTHON_EXECUTABLE)
     # Keep quiet on subsequent runs of cmake
     set(PythonInterp_FIND_QUIETLY ON)
 endif()
-find_package(PythonInterp 2.7)
-
-
-if (NOT ${PYTHON_VERSION_MAJOR} EQUAL 3)
-    find_package(Sphinx ${EXPECTED_SPHINX_VERSION} QUIET COMPONENTS pygments)
-else()
-    MESSAGE(STATUS "Can not build documentation with Python 3")
-endif()
+find_package(PythonInterp 3.5)
+find_package(Sphinx ${EXPECTED_SPHINX_VERSION} QUIET COMPONENTS pygments)
 
 # Even if we aren't going to make the full webpage, set up to put all
 # the documentation output in the same place, for convenience
@@ -113,10 +107,7 @@ elseif (BUILD_IS_INSOURCE)
     set(MANUAL_BUILD_NOT_POSSIBLE_REASON "the build is in-source")
 else()
     include(manual/UseLATEX.cmake)
-    if(${PYTHON_VERSION_MAJOR} EQUAL 3)
-        set(MANUAL_BUILD_IS_POSSIBLE OFF)
-        set(MANUAL_BUILD_NOT_POSSIBLE_REASON "We can not build the documentation when using python3")
-    elseif(NOT SPHINX_FOUND)
+    if(NOT SPHINX_FOUND)
         set(MANUAL_BUILD_IS_POSSIBLE OFF)
         set(MANUAL_BUILD_NOT_POSSIBLE_REASON "Sphinx has not been found and is needed to create the LaTex input files")
     elseif(NOT PDFLATEX_COMPILER)
@@ -570,7 +561,7 @@ if (SPHINX_FOUND)
         DEPENDS sphinx-programs
         DEPENDS sphinx-input
         DEPENDS sphinx-image-conversion
-        DEPENDS manual 
+        DEPENDS manual
         COMMAND
             ${CMAKE_COMMAND} -E make_directory ${SPHINX_INPUT_DIR}/_static
         COMMAND
index 039f68848056c51985fc5968d887d72505c0cc18..6fe0b3a0b3ebd0c01e6b89eca26175e025eea596 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -50,7 +50,7 @@ import datetime
 import os
 import sys
 
-execfile('conf-vars.py')
+exec(open('conf-vars.py').read())
 sys.path.append(gmx_sphinx_extension_path)
 if releng_path and os.path.isdir(releng_path):
     sys.path.append(releng_path)
@@ -346,7 +346,7 @@ latex_show_pagerefs = True
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 if tags.has('do_man'):
-    execfile('conf-man.py')
+    exec(open('conf-man.py').read())
 
 # If true, show URL addresses after external links.
 #man_show_urls = False
index 1f47a57731b267f0e968f40e0c10b700aa87c668..64a5cf4747e7fe14d9b67efb2fa5145f5697e79d 100644 (file)
@@ -57,7 +57,7 @@ gmx_dependent_option(
 mark_as_advanced(GMX_COMPACT_DOXYGEN)
 
 set(USE_PYTHON_SCRIPTS OFF)
-if (PYTHONINTERP_FOUND AND NOT PYTHON_VERSION_STRING VERSION_LESS "2.6" AND NOT ${PYTHON_VERSION_MAJOR} EQUAL 3)
+if (PYTHONINTERP_FOUND)
     set(USE_PYTHON_SCRIPTS ON)
 endif()
 
index a82a2630181bd4d465d9f0d0d0529d1f6e0d17cf..631b027e3eb0469156415647a42d877bfafd9bbf 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -316,8 +316,8 @@ def check_cycles(graph, reporter):
                     # Only report cycles to nodes that haven't been processed
                     # yet to avoid duplicates.
                     elif linkorder[nextnode] == preorder[nextnode]:
-                        for index in xrange(len(currlist)):
-                            if currlist[index][0] == nextnode:
+                        for index, node in enumerate(currlist):
+                            if node[0] == nextnode:
                                 cycle = [(nextnode, edge)]
                                 cycle.extend(currlist[index+1:])
                                 graph.report_cycle(cycle, reporter)
index 3faf21afec25535f7d4824b4e69d7a6c1fedc6d5..067d84af326e4b04c3edc8a6e0ecae410444d86d 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2015,2016,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -72,6 +72,7 @@ actually in the XML documentation.
 
 import os.path
 import xml.etree.ElementTree as ET
+import functools
 
 import reporter
 
@@ -81,10 +82,11 @@ import reporter
 def _show_list(title, objlist):
     """Helper function for formatting a list of objects for debug output."""
     if objlist:
-        print '{0}:'.format(title)
+        print('{0}:'.format(title))
         for obj in objlist:
-            print '  ', obj
+            print('  ', obj)
 
+@functools.total_ordering
 class DocType(object):
 
     """Documentation visibility in the generated documentation."""
@@ -104,9 +106,13 @@ class DocType(object):
         """Return string representation for the documentation type."""
         return self._names[self._value]
 
-    def __cmp__(self, other):
+    def __eq__(self, other):
         """Order documentation types in the order of visibility."""
-        return cmp(self._value, other._value)
+        return self._value == other._value
+
+    def __lt__(self, other):
+        """Order documentation types in the order of visibility."""
+        return self._value < other._value
 
 # Static values for documentation types.
 DocType.none = DocType(0)
@@ -138,6 +144,7 @@ class Location(object):
     def get_full_string(self):
         return '{0}:{1}:{2}'.format(self.filepath, self.line, self.column)
 
+@functools.total_ordering
 class BodyLocation(object):
 
     """Body location of a Doxygen entity.
@@ -157,13 +164,22 @@ class BodyLocation(object):
         self.startline = int(elem.attrib['bodystart'])
         self.endline = int(elem.attrib['bodyend'])
 
-    def __cmp__(self, other):
-        result = cmp(self.filepath, other.filepath)
-        if result == 0:
-            result = cmp(self.startline, other.startline)
-        if result == 0:
-            result = cmp(self.endline, other.endline)
-        return result
+    def __eq__(self, other):
+        return (self.filepath == other.filepath and
+                self.startline == other.startline and
+                self.endline == other.endline)
+
+    def __lt__(self, other):
+        if self.filepath != other.filepath:
+            if other.filepath is None:
+                return False
+            if self.filepath is None:
+                return True
+            return self.filepath < other.filepath
+        elif self.startline != other.startline:
+            return self.startline < other.startline
+        else:  # check for endline
+            return self.endline < other.endline
 
     def __hash__(self):
         return hash(self.filepath) ^ hash(self.startline) ^ hash(self.endline)
@@ -333,9 +349,9 @@ class Entity(object):
         This is called from subclass show() methods to show base information
         about the entity.
         """
-        print 'ID:         {0}'.format(self._id)
-        print 'Name:       {0}'.format(self._name)
-        print 'Location:   {0}'.format(self.get_reporter_location())
+        print('ID:         {0}'.format(self._id))
+        print('Name:       {0}'.format(self._name))
+        print('Location:   {0}'.format(self.get_reporter_location()))
         doctype = []
         if self._has_brief_description:
             doctype.append('brief')
@@ -345,8 +361,8 @@ class Entity(object):
             doctype.append('in-body')
         if not doctype:
             doctype.append('none')
-        print 'Doc:        {0}'.format(', '.join(doctype))
-        print 'Visibility: {0}'.format(self._visibility)
+        print('Doc:        {0}'.format(', '.join(doctype)))
+        print('Visibility: {0}'.format(self._visibility))
 
 # Member entities
 
@@ -505,10 +521,10 @@ class Member(Entity):
         self.show_base()
         if self._alternates:
             idlist = [x.get_id() for x in self._alternates]
-            print 'Alt. IDs:   {0}'.format(', '.join(idlist))
-        print 'Parent vis: {0}'.format(self.get_inherited_visibility())
-        print 'Location:   {0}'.format(self.get_location().get_full_string())
-        print 'Body loc:   {0}'.format(self.get_body_location().get_full_string())
+            print('Alt. IDs:   {0}'.format(', '.join(idlist)))
+        print('Parent vis: {0}'.format(self.get_inherited_visibility()))
+        print('Location:   {0}'.format(self.get_location().get_full_string()))
+        print('Body loc:   {0}'.format(self.get_body_location().get_full_string()))
         _show_list('Parents', self._parents)
 
 class Define(Member):
@@ -756,7 +772,7 @@ class Compound(Entity):
         """
         Entity.show_base(self)
         if self._groups:
-            print 'Groups:   {0}'.format(', '.join(map(str, self._groups)))
+            print('Groups:   {0}'.format(', '.join(map(str, self._groups))))
 
     def show_members(self):
         """Show list of members.
@@ -765,9 +781,9 @@ class Compound(Entity):
         to print the list of members.
         """
         for section in self._sections:
-            print 'Member section: {0}'.format(section)
+            print('Member section: {0}'.format(section))
             for member in section._members:
-                print '  ', member
+                print('  ', member)
 
 class File(Compound):
     def __init__(self, name, refid):
@@ -811,9 +827,9 @@ class File(Compound):
 
     def show(self):
         self.show_base()
-        print 'Path:      {0}'.format(self._path)
-        print 'Directory: {0}'.format(self._directory)
-        print 'Source:    {0}'.format(self._is_source_file)
+        print('Path:      {0}'.format(self._path))
+        print('Directory: {0}'.format(self._directory))
+        print('Source:    {0}'.format(self._is_source_file))
         _show_list('Namespaces', self._namespaces)
         _show_list('Classes', self._classes)
         self.show_members()
@@ -854,9 +870,9 @@ class Directory(Compound):
 
     def show(self):
         self.show_base()
-        print 'Path:      {0}'.format(self._path)
+        print('Path:      {0}'.format(self._path))
         if self._parent:
-            print 'Parent:    {0}'.format(self._parent)
+            print('Parent:    {0}'.format(self._parent))
         _show_list('Subdirectories', self._subdirs)
         _show_list('Files', self._files)
 
@@ -896,10 +912,10 @@ class Group(Compound):
 
     def show(self):
         self.show_base()
-        print 'Title:     {0}'.format(self._title)
-        print 'Inner compounds:'
+        print('Title:     {0}'.format(self._title))
+        print('Inner compounds:')
         for compound in self._children:
-            print '  ', compound
+            print('  ', compound)
         self.show_members()
 
 class Namespace(Compound):
@@ -936,7 +952,7 @@ class Namespace(Compound):
 
     def show(self):
         self.show_base()
-        print 'Doc. loc.: {0}'.format(self._doclocation.get_full_string())
+        print('Doc. loc.: {0}'.format(self._doclocation.get_full_string()))
         _show_list('Inner namespaces', self._innernamespaces)
         _show_list('Classes', self._classes)
         self.show_members()
@@ -1001,12 +1017,12 @@ class Class(Compound):
 
     def show(self):
         self.show_base()
-        print 'Namespace:  {0}'.format(self._namespace)
+        print('Namespace:  {0}'.format(self._namespace))
         if self._outerclass:
-            print 'Outer cls:  {0}'.format(self._outerclass)
+            print('Outer cls:  {0}'.format(self._outerclass))
         location = self._location
-        print 'Location:   {0}'.format(location.get_location().get_full_string())
-        print 'Body loc:   {0}'.format(location.get_body_location().get_full_string())
+        print('Location:   {0}'.format(location.get_location().get_full_string()))
+        print('Body loc:   {0}'.format(location.get_body_location().get_full_string()))
         _show_list('Inner classes', self._innerclasses)
         self.show_members()
 
@@ -1123,7 +1139,7 @@ class DocumentationSet(object):
         If filelist is set, it should be a list of file paths, and details will
         be loaded only for files in those paths.  The paths should be relative
         to the root of the Gromacs source tree."""
-        for compound in self._compounds.itervalues():
+        for compound in self._compounds.values():
             if isinstance(compound, (Directory, Group)):
                 compound.load_details()
             elif not filelist and isinstance(compound, File):
@@ -1134,7 +1150,7 @@ class DocumentationSet(object):
             # details are loaded, because Doxygen does not write that into
             # index.xml.  But we can use the Directory objects (where the name
             # is the relative path) to get the path.
-            for compound in self._compounds.itervalues():
+            for compound in self._compounds.values():
                 if isinstance(compound, File):
                     dirobj = compound.get_directory()
                     if not dirobj:
@@ -1147,7 +1163,7 @@ class DocumentationSet(object):
 
     def load_details(self):
         """Load detailed XML files for each compound."""
-        for compound in self._compounds.itervalues():
+        for compound in self._compounds.values():
             compound.load_details()
             if isinstance(compound, File):
                 self._files[compound.get_path()] = compound
@@ -1166,14 +1182,14 @@ class DocumentationSet(object):
         method.
         """
         members_by_body = dict()
-        for member in self._members.itervalues():
+        for member in self._members.values():
             bodyloc = member.get_body_location()
             if bodyloc:
                 index = (bodyloc, type(member), member.get_name())
                 if index not in members_by_body:
                     members_by_body[index] = []
                 members_by_body[index].append(member)
-        for memberlist in members_by_body.itervalues():
+        for memberlist in members_by_body.values():
             if len(memberlist) > 1:
                 declaration = None
                 otherdeclarations = []
@@ -1226,7 +1242,7 @@ class DocumentationSet(object):
 
     def get_compounds(self, types, predicate=None):
         result = []
-        for compound in self._compounds.itervalues():
+        for compound in self._compounds.values():
             if isinstance(compound, types) and \
                     (predicate is None or predicate(compound)):
                 result.append(compound)
@@ -1235,7 +1251,7 @@ class DocumentationSet(object):
     def get_members(self, types=None, predicate=None):
         # self._members can contain duplicates because of merge_duplicates()
         result = set()
-        for member in self._members.itervalues():
+        for member in self._members.values():
             if (types is None or isinstance(member, types)) and \
                     (predicate is None or predicate(member)):
                 result.add(member)
index 488dcfa6564065e633a4aa5f50f13efcb5d10f92..c6202ad8f4b7872b3559d1ac7551a007b354f45a 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
@@ -93,6 +93,9 @@ class IncludedFile(object):
         else:
             return '"{0}"'.format(self._included_path)
 
+    def __lt__(self, other):
+        return str(self) < str(other)
+
     def is_system(self):
         return self._is_system
 
@@ -361,7 +364,7 @@ class File(object):
 
         The return value is empty if find_define_file_uses() has not been called,
         as well as for headers that declare these defines."""
-        return set(self._used_defines.iterkeys())
+        return set(self._used_defines.keys())
 
     def get_used_defines(self, define_file):
         """Return set of defines used in this file for a given file like config.h.
@@ -585,7 +588,7 @@ class Module(object):
         return self._group
 
     def get_dependencies(self):
-        return self._dependencies.itervalues()
+        return self._dependencies.values()
 
 class Namespace(object):
 
@@ -814,7 +817,7 @@ class GromacsTree(object):
         if only_files:
             filelist = only_files
         else:
-            filelist = self._files.itervalues()
+            filelist = self._files.values()
         define_files = list(self.get_checked_define_files())
         for define_file in list(define_files):
             if isinstance(define_file, GeneratedFile) and \
@@ -969,8 +972,8 @@ class GromacsTree(object):
         args = ['git', 'check-attr', '--stdin', 'filter']
         git_check_attr = subprocess.Popen(args, stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE, cwd=self._source_root)
-        filelist = '\n'.join(map(File.get_relpath, self._files.itervalues()))
-        filters = git_check_attr.communicate(filelist)[0]
+        filelist = '\n'.join(map(File.get_relpath, self._files.values()))
+        filters = git_check_attr.communicate(filelist.encode())[0].decode()
         for fileinfo in filters.splitlines():
             path, dummy, value = fileinfo.split(': ')
             fileobj = self._files.get(path)
@@ -990,7 +993,7 @@ class GromacsTree(object):
                 args.extend(['-e', define])
             args.extend(['--', '*.cpp', '*.c', '*.cu', '*.h', '*.cuh'])
             define_re = r'\b(?:' + '|'.join(all_defines)+ r')\b'
-            output = subprocess.check_output(args, cwd=self._source_root)
+            output = subprocess.check_output(args, cwd=self._source_root).decode()
             for line in output.splitlines():
                 (filename, text) = line.split('\0')
                 fileobj = self._files.get(filename)
@@ -1060,11 +1063,11 @@ class GromacsTree(object):
 
     def get_files(self):
         """Get iterable for all files in the source tree."""
-        return self._files.itervalues()
+        return self._files.values()
 
     def get_modules(self):
         """Get iterable for all modules in the source tree."""
-        return self._modules.itervalues()
+        return self._modules.values()
 
     def get_classes(self):
         """Get iterable for all classes in the source tree."""
index 3fef8a81c111b2b9901d9f8117892aff98e5b354..5eead51b344fb07e6ed636bc7afc92a3e3bca277 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -58,9 +58,11 @@ The produced graphs are documented in doxygen.md.
 
 import os.path
 import re
+import functools
 
 from gmxtree import DocType
 
+@functools.total_ordering
 class EdgeType(object):
 
     """Enumeration type for edge types in include dependency graphs."""
@@ -81,9 +83,13 @@ class EdgeType(object):
         """Return string representation for the edge type (for debugging)."""
         return self._names[self._value]
 
-    def __cmp__(self, other):
+    def __eq__(self, other):
         """Order edge types in the order of increasing coupling."""
-        return cmp(self._value, other._value)
+        return self._value == other._value
+
+    def __lt__(self, other):
+        """Order edge types in the order of increasing coupling."""
+        return self._value < other._value
 
 # Tests depend on test
 EdgeType.test = EdgeType(0)
@@ -402,7 +408,7 @@ class GraphBuilder(object):
         are in the list of nodes.
         """
         edges = []
-        for fileobj in filenodes.iterkeys():
+        for fileobj in filenodes.keys():
             for includedfile in fileobj.get_includes():
                 otherfile = includedfile.get_file()
                 if otherfile and otherfile in filenodes:
@@ -453,7 +459,7 @@ class GraphBuilder(object):
         dependency are in the list of nodes.
         """
         edges = []
-        for moduleobj in modulenodes.iterkeys():
+        for moduleobj in modulenodes.keys():
             for dep in moduleobj.get_dependencies():
                 othermodule = dep.get_other_module()
                 if othermodule and othermodule in modulenodes:
index 4bc26636c5b2c63438249a89980234012ac094be..01e95a05fddfbcee20a8f3a0b3348ea11ec76cf4 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -53,7 +53,9 @@ checked by the check-source.py script.
 import os.path
 import re
 import sys
+import functools
 
+@functools.total_ordering
 class IncludeGroup(object):
 
     """Enumeration type for grouping includes."""
@@ -66,9 +68,13 @@ class IncludeGroup(object):
         """
         self._value = value
 
-    def __cmp__(self, other):
+    def __eq__(self, other):
         """Order include groups in the desired order."""
-        return cmp(self._value, other._value)
+        return self._value == other._value
+
+    def __lt__(self, other):
+        """Order include groups in the desired order."""
+        return self._value < other._value
 
 # gmxpre.h is always first
 IncludeGroup.pre = IncludeGroup(0)
@@ -277,8 +283,7 @@ class IncludeSorter(object):
         Returns a new list of lines for the block.
         If anything is changed, self._changed is set to True, and the caller
         can check that."""
-        includes = map(self._sortmethod.get_sortable_object, block.get_includes())
-        includes.sort()
+        includes = sorted(map(self._sortmethod.get_sortable_object, block.get_includes()))
         result = []
         prev = None
         current_line_number = block.get_first_line()-1
index 40f70c648a6bf0df66f444a36219372bbf3abfe5..0d06fea4b8539a38a376c0d9d676209e627373bd 100644 (file)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2014,2018, by the GROMACS development team, led by
+# Copyright (c) 2014,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -34,6 +34,7 @@
 # the research papers on the package. Check out http://www.gromacs.org.
 
 import sys
+import functools
 
 from fnmatch import fnmatch
 
@@ -48,6 +49,7 @@ other in the output, as well as filtering to make it possible to suppress
 certain messages.
 """
 
+@functools.total_ordering
 class Location(object):
 
     """Location for a reported message."""
@@ -61,7 +63,7 @@ class Location(object):
         self.filename = filename
         self.line = line
 
-    def __nonzero__(self):
+    def __bool__(self):
         """Make empty locations False in boolean context."""
         return self.filename is not None
 
@@ -74,13 +76,28 @@ class Location(object):
         else:
             return '<unknown>'
 
-    def __cmp__(self, other):
+    def __eq__(self, other):
         """Sort locations based on file name and line number."""
-        result = cmp(self.filename, other.filename)
-        if not self.filename or result != 0:
-            return result
-        return cmp(self.line, other.line)
+        return self.filename == other.filename and self.line == other.line
 
+    def __lt__(self, other):
+        """Sort locations based on file name and line number."""
+        if self.filename != other.filename:
+            if other.filename is None:
+                return False
+            if self.filename is None:
+                return True
+            return self.filename < other.filename
+        else:
+            if not self.filename:
+                return False
+            if other.line is None:
+                return False
+            if self.line is None:
+                return True
+            return self.line < other.line
+
+@functools.total_ordering
 class Message(object):
 
     """Single reported message.
@@ -109,9 +126,13 @@ class Message(object):
         self.message = message
         self.details = details
 
-    def __cmp__(self, other):
+    def __eq__(self, other):
+        """Sort messages based on file name and line number."""
+        return self.location == other.location
+
+    def __lt__(self, other):
         """Sort messages based on file name and line number."""
-        return cmp(self.location, other.location)
+        return self.location < other.location
 
 class Filter(object):
 
index d57aa11b756e709ee4e1bbf25823fc87ef79db47..1a1ffab7956132d9253e6acd7d05f5336f2889cd 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
index cf7d4f5e862501d3f614576a6df696f4e645f5a0..9dd5a51c3e84420618af3ad77ee02ef5731a36d6 100755 (executable)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2012,2018, by the GROMACS development team, led by
+# Copyright (c) 2012,2018,2019, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.