Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Shotgun Python API Changelog

Here you can see the full list of changes between each Python API release.

v3.1.2 (2019 Sept 17)
=====================
- Adds an optional `localized` property on the Shotgun object which allows to retrieve localized display names on
methods ``schema_entity_read()``, ``schema_field_read()``, and ``schema_read()``.

v3.1.1 (2019 August 29)
=====================
- Fixes a regression on Python 2.7.0-2.7.9 on Windows with the mimetypes module.
Expand Down
43 changes: 43 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -891,3 +891,46 @@ Stores the number of milliseconds to wait between request retries. By default,

In the case that both this environment variable and the config's ``rpc_attempt_interval`` property are set, the value in ``rpc_attempt_interal`` will be used.

************
Localization
************

The Shotgun API offers the ability to return localized display names in the current user's language.
Requests made from script/API users are localized in the site settings.

This functionality is currently supported by the methods ``Shotgun.schema_entity_read``, ``Shotgun.schema_field_read``, and ``Shotgun.schema_read``.

Localization is disabled by default. To enable localization, set the ``localized`` property to ``True``.

Example for a user whose language preference is set to Japanese:

.. code-block:: python
:emphasize-lines: 9,20

>>> sg = Shotgun(site_name, script_name, script_key)
>>> sg.config.localized # checking that localization is disabled
False
>>> sg.schema_field_read('Shot')
{
'sg_vendor_groups': {
'mandatory': {'editable': False, 'value': False},
# the value field (display name) is not localized
'name': {'editable': True, 'value': 'Vendor Groups'},
...
},
...
}
>>> sg.config.localized = True # enabling the localization
>>> sg.schema_field_read('Shot')
{
'sg_vendor_groups': {
'mandatory': {'editable': False, 'value': False},
# the value field (display name) is localized
'name': {'editable': True, 'value': '\xe3\x83\x99\xe3\x83\xb3\xe3\x83\x80\xe3\x83\xbc \xe3\x82\xb0\xe3\x83\xab\xe3\x83\xbc\xe3\x83\x97'},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a UTF-8 encoded string, right? I think we should mention that as part of the docs.

Also, it would be great if @willis102 did a quick CR on this to make sure that this approach still make sense for Python 3!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yes, this is returning a UTF-8 encoded string in Python 2, and a str in Python 3 (meaning in Python 3 we're not dealing with bytes.) It might be good to mention in the documentation that for Python 2/3 compatibility you can decode the string using shotgun_api3.lib.six.ensure_text(), ensuring that the Python3 str object is handled properly. I ran your test code in the branch on Python 2 and 3 and it behaved as I would expect.

So, my opinion is that this would be good to mention in the docs but that the current behavior is correct.

...
},
...
}

.. note::
If needed, the encoding of the returned localized string can be ensured regardless the Python version using shotgun_api3.lib.six.ensure_text().
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

setup(
name='shotgun_api3',
version='3.1.1',
version='3.1.2',
description='Shotgun Python API ',
long_description=readme,
author='Shotgun Software',
Expand Down
16 changes: 15 additions & 1 deletion shotgun_api3/shotgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _is_mimetypes_broken():

# ----------------------------------------------------------------------------
# Version
__version__ = "3.1.1"
__version__ = "3.1.2"

# ----------------------------------------------------------------------------
# Errors
Expand Down Expand Up @@ -427,6 +427,7 @@ def __init__(self, sg):
self.session_token = None
self.authorization = None
self.no_ssl_validation = False
self.localized = False

@property
def records_per_page(self):
Expand Down Expand Up @@ -1818,6 +1819,9 @@ def schema_entity_read(self, project_entity=None):
``{'type': 'Project', 'id': 3}``
:returns: dict of Entity Type to dict containing the display name.
:rtype: dict

.. note::
The returned display names for this method will be localized when the ``localize`` Shotgun config property is set to ``True``. See :ref:`localization` for more information.
"""

params = {}
Expand Down Expand Up @@ -1887,6 +1891,9 @@ def schema_read(self, project_entity=None):
types. Properties that are ``'editable': True``, can be updated using the
:meth:`~shotgun_api3.Shotgun.schema_field_update` method.
:rtype: dict

.. note::
The returned display names for this method will be localized when the ``localize`` Shotgun config property is set to ``True``. See :ref:`localization` for more information.
"""

params = {}
Expand Down Expand Up @@ -1917,6 +1924,9 @@ def schema_field_read(self, entity_type, field_name=None, project_entity=None):
.. note::
If you don't specify a ``project_entity``, everything is reported as visible.

.. note::
The returned display names for this method will be localized when the ``localize`` Shotgun config property is set to ``True``. See :ref:`localization` for more information.

>>> sg.schema_field_read('Asset', 'shots')
{'shots': {'data_type': {'editable': False, 'value': 'multi_entity'},
'description': {'editable': True, 'value': ''},
Expand Down Expand Up @@ -3205,6 +3215,10 @@ def _call_rpc(self, method, params, include_auth_params=True, first=False):
"content-type": "application/json; charset=utf-8",
"connection": "keep-alive"
}

if self.config.localized is True:
req_headers["locale"] = "auto"

http_status, resp_headers, body = self._make_call("POST", self.config.api_path,
encoded_payload, req_headers)
LOG.debug("Completed rpc call to %s" % (method))
Expand Down
1 change: 1 addition & 0 deletions tests/example_config
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# Full url to the Shotgun server server
# e.g. http://my-company.shotgunstudio.com
# be careful to not end server_url with a "/", or some tests may fail
server_url : http://0.0.0.0:3000
# script name as key as listed in admin panel for your server
script_name : test script name
Expand Down
22 changes: 22 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,28 @@ def test_authorization(self):
expected = "Basic " + b64encode(urllib.parse.unquote(login_password)).strip()
self.assertEqual(expected, headers.get("Authorization"))

def test_localization_header_default(self):
"""Localization header not passed to server without explicitly setting SG localization config to True"""
self.sg.info()

args, _ = self.sg._http_request.call_args
(_, _, _, headers) = args
expected_header_value = "auto"

self.assertEqual(None, headers.get("locale"))

def test_localization_header_when_localized(self):
"""Localization header passed to server when setting SG localization config to True"""
self.sg.config.localized = True

self.sg.info()

args, _ = self.sg._http_request.call_args
(_, _, _, headers) = args
expected_header_value = "auto"

self.assertEqual("auto", headers.get("locale"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably should test whether the schema content coming back from the server is actually localised. Is this easy to do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not hard to do (not sure if we can set the site language from the API, though) but then it would test the server, not the client.
IMO if the requests is made with the right header, the job is done.
Also, the localization on the server is already tested with Cypress on top of Ruby unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that makes sense! I was thinking about testing the "receiving end" of the transport, eg. asserting that the received object is a str and that it's utf-8 encoded etc. Maybe this is one for @willis102 to weigh in on - do you think this test would be meaningful in a py3 cpntext?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, since the encoding/decoding of received objects returned from _call_rpc is tested elsewhere, so we should be covered by those tests already for this as well. If we do add a test for this, it's important to note that on py3 we will get back a str object, not bytes, so it will not need to be decoded as in py2. As mentioned above, six.ensure_text() will ensure that the value is decoded properly on py2 and 3.


def test_user_agent(self):
"""User-Agent passed to server"""
# test default user agent
Expand Down