Skip to content

Commit

Permalink
SG-13264 Update sgsix, fix httplib2 import (#220)
Browse files Browse the repository at this point in the history
* Add platform and normalize_platform to sgsix to unify platform value on linux across Python 2/3
* Change httplib import procedure to emulate direct import of the module
* Add test to ensure httplib2 is importable as expected
  • Loading branch information
willis102 authored Dec 11, 2019
1 parent cd61c58 commit 3ec86fe
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 9 deletions.
41 changes: 32 additions & 9 deletions shotgun_api3/lib/httplib2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
from .. import six

# import the proper implementation into the module namespace depending on the
# Define all here to keep linters happy. It should be overwritten by the code
# below, but if in the future __all__ is not defined in httplib2 this will keep
# things from breaking.
__all__ = []

# Import the proper implementation into the module namespace depending on the
# current python version. httplib2 supports python 2/3 by forking the code rather
# than with a single cross-compatible module. Rather than modify third party code,
# we'll just import the appropriate branch here.
if six.PY3:
from .python3 import *
from .python3 import socks # ensure include in namespace
import ssl
ssl_error_classes = (ssl.SSLError, ssl.CertificateError)
# Generate ssl_error_classes
import ssl as __ssl
ssl_error_classes = (__ssl.SSLError, __ssl.CertificateError)
del __ssl

# get the python3 fork of httplib2
from . import python3 as __httplib2_compat


else:
from .python2 import *
from .python2 import socks # ensure include in namespace
from .python2 import SSLHandshakeError # TODO: shouldn't rely on this. not public
ssl_error_classes = (SSLHandshakeError,)
# Generate ssl_error_classes
from .python2 import SSLHandshakeError as __SSLHandshakeError # TODO: shouldn't rely on this. not public
ssl_error_classes = (__SSLHandshakeError,)
del __SSLHandshakeError

# get the python2 fork of httplib2
from . import python2 as __httplib2_compat

# Import all of the httplib2 module. Note that we can't use a star import because
# we need to import *everything*, not just what exists in __all__.
for __name in dir(__httplib2_compat):
globals()[__name] = getattr(__httplib2_compat, __name)
del __httplib2_compat
del __name

# Add ssl_error_classes to __all__
__all__.append("ssl_error_classes")
30 changes: 30 additions & 0 deletions shotgun_api3/lib/sgsix.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

from . import six
import io
import sys

# For python 3, the `file` type no longer exists, and open() returns an
# io.IOBase instance. We add file_types to allow comparison across python
Expand All @@ -55,3 +56,32 @@
else:
from .httplib2 import SSLHandshakeError
ShotgunSSLError = SSLHandshakeError


def normalize_platform(platform, python2=True):
"""
Normalize the return of sys.platform between Python 2 and 3.
On Python 2 on linux hosts, sys.platform was 'linux' appended with the
current kernel version that Python was built on. In Python3, this was
changed and sys.platform now returns 'linux' regardless of the kernel version.
See https://bugs.python.org/issue12326
This function will normalize platform strings to always conform to Python2 or
Python3 behavior.
:param str platform: The platform string to normalize
:param bool python2: The python version behavior to target. If True, a
Python2-style platform string will be returned (i.e. 'linux2'), otherwise
the modern 'linux' platform string will be returned.
:returns: The normalized platform string.
:rtype: str
"""
if python2:
return "linux2" if platform.startswith("linux") else platform
return "linux" if platform.startswith("linux") else platform


# sgsix.platform will mimick the python2 sys.platform behavior to ensure
# compatibility with existing comparisons and dict keys.
platform = normalize_platform(sys.platform)
41 changes: 41 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import os
from .mock import patch, MagicMock
import time
import types
import uuid
import unittest
from shotgun_api3.lib.six.moves import range, urllib
Expand Down Expand Up @@ -2767,6 +2768,46 @@ def test_modify_visibility(self):
)


class TestLibImports(base.LiveTestBase):
"""
Ensure that included modules are importable and that the correct version is
present.
"""

def test_import_httplib(self):
"""
Ensure that httplib2 is importable and objects are available
This is important, because httplib2 imports switch between
the Python 2 and 3 compatible versions, and the module imports are
proxied to allow this.
"""
from shotgun_api3.lib import httplib2
# Ensure that Http object is available. This is a good indication that
# the httplib2 module contents are importable.
self.assertTrue(hasattr(httplib2, "Http"))
self.assertTrue(isinstance(httplib2.Http, object))

# Ensure that the version of httplib2 compatible with the current Python
# version was imported.
# (The last module name for __module__ should be either python2 or
# python3, depending on what has been imported. Make sure we got the
# right one.)
httplib2_compat_version = httplib2.Http.__module__.split(".")[-1]
if six.PY2:
self.assertEquals(httplib2_compat_version, "python2")
elif six.PY3:
self.assertTrue(httplib2_compat_version, "python3")

# Ensure that socks submodule is present and importable using a from
# import -- this is a good indication that external httplib2 imports
# from shotgun_api3 will work as expected.
from shotgun_api3.lib.httplib2 import socks
self.assertTrue(isinstance(socks, types.ModuleType))
# Make sure that objects in socks are available as expected
self.assertTrue(hasattr(socks, "HTTPError"))


def _has_unicode(data):
for k, v in data.items():
if isinstance(k, six.text_type):
Expand Down

0 comments on commit 3ec86fe

Please sign in to comment.