Jump to content


P4Python broken for Python 3.6 on Windows


  • Please log in to reply
2 replies to this topic

#1 wadeb

wadeb

    Newbie

  • Members
  • Pip
  • 2 posts

Posted 12 January 2017 - 04:19 PM

With Python 3.6 on Windows, running "pip install p4python" currently fails with the ominous message "Loaded API into None", followed by an obscure exception.

Attempting to load API from ftp.perforce.com
Loaded API into None
****************************************************
No openssl in path and no ssl library path specified
Building without SSL
****************************************************
Traceback (most recent call last):
File "setup.py", line 639, in <module>
do_setup(p4_api_dir, ssl)
File "setup.py", line 460, in do_setup
apiVersion = VersionInfo(p4_api_dir)
File "setup.py", line 217, in __init__
verFile = os.path.join(p4ApiDir, "sample", "Version")
File "C:\Program Files\Python36\lib\ntpath.py", line 75, in join
path = os.fspath(path)
TypeError: expected str, bytes or os.PathLike object, not NoneType

The repository at https://pypi.python.org/pypi/P4Python does not yet contain .whl files for Python 3.6, so pip will download and attempt to compile the source bundle. Unfortunately, setup.py in the source bundle p4python-2016.1.1447008.tar.gz cannot properly download the P4 API for Windows. It also fails in a confusing way when the download fails.

Here is a patch which resolves both problems, in case it helps someone else.

diff --git "a/p4python-2016.1.1447008\\setup.py" "b/p4python-2016.1.1447008-wadeb\\setup.py"
index 8e4baca..af9efc3 100644
--- "a/p4python-2016.1.1447008\\setup.py"
+++ "b/p4python-2016.1.1447008-wadeb\\setup.py"
@@ -44,6 +44,7 @@ import tempfile
from ftplib import FTP, error_perm
import itertools
import subprocess
+import zipfile

if sys.version_info < (3,0):
	 from ConfigParser import ConfigParser
@@ -134,31 +135,54 @@ class P4APIFtp:
		 pwd = self.ftp.pwd()
		 apidir = None
		 tar = None
-	
+
+	 if platform.system() == "Windows":
+		 if sys.version_info >= (3, 5):
+			 p4api = 'p4api_vs2015_static.zip'
+		 elif sys.version_info >= (3, 3):
+			 p4api = 'p4api_vs2010_static.zip'
+		 else:
+			 p4api = 'p4api_vs2008_static.zip'
+	 else:
+		 p4api = 'p4api.tgz'
+
+	 tempdir = tempfile.gettempdir()
+	 filename = os.path.join(tempdir, p4api)
+
		 try:
-		 self.ftp.cwd(d)
-		 self.ftp.cwd(self.platform)
+		 self.ftp.cwd(d)
+		 self.ftp.cwd(self.platform.lower())

-		 p4api = 'p4api.tgz'
-		 tempdir = tempfile.gettempdir()
-		 filename = os.path.join(tempdir, p4api)
-		 with open(filename, 'wb') as f:
-			 self.ftp.retrbinary('RETR ' + p4api, f.write)
+		 with open(filename, 'wb') as f:
+			 self.ftp.retrbinary('RETR ' + p4api, f.write)

-		
-		 tar = tarfile.open(filename, 'r')
-		 apidir = os.path.join(tempdir, tar.getnames()[0])
-			
-		 # if apidir exists, don't unpack again, otherwise read-only errors will occur
-		 if not ( os.path.exists(apidir) and os.path.isdir(apidir) ):
-			 tar.extractall(tempdir)
		 except error_perm as e:
			 return None
+
		 finally:
			 self.ftp.cwd(pwd)

-		 if tar:
-			 tar.close()
+	 if 'tgz' in p4api:
+		 tar = tarfile.open(filename, 'r')
+		 apidir = os.path.join(tempdir, tar.getnames()[0])
+
+		 # if apidir exists, don't unpack again, otherwise read-only errors will occur
+		 if not ( os.path.exists(apidir) and os.path.isdir(apidir) ):
+			 tar.extractall(tempdir)
+
+		 if tar:
+			 tar.close()
+
+	 else:
+		 zip = zipfile.ZipFile(filename, 'r')
+		 apidir = os.path.join(tempdir, zip.namelist()[0])
+
+		 # if apidir exists, don't unpack again, otherwise read-only errors will occur
+		 if not ( os.path.exists(apidir) and os.path.isdir(apidir) ):
+		 zip.extractall(tempdir)
+
+		 if zip:
+			 zip.close()
			
		 return apidir

@@ -531,6 +555,9 @@ def get_api_dir():
			 print( "Attempting to load API from ftp.perforce.com" )
			 p4ftp = P4APIFtp()
			 p4_api_dir = p4ftp.loadAPI()
+		 if p4_api_dir is None:
+			 print ("Error: Failed to download API from ftp.perforce.com")
+			 sys.exit(1)
			 print( "Loaded API into {0}".format(p4_api_dir) )

	 return p4_api_dir


#2 P4Sven

P4Sven

    Member

  • Staff
  • 13 posts
  • LocationWokingham, UK

Posted 07 February 2017 - 12:54 PM

Thanks for that.

I have published P4Python 2016.2 with support for Python 3.6, this will hopefully solve the problem in a more elegant way - I did not want to assume that you have the correct Visual Studio compiler installed.

Keep in mind that you can build P4Python yourself by hand or use the "trick" published in this blog article: https://www.perforce...nce-space-makes

#3 wadeb

wadeb

    Newbie

  • Members
  • Pip
  • 2 posts

Posted 07 February 2017 - 02:52 PM

Excellent, thanks! I just installed P4Python for Python 3.6 on Windows using pip, and it worked great.

I did discover the "--apidir PATH" option as well, unfortunately it was only *after* writing the above patch :)
It might be a good idea to print a note about that option when the API download fails.

Best,
Wade




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users