Skip to content
This repository was archived by the owner on Jan 7, 2021. It is now read-only.

Commit a788b94

Browse files
committed
Attempted at Python3 compatibility and testing
1 parent e5addf3 commit a788b94

7 files changed

Lines changed: 58 additions & 44 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ python:
33
- '2.5'
44
- '2.6'
55
- '2.7'
6+
- '3.3'
67
install:
78
- pip install -r requirements.txt --use-mirrors
89
script:

documentcloud/MultipartPostHandler.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@
4141
"""
4242
import os
4343
import sys
44-
import urllib
45-
import urllib2
44+
import six
4645
import tempfile
4746
import mimetools
4847
import mimetypes
4948
from os import SEEK_END
50-
from cStringIO import StringIO
49+
from six.moves import urllib
50+
try:
51+
import io
52+
except ImportError:
53+
import cStringIO as io
5154

5255

5356
class Callable:
@@ -59,25 +62,25 @@ def __init__(self, anycallable):
5962
doseq = 1
6063

6164

62-
class MultipartPostHandler(urllib2.BaseHandler):
65+
class MultipartPostHandler(urllib2.request.BaseHandler):
6366
# needs to run first
64-
handler_order = urllib2.HTTPHandler.handler_order - 10
67+
handler_order = urllib2.request.HTTPHandler.handler_order - 10
6568

6669
def http_request(self, request):
6770
data = request.get_data()
6871
if data is not None and type(data) != str:
6972
v_files = []
7073
v_vars = []
7174
try:
72-
for(key, value) in data.items():
75+
for(key, value) in list(data.items()):
7376
if hasattr(value, 'read'):
7477
v_files.append((key, value))
7578
else:
7679
v_vars.append((key, value))
7780
except TypeError:
7881
raise TypeError
7982
if len(v_files) == 0:
80-
data = urllib.urlencode(v_vars, doseq)
83+
data = urllib.parse.urlencode(v_vars, doseq)
8184
else:
8285
boundary, data = self.multipart_encode(v_vars, v_files)
8386
contenttype = 'multipart/form-data; boundary=%s' % boundary
@@ -86,9 +89,11 @@ def http_request(self, request):
8689
request.get_header('Content-Type').find(
8790
'multipart/form-data') != 0
8891
):
89-
print "Replacing %s with %s" % (
90-
request.get_header('content-type'),
91-
'multipart/form-data'
92+
six.print_(
93+
"Replacing %s with %s" % (
94+
request.get_header('content-type'),
95+
'multipart/form-data'
96+
)
9297
)
9398
request.add_unredirected_header('Content-Type', contenttype)
9499
request.add_data(data)
@@ -99,7 +104,7 @@ def multipart_encode(vars, files, boundary=None, buf=None):
99104
if boundary is None:
100105
boundary = mimetools.choose_boundary()
101106
if buf is None:
102-
buf = StringIO()
107+
buf = io.StringIO()
103108
for(key, value) in vars:
104109
buf.write('--%s\r\n' % boundary)
105110
buf.write('Content-Disposition: form-data; name="%s"' % key)
@@ -141,7 +146,7 @@ def getsize(o_file):
141146

142147

143148
def main():
144-
opener = urllib2.build_opener(MultipartPostHandler)
149+
opener = urllib2.request.build_opener(MultipartPostHandler)
145150

146151
def validateFile(url):
147152
temp = tempfile.mkstemp(suffix=".html")

documentcloud/__init__.py

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@
1111
https://www.documentcloud.org/help/api
1212
1313
"""
14+
from __future__ import absolute_import
1415
import os
16+
import six
1517
import copy
1618
import base64
17-
import urllib
18-
import urllib2
19-
from toolbox import retry
20-
from toolbox import DoesNotExistError
21-
from toolbox import DuplicateObjectError
22-
from toolbox import credentials_required
23-
from toolbox import CredentialsFailedError
19+
from .toolbox import retry
20+
from six.moves import urllib
21+
from .toolbox import DoesNotExistError
22+
from .toolbox import DuplicateObjectError
23+
from .toolbox import credentials_required
24+
from .toolbox import CredentialsFailedError
2425
from dateutil.parser import parse as dateparser
25-
from MultipartPostHandler import MultipartPostHandler
26+
from .MultipartPostHandler import MultipartPostHandler
2627
try:
2728
import json
2829
except ImportError:
@@ -50,7 +51,7 @@ def _make_request(self, url, params=None, opener=None):
5051
"""
5152
# Create the request object
5253
args = [i for i in [url, params] if i]
53-
request = urllib2.Request(*args)
54+
request = urllib2.request.Request(*args)
5455
# If the client has credentials, include them as a header
5556
if self.username and self.password:
5657
credentials = '%s:%s' % (self.username, self.password)
@@ -62,14 +63,14 @@ def _make_request(self, url, params=None, opener=None):
6263
# If the request provides a custom opener, like the upload request,
6364
# which relies on a multipart request, it is applied here.
6465
if opener:
65-
opener = urllib2.build_opener(opener)
66+
opener = urllib2.request.build_opener(opener)
6667
request_method = opener.open
6768
else:
68-
request_method = urllib2.urlopen
69+
request_method = urllib2.request.urlopen
6970
# Make the request
7071
try:
7172
response = request_method(request)
72-
except urllib2.HTTPError, e:
73+
except urllib2.error.HTTPError as e:
7374
if e.code == 404:
7475
raise DoesNotExistError("The resource you've requested does \
7576
not exist or is unavailable without the proper credentials.")
@@ -95,7 +96,7 @@ def put(self, method, params):
9596
# Pull the document_ids out of the params
9697
document_ids = params.get("document_ids")
9798
del params['document_ids']
98-
params = urllib.urlencode(params, doseq=True)
99+
params = urllib.parse.urlencode(params, doseq=True)
99100
# These need to be specially formatted in the style documentcloud
100101
# expects arrays. The example they provide is:
101102
# ?document_ids[]=28-boumediene&document_ids[]=\
@@ -108,15 +109,16 @@ def put(self, method, params):
108109
# Pull them out of the dict
109110
data = params.get("data")
110111
del params['data']
111-
params = urllib.urlencode(params, doseq=True)
112+
params = urllib.parse.urlencode(params, doseq=True)
112113
# Format them in the style documentcloud expects
113114
# ?data['foo']=bar&data['tit']=tat
114115
params += "".join([
115-
'&data[%s]=%s' % (key, value) for key, value in data.items()
116+
'&data[%s]=%s' % (key, value) for key, value in
117+
list(data.items())
116118
])
117119
else:
118120
# Otherwise, we can just use the vanilla urllib prep method
119-
params = urllib.urlencode(params, doseq=True)
121+
params = urllib.parse.urlencode(params, doseq=True)
120122
# Make the request
121123
self._make_request(
122124
self.BASE_URI + method,
@@ -129,7 +131,7 @@ def fetch(self, method, params=None):
129131
"""
130132
# Encode params if they exist
131133
if params:
132-
params = urllib.urlencode(params, doseq=True)
134+
params = urllib.parse.urlencode(params, doseq=True)
133135
content = self._make_request(
134136
self.BASE_URI + method,
135137
params,
@@ -268,7 +270,7 @@ def upload(
268270
if project:
269271
params['project'] = project
270272
if data:
271-
for key, value in data.items():
273+
for key, value in list(data.items()):
272274
is_valid_data_keyword(key)
273275
params['data[%s]' % key] = value
274276
if secure:
@@ -425,7 +427,7 @@ def create(self, title, description=None, document_ids=None):
425427
}
426428
if description:
427429
params['description'] = description
428-
params = urllib.urlencode(params, doseq=True)
430+
params = urllib.parse.urlencode(params, doseq=True)
429431
if document_ids:
430432
# These need to be specially formatted in the style documentcloud
431433
# expects arrays. The example they provide is:
@@ -489,7 +491,7 @@ def __str__(self):
489491
return self.__unicode__().encode("utf-8")
490492

491493
def __unicode__(self):
492-
return unicode(self.title)
494+
return six.u(self.title)
493495

494496

495497
class Annotation(BaseAPIObject):
@@ -506,14 +508,14 @@ def __str__(self):
506508
return self.__unicode__().encode("utf-8")
507509

508510
def __unicode__(self):
509-
return u''
511+
return six.u('')
510512

511513
def get_location(self):
512514
"""
513515
Return the location as a good
514516
"""
515517
image_string = self.__dict__['location']['image']
516-
image_ints = map(int, image_string.split(","))
518+
image_ints = list(map(int, image_string.split(",")))
517519
return Location(*image_ints)
518520
location = property(get_location)
519521

@@ -623,7 +625,7 @@ def set_data(self, data):
623625
if not isinstance(data, type({})):
624626
raise TypeError("This attribute must be a dictionary.")
625627
# Validate keywords
626-
for keyword in data.keys():
628+
for keyword in list(data.keys()):
627629
is_valid_data_keyword(keyword)
628630
# Set the attribute
629631
self.__dict__['data'] = data
@@ -676,7 +678,7 @@ def get_entities(self):
676678
"documents/%s/entities.json" % self.id
677679
).get("entities")
678680
obj_list = []
679-
for type, entity_list in entities.items():
681+
for type, entity_list in list(entities.items()):
680682
for entity in entity_list:
681683
entity['type'] = type
682684
obj = Entity(entity)
@@ -691,10 +693,10 @@ def get_entities(self):
691693

