Skip to content

Commit c8e3bb9

Browse files
author
kennedy behrman
committed
added ensure_ascii flag
1 parent 8238e92 commit c8e3bb9

File tree

5 files changed

+91
-26
lines changed

5 files changed

+91
-26
lines changed

README.mdown

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ For Python 2.6 and higher, we will use the built-in json module... but installin
1313

1414
## Known Issues
1515

16-
Does not yet support the new summarize method from the summary_grouping branch.
1716

1817

1918

@@ -31,7 +30,7 @@ Shotgun provides a simple Python-based API for accessing Shotgun and integrating
3130
- Python v2.4 - v2.7. (We do have plans to eventually support Python 3)
3231

3332
## Installing
34-
To use Shotgun's Python API module, you need to place it in one of the directories specified by the environment variable PYTHONPATH. For more information on PYTHONPATH and using modules in Python, see http://docs.python.org/tutorial/modules.html
33+
To use Shotgun's Python API module, you need to place the package shotgun_api3 in one of the directories specified by the environment variable PYTHONPATH. For more information on PYTHONPATH and using modules in Python, see http://docs.python.org/tutorial/modules.html
3534

3635
## Documentation
3736
Tutorials and detailed documentation about the Python API are available on the [Shotgun Support website](https://support.shotgunsoftware.com/forums/48807-developer-api-info).
@@ -42,10 +41,23 @@ Some useful direct links within this section are below:
4241
* [Reference: Data Types](https://github.com/shotgunsoftware/python-api/wiki/Reference%3A-Data-Types)
4342
* [Reference: Filter Syntax](https://github.com/shotgunsoftware/python-api/wiki/Reference%3A-Filter-Syntax)
4443

44+
## Tests
45+
Integration and unit tests are provided.
46+
- test_client and tests_unit mock server interaction and do not require a shotgun instance to be available.
47+
- test_api and test_api_long do require a shotgun instance, with a script key available for the tests. These tests rely on a tests/config file, which can be created by renaming example_config and supplying the server and script user values. The tests will set up test data on the server based on the data set forth in the config. This data will be manipulated by the tests, and should not be used for other purposes.
48+
- To run all of the tests, use the shell script run-tests. This script require nose to be installed.
49+
4550
## Changelog
4651
(Note that many of these items may be specific to the XML-RPC version of the API and may not apply to the Python JSON API)
4752

4853

54+
**v3.0.8 - 2011 July 27**
55+
56+
+ summarize available in json api
57+
+ refactored single file into package
58+
+ tests added (Thanks to Aaron Morton https://github.com/amorton)
59+
+ added ensure_ascii parameter, insuring results are ascii
60+
4961
**v3.0.6 - 2010 Jan 25**
5062

5163
+ optimization: don't request paging_info unless required (and server support is available)

shotgun_api3/shotgun.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
# ----------------------------------------------------------------------------
6262
# Version
63-
__version__ = "3.1a1"
63+
__version__ = "3.0.8"
6464

6565
# ----------------------------------------------------------------------------
6666
# Errors
@@ -189,6 +189,7 @@ def __init__(self,
189189
api_key,
190190
convert_datetimes_to_utc=True,
191191
http_proxy=None,
192+
ensure_ascii=True,
192193
connect=True):
193194
"""Initialises a new instance of the Shotgun client.
194195
@@ -216,7 +217,6 @@ def __init__(self,
216217
self.config.script_name = script_name
217218
self.config.convert_datetimes_to_utc = convert_datetimes_to_utc
218219
self.config.proxy_info = http_proxy
219-
220220
self._connection = None
221221

222222
base_url = (base_url or "").lower()
@@ -241,6 +241,9 @@ def __init__(self,
241241
self.config.proxy_server, proxy_port = proxy_netloc.split(":", 1)
242242
self.config.proxy_port = int(proxy_port or 8080)
243243

244+
if ensure_ascii:
245+
self._json_loads = self._json_loads_ascii
246+
244247
self.client_caps = ClientCapabilities()
245248
self._server_caps = None
246249
#test to ensure the the server supports the json API
@@ -1045,12 +1048,39 @@ def _decode_response(self, headers, body):
10451048

10461049
ct = (headers.get("content-type") or "application/json").lower()
10471050

1048-
if ct.startswith("application/json") or \
1049-
ct.startswith("text/javascript"):
1050-
return json.loads(body)
1051-
1051+
if ct.startswith("application/json") or ct.startswith("text/javascript"):
1052+
return self._json_loads(body)
10521053
return body
10531054

1055+
def _json_loads(self, body):
1056+
return json.loads(body)
1057+
1058+
def _json_loads_ascii(self, body):
1059+
'''See http://stackoverflow.com/questions/956867'''
1060+
def _decode_list(lst):
1061+
newlist = []
1062+
for i in lst:
1063+
if isinstance(i, unicode):
1064+
i = i.encode('utf-8')
1065+
elif isinstance(i, list):
1066+
i = _decode_list(i)
1067+
newlist.append(i)
1068+
return newlist
1069+
1070+
def _decode_dict(dct):
1071+
newdict = {}
1072+
for k, v in dct.iteritems():
1073+
if isinstance(k, unicode):
1074+
k = k.encode('utf-8')
1075+
if isinstance(v, unicode):
1076+
v = v.encode('utf-8')
1077+
elif isinstance(v, list):
1078+
v = _decode_list(v)
1079+
newdict[k] = v
1080+
return newdict
1081+
return json.loads(body, object_hook=_decode_dict)
1082+
1083+
10541084
def _response_errors(self, sg_response):
10551085
"""Raises any API errors specified in the response.
10561086
@@ -1280,13 +1310,7 @@ def _dict_to_list(self, d, key_name="field_name", value_name="value"):
12801310
for k,v in (d or {}).iteritems()
12811311
]
12821312

1283-
1284-
1285-
1286-
1287-
1288-
1289-
# ----------------------------------------------------------------------------
1313+
12901314
# Helpers from the previous API, left as is.
12911315

12921316
# Based on http://code.activestate.com/recipes/146306/

tests/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ def _setup_db(self, config):
186186
'project':self.project}
187187
self.shot = _find_or_create_entity(self.sg, 'Shot', data, keys)
188188

189+
keys = ['project','user']
190+
data = {'project':self.project,
191+
'user':self.human_user,
192+
'content':'anything'}
193+
self.note = _find_or_create_entity(self.sg, 'Note', data, keys)
194+
189195

190196
class SgTestConfig(object):
191197
'''Reads test config and holds values'''

tests/example_config

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
1-
# Example server and login information to use for tests. This file should be renamed
2-
# to config and the appropriate values should be added.
3-
#
4-
# The unit tests can either connect to a server or use a mock server connection.
5-
# When connected to a server they use the entity id's listed below to add,
6-
# update and delete values. They also add, update and delete schema fields.
7-
#
8-
# For more details see the test_client.py file.
9-
10-
[TEST_RUN_OPTIONS]
11-
mock:True
1+
# Example server and login information to use for tests which require a live server.
2+
# This file should be renamed to config and the appropriate values should be added.
123

134
[SERVER_INFO]
145

tests/test_api.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
class TestShotgunApi(base.LiveTestBase):
1515
def setUp(self):
1616
super(TestShotgunApi, self).setUp()
17+
# give note unicode content
18+
self.sg.update('Note', self.note['id'], {'content':u'La Pe\xf1a'})
1719

1820
def test_info(self):
1921
"""Called info"""
@@ -174,3 +176,33 @@ def test_simple_summary(self):
174176
assert(result['groups'][0]['summaries'])
175177
assert(result['summaries'])
176178

179+
def test_ensure_ascii(self):
180+
'''test_ensure_ascii tests ensure_unicode flag.'''
181+
sg_ascii = api.Shotgun(self.config.server_url,
182+
self.config.script_name,
183+
self.config.api_key,
184+
ensure_ascii=True)
185+
186+
result = sg_ascii.find_one('Note', [['id','is',self.note['id']]], fields=['content'])
187+
self.assertFalse(_has_unicode(result))
188+
189+
190+
def test_ensure_unicode(self):
191+
'''test_ensure_unicode tests ensure_unicode flag.'''
192+
sg_unicode = api.Shotgun(self.config.server_url,
193+
self.config.script_name,
194+
self.config.api_key,
195+
ensure_ascii=False)
196+
result = sg_unicode.find_one('Note', [['id','is',self.note['id']]], fields=['content'])
197+
print result
198+
self.assertTrue(_has_unicode(result))
199+
200+
def _has_unicode(data):
201+
for k, v in data.items():
202+
if (isinstance(k, unicode)):
203+
return True
204+
if (isinstance(v, unicode)):
205+
return True
206+
return False
207+
208+

0 commit comments

Comments
 (0)