Repository: Bitmessage/PyBitmessage

Issue:
Minimum supported Python version
I think it is about time that the minimum supported version of Python be determined. Littered throughout [reddit](https://pay.reddit.com/r/bitmessage/), the [forums](https://bitmessage.org/forum/) and this [issue tracker](https://github.com/Bitmessage/PyBitmessage/issues) are posts from users experiencing difficulties running PyBitmessage. Most of these boil down to using older versions of Python. Since PyBitmessage has for a while now mandated Python 2.7.5 on OS X and just recently extended this to Windows (there doesn't seem to be any documentation for the rationale behind these constraints) it shouldn't be too hard to require that on all platforms.



Discussion:
the wiki (1) says:
"If you receive a warning that you need to use python 2.7.5 or greater ..."

(1) https://bitmessage.org/wiki/Compiling_instructions

Exactly my point. Linux users are not being told they are not using Python 2.7.5 or greater and PyBitmessage is failing in ways that drives most of them away. As I mentioned previously I can't find any information on why OS X and Windows are restricted to Python 2.7.5+ while Linux isn't. I find it hard to believe it is an oversight that the Python version isn't checked for Linux since it requires less code to check all platforms than specific platforms.

Python 2.7.3 became insufficient for Windows users just recently as issue #687 (merged) made use of memoryview- an issue I discovered during testing. Is a higher version of Python now required for Linux for the same reason? If so, we can have the version check apply to all operating systems. 

I don't know if [this issue](https://bitmessage.org/forum/index.php?topic=4081.0) is what you encountered, but it does affect all platforms.

I don't have a problem if you decide backwards compatibility to 2.5 is better (but that would mean a whole rewrite); I'll just try to write compatible code (I can't say the same for other developers though; they may prefer new syntax and features for convenience). But it needs to be specified to users and developers. I'm not the only developer to assume that Python 2.7.5 is the minimum version since it is the only version ever mentioned in the code or documentation.


Resolving patch:
diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py
--- a/src/bitmessagemain.py
+++ b/src/bitmessagemain.py
@@ -9,6 +9,26 @@
 
 # The software version variable is now held in shared.py
 
+
+import sys
+#Version check
+#Older versions of Python don't support the print function while Python 3 doesn't
+#like the print statement, so we use sys.stdout for the version check. After this
+#check we can then use the print function in the remainder of this file. Currently
+#in order to use logging, a lot of unnecessary code needs to be executed which could
+#potentially render this version check useless. So logging won't be used here until
+#there is a more efficient way to configure logging
+if sys.hexversion >= 0x3000000:
+    msg = "PyBitmessage does not support Python 3. Python 2.7.5 or later is required. Your version: %s" % sys.version
+    #logger.critical(msg)
+    sys.stdout.write(msg)
+    sys.exit(0)
+if sys.hexversion < 0x20705F0:
+    msg = "You should use Python 2.7.5 or greater (but not Python 3). Your version: %s" % sys.version
+    #logger.critical(msg)
+    sys.stdout.write(msg)
+    sys.exit(0)
+
 import signal  # Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
 # The next 3 are used for the API
 import singleton
@@ -45,15 +65,7 @@
 
 from subprocess import call
 import time
-
-# OSX python version check
-import sys
-if 'win' in sys.platform:
-    if float("{1}.{2}".format(*sys.version_info)) < 7.5:
-        msg = "You should use python 2.7.5 or greater. Your version: %s", "{0}.{1}.{2}".format(*sys.version_info)
-        logger.critical(msg)
-        print msg
-        sys.exit(0)
+    
 
 def connectToStream(streamNumber):
     shared.streamsInWhichIAmParticipating[streamNumber] = 'no data'
@@ -199,7 +211,7 @@ def start(self, daemon=False):
                 apiNotifyPath = ''
             if apiNotifyPath != '':
                 with shared.printLock:
-                    print 'Trying to call', apiNotifyPath
+                    print('Trying to call', apiNotifyPath)
 
                 call([apiNotifyPath, "startingUp"])
             singleAPIThread = singleAPI()
@@ -218,15 +230,15 @@ def start(self, daemon=False):
                 try:
                     from PyQt4 import QtCore, QtGui
                 except Exception as err:
-                    print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download   or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon'
-                    print 'Error message:', err
-                    print 'You can also run PyBitmessage with the new curses interface by providing \'-c\' as a commandline argument.'
+                    print('PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download   or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon')
+                    print('Error message:', err)
+                    print('You can also run PyBitmessage with the new curses interface by providing \'-c\' as a commandline argument.')
                     os._exit(0)
 
                 import bitmessageqt
                 bitmessageqt.run()
             else:
-                print 'Running with curses'
+                print('Running with curses')
                 import bitmessagecurses
                 bitmessagecurses.runwrapper()
         else:
@@ -234,16 +246,16 @@ def start(self, daemon=False):
 
             if daemon:
                 with shared.printLock:
-                    print 'Running as a daemon. The main program should exit this thread.'
+                    print('Running as a daemon. The main program should exit this thread.')
             else:
                 with shared.printLock:
-                    print 'Running as a daemon. You can use Ctrl+C to exit.'
+                    print('Running as a daemon. You can use Ctrl+C to exit.')
                 while True:
                     time.sleep(20)
 
     def stop(self):
         with shared.printLock:
-            print 'Stopping Bitmessage Deamon.'
+            print('Stopping Bitmessage Deamon.')
         shared.doCleanShutdown()
 
 


Test patch: