Skip to content

Commit 0fef00f

Browse files
SG-29997 - FIRST PHASE Python2 removing (#352)
* ensure_* functions ans six types replaced by native and sgutils * improvement for ensure_str function
1 parent 9efa0a2 commit 0fef00f

File tree

5 files changed

+111
-49
lines changed

5 files changed

+111
-49
lines changed

shotgun_api3/lib/mockgun/mockgun.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@
120120
from ...shotgun import _Config
121121
from .errors import MockgunError
122122
from .schema import SchemaFactory
123-
from .. import six
124123

125124
# ----------------------------------------------------------------------------
126125
# Version
@@ -505,14 +504,14 @@ def _validate_entity_data(self, entity_type, data):
505504
"float": float,
506505
"checkbox": bool,
507506
"percent": int,
508-
"text": six.string_types,
507+
"text": str,
509508
"serializable": dict,
510-
"entity_type": six.string_types,
511-
"date": six.string_types,
509+
"entity_type": str,
510+
"date": str,
512511
"date_time": datetime.datetime,
513512
"duration": int,
514-
"list": six.string_types,
515-
"status_list": six.string_types,
513+
"list": str,
514+
"status_list": str,
516515
"url": dict}[sg_type]
517516
except KeyError:
518517
raise ShotgunError(

shotgun_api3/lib/sgutils.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
-----------------------------------------------------------------------------
3+
Copyright (c) 2009-2024, Shotgun Software Inc.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
- Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
- Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
- Neither the name of the Shotgun Software Inc nor the names of its
16+
contributors may be used to endorse or promote products derived from this
17+
software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
"""
30+
31+
32+
def ensure_binary(s, encoding='utf-8', errors='strict'):
33+
"""
34+
Coerce **s** to bytes.
35+
36+
- `str` -> encoded to `bytes`
37+
- `bytes` -> `bytes`
38+
"""
39+
if isinstance(s, str):
40+
return s.encode(encoding, errors)
41+
elif isinstance(s, bytes):
42+
return s
43+
else:
44+
raise TypeError(f"not expecting type '{type(s)}'")
45+
46+
47+
def ensure_str(s, encoding='utf-8', errors='strict'):
48+
"""Coerce *s* to `str`.
49+
50+
- `str` -> `str`
51+
- `bytes` -> decoded to `str`
52+
"""
53+
if isinstance(s, str):
54+
return s
55+
56+
elif isinstance(s, bytes):
57+
return s.decode(encoding, errors)
58+
59+
raise TypeError(f"not expecting type '{type(s)}'")
60+
61+
62+
ensure_text = ensure_str

shotgun_api3/shotgun.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# Python 2/3 compatibility
3333
from .lib import six
3434
from .lib import sgsix
35+
from .lib import sgutils
3536
from .lib.six import BytesIO # used for attachment upload
3637
from .lib.six.moves import map
3738

@@ -665,7 +666,7 @@ def __init__(self,
665666
# the lowercase version of the credentials.
666667
auth, self.config.server = self._split_url(base_url)
667668
if auth:
668-
auth = base64encode(six.ensure_binary(
669+
auth = base64encode(sgutils.ensure_binary(
669670
urllib.parse.unquote(auth))).decode("utf-8")
670671
self.config.authorization = "Basic " + auth.strip()
671672

@@ -2440,7 +2441,7 @@ def upload(self, entity_type, entity_id, path, field_name=None, display_name=Non
24402441
# have to raise a sane exception. This will always work for ascii and utf-8
24412442
# encoded strings, but will fail on some others if the string includes non
24422443
# ascii characters.
2443-
if not isinstance(path, six.text_type):
2444+
if not isinstance(path, str):
24442445
try:
24452446
path = path.decode("utf-8")
24462447
except UnicodeDecodeError:
@@ -2721,7 +2722,7 @@ def download_attachment(self, attachment=False, file_path=None, attachment_id=No
27212722
elif e.code == 403:
27222723
# Only parse the body if it is an Amazon S3 url.
27232724
if url.find("s3.amazonaws.com") != -1 and e.headers["content-type"] == "application/xml":
2724-
body = [six.ensure_text(line) for line in e.readlines()]
2725+
body = [sgutils.ensure_text(line) for line in e.readlines()]
27252726
if body:
27262727
xml = "".join(body)
27272728
# Once python 2.4 support is not needed we can think about using
@@ -3545,7 +3546,7 @@ def _encode_payload(self, payload):
35453546
"""
35463547

35473548
wire = json.dumps(payload, ensure_ascii=False)
3548-
return six.ensure_binary(wire)
3549+
return sgutils.ensure_binary(wire)
35493550

35503551
def _make_call(self, verb, path, body, headers):
35513552
"""
@@ -3720,8 +3721,8 @@ def _json_loads_ascii(self, body):
37203721
def _decode_list(lst):
37213722
newlist = []
37223723
for i in lst:
3723-
if isinstance(i, six.text_type):
3724-
i = six.ensure_str(i)
3724+
if isinstance(i, str):
3725+
i = sgutils.ensure_str(i)
37253726
elif isinstance(i, list):
37263727
i = _decode_list(i)
37273728
newlist.append(i)
@@ -3730,10 +3731,10 @@ def _decode_list(lst):
37303731
def _decode_dict(dct):
37313732
newdict = {}
37323733
for k, v in six.iteritems(dct):
3733-
if isinstance(k, six.text_type):
3734-
k = six.ensure_str(k)
3735-
if isinstance(v, six.text_type):
3736-
v = six.ensure_str(v)
3734+
if isinstance(k, str):
3735+
k = sgutils.ensure_str(k)
3736+
if isinstance(v, str):
3737+
v = sgutils.ensure_str(v)
37373738
elif isinstance(v, list):
37383739
v = _decode_list(v)
37393740
newdict[k] = v
@@ -3844,8 +3845,8 @@ def _outbound_visitor(value):
38443845
return value.strftime("%Y-%m-%dT%H:%M:%SZ")
38453846

38463847
# ensure return is six.text_type
3847-
if isinstance(value, six.string_types):
3848-
return six.ensure_text(value)
3848+
if isinstance(value, str):
3849+
return sgutils.ensure_text(value)
38493850

38503851
return value
38513852

@@ -3865,7 +3866,7 @@ def _change_tz(x):
38653866
_change_tz = None
38663867

38673868
def _inbound_visitor(value):
3868-
if isinstance(value, six.string_types):
3869+
if isinstance(value, str):
38693870
if len(value) == 20 and self._DATE_TIME_PATTERN.match(value):
38703871
try:
38713872
# strptime was not on datetime in python2.4
@@ -4266,7 +4267,7 @@ def _send_form(self, url, params):
42664267
else:
42674268
raise ShotgunError("Unanticipated error occurred %s" % (e))
42684269

4269-
return six.ensure_text(result)
4270+
return sgutils.ensure_text(result)
42704271
else:
42714272
raise ShotgunError("Max attemps limit reached.")
42724273

@@ -4339,7 +4340,7 @@ def http_request(self, request):
43394340
data = request.data
43404341
else:
43414342
data = request.get_data()
4342-
if data is not None and not isinstance(data, six.string_types):
4343+
if data is not None and not isinstance(data, str):
43434344
files = []
43444345
params = []
43454346
for key, value in data.items():
@@ -4348,7 +4349,7 @@ def http_request(self, request):
43484349
else:
43494350
params.append((key, value))
43504351
if not files:
4351-
data = six.ensure_binary(urllib.parse.urlencode(params, True)) # sequencing on
4352+
data = sgutils.ensure_binary(urllib.parse.urlencode(params, True)) # sequencing on
43524353
else:
43534354
boundary, data = self.encode(params, files)
43544355
content_type = "multipart/form-data; boundary=%s" % boundary
@@ -4371,40 +4372,40 @@ def encode(self, params, files, boundary=None, buffer=None):
43714372
if buffer is None:
43724373
buffer = BytesIO()
43734374
for (key, value) in params:
4374-
if not isinstance(value, six.string_types):
4375+
if not isinstance(value, str):
43754376
# If value is not a string (e.g. int) cast to text
4376-
value = six.text_type(value)
4377-
value = six.ensure_text(value)
4378-
key = six.ensure_text(key)
4377+
value = str(value)
4378+
value = sgutils.ensure_text(value)
4379+
key = sgutils.ensure_text(key)
43794380

4380-
buffer.write(six.ensure_binary("--%s\r\n" % boundary))
4381-
buffer.write(six.ensure_binary("Content-Disposition: form-data; name=\"%s\"" % key))
4382-
buffer.write(six.ensure_binary("\r\n\r\n%s\r\n" % value))
4381+
buffer.write(sgutils.ensure_binary("--%s\r\n" % boundary))
4382+
buffer.write(sgutils.ensure_binary("Content-Disposition: form-data; name=\"%s\"" % key))
4383+
buffer.write(sgutils.ensure_binary("\r\n\r\n%s\r\n" % value))
43834384
for (key, fd) in files:
43844385
# On Windows, it's possible that we were forced to open a file
43854386
# with non-ascii characters as unicode. In that case, we need to
43864387
# encode it as a utf-8 string to remove unicode from the equation.
43874388
# If we don't, the mix of unicode and strings going into the
43884389
# buffer can cause UnicodeEncodeErrors to be raised.
43894390
filename = fd.name
4390-
filename = six.ensure_text(filename)
4391+
filename = sgutils.ensure_text(filename)
43914392
filename = filename.split("/")[-1]
4392-
key = six.ensure_text(key)
4393+
key = sgutils.ensure_text(key)
43934394
content_type = mimetypes.guess_type(filename)[0]
43944395
content_type = content_type or "application/octet-stream"
43954396
file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
4396-
buffer.write(six.ensure_binary("--%s\r\n" % boundary))
4397+
buffer.write(sgutils.ensure_binary("--%s\r\n" % boundary))
43974398
c_dis = "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"%s"
43984399
content_disposition = c_dis % (key, filename, "\r\n")
4399-
buffer.write(six.ensure_binary(content_disposition))
4400-
buffer.write(six.ensure_binary("Content-Type: %s\r\n" % content_type))
4401-
buffer.write(six.ensure_binary("Content-Length: %s\r\n" % file_size))
4400+
buffer.write(sgutils.ensure_binary(content_disposition))
4401+
buffer.write(sgutils.ensure_binary("Content-Type: %s\r\n" % content_type))
4402+
buffer.write(sgutils.ensure_binary("Content-Length: %s\r\n" % file_size))
44024403

4403-
buffer.write(six.ensure_binary("\r\n"))
4404+
buffer.write(sgutils.ensure_binary("\r\n"))
44044405
fd.seek(0)
44054406
shutil.copyfileobj(fd, buffer)
4406-
buffer.write(six.ensure_binary("\r\n"))
4407-
buffer.write(six.ensure_binary("--%s--\r\n\r\n" % boundary))
4407+
buffer.write(sgutils.ensure_binary("\r\n"))
4408+
buffer.write(sgutils.ensure_binary("--%s--\r\n\r\n" % boundary))
44084409
buffer = buffer.getvalue()
44094410
return boundary, buffer
44104411

tests/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def _mock_http(self, data, headers=None, status=None):
175175
if not isinstance(self.sg._http_request, mock.Mock):
176176
return
177177

178-
if not isinstance(data, six.string_types):
178+
if not isinstance(data, str):
179179
if six.PY2:
180180
data = json.dumps(
181181
data,
@@ -208,7 +208,7 @@ def _assert_http_method(self, method, params, check_auth=True):
208208
"""Asserts _http_request is called with the method and params."""
209209
args, _ = self.sg._http_request.call_args
210210
arg_body = args[2]
211-
assert isinstance(arg_body, six.binary_type)
211+
assert isinstance(arg_body, bytes)
212212
arg_body = json.loads(arg_body)
213213

214214
arg_params = arg_body.get("params")

tests/test_client.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import re
1818

1919
from shotgun_api3.lib.six.moves import urllib
20-
from shotgun_api3.lib import six
20+
from shotgun_api3.lib import six, sgutils
2121
try:
2222
import simplejson as json
2323
except ImportError:
@@ -44,7 +44,7 @@
4444

4545

4646
def b64encode(val):
47-
return base64encode(six.ensure_binary(val)).decode("utf-8")
47+
return base64encode(sgutils.ensure_binary(val)).decode("utf-8")
4848

4949

5050
class TestShotgunClient(base.MockTestBase):
@@ -424,7 +424,7 @@ def test_call_rpc(self):
424424

425425
# Test unicode mixed with utf-8 as reported in Ticket #17959
426426
d = {"results": ["foo", "bar"]}
427-
a = {"utf_str": "\xe2\x88\x9a", "unicode_str": six.ensure_text("\xe2\x88\x9a")}
427+
a = {"utf_str": "\xe2\x88\x9a", "unicode_str": sgutils.ensure_text("\xe2\x88\x9a")}
428428
self._mock_http(d)
429429
rv = self.sg._call_rpc("list", a)
430430
expected = "rpc response with list result"
@@ -585,14 +585,14 @@ def _datetime(s, f):
585585
return datetime.datetime(*time.strptime(s, f)[:6])
586586

587587
def assert_wire(wire, match):
588-
self.assertTrue(isinstance(wire["date"], six.string_types))
588+
self.assertTrue(isinstance(wire["date"], str))
589589
d = _datetime(wire["date"], "%Y-%m-%d").date()
590590
d = wire['date']
591591
self.assertEqual(match["date"], d)
592-
self.assertTrue(isinstance(wire["datetime"], six.string_types))
592+
self.assertTrue(isinstance(wire["datetime"], str))
593593
d = _datetime(wire["datetime"], "%Y-%m-%dT%H:%M:%SZ")
594594
self.assertEqual(match["datetime"], d)
595-
self.assertTrue(isinstance(wire["time"], six.string_types))
595+
self.assertTrue(isinstance(wire["time"], str))
596596
d = _datetime(wire["time"], "%Y-%m-%dT%H:%M:%SZ")
597597
self.assertEqual(match["time"], d.time())
598598

@@ -621,16 +621,16 @@ def test_encode_payload(self):
621621

622622
d = {"this is ": u"my data \u00E0"}
623623
j = self.sg._encode_payload(d)
624-
self.assertTrue(isinstance(j, six.binary_type))
624+
self.assertTrue(isinstance(j, bytes))
625625

626626
d = {
627627
"this is ": u"my data"
628628
}
629629
j = self.sg._encode_payload(d)
630-
self.assertTrue(isinstance(j, six.binary_type))
630+
self.assertTrue(isinstance(j, bytes))
631631

632632
def test_decode_response_ascii(self):
633-
self._assert_decode_resonse(True, six.ensure_str(u"my data \u00E0", encoding='utf8'))
633+
self._assert_decode_resonse(True, sgutils.ensure_str(u"my data \u00E0", encoding='utf8'))
634634

635635
def test_decode_response_unicode(self):
636636
self._assert_decode_resonse(False, u"my data \u00E0")

0 commit comments

Comments
 (0)