Skip to content

Commit 8296a0a

Browse files
committed
Removing support for Python 2. Fix for retrieving job results.
1 parent 33e4725 commit 8296a0a

File tree

4 files changed

+46
-75
lines changed

4 files changed

+46
-75
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,25 @@
44

55
# GenePattern Python Library
66

7-
This is a Python library for working with GenePattern programmatically. Calls from this library execute the GenePattern REST API.
7+
This is a Python library for working with GenePattern programmatically. Behind the scenes, calls from this library execute the GenePattern REST API.
88

9-
## Supported Versions
9+
## Supported Python Versions
1010

11-
This library supports Python 2.7 and Python 3.4+. The bundled data submodule `gp.data` requires [pandas](http://pandas.pydata.org/) and Python 3, although the rest of the module does not.
11+
This library requires Python 3.4+. The bundled data submodule `gp.data` also requires [pandas](http://pandas.pydata.org/), although the rest of the module does not.
12+
13+
**Python 2 Support:** Support for Python 2 was removed in version 1.4.0. Python 2 users should use version 1.3.1.
1214

1315
## Installing
1416

1517
It is recommended to install this library from PIP. Simply execute the command below:
1618

17-
> sudo pip install genepattern-python
19+
> pip install genepattern-python
1820
1921
## Upgrading
2022

2123
To upgrade to the latest version of the library, execute the command below:
2224

23-
> sudo pip install genepattern-python --upgrade
25+
> pip install genepattern-python --upgrade
2426
2527
## Tutorial
2628

gp/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
__authors__ = ['Thorin Tabor', 'Chet Birger']
22
__copyright__ = 'Copyright 2014-2017, Regents of the University of California & Broad Institute'
3-
__version__ = '1.3.2'
3+
__version__ = '1.4.0'
44
__status__ = 'Production'
55

66
"""
77
GenePattern Python Client
88
9-
Compatible with Python 2.7 and Python 3.4+
9+
Compatible with Python 3.4+
1010
"""
1111

1212
from .core import GPException, GPFile, GPJob, GPJobSpec, GPResource, GPServer, GPTask, GPTaskParam

gp/core.py

Lines changed: 35 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,8 @@
44
import json
55
import time
66
from contextlib import closing
7-
8-
# Imports requiring compatibility between Python 2 and Python 3
9-
if sys.version_info.major == 2:
10-
import urllib2
11-
import urllib as parse
12-
elif sys.version_info.major == 3:
13-
from urllib import request as urllib2
14-
import urllib.parse as parse
7+
import urllib.request
8+
import urllib.parse
159

1610

1711
class GPServer(object):
@@ -26,28 +20,8 @@ def __init__(self, url, username, password):
2620
self.url = url
2721
self.username = username
2822
self.password = password
29-
self.auth_header = None
3023
self.last_job = None
3124

32-
# Handle Basic Auth differences in Python 2 vs. Python 3
33-
if sys.version_info.major == 2:
34-
auth_string = base64.encodestring('%s:%s' % (self.username, self.password))[:-1]
35-
self.auth_header = "Basic %s" % auth_string
36-
elif sys.version_info.major == 3:
37-
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
38-
password_mgr.add_password(None, url, username, password)
39-
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
40-
opener = urllib2.build_opener(handler)
41-
urllib2.install_opener(opener)
42-
43-
# auth_string = '%s:%s' % (self.username, self.password)
44-
# if sys.version_info.major == 3: # Required for Python 3
45-
# auth_string = bytes(auth_string, 'ascii')
46-
# auth_string = base64.encodestring(auth_string)[:-1]
47-
# self.auth_header = "Basic %s" % auth_string
48-
else:
49-
raise GPException('Version of Python not supported')
50-
5125
def __str__(self):
5226
return self.url + ' ' + self.username + ' ' + self.password
5327

@@ -57,7 +31,7 @@ def authorization_header(self):
5731
with GenePattern. This string is included in the header of subsequent
5832
requests sent to GenePattern.
5933
"""
60-
return self.auth_header
34+
return 'Basic %s' % base64.b64encode(bytes(self.username + ':' + self.password, 'ascii')).decode('ascii')
6135

6236
def upload_file(self, file_name, file_path):
6337
"""
@@ -67,23 +41,21 @@ def upload_file(self, file_name, file_path):
6741
will be named filename.
6842
6943
Args:
70-
filename: The name that the uploaded file will be called on the server.
71-
filepath: The path of the local file to upload.
72-
server_data: GPServer object used to make the server call.
44+
:param file_name: The name that the uploaded file will be called on the server.
45+
:param file_path: The path of the local file to upload.
7346
7447
Returns:
75-
A GPFile object that wraps the URI of the uploaded file, or None if the
76-
upload fails.
48+
:return: A GPFile object that wraps the URI of the uploaded file, or None if the upload fails.
7749
"""
7850

79-
request = urllib2.Request(self.url + '/rest/v1/data/upload/job_input?name=' + file_name)
51+
request = urllib.request.Request(self.url + '/rest/v1/data/upload/job_input?name=' + file_name)
8052
if self.authorization_header() is not None:
8153
request.add_header('Authorization', self.authorization_header())
8254
request.add_header('User-Agent', 'GenePatternRest')
8355
data = open(file_path, 'rb').read()
8456

8557
try:
86-
response = urllib2.urlopen(request, data)
58+
response = urllib.request.urlopen(request, data)
8759
except IOError:
8860
print("authentication failed")
8961
return None
@@ -103,11 +75,9 @@ def run_job(self, job_spec, wait_until_done=True):
10375
polling the server, but can also run asynchronously.
10476
10577
Args:
106-
jobspec: A GPJobSpec object that contains the data defining the job to be
107-
run.
108-
server_data: GPServer object used to make the server call.
109-
waitUntilDone: Whether to wait until the job is finished before
110-
returning.
78+
:param job_spec: A GPJobSpec object that contains the data defining the job to be run.
79+
:param wait_until_done: Whether to wait until the job is finished before returning.
80+
:return:
11181
11282
Returns:
11383
a GPJob object that refers to the running job on the server. If called
@@ -120,12 +90,12 @@ def run_job(self, job_spec, wait_until_done=True):
12090
json_string = json.dumps({'lsid': job_spec.lsid, 'params': job_spec.params, 'tags': ['GenePattern Python Client']})
12191
if sys.version_info.major == 3: # Handle conversion to bytes for Python 3
12292
json_string = bytes(json_string, 'utf-8')
123-
request = urllib2.Request(self.url + '/rest/v1/jobs')
93+
request = urllib.request.Request(self.url + '/rest/v1/jobs')
12494
if self.authorization_header() is not None:
12595
request.add_header('Authorization', self.authorization_header())
12696
request.add_header('Content-Type', 'application/json')
12797
request.add_header('User-Agent', 'GenePatternRest')
128-
response = urllib2.urlopen(request, json_string)
98+
response = urllib.request.urlopen(request, json_string)
12999
if response.getcode() != 201:
130100
print(" job POST failed, status code = %i" % response.getcode())
131101
return None
@@ -143,11 +113,11 @@ def get_task_list(self):
143113
each representing one of the modules installed on the server. Useful
144114
for determining which are available on the server.
145115
"""
146-
request = urllib2.Request(self.url + '/rest/v1/tasks/all.json')
116+
request = urllib.request.Request(self.url + '/rest/v1/tasks/all.json')
147117
if self.authorization_header() is not None:
148118
request.add_header('Authorization', self.authorization_header())
149119
request.add_header('User-Agent', 'GenePatternRest')
150-
response = urllib2.urlopen(request)
120+
response = urllib.request.urlopen(request)
151121
response_string = response.read().decode('utf-8')
152122
category_and_tasks = json.loads(response_string)
153123
raw_list = category_and_tasks['all_modules']
@@ -157,7 +127,8 @@ def get_task_list(self):
157127
task_list.append(task)
158128
return task_list
159129

160-
def wait_until_complete(self, job_list):
130+
@staticmethod
131+
def wait_until_complete(job_list):
161132
"""
162133
Args: Accepts a list of GPJob objects
163134
@@ -188,11 +159,13 @@ def get_recent_jobs(self, n_jobs=10):
188159
"""
189160

190161
# Query the server for the list of jobs
191-
request = urllib2.Request(self.url + '/rest/v1/jobs/?pageSize=' + str(n_jobs) + '&userId=' + str(parse.quote(self.username)) + '&orderBy=-dateSubmitted')
162+
request = urllib.request.Request(self.url + '/rest/v1/jobs/?pageSize=' +
163+
str(n_jobs) + '&userId=' + str(urllib.parse.quote(self.username)) +
164+
'&orderBy=-dateSubmitted')
192165
if self.authorization_header() is not None:
193166
request.add_header('Authorization', self.authorization_header())
194167
request.add_header('User-Agent', 'GenePatternRest')
195-
response = urllib2.urlopen(request)
168+
response = urllib.request.urlopen(request)
196169
response_string = response.read().decode('utf-8')
197170
response_json = json.loads(response_string)
198171

@@ -207,6 +180,7 @@ def get_recent_jobs(self, n_jobs=10):
207180

208181
return job_list
209182

183+
210184
class GPResource(object):
211185
"""
212186
Base class for resources on a Gene Pattern server.
@@ -246,11 +220,11 @@ def open(self):
246220
247221
* getcode() - return the HTTP status code of the response
248222
"""
249-
request = urllib2.Request(self.uri)
223+
request = urllib.request.Request(self.uri)
250224
if self.server_data.authorization_header() is not None:
251225
request.add_header('Authorization', self.server_data.authorization_header())
252226
request.add_header('User-Agent', 'GenePatternRest')
253-
return urllib2.urlopen(request)
227+
return urllib.request.urlopen(request)
254228

255229
def read(self):
256230
"""
@@ -270,7 +244,7 @@ def get_name(self):
270244
"""
271245
Returns the file name of the output file
272246
"""
273-
return parse.unquote(self.get_url().split('/')[-1])
247+
return urllib.parse.unquote(self.get_url().split('/')[-1])
274248

275249

276250
class GPJob(GPResource):
@@ -314,11 +288,11 @@ def get_info(self):
314288
* URL of Output Files
315289
* Number of Output Files
316290
"""
317-
request = urllib2.Request(self.server_data.url + "/rest/v1/jobs/" + self.uri)
291+
request = urllib.request.Request(self.server_data.url + "/rest/v1/jobs/" + self.uri)
318292
if self.server_data.authorization_header() is not None:
319293
request.add_header('Authorization', self.server_data.authorization_header())
320294
request.add_header('User-Agent', 'GenePatternRest')
321-
response = urllib2.urlopen(request)
295+
response = urllib.request.urlopen(request)
322296

323297
self.json = response.read().decode('utf-8')
324298
self.info = json.loads(self.json)
@@ -574,18 +548,12 @@ def param_load(self):
574548
Queries the server for the parameter information and other metadata associated with
575549
this task
576550
"""
577-
# Differences between Python 2 and Python 3
578-
escaped_uri = self.uri
579-
if sys.version_info.major == 2:
580-
escaped_uri = urllib.quote(self.uri)
581-
elif sys.version_info.major == 3:
582-
escaped_uri = urllib.parse.quote(self.uri)
583-
584-
request = urllib2.Request(self.server_data.url + '/rest/v1/tasks/' + escaped_uri)
551+
escaped_uri = urllib.parse.quote(self.uri)
552+
request = urllib.request.Request(self.server_data.url + '/rest/v1/tasks/' + escaped_uri)
585553
if self.server_data.authorization_header() is not None:
586554
request.add_header('Authorization', self.server_data.authorization_header())
587555
request.add_header('User-Agent', 'GenePatternRest')
588-
response = urllib2.urlopen(request)
556+
response = urllib.request.urlopen(request)
589557
self.json = response.read().decode('utf-8')
590558
self.dto = json.loads(self.json)
591559

@@ -812,11 +780,11 @@ def get_choices(self):
812780
print(self.get_choice_status())
813781
print("choice status not initialized")
814782

815-
request = urllib2.Request(self.get_choice_href())
783+
request = urllib.request.Request(self.get_choice_href())
816784
if self.task.server_data.authorization_header() is not None:
817785
request.add_header('Authorization', self.task.server_data.authorization_header())
818786
request.add_header('User-Agent', 'GenePatternRest')
819-
response = urllib2.urlopen(request)
787+
response = urllib.request.urlopen(request)
820788
self.dto[self.name]['choiceInfo'] = json.loads(response.read().decode('utf-8'))
821789
return self.dto[self.name]['choiceInfo']['choices']
822790

@@ -843,7 +811,8 @@ def get_alt_description(self):
843811
else:
844812
return None
845813

846-
def _is_string_true(self, test):
814+
@staticmethod
815+
def _is_string_true(test):
847816
"""
848817
Determines whether a string value is "True" for the purposes of GenePattern's
849818
parameter parsing
@@ -861,4 +830,4 @@ def __init__(self, value):
861830
self.value = value
862831

863832
def __str__(self):
864-
return repr(self.value)
833+
return repr(self.value)

gp/modules.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Tools for converting Python scripts into GenePattern server modules
55
6-
Compatible with Python 2.7 and Python 3.4+
6+
Compatible with Python 3.4+
77
"""
88
import getpass
99
import json
@@ -21,7 +21,7 @@
2121

2222
__authors__ = ['Thorin Tabor']
2323
__version__ = '0.2.0'
24-
__status__ = 'Beta'
24+
__status__ = 'Alpha'
2525

2626

2727
class StringEnum(str, Enum):

0 commit comments

Comments
 (0)