Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
42 changes: 42 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -891,3 +891,45 @@ 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
************

Shotgun API offers the ability to return display namnes in user's language.
Copy link
Contributor

Choose a reason for hiding this comment

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

The Shotgun API

Copy link
Contributor

Choose a reason for hiding this comment

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

How does the API know what the user's language is? I think this is worth explaining. Is it based on the user's language preferences in Shotgun? (What happens with script/API users?)

Copy link
Contributor

Choose a reason for hiding this comment

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

namnes (typo)

Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: return display namnes in user's language -> return field display names in the current user's language.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for all the suggestions ! I updated the documentation.

The server currently returns localized display names for those methods:
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest changing this line (and removing the two bullet points below) to "This functionality is currently supported by the methods schema_entity_read and schema_field_read."


* Shotgun.schema_entity_read
* Shotgun.schema_field_read

Localization is disabled by default. To enable localization, set the ``localized`` property to ``True``.
The property cannot be set at Shotgun class instantiation. When the class is instantiated, the ``localized`` config property is set to ``False``.
Copy link
Contributor

Choose a reason for hiding this comment

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

line 905: i think we can skip this entire line! The example explains it well!


Example for a user whose Language preference is set to Japanese:
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: language should have a lower case l


.. code-block:: python
:emphasize-lines: 8,19
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️


>>> 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.

...
},
...
}
12 changes: 12 additions & 0 deletions shotgun_api3/shotgun.py
Original file line number Diff line number Diff line change
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 @@ -598,6 +599,7 @@ def __init__(self,
self.config.convert_datetimes_to_utc = convert_datetimes_to_utc
self.config.no_ssl_validation = NO_SSL_VALIDATION
self.config.raw_http_proxy = http_proxy
self.config.localized = False
Copy link
Contributor

Choose a reason for hiding this comment

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

i don't think we need this line - it's already initialized in the constructor for _Config, right? If we do need it, please explain the reason why with a comment!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's correct, I removed the line


try:
self.config.rpc_attempt_interval = int(os.environ.get("SHOTGUN_API_RETRY_INTERVAL", 3000))
Expand Down Expand Up @@ -1818,6 +1820,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 informations.
Copy link
Contributor

Choose a reason for hiding this comment

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

great!

Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: typo: informations -> information.

"""

params = {}
Expand Down Expand Up @@ -1917,6 +1922,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 informations.
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: typo: informations -> 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 +3213,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