From 0904162707b8493c85f3d16cb0afe1fe51fbf3ca Mon Sep 17 00:00:00 2001 From: hiroshi seki Date: Mon, 7 May 2018 15:48:34 +0900 Subject: [PATCH 1/5] 2to3 --- shotgun_api3/__init__.py | 4 +- shotgun_api3/lib/httplib2/__init__.py | 181 +- shotgun_api3/lib/httplib2/iri2uri.py | 42 +- shotgun_api3/lib/httplib2/socks.py | 2 +- shotgun_api3/lib/mimetypes.py | 34 +- shotgun_api3/lib/mockgun/mockgun.py | 18 +- shotgun_api3/lib/mockgun/schema.py | 2 +- shotgun_api3/lib/simplejson/__init__.py | 6 +- shotgun_api3/lib/simplejson/decoder.py | 18 +- shotgun_api3/lib/simplejson/encoder.py | 45 +- shotgun_api3/lib/simplejson/ordered_dict.py | 8 +- shotgun_api3/lib/simplejson/tool.py | 2 +- shotgun_api3/lib/xmlrpclib.py | 24 +- shotgun_api3/shotgun.py | 146 +- tests/base.py | 10 +- tests/dummy_data.py | 1708 +++++++++---------- tests/httplib2test.py | 220 +-- tests/mock.py | 18 +- tests/test_api.py | 64 +- tests/test_api_long.py | 14 +- tests/test_client.py | 26 +- tests/test_mockgun.py | 10 +- tests/tests_proxy.py | 2 +- tests/tests_unit.py | 66 +- 24 files changed, 1336 insertions(+), 1334 deletions(-) diff --git a/shotgun_api3/__init__.py b/shotgun_api3/__init__.py index 4f0890628..e62c17e95 100644 --- a/shotgun_api3/__init__.py +++ b/shotgun_api3/__init__.py @@ -1,6 +1,6 @@ -from shotgun import (Shotgun, ShotgunError, ShotgunFileDownloadError, Fault, +from .shotgun import (Shotgun, ShotgunError, ShotgunFileDownloadError, Fault, AuthenticationFault, MissingTwoFactorAuthenticationFault, UserCredentialsNotAllowedForSSOAuthenticationFault, ProtocolError, ResponseError, Error, __version__) -from shotgun import SG_TIMEZONE as sg_timezone +from .shotgun import SG_TIMEZONE as sg_timezone diff --git a/shotgun_api3/lib/httplib2/__init__.py b/shotgun_api3/lib/httplib2/__init__.py index 19e7cff11..a601ff797 100644 --- a/shotgun_api3/lib/httplib2/__init__.py +++ b/shotgun_api3/lib/httplib2/__init__.py @@ -1,4 +1,4 @@ -from __future__ import generators + """ httplib2 @@ -27,15 +27,15 @@ import re import sys import email -import email.Utils -import email.Message -import email.FeedParser -import StringIO +import email.utils +import email.message +import email.parser +import io import gzip import zlib -import httplib -import urlparse -import urllib +import http.client +import urllib.parse +import urllib.request, urllib.parse, urllib.error import base64 import os import copy @@ -59,7 +59,7 @@ from httplib2 import socks except ImportError: try: - import socks + from . import socks except (ImportError, AttributeError): socks = None @@ -88,11 +88,11 @@ def _ssl_wrap_socket(sock, key_file, cert_file, "the ssl module installed. To avoid this error, install " "the ssl module, or explicity disable validation.") ssl_sock = socket.ssl(sock, key_file, cert_file) - return httplib.FakeSocket(sock, ssl_sock) + return http.client.FakeSocket(sock, ssl_sock) if sys.version_info >= (2,3): - from iri2uri import iri2uri + from .iri2uri import iri2uri else: def iri2uri(uri): return uri @@ -126,11 +126,11 @@ def sorted(seq): def HTTPResponse__getheaders(self): """Return list of (header, value) tuples.""" if self.msg is None: - raise httplib.ResponseNotReady() - return self.msg.items() + raise http.client.ResponseNotReady() + return list(self.msg.items()) -if not hasattr(httplib.HTTPResponse, 'getheaders'): - httplib.HTTPResponse.getheaders = HTTPResponse__getheaders +if not hasattr(http.client.HTTPResponse, 'getheaders'): + http.client.HTTPResponse.getheaders = HTTPResponse__getheaders # All exceptions raised here derive from HttpLib2Error class HttpLib2Error(Exception): pass @@ -200,7 +200,7 @@ def __init__(self, desc, host, cert): def _get_end2end_headers(response): hopbyhop = list(HOP_BY_HOP) hopbyhop.extend([x.strip() for x in response.get('connection', '').split(',')]) - return [header for header in response.keys() if header not in hopbyhop] + return [header for header in list(response.keys()) if header not in hopbyhop] URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") @@ -248,7 +248,7 @@ def safename(filename): filename = filename.encode('idna') except UnicodeError: pass - if isinstance(filename,unicode): + if isinstance(filename,str): filename=filename.encode('utf-8') filemd5 = _md5(filename).hexdigest() filename = re_url_scheme.sub("", filename) @@ -261,11 +261,11 @@ def safename(filename): NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+') def _normalize_headers(headers): - return dict([ (key.lower(), NORMALIZE_SPACE.sub(value, ' ').strip()) for (key, value) in headers.iteritems()]) + return dict([ (key.lower(), NORMALIZE_SPACE.sub(value, ' ').strip()) for (key, value) in headers.items()]) def _parse_cache_control(headers): retval = {} - if headers.has_key('cache-control'): + if 'cache-control' in headers: parts = headers['cache-control'].split(',') parts_with_args = [tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=")] parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")] @@ -290,7 +290,7 @@ def _parse_www_authenticate(headers, headername='www-authenticate'): """Returns a dictionary of dictionaries, one dict per auth_scheme.""" retval = {} - if headers.has_key(headername): + if headername in headers: try: authenticate = headers[headername].strip() @@ -350,39 +350,39 @@ def _entry_disposition(response_headers, request_headers): cc = _parse_cache_control(request_headers) cc_response = _parse_cache_control(response_headers) - if request_headers.has_key('pragma') and request_headers['pragma'].lower().find('no-cache') != -1: + if 'pragma' in request_headers and request_headers['pragma'].lower().find('no-cache') != -1: retval = "TRANSPARENT" if 'cache-control' not in request_headers: request_headers['cache-control'] = 'no-cache' - elif cc.has_key('no-cache'): + elif 'no-cache' in cc: retval = "TRANSPARENT" - elif cc_response.has_key('no-cache'): + elif 'no-cache' in cc_response: retval = "STALE" - elif cc.has_key('only-if-cached'): + elif 'only-if-cached' in cc: retval = "FRESH" - elif response_headers.has_key('date'): - date = calendar.timegm(email.Utils.parsedate_tz(response_headers['date'])) + elif 'date' in response_headers: + date = calendar.timegm(email.utils.parsedate_tz(response_headers['date'])) now = time.time() current_age = max(0, now - date) - if cc_response.has_key('max-age'): + if 'max-age' in cc_response: try: freshness_lifetime = int(cc_response['max-age']) except ValueError: freshness_lifetime = 0 - elif response_headers.has_key('expires'): - expires = email.Utils.parsedate_tz(response_headers['expires']) + elif 'expires' in response_headers: + expires = email.utils.parsedate_tz(response_headers['expires']) if None == expires: freshness_lifetime = 0 else: freshness_lifetime = max(0, calendar.timegm(expires) - date) else: freshness_lifetime = 0 - if cc.has_key('max-age'): + if 'max-age' in cc: try: freshness_lifetime = int(cc['max-age']) except ValueError: freshness_lifetime = 0 - if cc.has_key('min-fresh'): + if 'min-fresh' in cc: try: min_fresh = int(cc['min-fresh']) except ValueError: @@ -398,7 +398,7 @@ def _decompressContent(response, new_content): encoding = response.get('content-encoding', None) if encoding in ['gzip', 'deflate']: if encoding == 'gzip': - content = gzip.GzipFile(fileobj=StringIO.StringIO(new_content)).read() + content = gzip.GzipFile(fileobj=io.StringIO(new_content)).read() if encoding == 'deflate': content = zlib.decompress(content) response['content-length'] = str(len(content)) @@ -414,11 +414,11 @@ def _updateCache(request_headers, response_headers, content, cache, cachekey): if cachekey: cc = _parse_cache_control(request_headers) cc_response = _parse_cache_control(response_headers) - if cc.has_key('no-store') or cc_response.has_key('no-store'): + if 'no-store' in cc or 'no-store' in cc_response: cache.delete(cachekey) else: - info = email.Message.Message() - for key, value in response_headers.iteritems(): + info = email.message.Message() + for key, value in response_headers.items(): if key not in ['status','content-encoding','transfer-encoding']: info[key] = value @@ -550,7 +550,7 @@ def request(self, method, request_uri, headers, content, cnonce = None): self.challenge['nc'] += 1 def response(self, response, content): - if not response.has_key('authentication-info'): + if 'authentication-info' not in response: challenge = _parse_www_authenticate(response, 'www-authenticate').get('digest', {}) if 'true' == challenge.get('stale'): self.challenge['nonce'] = challenge['nonce'] @@ -559,7 +559,7 @@ def response(self, response, content): else: updated_challenge = _parse_www_authenticate(response, 'authentication-info').get('digest', {}) - if updated_challenge.has_key('nextnonce'): + if 'nextnonce' in updated_challenge: self.challenge['nonce'] = updated_challenge['nextnonce'] self.challenge['nc'] = 1 return False @@ -651,7 +651,7 @@ def request(self, method, request_uri, headers, content): class GoogleLoginAuthentication(Authentication): def __init__(self, credentials, host, request_uri, headers, response, content, http): - from urllib import urlencode + from urllib.parse import urlencode Authentication.__init__(self, credentials, host, request_uri, headers, response, content, http) challenge = _parse_www_authenticate(response, 'www-authenticate') service = challenge['googlelogin'].get('service', 'xapi') @@ -829,7 +829,7 @@ def proxy_info_from_url(url, method='http'): """ Construct a ProxyInfo from a URL (such as http_proxy env var) """ - url = urlparse.urlparse(url) + url = urllib.parse.urlparse(url) username = None password = None port = None @@ -861,7 +861,7 @@ def proxy_info_from_url(url, method='http'): ) -class HTTPConnectionWithTimeout(httplib.HTTPConnection): +class HTTPConnectionWithTimeout(http.client.HTTPConnection): """ HTTPConnection subclass that supports timeouts @@ -872,7 +872,7 @@ class HTTPConnectionWithTimeout(httplib.HTTPConnection): """ def __init__(self, host, port=None, strict=None, timeout=None, proxy_info=None): - httplib.HTTPConnection.__init__(self, host, port, strict) + http.client.HTTPConnection.__init__(self, host, port, strict) self.timeout = timeout self.proxy_info = proxy_info @@ -909,25 +909,26 @@ def connect(self): self.sock.settimeout(self.timeout) # End of difference from httplib. if self.debuglevel > 0: - print "connect: (%s, %s) ************" % (self.host, self.port) + print("connect: (%s, %s) ************" % (self.host, self.port)) if use_proxy: - print "proxy: %s ************" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)) + print("proxy: %s ************" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass))) self.sock.connect((self.host, self.port) + sa[2:]) - except socket.error, msg: + except socket.error as _msg: + msg = _msg if self.debuglevel > 0: - print "connect fail: (%s, %s)" % (self.host, self.port) + print("connect fail: (%s, %s)" % (self.host, self.port)) if use_proxy: - print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)) + print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass))) if self.sock: self.sock.close() self.sock = None continue break if not self.sock: - raise socket.error, msg + raise socket.error(msg) -class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): +class HTTPSConnectionWithTimeout(http.client.HTTPSConnection): """ This class allows communication via SSL. @@ -939,7 +940,7 @@ class HTTPSConnectionWithTimeout(httplib.HTTPSConnection): def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=None, proxy_info=None, ca_certs=None, disable_ssl_certificate_validation=False): - httplib.HTTPSConnection.__init__(self, host, port=port, + http.client.HTTPSConnection.__init__(self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict) self.timeout = timeout @@ -1035,9 +1036,9 @@ def connect(self): sock, self.key_file, self.cert_file, self.disable_ssl_certificate_validation, self.ca_certs) if self.debuglevel > 0: - print "connect: (%s, %s)" % (self.host, self.port) + print("connect: (%s, %s)" % (self.host, self.port)) if use_proxy: - print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)) + print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass))) if not self.disable_ssl_certificate_validation: cert = self.sock.getpeercert() hostname = self.host.split(':', 0)[0] @@ -1045,7 +1046,7 @@ def connect(self): raise CertificateHostnameMismatch( 'Server presented certificate that does not match ' 'host %s: %s' % (hostname, cert), hostname, cert) - except ssl_SSLError, e: + except ssl_SSLError as e: if sock: sock.close() if self.sock: @@ -1061,18 +1062,18 @@ def connect(self): raise except (socket.timeout, socket.gaierror): raise - except socket.error, msg: + except socket.error as msg: if self.debuglevel > 0: - print "connect fail: (%s, %s)" % (self.host, self.port) + print("connect fail: (%s, %s)" % (self.host, self.port)) if use_proxy: - print "proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass)) + print("proxy: %s" % str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass))) if self.sock: self.sock.close() self.sock = None continue break if not self.sock: - raise socket.error, msg + raise socket.error(msg) SCHEME_TO_CONNECTION = { 'http': HTTPConnectionWithTimeout, @@ -1106,7 +1107,7 @@ def fixed_fetch(url, payload=None, method="GET", headers={}, validate_certificate=validate_certificate) return fixed_fetch - class AppEngineHttpConnection(httplib.HTTPConnection): + class AppEngineHttpConnection(http.client.HTTPConnection): """Use httplib on App Engine, but compensate for its weirdness. The parameters key_file, cert_file, proxy_info, ca_certs, and @@ -1115,15 +1116,15 @@ class AppEngineHttpConnection(httplib.HTTPConnection): def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=None, proxy_info=None, ca_certs=None, disable_ssl_certificate_validation=False): - httplib.HTTPConnection.__init__(self, host, port=port, + http.client.HTTPConnection.__init__(self, host, port=port, strict=strict, timeout=timeout) - class AppEngineHttpsConnection(httplib.HTTPSConnection): + class AppEngineHttpsConnection(http.client.HTTPSConnection): """Same as AppEngineHttpConnection, but for HTTPS URIs.""" def __init__(self, host, port=None, key_file=None, cert_file=None, strict=None, timeout=None, proxy_info=None, ca_certs=None, disable_ssl_certificate_validation=False): - httplib.HTTPSConnection.__init__(self, host, port=port, + http.client.HTTPSConnection.__init__(self, host, port=port, key_file=key_file, cert_file=cert_file, strict=strict, timeout=timeout) @@ -1188,7 +1189,7 @@ def __init__(self, cache=None, timeout=None, self.connections = {} # The location of the cache, for now a directory # where cached responses are held. - if cache and isinstance(cache, basestring): + if cache and isinstance(cache, str): self.cache = FileCache(cache) else: self.cache = cache @@ -1243,7 +1244,7 @@ def _auth_from_challenge(self, host, request_uri, headers, response, content): challenges = _parse_www_authenticate(response, 'www-authenticate') for cred in self.credentials.iter(host): for scheme in AUTH_SCHEME_ORDER: - if challenges.has_key(scheme): + if scheme in challenges: yield AUTH_SCHEME_CLASSES[scheme](cred, host, request_uri, headers, response, content, self) def add_credentials(self, name, password, domain=""): @@ -1279,7 +1280,7 @@ def _conn_request(self, conn, request_uri, method, body, headers): except ssl_SSLError: conn.close() raise - except socket.error, e: + except socket.error as e: err = 0 if hasattr(e, 'args'): err = getattr(e, 'args')[0] @@ -1287,7 +1288,7 @@ def _conn_request(self, conn, request_uri, method, body, headers): err = e.errno if err == errno.ECONNREFUSED: # Connection refused raise - except httplib.HTTPException: + except http.client.HTTPException: # Just because the server closed the connection doesn't apparently mean # that the server didn't send a response. if hasattr(conn, 'sock') and conn.sock is None: @@ -1304,7 +1305,7 @@ def _conn_request(self, conn, request_uri, method, body, headers): continue try: response = conn.getresponse() - except httplib.BadStatusLine: + except http.client.BadStatusLine: # If we get a BadStatusLine on the first try then that means # the connection just went stale, so retry regardless of the # number of RETRIES set. @@ -1317,7 +1318,7 @@ def _conn_request(self, conn, request_uri, method, body, headers): else: conn.close() raise - except (socket.error, httplib.HTTPException): + except (socket.error, http.client.HTTPException): if i < RETRIES-1: conn.close() conn.connect() @@ -1369,29 +1370,29 @@ def _request(self, conn, host, absolute_uri, request_uri, method, body, headers, # Pick out the location header and basically start from the beginning # remembering first to strip the ETag header and decrement our 'depth' if redirections: - if not response.has_key('location') and response.status != 300: + if 'location' not in response and response.status != 300: raise RedirectMissingLocation( _("Redirected but the response is missing a Location: header."), response, content) # Fix-up relative redirects (which violate an RFC 2616 MUST) - if response.has_key('location'): + if 'location' in response: location = response['location'] (scheme, authority, path, query, fragment) = parse_uri(location) if authority == None: - response['location'] = urlparse.urljoin(absolute_uri, location) + response['location'] = urllib.parse.urljoin(absolute_uri, location) if response.status == 301 and method in ["GET", "HEAD"]: response['-x-permanent-redirect-url'] = response['location'] - if not response.has_key('content-location'): + if 'content-location' not in response: response['content-location'] = absolute_uri _updateCache(headers, response, content, self.cache, cachekey) - if headers.has_key('if-none-match'): + if 'if-none-match' in headers: del headers['if-none-match'] - if headers.has_key('if-modified-since'): + if 'if-modified-since' in headers: del headers['if-modified-since'] if 'authorization' in headers and not self.forward_authorization_headers: del headers['authorization'] - if response.has_key('location'): + if 'location' in response: location = response['location'] old_response = copy.deepcopy(response) - if not old_response.has_key('content-location'): + if 'content-location' not in old_response: old_response['content-location'] = absolute_uri redirect_method = method if response.status in [302, 303]: @@ -1406,7 +1407,7 @@ def _request(self, conn, host, absolute_uri, request_uri, method, body, headers, raise RedirectLimit("Redirected more times than rediection_limit allows.", response, content) elif response.status in [200, 203] and method in ["GET", "HEAD"]: # Don't cache 206's since we aren't going to handle byte range requests - if not response.has_key('content-location'): + if 'content-location' not in response: response['content-location'] = absolute_uri _updateCache(headers, response, content, self.cache, cachekey) @@ -1448,7 +1449,7 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU else: headers = self._normalize_headers(headers) - if not headers.has_key('user-agent'): + if 'user-agent' not in headers: headers['user-agent'] = "Python-httplib2/%s (gzip)" % __version__ uri = iri2uri(uri) @@ -1493,7 +1494,7 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU if 'range' not in headers and 'accept-encoding' not in headers: headers['accept-encoding'] = 'gzip, deflate' - info = email.Message.Message() + info = email.message.Message() cached_value = None if self.cache: cachekey = defrag_uri.encode('utf-8') @@ -1506,7 +1507,7 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU # bug report: http://mail.python.org/pipermail/python-bugs-list/2005-September/030289.html try: info, content = cached_value.split('\r\n\r\n', 1) - feedparser = email.FeedParser.FeedParser() + feedparser = email.parser.FeedParser() feedparser.feed(info) info = feedparser.close() feedparser._parse = None @@ -1517,7 +1518,7 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU else: cachekey = None - if method in self.optimistic_concurrency_methods and self.cache and info.has_key('etag') and not self.ignore_etag and 'if-match' not in headers: + if method in self.optimistic_concurrency_methods and self.cache and 'etag' in info and not self.ignore_etag and 'if-match' not in headers: # http://www.w3.org/1999/04/Editing/ headers['if-match'] = info['etag'] @@ -1538,7 +1539,7 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU break if cached_value and method in ["GET", "HEAD"] and self.cache and 'range' not in headers: - if info.has_key('-x-permanent-redirect-url'): + if '-x-permanent-redirect-url' in info: # Should cached permanent redirects be counted in our redirection count? For now, yes. if redirections <= 0: raise RedirectLimit("Redirected more times than rediection_limit allows.", {}, "") @@ -1568,9 +1569,9 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU return (response, content) if entry_disposition == "STALE": - if info.has_key('etag') and not self.ignore_etag and not 'if-none-match' in headers: + if 'etag' in info and not self.ignore_etag and not 'if-none-match' in headers: headers['if-none-match'] = info['etag'] - if info.has_key('last-modified') and not 'last-modified' in headers: + if 'last-modified' in info and not 'last-modified' in headers: headers['if-modified-since'] = info['last-modified'] elif entry_disposition == "TRANSPARENT": pass @@ -1600,13 +1601,13 @@ def request(self, uri, method="GET", body=None, headers=None, redirections=DEFAU content = new_content else: cc = _parse_cache_control(headers) - if cc.has_key('only-if-cached'): + if 'only-if-cached' in cc: info['status'] = '504' response = Response(info) content = "" else: (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey) - except Exception, e: + except Exception as e: if self.force_exception_to_status_code: if isinstance(e, HttpLib2ErrorWithResponse): response = e.response @@ -1639,7 +1640,7 @@ def _get_proxy_info(self, scheme, authority): """Return a ProxyInfo instance (or None) based on the scheme and authority. """ - hostname, port = urllib.splitport(authority) + hostname, port = urllib.parse.splitport(authority) proxy_info = self.proxy_info if callable(proxy_info): proxy_info = proxy_info(scheme) @@ -1670,19 +1671,19 @@ class Response(dict): def __init__(self, info): # info is either an email.Message or # an httplib.HTTPResponse object. - if isinstance(info, httplib.HTTPResponse): + if isinstance(info, http.client.HTTPResponse): for key, value in info.getheaders(): self[key.lower()] = value self.status = info.status self['status'] = str(self.status) self.reason = info.reason self.version = info.version - elif isinstance(info, email.Message.Message): - for key, value in info.items(): + elif isinstance(info, email.message.Message): + for key, value in list(info.items()): self[key.lower()] = value self.status = int(self['status']) else: - for key, value in info.iteritems(): + for key, value in info.items(): self[key.lower()] = value self.status = int(self.get('status', self.status)) self.reason = self.get('reason', self.reason) @@ -1692,4 +1693,4 @@ def __getattr__(self, name): if name == 'dict': return self else: - raise AttributeError, name + raise AttributeError(name) diff --git a/shotgun_api3/lib/httplib2/iri2uri.py b/shotgun_api3/lib/httplib2/iri2uri.py index d88c91fdf..26fcff281 100644 --- a/shotgun_api3/lib/httplib2/iri2uri.py +++ b/shotgun_api3/lib/httplib2/iri2uri.py @@ -12,7 +12,7 @@ __history__ = """ """ -import urlparse +import urllib.parse # Convert an IRI to a URI following the rules in RFC 3987 @@ -50,6 +50,7 @@ (0x100000, 0x10FFFD), ] + def encode(c): retval = c i = ord(c) @@ -66,16 +67,17 @@ def iri2uri(uri): """Convert an IRI to a URI. Note that IRIs must be passed in a unicode strings. That is, do not utf-8 encode the IRI before passing it into the function.""" - if isinstance(uri ,unicode): - (scheme, authority, path, query, fragment) = urlparse.urlsplit(uri) + if isinstance(uri, str): + (scheme, authority, path, query, fragment) = urllib.parse.urlsplit(uri) authority = authority.encode('idna') # For each character in 'ucschar' or 'iprivate' # 1. encode as utf-8 # 2. then %-encode each octet of that utf-8 - uri = urlparse.urlunsplit((scheme, authority, path, query, fragment)) + uri = urllib.parse.urlunsplit((scheme, authority.decode(), path, query, fragment)) uri = "".join([encode(c) for c in uri]) return uri + if __name__ == "__main__": import unittest @@ -84,27 +86,25 @@ class Test(unittest.TestCase): def test_uris(self): """Test that URIs are invariant under the transformation.""" invariant = [ - u"ftp://ftp.is.co.za/rfc/rfc1808.txt", - u"http://www.ietf.org/rfc/rfc2396.txt", - u"ldap://[2001:db8::7]/c=GB?objectClass?one", - u"mailto:John.Doe@example.com", - u"news:comp.infosystems.www.servers.unix", - u"tel:+1-816-555-1212", - u"telnet://192.0.2.16:80/", - u"urn:oasis:names:specification:docbook:dtd:xml:4.1.2" ] + "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "http://www.ietf.org/rfc/rfc2396.txt", + "ldap://[2001:db8::7]/c=GB?objectClass?one", + "mailto:John.Doe@example.com", + "news:comp.infosystems.www.servers.unix", + "tel:+1-816-555-1212", + "telnet://192.0.2.16:80/", + "urn:oasis:names:specification:docbook:dtd:xml:4.1.2"] for uri in invariant: self.assertEqual(uri, iri2uri(uri)) def test_iri(self): """ Test that the right type of escaping is done for each part of the URI.""" - self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri(u"http://\N{COMET}.com/\N{COMET}")) - self.assertEqual("http://bitworking.org/?fred=%E2%98%84", iri2uri(u"http://bitworking.org/?fred=\N{COMET}")) - self.assertEqual("http://bitworking.org/#%E2%98%84", iri2uri(u"http://bitworking.org/#\N{COMET}")) - self.assertEqual("#%E2%98%84", iri2uri(u"#\N{COMET}")) - self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")) - self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"))) - self.assertNotEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(u"/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode('utf-8'))) + self.assertEqual("http://xn--o3h.com/%E2%98%84", iri2uri("http://\N{COMET}.com/\N{COMET}")) + self.assertEqual("http://bitworking.org/?fred=%E2%98%84", iri2uri("http://bitworking.org/?fred=\N{COMET}")) + self.assertEqual("http://bitworking.org/#%E2%98%84", iri2uri("http://bitworking.org/#\N{COMET}")) + self.assertEqual("#%E2%98%84", iri2uri("#\N{COMET}")) + self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")) + self.assertEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri(iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"))) + self.assertNotEqual("/fred?bar=%E2%98%9A#%E2%98%84", iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode('utf-8'))) unittest.main() - - diff --git a/shotgun_api3/lib/httplib2/socks.py b/shotgun_api3/lib/httplib2/socks.py index 0991f4cf6..bb23fc930 100644 --- a/shotgun_api3/lib/httplib2/socks.py +++ b/shotgun_api3/lib/httplib2/socks.py @@ -399,7 +399,7 @@ def connect(self, destpair): To select the proxy server use setproxy(). """ # Do a minimal input check first - if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (not isinstance(destpair[0], basestring)) or (type(destpair[1]) != int): + if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (not isinstance(destpair[0], str)) or (type(destpair[1]) != int): raise GeneralProxyError((5, _generalerrors[5])) if self.__proxy[0] == PROXY_TYPE_SOCKS5: if self.__proxy[2] != None: diff --git a/shotgun_api3/lib/mimetypes.py b/shotgun_api3/lib/mimetypes.py index a39b543c4..72a1e1951 100644 --- a/shotgun_api3/lib/mimetypes.py +++ b/shotgun_api3/lib/mimetypes.py @@ -26,9 +26,9 @@ import os import sys import posixpath -import urllib +import urllib.request, urllib.parse, urllib.error try: - import _winreg + import winreg except ImportError: _winreg = None @@ -68,9 +68,9 @@ def __init__(self, filenames=(), strict=True): self.suffix_map = suffix_map.copy() self.types_map = ({}, {}) # dict for (non-strict, strict) self.types_map_inv = ({}, {}) - for (ext, type) in types_map.items(): + for (ext, type) in list(types_map.items()): self.add_type(type, ext, True) - for (ext, type) in common_types.items(): + for (ext, type) in list(common_types.items()): self.add_type(type, ext, False) for name in filenames: self.read(name, strict) @@ -111,7 +111,7 @@ def guess_type(self, url, strict=True): Optional `strict' argument when False adds a bunch of commonly found, but non-standard types. """ - scheme, url = urllib.splittype(url) + scheme, url = urllib.parse.splittype(url) if scheme == 'data': # syntax of data URLs: # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data @@ -242,7 +242,7 @@ def enum_types(mimedb): i = 0 while True: try: - ctype = _winreg.EnumKey(mimedb, i) + ctype = winreg.EnumKey(mimedb, i) except EnvironmentError: break else: @@ -251,17 +251,17 @@ def enum_types(mimedb): i += 1 default_encoding = sys.getdefaultencoding() - with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, '') as hkcr: + with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, '') as hkcr: for subkeyname in enum_types(hkcr): try: - with _winreg.OpenKey(hkcr, subkeyname) as subkey: + with winreg.OpenKey(hkcr, subkeyname) as subkey: # Only check file extensions if not subkeyname.startswith("."): continue # raises EnvironmentError if no 'Content Type' value - mimetype, datatype = _winreg.QueryValueEx( + mimetype, datatype = winreg.QueryValueEx( subkey, 'Content Type') - if datatype != _winreg.REG_SZ: + if datatype != winreg.REG_SZ: continue try: mimetype = mimetype.encode(default_encoding) @@ -564,14 +564,14 @@ def _default_mime_types(): """ def usage(code, msg=''): - print USAGE - if msg: print msg + print(USAGE) + if msg: print(msg) sys.exit(code) try: opts, args = getopt.getopt(sys.argv[1:], 'hle', ['help', 'lenient', 'extension']) - except getopt.error, msg: + except getopt.error as msg: usage(1, msg) strict = 1 @@ -586,9 +586,9 @@ def usage(code, msg=''): for gtype in args: if extension: guess = guess_extension(gtype, strict) - if not guess: print "I don't know anything about type", gtype - else: print guess + if not guess: print("I don't know anything about type", gtype) + else: print(guess) else: guess, encoding = guess_type(gtype, strict) - if not guess: print "I don't know anything about type", gtype - else: print 'type:', guess, 'encoding:', encoding + if not guess: print("I don't know anything about type", gtype) + else: print('type:', guess, 'encoding:', encoding) diff --git a/shotgun_api3/lib/mockgun/mockgun.py b/shotgun_api3/lib/mockgun/mockgun.py index d3c73c865..5fd40ad61 100644 --- a/shotgun_api3/lib/mockgun/mockgun.py +++ b/shotgun_api3/lib/mockgun/mockgun.py @@ -242,7 +242,7 @@ def schema_field_read(self, entity_type, field_name=None): if field_name is None: return self._schema[entity_type] else: - return dict((k, v) for k, v in self._schema[entity_type].items() if k == field_name) + return dict((k, v) for k, v in list(self._schema[entity_type].items()) if k == field_name) def find( self, entity_type, filters, fields=None, order=None, filter_operator=None, @@ -282,7 +282,7 @@ def find( results = [ # Apply the filters for every single entities for the given entity type. - row for row in self._db[entity_type].values() + row for row in list(self._db[entity_type].values()) if self._row_matches_filters( entity_type, row, resolved_filters, filter_operator, retired_only ) @@ -392,7 +392,7 @@ def update(self, entity_type, entity_id, data): row = self._db[entity_type][entity_id] self._update_row(entity_type, row, data) - return [dict((field, item) for field, item in row.items() if field in data or field in ("type", "id"))] + return [dict((field, item) for field, item in list(row.items()) if field in data or field in ("type", "id"))] def delete(self, entity_type, entity_id): self._validate_entity_type(entity_type) @@ -433,9 +433,9 @@ def _validate_entity_data(self, entity_type, data): if "id" in data or "type" in data: raise ShotgunError("Can't set id or type on create or update") - self._validate_entity_fields(entity_type, data.keys()) + self._validate_entity_fields(entity_type, list(data.keys())) - for field, item in data.items(): + for field, item in list(data.items()): if item is None: # none is always ok @@ -491,12 +491,12 @@ def _validate_entity_data(self, entity_type, data): "float": float, "checkbox": bool, "percent": int, - "text": basestring, + "text": str, "serializable": dict, "date": datetime.date, "date_time": datetime.datetime, - "list": basestring, - "status_list": basestring, + "list": str, + "status_list": str, "url": dict}[sg_type] except KeyError: raise ShotgunError( @@ -779,7 +779,7 @@ def _rearrange_filters(self, filters): if "filter_operator" not in f or "filters" not in f: raise ShotgunError( "Bad filter operator, requires keys 'filter_operator' and 'filters', " - "found %s" % ", ".join(f.keys()) + "found %s" % ", ".join(list(f.keys())) ) new_filter = [None, f["filter_operator"], f["filters"]] else: diff --git a/shotgun_api3/lib/mockgun/schema.py b/shotgun_api3/lib/mockgun/schema.py index edd4b889a..1f4dbe6ca 100644 --- a/shotgun_api3/lib/mockgun/schema.py +++ b/shotgun_api3/lib/mockgun/schema.py @@ -30,7 +30,7 @@ ----------------------------------------------------------------------------- """ -import cPickle as pickle +import pickle as pickle import os import copy diff --git a/shotgun_api3/lib/simplejson/__init__.py b/shotgun_api3/lib/simplejson/__init__.py index 210b957a9..f456821b0 100644 --- a/shotgun_api3/lib/simplejson/__init__.py +++ b/shotgun_api3/lib/simplejson/__init__.py @@ -108,14 +108,14 @@ from decimal import Decimal -from decoder import JSONDecoder, JSONDecodeError -from encoder import JSONEncoder +from .decoder import JSONDecoder, JSONDecodeError +from .encoder import JSONEncoder def _import_OrderedDict(): import collections try: return collections.OrderedDict except AttributeError: - import ordered_dict + from . import ordered_dict return ordered_dict.OrderedDict OrderedDict = _import_OrderedDict() diff --git a/shotgun_api3/lib/simplejson/decoder.py b/shotgun_api3/lib/simplejson/decoder.py index e5496d6e7..d308e0e3c 100644 --- a/shotgun_api3/lib/simplejson/decoder.py +++ b/shotgun_api3/lib/simplejson/decoder.py @@ -87,8 +87,8 @@ def errmsg(msg, doc, pos, end=None): STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) BACKSLASH = { - '"': u'"', '\\': u'\\', '/': u'/', - 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t', + '"': '"', '\\': '\\', '/': '/', + 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', } DEFAULT_ENCODING = "utf-8" @@ -117,8 +117,8 @@ def py_scanstring(s, end, encoding=None, strict=True, content, terminator = chunk.groups() # Content is contains zero or more unescaped string characters if content: - if not isinstance(content, unicode): - content = unicode(content, encoding) + if not isinstance(content, str): + content = str(content, encoding) _append(content) # Terminator is the end of string, a literal control character, # or a backslash denoting that an escape sequence follows @@ -164,11 +164,11 @@ def py_scanstring(s, end, encoding=None, strict=True, uni2 = int(esc2, 16) uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) next_end += 6 - char = unichr(uni) + char = chr(uni) end = next_end # Append the unescaped character _append(char) - return u''.join(chunks), end + return ''.join(chunks), end # Use speedup if available @@ -177,10 +177,11 @@ def py_scanstring(s, end, encoding=None, strict=True, WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS) WHITESPACE_STR = ' \t\n\r' -def JSONObject((s, end), encoding, strict, scan_once, object_hook, +def JSONObject(xxx_todo_changeme, encoding, strict, scan_once, object_hook, object_pairs_hook, memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR): # Backwards compatibility + (s, end) = xxx_todo_changeme if memo is None: memo = {} memo_get = memo.setdefault @@ -269,7 +270,8 @@ def JSONObject((s, end), encoding, strict, scan_once, object_hook, pairs = object_hook(pairs) return pairs, end -def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): +def JSONArray(xxx_todo_changeme1, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): + (s, end) = xxx_todo_changeme1 values = [] nextchar = s[end:end + 1] if nextchar in _ws: diff --git a/shotgun_api3/lib/simplejson/encoder.py b/shotgun_api3/lib/simplejson/encoder.py index f43f6f430..49603e8ed 100644 --- a/shotgun_api3/lib/simplejson/encoder.py +++ b/shotgun_api3/lib/simplejson/encoder.py @@ -39,7 +39,7 @@ def encode_basestring(s): s = s.decode('utf-8') def replace(match): return ESCAPE_DCT[match.group(0)] - return u'"' + ESCAPE.sub(replace, s) + u'"' + return '"' + ESCAPE.sub(replace, s) + '"' def py_encode_basestring_ascii(s): @@ -160,7 +160,7 @@ def __init__(self, skipkeys=False, ensure_ascii=True, self.allow_nan = allow_nan self.sort_keys = sort_keys self.use_decimal = use_decimal - if isinstance(indent, (int, long)): + if isinstance(indent, int): indent = ' ' * indent self.indent = indent if separators is not None: @@ -200,7 +200,7 @@ def encode(self, o): """ # This is for extremely simple cases and benchmarks. - if isinstance(o, basestring): + if isinstance(o, str): if isinstance(o, str): _encoding = self.encoding if (_encoding is not None @@ -219,7 +219,7 @@ def encode(self, o): if self.ensure_ascii: return ''.join(chunks) else: - return u''.join(chunks) + return ''.join(chunks) def iterencode(self, o, _one_shot=False): """Encode the given object and yield each string @@ -302,7 +302,7 @@ def encode(self, o): if self.ensure_ascii: return ''.join(chunks) else: - return u''.join(chunks) + return ''.join(chunks) def iterencode(self, o, _one_shot=False): chunks = super(JSONEncoderForHTML, self).iterencode(o, _one_shot) @@ -317,10 +317,10 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, _use_decimal, ## HACK: hand-optimized bytecode; turn globals into locals - False=False, - True=True, + _False=False, + _True=True, ValueError=ValueError, - basestring=basestring, + str=str, Decimal=Decimal, dict=dict, float=float, @@ -328,8 +328,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, int=int, isinstance=isinstance, list=list, - long=long, - str=str, + long=int, tuple=tuple, ): @@ -354,18 +353,18 @@ def _iterencode_list(lst, _current_indent_level): first = True for value in lst: if first: - first = False + first = _False else: buf = separator - if isinstance(value, basestring): + if isinstance(value, str): yield buf + _encoder(value) elif value is None: yield buf + 'null' - elif value is True: + elif value is _True: yield buf + 'true' - elif value is False: + elif value is _False: yield buf + 'false' - elif isinstance(value, (int, long)): + elif isinstance(value, int): yield buf + str(value) elif isinstance(value, float): yield buf + _floatstr(value) @@ -408,12 +407,12 @@ def _iterencode_dict(dct, _current_indent_level): item_separator = _item_separator first = True if _sort_keys: - items = dct.items() + items = list(dct.items()) items.sort(key=lambda kv: kv[0]) else: - items = dct.iteritems() + items = iter(dct.items()) for key, value in items: - if isinstance(key, basestring): + if isinstance(key, str): pass # JavaScript is weakly typed for these, so it makes sense to # also allow them. Many encoders seem to do something like this. @@ -425,7 +424,7 @@ def _iterencode_dict(dct, _current_indent_level): key = 'false' elif key is None: key = 'null' - elif isinstance(key, (int, long)): + elif isinstance(key, int): key = str(key) elif _skipkeys: continue @@ -437,7 +436,7 @@ def _iterencode_dict(dct, _current_indent_level): yield item_separator yield _encoder(key) yield _key_separator - if isinstance(value, basestring): + if isinstance(value, str): yield _encoder(value) elif value is None: yield 'null' @@ -445,7 +444,7 @@ def _iterencode_dict(dct, _current_indent_level): yield 'true' elif value is False: yield 'false' - elif isinstance(value, (int, long)): + elif isinstance(value, int): yield str(value) elif isinstance(value, float): yield _floatstr(value) @@ -468,7 +467,7 @@ def _iterencode_dict(dct, _current_indent_level): del markers[markerid] def _iterencode(o, _current_indent_level): - if isinstance(o, basestring): + if isinstance(o, str): yield _encoder(o) elif o is None: yield 'null' @@ -476,7 +475,7 @@ def _iterencode(o, _current_indent_level): yield 'true' elif o is False: yield 'false' - elif isinstance(o, (int, long)): + elif isinstance(o, int): yield str(o) elif isinstance(o, float): yield _floatstr(o) diff --git a/shotgun_api3/lib/simplejson/ordered_dict.py b/shotgun_api3/lib/simplejson/ordered_dict.py index 87ad88824..76bc56672 100644 --- a/shotgun_api3/lib/simplejson/ordered_dict.py +++ b/shotgun_api3/lib/simplejson/ordered_dict.py @@ -66,9 +66,9 @@ def popitem(self, last=True): # Modified from original to support Python 2.4, see # http://code.google.com/p/simplejson/issues/detail?id=53 if last: - key = reversed(self).next() + key = next(reversed(self)) else: - key = iter(self).next() + key = next(iter(self)) value = self.pop(key) return key, value @@ -97,7 +97,7 @@ def keys(self): def __repr__(self): if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) def copy(self): return self.__class__(self) @@ -112,7 +112,7 @@ def fromkeys(cls, iterable, value=None): def __eq__(self, other): if isinstance(other, OrderedDict): return len(self)==len(other) and \ - all(p==q for p, q in zip(self.items(), other.items())) + all(p==q for p, q in zip(list(self.items()), list(other.items()))) return dict.__eq__(self, other) def __ne__(self, other): diff --git a/shotgun_api3/lib/simplejson/tool.py b/shotgun_api3/lib/simplejson/tool.py index 73370db55..967612565 100644 --- a/shotgun_api3/lib/simplejson/tool.py +++ b/shotgun_api3/lib/simplejson/tool.py @@ -29,7 +29,7 @@ def main(): obj = json.load(infile, object_pairs_hook=json.OrderedDict, use_decimal=True) - except ValueError, e: + except ValueError as e: raise SystemExit(e) json.dump(obj, outfile, sort_keys=True, indent=' ', use_decimal=True) outfile.write('\n') diff --git a/shotgun_api3/lib/xmlrpclib.py b/shotgun_api3/lib/xmlrpclib.py index ed2a90c1c..a99a657c2 100644 --- a/shotgun_api3/lib/xmlrpclib.py +++ b/shotgun_api3/lib/xmlrpclib.py @@ -142,15 +142,15 @@ from types import * import socket import errno -import httplib +import http.client # -------------------------------------------------------------------- # Internal stuff try: - unicode + str except NameError: - unicode = None # unicode support not available + str = None # unicode support not available try: import datetime @@ -165,16 +165,16 @@ def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search): # decode non-ascii string (if possible) - if unicode and encoding and is8bit(data): - data = unicode(data, encoding) + if str and encoding and is8bit(data): + data = str(data, encoding) return data -def escape(s, replace=string.replace): +def escape(s, replace=str.replace): s = replace(s, "&", "&") s = replace(s, "<", "<") return replace(s, ">", ">",) -if unicode: +if str: def _stringify(string): # convert to 7-bit ascii if possible try: @@ -188,8 +188,8 @@ def _stringify(string): #__version__ = "1.0.1" # xmlrpc integer limits -MAXINT = 2L**31-1 -MININT = -2L**31 +MAXINT = 2**31-1 +MININT = -2**31 # -------------------------------------------------------------------- # Error constants (from Dan Libby's specification at @@ -290,7 +290,7 @@ def __repr__(self): if _bool_is_builtin: boolean = Boolean = bool # to avoid breaking code which references xmlrpclib.{True,False} - True, False = True, False + _True, _False = True, False else: class Boolean: """Boolean-value wrapper. @@ -318,10 +318,10 @@ def __repr__(self): def __int__(self): return self.value - def __nonzero__(self): + def __bool__(self): return self.value - True, False = Boolean(1), Boolean(0) + _True, _False = Boolean(1), Boolean(0) ## # Map true or false value to XML-RPC boolean values. diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index a3e8c1d20..139cb8ef6 100755 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -30,11 +30,11 @@ """ import base64 -import cookielib # used for attachment upload -import cStringIO # used for attachment upload +import http.cookiejar # used for attachment upload +import io # used for attachment upload import datetime import logging -import mimetools # used for attachment upload +import email # used for attachment upload import os import re import copy @@ -42,19 +42,19 @@ import sys import time import types -import urllib -import urllib2 # used for image upload -import urlparse +import urllib.request, urllib.parse, urllib.error +import urllib.request, urllib.error, urllib.parse # used for image upload +import urllib.parse import shutil # used for attachment download -import httplib # Used for secure file upload. +import http.client # Used for secure file upload. # use relative import for versions >=2.5 and package import for python versions <2.5 if (sys.version_info[0] > 2) or (sys.version_info[0] == 2 and sys.version_info[1] >= 6): - from sg_26 import * + from .sg_26 import * elif (sys.version_info[0] > 2) or (sys.version_info[0] == 2 and sys.version_info[1] >= 5): - from sg_25 import * + from .sg_25 import * else: - from sg_24 import * + from .sg_24 import * # mimetypes imported in version specific imports mimetypes.add_type('video/webm','.webm') # webm and mp4 seem to be missing @@ -83,7 +83,7 @@ """ try: import ssl -except ImportError, e: +except ImportError as e: if "SHOTGUN_FORCE_CERTIFICATE_VALIDATION" in os.environ: raise ImportError("%s. SHOTGUN_FORCE_CERTIFICATE_VALIDATION environment variable prevents " "disabling SSL certificate validation." % e) @@ -554,20 +554,20 @@ def __init__(self, self.base_url = (base_url or "").lower() self.config.scheme, self.config.server, api_base, _, _ = \ - urlparse.urlsplit(self.base_url) + urllib.parse.urlsplit(self.base_url) if self.config.scheme not in ("http", "https"): raise ValueError("base_url must use http or https got '%s'" % self.base_url) - self.config.api_path = urlparse.urljoin(urlparse.urljoin( + self.config.api_path = urllib.parse.urljoin(urllib.parse.urljoin( api_base or "/", self.config.api_ver + "/"), "json") # if the service contains user information strip it out # copied from the xmlrpclib which turned the user:password into # and auth header - auth, self.config.server = urllib.splituser(urlparse.urlsplit(base_url).netloc) + auth, self.config.server = urllib.parse.splituser(urllib.parse.urlsplit(base_url).netloc) if auth: - auth = base64.encodestring(urllib.unquote(auth)) + auth = base64.encodestring(urllib.parse.unquote(auth)) self.config.authorization = "Basic " + auth.strip() # foo:bar@123.456.789.012:3456 @@ -598,7 +598,7 @@ def __init__(self, else: auth_string = "" proxy_addr = "http://%s%s:%d" % (auth_string, self.config.proxy_server, self.config.proxy_port) - self.config.proxy_handler = urllib2.ProxyHandler({self.config.scheme : proxy_addr}) + self.config.proxy_handler = urllib.request.ProxyHandler({self.config.scheme : proxy_addr}) if ensure_ascii: self._json_loads = self._json_loads_ascii @@ -952,7 +952,7 @@ def _construct_read_parameters(self, if order: sort_list = [] for sort in order: - if sort.has_key('column'): + if 'column' in sort: # TODO: warn about deprecation of 'column' param name sort['field_name'] = sort['column'] sort.setdefault("direction", "asc") @@ -1956,7 +1956,7 @@ def schema_field_update(self, entity_type, field_name, properties): "field_name" : field_name, "properties": [ {"property_name" : k, "value" : v} - for k, v in (properties or {}).iteritems() + for k, v in (properties or {}).items() ] } @@ -2127,7 +2127,7 @@ def share_thumbnail(self, entities, thumbnail_path=None, source_entity=None, "filmstrip_thumbnail" : filmstrip_thumbnail, } - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/share_thumbnail", None, None, None)) result = self._send_form(url, params) @@ -2297,7 +2297,7 @@ def _upload_to_storage(self, entity_type, entity_id, path, field_name, display_n # Step 3: create the attachment - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/api_link_file", None, None, None)) params = { @@ -2359,14 +2359,14 @@ def _upload_to_sg(self, entity_type, entity_id, path, field_name, display_name, params.update(self._auth_params()) if is_thumbnail: - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/publish_thumbnail", None, None, None)) params["thumb_image"] = open(path, "rb") if field_name == "filmstrip_thumb_image" or field_name == "filmstrip_image": params["filmstrip"] = True else: - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/upload_file", None, None, None)) if display_name is None: display_name = os.path.basename(path) @@ -2416,7 +2416,7 @@ def _get_attachment_upload_info(self, is_thumbnail, filename, is_multipart_uploa params["multipart_upload"] = is_multipart_upload upload_url = "/upload/api_get_upload_link_info" - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, upload_url, None, None, None)) upload_info = self._send_form(url, params) @@ -2484,7 +2484,7 @@ def download_attachment(self, attachment=False, file_path=None, attachment_id=No if file_path: try: fp = open(file_path, 'wb') - except IOError, e: + except IOError as e: raise IOError("Unable to write Attachment to disk using "\ "file_path. %s" % e) @@ -2497,16 +2497,16 @@ def download_attachment(self, attachment=False, file_path=None, attachment_id=No self.set_up_auth_cookie() try: - request = urllib2.Request(url) + request = urllib.request.Request(url) request.add_header('user-agent', "; ".join(self._user_agents)) - req = urllib2.urlopen(request) + req = urllib.request.urlopen(request) if file_path: shutil.copyfileobj(req, fp) else: attachment = req.read() # 400 [sg] Attachment id doesn't exist or is a local file # 403 [s3] link is invalid - except urllib2.URLError, e: + except urllib.error.URLError as e: if file_path: fp.close() err = "Failed to open %s\n%s" % (url, e) @@ -2549,14 +2549,14 @@ def set_up_auth_cookie(self): used internally for downloading attachments from the Shotgun server. """ sid = self.get_session_token() - cj = cookielib.LWPCookieJar() - c = cookielib.Cookie('0', '_session_id', sid, None, False, + cj = http.cookiejar.LWPCookieJar() + c = http.cookiejar.Cookie('0', '_session_id', sid, None, False, self.config.server, False, False, "/", True, False, None, True, None, None, {}) cj.set_cookie(c) - cookie_handler = urllib2.HTTPCookieProcessor(cj) + cookie_handler = urllib.request.HTTPCookieProcessor(cj) opener = self._build_opener(cookie_handler) - urllib2.install_opener(opener) + urllib.request.install_opener(opener) def get_attachment_download_url(self, attachment): """ @@ -2600,8 +2600,8 @@ def get_attachment_download_url(self, attachment): "dict, int, or NoneType. Instead got %s" % type(attachment)) if attachment_id: - url = urlparse.urlunparse((self.config.scheme, self.config.server, - "/file_serve/attachment/%s" % urllib.quote(str(attachment_id)), + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, + "/file_serve/attachment/%s" % urllib.parse.quote(str(attachment_id)), None, None, None)) return url @@ -2840,7 +2840,7 @@ def text_search(self, text, entity_types, project_ids=None, limit=None): raise ValueError("entity_types parameter must be a dictionary") api_entity_types = {} - for (entity_type, filter_list) in entity_types.iteritems(): + for (entity_type, filter_list) in entity_types.items(): if isinstance(filter_list, (list, tuple)): resolved_filters = _translate_filters(filter_list, filter_operator=None) @@ -3043,7 +3043,7 @@ def _build_opener(self, handler): handlers.append(self.config.proxy_handler) handlers.append(handler) - return urllib2.build_opener(*handlers) + return urllib.request.build_opener(*handlers) def _turn_off_ssl_validation(self): """ @@ -3097,7 +3097,7 @@ def _call_rpc(self, method, params, include_auth_params=True, first=False): LOG.debug("Completed rpc call to %s" % (method)) try: self._parse_http_status(http_status) - except ProtocolError, e: + except ProtocolError as e: e.headers = resp_headers # 403 is returned with custom error page when api access is blocked if e.errcode == 403: @@ -3209,7 +3209,7 @@ def _encode_payload(self, payload): """ wire = json.dumps(payload, ensure_ascii=False) - if isinstance(wire, unicode): + if isinstance(wire, str): return wire.encode("utf-8") return wire @@ -3235,7 +3235,7 @@ def _make_call(self, verb, path, body, headers): attempt += 1 try: return self._http_request(verb, path, body, req_headers) - except SSLHandshakeError, e: + except SSLHandshakeError as e: # Test whether the exception is due to the fact that this is an older version of # Python that cannot validate certificates encrypted with SHA-2. If it is, then # fall back on disabling the certificate validation and try again - unless the @@ -3277,7 +3277,7 @@ def _http_request(self, verb, path, body, headers): """ Make the actual HTTP request. """ - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, path, None, None, None)) LOG.debug("Request is %s:%s" % (verb, url)) LOG.debug("Request headers are %s" % headers) @@ -3290,7 +3290,7 @@ def _http_request(self, verb, path, body, headers): http_status = (resp.status, resp.reason) resp_headers = dict( (k.lower(), v) - for k, v in resp.iteritems() + for k, v in resp.items() ) resp_body = content @@ -3353,7 +3353,7 @@ def _json_loads_ascii(self, body): def _decode_list(lst): newlist = [] for i in lst: - if isinstance(i, unicode): + if isinstance(i, str): i = i.encode('utf-8') elif isinstance(i, list): i = _decode_list(i) @@ -3362,10 +3362,10 @@ def _decode_list(lst): def _decode_dict(dct): newdict = {} - for k, v in dct.iteritems(): - if isinstance(k, unicode): + for k, v in dct.items(): + if isinstance(k, str): k = k.encode('utf-8') - if isinstance(v, unicode): + if isinstance(v, str): v = v.encode('utf-8') elif isinstance(v, list): v = _decode_list(v) @@ -3417,7 +3417,7 @@ def _visit_data(self, data, visitor): if isinstance(data, dict): return dict( (k, recursive(v, visitor)) - for k, v in data.iteritems() + for k, v in data.items() ) return visitor(data) @@ -3482,7 +3482,7 @@ def _transform_inbound(self, data): _change_tz = None def _inbound_visitor(value): - if isinstance(value, basestring): + if isinstance(value, str): if len(value) == 20 and self._DATE_TIME_PATTERN.match(value): try: # strptime was not on datetime in python2.4 @@ -3527,7 +3527,7 @@ def _close_connection(self): if self._connection is None: return - for conn in self._connection.connections.values(): + for conn in list(self._connection.connections.values()): try: conn.close() except Exception: @@ -3564,12 +3564,12 @@ def _parse_records(self, records): continue # iterate over each item and check each field for possible injection - for k, v in rec.iteritems(): + for k, v in rec.items(): if not v: continue # Check for html entities in strings - if isinstance(v, types.StringTypes): + if isinstance(v, (str,)): rec[k] = rec[k].replace('<', '<') # check for thumbnail for older version (<3.3.0) of shotgun @@ -3602,8 +3602,8 @@ def _build_thumb_url(self, entity_type, entity_id): # curl "https://foo.com/upload/get_thumbnail_url?entity_type=Version&entity_id=1" # 1 # /files/0000/0000/0012/232/shot_thumb.jpg.jpg - entity_info = {'e_type':urllib.quote(entity_type), - 'e_id':urllib.quote(str(entity_id))} + entity_info = {'e_type':urllib.parse.quote(entity_type), + 'e_id':urllib.parse.quote(str(entity_id))} url = ("/upload/get_thumbnail_url?" + "entity_type=%(e_type)s&entity_id=%(e_id)s" % entity_info) @@ -3617,7 +3617,7 @@ def _build_thumb_url(self, entity_type, entity_id): raise ShotgunError(thumb_url) if code == 1: - return urlparse.urlunparse((self.config.scheme, + return urllib.parse.urlunparse((self.config.scheme, self.config.server, thumb_url.strip(), None, None, None)) # Comments in prev version said we can get this sometimes. @@ -3634,7 +3634,7 @@ def _dict_to_list(self, d, key_name="field_name", value_name="value", extra_data [{'field_name': 'foo', 'value': 'bar', 'thing1': 'value1'}] """ ret = [] - for k, v in (d or {}).iteritems(): + for k, v in (d or {}).items(): d = {key_name: k, value_name: v} d.update((extra_data or {}).get(k, {})) ret.append(d) @@ -3647,7 +3647,7 @@ def _dict_to_extra_data(self, d, key_name="value"): e.g. d {'foo' : 'bar'} changed to {'foo': {"value": 'bar'}] """ - return dict([(k, {key_name: v}) for (k,v) in (d or {}).iteritems()]) + return dict([(k, {key_name: v}) for (k,v) in (d or {}).items()]) def _upload_file_to_storage(self, path, storage_url): """ @@ -3720,7 +3720,7 @@ def _get_upload_part_link(self, upload_info, filename, part_number): "part_number": part_number } - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/api_get_upload_link_for_part", None, None, None)) result = self._send_form(url, params) @@ -3745,15 +3745,15 @@ def _upload_data_to_storage(self, data, content_type, size, storage_url): :rtype: str """ try: - opener = urllib2.build_opener(urllib2.HTTPHandler) + opener = urllib.request.build_opener(urllib.request.HTTPHandler) - request = urllib2.Request(storage_url, data=data) + request = urllib.request.Request(storage_url, data=data) request.add_header("Content-Type", content_type) request.add_header("Content-Length", size) request.get_method = lambda: "PUT" result = opener.open(request) etag = result.info().getheader("ETag") - except urllib2.HTTPError, e: + except urllib.error.HTTPError as e: if e.code == 500: raise ShotgunError("Server encountered an internal error.\n%s\n%s\n\n" % (storage_url, e)) else: @@ -3779,7 +3779,7 @@ def _complete_multipart_upload(self, upload_info, filename, etags): "etags": ",".join(etags) } - url = urlparse.urlunparse((self.config.scheme, self.config.server, + url = urllib.parse.urlunparse((self.config.scheme, self.config.server, "/upload/api_complete_multipart_upload", None, None, None)) result = self._send_form(url, params) @@ -3806,7 +3806,7 @@ def _send_form(self, url, params): resp = opener.open(url, params) result = resp.read() # response headers are in str(resp.info()).splitlines() - except urllib2.HTTPError, e: + except urllib.error.HTTPError as e: if e.code == 500: raise ShotgunError("Server encountered an internal error. " "\n%s\n(%s)\n%s\n\n" % (url, self._sanitize_auth_params(params), e)) @@ -3816,13 +3816,13 @@ def _send_form(self, url, params): return result -class CACertsHTTPSConnection(httplib.HTTPConnection): +class CACertsHTTPSConnection(http.client.HTTPConnection): """" This class allows to create an HTTPS connection that uses the custom certificates passed in. """ - default_port = httplib.HTTPS_PORT + default_port = http.client.HTTPS_PORT def __init__(self, *args, **kwargs): """ @@ -3832,11 +3832,11 @@ def __init__(self, *args, **kwargs): """ # Pop that argument, self.__ca_certs = kwargs.pop("ca_certs") - httplib.HTTPConnection.__init__(self, *args, **kwargs) + http.client.HTTPConnection.__init__(self, *args, **kwargs) def connect(self): "Connect to a host on a given (SSL) port." - httplib.HTTPConnection.connect(self) + http.client.HTTPConnection.connect(self) # Now that the regular HTTP socket has been created, wrap it with our SSL certs. self.sock = ssl.wrap_socket( self.sock, @@ -3845,12 +3845,12 @@ def connect(self): ) -class CACertsHTTPSHandler(urllib2.HTTPSHandler): +class CACertsHTTPSHandler(urllib.request.HTTPSHandler): """ Handler that ensures https connections are created with the custom CA certs. """ def __init__(self, cacerts): - urllib2.HTTPSHandler.__init__(self) + urllib.request.HTTPSHandler.__init__(self) self.__ca_certs = cacerts def https_open(self, req): @@ -3862,24 +3862,24 @@ def create_https_connection(self, *args, **kwargs): # Helpers from the previous API, left as is. # Based on http://code.activestate.com/recipes/146306/ -class FormPostHandler(urllib2.BaseHandler): +class FormPostHandler(urllib.request.BaseHandler): """ Handler for multipart form data """ - handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first + handler_order = urllib.request.HTTPHandler.handler_order - 10 # needs to run first def http_request(self, request): data = request.get_data() - if data is not None and not isinstance(data, basestring): + if data is not None and not isinstance(data, str): files = [] params = [] - for key, value in data.items(): + for key, value in list(data.items()): if isinstance(value, file): files.append((key, value)) else: params.append((key, value)) if not files: - data = urllib.urlencode(params, True) # sequencing on + data = urllib.parse.urlencode(params, True) # sequencing on else: boundary, data = self.encode(params, files) content_type = 'multipart/form-data; boundary=%s' % boundary @@ -3889,9 +3889,9 @@ def http_request(self, request): def encode(self, params, files, boundary=None, buffer=None): if boundary is None: - boundary = mimetools.choose_boundary() + boundary = email.choose_boundary() if buffer is None: - buffer = cStringIO.StringIO() + buffer = io.StringIO() for (key, value) in params: buffer.write('--%s\r\n' % boundary) buffer.write('Content-Disposition: form-data; name="%s"' % key) diff --git a/tests/base.py b/tests/base.py index 6f86b79d6..8aaa0eb90 100644 --- a/tests/base.py +++ b/tests/base.py @@ -2,10 +2,10 @@ import os import re import unittest -from ConfigParser import ConfigParser +from configparser import ConfigParser -import mock +from . import mock import shotgun_api3 as api from shotgun_api3.shotgun import json @@ -125,7 +125,7 @@ def _mock_http(self, data, headers=None, status=None): if not isinstance(self.sg._http_request, mock.Mock): return - if not isinstance(data, basestring): + if not isinstance(data, str): data = json.dumps(data, ensure_ascii=False, encoding="utf-8") resp_headers = { 'cache-control': 'no-cache', @@ -149,7 +149,7 @@ def _assert_http_method(self, method, params, check_auth=True): """Asserts _http_request is called with the method and params.""" args, _ = self.sg._http_request.call_args arg_body = args[2] - assert isinstance(arg_body, basestring) + assert isinstance(arg_body, str) arg_body = json.loads(arg_body) arg_params = arg_body.get("params") @@ -335,7 +335,7 @@ def _find_or_create_entity(sg, entity_type, data, identifyiers=None): @returns dicitonary of the entity values ''' identifyiers = identifyiers or ['name'] - fields = data.keys() + fields = list(data.keys()) filters = [[key, 'is', data[key]] for key in identifyiers] entity = sg.find_one(entity_type, filters, fields=fields) entity = entity or sg.create(entity_type, data, return_fields=fields) diff --git a/tests/dummy_data.py b/tests/dummy_data.py index 5cd5d1634..5038300cd 100644 --- a/tests/dummy_data.py +++ b/tests/dummy_data.py @@ -3,429 +3,429 @@ NOTE: Mostly abbreviated version of real data returned from the server. """ -schema_entity_read = {u'Version': {u'name': {u'editable': False, u'value': u'Version'}}} +schema_entity_read = {'Version': {'name': {'editable': False, 'value': 'Version'}}} schema_read = { - u'Version' : {u'code': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': True}, - u'name': {u'editable': True, u'value': u'Version Name'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'created_at': {u'data_type': {u'editable': False, u'value': u'date_time'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Date Created'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'created_by': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Created by'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}, - u'description': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Description'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'entity': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Link'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Asset', - u'Scene', - u'Sequence', - u'Shot']}}}, - u'frame_count': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Frame Count'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'frame_range': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Frame Range'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'id': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Id'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'image': {u'data_type': {u'editable': False, u'value': u'image'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Thumbnail'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'notes': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Notes'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Note']}}}, - u'open_notes': {u'data_type': {u'editable': False, - u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Open Notes'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Note']}}}, - u'open_notes_count': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Open Notes Count'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'playlists': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Playlists'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Playlist']}}}, - u'project': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Project'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Project']}}}, - u'sg_department': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'The department the Version was submitted from. This is used to find the latest Version from the same department.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Department'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_first_frame': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, - u'value': u'The first frame number contained in the Version. Used in playback of the movie or frames to calculate the first frame available in the Version.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'First Frame'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_frames_aspect_ratio': {u'data_type': {u'editable': False, - u'value': u'float'}, - u'description': {u'editable': True, - u'value': u'Aspect ratio of the high res frames. Used to format the image correctly for viewing.'}, - u'editable': {u'editable': False, - u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Frames Aspect Ratio'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_frames_have_slate': {u'data_type': {u'editable': False, - u'value': u'checkbox'}, - u'description': {u'editable': True, - u'value': u'Indicates whether the frames have a slate or not. This is used to include or omit the slate from playback.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Frames Have Slate'}, - u'properties': {u'default_value': {u'editable': False, - u'value': False}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_last_frame': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, - u'value': u'The last frame number contained in the Version. Used in playback of the movie or frames to calculate the last frame available in the Version.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Last Frame'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_movie_aspect_ratio': {u'data_type': {u'editable': False, - u'value': u'float'}, - u'description': {u'editable': True, - u'value': u'Aspect ratio of the the movie. Used to format the image correctly for viewing.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Movie Aspect Ratio'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_movie_has_slate': {u'data_type': {u'editable': False, - u'value': u'checkbox'}, - u'description': {u'editable': True, - u'value': u'Indicates whether the movie file has a slate or not. This is used to include or omit the slate from playback.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Movie Has Slate'}, - u'properties': {u'default_value': {u'editable': False, - u'value': False}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_path_to_frames': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'Location of the high res frames on your local filesystem. Used for playback of high resolution frames.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Path to Frames'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_path_to_movie': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'Location of the movie on your local filesystem (not uploaded). Used for playback of lower resolution movie media stored locally.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Path to Movie'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_status_list': {u'data_type': {u'editable': False, - u'value': u'status_list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Status'}, - u'properties': {u'default_value': {u'editable': True, - u'value': u'rev'}, - u'summary_default': {u'editable': True, - u'value': u'status_list'}, - u'valid_values': {u'editable': True, - u'value': [u'na', - u'rev', - u'vwd']}}}, - u'sg_task': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Task'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Task']}}}, - u'sg_uploaded_movie': {u'data_type': {u'editable': False, u'value': u'url'}, - u'description': {u'editable': True, - u'value': u'File field to contain the uploaded movie file. Used for playback of lower resolution movie media stored in Shotgun.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Uploaded Movie'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'open_in_new_window': {u'editable': True, - u'value': True}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_version_type': {u'data_type': {u'editable': False, u'value': u'list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Type'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_values': {u'editable': True, - u'value': []}}}, - u'step_0': {u'data_type': {u'editable': False, u'value': u'pivot_column'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'ALL TASKS'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': False, - u'value': u'none'}}}, - u'tag_list': {u'data_type': {u'editable': False, u'value': u'tag_list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Tags'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Tag']}}}, - u'task_template': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Task Template'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'TaskTemplate']}}}, - u'tasks': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Tasks'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Task']}}}, - u'updated_at': {u'data_type': {u'editable': False, u'value': u'date_time'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Date Updated'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'updated_by': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Updated by'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}, - u'user': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Artist'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}} + 'Version' : {'code': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': True}, + 'name': {'editable': True, 'value': 'Version Name'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'created_at': {'data_type': {'editable': False, 'value': 'date_time'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Date Created'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'created_by': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Created by'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}, + 'description': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Description'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'entity': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Link'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Asset', + 'Scene', + 'Sequence', + 'Shot']}}}, + 'frame_count': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Frame Count'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'frame_range': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Frame Range'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'id': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Id'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'image': {'data_type': {'editable': False, 'value': 'image'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Thumbnail'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'notes': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Notes'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Note']}}}, + 'open_notes': {'data_type': {'editable': False, + 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Open Notes'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Note']}}}, + 'open_notes_count': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Open Notes Count'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'playlists': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Playlists'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Playlist']}}}, + 'project': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Project'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Project']}}}, + 'sg_department': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'The department the Version was submitted from. This is used to find the latest Version from the same department.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Department'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_first_frame': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, + 'value': 'The first frame number contained in the Version. Used in playback of the movie or frames to calculate the first frame available in the Version.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'First Frame'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_frames_aspect_ratio': {'data_type': {'editable': False, + 'value': 'float'}, + 'description': {'editable': True, + 'value': 'Aspect ratio of the high res frames. Used to format the image correctly for viewing.'}, + 'editable': {'editable': False, + 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Frames Aspect Ratio'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_frames_have_slate': {'data_type': {'editable': False, + 'value': 'checkbox'}, + 'description': {'editable': True, + 'value': 'Indicates whether the frames have a slate or not. This is used to include or omit the slate from playback.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Frames Have Slate'}, + 'properties': {'default_value': {'editable': False, + 'value': False}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_last_frame': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, + 'value': 'The last frame number contained in the Version. Used in playback of the movie or frames to calculate the last frame available in the Version.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Last Frame'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_movie_aspect_ratio': {'data_type': {'editable': False, + 'value': 'float'}, + 'description': {'editable': True, + 'value': 'Aspect ratio of the the movie. Used to format the image correctly for viewing.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Movie Aspect Ratio'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_movie_has_slate': {'data_type': {'editable': False, + 'value': 'checkbox'}, + 'description': {'editable': True, + 'value': 'Indicates whether the movie file has a slate or not. This is used to include or omit the slate from playback.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Movie Has Slate'}, + 'properties': {'default_value': {'editable': False, + 'value': False}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_path_to_frames': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'Location of the high res frames on your local filesystem. Used for playback of high resolution frames.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Path to Frames'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_path_to_movie': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'Location of the movie on your local filesystem (not uploaded). Used for playback of lower resolution movie media stored locally.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Path to Movie'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_status_list': {'data_type': {'editable': False, + 'value': 'status_list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Status'}, + 'properties': {'default_value': {'editable': True, + 'value': 'rev'}, + 'summary_default': {'editable': True, + 'value': 'status_list'}, + 'valid_values': {'editable': True, + 'value': ['na', + 'rev', + 'vwd']}}}, + 'sg_task': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Task'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Task']}}}, + 'sg_uploaded_movie': {'data_type': {'editable': False, 'value': 'url'}, + 'description': {'editable': True, + 'value': 'File field to contain the uploaded movie file. Used for playback of lower resolution movie media stored in Shotgun.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Uploaded Movie'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'open_in_new_window': {'editable': True, + 'value': True}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_version_type': {'data_type': {'editable': False, 'value': 'list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Type'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_values': {'editable': True, + 'value': []}}}, + 'step_0': {'data_type': {'editable': False, 'value': 'pivot_column'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'ALL TASKS'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': False, + 'value': 'none'}}}, + 'tag_list': {'data_type': {'editable': False, 'value': 'tag_list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Tags'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Tag']}}}, + 'task_template': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Task Template'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['TaskTemplate']}}}, + 'tasks': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Tasks'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Task']}}}, + 'updated_at': {'data_type': {'editable': False, 'value': 'date_time'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Date Updated'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'updated_by': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Updated by'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}, + 'user': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Artist'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}} }} @@ -435,442 +435,442 @@ -schema_field_read_version = {u'code': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': True}, - u'name': {u'editable': True, u'value': u'Version Name'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'created_at': {u'data_type': {u'editable': False, u'value': u'date_time'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Date Created'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'created_by': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Created by'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}, - u'description': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Description'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'entity': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Link'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Asset', - u'Scene', - u'Sequence', - u'Shot']}}}, - u'frame_count': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Frame Count'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'frame_range': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Frame Range'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'id': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Id'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'image': {u'data_type': {u'editable': False, u'value': u'image'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Thumbnail'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'notes': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Notes'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Note']}}}, - u'open_notes': {u'data_type': {u'editable': False, - u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Open Notes'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Note']}}}, - u'open_notes_count': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Open Notes Count'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'playlists': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Playlists'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Playlist']}}}, - u'project': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Project'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Project']}}}, - u'sg_department': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'The department the Version was submitted from. This is used to find the latest Version from the same department.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Department'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_first_frame': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, - u'value': u'The first frame number contained in the Version. Used in playback of the movie or frames to calculate the first frame available in the Version.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'First Frame'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_frames_aspect_ratio': {u'data_type': {u'editable': False, - u'value': u'float'}, - u'description': {u'editable': True, - u'value': u'Aspect ratio of the high res frames. Used to format the image correctly for viewing.'}, - u'editable': {u'editable': False, - u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Frames Aspect Ratio'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_frames_have_slate': {u'data_type': {u'editable': False, - u'value': u'checkbox'}, - u'description': {u'editable': True, - u'value': u'Indicates whether the frames have a slate or not. This is used to include or omit the slate from playback.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Frames Have Slate'}, - u'properties': {u'default_value': {u'editable': False, - u'value': False}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_last_frame': {u'data_type': {u'editable': False, u'value': u'number'}, - u'description': {u'editable': True, - u'value': u'The last frame number contained in the Version. Used in playback of the movie or frames to calculate the last frame available in the Version.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Last Frame'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_movie_aspect_ratio': {u'data_type': {u'editable': False, - u'value': u'float'}, - u'description': {u'editable': True, - u'value': u'Aspect ratio of the the movie. Used to format the image correctly for viewing.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, - u'value': False}, - u'name': {u'editable': True, - u'value': u'Movie Aspect Ratio'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_movie_has_slate': {u'data_type': {u'editable': False, - u'value': u'checkbox'}, - u'description': {u'editable': True, - u'value': u'Indicates whether the movie file has a slate or not. This is used to include or omit the slate from playback.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Movie Has Slate'}, - u'properties': {u'default_value': {u'editable': False, - u'value': False}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_path_to_frames': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'Location of the high res frames on your local filesystem. Used for playback of high resolution frames.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Path to Frames'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_path_to_movie': {u'data_type': {u'editable': False, u'value': u'text'}, - u'description': {u'editable': True, - u'value': u'Location of the movie on your local filesystem (not uploaded). Used for playback of lower resolution movie media stored locally.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Path to Movie'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_status_list': {u'data_type': {u'editable': False, - u'value': u'status_list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Status'}, - u'properties': {u'default_value': {u'editable': True, - u'value': u'rev'}, - u'summary_default': {u'editable': True, - u'value': u'status_list'}, - u'valid_values': {u'editable': True, - u'value': [u'na', - u'rev', - u'vwd']}}}, - u'sg_task': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Task'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Task']}}}, - u'sg_uploaded_movie': {u'data_type': {u'editable': False, u'value': u'url'}, - u'description': {u'editable': True, - u'value': u'File field to contain the uploaded movie file. Used for playback of lower resolution movie media stored in Shotgun.'}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, - u'value': u'Uploaded Movie'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'open_in_new_window': {u'editable': True, - u'value': True}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'sg_version_type': {u'data_type': {u'editable': False, u'value': u'list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Type'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_values': {u'editable': True, - u'value': []}}}, - u'step_0': {u'data_type': {u'editable': False, u'value': u'pivot_column'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'ALL TASKS'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': False, - u'value': u'none'}}}, - u'tag_list': {u'data_type': {u'editable': False, u'value': u'tag_list'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Tags'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Tag']}}}, - u'task_template': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, - u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Task Template'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'TaskTemplate']}}}, - u'tasks': {u'data_type': {u'editable': False, u'value': u'multi_entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Tasks'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'Task']}}}, - u'updated_at': {u'data_type': {u'editable': False, u'value': u'date_time'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Date Updated'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}}}, - u'updated_by': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': False}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Updated by'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}, - u'user': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Artist'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}} +schema_field_read_version = {'code': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': True}, + 'name': {'editable': True, 'value': 'Version Name'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'created_at': {'data_type': {'editable': False, 'value': 'date_time'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Date Created'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'created_by': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Created by'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}, + 'description': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Description'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'entity': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Link'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Asset', + 'Scene', + 'Sequence', + 'Shot']}}}, + 'frame_count': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Frame Count'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'frame_range': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Frame Range'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'id': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Id'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'image': {'data_type': {'editable': False, 'value': 'image'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Thumbnail'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'notes': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Notes'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Note']}}}, + 'open_notes': {'data_type': {'editable': False, + 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Open Notes'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Note']}}}, + 'open_notes_count': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Open Notes Count'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'playlists': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Playlists'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Playlist']}}}, + 'project': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Project'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Project']}}}, + 'sg_department': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'The department the Version was submitted from. This is used to find the latest Version from the same department.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Department'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_first_frame': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, + 'value': 'The first frame number contained in the Version. Used in playback of the movie or frames to calculate the first frame available in the Version.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'First Frame'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_frames_aspect_ratio': {'data_type': {'editable': False, + 'value': 'float'}, + 'description': {'editable': True, + 'value': 'Aspect ratio of the high res frames. Used to format the image correctly for viewing.'}, + 'editable': {'editable': False, + 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Frames Aspect Ratio'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_frames_have_slate': {'data_type': {'editable': False, + 'value': 'checkbox'}, + 'description': {'editable': True, + 'value': 'Indicates whether the frames have a slate or not. This is used to include or omit the slate from playback.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Frames Have Slate'}, + 'properties': {'default_value': {'editable': False, + 'value': False}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_last_frame': {'data_type': {'editable': False, 'value': 'number'}, + 'description': {'editable': True, + 'value': 'The last frame number contained in the Version. Used in playback of the movie or frames to calculate the last frame available in the Version.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Last Frame'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_movie_aspect_ratio': {'data_type': {'editable': False, + 'value': 'float'}, + 'description': {'editable': True, + 'value': 'Aspect ratio of the the movie. Used to format the image correctly for viewing.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, + 'value': False}, + 'name': {'editable': True, + 'value': 'Movie Aspect Ratio'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_movie_has_slate': {'data_type': {'editable': False, + 'value': 'checkbox'}, + 'description': {'editable': True, + 'value': 'Indicates whether the movie file has a slate or not. This is used to include or omit the slate from playback.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Movie Has Slate'}, + 'properties': {'default_value': {'editable': False, + 'value': False}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_path_to_frames': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'Location of the high res frames on your local filesystem. Used for playback of high resolution frames.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Path to Frames'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_path_to_movie': {'data_type': {'editable': False, 'value': 'text'}, + 'description': {'editable': True, + 'value': 'Location of the movie on your local filesystem (not uploaded). Used for playback of lower resolution movie media stored locally.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Path to Movie'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_status_list': {'data_type': {'editable': False, + 'value': 'status_list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Status'}, + 'properties': {'default_value': {'editable': True, + 'value': 'rev'}, + 'summary_default': {'editable': True, + 'value': 'status_list'}, + 'valid_values': {'editable': True, + 'value': ['na', + 'rev', + 'vwd']}}}, + 'sg_task': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Task'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Task']}}}, + 'sg_uploaded_movie': {'data_type': {'editable': False, 'value': 'url'}, + 'description': {'editable': True, + 'value': 'File field to contain the uploaded movie file. Used for playback of lower resolution movie media stored in Shotgun.'}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, + 'value': 'Uploaded Movie'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'open_in_new_window': {'editable': True, + 'value': True}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'sg_version_type': {'data_type': {'editable': False, 'value': 'list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Type'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_values': {'editable': True, + 'value': []}}}, + 'step_0': {'data_type': {'editable': False, 'value': 'pivot_column'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'ALL TASKS'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': False, + 'value': 'none'}}}, + 'tag_list': {'data_type': {'editable': False, 'value': 'tag_list'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Tags'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Tag']}}}, + 'task_template': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, + 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Task Template'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['TaskTemplate']}}}, + 'tasks': {'data_type': {'editable': False, 'value': 'multi_entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Tasks'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['Task']}}}, + 'updated_at': {'data_type': {'editable': False, 'value': 'date_time'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Date Updated'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}}}, + 'updated_by': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': False}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Updated by'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}, + 'user': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Artist'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}} -schema_field_read_version_user = {u'user': {u'data_type': {u'editable': False, u'value': u'entity'}, - u'description': {u'editable': True, u'value': u''}, - u'editable': {u'editable': False, u'value': True}, - u'entity_type': {u'editable': False, u'value': u'Version'}, - u'mandatory': {u'editable': False, u'value': False}, - u'name': {u'editable': True, u'value': u'Artist'}, - u'properties': {u'default_value': {u'editable': False, - u'value': None}, - u'summary_default': {u'editable': True, - u'value': u'none'}, - u'valid_types': {u'editable': True, - u'value': [u'HumanUser', - u'ApiUser']}}}} +schema_field_read_version_user = {'user': {'data_type': {'editable': False, 'value': 'entity'}, + 'description': {'editable': True, 'value': ''}, + 'editable': {'editable': False, 'value': True}, + 'entity_type': {'editable': False, 'value': 'Version'}, + 'mandatory': {'editable': False, 'value': False}, + 'name': {'editable': True, 'value': 'Artist'}, + 'properties': {'default_value': {'editable': False, + 'value': None}, + 'summary_default': {'editable': True, + 'value': 'none'}, + 'valid_types': {'editable': True, + 'value': ['HumanUser', + 'ApiUser']}}}} \ No newline at end of file diff --git a/tests/httplib2test.py b/tests/httplib2test.py index 394aa876e..1ca87ddd4 100755 --- a/tests/httplib2test.py +++ b/tests/httplib2test.py @@ -17,18 +17,18 @@ import sys import unittest -import httplib +import http.client import httplib2 import os -import urlparse +import urllib.parse import time import base64 -import StringIO +import io # Python 2.3 support if not hasattr(unittest.TestCase, 'assertTrue'): - unittest.TestCase.assertTrue = unittest.TestCase.failUnless - unittest.TestCase.assertFalse = unittest.TestCase.failIf + unittest.TestCase.assertTrue = unittest.TestCase.assertTrue + unittest.TestCase.assertFalse = unittest.TestCase.assertFalse # The test resources base uri base = 'http://bitworking.org/projects/httplib2/test/' @@ -99,15 +99,15 @@ def test(self): self.assertEqual(233, len(httplib2.safename(uri))) # Unicode if sys.version_info >= (2,3): - self.assertEqual( "xn--http,-4y1d.org,fred,a=b,579924c35db315e5a32e3d9963388193", httplib2.safename(u"http://\u2304.org/fred/?a=b")) + self.assertEqual( "xn--http,-4y1d.org,fred,a=b,579924c35db315e5a32e3d9963388193", httplib2.safename("http://\u2304.org/fred/?a=b")) -class _MyResponse(StringIO.StringIO): +class _MyResponse(io.StringIO): def __init__(self, body, **kwargs): - StringIO.StringIO.__init__(self, body) + io.StringIO.__init__(self, body) self.headers = kwargs def iteritems(self): - return self.headers.iteritems() + return iter(self.headers.items()) class _MyHTTPConnection(object): @@ -168,21 +168,21 @@ def testGetUnknownServer(self): def testGetIRI(self): if sys.version_info >= (2,3): - uri = urlparse.urljoin(base, u"reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}") + uri = urllib.parse.urljoin(base, "reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}") (response, content) = self.http.request(uri, "GET") d = self.reflector(content) - self.assertTrue(d.has_key('QUERY_STRING')) + self.assertTrue('QUERY_STRING' in d) self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0) def testGetIsDefaultMethod(self): # Test that GET is the default method - uri = urlparse.urljoin(base, "methods/method_reflector.cgi") + uri = urllib.parse.urljoin(base, "methods/method_reflector.cgi") (response, content) = self.http.request(uri) self.assertEqual(response['x-method'], "GET") def testDifferentMethods(self): # Test that all methods can be used - uri = urlparse.urljoin(base, "methods/method_reflector.cgi") + uri = urllib.parse.urljoin(base, "methods/method_reflector.cgi") for method in ["GET", "PUT", "DELETE", "POST"]: (response, content) = self.http.request(uri, method, body=" ") self.assertEqual(response['x-method'], method) @@ -200,14 +200,14 @@ def testHeadRead(self): def testGetNoCache(self): # Test that can do a GET w/o the cache turned on. http = httplib2.Http() - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(response.previous, None) def testGetOnlyIfCachedCacheHit(self): # Test that can do a GET with cache and 'only-if-cached' - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'}) self.assertEqual(response.fromcache, True) @@ -215,7 +215,7 @@ def testGetOnlyIfCachedCacheHit(self): def testGetOnlyIfCachedCacheMiss(self): # Test that can do a GET with no cache with 'only-if-cached' - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET", headers={'cache-control': 'only-if-cached'}) self.assertEqual(response.fromcache, False) self.assertEqual(response.status, 504) @@ -226,14 +226,14 @@ def testGetOnlyIfCachedNoCacheAtAll(self): # that responds to the 'only-if-cached', so this # test can't really be guaranteed to pass. http = httplib2.Http() - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = http.request(uri, "GET", headers={'cache-control': 'only-if-cached'}) self.assertEqual(response.fromcache, False) self.assertEqual(response.status, 504) def testUserAgent(self): # Test that we provide a default user-agent - uri = urlparse.urljoin(base, "user-agent/test.cgi") + uri = urllib.parse.urljoin(base, "user-agent/test.cgi") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertTrue(content.startswith("Python-httplib2/")) @@ -241,14 +241,14 @@ def testUserAgent(self): def testUserAgentNonDefault(self): # Test that the default user-agent can be over-ridden - uri = urlparse.urljoin(base, "user-agent/test.cgi") + uri = urllib.parse.urljoin(base, "user-agent/test.cgi") (response, content) = self.http.request(uri, "GET", headers={'User-Agent': 'fred/1.0'}) self.assertEqual(response.status, 200) self.assertTrue(content.startswith("fred/1.0")) def testGet300WithLocation(self): # Test the we automatically follow 300 redirects if a Location: header is provided - uri = urlparse.urljoin(base, "300/with-location-header.asis") + uri = urllib.parse.urljoin(base, "300/with-location-header.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(content, "This is the final destination.\n") @@ -265,14 +265,14 @@ def testGet300WithLocation(self): def testGet300WithLocationNoRedirect(self): # Test the we automatically follow 300 redirects if a Location: header is provided self.http.follow_redirects = False - uri = urlparse.urljoin(base, "300/with-location-header.asis") + uri = urllib.parse.urljoin(base, "300/with-location-header.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 300) def testGet300WithoutLocation(self): # Not giving a Location: header in a 300 response is acceptable # In which case we just return the 300 response - uri = urlparse.urljoin(base, "300/without-location-header.asis") + uri = urllib.parse.urljoin(base, "300/without-location-header.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 300) self.assertTrue(response['content-type'].startswith("text/html")) @@ -281,11 +281,11 @@ def testGet300WithoutLocation(self): def testGet301(self): # Test that we automatically follow 301 redirects # and that we cache the 301 response - uri = urlparse.urljoin(base, "301/onestep.asis") - destination = urlparse.urljoin(base, "302/final-destination.txt") + uri = urllib.parse.urljoin(base, "301/onestep.asis") + destination = urllib.parse.urljoin(base, "302/final-destination.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - self.assertTrue(response.has_key('content-location')) + self.assertTrue('content-location' in response) self.assertEqual(response['content-location'], destination) self.assertEqual(content, "This is the final destination.\n") self.assertEqual(response.previous.status, 301) @@ -303,8 +303,8 @@ def testGet301NoRedirect(self): # Test that we automatically follow 301 redirects # and that we cache the 301 response self.http.follow_redirects = False - uri = urlparse.urljoin(base, "301/onestep.asis") - destination = urlparse.urljoin(base, "302/final-destination.txt") + uri = urllib.parse.urljoin(base, "301/onestep.asis") + destination = urllib.parse.urljoin(base, "302/final-destination.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 301) @@ -312,8 +312,8 @@ def testGet301NoRedirect(self): def testGet302(self): # Test that we automatically follow 302 redirects # and that we DO NOT cache the 302 response - uri = urlparse.urljoin(base, "302/onestep.asis") - destination = urlparse.urljoin(base, "302/final-destination.txt") + uri = urllib.parse.urljoin(base, "302/onestep.asis") + destination = urllib.parse.urljoin(base, "302/final-destination.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(response['content-location'], destination) @@ -321,7 +321,7 @@ def testGet302(self): self.assertEqual(response.previous.status, 302) self.assertEqual(response.previous.fromcache, False) - uri = urlparse.urljoin(base, "302/onestep.asis") + uri = urllib.parse.urljoin(base, "302/onestep.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(response.fromcache, True) @@ -331,7 +331,7 @@ def testGet302(self): self.assertEqual(response.previous.fromcache, False) self.assertEqual(response.previous['content-location'], uri) - uri = urlparse.urljoin(base, "302/twostep.asis") + uri = urllib.parse.urljoin(base, "302/twostep.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -346,13 +346,13 @@ def testGet302RedirectionLimit(self): # that limit. self.http.force_exception_to_status_code = False - uri = urlparse.urljoin(base, "302/twostep.asis") + uri = urllib.parse.urljoin(base, "302/twostep.asis") try: (response, content) = self.http.request(uri, "GET", redirections = 1) self.fail("This should not happen") except httplib2.RedirectLimit: pass - except Exception, e: + except Exception as e: self.fail("Threw wrong kind of exception ") # Re-run the test with out the exceptions @@ -369,13 +369,13 @@ def testGet302NoLocation(self): # Test that we throw an exception when we get # a 302 with no Location: header. self.http.force_exception_to_status_code = False - uri = urlparse.urljoin(base, "302/no-location.asis") + uri = urllib.parse.urljoin(base, "302/no-location.asis") try: (response, content) = self.http.request(uri, "GET") self.fail("Should never reach here") except httplib2.RedirectMissingLocation: pass - except Exception, e: + except Exception as e: self.fail("Threw wrong kind of exception ") # Re-run the test with out the exceptions @@ -437,7 +437,7 @@ def testGetViaHttpsKeyCert(self): def testGet303(self): # Do a follow-up GET on a Location: header # returned from a POST that gave a 303. - uri = urlparse.urljoin(base, "303/303.cgi") + uri = urllib.parse.urljoin(base, "303/303.cgi") (response, content) = self.http.request(uri, "POST", " ") self.assertEqual(response.status, 200) self.assertEqual(content, "This is the final destination.\n") @@ -447,20 +447,20 @@ def testGet303NoRedirect(self): # Do a follow-up GET on a Location: header # returned from a POST that gave a 303. self.http.follow_redirects = False - uri = urlparse.urljoin(base, "303/303.cgi") + uri = urllib.parse.urljoin(base, "303/303.cgi") (response, content) = self.http.request(uri, "POST", " ") self.assertEqual(response.status, 303) def test303ForDifferentMethods(self): # Test that all methods can be used - uri = urlparse.urljoin(base, "303/redirect-to-reflector.cgi") + uri = urllib.parse.urljoin(base, "303/redirect-to-reflector.cgi") for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]: (response, content) = self.http.request(uri, method, body=" ") self.assertEqual(response['x-method'], method_on_303) def testGet304(self): # Test that we use ETags properly to validate our cache - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") @@ -486,34 +486,34 @@ def testGet304(self): def testGetIgnoreEtag(self): # Test that we can forcibly ignore ETags - uri = urlparse.urljoin(base, "reflector/reflector.cgi") + uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) - self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH')) + self.assertTrue('HTTP_IF_NONE_MATCH' in d) self.http.ignore_etag = True (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) self.assertEqual(response.fromcache, False) - self.assertFalse(d.has_key('HTTP_IF_NONE_MATCH')) + self.assertFalse('HTTP_IF_NONE_MATCH' in d) def testOverrideEtag(self): # Test that we can forcibly ignore ETags - uri = urlparse.urljoin(base, "reflector/reflector.cgi") + uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) - self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH')) + self.assertTrue('HTTP_IF_NONE_MATCH' in d) self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'}) d = self.reflector(content) - self.assertTrue(d.has_key('HTTP_IF_NONE_MATCH')) + self.assertTrue('HTTP_IF_NONE_MATCH' in d) self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred") #MAP-commented this out because it consistently fails @@ -535,7 +535,7 @@ def testOverrideEtag(self): def testGet304LastModified(self): # Test that we can still handle a 304 # by only using the last-modified cache validator. - uri = urlparse.urljoin(base, "304/last-modified-only/last-modified-only.txt") + uri = urllib.parse.urljoin(base, "304/last-modified-only/last-modified-only.txt") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['last-modified'], "") @@ -547,7 +547,7 @@ def testGet304LastModified(self): def testGet307(self): # Test that we do follow 307 redirects but # do not cache the 307 - uri = urlparse.urljoin(base, "307/onestep.asis") + uri = urllib.parse.urljoin(base, "307/onestep.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(content, "This is the final destination.\n") @@ -563,7 +563,7 @@ def testGet307(self): def testGet410(self): # Test that we pass 410's through - uri = urlparse.urljoin(base, "410/410.asis") + uri = urllib.parse.urljoin(base, "410/410.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 410) @@ -578,10 +578,10 @@ def testVaryHeaderSimple(self): request-headers in the original request. """ # test that the vary header is sent - uri = urlparse.urljoin(base, "vary/accept.asis") + uri = urllib.parse.urljoin(base, "vary/accept.asis") (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) self.assertEqual(response.status, 200) - self.assertTrue(response.has_key('vary')) + self.assertTrue('vary' in response) # get the resource again, from the cache since accept header in this # request is the same as the request @@ -603,10 +603,10 @@ def testNoVary(self): # when there is no vary, a different Accept header (e.g.) should not # impact if the cache is used # test that the vary header is not sent - uri = urlparse.urljoin(base, "vary/no-vary.asis") + uri = urllib.parse.urljoin(base, "vary/no-vary.asis") (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) self.assertEqual(response.status, 200) - self.assertFalse(response.has_key('vary')) + self.assertFalse('vary' in response) (response, content) = self.http.request(uri, "GET", headers={'Accept': 'text/plain'}) self.assertEqual(response.status, 200) @@ -617,11 +617,11 @@ def testNoVary(self): self.assertEqual(response.fromcache, True, msg="Should be from cache") def testVaryHeaderDouble(self): - uri = urlparse.urljoin(base, "vary/accept-double.asis") + uri = urllib.parse.urljoin(base, "vary/accept-double.asis") (response, content) = self.http.request(uri, "GET", headers={ 'Accept': 'text/plain', 'Accept-Language': 'da, en-gb;q=0.8, en;q=0.7'}) self.assertEqual(response.status, 200) - self.assertTrue(response.has_key('vary')) + self.assertTrue('vary' in response) # we are from cache (response, content) = self.http.request(uri, "GET", headers={ @@ -640,7 +640,7 @@ def testVaryHeaderDouble(self): def testHeadGZip(self): # Test that we don't try to decompress a HEAD response - uri = urlparse.urljoin(base, "gzip/final-destination.txt") + uri = urllib.parse.urljoin(base, "gzip/final-destination.txt") (response, content) = self.http.request(uri, "HEAD") self.assertEqual(response.status, 200) self.assertNotEqual(int(response['content-length']), 0) @@ -648,18 +648,18 @@ def testHeadGZip(self): def testGetGZip(self): # Test that we support gzip compression - uri = urlparse.urljoin(base, "gzip/final-destination.txt") + uri = urllib.parse.urljoin(base, "gzip/final-destination.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - self.assertFalse(response.has_key('content-encoding')) - self.assertTrue(response.has_key('-content-encoding')) + self.assertFalse('content-encoding' in response) + self.assertTrue('-content-encoding' in response) self.assertEqual(int(response['content-length']), len("This is the final destination.\n")) self.assertEqual(content, "This is the final destination.\n") def testGetGZipFailure(self): # Test that we raise a good exception when the gzip fails self.http.force_exception_to_status_code = False - uri = urlparse.urljoin(base, "gzip/failed-compression.asis") + uri = urllib.parse.urljoin(base, "gzip/failed-compression.asis") try: (response, content) = self.http.request(uri, "GET") self.fail("Should never reach here") @@ -677,7 +677,7 @@ def testGetGZipFailure(self): def testTimeout(self): self.http.force_exception_to_status_code = True - uri = urlparse.urljoin(base, "timeout/timeout.cgi") + uri = urllib.parse.urljoin(base, "timeout/timeout.cgi") try: import socket socket.setdefaulttimeout(1) @@ -690,7 +690,7 @@ def testTimeout(self): self.assertTrue(content.startswith("Request Timeout")) def testIndividualTimeout(self): - uri = urlparse.urljoin(base, "timeout/timeout.cgi") + uri = urllib.parse.urljoin(base, "timeout/timeout.cgi") http = httplib2.Http(timeout=1) http.force_exception_to_status_code = True @@ -706,10 +706,10 @@ def testHTTPSInitTimeout(self): def testGetDeflate(self): # Test that we support deflate compression - uri = urlparse.urljoin(base, "deflate/deflated.asis") + uri = urllib.parse.urljoin(base, "deflate/deflated.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - self.assertFalse(response.has_key('content-encoding')) + self.assertFalse('content-encoding' in response) self.assertEqual(int(response['content-length']), len("This is the final destination.")) self.assertEqual(content, "This is the final destination.") @@ -717,7 +717,7 @@ def testGetDeflateFailure(self): # Test that we raise a good exception when the deflate fails self.http.force_exception_to_status_code = False - uri = urlparse.urljoin(base, "deflate/failed-compression.asis") + uri = urllib.parse.urljoin(base, "deflate/failed-compression.asis") try: (response, content) = self.http.request(uri, "GET") self.fail("Should never reach here") @@ -735,7 +735,7 @@ def testGetDeflateFailure(self): def testGetDuplicateHeaders(self): # Test that duplicate headers get concatenated via ',' - uri = urlparse.urljoin(base, "duplicate-headers/multilink.asis") + uri = urllib.parse.urljoin(base, "duplicate-headers/multilink.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) self.assertEqual(content, "This is content\n") @@ -743,7 +743,7 @@ def testGetDuplicateHeaders(self): def testGetCacheControlNoCache(self): # Test Cache-Control: no-cache on requests - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET") @@ -756,7 +756,7 @@ def testGetCacheControlNoCache(self): def testGetCacheControlPragmaNoCache(self): # Test Pragma: no-cache on requests - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET") @@ -769,7 +769,7 @@ def testGetCacheControlPragmaNoCache(self): def testGetCacheControlNoStoreRequest(self): # A no-store request means that the response should not be stored. - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET", headers={'Cache-Control': 'no-store'}) self.assertEqual(response.status, 200) @@ -781,7 +781,7 @@ def testGetCacheControlNoStoreRequest(self): def testGetCacheControlNoStoreResponse(self): # A no-store response means that the response should not be stored. - uri = urlparse.urljoin(base, "no-store/no-store.asis") + uri = urllib.parse.urljoin(base, "no-store/no-store.asis") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -794,7 +794,7 @@ def testGetCacheControlNoStoreResponse(self): def testGetCacheControlNoCacheNoStoreRequest(self): # Test that a no-store, no-cache clears the entry from the cache # even if it was cached previously. - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") (response, content) = self.http.request(uri, "GET") @@ -807,7 +807,7 @@ def testGetCacheControlNoCacheNoStoreRequest(self): def testUpdateInvalidatesCache(self): # Test that calling PUT or DELETE on a # URI that is cache invalidates that cache. - uri = urlparse.urljoin(base, "304/test_etag.txt") + uri = urllib.parse.urljoin(base, "304/test_etag.txt") (response, content) = self.http.request(uri, "GET") (response, content) = self.http.request(uri, "GET") @@ -820,7 +820,7 @@ def testUpdateInvalidatesCache(self): def testUpdateUsesCachedETag(self): # Test that we natively support http://www.w3.org/1999/04/Editing/ - uri = urlparse.urljoin(base, "conditional-updates/test.cgi") + uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -835,7 +835,7 @@ def testUpdateUsesCachedETag(self): def testUpdateUsesCachedETagAndOCMethod(self): # Test that we natively support http://www.w3.org/1999/04/Editing/ - uri = urlparse.urljoin(base, "conditional-updates/test.cgi") + uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -850,7 +850,7 @@ def testUpdateUsesCachedETagAndOCMethod(self): def testUpdateUsesCachedETagOverridden(self): # Test that we natively support http://www.w3.org/1999/04/Editing/ - uri = urlparse.urljoin(base, "conditional-updates/test.cgi") + uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -863,11 +863,11 @@ def testUpdateUsesCachedETagOverridden(self): def testBasicAuth(self): # Test Basic Authentication - uri = urlparse.urljoin(base, "basic/file.txt") + uri = urllib.parse.urljoin(base, "basic/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - uri = urlparse.urljoin(base, "basic/") + uri = urllib.parse.urljoin(base, "basic/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) @@ -875,17 +875,17 @@ def testBasicAuth(self): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "basic/file.txt") + uri = urllib.parse.urljoin(base, "basic/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) def testBasicAuthWithDomain(self): # Test Basic Authentication - uri = urlparse.urljoin(base, "basic/file.txt") + uri = urllib.parse.urljoin(base, "basic/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - uri = urlparse.urljoin(base, "basic/") + uri = urllib.parse.urljoin(base, "basic/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) @@ -893,16 +893,16 @@ def testBasicAuthWithDomain(self): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - uri = urlparse.urljoin(base, "basic/file.txt") + uri = urllib.parse.urljoin(base, "basic/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - domain = urlparse.urlparse(base)[1] + domain = urllib.parse.urlparse(base)[1] self.http.add_credentials('joe', 'password', domain) (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "basic/file.txt") + uri = urllib.parse.urljoin(base, "basic/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -913,11 +913,11 @@ def testBasicAuthWithDomain(self): def testBasicAuthTwoDifferentCredentials(self): # Test Basic Authentication with multiple sets of credentials - uri = urlparse.urljoin(base, "basic2/file.txt") + uri = urllib.parse.urljoin(base, "basic2/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - uri = urlparse.urljoin(base, "basic2/") + uri = urllib.parse.urljoin(base, "basic2/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) @@ -925,45 +925,45 @@ def testBasicAuthTwoDifferentCredentials(self): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "basic2/file.txt") + uri = urllib.parse.urljoin(base, "basic2/file.txt") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) def testBasicAuthNested(self): # Test Basic Authentication with resources # that are nested - uri = urlparse.urljoin(base, "basic-nested/") + uri = urllib.parse.urljoin(base, "basic-nested/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - uri = urlparse.urljoin(base, "basic-nested/subdir") + uri = urllib.parse.urljoin(base, "basic-nested/subdir") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) # Now add in credentials one at a time and test. self.http.add_credentials('joe', 'password') - uri = urlparse.urljoin(base, "basic-nested/") + uri = urllib.parse.urljoin(base, "basic-nested/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "basic-nested/subdir") + uri = urllib.parse.urljoin(base, "basic-nested/subdir") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) self.http.add_credentials('fred', 'barney') - uri = urlparse.urljoin(base, "basic-nested/") + uri = urllib.parse.urljoin(base, "basic-nested/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "basic-nested/subdir") + uri = urllib.parse.urljoin(base, "basic-nested/subdir") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) def testDigestAuth(self): # Test that we support Digest Authentication - uri = urlparse.urljoin(base, "digest/") + uri = urllib.parse.urljoin(base, "digest/") (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) @@ -971,13 +971,13 @@ def testDigestAuth(self): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) - uri = urlparse.urljoin(base, "digest/file.txt") + uri = urllib.parse.urljoin(base, "digest/file.txt") (response, content) = self.http.request(uri, "GET") def testDigestAuthNextNonceAndNC(self): # Test that if the server sets nextnonce that we reset # the nonce count back to 1 - uri = urlparse.urljoin(base, "digest/file.txt") + uri = urllib.parse.urljoin(base, "digest/file.txt") self.http.add_credentials('joe', 'password') (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) info = httplib2._parse_www_authenticate(response, 'authentication-info') @@ -986,12 +986,12 @@ def testDigestAuthNextNonceAndNC(self): info2 = httplib2._parse_www_authenticate(response, 'authentication-info') self.assertEqual(response.status, 200) - if info.has_key('nextnonce'): + if 'nextnonce' in info: self.assertEqual(info2['nc'], 1) def testDigestAuthStale(self): # Test that we can handle a nonce becoming stale - uri = urlparse.urljoin(base, "digest-expire/file.txt") + uri = urllib.parse.urljoin(base, "digest-expire/file.txt") self.http.add_credentials('joe', 'password') (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) info = httplib2._parse_www_authenticate(response, 'authentication-info') @@ -1010,18 +1010,18 @@ def reflector(self, content): return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] ) def testReflector(self): - uri = urlparse.urljoin(base, "reflector/reflector.cgi") + uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") d = self.reflector(content) - self.assertTrue(d.has_key('HTTP_USER_AGENT')) + self.assertTrue('HTTP_USER_AGENT' in d) def testConnectionClose(self): uri = "http://www.google.com/" (response, content) = self.http.request(uri, "GET") - for c in self.http.connections.values(): + for c in list(self.http.connections.values()): self.assertNotEqual(None, c.sock) (response, content) = self.http.request(uri, "GET", headers={"connection": "close"}) - for c in self.http.connections.values(): + for c in list(self.http.connections.values()): self.assertEqual(None, c.sock) @@ -1069,8 +1069,8 @@ def testParseCacheControl(self): def testNormalizeHeaders(self): # Test that we normalize headers to lowercase h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'}) - self.assertTrue(h.has_key('cache-control')) - self.assertTrue(h.has_key('other')) + self.assertTrue('cache-control' in h) + self.assertTrue('other' in h) self.assertEqual('Stuff', h['other']) def testExpirationModelTransparent(self): @@ -1208,22 +1208,22 @@ def testExpirationModelDateAndExpiresMinFresh2(self): def testParseWWWAuthenticateEmpty(self): res = httplib2._parse_www_authenticate({}) - self.assertEqual(len(res.keys()), 0) + self.assertEqual(len(list(res.keys())), 0) def testParseWWWAuthenticate(self): # different uses of spaces around commas res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'}) - self.assertEqual(len(res.keys()), 1) - self.assertEqual(len(res['test'].keys()), 5) + self.assertEqual(len(list(res.keys())), 1) + self.assertEqual(len(list(res['test'].keys())), 5) # tokens with non-alphanum res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'}) - self.assertEqual(len(res.keys()), 1) - self.assertEqual(len(res['t*!%#st'].keys()), 2) + self.assertEqual(len(list(res.keys())), 1) + self.assertEqual(len(list(res['t*!%#st'].keys())), 2) # quoted string with quoted pairs res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'}) - self.assertEqual(len(res.keys()), 1) + self.assertEqual(len(list(res.keys())), 1) self.assertEqual(res['test']['realm'], 'a "test" realm') def testParseWWWAuthenticateStrict(self): @@ -1387,12 +1387,12 @@ def testEnd2End(self): # Degenerate case of no headers response = {} end2end = httplib2._get_end2end_headers(response) - self.assertEquals(0, len(end2end)) + self.assertEqual(0, len(end2end)) # Degenerate case of connection referrring to a header not passed in response = {'connection': 'content-type'} end2end = httplib2._get_end2end_headers(response) - self.assertEquals(0, len(end2end)) + self.assertEqual(0, len(end2end)) if __name__ == '__main__': unittest.main() diff --git a/tests/mock.py b/tests/mock.py index b07172a3c..f22ce6107 100644 --- a/tests/mock.py +++ b/tests/mock.py @@ -57,13 +57,13 @@ def inner(f): return inner try: - unicode + str except NameError: # Python 3 - basestring = unicode = str + str = str = str try: - long + int except NameError: # Python 3 long = int @@ -116,7 +116,7 @@ def _copy_func_details(func, funcopy): funcopy.__dict__.update(func.__dict__) funcopy.__module__ = func.__module__ if not inPy3k: - funcopy.func_defaults = func.func_defaults + funcopy.__defaults__ = func.__defaults__ else: funcopy.__defaults__ = func.__defaults__ funcopy.__kwdefaults__ = func.__kwdefaults__ @@ -296,7 +296,7 @@ def reset_mock(self): self.call_count = 0 self.call_args_list = [] self.method_calls = [] - for child in self._children.values(): + for child in list(self._children.values()): child.reset_mock() if isinstance(self._return_value, Mock): if not self._return_value is self: @@ -572,7 +572,7 @@ def patched(*args, **keywargs): if hasattr(func, 'func_code'): # not in Python 3 patched.compat_co_firstlineno = getattr(func, "compat_co_firstlineno", - func.func_code.co_firstlineno) + func.__code__.co_firstlineno) return patched @@ -741,7 +741,7 @@ class _patch_dict(object): """ def __init__(self, in_dict, values=(), clear=False): - if isinstance(in_dict, basestring): + if isinstance(in_dict, str): in_dict = _importer(in_dict) self.in_dict = in_dict # support any argument supported by dict(...) constructor @@ -900,7 +900,7 @@ def method(self, *args, **kw): '__hash__': lambda self: object.__hash__(self), '__str__': lambda self: object.__str__(self), '__sizeof__': lambda self: object.__sizeof__(self), - '__unicode__': lambda self: unicode(object.__str__(self)), + '__unicode__': lambda self: str(object.__str__(self)), } _return_values = { @@ -915,7 +915,7 @@ def method(self, *args, **kw): '__nonzero__': True, '__oct__': '1', '__hex__': '0x1', - '__long__': long(1), + '__long__': int(1), '__index__': 1, } diff --git a/tests/test_api.py b/tests/test_api.py index e83674d43..d6a09f916 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -8,24 +8,24 @@ import sys import os import re -from mock import patch, Mock, MagicMock +from .mock import patch, Mock, MagicMock import time import uuid import unittest -import urlparse -import urllib2 +import urllib.parse +import urllib.request, urllib.error, urllib.parse import warnings import shotgun_api3 from shotgun_api3.lib.httplib2 import Http, SSLHandshakeError -import base +from . import base class TestShotgunApi(base.LiveTestBase): def setUp(self): super(TestShotgunApi, self).setUp() # give note unicode content - self.sg.update('Note', self.note['id'], {'content':u'La Pe\xf1a'}) + self.sg.update('Note', self.note['id'], {'content':'La Pe\xf1a'}) def test_info(self): """Called info""" @@ -139,7 +139,7 @@ def test_upload_download(self): # upload / download only works against a live server because it does # not use the standard http interface if 'localhost' in self.server_url: - print "upload / down tests skipped for localhost" + print("upload / down tests skipped for localhost") return this_dir, _ = os.path.split(__file__) @@ -396,8 +396,8 @@ def test_share_thumbnail(self): fields=['id', 'code', 'image'] ) - shot_url = urlparse.urlparse(response_shot_thumbnail.get('image')) - version_url = urlparse.urlparse(response_version_thumbnail.get('image')) + shot_url = urllib.parse.urlparse(response_shot_thumbnail.get('image')) + version_url = urllib.parse.urlparse(response_version_thumbnail.get('image')) shot_path = _get_path(shot_url) version_path = _get_path(version_url) self.assertEqual(shot_path, version_path) @@ -424,9 +424,9 @@ def test_share_thumbnail(self): fields=['id', 'code', 'image'] ) - shot_url = urlparse.urlparse(response_shot_thumbnail.get('image')) - version_url = urlparse.urlparse(response_version_thumbnail.get('image')) - asset_url = urlparse.urlparse(response_asset_thumbnail.get('image')) + shot_url = urllib.parse.urlparse(response_shot_thumbnail.get('image')) + version_url = urllib.parse.urlparse(response_version_thumbnail.get('image')) + asset_url = urllib.parse.urlparse(response_asset_thumbnail.get('image')) shot_path = _get_path(shot_url) version_path = _get_path(version_url) @@ -933,11 +933,11 @@ class TestFind(base.LiveTestBase): def setUp(self): super(TestFind, self).setUp() # We will need the created_at field for the shot - fields = self.shot.keys()[:] + fields = list(self.shot.keys())[:] fields.append('created_at') self.shot = self.sg.find_one('Shot', [['id', 'is', self.shot['id']]], fields) # We will need the uuid field for our LocalStorage - fields = self.local_storage.keys()[:] + fields = list(self.local_storage.keys())[:] fields.append('uuid') self.local_storage = self.sg.find_one('LocalStorage', [['id', 'is', self.local_storage['id']]], fields) @@ -1099,7 +1099,7 @@ def test_in_relation_comma_duration(self): Test that 'in' relation using commas (old format) works with duration fields. """ # we need to get the duration value - new_task_keys = self.task.keys()[:] + new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) filters = [['duration', 'in', self.task['duration']], @@ -1113,7 +1113,7 @@ def test_in_relation_list_duration(self): Test that 'in' relation using list (new format) works with duration fields. """ # we need to get the duration value - new_task_keys = self.task.keys()[:] + new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) filters = [['duration', 'in', [self.task['duration'],]], @@ -1127,7 +1127,7 @@ def test_not_in_relation_duration(self): Test that 'not_in' relation using commas (old format) works with duration fields. """ # we need to get the duration value - new_task_keys = self.task.keys()[:] + new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) @@ -1432,7 +1432,7 @@ def test_zero_is_not_none(self): ''' # Create a number field if it doesn't already exist num_field = 'sg_api_tests_number_field' - if num_field not in self.sg.schema_field_read('Asset').keys(): + if num_field not in list(self.sg.schema_field_read('Asset').keys()): self.sg.schema_field_create('Asset', 'number', num_field.replace('sg_','').replace('_',' ')) # Set to None @@ -1440,7 +1440,7 @@ def test_zero_is_not_none(self): # Should be filtered out result = self.sg.find( 'Asset', [['id','is',self.asset['id']],[num_field, 'is_not', None]] ,[num_field] ) - self.assertEquals([], result) + self.assertEqual([], result) # Set it to zero self.sg.update( 'Asset', self.asset['id'], { num_field: 0 }) @@ -1461,17 +1461,17 @@ def test_include_archived_projects(self): if self.sg.server_caps.version > (5, 3, 13): # Ticket #25082 result = self.sg.find_one('Shot', [['id','is',self.shot['id']]]) - self.assertEquals(self.shot['id'], result['id']) + self.assertEqual(self.shot['id'], result['id']) # archive project self.sg.update('Project', self.project['id'], {'archived':True}) # setting defaults to True, so we should get result result = self.sg.find_one('Shot', [['id','is',self.shot['id']]]) - self.assertEquals(self.shot['id'], result['id']) + self.assertEqual(self.shot['id'], result['id']) result = self.sg.find_one('Shot', [['id','is',self.shot['id']]], include_archived_projects=False) - self.assertEquals(None, result) + self.assertEqual(None, result) # unarchive project self.sg.update('Project', self.project['id'], {'archived':False}) @@ -1674,10 +1674,10 @@ def test_sha2_error_with_strict(self, mock_request): if original_env_val is not None: os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = original_env_val - @patch.object(urllib2.OpenerDirector, 'open') + @patch.object(urllib.request.OpenerDirector, 'open') def test_sanitized_auth_params(self, mock_open): # Simulate the server blowing up and giving us a 500 error - mock_open.side_effect = urllib2.HTTPError('url', 500, 'message', {}, None) + mock_open.side_effect = urllib.error.HTTPError('url', 500, 'message', {}, None) this_dir, _ = os.path.split(__file__) thumbnail_path = os.path.abspath(os.path.join(this_dir, "sg_logo.jpg")) @@ -1685,7 +1685,7 @@ def test_sanitized_auth_params(self, mock_open): try: # Try to upload a bogus file self.sg.upload('Note', 1234, thumbnail_path) - except shotgun_api3.ShotgunError, e: + except shotgun_api3.ShotgunError as e: self.assertFalse(self.api_key in str(e)) return @@ -1774,12 +1774,12 @@ def test_human_user_sudo_auth_fails(self): expected = "The user does not have permission to 'sudo':" try : x.find_one('Shot',[]) - except shotgun_api3.Fault, e: + except shotgun_api3.Fault as e: # py24 exceptions don't have message attr if hasattr(e, 'message'): - self.assert_(e.message.startswith(expected)) + self.assertTrue(e.message.startswith(expected)) else: - self.assert_(e.args[0].startswith(expected)) + self.assertTrue(e.args[0].startswith(expected)) @@ -2191,8 +2191,8 @@ def test_simple(self): # the uploaded thumbnail. strip off any s3 querystring # for the comparison reply_thumb = result[1]["user"]["image"] - url_obj_a = urlparse.urlparse(current_thumbnail) - url_obj_b = urlparse.urlparse(reply_thumb) + url_obj_a = urllib.parse.urlparse(current_thumbnail) + url_obj_b = urllib.parse.urlparse(reply_thumb) self.assertEqual("%s/%s" % (url_obj_a.netloc, url_obj_a.path), "%s/%s" % (url_obj_b.netloc, url_obj_b.path),) @@ -2515,10 +2515,10 @@ def test_multiple_latest_filters(self): def _has_unicode(data): - for k, v in data.items(): - if isinstance(k, unicode): + for k, v in list(data.items()): + if isinstance(k, str): return True - if isinstance(v, unicode): + if isinstance(v, str): return True return False diff --git a/tests/test_api_long.py b/tests/test_api_long.py index 12419ea78..9e9f69af2 100644 --- a/tests/test_api_long.py +++ b/tests/test_api_long.py @@ -3,7 +3,7 @@ Includes the schema functions and the automated searching for all entity types """ -import base +from . import base import random import shotgun_api3 import os @@ -13,7 +13,7 @@ class TestShotgunApiLong(base.LiveTestBase): def test_automated_find(self): """Called find for each entity type and read all fields""" - all_entities = self.sg.schema_entity_read().keys() + all_entities = list(self.sg.schema_entity_read().keys()) direction = "asc" filter_operator = "all" limit = 1 @@ -22,18 +22,18 @@ def test_automated_find(self): if entity_type in ("Asset", "Task", "Shot", "Attachment", "Candidate"): continue - print "Finding entity type", entity_type + print("Finding entity type", entity_type) fields = self.sg.schema_field_read(entity_type) if not fields: - print "No fields for %s skipping" % (entity_type,) + print("No fields for %s skipping" % (entity_type,)) continue # trying to use some different code paths to the other find test # pivot_column fields aren't valid for sorting so ensure we're # not using one. order_field = None - for field_name, field in fields.iteritems(): + for field_name, field in fields.items(): if field['data_type']["value"] != 'pivot_column': order_field = field_name break @@ -44,7 +44,7 @@ def test_automated_find(self): else: filters = [] - records = self.sg.find(entity_type, filters, fields=fields.keys(), + records = self.sg.find(entity_type, filters, fields=list(fields.keys()), order=order, filter_operator=filter_operator, limit=limit, page=page) @@ -131,7 +131,7 @@ def test_schema_with_project(self): self.assertTrue(schema, dict) self.assertTrue(len(schema) > 0) self.assertTrue('Version' in schema) - self.assertFalse('visible' in schema.keys()) + self.assertFalse('visible' in list(schema.keys())) schema = self.sg.schema_field_read('Version', project_entity=project_entity) self.assertTrue(schema, dict) diff --git a/tests/test_client.py b/tests/test_client.py index 5f83cac82..26a131dbc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -17,12 +17,12 @@ import sys import time import unittest -import mock +from . import mock import shotgun_api3.lib.httplib2 as httplib2 import shotgun_api3 as api from shotgun_api3.shotgun import ServerCapabilities, SG_TIMEZONE -import base +from . import base class TestShotgunClient(base.MockTestBase): '''Test case for shotgun api with server interactions mocked.''' @@ -160,7 +160,7 @@ def test_authorization(self): self.sg = api.Shotgun(auth_url, "foo", "bar", connect=False) self._setup_mock() - self._mock_http({ 'version': [2, 4, 0, u'Dev'] }) + self._mock_http({ 'version': [2, 4, 0, 'Dev'] }) self.sg.info() @@ -255,7 +255,7 @@ def test_rpc_error(self): try: self.sg.info() - except api.Fault, e: + except api.Fault as e: self.assertEqual("Go BANG", str(e)) def test_call_rpc(self): @@ -325,14 +325,14 @@ def _datetime(s, f): return datetime.datetime(*time.strptime(s, f)[:6]) def assert_wire(wire, match): - self.assertTrue(isinstance(wire["date"], basestring)) + self.assertTrue(isinstance(wire["date"], str)) d = _datetime(wire["date"], "%Y-%m-%d").date() d = wire['date'] self.assertEqual(match["date"], d) - self.assertTrue(isinstance(wire["datetime"], basestring)) + self.assertTrue(isinstance(wire["datetime"], str)) d = _datetime(wire["datetime"], "%Y-%m-%dT%H:%M:%SZ") self.assertEqual(match["datetime"], d) - self.assertTrue(isinstance(wire["time"], basestring)) + self.assertTrue(isinstance(wire["time"], str)) d = _datetime(wire["time"], "%Y-%m-%dT%H:%M:%SZ") self.assertEqual(match["time"], d.time()) @@ -360,22 +360,22 @@ def test_encode_payload(self): """Request body is encoded as JSON""" d = { - "this is " : u"my data \u00E0" + "this is " : "my data \u00E0" } j = self.sg._encode_payload(d) - self.assertTrue(isinstance(j, str)) + self.assertTrue(isinstance(j, bytes)) d = { - "this is " : u"my data" + "this is " : "my data" } j = self.sg._encode_payload(d) - self.assertTrue(isinstance(j, str)) + self.assertTrue(isinstance(j, bytes)) def test_decode_response_ascii(self): - self._assert_decode_resonse(True, u"my data \u00E0".encode('utf8')) + self._assert_decode_resonse(True, "my data \u00E0".encode('utf8')) def test_decode_response_unicode(self): - self._assert_decode_resonse(False, u"my data \u00E0") + self._assert_decode_resonse(False, "my data \u00E0") def _assert_decode_resonse(self, ensure_ascii, data): """HTTP Response is decoded as JSON or text""" diff --git a/tests/test_mockgun.py b/tests/test_mockgun.py index 2bd4ae108..51f029001 100644 --- a/tests/test_mockgun.py +++ b/tests/test_mockgun.py @@ -63,13 +63,13 @@ class TestBaseWithExceptionTests(unittest.TestCase): def assertRaisesRegexp(self, exception_type, re_msg, func): try: func() - except exception_type, exception: + except exception_type as exception: matches = re.findall(re_msg, str(exception)) if not matches: self.fail("Expected exception to match '%s', got '%s' instead." % ( re_msg, str(exception) )) - except Exception, ex: + except Exception as ex: self.fail("Expected exception of type %s, got %s" % (exception_type, type(ex))) else: self.fail("Expected %s was not raised." % exception_type) @@ -127,7 +127,7 @@ def test_filter_array_or_dict(self): ) # We can't have not dict/list values for filters however. - self.assertRaisesRegexp( + self.assertRaisesRegex( ShotgunError, "Filters can only be lists or dictionaries, not int.", lambda: self._mockgun.find( @@ -408,7 +408,7 @@ def test_nested_filter_operators(self): def test_invalid_operator(self): - self.assertRaisesRegexp( + self.assertRaisesRegex( ShotgunError, "Unknown filter_operator type: bad", lambda: self._mockgun.find( @@ -421,7 +421,7 @@ def test_invalid_operator(self): ]) ) - self.assertRaisesRegexp( + self.assertRaisesRegex( ShotgunError, "Bad filter operator, requires keys 'filter_operator' and 'filters',", lambda: self._mockgun.find( diff --git a/tests/tests_proxy.py b/tests/tests_proxy.py index 857641756..ea575371c 100644 --- a/tests/tests_proxy.py +++ b/tests/tests_proxy.py @@ -1,6 +1,6 @@ #! /opt/local/bin/python import sys -import base +from . import base import shotgun_api3 as api diff --git a/tests/tests_unit.py b/tests/tests_unit.py index 2f2a04db2..c9e2ff277 100644 --- a/tests/tests_unit.py +++ b/tests/tests_unit.py @@ -1,6 +1,6 @@ #! /opt/local/bin/python import unittest -from mock import patch, Mock +from .mock import patch, Mock import shotgun_api3 as api from shotgun_api3.sg_26 import _is_mimetypes_broken @@ -20,8 +20,8 @@ def test_http_proxy_server(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, 8080) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, 8080) proxy_server = "123.456.789.012" http_proxy = proxy_server sg = api.Shotgun(self.server_path, @@ -29,8 +29,8 @@ def test_http_proxy_server(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, 8080) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, 8080) def test_http_proxy_server_and_port(self): proxy_server = "someserver.com" @@ -41,8 +41,8 @@ def test_http_proxy_server_and_port(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, proxy_port) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, proxy_port) proxy_server = "123.456.789.012" proxy_port = 1234 http_proxy = "%s:%d" % (proxy_server, proxy_port) @@ -51,8 +51,8 @@ def test_http_proxy_server_and_port(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, proxy_port) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, proxy_port) def test_http_proxy_server_and_port_with_authentication(self): proxy_server = "someserver.com" @@ -66,10 +66,10 @@ def test_http_proxy_server_and_port_with_authentication(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, proxy_port) - self.assertEquals(sg.config.proxy_user, proxy_user) - self.assertEquals(sg.config.proxy_pass, proxy_pass) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, proxy_port) + self.assertEqual(sg.config.proxy_user, proxy_user) + self.assertEqual(sg.config.proxy_pass, proxy_pass) proxy_server = "123.456.789.012" proxy_port = 1234 proxy_user = "user" @@ -81,10 +81,10 @@ def test_http_proxy_server_and_port_with_authentication(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, proxy_port) - self.assertEquals(sg.config.proxy_user, proxy_user) - self.assertEquals(sg.config.proxy_pass, proxy_pass) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, proxy_port) + self.assertEqual(sg.config.proxy_user, proxy_user) + self.assertEqual(sg.config.proxy_pass, proxy_pass) def test_http_proxy_with_at_in_password(self): proxy_server = "someserver.com" @@ -98,10 +98,10 @@ def test_http_proxy_with_at_in_password(self): self.api_key, http_proxy=http_proxy, connect=False) - self.assertEquals(sg.config.proxy_server, proxy_server) - self.assertEquals(sg.config.proxy_port, proxy_port) - self.assertEquals(sg.config.proxy_user, proxy_user) - self.assertEquals(sg.config.proxy_pass, proxy_pass) + self.assertEqual(sg.config.proxy_server, proxy_server) + self.assertEqual(sg.config.proxy_port, proxy_port) + self.assertEqual(sg.config.proxy_user, proxy_user) + self.assertEqual(sg.config.proxy_pass, proxy_pass) def test_malformatted_proxy_info(self): proxy_server = "someserver.com" @@ -163,7 +163,7 @@ def test_filters(self): args = ['',[[path, relation, value]],None] result = self.get_call_rpc_params(args, {}) actual_condition = result['filters']['conditions'][0] - self.assertEquals(expected_condition, actual_condition) + self.assertEqual(expected_condition, actual_condition) @patch('shotgun_api3.Shotgun._call_rpc') def get_call_rpc_params(self, args, kws, call_rpc): @@ -175,7 +175,7 @@ def get_call_rpc_params(self, args, kws, call_rpc): def test_grouping(self): result = self.get_call_rpc_params(None, {}) - self.assertFalse(result.has_key('grouping')) + self.assertFalse('grouping' in result) grouping = ['something'] kws = {'grouping':grouping} result = self.get_call_rpc_params(None, kws) @@ -252,8 +252,8 @@ def assert_platform(self, sys_ret_val, expected): expected_local_path_field = "local_path_%s" % expected client_caps = api.shotgun.ClientCapabilities() - self.assertEquals(client_caps.platform, expected) - self.assertEquals(client_caps.local_path_field, expected_local_path_field) + self.assertEqual(client_caps.platform, expected) + self.assertEqual(client_caps.local_path_field, expected_local_path_field) finally: api.shotgun.sys.platform = platform @@ -262,8 +262,8 @@ def test_no_platform(self): try: api.shotgun.sys.platform = "unsupported" client_caps = api.shotgun.ClientCapabilities() - self.assertEquals(client_caps.platform, None) - self.assertEquals(client_caps.local_path_field, None) + self.assertEqual(client_caps.platform, None) + self.assertEqual(client_caps.local_path_field, None) finally: api.shotgun.sys.platform = platform @@ -275,7 +275,7 @@ def test_py_version(self, mock_sys): mock_sys.version_info = (major, minor, micro, 'final', 0) expected_py_version = "%s.%s" % (major, minor) client_caps = api.shotgun.ClientCapabilities() - self.assertEquals(client_caps.py_version, expected_py_version) + self.assertEqual(client_caps.py_version, expected_py_version) class TestFilters(unittest.TestCase): def test_empty(self): @@ -285,7 +285,7 @@ def test_empty(self): } result = api.shotgun._translate_filters([], None) - self.assertEquals(result, expected) + self.assertEqual(result, expected) def test_simple(self): filters = [ @@ -302,7 +302,7 @@ def test_simple(self): } result = api.shotgun._translate_filters(filters, "any") - self.assertEquals(result, expected) + self.assertEqual(result, expected) # Test both styles of passing arrays def test_arrays(self): @@ -318,14 +318,14 @@ def test_arrays(self): ] result = api.shotgun._translate_filters(filters, "all") - self.assertEquals(result, expected) + self.assertEqual(result, expected) filters = [ ["code", "in", ["test1", "test2", "test3"]] ] result = api.shotgun._translate_filters(filters, "all") - self.assertEquals(result, expected) + self.assertEqual(result, expected) def test_nested(self): filters = [ @@ -368,7 +368,7 @@ def test_nested(self): } result = api.shotgun._translate_filters(filters, "all") - self.assertEquals(result, expected) + self.assertEqual(result, expected) def test_invalid(self): self.assertRaises(api.ShotgunError, api.shotgun._translate_filters, [], "bogus") From cedb4b5c0ac94f907511c06db268d733a0fbf81e Mon Sep 17 00:00:00 2001 From: hiroshi seki Date: Tue, 8 May 2018 11:47:35 +0900 Subject: [PATCH 2/5] fix some bugs --- .travis.yml | 5 ++-- shotgun_api3/lib/httplib2/__init__.py | 4 ++-- shotgun_api3/lib/simplejson/decoder.py | 8 +------ shotgun_api3/shotgun.py | 33 +++++++++++++++++--------- tests/base.py | 6 ++--- tests/test_client.py | 8 +++---- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index d8bfa73f0..9b9f5d657 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: python python: - - "2.6" - - "2.7" + - "3.4" + - "3.5" + - "3.6" # command to install dependencies install: - pip install -r tests/ci_requirements.txt diff --git a/shotgun_api3/lib/httplib2/__init__.py b/shotgun_api3/lib/httplib2/__init__.py index a601ff797..d5f1278e2 100644 --- a/shotgun_api3/lib/httplib2/__init__.py +++ b/shotgun_api3/lib/httplib2/__init__.py @@ -398,7 +398,7 @@ def _decompressContent(response, new_content): encoding = response.get('content-encoding', None) if encoding in ['gzip', 'deflate']: if encoding == 'gzip': - content = gzip.GzipFile(fileobj=io.StringIO(new_content)).read() + content = gzip.GzipFile(fileobj=io.BytesIO(new_content)).read() if encoding == 'deflate': content = zlib.decompress(content) response['content-length'] = str(len(content)) @@ -942,7 +942,7 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, ca_certs=None, disable_ssl_certificate_validation=False): http.client.HTTPSConnection.__init__(self, host, port=port, key_file=key_file, - cert_file=cert_file, strict=strict) + cert_file=cert_file) self.timeout = timeout self.proxy_info = proxy_info if ca_certs is None: diff --git a/shotgun_api3/lib/simplejson/decoder.py b/shotgun_api3/lib/simplejson/decoder.py index d308e0e3c..8d5c4fa74 100644 --- a/shotgun_api3/lib/simplejson/decoder.py +++ b/shotgun_api3/lib/simplejson/decoder.py @@ -18,13 +18,7 @@ def _import_c_scanstring(): FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL def _floatconstants(): - _BYTES = '7FF80000000000007FF0000000000000'.decode('hex') - # The struct module in Python 2.4 would get frexp() out of range here - # when an endian is specified in the format string. Fixed in Python 2.5+ - if sys.byteorder != 'big': - _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1] - nan, inf = struct.unpack('dd', _BYTES) - return nan, inf, -inf + return float('nan'), float('inf'), float('-inf') NaN, PosInf, NegInf = _floatconstants() diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 139cb8ef6..9c920877c 100755 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -567,8 +567,8 @@ def __init__(self, # and auth header auth, self.config.server = urllib.parse.splituser(urllib.parse.urlsplit(base_url).netloc) if auth: - auth = base64.encodestring(urllib.parse.unquote(auth)) - self.config.authorization = "Basic " + auth.strip() + auth = base64.encodestring(urllib.parse.unquote(auth).encode()) + self.config.authorization = b"Basic " + auth.strip() # foo:bar@123.456.789.012:3456 if http_proxy: @@ -3075,6 +3075,19 @@ def entity_types(self): # ======================================================================== # RPC Functions + def _process_str(self, _s): + if isinstance(_s, dict): + return self._process_dict(_s) + elif isinstance(_s, bytes): + return _s.decode() + return _s + + def _process_dict(self, _d): + if not isinstance(_d, dict): + return _d + + return {self._process_str(k): self._process_str(v) for k, v in _d.items()} + def _call_rpc(self, method, params, include_auth_params=True, first=False): """ Call the specified method on the Shotgun Server sending the supplied payload. @@ -3109,12 +3122,12 @@ def _call_rpc(self, method, params, include_auth_params=True, first=False): response = self._transform_inbound(response) if not isinstance(response, dict) or "results" not in response: - return response + return self._process_dict(response) results = response.get("results") if first and isinstance(results, list): - return results[0] - return results + return self._process_dict(results[0]) + return self._process_dict(results) def _auth_params(self): """ @@ -3207,7 +3220,6 @@ def _encode_payload(self, payload): requires it. The unicode string is then encoded as 'utf-8' as it must be in a single byte encoding to go over the wire. """ - wire = json.dumps(payload, ensure_ascii=False) if isinstance(wire, str): return wire.encode("utf-8") @@ -3371,7 +3383,7 @@ def _decode_dict(dct): v = _decode_list(v) newdict[k] = v return newdict - return json.loads(body, object_hook=_decode_dict) + return json.loads(body.decode(), object_hook=_decode_dict) def _response_errors(self, sg_response): @@ -3420,6 +3432,9 @@ def _visit_data(self, data, visitor): for k, v in data.items() ) + # if isinstance(data, str): + # return data.encode() + return visitor(data) def _transform_outbound(self, data): @@ -3460,10 +3475,6 @@ def _outbound_visitor(value): value = _change_tz(value) return value.strftime("%Y-%m-%dT%H:%M:%SZ") - if isinstance(value, str): - # Convert strings to unicode - return value.decode("utf-8") - return value return self._visit_data(data, _outbound_visitor) diff --git a/tests/base.py b/tests/base.py index 8aaa0eb90..54172591b 100644 --- a/tests/base.py +++ b/tests/base.py @@ -126,7 +126,7 @@ def _mock_http(self, data, headers=None, status=None): return if not isinstance(data, str): - data = json.dumps(data, ensure_ascii=False, encoding="utf-8") + data = json.dumps(data, ensure_ascii=False) resp_headers = { 'cache-control': 'no-cache', 'connection': 'close', @@ -149,8 +149,8 @@ def _assert_http_method(self, method, params, check_auth=True): """Asserts _http_request is called with the method and params.""" args, _ = self.sg._http_request.call_args arg_body = args[2] - assert isinstance(arg_body, str) - arg_body = json.loads(arg_body) + assert isinstance(arg_body, bytes) + arg_body = json.loads(arg_body.decode()) arg_params = arg_body.get("params") diff --git a/tests/test_client.py b/tests/test_client.py index 26a131dbc..5dbfe8b6f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -147,7 +147,7 @@ def test_url(self): # login:password@domain auth_url = "%s%s@%s" % (self.uri_prefix, login_password, self.domain) sg = api.Shotgun(auth_url, None, None, connect=False) - expected = "Basic " + base64.encodestring(login_password).strip() + expected = b"Basic " + base64.encodestring(login_password.encode()).strip() self.assertEqual(expected, sg.config.authorization) def test_authorization(self): @@ -168,7 +168,7 @@ def test_authorization(self): verb, path, body, headers = args expected = "Basic " + base64.encodestring(login_password).strip() - self.assertEqual(expected, headers.get("Authorization")) + self.assertEqual(expected.encode(), headers.get("Authorization")) def test_user_agent(self): """User-Agent passed to server""" @@ -372,7 +372,7 @@ def test_encode_payload(self): self.assertTrue(isinstance(j, bytes)) def test_decode_response_ascii(self): - self._assert_decode_resonse(True, "my data \u00E0".encode('utf8')) + self._assert_decode_resonse(True, "my data \u00E0".encode('utf8').decode()) def test_decode_response_unicode(self): self._assert_decode_resonse(False, "my data \u00E0") @@ -393,7 +393,7 @@ def _assert_decode_resonse(self, ensure_ascii, data): ensure_ascii = ensure_ascii, connect=False) - j = json.dumps(d, ensure_ascii=ensure_ascii, encoding="utf-8") + j = json.dumps(d, ensure_ascii=ensure_ascii) self.assertEqual(d, sg._decode_response(headers, j)) headers["content-type"] = "text/javascript" From 6c35ab27833487db04529258aeede5b5f755c0ee Mon Sep 17 00:00:00 2001 From: hiroshi seki Date: Tue, 8 May 2018 12:17:22 +0900 Subject: [PATCH 3/5] use make_boundary instead of choose_boundary --- shotgun_api3/shotgun.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 9c920877c..3fa2db1ff 100755 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -34,7 +34,7 @@ import io # used for attachment upload import datetime import logging -import email # used for attachment upload +from email.generator import _make_boundary as make_boundary # used for attachment upload import os import re import copy @@ -3900,7 +3900,7 @@ def http_request(self, request): def encode(self, params, files, boundary=None, buffer=None): if boundary is None: - boundary = email.choose_boundary() + boundary = make_boundary() if buffer is None: buffer = io.StringIO() for (key, value) in params: From fdda095bc85e51cd4bf4ea448d079c41f24aba76 Mon Sep 17 00:00:00 2001 From: hiroshi seki Date: Tue, 8 May 2018 16:58:11 +0900 Subject: [PATCH 4/5] modify appvayor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index bd71103bf..0dafbdb99 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ environment: matrix: - - PYTHON: "C:\\Python27" + - PYTHON: "C:\\Python34" # To update these values, visit AppVeyor's site, click the user icon and scroll down to Encrypt Data. SG_SERVER_URL: From a2abb9f3187ba40b0b1a65c0c3c1e5cb4c28580f Mon Sep 17 00:00:00 2001 From: hiroshi seki Date: Tue, 8 May 2018 17:29:29 +0900 Subject: [PATCH 5/5] autopep8 --in-place --select=F401,W291,E201,E202,E203,E231 ./tests/*.py ./shotgun_api3/shotgun.py ./shotgun_api3/lib/*.py --- shotgun_api3/lib/mimetypes.py | 258 ++++++++++++++-------------- shotgun_api3/lib/sgtimezone.py | 4 +- shotgun_api3/shotgun.py | 126 +++++++------- tests/base.py | 132 +++++++-------- tests/dummy_data.py | 6 +- tests/httplib2test.py | 184 ++++++++++---------- tests/test_api.py | 296 ++++++++++++++++----------------- tests/test_api_long.py | 10 +- tests/test_client.py | 106 ++++++------ tests/tests_proxy.py | 2 +- tests/tests_unit.py | 100 +++++------ 11 files changed, 612 insertions(+), 612 deletions(-) diff --git a/shotgun_api3/lib/mimetypes.py b/shotgun_api3/lib/mimetypes.py index 72a1e1951..b2c734934 100644 --- a/shotgun_api3/lib/mimetypes.py +++ b/shotgun_api3/lib/mimetypes.py @@ -33,8 +33,8 @@ _winreg = None __all__ = [ - "guess_type","guess_extension","guess_all_extensions", - "add_type","read_mime_types","init" + "guess_type", "guess_extension", "guess_all_extensions", + "add_type", "read_mime_types", "init" ] knownfiles = [ @@ -402,131 +402,131 @@ def _default_mime_types(): # If you add to these, please keep them sorted! types_map = { - '.a' : 'application/octet-stream', - '.ai' : 'application/postscript', - '.aif' : 'audio/x-aiff', - '.aifc' : 'audio/x-aiff', - '.aiff' : 'audio/x-aiff', - '.au' : 'audio/basic', - '.avi' : 'video/x-msvideo', - '.bat' : 'text/plain', - '.bcpio' : 'application/x-bcpio', - '.bin' : 'application/octet-stream', - '.bmp' : 'image/x-ms-bmp', - '.c' : 'text/plain', + '.a': 'application/octet-stream', + '.ai': 'application/postscript', + '.aif': 'audio/x-aiff', + '.aifc': 'audio/x-aiff', + '.aiff': 'audio/x-aiff', + '.au': 'audio/basic', + '.avi': 'video/x-msvideo', + '.bat': 'text/plain', + '.bcpio': 'application/x-bcpio', + '.bin': 'application/octet-stream', + '.bmp': 'image/x-ms-bmp', + '.c': 'text/plain', # Duplicates :( - '.cdf' : 'application/x-cdf', - '.cdf' : 'application/x-netcdf', - '.cpio' : 'application/x-cpio', - '.csh' : 'application/x-csh', - '.css' : 'text/css', - '.dll' : 'application/octet-stream', - '.doc' : 'application/msword', - '.dot' : 'application/msword', - '.dvi' : 'application/x-dvi', - '.eml' : 'message/rfc822', - '.eps' : 'application/postscript', - '.etx' : 'text/x-setext', - '.exe' : 'application/octet-stream', - '.gif' : 'image/gif', - '.gtar' : 'application/x-gtar', - '.h' : 'text/plain', - '.hdf' : 'application/x-hdf', - '.htm' : 'text/html', - '.html' : 'text/html', - '.ico' : 'image/vnd.microsoft.icon', - '.ief' : 'image/ief', - '.jpe' : 'image/jpeg', - '.jpeg' : 'image/jpeg', - '.jpg' : 'image/jpeg', - '.js' : 'application/javascript', - '.ksh' : 'text/plain', - '.latex' : 'application/x-latex', - '.m1v' : 'video/mpeg', - '.man' : 'application/x-troff-man', - '.me' : 'application/x-troff-me', - '.mht' : 'message/rfc822', - '.mhtml' : 'message/rfc822', - '.mif' : 'application/x-mif', - '.mov' : 'video/quicktime', - '.movie' : 'video/x-sgi-movie', - '.mp2' : 'audio/mpeg', - '.mp3' : 'audio/mpeg', - '.mp4' : 'video/mp4', - '.mpa' : 'video/mpeg', - '.mpe' : 'video/mpeg', - '.mpeg' : 'video/mpeg', - '.mpg' : 'video/mpeg', - '.ms' : 'application/x-troff-ms', - '.nc' : 'application/x-netcdf', - '.nws' : 'message/rfc822', - '.o' : 'application/octet-stream', - '.obj' : 'application/octet-stream', - '.oda' : 'application/oda', - '.p12' : 'application/x-pkcs12', - '.p7c' : 'application/pkcs7-mime', - '.pbm' : 'image/x-portable-bitmap', - '.pdf' : 'application/pdf', - '.pfx' : 'application/x-pkcs12', - '.pgm' : 'image/x-portable-graymap', - '.pl' : 'text/plain', - '.png' : 'image/png', - '.pnm' : 'image/x-portable-anymap', - '.pot' : 'application/vnd.ms-powerpoint', - '.ppa' : 'application/vnd.ms-powerpoint', - '.ppm' : 'image/x-portable-pixmap', - '.pps' : 'application/vnd.ms-powerpoint', - '.ppt' : 'application/vnd.ms-powerpoint', - '.ps' : 'application/postscript', - '.pwz' : 'application/vnd.ms-powerpoint', - '.py' : 'text/x-python', - '.pyc' : 'application/x-python-code', - '.pyo' : 'application/x-python-code', - '.qt' : 'video/quicktime', - '.ra' : 'audio/x-pn-realaudio', - '.ram' : 'application/x-pn-realaudio', - '.ras' : 'image/x-cmu-raster', - '.rdf' : 'application/xml', - '.rgb' : 'image/x-rgb', - '.roff' : 'application/x-troff', - '.rtx' : 'text/richtext', - '.sgm' : 'text/x-sgml', - '.sgml' : 'text/x-sgml', - '.sh' : 'application/x-sh', - '.shar' : 'application/x-shar', - '.snd' : 'audio/basic', - '.so' : 'application/octet-stream', - '.src' : 'application/x-wais-source', + '.cdf': 'application/x-cdf', + '.cdf': 'application/x-netcdf', + '.cpio': 'application/x-cpio', + '.csh': 'application/x-csh', + '.css': 'text/css', + '.dll': 'application/octet-stream', + '.doc': 'application/msword', + '.dot': 'application/msword', + '.dvi': 'application/x-dvi', + '.eml': 'message/rfc822', + '.eps': 'application/postscript', + '.etx': 'text/x-setext', + '.exe': 'application/octet-stream', + '.gif': 'image/gif', + '.gtar': 'application/x-gtar', + '.h': 'text/plain', + '.hdf': 'application/x-hdf', + '.htm': 'text/html', + '.html': 'text/html', + '.ico': 'image/vnd.microsoft.icon', + '.ief': 'image/ief', + '.jpe': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.jpg': 'image/jpeg', + '.js': 'application/javascript', + '.ksh': 'text/plain', + '.latex': 'application/x-latex', + '.m1v': 'video/mpeg', + '.man': 'application/x-troff-man', + '.me': 'application/x-troff-me', + '.mht': 'message/rfc822', + '.mhtml': 'message/rfc822', + '.mif': 'application/x-mif', + '.mov': 'video/quicktime', + '.movie': 'video/x-sgi-movie', + '.mp2': 'audio/mpeg', + '.mp3': 'audio/mpeg', + '.mp4': 'video/mp4', + '.mpa': 'video/mpeg', + '.mpe': 'video/mpeg', + '.mpeg': 'video/mpeg', + '.mpg': 'video/mpeg', + '.ms': 'application/x-troff-ms', + '.nc': 'application/x-netcdf', + '.nws': 'message/rfc822', + '.o': 'application/octet-stream', + '.obj': 'application/octet-stream', + '.oda': 'application/oda', + '.p12': 'application/x-pkcs12', + '.p7c': 'application/pkcs7-mime', + '.pbm': 'image/x-portable-bitmap', + '.pdf': 'application/pdf', + '.pfx': 'application/x-pkcs12', + '.pgm': 'image/x-portable-graymap', + '.pl': 'text/plain', + '.png': 'image/png', + '.pnm': 'image/x-portable-anymap', + '.pot': 'application/vnd.ms-powerpoint', + '.ppa': 'application/vnd.ms-powerpoint', + '.ppm': 'image/x-portable-pixmap', + '.pps': 'application/vnd.ms-powerpoint', + '.ppt': 'application/vnd.ms-powerpoint', + '.ps': 'application/postscript', + '.pwz': 'application/vnd.ms-powerpoint', + '.py': 'text/x-python', + '.pyc': 'application/x-python-code', + '.pyo': 'application/x-python-code', + '.qt': 'video/quicktime', + '.ra': 'audio/x-pn-realaudio', + '.ram': 'application/x-pn-realaudio', + '.ras': 'image/x-cmu-raster', + '.rdf': 'application/xml', + '.rgb': 'image/x-rgb', + '.roff': 'application/x-troff', + '.rtx': 'text/richtext', + '.sgm': 'text/x-sgml', + '.sgml': 'text/x-sgml', + '.sh': 'application/x-sh', + '.shar': 'application/x-shar', + '.snd': 'audio/basic', + '.so': 'application/octet-stream', + '.src': 'application/x-wais-source', '.sv4cpio': 'application/x-sv4cpio', - '.sv4crc' : 'application/x-sv4crc', - '.swf' : 'application/x-shockwave-flash', - '.t' : 'application/x-troff', - '.tar' : 'application/x-tar', - '.tcl' : 'application/x-tcl', - '.tex' : 'application/x-tex', - '.texi' : 'application/x-texinfo', + '.sv4crc': 'application/x-sv4crc', + '.swf': 'application/x-shockwave-flash', + '.t': 'application/x-troff', + '.tar': 'application/x-tar', + '.tcl': 'application/x-tcl', + '.tex': 'application/x-tex', + '.texi': 'application/x-texinfo', '.texinfo': 'application/x-texinfo', - '.tif' : 'image/tiff', - '.tiff' : 'image/tiff', - '.tr' : 'application/x-troff', - '.tsv' : 'text/tab-separated-values', - '.txt' : 'text/plain', - '.ustar' : 'application/x-ustar', - '.vcf' : 'text/x-vcard', - '.wav' : 'audio/x-wav', - '.wiz' : 'application/msword', - '.wsdl' : 'application/xml', - '.xbm' : 'image/x-xbitmap', - '.xlb' : 'application/vnd.ms-excel', + '.tif': 'image/tiff', + '.tiff': 'image/tiff', + '.tr': 'application/x-troff', + '.tsv': 'text/tab-separated-values', + '.txt': 'text/plain', + '.ustar': 'application/x-ustar', + '.vcf': 'text/x-vcard', + '.wav': 'audio/x-wav', + '.wiz': 'application/msword', + '.wsdl': 'application/xml', + '.xbm': 'image/x-xbitmap', + '.xlb': 'application/vnd.ms-excel', # Duplicates :( - '.xls' : 'application/excel', - '.xls' : 'application/vnd.ms-excel', - '.xml' : 'text/xml', - '.xpdl' : 'application/xml', - '.xpm' : 'image/x-xpixmap', - '.xsl' : 'application/xml', - '.xwd' : 'image/x-xwindowdump', - '.zip' : 'application/zip', + '.xls': 'application/excel', + '.xls': 'application/vnd.ms-excel', + '.xml': 'text/xml', + '.xpdl': 'application/xml', + '.xpm': 'image/x-xpixmap', + '.xsl': 'application/xml', + '.xwd': 'image/x-xwindowdump', + '.zip': 'application/zip', } # These are non-standard types, commonly found in the wild. They will @@ -534,14 +534,14 @@ def _default_mime_types(): # Please sort these too common_types = { - '.jpg' : 'image/jpg', - '.mid' : 'audio/midi', + '.jpg': 'image/jpg', + '.mid': 'audio/midi', '.midi': 'audio/midi', - '.pct' : 'image/pict', - '.pic' : 'image/pict', + '.pct': 'image/pict', + '.pic': 'image/pict', '.pict': 'image/pict', - '.rtf' : 'application/rtf', - '.xul' : 'text/xul' + '.rtf': 'application/rtf', + '.xul': 'text/xul' } diff --git a/shotgun_api3/lib/sgtimezone.py b/shotgun_api3/lib/sgtimezone.py index ef06c8640..fbaf6678a 100644 --- a/shotgun_api3/lib/sgtimezone.py +++ b/shotgun_api3/lib/sgtimezone.py @@ -1,7 +1,7 @@ #! /opt/local/bin/python # ---------------------------------------------------------------------------- # SG_TIMEZONE module -# this is rolled into the this shotgun api file to avoid having to require +# this is rolled into the this shotgun api file to avoid having to require # current users of api2 to install new modules and modify PYTHONPATH info. # ---------------------------------------------------------------------------- @@ -23,7 +23,7 @@ class SgTimezone(object): DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET - def __init__(self): + def __init__(self): self.utc = UTC() self.local = LocalTimezone() diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 3fa2db1ff..338c74ad2 100755 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -57,7 +57,7 @@ from .sg_24 import * # mimetypes imported in version specific imports -mimetypes.add_type('video/webm','.webm') # webm and mp4 seem to be missing +mimetypes.add_type('video/webm', '.webm') # webm and mp4 seem to be missing mimetypes.add_type('video/mp4', '.mp4') # from some OS/distros LOG = logging.getLogger("shotgun_api3") @@ -598,7 +598,7 @@ def __init__(self, else: auth_string = "" proxy_addr = "http://%s%s:%d" % (auth_string, self.config.proxy_server, self.config.proxy_port) - self.config.proxy_handler = urllib.request.ProxyHandler({self.config.scheme : proxy_addr}) + self.config.proxy_handler = urllib.request.ProxyHandler({self.config.scheme: proxy_addr}) if ensure_ascii: self._json_loads = self._json_loads_ascii @@ -939,8 +939,8 @@ def _construct_read_parameters(self, params["return_fields"] = fields or ["id"] params["filters"] = filters params["return_only"] = (retired_only and 'retired') or "active" - params["paging"] = { "entities_per_page": self.config.records_per_page, - "current_page": 1 } + params["paging"] = {"entities_per_page": self.config.records_per_page, + "current_page": 1} if additional_filter_presets: params["additional_filter_presets"] = additional_filter_presets; @@ -958,7 +958,7 @@ def _construct_read_parameters(self, sort.setdefault("direction", "asc") sort_list.append({ 'field_name': sort['field_name'], - 'direction' : sort['direction'] + 'direction': sort['direction'] }) params['sorts'] = sort_list return params @@ -1225,9 +1225,9 @@ def create(self, entity_type, data, return_fields=None): upload_filmstrip_image = data.pop('filmstrip_image') params = { - "type" : entity_type, - "fields" : self._dict_to_list(data), - "return_fields" : return_fields + "type": entity_type, + "fields": self._dict_to_list(data), + "return_fields": return_fields } record = self._call_rpc("create", params, first=True) @@ -1299,9 +1299,9 @@ def update(self, entity_type, entity_id, data, multi_entity_update_modes=None): if data: params = { - "type" : entity_type, - "id" : entity_id, - "fields" : self._dict_to_list( + "type": entity_type, + "id": entity_id, + "fields": self._dict_to_list( data, extra_data=self._dict_to_extra_data( multi_entity_update_modes, "multi_entity_update_mode")) @@ -1348,8 +1348,8 @@ def delete(self, entity_type, entity_id): """ params = { - "type" : entity_type, - "id" : entity_id + "type": entity_type, + "id": entity_id } return self._call_rpc("delete", params) @@ -1369,8 +1369,8 @@ def revive(self, entity_type, entity_id): """ params = { - "type" : entity_type, - "id" : entity_id + "type": entity_type, + "id": entity_id } return self._call_rpc("revive", params) @@ -1455,7 +1455,7 @@ def _required_keys(message, required_keys, data): ['request_type', 'entity_type'], req) request_params = {'request_type': req['request_type'], - "type" : req["entity_type"]} + "type": req["entity_type"]} if req["request_type"] == "create": _required_keys("Batched create request", ['data'], req) @@ -1712,7 +1712,7 @@ def following(self, user, project=None, entity_type=None): self.server_caps.ensure_user_following_support() params = { - "user":user + "user": user } if project: params["project"] = project @@ -1918,9 +1918,9 @@ def schema_field_create(self, entity_type, data_type, display_name, properties=N """ params = { - "type" : entity_type, - "data_type" : data_type, - "properties" : [ + "type": entity_type, + "data_type": data_type, + "properties": [ {'property_name': 'name', 'value': display_name} ] } @@ -1952,10 +1952,10 @@ def schema_field_update(self, entity_type, field_name, properties): """ params = { - "type" : entity_type, - "field_name" : field_name, + "type": entity_type, + "field_name": field_name, "properties": [ - {"property_name" : k, "value" : v} + {"property_name": k, "value": v} for k, v in (properties or {}).items() ] } @@ -1976,8 +1976,8 @@ def schema_field_delete(self, entity_type, field_name): """ params = { - "type" : entity_type, - "field_name" : field_name + "type": entity_type, + "field_name": field_name } return self._call_rpc("schema_field_delete", params) @@ -2122,9 +2122,9 @@ def share_thumbnail(self, entities, thumbnail_path=None, source_entity=None, if filmstrip_thumbnail: filmstrip_thumbnail = 1 params = { - "entities" : ','.join(entities_str), + "entities": ','.join(entities_str), "source_entity": "%s_%s" % (source_entity['type'], source_entity['id']), - "filmstrip_thumbnail" : filmstrip_thumbnail, + "filmstrip_thumbnail": filmstrip_thumbnail, } url = urllib.parse.urlunparse((self.config.scheme, self.config.server, @@ -2301,8 +2301,8 @@ def _upload_to_storage(self, entity_type, entity_id, path, field_name, display_n "/upload/api_link_file", None, None, None)) params = { - "entity_type" : entity_type, - "entity_id" : entity_id, + "entity_type": entity_type, + "entity_id": entity_id, "upload_link_info": upload_info['upload_info'] } @@ -2352,8 +2352,8 @@ def _upload_to_sg(self, entity_type, entity_id, path, field_name, display_name, """ params = { - "entity_type" : entity_type, - "entity_id" : entity_id, + "entity_type": entity_type, + "entity_id": entity_id, } params.update(self._auth_params()) @@ -2409,8 +2409,8 @@ def _get_attachment_upload_info(self, is_thumbnail, filename, is_multipart_uploa upload_type = "Attachment" params = { - "upload_type" : upload_type, - "filename" : filename + "upload_type": upload_type, + "filename": filename } params["multipart_upload"] = is_multipart_upload @@ -2430,11 +2430,11 @@ def _get_attachment_upload_info(self, is_thumbnail, filename, is_multipart_uploa upload_info_parts = str(upload_info).split("\n") return { - "upload_url" : upload_info_parts[1], - "timestamp" : upload_info_parts[2], - "upload_type" : upload_info_parts[3], + "upload_url": upload_info_parts[1], + "timestamp": upload_info_parts[2], + "upload_type": upload_info_parts[3], "upload_id": upload_info_parts[4], - "upload_info" : upload_info + "upload_info": upload_info } def download_attachment(self, attachment=False, file_path=None, attachment_id=None): @@ -2691,7 +2691,7 @@ def update_project_last_accessed(self, project, user=None): if self.config.user_login: user = self.find_one('HumanUser', [['login', 'is', self.config.user_login]]) - params = { "project_id": project['id'], } + params = {"project_id": project['id'], } if user: params['user_id'] = user['id'] @@ -2767,7 +2767,7 @@ def note_thread_read(self, note_id, entity_fields=None): if not isinstance(entity_fields, dict): raise ValueError("entity_fields parameter must be a dictionary") - params = { "note_id": note_id, "entity_fields": entity_fields } + params = {"note_id": note_id, "entity_fields": entity_fields} record = self._call_rpc("note_thread_contents", params) result = self._parse_records(record) @@ -2851,10 +2851,10 @@ def text_search(self, text, entity_types, project_ids=None, limit=None): project_ids = project_ids or [] - params = { "text": text, + params = {"text": text, "entity_types": api_entity_types, "project_ids": project_ids, - "max_results": limit } + "max_results": limit} record = self._call_rpc("query_display_name_cache", params) result = self._parse_records(record)[0] @@ -2937,12 +2937,12 @@ def activity_stream_read(self, entity_type, entity_id, entity_fields=None, min_i if not isinstance(entity_fields, dict): raise ValueError("entity_fields parameter must be a dictionary") - params = { "type": entity_type, + params = {"type": entity_type, "id": entity_id, "max_id": max_id, "min_id": min_id, "limit": limit, - "entity_fields": entity_fields } + "entity_fields": entity_fields} record = self._call_rpc("activity_stream", params) result = self._parse_records(record)[0] @@ -2962,7 +2962,7 @@ def nav_expand(self, path, seed_entity_field=None, entity_fields=None): return self._call_rpc( "nav_expand", { - "path":path, + "path": path, "seed_entity_field": seed_entity_field, "entity_fields": entity_fields } @@ -2981,9 +2981,9 @@ def nav_search_string(self, root_path, search_string, seed_entity_field=None): return self._call_rpc( "nav_search", { - "root_path":root_path, + "root_path": root_path, "seed_entity_field": seed_entity_field, - "search_criteria": { "search_string": search_string } + "search_criteria": {"search_string": search_string} } ) @@ -3003,7 +3003,7 @@ def nav_search_entity(self, root_path, entity, seed_entity_field=None): { "root_path": root_path, "seed_entity_field": seed_entity_field, - "search_criteria": {"entity": entity } + "search_criteria": {"entity": entity} } ) @@ -3102,8 +3102,8 @@ def _call_rpc(self, method, params, include_auth_params=True, first=False): encoded_payload = self._encode_payload(payload) req_headers = { - "content-type" : "application/json; charset=utf-8", - "connection" : "keep-alive" + "content-type": "application/json; charset=utf-8", + "connection": "keep-alive" } http_status, resp_headers, body = self._make_call("POST", self.config.api_path, encoded_payload, req_headers) @@ -3136,8 +3136,8 @@ def _auth_params(self): # Used to authenticate HumanUser credentials if self.config.user_login and self.config.user_password: auth_params = { - "user_login" : str(self.config.user_login), - "user_password" : str(self.config.user_password), + "user_login": str(self.config.user_login), + "user_password": str(self.config.user_password), } if self.config.auth_token: auth_params["auth_token"] = str(self.config.auth_token) @@ -3145,8 +3145,8 @@ def _auth_params(self): # Use script name instead elif self.config.script_name and self.config.api_key: auth_params = { - "script_name" : str(self.config.script_name), - "script_key" : str(self.config.api_key), + "script_name": str(self.config.script_name), + "script_key": str(self.config.api_key), } # Authenticate using session_id @@ -3155,7 +3155,7 @@ def _auth_params(self): raise ShotgunError("Session token based authentication requires server version " "5.3.0 or higher, server is %s" % (self.server_caps.version,)) - auth_params = {"session_token" : str(self.config.session_token)} + auth_params = {"session_token": str(self.config.session_token)} # Request server side to raise exception for expired sessions. # This was added in as part of Shotgun 5.4.4 @@ -3208,8 +3208,8 @@ def _build_payload(self, method, params, include_auth_params=True): call_params.append(params) return { - "method_name" : method, - "params" : call_params + "method_name": method, + "params": call_params } def _encode_payload(self, payload): @@ -3613,8 +3613,8 @@ def _build_thumb_url(self, entity_type, entity_id): # curl "https://foo.com/upload/get_thumbnail_url?entity_type=Version&entity_id=1" # 1 # /files/0000/0000/0012/232/shot_thumb.jpg.jpg - entity_info = {'e_type':urllib.parse.quote(entity_type), - 'e_id':urllib.parse.quote(str(entity_id))} + entity_info = {'e_type': urllib.parse.quote(entity_type), + 'e_id': urllib.parse.quote(str(entity_id))} url = ("/upload/get_thumbnail_url?" + "entity_type=%(e_type)s&entity_id=%(e_id)s" % entity_info) @@ -3658,7 +3658,7 @@ def _dict_to_extra_data(self, d, key_name="value"): e.g. d {'foo' : 'bar'} changed to {'foo': {"value": 'bar'}] """ - return dict([(k, {key_name: v}) for (k,v) in (d or {}).items()]) + return dict([(k, {key_name: v}) for (k, v) in (d or {}).items()]) def _upload_file_to_storage(self, path, storage_url): """ @@ -3674,7 +3674,7 @@ def _upload_file_to_storage(self, path, storage_url): content_type = mimetypes.guess_type(filename)[0] content_type = content_type or "application/octet-stream" file_size = os.fstat(fd.fileno())[stat.ST_SIZE] - self._upload_data_to_storage(fd, content_type, file_size, storage_url ) + self._upload_data_to_storage(fd, content_type, file_size, storage_url) finally: fd.close() @@ -3703,7 +3703,7 @@ def _multipart_upload_file_to_storage(self, path, upload_info): data = fd.read(chunk_size) bytes_read += len(data) part_url = self._get_upload_part_link(upload_info, filename, part_number) - etags.append(self._upload_data_to_storage(data, content_type, len(data), part_url )) + etags.append(self._upload_data_to_storage(data, content_type, len(data), part_url)) part_number += 1 self._complete_multipart_upload(upload_info, filename, etags) @@ -3950,7 +3950,7 @@ def _translate_filters_dict(sg_filter): else: raise ShotgunError("Invalid filter_operator %s" % filter_operator) - if not isinstance(sg_filter["filters"], (list,tuple)): + if not isinstance(sg_filter["filters"], (list, tuple)): raise ShotgunError("Invalid filters, expected a list or a tuple, got %s" % sg_filter["filters"]) @@ -3962,7 +3962,7 @@ def _translate_filters_list(filters): conditions = [] for sg_filter in filters: - if isinstance(sg_filter, (list,tuple)): + if isinstance(sg_filter, (list, tuple)): conditions.append(_translate_filters_simple(sg_filter)) elif isinstance(sg_filter, dict): conditions.append(_translate_filters_dict(sg_filter)) diff --git a/tests/base.py b/tests/base.py index 54172591b..6d6202516 100644 --- a/tests/base.py +++ b/tests/base.py @@ -52,26 +52,26 @@ def setUp(self, auth_mode='ApiUser'): self.config.script_name, self.config.api_key, http_proxy=self.config.http_proxy, - connect=self.connect ) + connect=self.connect) elif auth_mode == 'HumanUser': self.sg = api.Shotgun(self.config.server_url, login=self.human_login, password=self.human_password, http_proxy=self.config.http_proxy, - connect=self.connect ) + connect=self.connect) elif auth_mode == 'SessionToken': - # first make an instance based on script key/name so + # first make an instance based on script key/name so # we can generate a session token sg = api.Shotgun(self.config.server_url, self.config.script_name, self.config.api_key, - http_proxy=self.config.http_proxy ) + http_proxy=self.config.http_proxy) self.session_token = sg.get_session_token() # now log in using session token self.sg = api.Shotgun(self.config.server_url, session_token=self.session_token, http_proxy=self.config.http_proxy, - connect=self.connect ) + connect=self.connect) else: raise ValueError("Unknown value for auth_mode: %s" % auth_mode) @@ -112,7 +112,7 @@ def _setup_mock(self): #create the server caps directly to say we have the correct version self.sg._server_caps = ServerCapabilities(self.sg.config.server, - {"version" : [2,4,0]}) + {"version": [2, 4, 0]}) def _mock_http(self, data, headers=None, status=None): @@ -128,13 +128,13 @@ def _mock_http(self, data, headers=None, status=None): if not isinstance(data, str): data = json.dumps(data, ensure_ascii=False) - resp_headers = { 'cache-control': 'no-cache', + resp_headers = {'cache-control': 'no-cache', 'connection': 'close', - 'content-length': (data and str(len(data))) or 0 , + 'content-length': (data and str(len(data))) or 0, 'content-type': 'application/json; charset=utf-8', 'date': 'Wed, 13 Apr 2011 04:18:58 GMT', 'server': 'Apache/2.2.3 (CentOS)', - 'status': '200 OK' } + 'status': '200 OK'} if headers: resp_headers.update(headers) @@ -166,27 +166,27 @@ def _assert_http_method(self, method, params, check_auth=True): def _setup_mock_data(self): - self.human_user = { 'id':1, - 'login':self.config.human_login, - 'type':'HumanUser' } - self.project = { 'id':2, - 'name':self.config.project_name, - 'type':'Project' } - self.shot = { 'id':3, - 'code':self.config.shot_code, - 'type':'Shot' } - self.asset = { 'id':4, - 'code':self.config.asset_code, - 'type':'Asset' } - self.version = { 'id':5, - 'code':self.config.version_code, - 'type':'Version' } - self.ticket = { 'id':6, - 'title':self.config.ticket_title, - 'type':'Ticket' } - self.playlist = { 'id':7, - 'code':self.config.playlist_code, - 'type':'Playlist'} + self.human_user = {'id': 1, + 'login': self.config.human_login, + 'type': 'HumanUser'} + self.project = {'id': 2, + 'name': self.config.project_name, + 'type': 'Project'} + self.shot = {'id': 3, + 'code': self.config.shot_code, + 'type': 'Shot'} + self.asset = {'id': 4, + 'code': self.config.asset_code, + 'type': 'Asset'} + self.version = {'id': 5, + 'code': self.config.version_code, + 'type': 'Version'} + self.ticket = {'id': 6, + 'title': self.config.ticket_title, + 'type': 'Ticket'} + self.playlist = {'id': 7, + 'code': self.config.playlist_code, + 'type': 'Playlist'} class LiveTestBase(TestBase): '''Test base for tests relying on connection to server.''' @@ -203,46 +203,46 @@ def setUp(self, auth_mode='ApiUser'): self.server_address = self.sg.server_caps.host def _setup_db(self, config): - data = {'name':self.config.project_name} + data = {'name': self.config.project_name} self.project = _find_or_create_entity(self.sg, 'Project', data) - data = {'name':self.config.human_name, - 'login':self.config.human_login, - 'password_proxy':self.config.human_password} + data = {'name': self.config.human_name, + 'login': self.config.human_login, + 'password_proxy': self.config.human_password} if self.sg_version >= (3, 0, 0): data['locked_until'] = None self.human_user = _find_or_create_entity(self.sg, 'HumanUser', data) - data = {'code':self.config.asset_code, - 'project':self.project} + data = {'code': self.config.asset_code, + 'project': self.project} keys = ['code'] self.asset = _find_or_create_entity(self.sg, 'Asset', data, keys) - data = {'project':self.project, - 'code':self.config.version_code, - 'entity':self.asset, - 'user':self.human_user, + data = {'project': self.project, + 'code': self.config.version_code, + 'entity': self.asset, + 'user': self.human_user, 'sg_frames_aspect_ratio': 13.3, 'frame_count': 33} - keys = ['code','project'] + keys = ['code', 'project'] self.version = _find_or_create_entity(self.sg, 'Version', data, keys) - keys = ['code','project'] - data = {'code':self.config.shot_code, - 'project':self.project} + keys = ['code', 'project'] + data = {'code': self.config.shot_code, + 'project': self.project} self.shot = _find_or_create_entity(self.sg, 'Shot', data, keys) - keys = ['project','user'] - data = {'project':self.project, - 'user':self.human_user, - 'content':'anything'} + keys = ['project', 'user'] + data = {'project': self.project, + 'user': self.human_user, + 'content': 'anything'} self.note = _find_or_create_entity(self.sg, 'Note', data, keys) - keys = ['code','project'] - data = {'project':self.project, - 'code':self.config.playlist_code} + keys = ['code', 'project'] + data = {'project': self.project, + 'code': self.config.playlist_code} self.playlist = _find_or_create_entity(self.sg, 'Playlist', data, keys) keys = ['code', 'entity_type'] @@ -251,26 +251,26 @@ def _setup_db(self, config): self.step = _find_or_create_entity(self.sg, 'Step', data, keys) keys = ['project', 'entity', 'content'] - data = {'project':self.project, - 'entity':self.asset, - 'content':self.config.task_content, - 'color':'Black', - 'due_date':'1968-10-13', + data = {'project': self.project, + 'entity': self.asset, + 'content': self.config.task_content, + 'color': 'Black', + 'due_date': '1968-10-13', 'task_assignees': [self.human_user], 'sg_status_list': 'ip'} self.task = _find_or_create_entity(self.sg, 'Task', data, keys) - data = {'project':self.project, - 'title':self.config.ticket_title, + data = {'project': self.project, + 'title': self.config.ticket_title, 'sg_priority': '3'} - keys = ['title','project', 'sg_priority'] + keys = ['title', 'project', 'sg_priority'] self.ticket = _find_or_create_entity(self.sg, 'Ticket', data, keys) keys = ['code'] - data = {'code':'api wrapper test storage', - 'mac_path':'nowhere', - 'windows_path':'nowhere', - 'linux_path':'nowhere'} + data = {'code': 'api wrapper test storage', + 'mac_path': 'nowhere', + 'windows_path': 'nowhere', + 'linux_path': 'nowhere'} self.local_storage = _find_or_create_entity(self.sg, 'LocalStorage', data, keys) @@ -301,14 +301,14 @@ def __init__(self): # configuration naming of "SG_{KEY}". Default is None. value = os.environ.get('SG_%s' % (str(key).upper())) if key in ['mock']: - value = (value == None) or (str(value).lower() in ['true','1']) + value = (value == None) or (str(value).lower() in ['true', '1']) setattr(self, key, value) def config_keys(self): return [ 'api_key', 'asset_code', 'http_proxy', 'human_login', 'human_name', - 'human_password', 'mock', 'project_name', 'script_name', - 'server_url', 'session_uuid', 'shot_code', 'task_content', + 'human_password', 'mock', 'project_name', 'script_name', + 'server_url', 'session_uuid', 'shot_code', 'task_content', 'version_code', 'playlist_code' ] diff --git a/tests/dummy_data.py b/tests/dummy_data.py index 5038300cd..c98f56f7f 100644 --- a/tests/dummy_data.py +++ b/tests/dummy_data.py @@ -1,12 +1,12 @@ """Dummy data returned for schema functions when mocking the server. -NOTE: Mostly abbreviated version of real data returned from the server. +NOTE: Mostly abbreviated version of real data returned from the server. """ schema_entity_read = {'Version': {'name': {'editable': False, 'value': 'Version'}}} schema_read = { - 'Version' : {'code': {'data_type': {'editable': False, 'value': 'text'}, + 'Version': {'code': {'data_type': {'editable': False, 'value': 'text'}, 'description': {'editable': True, 'value': ''}, 'editable': {'editable': False, 'value': True}, 'entity_type': {'editable': False, 'value': 'Version'}, @@ -873,4 +873,4 @@ 'valid_types': {'editable': True, 'value': ['HumanUser', 'ApiUser']}}}} - \ No newline at end of file + diff --git a/tests/httplib2test.py b/tests/httplib2test.py index 1ca87ddd4..61e6f9a58 100755 --- a/tests/httplib2test.py +++ b/tests/httplib2test.py @@ -57,24 +57,24 @@ def test(self): class ParserTest(unittest.TestCase): def testFromStd66(self): - self.assertEqual( ('http', 'example.com', '', None, None ), httplib2.parse_uri("http://example.com")) - self.assertEqual( ('https', 'example.com', '', None, None ), httplib2.parse_uri("https://example.com")) - self.assertEqual( ('https', 'example.com:8080', '', None, None ), httplib2.parse_uri("https://example.com:8080")) - self.assertEqual( ('http', 'example.com', '/', None, None ), httplib2.parse_uri("http://example.com/")) - self.assertEqual( ('http', 'example.com', '/path', None, None ), httplib2.parse_uri("http://example.com/path")) - self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', None ), httplib2.parse_uri("http://example.com/path?a=1&b=2")) - self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred")) - self.assertEqual( ('http', 'example.com', '/path', 'a=1&b=2', 'fred' ), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred")) + self.assertEqual(('http', 'example.com', '', None, None), httplib2.parse_uri("http://example.com")) + self.assertEqual(('https', 'example.com', '', None, None), httplib2.parse_uri("https://example.com")) + self.assertEqual(('https', 'example.com:8080', '', None, None), httplib2.parse_uri("https://example.com:8080")) + self.assertEqual(('http', 'example.com', '/', None, None), httplib2.parse_uri("http://example.com/")) + self.assertEqual(('http', 'example.com', '/path', None, None), httplib2.parse_uri("http://example.com/path")) + self.assertEqual(('http', 'example.com', '/path', 'a=1&b=2', None), httplib2.parse_uri("http://example.com/path?a=1&b=2")) + self.assertEqual(('http', 'example.com', '/path', 'a=1&b=2', 'fred'), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred")) + self.assertEqual(('http', 'example.com', '/path', 'a=1&b=2', 'fred'), httplib2.parse_uri("http://example.com/path?a=1&b=2#fred")) class UrlNormTest(unittest.TestCase): def test(self): - self.assertEqual( "http://example.org/", httplib2.urlnorm("http://example.org")[-1]) - self.assertEqual( "http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1]) - self.assertEqual( "http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1]) - self.assertEqual( "http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1]) - self.assertEqual( "http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1]) - self.assertEqual( httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80")) + self.assertEqual("http://example.org/", httplib2.urlnorm("http://example.org")[-1]) + self.assertEqual("http://example.org/", httplib2.urlnorm("http://EXAMple.org")[-1]) + self.assertEqual("http://example.org/?=b", httplib2.urlnorm("http://EXAMple.org?=b")[-1]) + self.assertEqual("http://example.org/mypath?a=b", httplib2.urlnorm("http://EXAMple.org/mypath?a=b")[-1]) + self.assertEqual("http://localhost:80/", httplib2.urlnorm("http://localhost:80")[-1]) + self.assertEqual(httplib2.urlnorm("http://localhost:80/"), httplib2.urlnorm("HTTP://LOCALHOST:80")) try: httplib2.urlnorm("/") self.fail("Non-absolute URIs should raise an exception") @@ -84,22 +84,22 @@ def test(self): class UrlSafenameTest(unittest.TestCase): def test(self): # Test that different URIs end up generating different safe names - self.assertEqual( "example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b")) - self.assertEqual( "example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b")) - self.assertEqual( "www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b")) - self.assertEqual( httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1])) - self.assertEqual( "www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b")) - self.assertNotEqual( httplib2.safename("http://www"), httplib2.safename("https://www")) + self.assertEqual("example.org,fred,a=b,58489f63a7a83c3b7794a6a398ee8b1f", httplib2.safename("http://example.org/fred/?a=b")) + self.assertEqual("example.org,fred,a=b,8c5946d56fec453071f43329ff0be46b", httplib2.safename("http://example.org/fred?/a=b")) + self.assertEqual("www.example.org,fred,a=b,499c44b8d844a011b67ea2c015116968", httplib2.safename("http://www.example.org/fred?/a=b")) + self.assertEqual(httplib2.safename(httplib2.urlnorm("http://www")[-1]), httplib2.safename(httplib2.urlnorm("http://WWW")[-1])) + self.assertEqual("www.example.org,fred,a=b,692e843a333484ce0095b070497ab45d", httplib2.safename("https://www.example.org/fred?/a=b")) + self.assertNotEqual(httplib2.safename("http://www"), httplib2.safename("https://www")) # Test the max length limits uri = "http://" + ("w" * 200) + ".org" uri2 = "http://" + ("w" * 201) + ".org" - self.assertNotEqual( httplib2.safename(uri2), httplib2.safename(uri)) + self.assertNotEqual(httplib2.safename(uri2), httplib2.safename(uri)) # Max length should be 200 + 1 (",") + 32 self.assertEqual(233, len(httplib2.safename(uri2))) self.assertEqual(233, len(httplib2.safename(uri))) # Unicode - if sys.version_info >= (2,3): - self.assertEqual( "xn--http,-4y1d.org,fred,a=b,579924c35db315e5a32e3d9963388193", httplib2.safename("http://\u2304.org/fred/?a=b")) + if sys.version_info >= (2, 3): + self.assertEqual("xn--http,-4y1d.org,fred,a=b,579924c35db315e5a32e3d9963388193", httplib2.safename("http://\u2304.org/fred/?a=b")) class _MyResponse(io.StringIO): def __init__(self, body, **kwargs): @@ -139,19 +139,19 @@ def getresponse(self): class HttpTest(unittest.TestCase): def setUp(self): - if os.path.exists(cacheDirName): + if os.path.exists(cacheDirName): [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)] self.http = httplib2.Http(cacheDirName) self.http.clear_credentials() def testConnectionType(self): - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False response, content = self.http.request("http://bitworking.org", connection_type=_MyHTTPConnection) self.assertEqual(response['content-location'], "http://bitworking.org") self.assertEqual(content, "the body") def testGetUnknownServer(self): - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False try: self.http.request("http://fred.bitworking.org/") self.fail("An httplib2.ServerNotFoundError Exception must be thrown on an unresolvable server.") @@ -167,12 +167,12 @@ def testGetUnknownServer(self): self.assertEqual(response.status, 400) def testGetIRI(self): - if sys.version_info >= (2,3): + if sys.version_info >= (2, 3): uri = urllib.parse.urljoin(base, "reflector/reflector.cgi?d=\N{CYRILLIC CAPITAL LETTER DJE}") (response, content) = self.http.request(uri, "GET") d = self.reflector(content) - self.assertTrue('QUERY_STRING' in d) - self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0) + self.assertTrue('QUERY_STRING' in d) + self.assertTrue(d['QUERY_STRING'].find('%D0%82') > 0) def testGetIsDefaultMethod(self): # Test that GET is the default method @@ -344,7 +344,7 @@ def testGet302RedirectionLimit(self): # Test that we can set a lower redirection limit # and that we raise an exception when we exceed # that limit. - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False uri = urllib.parse.urljoin(base, "302/twostep.asis") try: @@ -356,7 +356,7 @@ def testGet302RedirectionLimit(self): self.fail("Threw wrong kind of exception ") # Re-run the test with out the exceptions - self.http.force_exception_to_status_code = True + self.http.force_exception_to_status_code = True (response, content) = self.http.request(uri, "GET", redirections = 1) self.assertEqual(response.status, 500) @@ -368,7 +368,7 @@ def testGet302RedirectionLimit(self): def testGet302NoLocation(self): # Test that we throw an exception when we get # a 302 with no Location: header. - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False uri = urllib.parse.urljoin(base, "302/no-location.asis") try: (response, content) = self.http.request(uri, "GET") @@ -379,7 +379,7 @@ def testGet302NoLocation(self): self.fail("Threw wrong kind of exception ") # Re-run the test with out the exceptions - self.http.force_exception_to_status_code = True + self.http.force_exception_to_status_code = True (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 500) @@ -402,7 +402,7 @@ def testGetViaHttps(self): def testGetViaHttpsSpecViolationOnLocation(self): # Test that we follow redirects through HTTPS # even if they violate the spec by including - # a relative Location: header instead of an + # a relative Location: header instead of an # absolute one. (response, content) = self.http.request("https://google.com/adsense", "GET") self.assertEqual(200, response.status) @@ -411,8 +411,8 @@ def testGetViaHttpsSpecViolationOnLocation(self): def testGetViaHttpsKeyCert(self): # At this point I can only test - # that the key and cert files are passed in - # correctly to httplib. It would be nice to have + # that the key and cert files are passed in + # correctly to httplib. It would be nice to have # a real https endpoint to test against. http = httplib2.Http(timeout=2) @@ -454,7 +454,7 @@ def testGet303NoRedirect(self): def test303ForDifferentMethods(self): # Test that all methods can be used uri = urllib.parse.urljoin(base, "303/redirect-to-reflector.cgi") - for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]: + for (method, method_on_303) in [("PUT", "GET"), ("DELETE", "GET"), ("POST", "GET"), ("GET", "GET"), ("HEAD", "GET")]: (response, content) = self.http.request(uri, method, body=" ") self.assertEqual(response['x-method'], method_on_303) @@ -485,36 +485,36 @@ def testGet304(self): self.assertEqual(response.fromcache, False) def testGetIgnoreEtag(self): - # Test that we can forcibly ignore ETags + # Test that we can forcibly ignore ETags uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) - self.assertTrue('HTTP_IF_NONE_MATCH' in d) + self.assertTrue('HTTP_IF_NONE_MATCH' in d) self.http.ignore_etag = True (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) self.assertEqual(response.fromcache, False) - self.assertFalse('HTTP_IF_NONE_MATCH' in d) + self.assertFalse('HTTP_IF_NONE_MATCH' in d) def testOverrideEtag(self): - # Test that we can forcibly ignore ETags + # Test that we can forcibly ignore ETags uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") self.assertNotEqual(response['etag'], "") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0'}) d = self.reflector(content) - self.assertTrue('HTTP_IF_NONE_MATCH' in d) - self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred") + self.assertTrue('HTTP_IF_NONE_MATCH' in d) + self.assertNotEqual(d['HTTP_IF_NONE_MATCH'], "fred") (response, content) = self.http.request(uri, "GET", headers = {'cache-control': 'max-age=0', 'if-none-match': 'fred'}) d = self.reflector(content) - self.assertTrue('HTTP_IF_NONE_MATCH' in d) - self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred") + self.assertTrue('HTTP_IF_NONE_MATCH' in d) + self.assertEqual(d['HTTP_IF_NONE_MATCH'], "fred") #MAP-commented this out because it consistently fails # def testGet304EndToEnd(self): @@ -533,7 +533,7 @@ def testOverrideEtag(self): # self.assertEqual(response.fromcache, True) def testGet304LastModified(self): - # Test that we can still handle a 304 + # Test that we can still handle a 304 # by only using the last-modified cache validator. uri = urllib.parse.urljoin(base, "304/last-modified-only/last-modified-only.txt") (response, content) = self.http.request(uri, "GET") @@ -639,7 +639,7 @@ def testVaryHeaderDouble(self): def testHeadGZip(self): - # Test that we don't try to decompress a HEAD response + # Test that we don't try to decompress a HEAD response uri = urllib.parse.urljoin(base, "gzip/final-destination.txt") (response, content) = self.http.request(uri, "HEAD") self.assertEqual(response.status, 200) @@ -658,7 +658,7 @@ def testGetGZip(self): def testGetGZipFailure(self): # Test that we raise a good exception when the gzip fails - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False uri = urllib.parse.urljoin(base, "gzip/failed-compression.asis") try: (response, content) = self.http.request(uri, "GET") @@ -669,21 +669,21 @@ def testGetGZipFailure(self): self.fail("Threw wrong kind of exception") # Re-run the test with out the exceptions - self.http.force_exception_to_status_code = True + self.http.force_exception_to_status_code = True (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 500) self.assertTrue(response.reason.startswith("Content purported")) def testTimeout(self): - self.http.force_exception_to_status_code = True + self.http.force_exception_to_status_code = True uri = urllib.parse.urljoin(base, "timeout/timeout.cgi") try: import socket - socket.setdefaulttimeout(1) + socket.setdefaulttimeout(1) except: # Don't run the test if we can't set the timeout - return + return (response, content) = self.http.request(uri) self.assertEqual(response.status, 408) self.assertTrue(response.reason.startswith("Request Timeout")) @@ -692,7 +692,7 @@ def testTimeout(self): def testIndividualTimeout(self): uri = urllib.parse.urljoin(base, "timeout/timeout.cgi") http = httplib2.Http(timeout=1) - http.force_exception_to_status_code = True + http.force_exception_to_status_code = True (response, content) = http.request(uri) self.assertEqual(response.status, 408) @@ -715,7 +715,7 @@ def testGetDeflate(self): def testGetDeflateFailure(self): # Test that we raise a good exception when the deflate fails - self.http.force_exception_to_status_code = False + self.http.force_exception_to_status_code = False uri = urllib.parse.urljoin(base, "deflate/failed-compression.asis") try: @@ -727,7 +727,7 @@ def testGetDeflateFailure(self): self.fail("Threw wrong kind of exception") # Re-run the test with out the exceptions - self.http.force_exception_to_status_code = True + self.http.force_exception_to_status_code = True (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 500) @@ -805,7 +805,7 @@ def testGetCacheControlNoCacheNoStoreRequest(self): self.assertEqual(response.fromcache, False) def testUpdateInvalidatesCache(self): - # Test that calling PUT or DELETE on a + # Test that calling PUT or DELETE on a # URI that is cache invalidates that cache. uri = urllib.parse.urljoin(base, "304/test_etag.txt") @@ -819,7 +819,7 @@ def testUpdateInvalidatesCache(self): self.assertEqual(response.fromcache, False) def testUpdateUsesCachedETag(self): - # Test that we natively support http://www.w3.org/1999/04/Editing/ + # Test that we natively support http://www.w3.org/1999/04/Editing/ uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") @@ -834,7 +834,7 @@ def testUpdateUsesCachedETag(self): self.assertEqual(response.status, 412) def testUpdateUsesCachedETagAndOCMethod(self): - # Test that we natively support http://www.w3.org/1999/04/Editing/ + # Test that we natively support http://www.w3.org/1999/04/Editing/ uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") @@ -849,7 +849,7 @@ def testUpdateUsesCachedETagAndOCMethod(self): def testUpdateUsesCachedETagOverridden(self): - # Test that we natively support http://www.w3.org/1999/04/Editing/ + # Test that we natively support http://www.w3.org/1999/04/Editing/ uri = urllib.parse.urljoin(base, "conditional-updates/test.cgi") (response, content) = self.http.request(uri, "GET") @@ -897,7 +897,7 @@ def testBasicAuthWithDomain(self): (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 401) - domain = urllib.parse.urlparse(base)[1] + domain = urllib.parse.urlparse(base)[1] self.http.add_credentials('joe', 'password', domain) (response, content) = self.http.request(uri, "GET") self.assertEqual(response.status, 200) @@ -979,10 +979,10 @@ def testDigestAuthNextNonceAndNC(self): # the nonce count back to 1 uri = urllib.parse.urljoin(base, "digest/file.txt") self.http.add_credentials('joe', 'password') - (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) + (response, content) = self.http.request(uri, "GET", headers = {"cache-control": "no-cache"}) info = httplib2._parse_www_authenticate(response, 'authentication-info') self.assertEqual(response.status, 200) - (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) + (response, content) = self.http.request(uri, "GET", headers = {"cache-control": "no-cache"}) info2 = httplib2._parse_www_authenticate(response, 'authentication-info') self.assertEqual(response.status, 200) @@ -993,27 +993,27 @@ def testDigestAuthStale(self): # Test that we can handle a nonce becoming stale uri = urllib.parse.urljoin(base, "digest-expire/file.txt") self.http.add_credentials('joe', 'password') - (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) + (response, content) = self.http.request(uri, "GET", headers = {"cache-control": "no-cache"}) info = httplib2._parse_www_authenticate(response, 'authentication-info') self.assertEqual(response.status, 200) time.sleep(3) # Sleep long enough that the nonce becomes stale - (response, content) = self.http.request(uri, "GET", headers = {"cache-control":"no-cache"}) + (response, content) = self.http.request(uri, "GET", headers = {"cache-control": "no-cache"}) self.assertFalse(response.fromcache) self.assertTrue(response._stale_digest) info3 = httplib2._parse_www_authenticate(response, 'authentication-info') self.assertEqual(response.status, 200) def reflector(self, content): - return dict( [tuple(x.split("=", 1)) for x in content.strip().split("\n")] ) + return dict([tuple(x.split("=", 1)) for x in content.strip().split("\n")]) def testReflector(self): uri = urllib.parse.urljoin(base, "reflector/reflector.cgi") (response, content) = self.http.request(uri, "GET") d = self.reflector(content) - self.assertTrue('HTTP_USER_AGENT' in d) + self.assertTrue('HTTP_USER_AGENT' in d) def testConnectionClose(self): uri = "http://www.google.com/" @@ -1067,7 +1067,7 @@ def testParseCacheControl(self): self.fail("Should not throw exception") def testNormalizeHeaders(self): - # Test that we normalize headers to lowercase + # Test that we normalize headers to lowercase h = httplib2._normalize_headers({'Cache-Control': 'no-cache', 'Other': 'Stuff'}) self.assertTrue('cache-control' in h) self.assertTrue('other' in h) @@ -1208,21 +1208,21 @@ def testExpirationModelDateAndExpiresMinFresh2(self): def testParseWWWAuthenticateEmpty(self): res = httplib2._parse_www_authenticate({}) - self.assertEqual(len(list(res.keys())), 0) + self.assertEqual(len(list(res.keys())), 0) def testParseWWWAuthenticate(self): # different uses of spaces around commas - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Test realm="test realm" , foo=foo ,bar="bar", baz=baz,qux=qux'}) self.assertEqual(len(list(res.keys())), 1) self.assertEqual(len(list(res['test'].keys())), 5) # tokens with non-alphanum - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'T*!%#st realm=to*!%#en, to*!%#en="quoted string"'}) self.assertEqual(len(list(res.keys())), 1) self.assertEqual(len(list(res['t*!%#st'].keys())), 2) # quoted string with quoted pairs - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Test realm="a \\"test\\" realm"'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Test realm="a \\"test\\" realm"'}) self.assertEqual(len(list(res.keys())), 1) self.assertEqual(res['test']['realm'], 'a "test" realm') @@ -1232,34 +1232,34 @@ def testParseWWWAuthenticateStrict(self): httplib2.USE_WWW_AUTH_STRICT_PARSING = 0; def testParseWWWAuthenticateBasic(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me"'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Basic realm="me"'}) basic = res['basic'] self.assertEqual('me', basic['realm']) - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm="MD5"'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Basic realm="me", algorithm="MD5"'}) basic = res['basic'] self.assertEqual('me', basic['realm']) self.assertEqual('MD5', basic['algorithm']) - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me", algorithm=MD5'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Basic realm="me", algorithm=MD5'}) basic = res['basic'] self.assertEqual('me', basic['realm']) self.assertEqual('MD5', basic['algorithm']) def testParseWWWAuthenticateBasic2(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic realm="me",other="fred" '}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Basic realm="me",other="fred" '}) basic = res['basic'] self.assertEqual('me', basic['realm']) self.assertEqual('fred', basic['other']) def testParseWWWAuthenticateBasic3(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': 'Basic REAlm="me" '}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Basic REAlm="me" '}) basic = res['basic'] self.assertEqual('me', basic['realm']) def testParseWWWAuthenticateDigest(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': + res = httplib2._parse_www_authenticate({'www-authenticate': 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"'}) digest = res['digest'] self.assertEqual('testrealm@host.com', digest['realm']) @@ -1267,7 +1267,7 @@ def testParseWWWAuthenticateDigest(self): def testParseWWWAuthenticateMultiple(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': + res = httplib2._parse_www_authenticate({'www-authenticate': 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Basic REAlm="me" '}) digest = res['digest'] self.assertEqual('testrealm@host.com', digest['realm']) @@ -1280,7 +1280,7 @@ def testParseWWWAuthenticateMultiple(self): def testParseWWWAuthenticateMultiple2(self): # Handle an added comma between challenges, which might get thrown in if the challenges were # originally sent in separate www-authenticate headers. - res = httplib2._parse_www_authenticate({ 'www-authenticate': + res = httplib2._parse_www_authenticate({'www-authenticate': 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me" '}) digest = res['digest'] self.assertEqual('testrealm@host.com', digest['realm']) @@ -1293,7 +1293,7 @@ def testParseWWWAuthenticateMultiple2(self): def testParseWWWAuthenticateMultiple3(self): # Handle an added comma between challenges, which might get thrown in if the challenges were # originally sent in separate www-authenticate headers. - res = httplib2._parse_www_authenticate({ 'www-authenticate': + res = httplib2._parse_www_authenticate({'www-authenticate': 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'}) digest = res['digest'] self.assertEqual('testrealm@host.com', digest['realm']) @@ -1307,22 +1307,22 @@ def testParseWWWAuthenticateMultiple3(self): self.assertEqual('UsernameToken', wsse['profile']) def testParseWWWAuthenticateMultiple4(self): - res = httplib2._parse_www_authenticate({ 'www-authenticate': - 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'}) + res = httplib2._parse_www_authenticate({'www-authenticate': + 'Digest realm="test-real.m@host.com", qop \t=\t"\tauth,auth-int", nonce="(*)&^&$%#",opaque="5ccc069c403ebaf9f0171e9517f40e41", Basic REAlm="me", WSSE realm="foo", profile="UsernameToken"'}) digest = res['digest'] self.assertEqual('test-real.m@host.com', digest['realm']) self.assertEqual('\tauth,auth-int', digest['qop']) self.assertEqual('(*)&^&$%#', digest['nonce']) def testParseWWWAuthenticateMoreQuoteCombos(self): - res = httplib2._parse_www_authenticate({'www-authenticate':'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'}) + res = httplib2._parse_www_authenticate({'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true'}) digest = res['digest'] self.assertEqual('myrealm', digest['realm']) def testDigestObject(self): credentials = ('joe', 'password') host = None - request_uri = '/projects/httplib2/test/digest/' + request_uri = '/projects/httplib2/test/digest/' headers = {} response = { 'www-authenticate': 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth"' @@ -1330,7 +1330,7 @@ def testDigestObject(self): content = "" d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None) - d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46") + d.request("GET", request_uri, headers, content, cnonce="33033375ec278a46") our_request = "Authorization: %s" % headers['Authorization'] working_request = 'Authorization: Digest username="joe", realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", uri="/projects/httplib2/test/digest/", algorithm=MD5, response="97ed129401f7cdc60e5db58a80f3ea8b", qop=auth, nc=00000001, cnonce="33033375ec278a46"' self.assertEqual(our_request, working_request) @@ -1339,28 +1339,28 @@ def testDigestObject(self): def testDigestObjectStale(self): credentials = ('joe', 'password') host = None - request_uri = '/projects/httplib2/test/digest/' + request_uri = '/projects/httplib2/test/digest/' headers = {} - response = httplib2.Response({ }) + response = httplib2.Response({}) response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true' response.status = 401 content = "" d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None) # Returns true to force a retry - self.assertTrue( d.response(response, content) ) + self.assertTrue(d.response(response, content)) def testDigestObjectAuthInfo(self): credentials = ('joe', 'password') host = None - request_uri = '/projects/httplib2/test/digest/' + request_uri = '/projects/httplib2/test/digest/' headers = {} - response = httplib2.Response({ }) + response = httplib2.Response({}) response['www-authenticate'] = 'Digest realm="myrealm", nonce="Ygk86AsKBAA=3516200d37f9a3230352fde99977bd6d472d4306", algorithm=MD5, qop="auth", stale=true' response['authentication-info'] = 'nextnonce="fred"' content = "" d = httplib2.DigestAuthentication(credentials, host, request_uri, headers, response, content, None) # Returns true to force a retry - self.assertFalse( d.response(response, content) ) + self.assertFalse(d.response(response, content)) self.assertEqual('fred', d.challenge['nonce']) self.assertEqual(1, d.challenge['nc']) @@ -1389,7 +1389,7 @@ def testEnd2End(self): end2end = httplib2._get_end2end_headers(response) self.assertEqual(0, len(end2end)) - # Degenerate case of connection referrring to a header not passed in + # Degenerate case of connection referrring to a header not passed in response = {'connection': 'content-type'} end2end = httplib2._get_end2end_headers(response) self.assertEqual(0, len(end2end)) diff --git a/tests/test_api.py b/tests/test_api.py index d6a09f916..f18343f49 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -25,7 +25,7 @@ class TestShotgunApi(base.LiveTestBase): def setUp(self): super(TestShotgunApi, self).setUp() # give note unicode content - self.sg.update('Note', self.note['id'], {'content':'La Pe\xf1a'}) + self.sg.update('Note', self.note['id'], {'content': 'La Pe\xf1a'}) def test_info(self): """Called info""" @@ -35,8 +35,8 @@ def test_info(self): def test_server_dates(self): """Pass datetimes to the server""" #TODO check results - t = { 'project': self.project, - 'start_date': datetime.date.today() } + t = {'project': self.project, + 'start_date': datetime.date.today()} self.sg.create('Task', t, ['content', 'sg_status_list']) @@ -45,19 +45,19 @@ def test_batch(self): requests = [ { - "request_type" : "create", - "entity_type" : "Shot", + "request_type": "create", + "entity_type": "Shot", "data": { - "code" : "New Shot 5", - "project" : self.project + "code": "New Shot 5", + "project": self.project } }, { - "request_type" : "update", - "entity_type" : "Shot", - "entity_id" : self.shot['id'], - "data" : { - "code" : "Changed 1" + "request_type": "update", + "entity_type": "Shot", + "entity_id": self.shot['id'], + "data": { + "code": "Changed 1" } }] @@ -67,16 +67,16 @@ def test_batch(self): self.assertTrue(new_shot.get("id")) new_shot_id = new_shot["id"] - requests = [{ "request_type" : "delete", - "entity_type" : "Shot", - "entity_id" : new_shot_id + requests = [{"request_type": "delete", + "entity_type": "Shot", + "entity_id": new_shot_id }, { - "request_type" : "update", - "entity_type" : "Shot", - "entity_id" : self.shot['id'], - "data" : { - "code" : self.shot['code'] + "request_type": "update", + "entity_type": "Shot", + "entity_id": self.shot['id'], + "data": { + "code": self.shot['code'] } } ] @@ -93,9 +93,9 @@ def test_create_update_delete(self): """Called create, update, delete, revive""" data = { 'project': self.project, - 'code':'JohnnyApple_Design01_FaceFinal', + 'code': 'JohnnyApple_Design01_FaceFinal', 'description': 'fixed rig per director final notes', - 'sg_status_list':'rev', + 'sg_status_list': 'rev', 'entity': self.asset, 'user': self.human_user } @@ -107,7 +107,7 @@ def test_create_update_delete(self): #TODO: test returned fields are requested fields data = data = { - "description" : "updated test" + "description": "updated test" } version = self.sg.update("Version", version["id"], data) self.assertTrue(isinstance(version, dict)) @@ -144,7 +144,7 @@ def test_upload_download(self): this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size attach_id = self.sg.upload("Ticket", @@ -215,7 +215,7 @@ def test_upload_download(self): self.assertRaises(TypeError, self.sg.download_attachment, "/path/to/some/file.jpg") self.assertRaises(ValueError, self.sg.download_attachment, - {"id":123, "type":"Shot"}) + {"id": 123, "type": "Shot"}) self.assertRaises(TypeError, self.sg.download_attachment) # cleanup @@ -225,7 +225,7 @@ def test_upload_thumbnail_in_create(self): """Upload a thumbnail via the create method""" this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size # test thumbnail upload @@ -268,7 +268,7 @@ def test_upload_thumbnail_for_version(self): """simple upload thumbnail for version test.""" this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size # upload thumbnail @@ -292,7 +292,7 @@ def test_upload_thumbnail_for_version(self): # clear thumbnail response_clear_thumbnail = self.sg.update("Version", - self.version['id'], {'image':None}) + self.version['id'], {'image': None}) expected_clear_thumbnail = {'id': self.version['id'], 'image': None, 'type': 'Version'} self.assertEqual(expected_clear_thumbnail, response_clear_thumbnail) @@ -300,7 +300,7 @@ def test_upload_thumbnail_for_task(self): """simple upload thumbnail for task test.""" this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size # upload thumbnail @@ -379,7 +379,7 @@ def test_share_thumbnail(self): """share thumbnail between two entities""" this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) # upload thumbnail to first entity and share it with the rest thumbnail_id = self.sg.share_thumbnail( @@ -466,7 +466,7 @@ def test_summary_include_archived_projects(self): """Test summarize with archived project""" if self.sg.server_caps.version > (5, 3, 13): # archive project - self.sg.update('Project', self.project['id'], {'archived':True}) + self.sg.update('Project', self.project['id'], {'archived': True}) # Ticket #25082 ability to hide archived projects in summary summaries = [{'field': 'id', 'type': 'count'}] grouping = [{'direction': 'asc', 'field': 'id', 'type': 'exact'}] @@ -477,7 +477,7 @@ def test_summary_include_archived_projects(self): grouping=grouping, include_archived_projects=False) self.assertEqual(result['summaries']['id'], 0) - self.sg.update('Project', self.project['id'], {'archived':False}) + self.sg.update('Project', self.project['id'], {'archived': False}) def test_summary_values(self): """Test summarize return data""" @@ -576,7 +576,7 @@ def test_ensure_ascii(self): self.config.api_key, ensure_ascii=True) - result = sg_ascii.find_one('Note', [['id','is',self.note['id']]], fields=['content']) + result = sg_ascii.find_one('Note', [['id', 'is', self.note['id']]], fields=['content']) self.assertFalse(_has_unicode(result)) @@ -586,7 +586,7 @@ def test_ensure_unicode(self): self.config.script_name, self.config.api_key, ensure_ascii=False) - result = sg_unicode.find_one('Note', [['id','is',self.note['id']]], fields=['content']) + result = sg_unicode.find_one('Note', [['id', 'is', self.note['id']]], fields=['content']) self.assertTrue(_has_unicode(result)) def test_work_schedule(self): @@ -746,7 +746,7 @@ def test_set_list(self): entity = 'Note' entity_id = self.note['id'] field_name = 'sg_note_type' - pos_values = ['Internal','Client'] + pos_values = ['Internal', 'Client'] expected, actual = self.assert_set_field(entity, entity_id, field_name, @@ -755,17 +755,17 @@ def test_set_list(self): def test_set_multi_entity(self): - sg = shotgun_api3.Shotgun( self.config.server_url, + sg = shotgun_api3.Shotgun(self.config.server_url, self.config.script_name, - self.config.api_key ) - keys = ['project','user','code'] - data = {'project':self.project, - 'user':self.human_user, - 'code':'Alpha'} + self.config.api_key) + keys = ['project', 'user', 'code'] + data = {'project': self.project, + 'user': self.human_user, + 'code': 'Alpha'} version_1 = base._find_or_create_entity(sg, 'Version', data, keys) - data = {'project':self.project, - 'user':self.human_user, - 'code':'Beta'} + data = {'project': self.project, + 'user': self.human_user, + 'code': 'Beta'} version_2 = base._find_or_create_entity(sg, 'Version', data, keys) entity = 'Playlist' @@ -788,7 +788,7 @@ def test_set_multi_entity(self): field_name, pos_values, multi_entity_update_mode='remove') self.assertEqual(1, len(actual)) self.assertEqual(len(expected), len(actual)) - self.assertNotEqual(expected[0]['id'],actual[0]['id']) + self.assertNotEqual(expected[0]['id'], actual[0]['id']) self.assertEqual(version_2['id'], actual[0]['id']) # Multi-entity add mode @@ -824,7 +824,7 @@ def test_set_status_list(self): entity = 'Task' entity_id = self.task['id'] field_name = 'sg_status_list' - pos_values = ['rdy','fin'] + pos_values = ['rdy', 'fin'] expected, actual = self.assert_set_field(entity, entity_id, field_name, @@ -835,7 +835,7 @@ def test_set_status_list(self): entity = 'Task' entity_id = self.task['id'] field_name = 'sg_status_list' - pos_values = ['rdy','fin'] + pos_values = ['rdy', 'fin'] expected, actual = self.assert_set_field(entity, entity_id, field_name, @@ -846,7 +846,7 @@ def test_set_tag_list(self): entity = 'Task' entity_id = self.task['id'] field_name = 'tag_list' - pos_values = [['a','b'],['c']] + pos_values = [['a', 'b'], ['c']] expected, actual = self.assert_set_field(entity, entity_id, field_name, @@ -882,10 +882,10 @@ def assert_set_field(self, entity, entity_id, field_name, pos_values, multi_enti initial_value = query_result[field_name] new_value = (initial_value == pos_values[0] and pos_values[1]) or pos_values[0] if multi_entity_update_mode: - self.sg.update(entity, entity_id, {field_name:new_value}, - multi_entity_update_modes={field_name:multi_entity_update_mode}) + self.sg.update(entity, entity_id, {field_name: new_value}, + multi_entity_update_modes={field_name: multi_entity_update_mode}) else: - self.sg.update(entity, entity_id, {field_name:new_value}) + self.sg.update(entity, entity_id, {field_name: new_value}) new_values = self.sg.find_one(entity, [['id', 'is', entity_id]], [field_name]) @@ -924,8 +924,8 @@ def _assert_expected(self, sg, date_time, expected): entity_name = 'HumanUser' entity_id = self.human_user['id'] field_name = 'locked_until' - sg.update(entity_name, entity_id, {field_name:date_time}) - result = sg.find_one(entity_name, [['id','is',entity_id]],[field_name]) + sg.update(entity_name, entity_id, {field_name: date_time}) + result = sg.find_one(entity_name, [['id', 'is', entity_id]], [field_name]) self.assertEqual(result[field_name], expected) @@ -944,8 +944,8 @@ def setUp(self): def test_find(self): """Called find, find_one for known entities""" filters = [] - filters.append(['project','is', self.project]) - filters.append(['id','is', self.version['id']]) + filters.append(['project', 'is', self.project]) + filters.append(['id', 'is', self.version['id']]) fields = ['id'] @@ -1101,7 +1101,7 @@ def test_in_relation_comma_duration(self): # we need to get the duration value new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') - self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) + self.task = self.sg.find_one('Task', [['id', 'is', self.task['id']]], new_task_keys) filters = [['duration', 'in', self.task['duration']], ['project', 'is', self.project]] @@ -1115,8 +1115,8 @@ def test_in_relation_list_duration(self): # we need to get the duration value new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') - self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) - filters = [['duration', 'in', [self.task['duration'],]], + self.task = self.sg.find_one('Task', [['id', 'is', self.task['id']]], new_task_keys) + filters = [['duration', 'in', [self.task['duration'], ]], ['project', 'is', self.project]] result = self._id_in_result('Task', filters, self.task['id']) @@ -1129,9 +1129,9 @@ def test_not_in_relation_duration(self): # we need to get the duration value new_task_keys = list(self.task.keys())[:] new_task_keys.append('duration') - self.task = self.sg.find_one('Task',[['id', 'is', self.task['id']]], new_task_keys) + self.task = self.sg.find_one('Task', [['id', 'is', self.task['id']]], new_task_keys) - filters = [['duration', 'not_in', [self.task['duration'],]], + filters = [['duration', 'not_in', [self.task['duration'], ]], ['project', 'is', self.project]] result = self._id_in_result('Task', filters, self.task['id']) @@ -1349,7 +1349,7 @@ def test_in_relation_comma_uuid(self): """ Test that 'in' relation using commas (old format) works with uuid fields. """ - filters = [['uuid', 'in', self.local_storage['uuid'],]] + filters = [['uuid', 'in', self.local_storage['uuid'], ]] result = self._id_in_result('LocalStorage', filters, self.local_storage['id']) self.assertTrue(result) @@ -1358,7 +1358,7 @@ def test_in_relation_list_uuid(self): """ Test that 'in' relation using list (new format) works with uuid fields. """ - filters = [['uuid', 'in', [self.local_storage['uuid'],]]] + filters = [['uuid', 'in', [self.local_storage['uuid'], ]]] result = self._id_in_result('LocalStorage', filters, self.local_storage['id']) self.assertTrue(result) @@ -1367,7 +1367,7 @@ def test_not_in_relation_uuid(self): """ Test that 'not_in' relation using commas (old format) works with uuid fields. """ - filters = [['uuid', 'not_in', [self.local_storage['uuid'],]]] + filters = [['uuid', 'not_in', [self.local_storage['uuid'], ]]] result = self._id_in_result('LocalStorage', filters, self.local_storage['id']) self.assertFalse(result) @@ -1375,8 +1375,8 @@ def test_not_in_relation_uuid(self): def test_find(self): """Called find, find_one for known entities""" filters = [] - filters.append(['project','is', self.project]) - filters.append(['id','is', self.version['id']]) + filters.append(['project', 'is', self.project]) + filters.append(['id', 'is', self.version['id']]) fields = ['id'] @@ -1421,7 +1421,7 @@ def test_find_in(self): self.assertEqual(self.project['id'], project['id']) def test_unsupported_filters(self): - self.assertRaises(shotgun_api3.Fault, self.sg.find_one, 'Shot', [['image', 'is_not', [ {"type": "Thumbnail", "id": 9 }]]]) + self.assertRaises(shotgun_api3.Fault, self.sg.find_one, 'Shot', [['image', 'is_not', [{"type": "Thumbnail", "id": 9}]]]) self.assertRaises(shotgun_api3.Fault, self.sg.find_one, 'HumanUser', [['password_proxy', 'is_not', [None]]]) self.assertRaises(shotgun_api3.Fault, self.sg.find_one, 'EventLogEntry', [['meta', 'is_not', [None]]]) self.assertRaises(shotgun_api3.Fault, self.sg.find_one, 'Revision', [['meta', 'attachment', [None]]]) @@ -1433,53 +1433,53 @@ def test_zero_is_not_none(self): # Create a number field if it doesn't already exist num_field = 'sg_api_tests_number_field' if num_field not in list(self.sg.schema_field_read('Asset').keys()): - self.sg.schema_field_create('Asset', 'number', num_field.replace('sg_','').replace('_',' ')) + self.sg.schema_field_create('Asset', 'number', num_field.replace('sg_', '').replace('_', ' ')) # Set to None - self.sg.update( 'Asset', self.asset['id'], { num_field: None }) + self.sg.update('Asset', self.asset['id'], {num_field: None}) # Should be filtered out - result = self.sg.find( 'Asset', [['id','is',self.asset['id']],[num_field, 'is_not', None]] ,[num_field] ) + result = self.sg.find('Asset', [['id', 'is', self.asset['id']], [num_field, 'is_not', None]], [num_field]) self.assertEqual([], result) # Set it to zero - self.sg.update( 'Asset', self.asset['id'], { num_field: 0 }) + self.sg.update('Asset', self.asset['id'], {num_field: 0}) # Should not be filtered out - result = self.sg.find_one( 'Asset', [['id','is',self.asset['id']],[num_field, 'is_not', None]] ,[num_field] ) + result = self.sg.find_one('Asset', [['id', 'is', self.asset['id']], [num_field, 'is_not', None]], [num_field]) self.assertFalse(result == None) # Set it to some other number - self.sg.update( 'Asset', self.asset['id'], { num_field: 1 }) + self.sg.update('Asset', self.asset['id'], {num_field: 1}) # Should not be filtered out - result = self.sg.find_one( 'Asset', [['id','is',self.asset['id']],[num_field, 'is_not', None]] ,[num_field] ) + result = self.sg.find_one('Asset', [['id', 'is', self.asset['id']], [num_field, 'is_not', None]], [num_field]) self.assertFalse(result == None) def test_include_archived_projects(self): if self.sg.server_caps.version > (5, 3, 13): # Ticket #25082 - result = self.sg.find_one('Shot', [['id','is',self.shot['id']]]) + result = self.sg.find_one('Shot', [['id', 'is', self.shot['id']]]) self.assertEqual(self.shot['id'], result['id']) # archive project - self.sg.update('Project', self.project['id'], {'archived':True}) + self.sg.update('Project', self.project['id'], {'archived': True}) # setting defaults to True, so we should get result - result = self.sg.find_one('Shot', [['id','is',self.shot['id']]]) + result = self.sg.find_one('Shot', [['id', 'is', self.shot['id']]]) self.assertEqual(self.shot['id'], result['id']) - result = self.sg.find_one('Shot', [['id','is',self.shot['id']]], include_archived_projects=False) + result = self.sg.find_one('Shot', [['id', 'is', self.shot['id']]], include_archived_projects=False) self.assertEqual(None, result) # unarchive project - self.sg.update('Project', self.project['id'], {'archived':False}) + self.sg.update('Project', self.project['id'], {'archived': False}) class TestFollow(base.LiveTestBase): def setUp(self): super(TestFollow, self).setUp() - self.sg.update( 'HumanUser', self.human_user['id'], {'projects':[self.project]}) + self.sg.update('HumanUser', self.human_user['id'], {'projects': [self.project]}) # As the Follow entity isn't exposed directly, we clear out existing # follows for the user before running our tests. @@ -1509,8 +1509,8 @@ def test_followers(self): assert(result['followed']) result = self.sg.followers(self.shot) - self.assertEqual( 1, len(result) ) - self.assertEqual( self.human_user['id'], result[0]['id'] ) + self.assertEqual(1, len(result)) + self.assertEqual(self.human_user['id'], result[0]['id']) def test_following(self): '''Test following method''' @@ -1523,40 +1523,40 @@ def test_following(self): assert(result['followed']) result = self.sg.following(self.human_user) - self.assertEqual( 1, len(result) ) - self.assertEqual( self.shot['id'], result[0]['id'] ) + self.assertEqual(1, len(result)) + self.assertEqual(self.shot['id'], result[0]['id']) result = self.sg.follow(self.human_user, self.task) assert(result['followed']) result = self.sg.following(self.human_user) - self.assertEqual( 2, len(result) ) + self.assertEqual(2, len(result)) result = self.sg.following(self.human_user, entity_type="Task") - self.assertEqual( 1, len(result) ) + self.assertEqual(1, len(result)) result = self.sg.following(self.human_user, entity_type="Shot") - self.assertEqual( 1, len(result) ) + self.assertEqual(1, len(result)) shot_project_id = self.sg.find_one("Shot", - [["id","is",self.shot["id"]]], + [["id", "is", self.shot["id"]]], ["project.Project.id"])["project.Project.id"] task_project_id = self.sg.find_one("Task", - [["id","is",self.task["id"]]], + [["id", "is", self.task["id"]]], ["project.Project.id"])["project.Project.id"] project_count = 2 if shot_project_id == task_project_id else 1 - result = self.sg.following(self.human_user, - project={"type":"Project", "id":shot_project_id}) - self.assertEqual( project_count, len(result) ) - result = self.sg.following(self.human_user, - project={"type":"Project", "id":task_project_id}) - self.assertEqual( project_count, len(result) ) - result = self.sg.following(self.human_user, - project={"type":"Project", "id":shot_project_id}, + result = self.sg.following(self.human_user, + project={"type": "Project", "id": shot_project_id}) + self.assertEqual(project_count, len(result)) + result = self.sg.following(self.human_user, + project={"type": "Project", "id": task_project_id}) + self.assertEqual(project_count, len(result)) + result = self.sg.following(self.human_user, + project={"type": "Project", "id": shot_project_id}, entity_type="Shot") - self.assertEqual( 1, len(result) ) - result = self.sg.following(self.human_user, - project={"type":"Project", "id":task_project_id}, + self.assertEqual(1, len(result)) + result = self.sg.following(self.human_user, + project={"type": "Project", "id": task_project_id}, entity_type="Task") - self.assertEqual( 1, len(result) ) + self.assertEqual(1, len(result)) class TestErrors(base.TestBase): def test_bad_auth(self): @@ -1585,15 +1585,15 @@ def test_bad_auth(self): # Test failed authentications sg = shotgun_api3.Shotgun(server_url, script_name, api_key) - self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot',[]) + self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot', []) script_name = self.config.script_name api_key = 'notrealapikey' sg = shotgun_api3.Shotgun(server_url, script_name, api_key) - self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot',[]) + self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot', []) sg = shotgun_api3.Shotgun(server_url, login=login, password='not a real password') - self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot',[]) + self.assertRaises(shotgun_api3.AuthenticationFault, sg.find_one, 'Shot', []) # This may trigger an account lockdown. Make sure it is not locked anymore. user = self.sg.find_one("HumanUser", [["login", "is", login]]) @@ -1698,10 +1698,10 @@ def test_upload_empty_file(self): Test uploading an empty file raises an error. """ this_dir, _ = os.path.split(__file__) - path = os.path.abspath(os.path.expanduser(os.path.join(this_dir,"empty.txt"))) + path = os.path.abspath(os.path.expanduser(os.path.join(this_dir, "empty.txt"))) self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload, 'Version', 123, path) self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_thumbnail, 'Version', 123, path) - self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_filmstrip_thumbnail, 'Version', + self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_filmstrip_thumbnail, 'Version', 123, path) def test_upload_missing_file(self): @@ -1711,7 +1711,7 @@ def test_upload_missing_file(self): path = "/path/to/nowhere/foo.txt" self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload, 'Version', 123, path) self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_thumbnail, 'Version', 123, path) - self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_filmstrip_thumbnail, 'Version', + self.assertRaises(shotgun_api3.ShotgunError, self.sg.upload_filmstrip_thumbnail, 'Version', 123, path) # def test_malformed_response(self): @@ -1735,22 +1735,22 @@ def test_user_is_creator(self): self.config.script_name, self.config.api_key, http_proxy=self.config.http_proxy, - sudo_as_login=self.config.human_login ) + sudo_as_login=self.config.human_login) data = { 'project': self.project, - 'code':'JohnnyApple_Design01_FaceFinal', + 'code': 'JohnnyApple_Design01_FaceFinal', 'description': 'fixed rig per director final notes', - 'sg_status_list':'na', + 'sg_status_list': 'na', 'entity': self.asset, 'user': self.human_user } - version = x.create("Version", data, return_fields = ["id","created_by"]) + version = x.create("Version", data, return_fields = ["id", "created_by"]) self.assertTrue(isinstance(version, dict)) self.assertTrue("id" in version) self.assertTrue("created_by" in version) - self.assertEqual( self.config.human_name, version['created_by']['name'] ) + self.assertEqual(self.config.human_name, version['created_by']['name']) class TestHumanUserSudoAuth(base.TestBase): def setUp(self): @@ -1769,11 +1769,11 @@ def test_human_user_sudo_auth_fails(self): login=self.config.human_login, password=self.config.human_password, http_proxy=self.config.http_proxy, - sudo_as_login="blah" ) + sudo_as_login="blah") self.assertRaises(shotgun_api3.Fault, x.find_one, 'Shot', []) expected = "The user does not have permission to 'sudo':" - try : - x.find_one('Shot',[]) + try: + x.find_one('Shot', []) except shotgun_api3.Fault as e: # py24 exceptions don't have message attr if hasattr(e, 'message'): @@ -1811,7 +1811,7 @@ def test_humanuser_upload_thumbnail_for_version(self): """simple upload thumbnail for version test as human user.""" this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size # upload thumbnail @@ -1835,7 +1835,7 @@ def test_humanuser_upload_thumbnail_for_version(self): # clear thumbnail response_clear_thumbnail = self.sg.update("Version", - self.version['id'], {'image':None}) + self.version['id'], {'image': None}) expected_clear_thumbnail = {'id': self.version['id'], 'image': None, 'type': 'Version'} self.assertEqual(expected_clear_thumbnail, response_clear_thumbnail) @@ -1874,7 +1874,7 @@ def test_humanuser_upload_thumbnail_for_version(self): this_dir, _ = os.path.split(__file__) path = os.path.abspath(os.path.expanduser( - os.path.join(this_dir,"sg_logo.jpg"))) + os.path.join(this_dir, "sg_logo.jpg"))) size = os.stat(path).st_size # upload thumbnail @@ -1898,7 +1898,7 @@ def test_humanuser_upload_thumbnail_for_version(self): # clear thumbnail response_clear_thumbnail = self.sg.update("Version", - self.version['id'], {'image':None}) + self.version['id'], {'image': None}) expected_clear_thumbnail = {'id': self.version['id'], 'image': None, 'type': 'Version'} self.assertEqual(expected_clear_thumbnail, response_clear_thumbnail) @@ -1914,12 +1914,12 @@ def test_logged_in_user(self): password=self.config.human_password, http_proxy=self.config.http_proxy) - initial = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) + initial = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) sg.update_project_last_accessed(self.project) - current = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) - self.assertNotEqual( initial, current ) + current = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) + self.assertNotEqual(initial, current) # it's possible initial is None if initial: assert(initial['last_accessed_by_current_user'] < current['last_accessed_by_current_user']) @@ -1929,19 +1929,19 @@ def test_pass_in_user(self): if self.sg.server_caps.version and self.sg.server_caps.version < (5, 3, 20): return - sg = shotgun_api3.Shotgun( self.config.server_url, + sg = shotgun_api3.Shotgun(self.config.server_url, login=self.config.human_login, password=self.config.human_password, - http_proxy=self.config.http_proxy ) + http_proxy=self.config.http_proxy) - initial = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) + initial = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) time.sleep(1) # this instance of the api is not logged in as a user self.sg.update_project_last_accessed(self.project, user=self.human_user) - current = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) - self.assertNotEqual( initial, current ) + current = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) + self.assertNotEqual(initial, current) # it's possible initial is None if initial: assert(initial['last_accessed_by_current_user'] < current['last_accessed_by_current_user']) @@ -1950,19 +1950,19 @@ def test_sudo_as_user(self): if self.sg.server_caps.version and self.sg.server_caps.version < (5, 3, 20): return - sg = shotgun_api3.Shotgun( self.config.server_url, + sg = shotgun_api3.Shotgun(self.config.server_url, self.config.script_name, self.config.api_key, http_proxy=self.config.http_proxy, - sudo_as_login=self.config.human_login ) + sudo_as_login=self.config.human_login) - initial = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) + initial = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) time.sleep(1) sg.update_project_last_accessed(self.project) - current = sg.find_one('Project', [['id','is',self.project['id']]], ['last_accessed_by_current_user']) - self.assertNotEqual( initial, current ) + current = sg.find_one('Project', [['id', 'is', self.project['id']]], ['last_accessed_by_current_user']) + self.assertNotEqual(initial, current) # it's possible initial is None if initial: assert(initial['last_accessed_by_current_user'] < current['last_accessed_by_current_user']) @@ -1989,7 +1989,7 @@ def setUp(self): # disabled, these tests will fail because the activity stream is # connected to events. In this case, print a warning to the user d = self.sg.find_one("Shot", - [["id", "is", self._shot["id"] ]], + [["id", "is", self._shot["id"]]], ["created_by.ApiUser.generate_event_log_entries"]) if d["created_by.ApiUser.generate_event_log_entries"] is False: @@ -2150,7 +2150,7 @@ def test_simple(self): return # create note - note = self.sg.create( "Note", {"content": "Test!", "project": self.project}) + note = self.sg.create("Note", {"content": "Test!", "project": self.project}) # for this test, we check that the replies returned also # contain the thumbnail associated with the user doing the @@ -2181,7 +2181,7 @@ def test_simple(self): self._check_note(result[0], note["id"], additional_fields=[]) # now add a reply - reply = self.sg.create( "Reply", {"content": "Reply Content", "entity": note}) + reply = self.sg.create("Reply", {"content": "Reply Content", "entity": note}) # get thread result = self.sg.note_thread_read(note["id"]) @@ -2222,13 +2222,13 @@ def test_complex(self): "Note": ["created_by.HumanUser.image", "addressings_to", "playlist", - "user" ], + "user"], "Reply": ["content"], "Attachment": ["this_file"] } # create note - note = self.sg.create( "Note", {"content": "Test!", + note = self.sg.create("Note", {"content": "Test!", "project": self.project, "addressings_to": [self.human_user]}) @@ -2239,7 +2239,7 @@ def test_complex(self): self._check_note(result[0], note["id"], additional_fields["Note"]) # now add a reply - reply = self.sg.create( "Reply", {"content": "Reply Content", "entity": note}) + reply = self.sg.create("Reply", {"content": "Reply Content", "entity": note}) # get thread result = self.sg.note_thread_read(note["id"], additional_fields) @@ -2271,14 +2271,14 @@ def setUp(self): batch_data = [] for i in range(5): - data = { "code":"%s Text Search %s" % (self._prefix, i), - "project": self.project } - batch_data.append( {"request_type": "create", + data = {"code": "%s Text Search %s" % (self._prefix, i), + "project": self.project} + batch_data.append({"request_type": "create", "entity_type": "Shot", - "data": data} ) - batch_data.append( {"request_type": "create", + "data": data}) + batch_data.append({"request_type": "create", "entity_type": "Asset", - "data": data} ) + "data": data}) data = self.sg.batch(batch_data) self._shot_ids = [x["id"] for x in data if x["type"] == "Shot"] @@ -2309,7 +2309,7 @@ def test_simple(self): if not self.sg.server_caps.version or self.sg.server_caps.version < (6, 2, 0): return - result = self.sg.text_search("%s Text Search" % self._prefix, {"Shot" : [] } ) + result = self.sg.text_search("%s Text Search" % self._prefix, {"Shot": []}) self.assertEqual(set(["matches", "terms"]), set(result.keys())) self.assertEqual(result["terms"], [self._prefix, "text", "search"]) @@ -2329,7 +2329,7 @@ def test_limit(self): if not self.sg.server_caps.version or self.sg.server_caps.version < (6, 2, 0): return - result = self.sg.text_search("%s Text Search" % self._prefix, {"Shot" : [] }, limit=3 ) + result = self.sg.text_search("%s Text Search" % self._prefix, {"Shot": []}, limit=3) matches = result["matches"] self.assertEqual(len(matches), 3) @@ -2341,7 +2341,7 @@ def test_entity_filter(self): return result = self.sg.text_search("%s Text Search" % self._prefix, - {"Shot": [], "Asset": [] } ) + {"Shot": [], "Asset": []}) matches = result["matches"] @@ -2450,7 +2450,7 @@ def test_invalid_filter(self): fields = ["id"] - additional_filters = [{"preset_name" : "BAD_FILTER"}] + additional_filters = [{"preset_name": "BAD_FILTER"}] self.assertRaises(shotgun_api3.Fault, self.sg.find, @@ -2525,7 +2525,7 @@ def _has_unicode(data): def _get_path(url): """Returns path component of a url without the sheme, host, query, anchor, or any other - additional elements. + additional elements. For example, the url "https://foo.shotgunstudio.com/page/2128#Shot_1190_sr10101_034" returns "/page/2128" """ diff --git a/tests/test_api_long.py b/tests/test_api_long.py index 9e9f69af2..a4affe65e 100644 --- a/tests/test_api_long.py +++ b/tests/test_api_long.py @@ -30,13 +30,13 @@ def test_automated_find(self): continue # trying to use some different code paths to the other find test - # pivot_column fields aren't valid for sorting so ensure we're + # pivot_column fields aren't valid for sorting so ensure we're # not using one. order_field = None for field_name, field in fields.items(): if field['data_type']["value"] != 'pivot_column': order_field = field_name - break + break # TODO for our test project, we haven't populated these entities.... order = [{'field_name': order_field, 'direction': direction}] if "project" in fields: @@ -83,7 +83,7 @@ def test_schema(self): # An explanation is in order here. the field code that is created in shotgun is based on the human display name # that is provided , so for example "Money Count" would generate the field code 'sg_monkey_count' . The field - # that is created in this test is retired at the end of the test but when this test is run again against + # that is created in this test is retired at the end of the test but when this test is run again against # the same database ( which happens on our Continuous Integration server ) trying to create a new field # called "Monkey Count" will now fail due to the new Delete Field Forever features we have added to shotgun # since there will a retired field called sg_monkey_count. The old behavior was to go ahead and create a new @@ -93,8 +93,8 @@ def test_schema(self): # make a the name of the field somewhat unique human_field_name = "Monkey " + str(random.getrandbits(24)) - properties = { "description" : "How many monkeys were needed" } - new_field_name = self.sg.schema_field_create("Version", "number", human_field_name, + properties = {"description": "How many monkeys were needed"} + new_field_name = self.sg.schema_field_create("Version", "number", human_field_name, properties=properties) properties = {"description": "How many monkeys turned up"} diff --git a/tests/test_client.py b/tests/test_client.py index 5dbfe8b6f..6276d8bed 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -55,46 +55,46 @@ def test_detect_server_caps(self): '''test_detect_server_caps tests that ServerCapabilities object is made with appropriate settings for given server version.''' #has paging is tested else where. - server_info = { "version" : [9,9,9] } + server_info = {"version": [9, 9, 9]} self._mock_http(server_info) # ensrue the server caps is re-read self.sg._server_caps = None self.assertTrue(self.sg.server_caps is not None) self.assertFalse(self.sg.server_caps.is_dev) - self.assertEqual((9,9,9), self.sg.server_caps.version) + self.assertEqual((9, 9, 9), self.sg.server_caps.version) self.assertTrue(self.server_url.endswith(self.sg.server_caps.host)) - self.assertTrue(str(self.sg.server_caps).startswith( "ServerCapabilities")) + self.assertTrue(str(self.sg.server_caps).startswith("ServerCapabilities")) self.assertEqual(server_info, self.sg.server_info) - self._mock_http({ "version" : [9,9,9, "Dev"] }) + self._mock_http({"version": [9, 9, 9, "Dev"]}) self.sg._server_caps = None self.assertTrue(self.sg.server_caps.is_dev) def test_server_version_json(self): '''test_server_version_json tests expected versions for json support.''' - sc = ServerCapabilities("foo", {"version" : (2,4,0)}) + sc = ServerCapabilities("foo", {"version": (2, 4, 0)}) - sc.version = (2,3,99) + sc.version = (2, 3, 99) self.assertRaises(api.ShotgunError, sc._ensure_json_supported) self.assertRaises(api.ShotgunError, ServerCapabilities, "foo", - {"version" : (2,2,0)}) + {"version": (2, 2, 0)}) - sc.version = (0,0,0) + sc.version = (0, 0, 0) self.assertRaises(api.ShotgunError, sc._ensure_json_supported) - sc.version = (2,4,0) + sc.version = (2, 4, 0) sc._ensure_json_supported() - sc.version = (2,5,0) + sc.version = (2, 5, 0) sc._ensure_json_supported() def test_extra_auth_params(self): """test_extra_auth_params tests provided auth_params are included in request""" # ok for the mock server to just return an error, we want to look at # what's in the request - self._mock_http({ "message":"Go BANG", - "exception":True }) + self._mock_http({"message": "Go BANG", + "exception": True}) def auth_args(): args = self.sg._http_request.call_args[0] @@ -106,7 +106,7 @@ def auth_args(): self.assertRaises(api.Fault, self.sg.delete, "FakeType", 1) self.assertTrue("product" not in auth_args()) - self.sg.config.extra_auth_params = {"product":"rv"} + self.sg.config.extra_auth_params = {"product": "rv"} self.assertRaises(api.Fault, self.sg.delete, "FakeType", 1) self.assertEqual("rv", auth_args()["product"]) @@ -114,8 +114,8 @@ def test_session_uuid(self): """test_session_uuid tests session UUID is included in request""" #ok for the mock server to just return an error, we want to look at #whats in the request - self._mock_http({ "message":"Go BANG", - "exception":True }) + self._mock_http({"message": "Go BANG", + "exception": True}) def auth_args(): args = self.sg._http_request.call_args[0] @@ -138,7 +138,7 @@ def test_url(self): password = self.human_password self.assertRaises(ValueError, api.Shotgun, None, None, None, connect=False) - self.assertRaises(ValueError, api.Shotgun, "file://foo.com",None,None, connect=False) + self.assertRaises(ValueError, api.Shotgun, "file://foo.com", None, None, connect=False) self.assertEqual("/api3/json", self.sg.config.api_path) @@ -160,7 +160,7 @@ def test_authorization(self): self.sg = api.Shotgun(auth_url, "foo", "bar", connect=False) self._setup_mock() - self._mock_http({ 'version': [2, 4, 0, 'Dev'] }) + self._mock_http({'version': [2, 4, 0, 'Dev']}) self.sg.info() @@ -180,7 +180,7 @@ def test_user_agent(self): (_, _, _, headers) = args ssl_validate_lut = {True: "no-validate", False: "validate"} expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s)" % ( - api.__version__, + api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, @@ -194,7 +194,7 @@ def test_user_agent(self): args, _ = self.sg._http_request.call_args (_, _, _, headers) = args expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s); test-agent" % ( - api.__version__, + api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, @@ -208,7 +208,7 @@ def test_user_agent(self): args, _ = self.sg._http_request.call_args (_, _, _, headers) = args expected = "shotgun-json (%s); Python %s (%s); ssl %s (%s)" % ( - api.__version__, + api.__version__, client_caps.py_version, client_caps.platform.capitalize(), client_caps.ssl_version, @@ -237,7 +237,7 @@ def test_network_retry(self): def test_http_error(self): """HTTP error raised and not retried.""" - self._mock_http( "big old error string", + self._mock_http("big old error string", status=(500, "Internal Server Error")) self.assertRaises(api.ProtocolError, self.sg.info) @@ -248,8 +248,8 @@ def test_http_error(self): def test_rpc_error(self): """RPC error transformed into Python error""" - self._mock_http({ "message":"Go BANG", - "exception":True }) + self._mock_http({"message": "Go BANG", + "exception": True}) self.assertRaises(api.Fault, self.sg.info) @@ -261,43 +261,43 @@ def test_rpc_error(self): def test_call_rpc(self): """Named rpc method is called and results handled""" - d = { "no-results" : "data without a results key" } + d = {"no-results": "data without a results key"} self._mock_http(d) rv = self.sg._call_rpc("no-results", None) self._assert_http_method("no-results", None) expected = "rpc response without results key is returned as-is" - self.assertEqual(d, rv, expected ) + self.assertEqual(d, rv, expected) - d = { "results" : {"singleton" : "result"} } + d = {"results": {"singleton": "result"}} self._mock_http(d) rv = self.sg._call_rpc("singleton", None) self._assert_http_method("singleton", None) expected = "rpc response with singleton result" - self.assertEqual(d["results"], rv, expected ) + self.assertEqual(d["results"], rv, expected) - d = { "results" : ["foo", "bar"] } - a = {"some" : "args"} + d = {"results": ["foo", "bar"]} + a = {"some": "args"} self._mock_http(d) rv = self.sg._call_rpc("list", a) self._assert_http_method("list", a) expected = "rpc response with list result" - self.assertEqual(d["results"], rv, expected ) + self.assertEqual(d["results"], rv, expected) - d = { "results" : ["foo", "bar"] } - a = {"some" : "args"} + d = {"results": ["foo", "bar"]} + a = {"some": "args"} self._mock_http(d) rv = self.sg._call_rpc("list-first", a, first=True) self._assert_http_method("list-first", a) expected = "rpc response with list result, first item" - self.assertEqual(d["results"][0], rv, expected ) + self.assertEqual(d["results"][0], rv, expected) # Test unicode mixed with utf-8 as reported in Ticket #17959 - d = { "results" : ["foo", "bar"] } - a = { "utf_str": "\xe2\x88\x9a", "unicode_str": "\xe2\x88\x9a".decode("utf-8") } + d = {"results": ["foo", "bar"]} + a = {"utf_str": "\xe2\x88\x9a", "unicode_str": "\xe2\x88\x9a".decode("utf-8")} self._mock_http(d) rv = self.sg._call_rpc("list", a) expected = "rpc response with list result" - self.assertEqual(d["results"], rv, expected ) + self.assertEqual(d["results"], rv, expected) @@ -310,15 +310,15 @@ def test_transform_data(self): utc_now = datetime.datetime.utcfromtimestamp(timestamp).replace( microsecond=0) local = { - "date" : now.strftime('%Y-%m-%d'), - "datetime" : now, - "time" : now.time() + "date": now.strftime('%Y-%m-%d'), + "datetime": now, + "time": now.time() } #date will still be the local date, because they are not transformed utc = { - "date" : now.strftime('%Y-%m-%d'), + "date": now.strftime('%Y-%m-%d'), "datetime": utc_now, - "time" : utc_now.time() + "time": utc_now.time() } def _datetime(s, f): @@ -360,13 +360,13 @@ def test_encode_payload(self): """Request body is encoded as JSON""" d = { - "this is " : "my data \u00E0" + "this is ": "my data \u00E0" } j = self.sg._encode_payload(d) self.assertTrue(isinstance(j, bytes)) d = { - "this is " : "my data" + "this is ": "my data" } j = self.sg._encode_payload(d) self.assertTrue(isinstance(j, bytes)) @@ -381,10 +381,10 @@ def _assert_decode_resonse(self, ensure_ascii, data): """HTTP Response is decoded as JSON or text""" headers = { - "content-type" : "application/json;charset=utf-8" + "content-type": "application/json;charset=utf-8" } d = { - "this is " : data + "this is ": data } sg = api.Shotgun(self.config.server_url, self.config.script_name, @@ -415,11 +415,11 @@ def test_parse_records(self): elif system == 'linux': local_path_field = "local_path_linux" orig = { - "type" : "FakeAsset", - "id" : 1234, - "image" : "blah", - "foo" : { - "link_type" : "local", + "type": "FakeAsset", + "id": 1234, + "image": "blah", + "foo": { + "link_type": "local", local_path_field: "/foo/bar.jpg", } } @@ -445,7 +445,7 @@ def test_thumb_url(self): #the thumbnail service returns a two line #test response success code on line 1, data on line 2 resp = "1\n/files/0000/0000/0012/232/shot_thumb.jpg" - self._mock_http(resp, headers={"content-type" : "text/plain"}) + self._mock_http(resp, headers={"content-type": "text/plain"}) self.sg.config.scheme = "http" self.sg.config.server = "foo.com" @@ -462,12 +462,12 @@ def test_thumb_url(self): path, "thumbnail url called with correct args") resp = "0\nSome Error" - self._mock_http(resp, headers={"content-type" : "text/plain"}) + self._mock_http(resp, headers={"content-type": "text/plain"}) self.assertRaises(api.ShotgunError, self.sg._build_thumb_url, "FakeAsset", 456) resp = "99\nSome Error" - self._mock_http(resp, headers={"content-type" : "text/plain"}) + self._mock_http(resp, headers={"content-type": "text/plain"}) self.assertRaises(RuntimeError, self.sg._build_thumb_url, "FakeAsset", 456) diff --git a/tests/tests_proxy.py b/tests/tests_proxy.py index ea575371c..3c2a23e08 100644 --- a/tests/tests_proxy.py +++ b/tests/tests_proxy.py @@ -19,7 +19,7 @@ def test_proxy_info(self): self.sg.connect() if self.config.http_proxy: sys.stderr.write("[WITH PROXY] ") - self.assertTrue(isinstance(self.sg._connection.proxy_info, + self.assertTrue(isinstance(self.sg._connection.proxy_info, api.lib.httplib2.ProxyInfo)) else: sys.stderr.write("[NO PROXY] ") diff --git a/tests/tests_unit.py b/tests/tests_unit.py index c9e2ff277..8b2089c44 100644 --- a/tests/tests_unit.py +++ b/tests/tests_unit.py @@ -15,18 +15,18 @@ def setUp(self): def test_http_proxy_server(self): proxy_server = "someserver.com" http_proxy = proxy_server - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) self.assertEqual(sg.config.proxy_port, 8080) proxy_server = "123.456.789.012" http_proxy = proxy_server - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -36,9 +36,9 @@ def test_http_proxy_server_and_port(self): proxy_server = "someserver.com" proxy_port = 1234 http_proxy = "%s:%d" % (proxy_server, proxy_port) - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -46,9 +46,9 @@ def test_http_proxy_server_and_port(self): proxy_server = "123.456.789.012" proxy_port = 1234 http_proxy = "%s:%d" % (proxy_server, proxy_port) - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -59,11 +59,11 @@ def test_http_proxy_server_and_port_with_authentication(self): proxy_port = 1234 proxy_user = "user" proxy_pass = "password" - http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, + http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, proxy_port) - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -74,11 +74,11 @@ def test_http_proxy_server_and_port_with_authentication(self): proxy_port = 1234 proxy_user = "user" proxy_pass = "password" - http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, + http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, proxy_port) - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -91,11 +91,11 @@ def test_http_proxy_with_at_in_password(self): proxy_port = 1234 proxy_user = "user" proxy_pass = "p@ssword" - http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, + http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, proxy_port) - sg = api.Shotgun(self.server_path, - self.script_name, - self.api_key, + sg = api.Shotgun(self.server_path, + self.script_name, + self.api_key, http_proxy=http_proxy, connect=False) self.assertEqual(sg.config.proxy_server, proxy_server) @@ -108,12 +108,12 @@ def test_malformatted_proxy_info(self): proxy_port = 1234 proxy_user = "user" proxy_pass = "password" - http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, + http_proxy = "%s:%s@%s:%d" % (proxy_user, proxy_pass, proxy_server, proxy_port) conn_info = { 'base_url': self.server_path, 'script_name': self.script_name, - 'api_key': self.api_key, + 'api_key': self.api_key, 'connect': False, } conn_info['http_proxy'] = 'http://someserver.com' @@ -130,8 +130,8 @@ class TestShotgunSummarize(unittest.TestCase): Does not require database connection or test data.''' def setUp(self): self.sg = api.Shotgun('http://server_path', - 'script_name', - 'api_key', + 'script_name', + 'api_key', connect=False) @@ -141,7 +141,7 @@ def test_filter_operator_none(self): self._assert_filter_operator(expected_logical_operator, filter_operator) def _assert_filter_operator(self, expected_logical_operator, filter_operator): - result = self.get_call_rpc_params(None, {'filter_operator':filter_operator}) + result = self.get_call_rpc_params(None, {'filter_operator': filter_operator}) actual_logical_operator = result['filters']['logical_operator'] self.assertEqual(expected_logical_operator, actual_logical_operator) @@ -159,8 +159,8 @@ def test_filters(self): path = 'path' relation = 'relation' value = 'value' - expected_condition = {'path':path, 'relation':relation, 'values':[value]} - args = ['',[[path, relation, value]],None] + expected_condition = {'path': path, 'relation': relation, 'values': [value]} + args = ['', [[path, relation, value]], None] result = self.get_call_rpc_params(args, {}) actual_condition = result['filters']['conditions'][0] self.assertEqual(expected_condition, actual_condition) @@ -177,7 +177,7 @@ def test_grouping(self): result = self.get_call_rpc_params(None, {}) self.assertFalse('grouping' in result) grouping = ['something'] - kws = {'grouping':grouping} + kws = {'grouping': grouping} result = self.get_call_rpc_params(None, kws) self.assertEqual(grouping, result['grouping']) @@ -188,8 +188,8 @@ def test_grouping_type(self): class TestShotgunBatch(unittest.TestCase): def setUp(self): self.sg = api.Shotgun('http://server_path', - 'script_name', - 'api_key', + 'script_name', + 'api_key', connect=False) def test_missing_required_key(self): @@ -223,15 +223,15 @@ def test_no_server_version(self): def test_bad_version(self): '''test_bad_meta tests passing bad meta data type''' - self.assertRaises(api.ShotgunError, api.shotgun.ServerCapabilities, 'host', {'version':(0,0,0)}) + self.assertRaises(api.ShotgunError, api.shotgun.ServerCapabilities, 'host', {'version': (0, 0, 0)}) def test_dev_version(self): - serverCapabilities = api.shotgun.ServerCapabilities('host', {'version':(3,4,0,'Dev')}) - self.assertEqual(serverCapabilities.version, (3,4,0)) + serverCapabilities = api.shotgun.ServerCapabilities('host', {'version': (3, 4, 0, 'Dev')}) + self.assertEqual(serverCapabilities.version, (3, 4, 0)) self.assertTrue(serverCapabilities.is_dev) - serverCapabilities = api.shotgun.ServerCapabilities('host', {'version':(2,4,0)}) - self.assertEqual(serverCapabilities.version, (2,4,0)) + serverCapabilities = api.shotgun.ServerCapabilities('host', {'version': (2, 4, 0)}) + self.assertEqual(serverCapabilities.version, (2, 4, 0)) self.assertFalse(serverCapabilities.is_dev) class TestClientCapabilities(unittest.TestCase): @@ -240,7 +240,7 @@ def test_darwin(self): self.assert_platform('Darwin', 'mac') def test_windows(self): - self.assert_platform('win32','windows') + self.assert_platform('win32', 'windows') def test_linux(self): self.assert_platform('Linux', 'linux') @@ -296,8 +296,8 @@ def test_simple(self): expected = { "logical_operator": "or", "conditions": [ - { "path": "code", "relation": "is", "values": ["test"] }, - { "path": "sg_status_list", "relation": "is", "values": ["ip"] } + {"path": "code", "relation": "is", "values": ["test"]}, + {"path": "sg_status_list", "relation": "is", "values": ["ip"]} ] } @@ -309,7 +309,7 @@ def test_arrays(self): expected = { "logical_operator": "and", "conditions": [ - { "path": "code", "relation": "in", "values": ["test1", "test2", "test3"] } + {"path": "code", "relation": "in", "values": ["test1", "test2", "test3"]} ] } @@ -339,7 +339,7 @@ def test_nested(self): "filter_operator": "all", "filters": [ ["sg_status_list", "is", "hld"], - ["assets", "is", { "type": "Asset", "id": 9 }] + ["assets", "is", {"type": "Asset", "id": 9}] ] } ] @@ -349,17 +349,17 @@ def test_nested(self): expected = { "logical_operator": "and", "conditions": [ - { "path": "code", "relation": "in", "values": ["test"] }, + {"path": "code", "relation": "in", "values": ["test"]}, { "logical_operator": "or", "conditions": [ - { "path": "sg_status_list", "relation": "is", "values": ["ip"] }, - { "path": "sg_status_list", "relation": "is", "values": ["fin"] }, + {"path": "sg_status_list", "relation": "is", "values": ["ip"]}, + {"path": "sg_status_list", "relation": "is", "values": ["fin"]}, { "logical_operator": "and", "conditions": [ - { "path": "sg_status_list", "relation": "is", "values": ["hld"] }, - { "path": "assets", "relation": "is", "values": [ { "type": "Asset", "id": 9 } ] }, + {"path": "sg_status_list", "relation": "is", "values": ["hld"]}, + {"path": "assets", "relation": "is", "values": [{"type": "Asset", "id": 9}]}, ] } ] @@ -389,7 +389,7 @@ def test_invalid(self): filters = [{ "filter_operator": "all", - "filters": { "bogus": "bogus" } + "filters": {"bogus": "bogus"} }] self.assertRaises(api.ShotgunError, api.shotgun._translate_filters, filters, "all")