692694
def _get_url(self, url):
693695
if self.access == 'public':
694-
req = urllib2.Request(url)
696+
req = urllib2.request.Request(url)
695697
try:
696-
return urllib2.urlopen(req).read()
697-
except urllib2.HTTPError:
698+
return urllib2.request.urlopen(req).read()
699+
except urllib2.error.HTTPError:
698700
raise NotImplementedError(
699701
"Currently, DocumentCloud only allows you to access this \
700702
resource on public documents."
@@ -931,7 +933,7 @@ class Entity(BaseAPIObject):
931933
Keywords and such extracted from the document by OpenCalais.
932934
"""
933935
def __unicode__(self):
934-
return unicode(self.value)
936+
return six.u(self.value)
935937

936938

937939
class Location(object):
@@ -945,7 +947,7 @@ def __str__(self):
945947
return self.__unicode__().encode("utf-8")
946948

947949
def __unicode__(self):
948-
return u''
950+
return six.u('')
949951

950952
def __init__(self, top, right, bottom, left):
951953
self.top = top
@@ -959,7 +961,7 @@ class Mention(BaseAPIObject):
959961
A mention of a search found in the document.
960962
"""
961963
def __unicode__(self):
962-
return unicode("Page %s" % (self.page))
964+
return six.u("Page %s" % (self.page))
963965

964966

965967
class Project(BaseAPIObject):
@@ -1058,7 +1060,7 @@ def __str__(self):
10581060
return self.__unicode__().encode("utf-8")
10591061

10601062
def __unicode__(self):
1061-
return u''
1063+
return six.u('')
10621064

10631065
def __getattr__(self, name):
10641066
# When these attrs don't exist in the DocumentCloud db,

documentcloud/toolbox.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
A few toys the API will use.
33
"""
4+
import six
45
import time
56
from functools import wraps
67

@@ -74,7 +75,7 @@ def f_retry(*args, **kwargs):
7475
try_one_last_time = False
7576
break
7677
except ExceptionToCheck:
77-
print "Retrying in %s seconds" % str(mdelay)
78+
six.print_("Retrying in %s seconds" % str(mdelay))
7879
time.sleep(mdelay)
7980
mtries -= 1
8081
mdelay *= backoff

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
argparse==1.2.1
22
pep8==1.4.6
3+
pyflakes==0.7.3
34
python-dateutil==1.5
45
simplejson==3.3.1
6+
six==1.4.1

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def fullsplit(path, result=None):
8888
install_requires=[
8989
'python-dateutil==1.5',
9090
'simplejson==3.3.1',
91+
'six==1.4.1',
9192
],
9293
)
9394

test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def get_editable_document(self, version):
7777
"2.5": "351008-lbex-docid-3383445",
7878
"2.6": "15144-mitchrpt",
7979
"2.7": "351151-lbex-docid-130036",
80+
"3.3": "50986-lbhi_sec07940_755445",
8081
}
8182
return version2slug[str(version)]
8283

@@ -93,6 +94,7 @@ def get_editable_project(self, version):
9394
"2.5": 11051,
9495
"2.6": 11048,
9596
"2.7": 11047,
97+
"3.3": 11070,
9698
}
9799
return version2slug[str(version)]
98100

0 commit comments

Comments
 (0)