From e36a4369146e77ee5709a135594ec5e79fcff619 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Mon, 9 May 2011 23:20:11 -0400 Subject: [PATCH 1/2] Modified PUT implementation so that it conforms to the OAuth spec. Fixes #70 --- build/lib/oauth2/__init__.py | 860 +++++++++++ build/lib/oauth2/_version.py | 18 + build/lib/oauth2/clients/__init__.py | 0 build/lib/oauth2/clients/imap.py | 40 + build/lib/oauth2/clients/smtp.py | 41 + build/lib/tests/__init__.py | 0 build/lib/tests/test_oauth.py | 1309 +++++++++++++++++ .../EGG-INFO/PKG-INFO | 35 + .../EGG-INFO/SOURCES.txt | 43 + .../EGG-INFO/dependency_links.txt | 1 + .../EGG-INFO/entry_points.txt | 3 + .../EGG-INFO/native_libs.txt | 1 + .../EGG-INFO/not-zip-safe | 1 + .../EGG-INFO/top_level.txt | 1 + .../coverage/__init__.py | 88 ++ .../coverage/__main__.py | 3 + .../coverage/annotate.py | 101 ++ .../coverage/backward.py | 73 + .../coverage/bytecode.py | 81 + .../coverage/cmdline.py | 661 +++++++++ .../coverage/codeunit.py | 117 ++ .../coverage/collector.py | 292 ++++ .../coverage/config.py | 118 ++ .../coverage/control.py | 624 ++++++++ .../coverage/data.py | 261 ++++ .../coverage/execfile.py | 72 + .../coverage/files.py | 132 ++ .../coverage/html.py | 183 +++ .../coverage/htmlfiles/coverage_html.js | 87 ++ .../coverage/htmlfiles/index.html | 81 + .../coverage/htmlfiles/jquery-1.3.2.min.js | 19 + .../htmlfiles/jquery.tablesorter.min.js | 2 + .../coverage/htmlfiles/pyfile.html | 61 + .../coverage/htmlfiles/style.css | 230 +++ .../coverage/misc.py | 85 ++ .../coverage/parser.py | 783 ++++++++++ .../coverage/phystokens.py | 108 ++ .../coverage/report.py | 83 ++ .../coverage/results.py | 233 +++ .../coverage/summary.py | 81 + .../coverage/templite.py | 166 +++ .../coverage/tracer.py | 7 + .../coverage/tracer.so | Bin 0 -> 15256 bytes .../coverage/xmlreport.py | 147 ++ dist/oauth2-1.5.170-py2.7.egg | Bin 0 -> 52228 bytes files.txt | 1 + mock-0.7.0-py2.7.egg | Bin 0 -> 24549 bytes oauth2/__init__.py | 2 +- 48 files changed, 7334 insertions(+), 1 deletion(-) create mode 100644 build/lib/oauth2/__init__.py create mode 100644 build/lib/oauth2/_version.py create mode 100644 build/lib/oauth2/clients/__init__.py create mode 100644 build/lib/oauth2/clients/imap.py create mode 100644 build/lib/oauth2/clients/smtp.py create mode 100644 build/lib/tests/__init__.py create mode 100644 build/lib/tests/test_oauth.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/coverage_html.js create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/pyfile.html create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py create mode 100755 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so create mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/xmlreport.py create mode 100644 dist/oauth2-1.5.170-py2.7.egg create mode 100644 files.txt create mode 100644 mock-0.7.0-py2.7.egg diff --git a/build/lib/oauth2/__init__.py b/build/lib/oauth2/__init__.py new file mode 100644 index 00000000..3b9b3dcf --- /dev/null +++ b/build/lib/oauth2/__init__.py @@ -0,0 +1,860 @@ +""" +The MIT License + +Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import base64 +import urllib +import time +import random +import urlparse +import hmac +import binascii +import httplib2 + +try: + from urlparse import parse_qs + parse_qs # placate pyflakes +except ImportError: + # fall back for Python 2.5 + from cgi import parse_qs + +try: + from hashlib import sha1 + sha = sha1 +except ImportError: + # hashlib was added in Python 2.5 + import sha + +import _version + +__version__ = _version.__version__ + +OAUTH_VERSION = '1.0' # Hi Blaine! +HTTP_METHOD = 'GET' +SIGNATURE_METHOD = 'PLAINTEXT' + + +class Error(RuntimeError): + """Generic exception class.""" + + def __init__(self, message='OAuth error occurred.'): + self._message = message + + @property + def message(self): + """A hack to get around the deprecation errors in 2.6.""" + return self._message + + def __str__(self): + return self._message + + +class MissingSignature(Error): + pass + + +def build_authenticate_header(realm=''): + """Optional WWW-Authenticate header (401 error)""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + + +def build_xoauth_string(url, consumer, token=None): + """Build an XOAUTH string for use in SMTP/IMPA authentication.""" + request = Request.from_consumer_and_token(consumer, token, + "GET", url) + + signing_method = SignatureMethod_HMAC_SHA1() + request.sign_request(signing_method, consumer, token) + + params = [] + for k, v in sorted(request.iteritems()): + if v is not None: + params.append('%s="%s"' % (k, escape(v))) + + return "%s %s %s" % ("GET", url, ','.join(params)) + + +def to_unicode(s): + """ Convert to unicode, raise exception with instructive error + message if s is not unicode, ascii, or utf-8. """ + if not isinstance(s, unicode): + if not isinstance(s, str): + raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s)) + try: + s = s.decode('utf-8') + except UnicodeDecodeError, le: + raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,)) + return s + +def to_utf8(s): + return to_unicode(s).encode('utf-8') + +def to_unicode_if_string(s): + if isinstance(s, basestring): + return to_unicode(s) + else: + return s + +def to_utf8_if_string(s): + if isinstance(s, basestring): + return to_utf8(s) + else: + return s + +def to_unicode_optional_iterator(x): + """ + Raise TypeError if x is a str containing non-utf8 bytes or if x is + an iterable which contains such a str. + """ + if isinstance(x, basestring): + return to_unicode(x) + + try: + l = list(x) + except TypeError, e: + assert 'is not iterable' in str(e) + return x + else: + return [ to_unicode(e) for e in l ] + +def to_utf8_optional_iterator(x): + """ + Raise TypeError if x is a str or if x is an iterable which + contains a str. + """ + if isinstance(x, basestring): + return to_utf8(x) + + try: + l = list(x) + except TypeError, e: + assert 'is not iterable' in str(e) + return x + else: + return [ to_utf8_if_string(e) for e in l ] + +def escape(s): + """Escape a URL including any /.""" + return urllib.quote(s.encode('utf-8'), safe='~') + +def generate_timestamp(): + """Get seconds since epoch (UTC).""" + return int(time.time()) + + +def generate_nonce(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + + +def generate_verifier(length=8): + """Generate pseudorandom number.""" + return ''.join([str(random.randint(0, 9)) for i in range(length)]) + + +class Consumer(object): + """A consumer of OAuth-protected services. + + The OAuth consumer is a "third-party" service that wants to access + protected resources from an OAuth service provider on behalf of an end + user. It's kind of the OAuth client. + + Usually a consumer must be registered with the service provider by the + developer of the consumer software. As part of that process, the service + provider gives the consumer a *key* and a *secret* with which the consumer + software can identify itself to the service. The consumer will include its + key in each request to identify itself, but will use its secret only when + signing requests, to prove that the request is from that particular + registered consumer. + + Once registered, the consumer can then use its consumer credentials to ask + the service provider for a request token, kicking off the OAuth + authorization process. + """ + + key = None + secret = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + if self.key is None or self.secret is None: + raise ValueError("Key and secret must be set.") + + def __str__(self): + data = {'oauth_consumer_key': self.key, + 'oauth_consumer_secret': self.secret} + + return urllib.urlencode(data) + + +class Token(object): + """An OAuth credential used to request authorization or a protected + resource. + + Tokens in OAuth comprise a *key* and a *secret*. The key is included in + requests to identify the token being used, but the secret is used only in + the signature, to prove that the requester is who the server gave the + token to. + + When first negotiating the authorization, the consumer asks for a *request + token* that the live user authorizes with the service provider. The + consumer then exchanges the request token for an *access token* that can + be used to access protected resources. + """ + + key = None + secret = None + callback = None + callback_confirmed = None + verifier = None + + def __init__(self, key, secret): + self.key = key + self.secret = secret + + if self.key is None or self.secret is None: + raise ValueError("Key and secret must be set.") + + def set_callback(self, callback): + self.callback = callback + self.callback_confirmed = 'true' + + def set_verifier(self, verifier=None): + if verifier is not None: + self.verifier = verifier + else: + self.verifier = generate_verifier() + + def get_callback_url(self): + if self.callback and self.verifier: + # Append the oauth_verifier. + parts = urlparse.urlparse(self.callback) + scheme, netloc, path, params, query, fragment = parts[:6] + if query: + query = '%s&oauth_verifier=%s' % (query, self.verifier) + else: + query = 'oauth_verifier=%s' % self.verifier + return urlparse.urlunparse((scheme, netloc, path, params, + query, fragment)) + return self.callback + + def to_string(self): + """Returns this token as a plain string, suitable for storage. + + The resulting string includes the token's secret, so you should never + send or store this string where a third party can read it. + """ + + data = { + 'oauth_token': self.key, + 'oauth_token_secret': self.secret, + } + + if self.callback_confirmed is not None: + data['oauth_callback_confirmed'] = self.callback_confirmed + return urllib.urlencode(data) + + @staticmethod + def from_string(s): + """Deserializes a token from a string like one returned by + `to_string()`.""" + + if not len(s): + raise ValueError("Invalid parameter string.") + + params = parse_qs(s, keep_blank_values=False) + if not len(params): + raise ValueError("Invalid parameter string.") + + try: + key = params['oauth_token'][0] + except Exception: + raise ValueError("'oauth_token' not found in OAuth request.") + + try: + secret = params['oauth_token_secret'][0] + except Exception: + raise ValueError("'oauth_token_secret' not found in " + "OAuth request.") + + token = Token(key, secret) + try: + token.callback_confirmed = params['oauth_callback_confirmed'][0] + except KeyError: + pass # 1.0, no callback confirmed. + return token + + def __str__(self): + return self.to_string() + + +def setter(attr): + name = attr.__name__ + + def getter(self): + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) + + def deleter(self): + del self.__dict__[name] + + return property(getter, attr, deleter) + + +class Request(dict): + + """The parameters and information for an HTTP request, suitable for + authorizing with OAuth credentials. + + When a consumer wants to access a service's protected resources, it does + so using a signed HTTP request identifying itself (the consumer) with its + key, and providing an access token authorized by the end user to access + those resources. + + """ + + version = OAUTH_VERSION + + def __init__(self, method=HTTP_METHOD, url=None, parameters=None, + body='', is_form_encoded=False): + if url is not None: + self.url = to_unicode(url) + self.method = method + if parameters is not None: + for k, v in parameters.iteritems(): + k = to_unicode(k) + v = to_unicode_optional_iterator(v) + self[k] = v + self.body = body + self.is_form_encoded = is_form_encoded + + + @setter + def url(self, value): + self.__dict__['url'] = value + if value is not None: + scheme, netloc, path, params, query, fragment = urlparse.urlparse(value) + + # Exclude default port numbers. + if scheme == 'http' and netloc[-3:] == ':80': + netloc = netloc[:-3] + elif scheme == 'https' and netloc[-4:] == ':443': + netloc = netloc[:-4] + if scheme not in ('http', 'https'): + raise ValueError("Unsupported URL %s (%s)." % (value, scheme)) + + # Normalized URL excludes params, query, and fragment. + self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None)) + else: + self.normalized_url = None + self.__dict__['url'] = None + + @setter + def method(self, value): + self.__dict__['method'] = value.upper() + + def _get_timestamp_nonce(self): + return self['oauth_timestamp'], self['oauth_nonce'] + + def get_nonoauth_parameters(self): + """Get any non-OAuth parameters.""" + return dict([(k, v) for k, v in self.iteritems() + if not k.startswith('oauth_')]) + + def to_header(self, realm=''): + """Serialize as a header for an HTTPAuth request.""" + oauth_params = ((k, v) for k, v in self.items() + if k.startswith('oauth_')) + stringy_params = ((k, escape(str(v))) for k, v in oauth_params) + header_params = ('%s="%s"' % (k, v) for k, v in stringy_params) + params_header = ', '.join(header_params) + + auth_header = 'OAuth realm="%s"' % realm + if params_header: + auth_header = "%s, %s" % (auth_header, params_header) + + return {'Authorization': auth_header} + + def to_postdata(self): + """Serialize as post data for a POST request.""" + d = {} + for k, v in self.iteritems(): + d[k.encode('utf-8')] = to_utf8_optional_iterator(v) + + # tell urlencode to deal with sequence values and map them correctly + # to resulting querystring. for example self["k"] = ["v1", "v2"] will + # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D + return urllib.urlencode(d, True).replace('+', '%20') + + def to_url(self): + """Serialize as a URL for a GET request.""" + base_url = urlparse.urlparse(self.url) + try: + query = base_url.query + except AttributeError: + # must be python <2.5 + query = base_url[4] + query = parse_qs(query) + for k, v in self.items(): + query.setdefault(k, []).append(v) + + try: + scheme = base_url.scheme + netloc = base_url.netloc + path = base_url.path + params = base_url.params + fragment = base_url.fragment + except AttributeError: + # must be python <2.5 + scheme = base_url[0] + netloc = base_url[1] + path = base_url[2] + params = base_url[3] + fragment = base_url[5] + + url = (scheme, netloc, path, params, + urllib.urlencode(query, True), fragment) + return urlparse.urlunparse(url) + + def get_parameter(self, parameter): + ret = self.get(parameter) + if ret is None: + raise Error('Parameter not found: %s' % parameter) + + return ret + + def get_normalized_parameters(self): + """Return a string that contains the parameters that must be signed.""" + items = [] + for key, value in self.iteritems(): + if key == 'oauth_signature': + continue + # 1.0a/9.1.1 states that kvp must be sorted by key, then by value, + # so we unpack sequence values into multiple items for sorting. + if isinstance(value, basestring): + items.append((to_utf8_if_string(key), to_utf8(value))) + else: + try: + value = list(value) + except TypeError, e: + assert 'is not iterable' in str(e) + items.append((to_utf8_if_string(key), to_utf8_if_string(value))) + else: + items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value) + + # Include any query string parameters from the provided URL + query = urlparse.urlparse(self.url)[4] + + url_items = self._split_url_string(query).items() + url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ] + items.extend(url_items) + + items.sort() + encoded_str = urllib.urlencode(items) + # Encode signature parameters per Oauth Core 1.0 protocol + # spec draft 7, section 3.6 + # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6) + # Spaces must be encoded with "%20" instead of "+" + return encoded_str.replace('+', '%20').replace('%7E', '~') + + def sign_request(self, signature_method, consumer, token): + """Set the signature parameter to the result of sign.""" + + if not self.is_form_encoded: + # according to + # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html + # section 4.1.1 "OAuth Consumers MUST NOT include an + # oauth_body_hash parameter on requests with form-encoded + # request bodies." + self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest()) + + if 'oauth_consumer_key' not in self: + self['oauth_consumer_key'] = consumer.key + + if token and 'oauth_token' not in self: + self['oauth_token'] = token.key + + self['oauth_signature_method'] = signature_method.name + self['oauth_signature'] = signature_method.sign(self, consumer, token) + + @classmethod + def make_timestamp(cls): + """Get seconds since epoch (UTC).""" + return str(int(time.time())) + + @classmethod + def make_nonce(cls): + """Generate pseudorandom number.""" + return str(random.randint(0, 100000000)) + + @classmethod + def from_request(cls, http_method, http_url, headers=None, parameters=None, + query_string=None): + """Combines multiple parameter sources.""" + if parameters is None: + parameters = {} + + # Headers + if headers and 'Authorization' in headers: + auth_header = headers['Authorization'] + # Check that the authorization header is OAuth. + if auth_header[:6] == 'OAuth ': + auth_header = auth_header[6:] + try: + # Get the parameters from the header. + header_params = cls._split_header(auth_header) + parameters.update(header_params) + except: + raise Error('Unable to parse OAuth parameters from ' + 'Authorization header.') + + # GET or POST query string. + if query_string: + query_params = cls._split_url_string(query_string) + parameters.update(query_params) + + # URL parameters. + param_str = urlparse.urlparse(http_url)[4] # query + url_params = cls._split_url_string(param_str) + parameters.update(url_params) + + if parameters: + return cls(http_method, http_url, parameters) + + return None + + @classmethod + def from_consumer_and_token(cls, consumer, token=None, + http_method=HTTP_METHOD, http_url=None, parameters=None, + body='', is_form_encoded=False): + if not parameters: + parameters = {} + + defaults = { + 'oauth_consumer_key': consumer.key, + 'oauth_timestamp': cls.make_timestamp(), + 'oauth_nonce': cls.make_nonce(), + 'oauth_version': cls.version, + } + + defaults.update(parameters) + parameters = defaults + + if token: + parameters['oauth_token'] = token.key + if token.verifier: + parameters['oauth_verifier'] = token.verifier + + return Request(http_method, http_url, parameters, body=body, + is_form_encoded=is_form_encoded) + + @classmethod + def from_token_and_callback(cls, token, callback=None, + http_method=HTTP_METHOD, http_url=None, parameters=None): + + if not parameters: + parameters = {} + + parameters['oauth_token'] = token.key + + if callback: + parameters['oauth_callback'] = callback + + return cls(http_method, http_url, parameters) + + @staticmethod + def _split_header(header): + """Turn Authorization: header into parameters.""" + params = {} + parts = header.split(',') + for param in parts: + # Ignore realm parameter. + if param.find('realm') > -1: + continue + # Remove whitespace. + param = param.strip() + # Split key-value. + param_parts = param.split('=', 1) + # Remove quotes and unescape the value. + params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + return params + + @staticmethod + def _split_url_string(param_str): + """Turn URL string into parameters.""" + parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) + for k, v in parameters.iteritems(): + parameters[k] = urllib.unquote(v[0]) + return parameters + + +class Client(httplib2.Http): + """OAuthClient is a worker to attempt to execute a request.""" + + def __init__(self, consumer, token=None, cache=None, timeout=None, + proxy_info=None): + + if consumer is not None and not isinstance(consumer, Consumer): + raise ValueError("Invalid consumer.") + + if token is not None and not isinstance(token, Token): + raise ValueError("Invalid token.") + + self.consumer = consumer + self.token = token + self.method = SignatureMethod_HMAC_SHA1() + + httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info) + + def set_signature_method(self, method): + if not isinstance(method, SignatureMethod): + raise ValueError("Invalid signature method.") + + self.method = method + + def request(self, uri, method="GET", body='', headers=None, + redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None): + DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded' + + if not isinstance(headers, dict): + headers = {} + + if method == "POST": + headers['Content-Type'] = headers.get('Content-Type', + DEFAULT_POST_CONTENT_TYPE) + + is_form_encoded = \ + ((headers.get('Content-Type') == 'application/x-www-form-urlencoded') and (method == "POST")) + + if is_form_encoded and body: + parameters = parse_qs(body) + else: + parameters = None + + req = Request.from_consumer_and_token(self.consumer, + token=self.token, http_method=method, http_url=uri, + parameters=parameters, body=body, is_form_encoded=is_form_encoded) + + req.sign_request(self.method, self.consumer, self.token) + + schema, rest = urllib.splittype(uri) + if rest.startswith('//'): + hierpart = '//' + else: + hierpart = '' + host, rest = urllib.splithost(rest) + + realm = schema + ':' + hierpart + host + + if is_form_encoded: + body = req.to_postdata() + elif method == "GET": + uri = req.to_url() + else: + headers.update(req.to_header(realm=realm)) + + return httplib2.Http.request(self, uri, method=method, body=body, + headers=headers, redirections=redirections, + connection_type=connection_type) + + +class Server(object): + """A skeletal implementation of a service provider, providing protected + resources to requests from authorized consumers. + + This class implements the logic to check requests for authorization. You + can use it with your web server or web framework to protect certain + resources with OAuth. + """ + + timestamp_threshold = 300 # In seconds, five minutes. + version = OAUTH_VERSION + signature_methods = None + + def __init__(self, signature_methods=None): + self.signature_methods = signature_methods or {} + + def add_signature_method(self, signature_method): + self.signature_methods[signature_method.name] = signature_method + return self.signature_methods + + def verify_request(self, request, consumer, token): + """Verifies an api call and checks all the parameters.""" + + self._check_version(request) + self._check_signature(request, consumer, token) + parameters = request.get_nonoauth_parameters() + return parameters + + def build_authenticate_header(self, realm=''): + """Optional support for the authenticate header.""" + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} + + def _check_version(self, request): + """Verify the correct version of the request for this server.""" + version = self._get_version(request) + if version and version != self.version: + raise Error('OAuth version %s not supported.' % str(version)) + + def _get_version(self, request): + """Return the version of the request for this server.""" + try: + version = request.get_parameter('oauth_version') + except: + version = OAUTH_VERSION + + return version + + def _get_signature_method(self, request): + """Figure out the signature with some defaults.""" + try: + signature_method = request.get_parameter('oauth_signature_method') + except: + signature_method = SIGNATURE_METHOD + + try: + # Get the signature method object. + signature_method = self.signature_methods[signature_method] + except: + signature_method_names = ', '.join(self.signature_methods.keys()) + raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) + + return signature_method + + def _get_verifier(self, request): + return request.get_parameter('oauth_verifier') + + def _check_signature(self, request, consumer, token): + timestamp, nonce = request._get_timestamp_nonce() + self._check_timestamp(timestamp) + signature_method = self._get_signature_method(request) + + try: + signature = request.get_parameter('oauth_signature') + except: + raise MissingSignature('Missing oauth_signature.') + + # Validate the signature. + valid = signature_method.check(request, consumer, token, signature) + + if not valid: + key, base = signature_method.signing_base(request, consumer, token) + + raise Error('Invalid signature. Expected signature base ' + 'string: %s' % base) + + def _check_timestamp(self, timestamp): + """Verify that timestamp is recentish.""" + timestamp = int(timestamp) + now = int(time.time()) + lapsed = now - timestamp + if lapsed > self.timestamp_threshold: + raise Error('Expired timestamp: given %d and now %s has a ' + 'greater difference than threshold %d' % (timestamp, now, + self.timestamp_threshold)) + + +class SignatureMethod(object): + """A way of signing requests. + + The OAuth protocol lets consumers and service providers pick a way to sign + requests. This interface shows the methods expected by the other `oauth` + modules for signing requests. Subclass it and implement its methods to + provide a new way to sign requests. + """ + + def signing_base(self, request, consumer, token): + """Calculates the string that needs to be signed. + + This method returns a 2-tuple containing the starting key for the + signing and the message to be signed. The latter may be used in error + messages to help clients debug their software. + + """ + raise NotImplementedError + + def sign(self, request, consumer, token): + """Returns the signature for the given request, based on the consumer + and token also provided. + + You should use your implementation of `signing_base()` to build the + message to sign. Otherwise it may be less useful for debugging. + + """ + raise NotImplementedError + + def check(self, request, consumer, token, signature): + """Returns whether the given signature is the correct signature for + the given consumer and token signing the given request.""" + built = self.sign(request, consumer, token) + return built == signature + + +class SignatureMethod_HMAC_SHA1(SignatureMethod): + name = 'HMAC-SHA1' + + def signing_base(self, request, consumer, token): + if not hasattr(request, 'normalized_url') or request.normalized_url is None: + raise ValueError("Base URL for request is not set.") + + sig = ( + escape(request.method), + escape(request.normalized_url), + escape(request.get_normalized_parameters()), + ) + + key = '%s&' % escape(consumer.secret) + if token: + key += escape(token.secret) + raw = '&'.join(sig) + return key, raw + + def sign(self, request, consumer, token): + """Builds the base signature string.""" + key, raw = self.signing_base(request, consumer, token) + + hashed = hmac.new(key, raw, sha) + + # Calculate the digest base 64. + return binascii.b2a_base64(hashed.digest())[:-1] + + +class SignatureMethod_PLAINTEXT(SignatureMethod): + + name = 'PLAINTEXT' + + def signing_base(self, request, consumer, token): + """Concatenates the consumer key and secret with the token's + secret.""" + sig = '%s&' % escape(consumer.secret) + if token: + sig = sig + escape(token.secret) + return sig, sig + + def sign(self, request, consumer, token): + key, raw = self.signing_base(request, consumer, token) + return raw diff --git a/build/lib/oauth2/_version.py b/build/lib/oauth2/_version.py new file mode 100644 index 00000000..0d74e056 --- /dev/null +++ b/build/lib/oauth2/_version.py @@ -0,0 +1,18 @@ +# This is the version of this source code. + +manual_verstr = "1.5" + + + +auto_build_num = "170" + + + +verstr = manual_verstr + "." + auto_build_num +try: + from pyutil.version_class import Version as pyutil_Version + __version__ = pyutil_Version(verstr) +except (ImportError, ValueError): + # Maybe there is no pyutil installed. + from distutils.version import LooseVersion as distutils_Version + __version__ = distutils_Version(verstr) diff --git a/build/lib/oauth2/clients/__init__.py b/build/lib/oauth2/clients/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/oauth2/clients/imap.py b/build/lib/oauth2/clients/imap.py new file mode 100644 index 00000000..68b7cd8c --- /dev/null +++ b/build/lib/oauth2/clients/imap.py @@ -0,0 +1,40 @@ +""" +The MIT License + +Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import oauth2 +import imaplib + + +class IMAP4_SSL(imaplib.IMAP4_SSL): + """IMAP wrapper for imaplib.IMAP4_SSL that implements XOAUTH.""" + + def authenticate(self, url, consumer, token): + if consumer is not None and not isinstance(consumer, oauth2.Consumer): + raise ValueError("Invalid consumer.") + + if token is not None and not isinstance(token, oauth2.Token): + raise ValueError("Invalid token.") + + imaplib.IMAP4_SSL.authenticate(self, 'XOAUTH', + lambda x: oauth2.build_xoauth_string(url, consumer, token)) diff --git a/build/lib/oauth2/clients/smtp.py b/build/lib/oauth2/clients/smtp.py new file mode 100644 index 00000000..3e7bf0b0 --- /dev/null +++ b/build/lib/oauth2/clients/smtp.py @@ -0,0 +1,41 @@ +""" +The MIT License + +Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import oauth2 +import smtplib +import base64 + + +class SMTP(smtplib.SMTP): + """SMTP wrapper for smtplib.SMTP that implements XOAUTH.""" + + def authenticate(self, url, consumer, token): + if consumer is not None and not isinstance(consumer, oauth2.Consumer): + raise ValueError("Invalid consumer.") + + if token is not None and not isinstance(token, oauth2.Token): + raise ValueError("Invalid token.") + + self.docmd('AUTH', 'XOAUTH %s' % \ + base64.b64encode(oauth2.build_xoauth_string(url, consumer, token))) diff --git a/build/lib/tests/__init__.py b/build/lib/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/build/lib/tests/test_oauth.py b/build/lib/tests/test_oauth.py new file mode 100644 index 00000000..099e5794 --- /dev/null +++ b/build/lib/tests/test_oauth.py @@ -0,0 +1,1309 @@ +# -*- coding: utf-8 -*- + +""" +The MIT License + +Copyright (c) 2009 Vic Fryzel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" +import sys +import os +import unittest +import oauth2 as oauth +import random +import time +import urllib +import urlparse +from types import ListType +import mock +import httplib2 + +# Fix for python2.5 compatibility +try: + from urlparse import parse_qs, parse_qsl +except ImportError: + from cgi import parse_qs, parse_qsl + + +sys.path[0:0] = [os.path.join(os.path.dirname(__file__), ".."),] + + +class TestError(unittest.TestCase): + def test_message(self): + try: + raise oauth.Error + except oauth.Error, e: + self.assertEqual(e.message, 'OAuth error occurred.') + + msg = 'OMG THINGS BROKE!!!!' + try: + raise oauth.Error(msg) + except oauth.Error, e: + self.assertEqual(e.message, msg) + + def test_str(self): + try: + raise oauth.Error + except oauth.Error, e: + self.assertEquals(str(e), 'OAuth error occurred.') + +class TestGenerateFunctions(unittest.TestCase): + def test_build_auth_header(self): + header = oauth.build_authenticate_header() + self.assertEqual(header['WWW-Authenticate'], 'OAuth realm=""') + self.assertEqual(len(header), 1) + realm = 'http://example.myrealm.com/' + header = oauth.build_authenticate_header(realm) + self.assertEqual(header['WWW-Authenticate'], 'OAuth realm="%s"' % + realm) + self.assertEqual(len(header), 1) + + def test_build_xoauth_string(self): + consumer = oauth.Consumer('consumer_token', 'consumer_secret') + token = oauth.Token('user_token', 'user_secret') + url = "https://mail.google.com/mail/b/joe@example.com/imap/" + xoauth_string = oauth.build_xoauth_string(url, consumer, token) + + method, oauth_url, oauth_string = xoauth_string.split(' ') + + self.assertEqual("GET", method) + self.assertEqual(url, oauth_url) + + returned = {} + parts = oauth_string.split(',') + for part in parts: + var, val = part.split('=') + returned[var] = val.strip('"') + + self.assertEquals('HMAC-SHA1', returned['oauth_signature_method']) + self.assertEquals('user_token', returned['oauth_token']) + self.assertEquals('consumer_token', returned['oauth_consumer_key']) + self.assertTrue('oauth_signature' in returned, 'oauth_signature') + + def test_escape(self): + string = 'http://whatever.com/~someuser/?test=test&other=other' + self.assert_('~' in oauth.escape(string)) + string = '../../../../../../../etc/passwd' + self.assert_('../' not in oauth.escape(string)) + + def test_gen_nonce(self): + nonce = oauth.generate_nonce() + self.assertEqual(len(nonce), 8) + nonce = oauth.generate_nonce(20) + self.assertEqual(len(nonce), 20) + + def test_gen_verifier(self): + verifier = oauth.generate_verifier() + self.assertEqual(len(verifier), 8) + verifier = oauth.generate_verifier(16) + self.assertEqual(len(verifier), 16) + + def test_gen_timestamp(self): + exp = int(time.time()) + now = oauth.generate_timestamp() + self.assertEqual(exp, now) + +class TestConsumer(unittest.TestCase): + def setUp(self): + self.key = 'my-key' + self.secret = 'my-secret' + self.consumer = oauth.Consumer(key=self.key, secret=self.secret) + + def test_init(self): + self.assertEqual(self.consumer.key, self.key) + self.assertEqual(self.consumer.secret, self.secret) + + def test_basic(self): + self.assertRaises(ValueError, lambda: oauth.Consumer(None, None)) + self.assertRaises(ValueError, lambda: oauth.Consumer('asf', None)) + self.assertRaises(ValueError, lambda: oauth.Consumer(None, 'dasf')) + + def test_str(self): + res = dict(parse_qsl(str(self.consumer))) + self.assertTrue('oauth_consumer_key' in res) + self.assertTrue('oauth_consumer_secret' in res) + self.assertEquals(res['oauth_consumer_key'], self.consumer.key) + self.assertEquals(res['oauth_consumer_secret'], self.consumer.secret) + +class TestToken(unittest.TestCase): + def setUp(self): + self.key = 'my-key' + self.secret = 'my-secret' + self.token = oauth.Token(self.key, self.secret) + + def test_basic(self): + self.assertRaises(ValueError, lambda: oauth.Token(None, None)) + self.assertRaises(ValueError, lambda: oauth.Token('asf', None)) + self.assertRaises(ValueError, lambda: oauth.Token(None, 'dasf')) + + def test_init(self): + self.assertEqual(self.token.key, self.key) + self.assertEqual(self.token.secret, self.secret) + self.assertEqual(self.token.callback, None) + self.assertEqual(self.token.callback_confirmed, None) + self.assertEqual(self.token.verifier, None) + + def test_set_callback(self): + self.assertEqual(self.token.callback, None) + self.assertEqual(self.token.callback_confirmed, None) + cb = 'http://www.example.com/my-callback' + self.token.set_callback(cb) + self.assertEqual(self.token.callback, cb) + self.assertEqual(self.token.callback_confirmed, 'true') + self.token.set_callback(None) + self.assertEqual(self.token.callback, None) + # TODO: The following test should probably not pass, but it does + # To fix this, check for None and unset 'true' in set_callback + # Additionally, should a confirmation truly be done of the callback? + self.assertEqual(self.token.callback_confirmed, 'true') + + def test_set_verifier(self): + self.assertEqual(self.token.verifier, None) + v = oauth.generate_verifier() + self.token.set_verifier(v) + self.assertEqual(self.token.verifier, v) + self.token.set_verifier() + self.assertNotEqual(self.token.verifier, v) + self.token.set_verifier('') + self.assertEqual(self.token.verifier, '') + + def test_get_callback_url(self): + self.assertEqual(self.token.get_callback_url(), None) + + self.token.set_verifier() + self.assertEqual(self.token.get_callback_url(), None) + + cb = 'http://www.example.com/my-callback?save=1&return=true' + v = oauth.generate_verifier() + self.token.set_callback(cb) + self.token.set_verifier(v) + url = self.token.get_callback_url() + verifier_str = '&oauth_verifier=%s' % v + self.assertEqual(url, '%s%s' % (cb, verifier_str)) + + cb = 'http://www.example.com/my-callback-no-query' + v = oauth.generate_verifier() + self.token.set_callback(cb) + self.token.set_verifier(v) + url = self.token.get_callback_url() + verifier_str = '?oauth_verifier=%s' % v + self.assertEqual(url, '%s%s' % (cb, verifier_str)) + + def test_to_string(self): + string = 'oauth_token_secret=%s&oauth_token=%s' % (self.secret, + self.key) + self.assertEqual(self.token.to_string(), string) + + self.token.set_callback('http://www.example.com/my-callback') + string += '&oauth_callback_confirmed=true' + self.assertEqual(self.token.to_string(), string) + + def _compare_tokens(self, new): + self.assertEqual(self.token.key, new.key) + self.assertEqual(self.token.secret, new.secret) + # TODO: What about copying the callback to the new token? + # self.assertEqual(self.token.callback, new.callback) + self.assertEqual(self.token.callback_confirmed, + new.callback_confirmed) + # TODO: What about copying the verifier to the new token? + # self.assertEqual(self.token.verifier, new.verifier) + + def test_to_string(self): + tok = oauth.Token('tooken', 'seecret') + self.assertEqual(str(tok), 'oauth_token_secret=seecret&oauth_token=tooken') + + def test_from_string(self): + self.assertRaises(ValueError, lambda: oauth.Token.from_string('')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('blahblahblah')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('blah=blah')) + + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=asfdasf')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=asfdasf')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=&oauth_token_secret=')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=tooken%26oauth_token_secret=seecret')) + + string = self.token.to_string() + new = oauth.Token.from_string(string) + self._compare_tokens(new) + + self.token.set_callback('http://www.example.com/my-callback') + string = self.token.to_string() + new = oauth.Token.from_string(string) + self._compare_tokens(new) + +class ReallyEqualMixin: + def failUnlessReallyEqual(self, a, b, msg=None): + self.failUnlessEqual(a, b, msg=msg) + self.failUnlessEqual(type(a), type(b), msg="a :: %r, b :: %r, %r" % (a, b, msg)) + +class TestFuncs(unittest.TestCase): + def test_to_unicode(self): + self.failUnlessRaises(TypeError, oauth.to_unicode, '\xae') + self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, '\xae') + self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, ['\xae']) + + self.failUnlessEqual(oauth.to_unicode(':-)'), u':-)') + self.failUnlessEqual(oauth.to_unicode(u'\u00ae'), u'\u00ae') + self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), u'\u00ae') + self.failUnlessEqual(oauth.to_unicode_optional_iterator([':-)']), [u':-)']) + self.failUnlessEqual(oauth.to_unicode_optional_iterator([u'\u00ae']), [u'\u00ae']) + +class TestRequest(unittest.TestCase, ReallyEqualMixin): + def test_setter(self): + url = "http://example.com" + method = "GET" + req = oauth.Request(method) + self.assertTrue(not hasattr(req, 'url') or req.url is None) + self.assertTrue(not hasattr(req, 'normalized_url') or req.normalized_url is None) + + def test_deleter(self): + url = "http://example.com" + method = "GET" + req = oauth.Request(method, url) + + try: + del req.url + url = req.url + self.fail("AttributeError should have been raised on empty url.") + except AttributeError: + pass + except Exception, e: + self.fail(str(e)) + + def test_url(self): + url1 = "http://example.com:80/foo.php" + url2 = "https://example.com:443/foo.php" + exp1 = "http://example.com/foo.php" + exp2 = "https://example.com/foo.php" + method = "GET" + + req = oauth.Request(method, url1) + self.assertEquals(req.normalized_url, exp1) + self.assertEquals(req.url, url1) + + req = oauth.Request(method, url2) + self.assertEquals(req.normalized_url, exp2) + self.assertEquals(req.url, url2) + + def test_bad_url(self): + request = oauth.Request() + try: + request.url = "ftp://example.com" + self.fail("Invalid URL scheme was accepted.") + except ValueError: + pass + + def test_unset_consumer_and_token(self): + consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') + token = oauth.Token('my_key', 'my_secret') + request = oauth.Request("GET", "http://example.com/fetch.php") + request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, + token) + + self.assertEquals(consumer.key, request['oauth_consumer_key']) + self.assertEquals(token.key, request['oauth_token']) + + def test_no_url_set(self): + consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') + token = oauth.Token('my_key', 'my_secret') + request = oauth.Request() + + try: + try: + request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), + consumer, token) + except TypeError: + self.fail("Signature method didn't check for a normalized URL.") + except ValueError: + pass + + def test_url_query(self): + url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + normalized_url = urlparse.urlunparse(urlparse.urlparse(url)[:3] + (None, None, None)) + method = "GET" + + req = oauth.Request(method, url) + self.assertEquals(req.url, url) + self.assertEquals(req.normalized_url, normalized_url) + + def test_get_parameter(self): + url = "http://example.com" + method = "GET" + params = {'oauth_consumer' : 'asdf'} + req = oauth.Request(method, url, parameters=params) + + self.assertEquals(req.get_parameter('oauth_consumer'), 'asdf') + self.assertRaises(oauth.Error, req.get_parameter, 'blah') + + def test_get_nonoauth_parameters(self): + + oauth_params = { + 'oauth_consumer': 'asdfasdfasdf' + } + + other_params = { + u'foo': u'baz', + u'bar': u'foo', + u'multi': [u'FOO',u'BAR'], + u'uni_utf8': u'\xae', + u'uni_unicode': u'\u00ae', + u'uni_unicode_2': u'åÅøØ', + } + + params = oauth_params + params.update(other_params) + + req = oauth.Request("GET", "http://example.com", params) + self.assertEquals(other_params, req.get_nonoauth_parameters()) + + def test_to_header(self): + realm = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", realm, params) + header, value = req.to_header(realm).items()[0] + + parts = value.split('OAuth ') + vars = parts[1].split(', ') + self.assertTrue(len(vars), (len(params) + 1)) + + res = {} + for v in vars: + var, val = v.split('=') + res[var] = urllib.unquote(val.strip('"')) + + self.assertEquals(realm, res['realm']) + del res['realm'] + + self.assertTrue(len(res), len(params)) + + for key, val in res.items(): + self.assertEquals(val, params.get(key)) + + def test_to_postdata_nonascii(self): + realm = "http://sp.example.com/" + + params = { + 'nonasciithing': u'q\xbfu\xe9 ,aasp u?..a.s', + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", realm, params) + + self.failUnlessReallyEqual(req.to_postdata(), 'nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s&oauth_nonce=4572616e48616d6d65724c61686176&oauth_timestamp=137131200&oauth_consumer_key=0685bd9184jfhq22&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=ad180jjd733klru7&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D') + + def test_to_postdata(self): + realm = "http://sp.example.com/" + + params = { + 'multi': ['FOO','BAR'], + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", realm, params) + + flat = [('multi','FOO'),('multi','BAR')] + del params['multi'] + flat.extend(params.items()) + kf = lambda x: x[0] + self.assertEquals(sorted(flat, key=kf), sorted(parse_qsl(req.to_postdata()), key=kf)) + + def test_to_url(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", url, params) + exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params))) + res = urlparse.urlparse(req.to_url()) + self.assertEquals(exp.scheme, res.scheme) + self.assertEquals(exp.netloc, res.netloc) + self.assertEquals(exp.path, res.path) + + a = parse_qs(exp.query) + b = parse_qs(res.query) + self.assertEquals(a, b) + + def test_to_url_with_query(self): + url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", url, params) + # Note: the url above already has query parameters, so append new ones with & + exp = urlparse.urlparse("%s&%s" % (url, urllib.urlencode(params))) + res = urlparse.urlparse(req.to_url()) + self.assertEquals(exp.scheme, res.scheme) + self.assertEquals(exp.netloc, res.netloc) + self.assertEquals(exp.path, res.path) + + a = parse_qs(exp.query) + b = parse_qs(res.query) + self.assertTrue('alt' in b) + self.assertTrue('max-contacts' in b) + self.assertEquals(b['alt'], ['json']) + self.assertEquals(b['max-contacts'], ['10']) + self.assertEquals(a, b) + + def test_signature_base_string_nonascii_nonutf8(self): + consumer = oauth.Consumer('consumer_token', 'consumer_secret') + + url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\u2766,+CA' + req = oauth.Request("GET", url) + self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + + url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\xe2\x9d\xa6,+CA' + req = oauth.Request("GET", url) + self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + + url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' + req = oauth.Request("GET", url) + self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + + url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' + req = oauth.Request("GET", url) + self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + + def test_signature_base_string_with_query(self): + url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + req = oauth.Request("GET", url, params) + self.assertEquals(req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') + self.assertEquals(req.url, 'https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10') + normalized_params = parse_qsl(req.get_normalized_parameters()) + self.assertTrue(len(normalized_params), len(params) + 2) + normalized_params = dict(normalized_params) + for key, value in params.iteritems(): + if key == 'oauth_signature': + continue + self.assertEquals(value, normalized_params[key]) + self.assertEquals(normalized_params['alt'], 'json') + self.assertEquals(normalized_params['max-contacts'], '10') + + def test_get_normalized_parameters_empty(self): + url = "http://sp.example.com/?empty=" + + req = oauth.Request("GET", url) + + res = req.get_normalized_parameters() + + expected='empty=' + + self.assertEquals(expected, res) + + def test_get_normalized_parameters_duplicate(self): + url = "http://example.com/v2/search/videos?oauth_nonce=79815175&oauth_timestamp=1295397962&oauth_consumer_key=mykey&oauth_signature_method=HMAC-SHA1&q=car&oauth_version=1.0&offset=10&oauth_signature=spWLI%2FGQjid7sQVd5%2FarahRxzJg%3D" + + req = oauth.Request("GET", url) + + res = req.get_normalized_parameters() + + expected='oauth_consumer_key=mykey&oauth_nonce=79815175&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1295397962&oauth_version=1.0&offset=10&q=car' + + self.assertEquals(expected, res) + + def test_get_normalized_parameters_from_url(self): + # example copied from + # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js + # which in turns says that it was copied from + # http://oauth.net/core/1.0/#sig_base_example . + url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" + + req = oauth.Request("GET", url) + + res = req.get_normalized_parameters() + + expected = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original' + + self.assertEquals(expected, res) + + def test_signing_base(self): + # example copied from + # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js + # which in turns says that it was copied from + # http://oauth.net/core/1.0/#sig_base_example . + url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" + + req = oauth.Request("GET", url) + + sm = oauth.SignatureMethod_HMAC_SHA1() + + consumer = oauth.Consumer('dpf43f3p2l4k3l03', 'foo') + key, raw = sm.signing_base(req, consumer, None) + + expected = 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal' + self.assertEquals(expected, raw) + + def test_get_normalized_parameters(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'multi': ['FOO','BAR', u'\u00ae', '\xc2\xae'], + 'multi_same': ['FOO','FOO'], + 'uni_utf8_bytes': '\xc2\xae', + 'uni_unicode_object': u'\u00ae' + } + + req = oauth.Request("GET", url, params) + + res = req.get_normalized_parameters() + + expected='multi=BAR&multi=FOO&multi=%C2%AE&multi=%C2%AE&multi_same=FOO&multi_same=FOO&oauth_consumer_key=0685bd9184jfhq22&oauth_nonce=4572616e48616d6d65724c61686176&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131200&oauth_token=ad180jjd733klru7&oauth_version=1.0&uni_unicode_object=%C2%AE&uni_utf8_bytes=%C2%AE' + + self.assertEquals(expected, res) + + def test_get_normalized_parameters_ignores_auth_signature(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_signature': "some-random-signature-%d" % random.randint(1000, 2000), + 'oauth_token': "ad180jjd733klru7", + } + + req = oauth.Request("GET", url, params) + + res = req.get_normalized_parameters() + + self.assertNotEquals(urllib.urlencode(sorted(params.items())), res) + + foo = params.copy() + del foo["oauth_signature"] + self.assertEqual(urllib.urlencode(sorted(foo.items())), res) + + def test_set_signature_method(self): + consumer = oauth.Consumer('key', 'secret') + client = oauth.Client(consumer) + + class Blah: + pass + + try: + client.set_signature_method(Blah()) + self.fail("Client.set_signature_method() accepted invalid method.") + except ValueError: + pass + + m = oauth.SignatureMethod_HMAC_SHA1() + client.set_signature_method(m) + self.assertEquals(m, client.method) + + def test_get_normalized_string_escapes_spaces_properly(self): + url = "http://sp.example.com/" + params = { + "some_random_data": random.randint(100, 1000), + "data": "This data with a random number (%d) has spaces!" % random.randint(1000, 2000), + } + + req = oauth.Request("GET", url, params) + res = req.get_normalized_parameters() + expected = urllib.urlencode(sorted(params.items())).replace('+', '%20') + self.assertEqual(expected, res) + + @mock.patch('oauth2.Request.make_timestamp') + @mock.patch('oauth2.Request.make_nonce') + def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): + mock_make_nonce.return_value = 5 + mock_make_timestamp.return_value = 6 + + tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") + con = oauth.Consumer(key="con-test-key", secret="con-test-secret") + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_token': tok.key, + 'oauth_consumer_key': con.key + } + + # If someone passes a sequence of bytes which is not ascii for + # url, we'll raise an exception as early as possible. + url = "http://sp.example.com/\x92" # It's actually cp1252-encoding... + self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + + # And if they pass an unicode, then we'll use it. + url = u'http://sp.example.com/\u2019' + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'cMzvCkhvLL57+sTIxLITTHfkqZk=') + + # And if it is a utf-8-encoded-then-percent-encoded non-ascii + # thing, we'll decode it and use it. + url = "http://sp.example.com/%E2%80%99" + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'yMLKOyNKC/DkyhUOb8DLSvceEWE=') + + # Same thing with the params. + url = "http://sp.example.com/" + + # If someone passes a sequence of bytes which is not ascii in + # params, we'll raise an exception as early as possible. + params['non_oauth_thing'] = '\xae', # It's actually cp1252-encoding... + self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + + # And if they pass a unicode, then we'll use it. + params['non_oauth_thing'] = u'\u2019' + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_signature'], '0GU50m0v60CVDB5JnoBXnvvvKx4=') + + # And if it is a utf-8-encoded non-ascii thing, we'll decode + # it and use it. + params['non_oauth_thing'] = '\xc2\xae' + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_signature'], 'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') + + + # Also if there are non-utf8 bytes in the query args. + url = "http://sp.example.com/?q=\x92" # cp1252 + self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + + def test_request_hash_of_body(self): + tok = oauth.Token(key="token", secret="tok-test-secret") + con = oauth.Consumer(key="consumer", secret="con-test-secret") + + # Example 1a from Appendix A.1 of + # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html + # Except that we get a differetn result than they do. + + params = { + 'oauth_version': "1.0", + 'oauth_token': tok.key, + 'oauth_nonce': 10288510250934, + 'oauth_timestamp': 1236874155, + 'oauth_consumer_key': con.key + } + + url = u"http://www.example.com/resource" + req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + self.failUnlessReallyEqual(req['oauth_signature'], 't+MX8l/0S8hdbVQL99nD0X1fPnM=') + # oauth-bodyhash.html A.1 has + # '08bUFF%2Fjmp59mWB7cSgCYBUpJ0U%3D', but I don't see how that + # is possible. + + # Example 1b + params = { + 'oauth_version': "1.0", + 'oauth_token': tok.key, + 'oauth_nonce': 10369470270925, + 'oauth_timestamp': 1236874236, + 'oauth_consumer_key': con.key + } + + req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + self.failUnlessReallyEqual(req['oauth_signature'], 'CTFmrqJIGT7NsWJ42OrujahTtTc=') + + # Appendix A.2 + params = { + 'oauth_version': "1.0", + 'oauth_token': tok.key, + 'oauth_nonce': 8628868109991, + 'oauth_timestamp': 1238395022, + 'oauth_consumer_key': con.key + } + + req = oauth.Request(method="GET", url=url, parameters=params, is_form_encoded=False) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) + self.failUnlessReallyEqual(req['oauth_body_hash'], '2jmj7l5rSw0yVb/vlWAYkK/YBwk=') + self.failUnlessReallyEqual(req['oauth_signature'], 'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') + + + def test_sign_request(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200" + } + + tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") + con = oauth.Consumer(key="con-test-key", secret="con-test-secret") + + params['oauth_token'] = tok.key + params['oauth_consumer_key'] = con.key + req = oauth.Request(method="GET", url=url, parameters=params) + + methods = { + 'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), + 'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT() + } + + for exp, method in methods.items(): + req.sign_request(method, con, tok) + self.assertEquals(req['oauth_signature_method'], method.name) + self.assertEquals(req['oauth_signature'], exp) + + # Also if there are non-ascii chars in the URL. + url = "http://sp.example.com/\xe2\x80\x99" # utf-8 bytes + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) + self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + + url = u'http://sp.example.com/\u2019' # Python unicode object + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) + self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + + # Also if there are non-ascii chars in the query args. + url = "http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) + self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + + url = u'http://sp.example.com/?q=\u2019' # Python unicode object + req = oauth.Request(method="GET", url=url, parameters=params) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) + self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + + def test_from_request(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + req = oauth.Request("GET", url, params) + headers = req.to_header() + + # Test from the headers + req = oauth.Request.from_request("GET", url, headers) + self.assertEquals(req.method, "GET") + self.assertEquals(req.url, url) + + self.assertEquals(params, req.copy()) + + # Test with bad OAuth headers + bad_headers = { + 'Authorization' : 'OAuth this is a bad header' + } + + self.assertRaises(oauth.Error, oauth.Request.from_request, "GET", + url, bad_headers) + + # Test getting from query string + qs = urllib.urlencode(params) + req = oauth.Request.from_request("GET", url, query_string=qs) + + exp = parse_qs(qs, keep_blank_values=False) + for k, v in exp.iteritems(): + exp[k] = urllib.unquote(v[0]) + + self.assertEquals(exp, req.copy()) + + # Test that a boned from_request() call returns None + req = oauth.Request.from_request("GET", url) + self.assertEquals(None, req) + + def test_from_token_and_callback(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': "137131200", + 'oauth_consumer_key': "0685bd9184jfhq22", + 'oauth_signature_method': "HMAC-SHA1", + 'oauth_token': "ad180jjd733klru7", + 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", + } + + tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") + req = oauth.Request.from_token_and_callback(tok) + self.assertFalse('oauth_callback' in req) + self.assertEquals(req['oauth_token'], tok.key) + + req = oauth.Request.from_token_and_callback(tok, callback=url) + self.assertTrue('oauth_callback' in req) + self.assertEquals(req['oauth_callback'], url) + + def test_from_consumer_and_token(self): + url = "http://sp.example.com/" + + tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") + tok.set_verifier('this_is_a_test_verifier') + con = oauth.Consumer(key="con-test-key", secret="con-test-secret") + req = oauth.Request.from_consumer_and_token(con, token=tok, + http_method="GET", http_url=url) + + self.assertEquals(req['oauth_token'], tok.key) + self.assertEquals(req['oauth_consumer_key'], con.key) + self.assertEquals(tok.verifier, req['oauth_verifier']) + +class SignatureMethod_Bad(oauth.SignatureMethod): + name = "BAD" + + def signing_base(self, request, consumer, token): + return "" + + def sign(self, request, consumer, token): + return "invalid-signature" + + +class TestServer(unittest.TestCase): + def setUp(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': "1.0", + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': int(time.time()), + 'bar': 'blerg', + 'multi': ['FOO','BAR'], + 'foo': 59 + } + + self.consumer = oauth.Consumer(key="consumer-key", + secret="consumer-secret") + self.token = oauth.Token(key="token-key", secret="token-secret") + + params['oauth_token'] = self.token.key + params['oauth_consumer_key'] = self.consumer.key + self.request = oauth.Request(method="GET", url=url, parameters=params) + + signature_method = oauth.SignatureMethod_HMAC_SHA1() + self.request.sign_request(signature_method, self.consumer, self.token) + + def test_init(self): + server = oauth.Server(signature_methods={'HMAC-SHA1' : oauth.SignatureMethod_HMAC_SHA1()}) + self.assertTrue('HMAC-SHA1' in server.signature_methods) + self.assertTrue(isinstance(server.signature_methods['HMAC-SHA1'], + oauth.SignatureMethod_HMAC_SHA1)) + + server = oauth.Server() + self.assertEquals(server.signature_methods, {}) + + def test_add_signature_method(self): + server = oauth.Server() + res = server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + self.assertTrue(len(res) == 1) + self.assertTrue('HMAC-SHA1' in res) + self.assertTrue(isinstance(res['HMAC-SHA1'], + oauth.SignatureMethod_HMAC_SHA1)) + + res = server.add_signature_method(oauth.SignatureMethod_PLAINTEXT()) + self.assertTrue(len(res) == 2) + self.assertTrue('PLAINTEXT' in res) + self.assertTrue(isinstance(res['PLAINTEXT'], + oauth.SignatureMethod_PLAINTEXT)) + + def test_verify_request(self): + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + parameters = server.verify_request(self.request, self.consumer, + self.token) + + self.assertTrue('bar' in parameters) + self.assertTrue('foo' in parameters) + self.assertTrue('multi' in parameters) + self.assertEquals(parameters['bar'], 'blerg') + self.assertEquals(parameters['foo'], 59) + self.assertEquals(parameters['multi'], ['FOO','BAR']) + + def test_build_authenticate_header(self): + server = oauth.Server() + headers = server.build_authenticate_header('example.com') + self.assertTrue('WWW-Authenticate' in headers) + self.assertEquals('OAuth realm="example.com"', + headers['WWW-Authenticate']) + + def test_no_version(self): + url = "http://sp.example.com/" + + params = { + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': int(time.time()), + 'bar': 'blerg', + 'multi': ['FOO','BAR'], + 'foo': 59 + } + + self.consumer = oauth.Consumer(key="consumer-key", + secret="consumer-secret") + self.token = oauth.Token(key="token-key", secret="token-secret") + + params['oauth_token'] = self.token.key + params['oauth_consumer_key'] = self.consumer.key + self.request = oauth.Request(method="GET", url=url, parameters=params) + + signature_method = oauth.SignatureMethod_HMAC_SHA1() + self.request.sign_request(signature_method, self.consumer, self.token) + + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + parameters = server.verify_request(self.request, self.consumer, + self.token) + + def test_invalid_version(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': '222.9922', + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': int(time.time()), + 'bar': 'blerg', + 'multi': ['foo','bar'], + 'foo': 59 + } + + consumer = oauth.Consumer(key="consumer-key", + secret="consumer-secret") + token = oauth.Token(key="token-key", secret="token-secret") + + params['oauth_token'] = token.key + params['oauth_consumer_key'] = consumer.key + request = oauth.Request(method="GET", url=url, parameters=params) + + signature_method = oauth.SignatureMethod_HMAC_SHA1() + request.sign_request(signature_method, consumer, token) + + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + self.assertRaises(oauth.Error, server.verify_request, request, consumer, token) + + def test_invalid_signature_method(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': '1.0', + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': int(time.time()), + 'bar': 'blerg', + 'multi': ['FOO','BAR'], + 'foo': 59 + } + + consumer = oauth.Consumer(key="consumer-key", + secret="consumer-secret") + token = oauth.Token(key="token-key", secret="token-secret") + + params['oauth_token'] = token.key + params['oauth_consumer_key'] = consumer.key + request = oauth.Request(method="GET", url=url, parameters=params) + + signature_method = SignatureMethod_Bad() + request.sign_request(signature_method, consumer, token) + + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + self.assertRaises(oauth.Error, server.verify_request, request, + consumer, token) + + def test_missing_signature(self): + url = "http://sp.example.com/" + + params = { + 'oauth_version': '1.0', + 'oauth_nonce': "4572616e48616d6d65724c61686176", + 'oauth_timestamp': int(time.time()), + 'bar': 'blerg', + 'multi': ['FOO','BAR'], + 'foo': 59 + } + + consumer = oauth.Consumer(key="consumer-key", + secret="consumer-secret") + token = oauth.Token(key="token-key", secret="token-secret") + + params['oauth_token'] = token.key + params['oauth_consumer_key'] = consumer.key + request = oauth.Request(method="GET", url=url, parameters=params) + + signature_method = oauth.SignatureMethod_HMAC_SHA1() + request.sign_request(signature_method, consumer, token) + del request['oauth_signature'] + + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + + self.assertRaises(oauth.MissingSignature, server.verify_request, + request, consumer, token) + + +# Request Token: http://oauth-sandbox.sevengoslings.net/request_token +# Auth: http://oauth-sandbox.sevengoslings.net/authorize +# Access Token: http://oauth-sandbox.sevengoslings.net/access_token +# Two-legged: http://oauth-sandbox.sevengoslings.net/two_legged +# Three-legged: http://oauth-sandbox.sevengoslings.net/three_legged +# Key: bd37aed57e15df53 +# Secret: 0e9e6413a9ef49510a4f68ed02cd +class TestClient(unittest.TestCase): +# oauth_uris = { +# 'request_token': '/request_token.php', +# 'access_token': '/access_token.php' +# } + + oauth_uris = { + 'request_token': '/request_token', + 'authorize': '/authorize', + 'access_token': '/access_token', + 'two_legged': '/two_legged', + 'three_legged': '/three_legged' + } + + consumer_key = 'bd37aed57e15df53' + consumer_secret = '0e9e6413a9ef49510a4f68ed02cd' + host = 'http://oauth-sandbox.sevengoslings.net' + + def setUp(self): + self.consumer = oauth.Consumer(key=self.consumer_key, + secret=self.consumer_secret) + + self.body = { + 'foo': 'bar', + 'bar': 'foo', + 'multi': ['FOO','BAR'], + 'blah': 599999 + } + + def _uri(self, type): + uri = self.oauth_uris.get(type) + if uri is None: + raise KeyError("%s is not a valid OAuth URI type." % type) + + return "%s%s" % (self.host, uri) + + def create_simple_multipart_data(self, data): + boundary = '---Boundary-%d' % random.randint(1,1000) + crlf = '\r\n' + items = [] + for key, value in data.iteritems(): + items += [ + '--'+boundary, + 'Content-Disposition: form-data; name="%s"'%str(key), + '', + str(value), + ] + items += ['', '--'+boundary+'--', ''] + content_type = 'multipart/form-data; boundary=%s' % boundary + return content_type, crlf.join(items) + + def test_init(self): + class Blah(): + pass + + try: + client = oauth.Client(Blah()) + self.fail("Client.__init__() accepted invalid Consumer.") + except ValueError: + pass + + consumer = oauth.Consumer('token', 'secret') + try: + client = oauth.Client(consumer, Blah()) + self.fail("Client.__init__() accepted invalid Token.") + except ValueError: + pass + + def test_access_token_get(self): + """Test getting an access token via GET.""" + client = oauth.Client(self.consumer, None) + resp, content = client.request(self._uri('request_token'), "GET") + + self.assertEquals(int(resp['status']), 200) + + def test_access_token_post(self): + """Test getting an access token via POST.""" + client = oauth.Client(self.consumer, None) + resp, content = client.request(self._uri('request_token'), "POST") + + self.assertEquals(int(resp['status']), 200) + + res = dict(parse_qsl(content)) + self.assertTrue('oauth_token' in res) + self.assertTrue('oauth_token_secret' in res) + + def _two_legged(self, method): + client = oauth.Client(self.consumer, None) + + return client.request(self._uri('two_legged'), method, + body=urllib.urlencode(self.body)) + + def test_two_legged_post(self): + """A test of a two-legged OAuth POST request.""" + resp, content = self._two_legged("POST") + + self.assertEquals(int(resp['status']), 200) + + def test_two_legged_get(self): + """A test of a two-legged OAuth GET request.""" + resp, content = self._two_legged("GET") + self.assertEquals(int(resp['status']), 200) + + @mock.patch('httplib2.Http.request') + def test_multipart_post_does_not_alter_body(self, mockHttpRequest): + random_result = random.randint(1,100) + + data = { + 'rand-%d'%random.randint(1,100):random.randint(1,100), + } + content_type, body = self.create_simple_multipart_data(data) + + client = oauth.Client(self.consumer, None) + uri = self._uri('two_legged') + + def mockrequest(cl, ur, **kw): + self.failUnless(cl is client) + self.failUnless(ur is uri) + self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) + self.failUnlessEqual(kw['body'], body) + self.failUnlessEqual(kw['connection_type'], None) + self.failUnlessEqual(kw['method'], 'POST') + self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) + self.failUnless(isinstance(kw['headers'], dict)) + + return random_result + + mockHttpRequest.side_effect = mockrequest + + result = client.request(uri, 'POST', headers={'Content-Type':content_type}, body=body) + self.assertEqual(result, random_result) + + @mock.patch('httplib2.Http.request') + def test_url_with_query_string(self, mockHttpRequest): + uri = 'http://example.com/foo/bar/?show=thundercats&character=snarf' + client = oauth.Client(self.consumer, None) + random_result = random.randint(1,100) + + def mockrequest(cl, ur, **kw): + self.failUnless(cl is client) + self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) + self.failUnlessEqual(kw['body'], '') + self.failUnlessEqual(kw['connection_type'], None) + self.failUnlessEqual(kw['method'], 'GET') + self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) + self.failUnless(isinstance(kw['headers'], dict)) + + req = oauth.Request.from_consumer_and_token(self.consumer, None, + http_method='GET', http_url=uri, parameters={}) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) + expected = parse_qsl(urlparse.urlparse(req.to_url()).query) + actual = parse_qsl(urlparse.urlparse(ur).query) + self.failUnlessEqual(len(expected), len(actual)) + actual = dict(actual) + for key, value in expected: + if key not in ('oauth_signature', 'oauth_nonce', 'oauth_timestamp'): + self.failUnlessEqual(actual[key], value) + + return random_result + + mockHttpRequest.side_effect = mockrequest + + client.request(uri, 'GET') + + @mock.patch('httplib2.Http.request') + @mock.patch('oauth2.Request.from_consumer_and_token') + def test_multiple_values_for_a_key(self, mockReqConstructor, mockHttpRequest): + client = oauth.Client(self.consumer, None) + + request = oauth.Request("GET", "http://example.com/fetch.php", parameters={'multi': ['1', '2']}) + mockReqConstructor.return_value = request + + client.request('http://whatever', 'POST', body='multi=1&multi=2') + + self.failUnlessEqual(mockReqConstructor.call_count, 1) + self.failUnlessEqual(mockReqConstructor.call_args[1]['parameters'], {'multi': ['1', '2']}) + + self.failUnless('multi=1' in mockHttpRequest.call_args[1]['body']) + self.failUnless('multi=2' in mockHttpRequest.call_args[1]['body']) + +if __name__ == "__main__": + unittest.main() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO new file mode 100644 index 00000000..4201c268 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,35 @@ +Metadata-Version: 1.0 +Name: coverage +Version: 3.4 +Summary: Code coverage measurement for Python +Home-page: http://nedbatchelder.com/code/coverage +Author: Ned Batchelder and others +Author-email: ned@nedbatchelder.com +License: BSD +Description: Coverage.py measures code coverage, typically during test execution. It uses + the code analysis tools and tracing hooks provided in the Python standard + library to determine which lines are executable, and which have been executed. + + Coverage.py runs on Pythons 2.3 through 3.2. + + Documentation is at `nedbatchelder.com `_. Code repository and issue + tracker are at `bitbucket.org `_. + + New in 3.2: Branch coverage! + + New in 3.3: .coveragerc files. + + New in 3.4: Better control over source to measure, and unexecuted files + can be reported. + +Keywords: code coverage testing +Platform: UNKNOWN +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Classifier: Development Status :: 5 - Production/Stable diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 00000000..a18ab1d1 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,43 @@ +AUTHORS.txt +CHANGES.txt +MANIFEST.in +README.txt +distribute_setup.py +ez_setup.py +setup.cfg +setup.py +coverage/__init__.py +coverage/__main__.py +coverage/annotate.py +coverage/backward.py +coverage/bytecode.py +coverage/cmdline.py +coverage/codeunit.py +coverage/collector.py +coverage/config.py +coverage/control.py +coverage/data.py +coverage/execfile.py +coverage/files.py +coverage/html.py +coverage/misc.py +coverage/parser.py +coverage/phystokens.py +coverage/report.py +coverage/results.py +coverage/summary.py +coverage/templite.py +coverage/tracer.c +coverage/xmlreport.py +coverage.egg-info/PKG-INFO +coverage.egg-info/SOURCES.txt +coverage.egg-info/dependency_links.txt +coverage.egg-info/entry_points.txt +coverage.egg-info/not-zip-safe +coverage.egg-info/top_level.txt +coverage/htmlfiles/coverage_html.js +coverage/htmlfiles/index.html +coverage/htmlfiles/jquery-1.3.2.min.js +coverage/htmlfiles/jquery.tablesorter.min.js +coverage/htmlfiles/pyfile.html +coverage/htmlfiles/style.css \ No newline at end of file diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt new file mode 100644 index 00000000..d82bb0a8 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +coverage = coverage:main + diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt new file mode 100644 index 00000000..89d7cb13 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt @@ -0,0 +1 @@ +coverage/tracer.so diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt new file mode 100644 index 00000000..4ebc8aea --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +coverage diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py new file mode 100644 index 00000000..65bc003f --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py @@ -0,0 +1,88 @@ +"""Code coverage measurement for Python. + +Ned Batchelder +http://nedbatchelder.com/code/coverage + +""" + +__version__ = "3.4" # see detailed history in CHANGES.txt + +__url__ = "http://nedbatchelder.com/code/coverage" +if 'b' in __version__: + # For beta, use a version-specific URL. + __url__ += "/" + __version__ + +from coverage.control import coverage, process_startup +from coverage.data import CoverageData +from coverage.cmdline import main, CoverageScript +from coverage.misc import CoverageException + + +# Module-level functions. The original API to this module was based on +# functions defined directly in the module, with a singleton of the coverage() +# class. That design hampered programmability, so the current api uses +# explicitly-created coverage objects. But for backward compatibility, here we +# define the top-level functions to create the singleton when they are first +# called. + +# Singleton object for use with module-level functions. The singleton is +# created as needed when one of the module-level functions is called. +_the_coverage = None + +def _singleton_method(name): + """Return a function to the `name` method on a singleton `coverage` object. + + The singleton object is created the first time one of these functions is + called. + + """ + def wrapper(*args, **kwargs): + """Singleton wrapper around a coverage method.""" + global _the_coverage + if not _the_coverage: + _the_coverage = coverage(auto_data=True) + return getattr(_the_coverage, name)(*args, **kwargs) + return wrapper + + +# Define the module-level functions. +use_cache = _singleton_method('use_cache') +start = _singleton_method('start') +stop = _singleton_method('stop') +erase = _singleton_method('erase') +exclude = _singleton_method('exclude') +analysis = _singleton_method('analysis') +analysis2 = _singleton_method('analysis2') +report = _singleton_method('report') +annotate = _singleton_method('annotate') + + +# COPYRIGHT AND LICENSE +# +# Copyright 2001 Gareth Rees. All rights reserved. +# Copyright 2004-2010 Ned Batchelder. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py new file mode 100644 index 00000000..af5fa9f6 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py @@ -0,0 +1,3 @@ +"""Coverage.py's main entrypoint.""" +from coverage.cmdline import main +main() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py new file mode 100644 index 00000000..a556d853 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py @@ -0,0 +1,101 @@ +"""Source file annotation for Coverage.""" + +import os, re + +from coverage.report import Reporter + +class AnnotateReporter(Reporter): + """Generate annotated source files showing line coverage. + + This reporter creates annotated copies of the measured source files. Each + .py file is copied as a .py,cover file, with a left-hand margin annotating + each line:: + + > def h(x): + - if 0: #pragma: no cover + - pass + > if x == 1: + ! a = 1 + > else: + > a = 2 + + > h(2) + + Executed lines use '>', lines not executed use '!', lines excluded from + consideration use '-'. + + """ + + def __init__(self, coverage, ignore_errors=False): + super(AnnotateReporter, self).__init__(coverage, ignore_errors) + self.directory = None + + blank_re = re.compile(r"\s*(#|$)") + else_re = re.compile(r"\s*else\s*:\s*(#|$)") + + def report(self, morfs, config, directory=None): + """Run the report. + + See `coverage.report()` for arguments. + + """ + self.report_files(self.annotate_file, morfs, config, directory) + + def annotate_file(self, cu, analysis): + """Annotate a single file. + + `cu` is the CodeUnit for the file to annotate. + + """ + if not cu.relative: + return + + filename = cu.filename + source = cu.source_file() + if self.directory: + dest_file = os.path.join(self.directory, cu.flat_rootname()) + dest_file += ".py,cover" + else: + dest_file = filename + ",cover" + dest = open(dest_file, 'w') + + statements = analysis.statements + missing = analysis.missing + excluded = analysis.excluded + + lineno = 0 + i = 0 + j = 0 + covered = True + while True: + line = source.readline() + if line == '': + break + lineno += 1 + while i < len(statements) and statements[i] < lineno: + i += 1 + while j < len(missing) and missing[j] < lineno: + j += 1 + if i < len(statements) and statements[i] == lineno: + covered = j >= len(missing) or missing[j] > lineno + if self.blank_re.match(line): + dest.write(' ') + elif self.else_re.match(line): + # Special logic for lines containing only 'else:'. + if i >= len(statements) and j >= len(missing): + dest.write('! ') + elif i >= len(statements) or j >= len(missing): + dest.write('> ') + elif statements[i] == missing[j]: + dest.write('! ') + else: + dest.write('> ') + elif lineno in excluded: + dest.write('- ') + elif covered: + dest.write('> ') + else: + dest.write('! ') + dest.write(line) + source.close() + dest.close() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py new file mode 100644 index 00000000..425bcc6e --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py @@ -0,0 +1,73 @@ +"""Add things to old Pythons so I can pretend they are newer.""" + +# This file does lots of tricky stuff, so disable a bunch of lintisms. +# pylint: disable-msg=F0401,W0611,W0622 +# F0401: Unable to import blah +# W0611: Unused import blah +# W0622: Redefining built-in blah + +import os, sys + +# Python 2.3 doesn't have `set` +try: + set = set # new in 2.4 +except NameError: + from sets import Set as set + +# Python 2.3 doesn't have `sorted`. +try: + sorted = sorted +except NameError: + def sorted(iterable): + """A 2.3-compatible implementation of `sorted`.""" + lst = list(iterable) + lst.sort() + return lst + +# Pythons 2 and 3 differ on where to get StringIO +try: + from cStringIO import StringIO + BytesIO = StringIO +except ImportError: + from io import StringIO, BytesIO + +# What's a string called? +try: + string_class = basestring +except NameError: + string_class = str + +# Where do pickles come from? +try: + import cPickle as pickle +except ImportError: + import pickle + +# range or xrange? +try: + range = xrange +except NameError: + range = range + +# Exec is a statement in Py2, a function in Py3 +if sys.version_info >= (3, 0): + def exec_code_object(code, global_map): + """A wrapper around exec().""" + exec(code, global_map) +else: + # OK, this is pretty gross. In Py2, exec was a statement, but that will + # be a syntax error if we try to put it in a Py3 file, even if it is never + # executed. So hide it inside an evaluated string literal instead. + eval( + compile( + "def exec_code_object(code, global_map):\n" + " exec code in global_map\n", + "", "exec" + ) + ) + +# ConfigParser was renamed to the more-standard configparser +try: + import configparser +except ImportError: + import ConfigParser as configparser diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py new file mode 100644 index 00000000..ab522d6c --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py @@ -0,0 +1,81 @@ +"""Bytecode manipulation for coverage.py""" + +import opcode, sys, types + +class ByteCode(object): + """A single bytecode.""" + def __init__(self): + self.offset = -1 + self.op = -1 + self.arg = -1 + self.next_offset = -1 + self.jump_to = -1 + + +class ByteCodes(object): + """Iterator over byte codes in `code`. + + Returns `ByteCode` objects. + + """ + def __init__(self, code): + self.code = code + self.offset = 0 + + if sys.version_info >= (3, 0): + def __getitem__(self, i): + return self.code[i] + else: + def __getitem__(self, i): + return ord(self.code[i]) + + def __iter__(self): + return self + + def __next__(self): + if self.offset >= len(self.code): + raise StopIteration + + bc = ByteCode() + bc.op = self[self.offset] + bc.offset = self.offset + + next_offset = self.offset+1 + if bc.op >= opcode.HAVE_ARGUMENT: + bc.arg = self[self.offset+1] + 256*self[self.offset+2] + next_offset += 2 + + label = -1 + if bc.op in opcode.hasjrel: + label = next_offset + bc.arg + elif bc.op in opcode.hasjabs: + label = bc.arg + bc.jump_to = label + + bc.next_offset = self.offset = next_offset + return bc + + next = __next__ # Py2k uses an old-style non-dunder name. + + +class CodeObjects(object): + """Iterate over all the code objects in `code`.""" + def __init__(self, code): + self.stack = [code] + + def __iter__(self): + return self + + def __next__(self): + if self.stack: + # We're going to return the code object on the stack, but first + # push its children for later returning. + code = self.stack.pop() + for c in code.co_consts: + if isinstance(c, types.CodeType): + self.stack.append(c) + return code + + raise StopIteration + + next = __next__ diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py new file mode 100644 index 00000000..e5d6bb84 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py @@ -0,0 +1,661 @@ +"""Command-line support for Coverage.""" + +import optparse, re, sys, traceback + +from coverage.backward import sorted # pylint: disable-msg=W0622 +from coverage.execfile import run_python_file +from coverage.misc import CoverageException, ExceptionDuringRun + + +class Opts(object): + """A namespace class for individual options we'll build parsers from.""" + + append = optparse.make_option( + '-a', '--append', action='store_false', dest="erase_first", + help="Append coverage data to .coverage, otherwise it is started " + "clean with each run." + ) + branch = optparse.make_option( + '', '--branch', action='store_true', + help="Measure branch coverage in addition to statement coverage." + ) + directory = optparse.make_option( + '-d', '--directory', action='store', + metavar="DIR", + help="Write the output files to DIR." + ) + help = optparse.make_option( + '-h', '--help', action='store_true', + help="Get help on this command." + ) + ignore_errors = optparse.make_option( + '-i', '--ignore-errors', action='store_true', + help="Ignore errors while reading source files." + ) + include = optparse.make_option( + '', '--include', action='store', + metavar="PAT1,PAT2,...", + help="Include files only when their filename path matches one of " + "these patterns. Usually needs quoting on the command line." + ) + pylib = optparse.make_option( + '-L', '--pylib', action='store_true', + help="Measure coverage even inside the Python installed library, " + "which isn't done by default." + ) + show_missing = optparse.make_option( + '-m', '--show-missing', action='store_true', + help="Show line numbers of statements in each module that weren't " + "executed." + ) + old_omit = optparse.make_option( + '-o', '--omit', action='store', + metavar="PAT1,PAT2,...", + help="Omit files when their filename matches one of these patterns. " + "Usually needs quoting on the command line." + ) + omit = optparse.make_option( + '', '--omit', action='store', + metavar="PAT1,PAT2,...", + help="Omit files when their filename matches one of these patterns. " + "Usually needs quoting on the command line." + ) + output_xml = optparse.make_option( + '-o', '', action='store', dest="outfile", + metavar="OUTFILE", + help="Write the XML report to this file. Defaults to 'coverage.xml'" + ) + parallel_mode = optparse.make_option( + '-p', '--parallel-mode', action='store_true', + help="Append the machine name, process id and random number to the " + ".coverage data file name to simplify collecting data from " + "many processes." + ) + rcfile = optparse.make_option( + '', '--rcfile', action='store', + help="Specify configuration file. Defaults to '.coveragerc'" + ) + source = optparse.make_option( + '', '--source', action='store', metavar="SRC1,SRC2,...", + help="A list of packages or directories of code to be measured." + ) + timid = optparse.make_option( + '', '--timid', action='store_true', + help="Use a simpler but slower trace method. Try this if you get " + "seemingly impossible results!" + ) + version = optparse.make_option( + '', '--version', action='store_true', + help="Display version information and exit." + ) + + +class CoverageOptionParser(optparse.OptionParser, object): + """Base OptionParser for coverage. + + Problems don't exit the program. + Defaults are initialized for all options. + + """ + + def __init__(self, *args, **kwargs): + super(CoverageOptionParser, self).__init__( + add_help_option=False, *args, **kwargs + ) + self.set_defaults( + actions=[], + branch=None, + directory=None, + help=None, + ignore_errors=None, + include=None, + omit=None, + parallel_mode=None, + pylib=None, + rcfile=True, + show_missing=None, + source=None, + timid=None, + erase_first=None, + version=None, + ) + + self.disable_interspersed_args() + self.help_fn = self.help_noop + + def help_noop(self, error=None, topic=None, parser=None): + """No-op help function.""" + pass + + class OptionParserError(Exception): + """Used to stop the optparse error handler ending the process.""" + pass + + def parse_args(self, args=None, options=None): + """Call optparse.parse_args, but return a triple: + + (ok, options, args) + + """ + try: + options, args = \ + super(CoverageOptionParser, self).parse_args(args, options) + except self.OptionParserError: + return False, None, None + return True, options, args + + def error(self, msg): + """Override optparse.error so sys.exit doesn't get called.""" + self.help_fn(msg) + raise self.OptionParserError + + +class ClassicOptionParser(CoverageOptionParser): + """Command-line parser for coverage.py classic arguments.""" + + def __init__(self): + super(ClassicOptionParser, self).__init__() + + self.add_action('-a', '--annotate', 'annotate') + self.add_action('-b', '--html', 'html') + self.add_action('-c', '--combine', 'combine') + self.add_action('-e', '--erase', 'erase') + self.add_action('-r', '--report', 'report') + self.add_action('-x', '--execute', 'execute') + + self.add_options([ + Opts.directory, + Opts.help, + Opts.ignore_errors, + Opts.pylib, + Opts.show_missing, + Opts.old_omit, + Opts.parallel_mode, + Opts.timid, + Opts.version, + ]) + + def add_action(self, dash, dashdash, action_code): + """Add a specialized option that is the action to execute.""" + option = self.add_option(dash, dashdash, action='callback', + callback=self._append_action + ) + option.action_code = action_code + + def _append_action(self, option, opt_unused, value_unused, parser): + """Callback for an option that adds to the `actions` list.""" + parser.values.actions.append(option.action_code) + + +class CmdOptionParser(CoverageOptionParser): + """Parse one of the new-style commands for coverage.py.""" + + def __init__(self, action, options=None, defaults=None, usage=None, + cmd=None, description=None + ): + """Create an OptionParser for a coverage command. + + `action` is the slug to put into `options.actions`. + `options` is a list of Option's for the command. + `defaults` is a dict of default value for options. + `usage` is the usage string to display in help. + `cmd` is the command name, if different than `action`. + `description` is the description of the command, for the help text. + + """ + if usage: + usage = "%prog " + usage + super(CmdOptionParser, self).__init__( + prog="coverage %s" % (cmd or action), + usage=usage, + description=description, + ) + self.set_defaults(actions=[action], **(defaults or {})) + if options: + self.add_options(options) + self.cmd = cmd or action + + def __eq__(self, other): + # A convenience equality, so that I can put strings in unit test + # results, and they will compare equal to objects. + return (other == "" % self.cmd) + +GLOBAL_ARGS = [ + Opts.rcfile, + Opts.help, + ] + +CMDS = { + 'annotate': CmdOptionParser("annotate", + [ + Opts.directory, + Opts.ignore_errors, + Opts.omit, + Opts.include, + ] + GLOBAL_ARGS, + usage = "[options] [modules]", + description = "Make annotated copies of the given files, marking " + "statements that are executed with > and statements that are " + "missed with !." + ), + + 'combine': CmdOptionParser("combine", GLOBAL_ARGS, + usage = " ", + description = "Combine data from multiple coverage files collected " + "with 'run -p'. The combined results are written to a single " + "file representing the union of the data." + ), + + 'debug': CmdOptionParser("debug", GLOBAL_ARGS, + usage = "", + description = "Display information on the internals of coverage.py, " + "for diagnosing problems. " + "Topics are 'data' to show a summary of the collected data, " + "or 'sys' to show installation information." + ), + + 'erase': CmdOptionParser("erase", GLOBAL_ARGS, + usage = " ", + description = "Erase previously collected coverage data." + ), + + 'help': CmdOptionParser("help", GLOBAL_ARGS, + usage = "[command]", + description = "Describe how to use coverage.py" + ), + + 'html': CmdOptionParser("html", + [ + Opts.directory, + Opts.ignore_errors, + Opts.omit, + Opts.include, + ] + GLOBAL_ARGS, + usage = "[options] [modules]", + description = "Create an HTML report of the coverage of the files. " + "Each file gets its own page, with the source decorated to show " + "executed, excluded, and missed lines." + ), + + 'report': CmdOptionParser("report", + [ + Opts.ignore_errors, + Opts.omit, + Opts.include, + Opts.show_missing, + ] + GLOBAL_ARGS, + usage = "[options] [modules]", + description = "Report coverage statistics on modules." + ), + + 'run': CmdOptionParser("execute", + [ + Opts.append, + Opts.branch, + Opts.pylib, + Opts.parallel_mode, + Opts.timid, + Opts.source, + Opts.omit, + Opts.include, + ] + GLOBAL_ARGS, + defaults = {'erase_first': True}, + cmd = "run", + usage = "[options] [program options]", + description = "Run a Python program, measuring code execution." + ), + + 'xml': CmdOptionParser("xml", + [ + Opts.ignore_errors, + Opts.omit, + Opts.include, + Opts.output_xml, + ] + GLOBAL_ARGS, + cmd = "xml", + defaults = {'outfile': 'coverage.xml'}, + usage = "[options] [modules]", + description = "Generate an XML report of coverage results." + ), + } + + +OK, ERR = 0, 1 + + +class CoverageScript(object): + """The command-line interface to Coverage.""" + + def __init__(self, _covpkg=None, _run_python_file=None, _help_fn=None): + # _covpkg is for dependency injection, so we can test this code. + if _covpkg: + self.covpkg = _covpkg + else: + import coverage + self.covpkg = coverage + + # _run_python_file is for dependency injection also. + self.run_python_file = _run_python_file or run_python_file + + # _help_fn is for dependency injection. + self.help_fn = _help_fn or self.help + + self.coverage = None + + def help(self, error=None, topic=None, parser=None): + """Display an error message, or the named topic.""" + assert error or topic or parser + if error: + print(error) + print("Use 'coverage help' for help.") + elif parser: + print(parser.format_help().strip()) + else: + # Parse out the topic we want from HELP_TOPICS + topic_list = re.split("(?m)^=+ (\w+) =+$", HELP_TOPICS) + topics = dict(zip(topic_list[1::2], topic_list[2::2])) + help_msg = topics.get(topic, '').strip() + if help_msg: + print(help_msg % self.covpkg.__dict__) + else: + print("Don't know topic %r" % topic) + + def command_line(self, argv): + """The bulk of the command line interface to Coverage. + + `argv` is the argument list to process. + + Returns 0 if all is well, 1 if something went wrong. + + """ + # Collect the command-line options. + + if not argv: + self.help_fn(topic='minimum_help') + return OK + + # The command syntax we parse depends on the first argument. Classic + # syntax always starts with an option. + classic = argv[0].startswith('-') + if classic: + parser = ClassicOptionParser() + else: + parser = CMDS.get(argv[0]) + if not parser: + self.help_fn("Unknown command: '%s'" % argv[0]) + return ERR + argv = argv[1:] + + parser.help_fn = self.help_fn + ok, options, args = parser.parse_args(argv) + if not ok: + return ERR + + # Handle help. + if options.help: + if classic: + self.help_fn(topic='help') + else: + self.help_fn(parser=parser) + return OK + + if "help" in options.actions: + if args: + for a in args: + parser = CMDS.get(a) + if parser: + self.help_fn(parser=parser) + else: + self.help_fn(topic=a) + else: + self.help_fn(topic='help') + return OK + + # Handle version. + if options.version: + self.help_fn(topic='version') + return OK + + # Check for conflicts and problems in the options. + for i in ['erase', 'execute']: + for j in ['annotate', 'html', 'report', 'combine']: + if (i in options.actions) and (j in options.actions): + self.help_fn("You can't specify the '%s' and '%s' " + "options at the same time." % (i, j)) + return ERR + + if not options.actions: + self.help_fn( + "You must specify at least one of -e, -x, -c, -r, -a, or -b." + ) + return ERR + args_allowed = ( + 'execute' in options.actions or + 'annotate' in options.actions or + 'html' in options.actions or + 'debug' in options.actions or + 'report' in options.actions or + 'xml' in options.actions + ) + if not args_allowed and args: + self.help_fn("Unexpected arguments: %s" % " ".join(args)) + return ERR + + if 'execute' in options.actions and not args: + self.help_fn("Nothing to do.") + return ERR + + # Listify the list options. + source = unshell_list(options.source) + omit = unshell_list(options.omit) + include = unshell_list(options.include) + + # Do something. + self.coverage = self.covpkg.coverage( + data_suffix = options.parallel_mode, + cover_pylib = options.pylib, + timid = options.timid, + branch = options.branch, + config_file = options.rcfile, + source = source, + omit = omit, + include = include, + ) + + if 'debug' in options.actions: + if not args: + self.help_fn("What information would you like: data, sys?") + return ERR + for info in args: + if info == 'sys': + print("-- sys ----------------------------------------") + for label, info in self.coverage.sysinfo(): + if info == []: + info = "-none-" + if isinstance(info, list): + print("%15s:" % label) + for e in info: + print("%15s %s" % ("", e)) + else: + print("%15s: %s" % (label, info)) + elif info == 'data': + print("-- data ---------------------------------------") + self.coverage.load() + print("path: %s" % self.coverage.data.filename) + print("has_arcs: %r" % self.coverage.data.has_arcs()) + summary = self.coverage.data.summary(fullpath=True) + if summary: + filenames = sorted(summary.keys()) + print("\n%d files:" % len(filenames)) + for f in filenames: + print("%s: %d lines" % (f, summary[f])) + else: + print("No data collected") + else: + self.help_fn("Don't know what you mean by %r" % info) + return ERR + return OK + + if 'erase' in options.actions or options.erase_first: + self.coverage.erase() + else: + self.coverage.load() + + if 'execute' in options.actions: + # Run the script. + self.coverage.start() + try: + self.run_python_file(args[0], args) + finally: + self.coverage.stop() + self.coverage.save() + + if 'combine' in options.actions: + self.coverage.combine() + self.coverage.save() + + # Remaining actions are reporting, with some common options. + report_args = dict( + morfs = args, + ignore_errors = options.ignore_errors, + omit = omit, + include = include, + ) + + if 'report' in options.actions: + self.coverage.report( + show_missing=options.show_missing, **report_args) + if 'annotate' in options.actions: + self.coverage.annotate( + directory=options.directory, **report_args) + if 'html' in options.actions: + self.coverage.html_report( + directory=options.directory, **report_args) + if 'xml' in options.actions: + outfile = options.outfile + self.coverage.xml_report(outfile=outfile, **report_args) + + return OK + + +def unshell_list(s): + """Turn a command-line argument into a list.""" + if not s: + return None + if sys.platform == 'win32': + # When running coverage as coverage.exe, some of the behavior + # of the shell is emulated: wildcards are expanded into a list of + # filenames. So you have to single-quote patterns on the command + # line, but (not) helpfully, the single quotes are included in the + # argument, so we have to strip them off here. + s = s.strip("'") + return s.split(',') + + +HELP_TOPICS = r""" + +== classic ==================================================================== +Coverage.py version %(__version__)s +Measure, collect, and report on code coverage in Python programs. + +Usage: + +coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] + Execute the module, passing the given command-line arguments, collecting + coverage data. With the -p option, include the machine name and process + id in the .coverage file name. With -L, measure coverage even inside the + Python installed library, which isn't done by default. With --timid, use a + simpler but slower trace method. + +coverage -e + Erase collected coverage data. + +coverage -c + Combine data from multiple coverage files (as created by -p option above) + and store it into a single file representing the union of the coverage. + +coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...] + Report on the statement coverage for the given files. With the -m + option, show line numbers of the statements that weren't executed. + +coverage -b -d DIR [-i] [-o DIR,...] [FILE1 FILE2 ...] + Create an HTML report of the coverage of the given files. Each file gets + its own page, with the file listing decorated to show executed, excluded, + and missed lines. + +coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...] + Make annotated copies of the given files, marking statements that + are executed with > and statements that are missed with !. + +-d DIR + Write output files for -b or -a to this directory. + +-i Ignore errors while reporting or annotating. + +-o DIR,... + Omit reporting or annotating files when their filename path starts with + a directory listed in the omit list. + e.g. coverage -i -r -o c:\python25,lib\enthought\traits + +Coverage data is saved in the file .coverage by default. Set the +COVERAGE_FILE environment variable to save it somewhere else. + +== help ======================================================================= +Coverage.py, version %(__version__)s +Measure, collect, and report on code coverage in Python programs. + +usage: coverage [options] [args] + +Commands: + annotate Annotate source files with execution information. + combine Combine a number of data files. + erase Erase previously collected coverage data. + help Get help on using coverage.py. + html Create an HTML report. + report Report coverage stats on modules. + run Run a Python program and measure code execution. + xml Create an XML report of coverage results. + +Use "coverage help " for detailed help on any command. +Use "coverage help classic" for help on older command syntax. +For more information, see %(__url__)s + +== minimum_help =============================================================== +Code coverage for Python. Use 'coverage help' for help. + +== version ==================================================================== +Coverage.py, version %(__version__)s. %(__url__)s + +""" + + +def main(argv=None): + """The main entrypoint to Coverage. + + This is installed as the script entrypoint. + + """ + if argv is None: + argv = sys.argv[1:] + try: + status = CoverageScript().command_line(argv) + except ExceptionDuringRun: + # An exception was caught while running the product code. The + # sys.exc_info() return tuple is packed into an ExceptionDuringRun + # exception. + _, err, _ = sys.exc_info() + traceback.print_exception(*err.args) + status = ERR + except CoverageException: + # A controlled error inside coverage.py: print the message to the user. + _, err, _ = sys.exc_info() + print(err) + status = ERR + except SystemExit: + # The user called `sys.exit()`. Exit with their argument, if any. + _, err, _ = sys.exc_info() + if err.args: + status = err.args[0] + else: + status = None + return status diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py new file mode 100644 index 00000000..dfc4560d --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py @@ -0,0 +1,117 @@ +"""Code unit (module) handling for Coverage.""" + +import glob, os + +from coverage.backward import string_class, StringIO +from coverage.misc import CoverageException + + +def code_unit_factory(morfs, file_locator): + """Construct a list of CodeUnits from polymorphic inputs. + + `morfs` is a module or a filename, or a list of same. + + `file_locator` is a FileLocator that can help resolve filenames. + + Returns a list of CodeUnit objects. + + """ + # Be sure we have a list. + if not isinstance(morfs, (list, tuple)): + morfs = [morfs] + + # On Windows, the shell doesn't expand wildcards. Do it here. + globbed = [] + for morf in morfs: + if isinstance(morf, string_class) and ('?' in morf or '*' in morf): + globbed.extend(glob.glob(morf)) + else: + globbed.append(morf) + morfs = globbed + + code_units = [CodeUnit(morf, file_locator) for morf in morfs] + + return code_units + + +class CodeUnit(object): + """Code unit: a filename or module. + + Instance attributes: + + `name` is a human-readable name for this code unit. + `filename` is the os path from which we can read the source. + `relative` is a boolean. + + """ + def __init__(self, morf, file_locator): + self.file_locator = file_locator + + if hasattr(morf, '__file__'): + f = morf.__file__ + else: + f = morf + # .pyc files should always refer to a .py instead. + if f.endswith('.pyc'): + f = f[:-1] + self.filename = self.file_locator.canonical_filename(f) + + if hasattr(morf, '__name__'): + n = modname = morf.__name__ + self.relative = True + else: + n = os.path.splitext(morf)[0] + rel = self.file_locator.relative_filename(n) + if os.path.isabs(n): + self.relative = (rel != n) + else: + self.relative = True + n = rel + modname = None + self.name = n + self.modname = modname + + def __repr__(self): + return "" % (self.name, self.filename) + + # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all + # of them defined. + + def __lt__(self, other): return self.name < other.name + def __le__(self, other): return self.name <= other.name + def __eq__(self, other): return self.name == other.name + def __ne__(self, other): return self.name != other.name + def __gt__(self, other): return self.name > other.name + def __ge__(self, other): return self.name >= other.name + + def flat_rootname(self): + """A base for a flat filename to correspond to this code unit. + + Useful for writing files about the code where you want all the files in + the same directory, but need to differentiate same-named files from + different directories. + + For example, the file a/b/c.py might return 'a_b_c' + + """ + if self.modname: + return self.modname.replace('.', '_') + else: + root = os.path.splitdrive(self.name)[1] + return root.replace('\\', '_').replace('/', '_').replace('.', '_') + + def source_file(self): + """Return an open file for reading the source of the code unit.""" + if os.path.exists(self.filename): + # A regular text file: open it. + return open(self.filename) + + # Maybe it's in a zip file? + source = self.file_locator.get_zip_data(self.filename) + if source is not None: + return StringIO(source) + + # Couldn't find source. + raise CoverageException( + "No source for code %r." % self.filename + ) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py new file mode 100644 index 00000000..decdba9f --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py @@ -0,0 +1,292 @@ +"""Raw data collector for Coverage.""" + +import sys, threading + +try: + # Use the C extension code when we can, for speed. + from coverage.tracer import Tracer +except ImportError: + # Couldn't import the C extension, maybe it isn't built. + Tracer = None + + +class PyTracer(object): + """Python implementation of the raw data tracer.""" + + # Because of poor implementations of trace-function-manipulating tools, + # the Python trace function must be kept very simple. In particular, there + # must be only one function ever set as the trace function, both through + # sys.settrace, and as the return value from the trace function. Put + # another way, the trace function must always return itself. It cannot + # swap in other functions, or return None to avoid tracing a particular + # frame. + # + # The trace manipulator that introduced this restriction is DecoratorTools, + # which sets a trace function, and then later restores the pre-existing one + # by calling sys.settrace with a function it found in the current frame. + # + # Systems that use DecoratorTools (or similar trace manipulations) must use + # PyTracer to get accurate results. The command-line --timid argument is + # used to force the use of this tracer. + + def __init__(self): + self.data = None + self.should_trace = None + self.should_trace_cache = None + self.cur_file_data = None + self.last_line = 0 + self.data_stack = [] + self.last_exc_back = None + self.last_exc_firstlineno = 0 + self.arcs = False + + def _trace(self, frame, event, arg_unused): + """The trace function passed to sys.settrace.""" + + #print "trace event: %s %r @%d" % ( + # event, frame.f_code.co_filename, frame.f_lineno) + + if self.last_exc_back: + if frame == self.last_exc_back: + # Someone forgot a return event. + if self.arcs and self.cur_file_data: + pair = (self.last_line, -self.last_exc_firstlineno) + self.cur_file_data[pair] = None + self.cur_file_data, self.last_line = self.data_stack.pop() + self.last_exc_back = None + + if event == 'call': + # Entering a new function context. Decide if we should trace + # in this file. + self.data_stack.append((self.cur_file_data, self.last_line)) + filename = frame.f_code.co_filename + tracename = self.should_trace_cache.get(filename) + if tracename is None: + tracename = self.should_trace(filename, frame) + self.should_trace_cache[filename] = tracename + if tracename: + if tracename not in self.data: + self.data[tracename] = {} + self.cur_file_data = self.data[tracename] + else: + self.cur_file_data = None + # Set the last_line to -1 because the next arc will be entering a + # code block, indicated by (-1, n). + self.last_line = -1 + elif event == 'line': + # Record an executed line. + if self.cur_file_data is not None: + if self.arcs: + #print "lin", self.last_line, frame.f_lineno + self.cur_file_data[(self.last_line, frame.f_lineno)] = None + else: + #print "lin", frame.f_lineno + self.cur_file_data[frame.f_lineno] = None + self.last_line = frame.f_lineno + elif event == 'return': + if self.arcs and self.cur_file_data: + first = frame.f_code.co_firstlineno + self.cur_file_data[(self.last_line, -first)] = None + # Leaving this function, pop the filename stack. + self.cur_file_data, self.last_line = self.data_stack.pop() + elif event == 'exception': + #print "exc", self.last_line, frame.f_lineno + self.last_exc_back = frame.f_back + self.last_exc_firstlineno = frame.f_code.co_firstlineno + return self._trace + + def start(self): + """Start this Tracer. + + Return a Python function suitable for use with sys.settrace(). + + """ + sys.settrace(self._trace) + return self._trace + + def stop(self): + """Stop this Tracer.""" + sys.settrace(None) + + def get_stats(self): + """Return a dictionary of statistics, or None.""" + return None + + +class Collector(object): + """Collects trace data. + + Creates a Tracer object for each thread, since they track stack + information. Each Tracer points to the same shared data, contributing + traced data points. + + When the Collector is started, it creates a Tracer for the current thread, + and installs a function to create Tracers for each new thread started. + When the Collector is stopped, all active Tracers are stopped. + + Threads started while the Collector is stopped will never have Tracers + associated with them. + + """ + + # The stack of active Collectors. Collectors are added here when started, + # and popped when stopped. Collectors on the stack are paused when not + # the top, and resumed when they become the top again. + _collectors = [] + + def __init__(self, should_trace, timid, branch): + """Create a collector. + + `should_trace` is a function, taking a filename, and returning a + canonicalized filename, or False depending on whether the file should + be traced or not. + + If `timid` is true, then a slower simpler trace function will be + used. This is important for some environments where manipulation of + tracing functions make the faster more sophisticated trace function not + operate properly. + + If `branch` is true, then branches will be measured. This involves + collecting data on which statements followed each other (arcs). Use + `get_arc_data` to get the arc data. + + """ + self.should_trace = should_trace + self.branch = branch + self.reset() + + if timid: + # Being timid: use the simple Python trace function. + self._trace_class = PyTracer + else: + # Being fast: use the C Tracer if it is available, else the Python + # trace function. + self._trace_class = Tracer or PyTracer + + def __repr__(self): + return "" % id(self) + + def tracer_name(self): + """Return the class name of the tracer we're using.""" + return self._trace_class.__name__ + + def reset(self): + """Clear collected data, and prepare to collect more.""" + # A dictionary mapping filenames to dicts with linenumber keys, + # or mapping filenames to dicts with linenumber pairs as keys. + self.data = {} + + # A cache of the results from should_trace, the decision about whether + # to trace execution in a file. A dict of filename to (filename or + # False). + self.should_trace_cache = {} + + # Our active Tracers. + self.tracers = [] + + def _start_tracer(self): + """Start a new Tracer object, and store it in self.tracers.""" + tracer = self._trace_class() + tracer.data = self.data + tracer.arcs = self.branch + tracer.should_trace = self.should_trace + tracer.should_trace_cache = self.should_trace_cache + fn = tracer.start() + self.tracers.append(tracer) + return fn + + # The trace function has to be set individually on each thread before + # execution begins. Ironically, the only support the threading module has + # for running code before the thread main is the tracing function. So we + # install this as a trace function, and the first time it's called, it does + # the real trace installation. + + def _installation_trace(self, frame_unused, event_unused, arg_unused): + """Called on new threads, installs the real tracer.""" + # Remove ourselves as the trace function + sys.settrace(None) + # Install the real tracer. + fn = self._start_tracer() + # Invoke the real trace function with the current event, to be sure + # not to lose an event. + if fn: + fn = fn(frame_unused, event_unused, arg_unused) + # Return the new trace function to continue tracing in this scope. + return fn + + def start(self): + """Start collecting trace information.""" + if self._collectors: + self._collectors[-1].pause() + self._collectors.append(self) + #print >>sys.stderr, "Started: %r" % self._collectors + # Install the tracer on this thread. + self._start_tracer() + # Install our installation tracer in threading, to jump start other + # threads. + threading.settrace(self._installation_trace) + + def stop(self): + """Stop collecting trace information.""" + #print >>sys.stderr, "Stopping: %r" % self._collectors + assert self._collectors + assert self._collectors[-1] is self + + self.pause() + self.tracers = [] + + # Remove this Collector from the stack, and resume the one underneath + # (if any). + self._collectors.pop() + if self._collectors: + self._collectors[-1].resume() + + def pause(self): + """Pause tracing, but be prepared to `resume`.""" + for tracer in self.tracers: + tracer.stop() + stats = tracer.get_stats() + if stats: + print("\nCoverage.py tracer stats:") + for k in sorted(stats.keys()): + print("%16s: %s" % (k, stats[k])) + threading.settrace(None) + + def resume(self): + """Resume tracing after a `pause`.""" + for tracer in self.tracers: + tracer.start() + threading.settrace(self._installation_trace) + + def get_line_data(self): + """Return the line data collected. + + Data is { filename: { lineno: None, ...}, ...} + + """ + if self.branch: + # If we were measuring branches, then we have to re-build the dict + # to show line data. + line_data = {} + for f, arcs in self.data.items(): + line_data[f] = ldf = {} + for l1, _ in list(arcs.keys()): + if l1: + ldf[l1] = None + return line_data + else: + return self.data + + def get_arc_data(self): + """Return the arc data collected. + + Data is { filename: { (l1, l2): None, ...}, ...} + + Note that no data is collected or returned if the Collector wasn't + created with `branch` true. + + """ + if self.branch: + return self.data + else: + return {} diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py new file mode 100644 index 00000000..1f6a879f --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py @@ -0,0 +1,118 @@ +"""Config file for coverage.py""" + +import os +from coverage.backward import configparser # pylint: disable-msg=W0622 + + +class CoverageConfig(object): + """Coverage.py configuration. + + The attributes of this class are the various settings that control the + operation of coverage.py. + + """ + + def __init__(self): + """Initialize the configuration attributes to their defaults.""" + # Defaults for [run] + self.branch = False + self.cover_pylib = False + self.data_file = ".coverage" + self.parallel = False + self.timid = False + self.source = None + + # Defaults for [report] + self.exclude_list = ['(?i)# *pragma[: ]*no *cover'] + self.ignore_errors = False + self.omit = None + self.include = None + self.precision = 0 + + # Defaults for [html] + self.html_dir = "htmlcov" + + # Defaults for [xml] + self.xml_output = "coverage.xml" + + def from_environment(self, env_var): + """Read configuration from the `env_var` environment variable.""" + # Timidity: for nose users, read an environment variable. This is a + # cheap hack, since the rest of the command line arguments aren't + # recognized, but it solves some users' problems. + env = os.environ.get(env_var, '') + if env: + self.timid = ('--timid' in env) + + def from_args(self, **kwargs): + """Read config values from `kwargs`.""" + for k, v in kwargs.items(): + if v is not None: + setattr(self, k, v) + + def from_file(self, *files): + """Read configuration from .rc files. + + Each argument in `files` is a file name to read. + + """ + cp = configparser.RawConfigParser() + cp.read(files) + + # [run] + if cp.has_option('run', 'branch'): + self.branch = cp.getboolean('run', 'branch') + if cp.has_option('run', 'cover_pylib'): + self.cover_pylib = cp.getboolean('run', 'cover_pylib') + if cp.has_option('run', 'data_file'): + self.data_file = cp.get('run', 'data_file') + if cp.has_option('run', 'parallel'): + self.parallel = cp.getboolean('run', 'parallel') + if cp.has_option('run', 'timid'): + self.timid = cp.getboolean('run', 'timid') + if cp.has_option('run', 'source'): + self.source = self.get_list(cp, 'run', 'source') + if cp.has_option('run', 'omit'): + self.omit = self.get_list(cp, 'run', 'omit') + if cp.has_option('run', 'include'): + self.include = self.get_list(cp, 'run', 'include') + + # [report] + if cp.has_option('report', 'exclude_lines'): + # exclude_lines is a list of lines, leave out the blank ones. + exclude_list = cp.get('report', 'exclude_lines') + self.exclude_list = list(filter(None, exclude_list.split('\n'))) + if cp.has_option('report', 'ignore_errors'): + self.ignore_errors = cp.getboolean('report', 'ignore_errors') + if cp.has_option('report', 'omit'): + self.omit = self.get_list(cp, 'report', 'omit') + if cp.has_option('report', 'include'): + self.include = self.get_list(cp, 'report', 'include') + if cp.has_option('report', 'precision'): + self.precision = cp.getint('report', 'precision') + + # [html] + if cp.has_option('html', 'directory'): + self.html_dir = cp.get('html', 'directory') + + # [xml] + if cp.has_option('xml', 'output'): + self.xml_output = cp.get('xml', 'output') + + def get_list(self, cp, section, option): + """Read a list of strings from the ConfigParser `cp`. + + The value of `section` and `option` is treated as a comma- and newline- + separated list of strings. Each value is stripped of whitespace. + + Returns the list of strings. + + """ + value_list = cp.get(section, option) + values = [] + for value_line in value_list.split('\n'): + for value in value_line.split(','): + value = value.strip() + if value: + values.append(value) + return values diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py new file mode 100644 index 00000000..28c8850f --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py @@ -0,0 +1,624 @@ +"""Core control stuff for Coverage.""" + +import atexit, os, random, socket, sys + +from coverage.annotate import AnnotateReporter +from coverage.backward import string_class +from coverage.codeunit import code_unit_factory, CodeUnit +from coverage.collector import Collector +from coverage.config import CoverageConfig +from coverage.data import CoverageData +from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher +from coverage.files import find_python_files +from coverage.html import HtmlReporter +from coverage.misc import CoverageException, bool_or_none +from coverage.results import Analysis, Numbers +from coverage.summary import SummaryReporter +from coverage.xmlreport import XmlReporter + +class coverage(object): + """Programmatic access to Coverage. + + To use:: + + from coverage import coverage + + cov = coverage() + cov.start() + #.. blah blah (run your code) blah blah .. + cov.stop() + cov.html_report(directory='covhtml') + + """ + def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, + auto_data=False, timid=None, branch=None, config_file=True, + source=None, omit=None, include=None): + """ + `data_file` is the base name of the data file to use, defaulting to + ".coverage". `data_suffix` is appended (with a dot) to `data_file` to + create the final file name. If `data_suffix` is simply True, then a + suffix is created with the machine and process identity included. + + `cover_pylib` is a boolean determining whether Python code installed + with the Python interpreter is measured. This includes the Python + standard library and any packages installed with the interpreter. + + If `auto_data` is true, then any existing data file will be read when + coverage measurement starts, and data will be saved automatically when + measurement stops. + + If `timid` is true, then a slower and simpler trace function will be + used. This is important for some environments where manipulation of + tracing functions breaks the faster trace function. + + If `branch` is true, then branch coverage will be measured in addition + to the usual statement coverage. + + `config_file` determines what config file to read. If it is a string, + it is the name of the config file to read. If it is True, then a + standard file is read (".coveragerc"). If it is False, then no file is + read. + + `source` is a list of file paths or package names. Only code located + in the trees indicated by the file paths or package names will be + measured. + + `include` and `omit` are lists of filename patterns. Files that match + `include` will be measured, files that match `omit` will not. + + """ + from coverage import __version__ + + # Build our configuration from a number of sources: + # 1: defaults: + self.config = CoverageConfig() + + # 2: from the coveragerc file: + if config_file: + if config_file is True: + config_file = ".coveragerc" + try: + self.config.from_file(config_file) + except ValueError: + _, err, _ = sys.exc_info() + raise CoverageException( + "Couldn't read config file %s: %s" % (config_file, err) + ) + + # 3: from environment variables: + self.config.from_environment('COVERAGE_OPTIONS') + env_data_file = os.environ.get('COVERAGE_FILE') + if env_data_file: + self.config.data_file = env_data_file + + # 4: from constructor arguments: + self.config.from_args( + data_file=data_file, cover_pylib=cover_pylib, timid=timid, + branch=branch, parallel=bool_or_none(data_suffix), + source=source, omit=omit, include=include + ) + + self.auto_data = auto_data + self.atexit_registered = False + + self.exclude_re = "" + self._compile_exclude() + + self.file_locator = FileLocator() + + # The source argument can be directories or package names. + self.source = [] + self.source_pkgs = [] + for src in self.config.source or []: + if os.path.exists(src): + self.source.append(self.file_locator.canonical_filename(src)) + else: + self.source_pkgs.append(src) + + self.omit = self._abs_files(self.config.omit) + self.include = self._abs_files(self.config.include) + + self.collector = Collector( + self._should_trace, timid=self.config.timid, + branch=self.config.branch + ) + + # Suffixes are a bit tricky. We want to use the data suffix only when + # collecting data, not when combining data. So we save it as + # `self.run_suffix` now, and promote it to `self.data_suffix` if we + # find that we are collecting data later. + if data_suffix or self.config.parallel: + if not isinstance(data_suffix, string_class): + # if data_suffix=True, use .machinename.pid.random + data_suffix = True + else: + data_suffix = None + self.data_suffix = None + self.run_suffix = data_suffix + + # Create the data file. We do this at construction time so that the + # data file will be written into the directory where the process + # started rather than wherever the process eventually chdir'd to. + self.data = CoverageData( + basename=self.config.data_file, + collector="coverage v%s" % __version__ + ) + + # The dirs for files considered "installed with the interpreter". + self.pylib_dirs = [] + if not self.config.cover_pylib: + # Look at where some standard modules are located. That's the + # indication for "installed with the interpreter". In some + # environments (virtualenv, for example), these modules may be + # spread across a few locations. Look at all the candidate modules + # we've imported, and take all the different ones. + for m in (atexit, os, random, socket): + if hasattr(m, "__file__"): + m_dir = self._canonical_dir(m.__file__) + if m_dir not in self.pylib_dirs: + self.pylib_dirs.append(m_dir) + + # To avoid tracing the coverage code itself, we skip anything located + # where we are. + self.cover_dir = self._canonical_dir(__file__) + + # The matchers for _should_trace, created when tracing starts. + self.source_match = None + self.pylib_match = self.cover_match = None + self.include_match = self.omit_match = None + + # Only _harvest_data once per measurement cycle. + self._harvested = False + + # Set the reporting precision. + Numbers.set_precision(self.config.precision) + + # When tearing down the coverage object, modules can become None. + # Saving the modules as object attributes avoids problems, but it is + # quite ad-hoc which modules need to be saved and which references + # need to use the object attributes. + self.socket = socket + self.os = os + self.random = random + + def _canonical_dir(self, f): + """Return the canonical directory of the file `f`.""" + return os.path.split(self.file_locator.canonical_filename(f))[0] + + def _source_for_file(self, filename): + """Return the source file for `filename`.""" + if not filename.endswith(".py"): + if filename[-4:-1] == ".py": + filename = filename[:-1] + return filename + + def _should_trace(self, filename, frame): + """Decide whether to trace execution in `filename` + + This function is called from the trace function. As each new file name + is encountered, this function determines whether it is traced or not. + + Returns a canonicalized filename if it should be traced, False if it + should not. + + """ + if os is None: + return False + + if filename.startswith('<'): + # Lots of non-file execution is represented with artificial + # filenames like "", "", or + # "". Don't ever trace these executions, since we + # can't do anything with the data later anyway. + return False + + if filename.endswith(".html"): + # Jinja and maybe other templating systems compile templates into + # Python code, but use the template filename as the filename in + # the compiled code. Of course, those filenames are useless later + # so don't bother collecting. TODO: How should we really separate + # out good file extensions from bad? + return False + + self._check_for_packages() + + # Compiled Python files have two filenames: frame.f_code.co_filename is + # the filename at the time the .pyc was compiled. The second name + # is __file__, which is where the .pyc was actually loaded from. Since + # .pyc files can be moved after compilation (for example, by being + # installed), we look for __file__ in the frame and prefer it to the + # co_filename value. + dunder_file = frame.f_globals.get('__file__') + if dunder_file: + filename = self._source_for_file(dunder_file) + canonical = self.file_locator.canonical_filename(filename) + + # If the user specified source, then that's authoritative about what to + # measure. If they didn't, then we have to exclude the stdlib and + # coverage.py directories. + if self.source_match: + if not self.source_match.match(canonical): + return False + else: + # If we aren't supposed to trace installed code, then check if this + # is near the Python standard library and skip it if so. + if self.pylib_match and self.pylib_match.match(canonical): + return False + + # We exclude the coverage code itself, since a little of it will be + # measured otherwise. + if self.cover_match and self.cover_match.match(canonical): + return False + + # Check the file against the include and omit patterns. + if self.include_match and not self.include_match.match(canonical): + return False + if self.omit_match and self.omit_match.match(canonical): + return False + + return canonical + + # To log what should_trace returns, change this to "if 1:" + if 0: + _real_should_trace = _should_trace + def _should_trace(self, filename, frame): # pylint: disable-msg=E0102 + """A logging decorator around the real _should_trace function.""" + ret = self._real_should_trace(filename, frame) + print("should_trace: %r -> %r" % (filename, ret)) + return ret + + def _warn(self, msg): + """Use `msg` as a warning.""" + sys.stderr.write("Coverage.py warning: " + msg + "\n") + + def _abs_files(self, files): + """Return a list of absolute file names for the names in `files`.""" + files = files or [] + return [self.file_locator.abs_file(f) for f in files] + + def _check_for_packages(self): + """Update the source_match matcher with latest imported packages.""" + # Our self.source_pkgs attribute is a list of package names we want to + # measure. Each time through here, we see if we've imported any of + # them yet. If so, we add its file to source_match, and we don't have + # to look for that package any more. + if self.source_pkgs: + found = [] + for pkg in self.source_pkgs: + try: + mod = sys.modules[pkg] + except KeyError: + continue + + found.append(pkg) + + try: + pkg_file = mod.__file__ + except AttributeError: + self._warn("Module %s has no python source." % pkg) + else: + d, f = os.path.split(pkg_file) + if f.startswith('__init__.'): + # This is actually a package, return the directory. + pkg_file = d + else: + pkg_file = self._source_for_file(pkg_file) + pkg_file = self.file_locator.canonical_filename(pkg_file) + self.source.append(pkg_file) + self.source_match.add(pkg_file) + + for pkg in found: + self.source_pkgs.remove(pkg) + + def use_cache(self, usecache): + """Control the use of a data file (incorrectly called a cache). + + `usecache` is true or false, whether to read and write data on disk. + + """ + self.data.usefile(usecache) + + def load(self): + """Load previously-collected coverage data from the data file.""" + self.collector.reset() + self.data.read() + + def start(self): + """Start measuring code coverage.""" + if self.run_suffix: + # Calling start() means we're running code, so use the run_suffix + # as the data_suffix when we eventually save the data. + self.data_suffix = self.run_suffix + if self.auto_data: + self.load() + # Save coverage data when Python exits. + if not self.atexit_registered: + atexit.register(self.save) + self.atexit_registered = True + + # Create the matchers we need for _should_trace + if self.source or self.source_pkgs: + self.source_match = TreeMatcher(self.source) + else: + if self.cover_dir: + self.cover_match = TreeMatcher([self.cover_dir]) + if self.pylib_dirs: + self.pylib_match = TreeMatcher(self.pylib_dirs) + if self.include: + self.include_match = FnmatchMatcher(self.include) + if self.omit: + self.omit_match = FnmatchMatcher(self.omit) + + self._harvested = False + self.collector.start() + + def stop(self): + """Stop measuring code coverage.""" + self.collector.stop() + self._harvest_data() + + def erase(self): + """Erase previously-collected coverage data. + + This removes the in-memory data collected in this session as well as + discarding the data file. + + """ + self.collector.reset() + self.data.erase() + + def clear_exclude(self): + """Clear the exclude list.""" + self.config.exclude_list = [] + self.exclude_re = "" + + def exclude(self, regex): + """Exclude source lines from execution consideration. + + `regex` is a regular expression. Lines matching this expression are + not considered executable when reporting code coverage. A list of + regexes is maintained; this function adds a new regex to the list. + Matching any of the regexes excludes a source line. + + """ + self.config.exclude_list.append(regex) + self._compile_exclude() + + def _compile_exclude(self): + """Build the internal usable form of the exclude list.""" + self.exclude_re = "(" + ")|(".join(self.config.exclude_list) + ")" + + def get_exclude_list(self): + """Return the list of excluded regex patterns.""" + return self.config.exclude_list + + def save(self): + """Save the collected coverage data to the data file.""" + data_suffix = self.data_suffix + if data_suffix is True: + # If data_suffix was a simple true value, then make a suffix with + # plenty of distinguishing information. We do this here in + # `save()` at the last minute so that the pid will be correct even + # if the process forks. + data_suffix = "%s.%s.%06d" % ( + self.socket.gethostname(), self.os.getpid(), + self.random.randint(0, 99999) + ) + + self._harvest_data() + self.data.write(suffix=data_suffix) + + def combine(self): + """Combine together a number of similarly-named coverage data files. + + All coverage data files whose name starts with `data_file` (from the + coverage() constructor) will be read, and combined together into the + current measurements. + + """ + self.data.combine_parallel_data() + + def _harvest_data(self): + """Get the collected data and reset the collector. + + Also warn about various problems collecting data. + + """ + if not self._harvested: + self.data.add_line_data(self.collector.get_line_data()) + self.data.add_arc_data(self.collector.get_arc_data()) + self.collector.reset() + + # If there are still entries in the source_pkgs list, then we never + # encountered those packages. + for pkg in self.source_pkgs: + self._warn("Source module %s was never encountered." % pkg) + + # Find out if we got any data. + summary = self.data.summary() + if not summary: + self._warn("No data was collected.") + + # Find files that were never executed at all. + for src in self.source: + for py_file in find_python_files(src): + self.data.touch_file(py_file) + + self._harvested = True + + # Backward compatibility with version 1. + def analysis(self, morf): + """Like `analysis2` but doesn't return excluded line numbers.""" + f, s, _, m, mf = self.analysis2(morf) + return f, s, m, mf + + def analysis2(self, morf): + """Analyze a module. + + `morf` is a module or a filename. It will be analyzed to determine + its coverage statistics. The return value is a 5-tuple: + + * The filename for the module. + * A list of line numbers of executable statements. + * A list of line numbers of excluded statements. + * A list of line numbers of statements not run (missing from + execution). + * A readable formatted string of the missing line numbers. + + The analysis uses the source file itself and the current measured + coverage data. + + """ + analysis = self._analyze(morf) + return ( + analysis.filename, analysis.statements, analysis.excluded, + analysis.missing, analysis.missing_formatted() + ) + + def _analyze(self, it): + """Analyze a single morf or code unit. + + Returns an `Analysis` object. + + """ + if not isinstance(it, CodeUnit): + it = code_unit_factory(it, self.file_locator)[0] + + return Analysis(self, it) + + def report(self, morfs=None, show_missing=True, ignore_errors=None, + file=None, # pylint: disable-msg=W0622 + omit=None, include=None + ): + """Write a summary report to `file`. + + Each module in `morfs` is listed, with counts of statements, executed + statements, missing statements, and a list of lines missed. + + `include` is a list of filename patterns. Modules whose filenames + match those patterns will be included in the report. Modules matching + `omit` will not be included in the report. + + """ + self.config.from_args( + ignore_errors=ignore_errors, omit=omit, include=include + ) + reporter = SummaryReporter( + self, show_missing, self.config.ignore_errors + ) + reporter.report(morfs, outfile=file, config=self.config) + + def annotate(self, morfs=None, directory=None, ignore_errors=None, + omit=None, include=None): + """Annotate a list of modules. + + Each module in `morfs` is annotated. The source is written to a new + file, named with a ",cover" suffix, with each line prefixed with a + marker to indicate the coverage of the line. Covered lines have ">", + excluded lines have "-", and missing lines have "!". + + See `coverage.report()` for other arguments. + + """ + self.config.from_args( + ignore_errors=ignore_errors, omit=omit, include=include + ) + reporter = AnnotateReporter(self, self.config.ignore_errors) + reporter.report(morfs, config=self.config, directory=directory) + + def html_report(self, morfs=None, directory=None, ignore_errors=None, + omit=None, include=None): + """Generate an HTML report. + + See `coverage.report()` for other arguments. + + """ + self.config.from_args( + ignore_errors=ignore_errors, omit=omit, include=include, + html_dir=directory, + ) + reporter = HtmlReporter(self, self.config.ignore_errors) + reporter.report(morfs, config=self.config) + + def xml_report(self, morfs=None, outfile=None, ignore_errors=None, + omit=None, include=None): + """Generate an XML report of coverage results. + + The report is compatible with Cobertura reports. + + Each module in `morfs` is included in the report. `outfile` is the + path to write the file to, "-" will write to stdout. + + See `coverage.report()` for other arguments. + + """ + self.config.from_args( + ignore_errors=ignore_errors, omit=omit, include=include, + xml_output=outfile, + ) + file_to_close = None + if self.config.xml_output: + if self.config.xml_output == '-': + outfile = sys.stdout + else: + outfile = open(self.config.xml_output, "w") + file_to_close = outfile + try: + reporter = XmlReporter(self, self.config.ignore_errors) + reporter.report(morfs, outfile=outfile, config=self.config) + finally: + if file_to_close: + file_to_close.close() + + def sysinfo(self): + """Return a list of (key, value) pairs showing internal information.""" + + import coverage as covmod + import platform, re + + info = [ + ('version', covmod.__version__), + ('coverage', covmod.__file__), + ('cover_dir', self.cover_dir), + ('pylib_dirs', self.pylib_dirs), + ('tracer', self.collector.tracer_name()), + ('data_path', self.data.filename), + ('python', sys.version.replace('\n', '')), + ('platform', platform.platform()), + ('cwd', os.getcwd()), + ('path', sys.path), + ('environment', [ + ("%s = %s" % (k, v)) for k, v in os.environ.items() + if re.search("^COV|^PY", k) + ]), + ] + return info + + +def process_startup(): + """Call this at Python startup to perhaps measure coverage. + + If the environment variable COVERAGE_PROCESS_START is defined, coverage + measurement is started. The value of the variable is the config file + to use. + + There are two ways to configure your Python installation to invoke this + function when Python starts: + + #. Create or append to sitecustomize.py to add these lines:: + + import coverage + coverage.process_startup() + + #. Create a .pth file in your Python installation containing:: + + import coverage; coverage.process_startup() + + """ + cps = os.environ.get("COVERAGE_PROCESS_START") + if cps: + cov = coverage(config_file=cps, auto_data=True) + if os.environ.get("COVERAGE_COVERAGE"): + # Measuring coverage within coverage.py takes yet more trickery. + cov.cover_dir = "Please measure coverage.py!" + cov.start() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py new file mode 100644 index 00000000..3d750c42 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py @@ -0,0 +1,261 @@ +"""Coverage data for Coverage.""" + +import os + +from coverage.backward import pickle, sorted # pylint: disable-msg=W0622 + + +class CoverageData(object): + """Manages collected coverage data, including file storage. + + The data file format is a pickled dict, with these keys: + + * collector: a string identifying the collecting software + + * lines: a dict mapping filenames to sorted lists of line numbers + executed: + { 'file1': [17,23,45], 'file2': [1,2,3], ... } + + * arcs: a dict mapping filenames to sorted lists of line number pairs: + { 'file1': [(17,23), (17,25), (25,26)], ... } + + """ + + def __init__(self, basename=None, collector=None): + """Create a CoverageData. + + `basename` is the name of the file to use for storing data. + + `collector` is a string describing the coverage measurement software. + + """ + self.collector = collector or 'unknown' + + self.use_file = True + + # Construct the filename that will be used for data file storage, if we + # ever do any file storage. + self.filename = basename or ".coverage" + self.filename = os.path.abspath(self.filename) + + # A map from canonical Python source file name to a dictionary in + # which there's an entry for each line number that has been + # executed: + # + # { + # 'filename1.py': { 12: None, 47: None, ... }, + # ... + # } + # + self.lines = {} + + # A map from canonical Python source file name to a dictionary with an + # entry for each pair of line numbers forming an arc: + # + # { + # 'filename1.py': { (12,14): None, (47,48): None, ... }, + # ... + # } + # + self.arcs = {} + + self.os = os + self.sorted = sorted + self.pickle = pickle + + def usefile(self, use_file=True): + """Set whether or not to use a disk file for data.""" + self.use_file = use_file + + def read(self): + """Read coverage data from the coverage data file (if it exists).""" + if self.use_file: + self.lines, self.arcs = self._read_file(self.filename) + else: + self.lines, self.arcs = {}, {} + + def write(self, suffix=None): + """Write the collected coverage data to a file. + + `suffix` is a suffix to append to the base file name. This can be used + for multiple or parallel execution, so that many coverage data files + can exist simultaneously. A dot will be used to join the base name and + the suffix. + + """ + if self.use_file: + filename = self.filename + if suffix: + filename += "." + suffix + self.write_file(filename) + + def erase(self): + """Erase the data, both in this object, and from its file storage.""" + if self.use_file: + if self.filename and os.path.exists(self.filename): + os.remove(self.filename) + self.lines = {} + self.arcs = {} + + def line_data(self): + """Return the map from filenames to lists of line numbers executed.""" + return dict( + [(f, self.sorted(lmap.keys())) for f, lmap in self.lines.items()] + ) + + def arc_data(self): + """Return the map from filenames to lists of line number pairs.""" + return dict( + [(f, self.sorted(amap.keys())) for f, amap in self.arcs.items()] + ) + + def write_file(self, filename): + """Write the coverage data to `filename`.""" + + # Create the file data. + data = {} + + data['lines'] = self.line_data() + arcs = self.arc_data() + if arcs: + data['arcs'] = arcs + + if self.collector: + data['collector'] = self.collector + + # Write the pickle to the file. + fdata = open(filename, 'wb') + try: + self.pickle.dump(data, fdata, 2) + finally: + fdata.close() + + def read_file(self, filename): + """Read the coverage data from `filename`.""" + self.lines, self.arcs = self._read_file(filename) + + def raw_data(self, filename): + """Return the raw pickled data from `filename`.""" + fdata = open(filename, 'rb') + try: + data = pickle.load(fdata) + finally: + fdata.close() + return data + + def _read_file(self, filename): + """Return the stored coverage data from the given file. + + Returns two values, suitable for assigning to `self.lines` and + `self.arcs`. + + """ + lines = {} + arcs = {} + try: + data = self.raw_data(filename) + if isinstance(data, dict): + # Unpack the 'lines' item. + lines = dict([ + (f, dict.fromkeys(linenos, None)) + for f, linenos in data.get('lines', {}).items() + ]) + # Unpack the 'arcs' item. + arcs = dict([ + (f, dict.fromkeys(arcpairs, None)) + for f, arcpairs in data.get('arcs', {}).items() + ]) + except Exception: + pass + return lines, arcs + + def combine_parallel_data(self): + """Combine a number of data files together. + + Treat `self.filename` as a file prefix, and combine the data from all + of the data files starting with that prefix plus a dot. + + """ + data_dir, local = os.path.split(self.filename) + localdot = local + '.' + for f in os.listdir(data_dir or '.'): + if f.startswith(localdot): + full_path = os.path.join(data_dir, f) + new_lines, new_arcs = self._read_file(full_path) + for filename, file_data in new_lines.items(): + self.lines.setdefault(filename, {}).update(file_data) + for filename, file_data in new_arcs.items(): + self.arcs.setdefault(filename, {}).update(file_data) + if f != local: + os.remove(full_path) + + def add_line_data(self, line_data): + """Add executed line data. + + `line_data` is { filename: { lineno: None, ... }, ...} + + """ + for filename, linenos in line_data.items(): + self.lines.setdefault(filename, {}).update(linenos) + + def add_arc_data(self, arc_data): + """Add measured arc data. + + `arc_data` is { filename: { (l1,l2): None, ... }, ...} + + """ + for filename, arcs in arc_data.items(): + self.arcs.setdefault(filename, {}).update(arcs) + + def touch_file(self, filename): + """Ensure that `filename` appears in the data, empty if needed.""" + self.lines.setdefault(filename, {}) + + def measured_files(self): + """A list of all files that had been measured.""" + return list(self.lines.keys()) + + def executed_lines(self, filename): + """A map containing all the line numbers executed in `filename`. + + If `filename` hasn't been collected at all (because it wasn't executed) + then return an empty map. + + """ + return self.lines.get(filename) or {} + + def executed_arcs(self, filename): + """A map containing all the arcs executed in `filename`.""" + return self.arcs.get(filename) or {} + + def summary(self, fullpath=False): + """Return a dict summarizing the coverage data. + + Keys are based on the filenames, and values are the number of executed + lines. If `fullpath` is true, then the keys are the full pathnames of + the files, otherwise they are the basenames of the files. + + """ + summ = {} + if fullpath: + filename_fn = lambda f: f + else: + filename_fn = self.os.path.basename + for filename, lines in self.lines.items(): + summ[filename_fn(filename)] = len(lines) + return summ + + def has_arcs(self): + """Does this data have arcs?""" + return bool(self.arcs) + + +if __name__ == '__main__': + # Ad-hoc: show the raw data in a data file. + import pprint, sys + covdata = CoverageData() + if sys.argv[1:]: + fname = sys.argv[1] + else: + fname = covdata.filename + pprint.pprint(covdata.raw_data(fname)) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py new file mode 100644 index 00000000..333163f8 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py @@ -0,0 +1,72 @@ +"""Execute files of Python code.""" + +import imp, os, sys + +from coverage.backward import exec_code_object +from coverage.misc import NoSource, ExceptionDuringRun + + +try: + # In Py 2.x, the builtins were in __builtin__ + BUILTINS = sys.modules['__builtin__'] +except KeyError: + # In Py 3.x, they're in builtins + BUILTINS = sys.modules['builtins'] + + +def run_python_file(filename, args): + """Run a python file as if it were the main program on the command line. + + `filename` is the path to the file to execute, it need not be a .py file. + `args` is the argument array to present as sys.argv, including the first + element representing the file being executed. + + """ + # Create a module to serve as __main__ + old_main_mod = sys.modules['__main__'] + main_mod = imp.new_module('__main__') + sys.modules['__main__'] = main_mod + main_mod.__file__ = filename + main_mod.__builtins__ = BUILTINS + + # Set sys.argv and the first path element properly. + old_argv = sys.argv + old_path0 = sys.path[0] + sys.argv = args + sys.path[0] = os.path.dirname(filename) + + try: + # Open the source file. + try: + source = open(filename, 'rU').read() + except IOError: + raise NoSource("No file to run: %r" % filename) + + # We have the source. `compile` still needs the last line to be clean, + # so make sure it is, then compile a code object from it. + if source[-1] != '\n': + source += '\n' + code = compile(source, filename, "exec") + + # Execute the source file. + try: + exec_code_object(code, main_mod.__dict__) + except SystemExit: + # The user called sys.exit(). Just pass it along to the upper + # layers, where it will be handled. + raise + except: + # Something went wrong while executing the user code. + # Get the exc_info, and pack them into an exception that we can + # throw up to the outer loop. We peel two layers off the traceback + # so that the coverage.py code doesn't appear in the final printed + # traceback. + typ, err, tb = sys.exc_info() + raise ExceptionDuringRun(typ, err, tb.tb_next.tb_next) + finally: + # Restore the old __main__ + sys.modules['__main__'] = old_main_mod + + # Restore the old argv and path + sys.argv = old_argv + sys.path[0] = old_path0 diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py new file mode 100644 index 00000000..9a8ac564 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py @@ -0,0 +1,132 @@ +"""File wrangling.""" + +import fnmatch, os, sys + +class FileLocator(object): + """Understand how filenames work.""" + + def __init__(self): + # The absolute path to our current directory. + self.relative_dir = self.abs_file(os.curdir) + os.sep + + # Cache of results of calling the canonical_filename() method, to + # avoid duplicating work. + self.canonical_filename_cache = {} + + def abs_file(self, filename): + """Return the absolute normalized form of `filename`.""" + return os.path.normcase(os.path.abspath(os.path.realpath(filename))) + + def relative_filename(self, filename): + """Return the relative form of `filename`. + + The filename will be relative to the current directory when the + `FileLocator` was constructed. + + """ + if filename.startswith(self.relative_dir): + filename = filename.replace(self.relative_dir, "") + return filename + + def canonical_filename(self, filename): + """Return a canonical filename for `filename`. + + An absolute path with no redundant components and normalized case. + + """ + if filename not in self.canonical_filename_cache: + f = filename + if os.path.isabs(f) and not os.path.exists(f): + if self.get_zip_data(f) is None: + f = os.path.basename(f) + if not os.path.isabs(f): + for path in [os.curdir] + sys.path: + if path is None: + continue + g = os.path.join(path, f) + if os.path.exists(g): + f = g + break + cf = self.abs_file(f) + self.canonical_filename_cache[filename] = cf + return self.canonical_filename_cache[filename] + + def get_zip_data(self, filename): + """Get data from `filename` if it is a zip file path. + + Returns the string data read from the zip file, or None if no zip file + could be found or `filename` isn't in it. The data returned will be + an empty string if the file is empty. + + """ + import zipimport + markers = ['.zip'+os.sep, '.egg'+os.sep] + for marker in markers: + if marker in filename: + parts = filename.split(marker) + try: + zi = zipimport.zipimporter(parts[0]+marker[:-1]) + except zipimport.ZipImportError: + continue + try: + data = zi.get_data(parts[1]) + except IOError: + continue + if sys.version_info >= (3, 0): + data = data.decode('utf8') # TODO: How to do this properly? + return data + return None + + +class TreeMatcher(object): + """A matcher for files in a tree.""" + def __init__(self, directories): + self.dirs = directories[:] + + def __repr__(self): + return "" % self.dirs + + def add(self, directory): + """Add another directory to the list we match for.""" + self.dirs.append(directory) + + def match(self, fpath): + """Does `fpath` indicate a file in one of our trees?""" + for d in self.dirs: + if fpath.startswith(d): + if fpath == d: + # This is the same file! + return True + if fpath[len(d)] == os.sep: + # This is a file in the directory + return True + return False + + +class FnmatchMatcher(object): + """A matcher for files by filename pattern.""" + def __init__(self, pats): + self.pats = pats[:] + + def __repr__(self): + return "" % self.pats + + def match(self, fpath): + """Does `fpath` match one of our filename patterns?""" + for pat in self.pats: + if fnmatch.fnmatch(fpath, pat): + return True + return False + + +def find_python_files(dirname): + """Yield all of the importable Python files in `dirname`, recursively.""" + for dirpath, dirnames, filenames in os.walk(dirname, topdown=True): + if '__init__.py' not in filenames: + # If a directory doesn't have __init__.py, then it isn't + # importable and neither are its files + del dirnames[:] + continue + for filename in filenames: + if fnmatch.fnmatch(filename, "*.py"): + yield os.path.join(dirpath, filename) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py new file mode 100644 index 00000000..dac4ff96 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py @@ -0,0 +1,183 @@ +"""HTML reporting for Coverage.""" + +import os, re, shutil + +from coverage import __url__, __version__ # pylint: disable-msg=W0611 +from coverage.misc import CoverageException +from coverage.phystokens import source_token_lines +from coverage.report import Reporter +from coverage.templite import Templite + +# Disable pylint msg W0612, because a bunch of variables look unused, but +# they're accessed in a Templite context via locals(). +# pylint: disable-msg=W0612 + +def data_filename(fname): + """Return the path to a data file of ours.""" + return os.path.join(os.path.split(__file__)[0], fname) + +def data(fname): + """Return the contents of a data file of ours.""" + return open(data_filename(fname)).read() + + +class HtmlReporter(Reporter): + """HTML reporting.""" + + def __init__(self, coverage, ignore_errors=False): + super(HtmlReporter, self).__init__(coverage, ignore_errors) + self.directory = None + self.source_tmpl = Templite(data("htmlfiles/pyfile.html"), globals()) + + self.files = [] + self.arcs = coverage.data.has_arcs() + + def report(self, morfs, config=None): + """Generate an HTML report for `morfs`. + + `morfs` is a list of modules or filenames. `config` is a + CoverageConfig instance. + + """ + assert config.html_dir, "must provide a directory for html reporting" + + # Process all the files. + self.report_files(self.html_file, morfs, config, config.html_dir) + + if not self.files: + raise CoverageException("No data to report.") + + # Write the index file. + self.index_file() + + # Create the once-per-directory files. + for static in [ + "style.css", "coverage_html.js", + "jquery-1.3.2.min.js", "jquery.tablesorter.min.js" + ]: + shutil.copyfile( + data_filename("htmlfiles/" + static), + os.path.join(self.directory, static) + ) + + def html_file(self, cu, analysis): + """Generate an HTML file for one source file.""" + + source = cu.source_file().read() + + nums = analysis.numbers + + missing_branch_arcs = analysis.missing_branch_arcs() + n_par = 0 # accumulated below. + arcs = self.arcs + + # These classes determine which lines are highlighted by default. + c_run = "run hide_run" + c_exc = "exc" + c_mis = "mis" + c_par = "par " + c_run + + lines = [] + + for lineno, line in enumerate(source_token_lines(source)): + lineno += 1 # 1-based line numbers. + # Figure out how to mark this line. + line_class = [] + annotate_html = "" + annotate_title = "" + if lineno in analysis.statements: + line_class.append("stm") + if lineno in analysis.excluded: + line_class.append(c_exc) + elif lineno in analysis.missing: + line_class.append(c_mis) + elif self.arcs and lineno in missing_branch_arcs: + line_class.append(c_par) + n_par += 1 + annlines = [] + for b in missing_branch_arcs[lineno]: + if b < 0: + annlines.append("exit") + else: + annlines.append(str(b)) + annotate_html = "   ".join(annlines) + if len(annlines) > 1: + annotate_title = "no jumps to these line numbers" + elif len(annlines) == 1: + annotate_title = "no jump to this line number" + elif lineno in analysis.statements: + line_class.append(c_run) + + # Build the HTML for the line + html = [] + for tok_type, tok_text in line: + if tok_type == "ws": + html.append(escape(tok_text)) + else: + tok_html = escape(tok_text) or ' ' + html.append( + "%s" % (tok_type, tok_html) + ) + + lines.append({ + 'html': ''.join(html), + 'number': lineno, + 'class': ' '.join(line_class) or "pln", + 'annotate': annotate_html, + 'annotate_title': annotate_title, + }) + + # Write the HTML page for this file. + html_filename = cu.flat_rootname() + ".html" + html_path = os.path.join(self.directory, html_filename) + html = spaceless(self.source_tmpl.render(locals())) + fhtml = open(html_path, 'w') + fhtml.write(html) + fhtml.close() + + # Save this file's information for the index file. + self.files.append({ + 'nums': nums, + 'par': n_par, + 'html_filename': html_filename, + 'cu': cu, + }) + + def index_file(self): + """Write the index.html file for this report.""" + index_tmpl = Templite(data("htmlfiles/index.html"), globals()) + + files = self.files + arcs = self.arcs + + totals = sum([f['nums'] for f in files]) + + fhtml = open(os.path.join(self.directory, "index.html"), "w") + fhtml.write(index_tmpl.render(locals())) + fhtml.close() + + +# Helpers for templates and generating HTML + +def escape(t): + """HTML-escape the text in `t`.""" + return (t + # Convert HTML special chars into HTML entities. + .replace("&", "&").replace("<", "<").replace(">", ">") + .replace("'", "'").replace('"', """) + # Convert runs of spaces: "......" -> " . . ." + .replace(" ", "  ") + # To deal with odd-length runs, convert the final pair of spaces + # so that "....." -> " .  ." + .replace(" ", "  ") + ) + +def spaceless(html): + """Squeeze out some annoying extra space from an HTML string. + + Nicely-formatted templates mean lots of extra space in the result. + Get rid of some. + + """ + html = re.sub(">\s+

\n

-1) { + cookies = document.cookie.split(";"); + for (var i=0; i < cookies.length; i++) { + parts = cookies[i].split("=") + + if ($.trim(parts[0]) == cookie_name && parts[1]) { + sort_list = eval("[[" + parts[1] + "]]"); + break; + } + } + } + + // Create a new widget which exists only to save and restore + // the sort order: + $.tablesorter.addWidget({ + id: "persistentSort", + + // Format is called by the widget before displaying: + format: function(table) { + if (table.config.sortList.length == 0 && sort_list.length > 0) { + // This table hasn't been sorted before - we'll use + // our stored settings: + $(table).trigger('sorton', [sort_list]); + } + else { + // This is not the first load - something has + // already defined sorting so we'll just update + // our stored value to match: + sort_list = table.config.sortList; + } + } + }); + + // Configure our tablesorter to handle the variable number of + // columns produced depending on report options: + var headers = {}; + var col_count = $("table.index > thead > tr > th").length; + + headers[0] = { sorter: 'text' }; + for (var i = 1; i < col_count-1; i++) { + headers[i] = { sorter: 'digit' }; + } + headers[col_count-1] = { sorter: 'percent' }; + + // Enable the table sorter: + $("table.index").tablesorter({ + widgets: ['persistentSort'], + headers: headers + }); + + // Watch for page unload events so we can save the final sort settings: + $(window).unload(function() { + document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/" + }); +} + +// -- pyfile stuff -- + +function pyfile_ready($) { + // If we're directed to a particular line number, highlight the line. + var frag = location.hash; + if (frag.length > 2 && frag[1] == 'n') { + $(frag).addClass('highlight'); + } +} + +function toggle_lines(btn, cls) { + btn = $(btn); + var hide = "hide_"+cls; + if (btn.hasClass(hide)) { + $("#source ."+cls).removeClass(hide); + btn.removeClass(hide); + } + else { + $("#source ."+cls).addClass(hide); + btn.addClass(hide); + } +} diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html new file mode 100644 index 00000000..bec2584f --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html @@ -0,0 +1,81 @@ + + + + + Coverage report + + + + + + + + +

+ +
+ + + {# The title='' attr doesn't work in Safari. #} + + + + + + {% if arcs %} + + + {% endif %} + + + + {# HTML syntax requires thead, tfoot, tbody #} + + + + + + + {% if arcs %} + + + {% endif %} + + + + + {% for file in files %} + + + + + + {% if arcs %} + + + {% endif %} + + + {% endfor %} + +
Modulestatementsmissingexcludedbranchespartialcoverage
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_missing_branches}}{{totals.pc_covered_str}}%
{{file.cu.name}}{{file.nums.n_statements}}{{file.nums.n_missing}}{{file.nums.n_excluded}}{{file.nums.n_branches}}{{file.nums.n_missing_branches}}{{file.nums.pc_covered_str}}%
+
+ + + + + diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js new file mode 100644 index 00000000..b1ae21d8 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js new file mode 100644 index 00000000..64c70071 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js @@ -0,0 +1,2 @@ + +(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i + + + + {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #} + {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #} + + Coverage for {{cu.name|escape}}: {{nums.pc_covered_str}}% + + + + + + + + + +
+ + + + + +
+ {% for line in lines %} +

{{line.number}}

+ {% endfor %} +
+ {% for line in lines %} +

{% if line.annotate %}{{line.annotate}}{% endif %}{{line.html}} 

+ {% endfor %} +
+
+ + + + + diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css new file mode 100644 index 00000000..9a06a2b4 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css @@ -0,0 +1,230 @@ +/* CSS styles for Coverage. */ +/* Page-wide styles */ +html, body, h1, h2, h3, p, td, th { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; + } + +/* Set baseline grid to 16 pt. */ +body { + font-family: georgia, serif; + font-size: 1em; + } + +html>body { + font-size: 16px; + } + +/* Set base font size to 12/16 */ +p { + font-size: .75em; /* 12/16 */ + line-height: 1.3333em; /* 16/12 */ + } + +table { + border-collapse: collapse; + } + +a.nav { + text-decoration: none; + color: inherit; + } +a.nav:hover { + text-decoration: underline; + color: inherit; + } + +/* Page structure */ +#header { + background: #f8f8f8; + width: 100%; + border-bottom: 1px solid #eee; + } + +#source { + padding: 1em; + font-family: "courier new", monospace; + } + +#indexfile #footer { + margin: 1em 3em; + } + +#pyfile #footer { + margin: 1em 1em; + } + +#footer .content { + padding: 0; + font-size: 85%; + font-family: verdana, sans-serif; + color: #666666; + font-style: italic; + } + +#index { + margin: 1em 0 0 3em; + } + +/* Header styles */ +#header .content { + padding: 1em 3em; + } + +h1 { + font-size: 1.25em; +} + +h2.stats { + margin-top: .5em; + font-size: 1em; +} +.stats span { + border: 1px solid; + padding: .1em .25em; + margin: 0 .1em; + cursor: pointer; + border-color: #999 #ccc #ccc #999; +} +.stats span.hide_run, .stats span.hide_exc, +.stats span.hide_mis, .stats span.hide_par, +.stats span.par.hide_run.hide_par { + border-color: #ccc #999 #999 #ccc; +} +.stats span.par.hide_run { + border-color: #999 #ccc #ccc #999; +} + +/* Source file styles */ +.linenos p { + text-align: right; + margin: 0; + padding: 0 .5em; + color: #999999; + font-family: verdana, sans-serif; + font-size: .625em; /* 10/16 */ + line-height: 1.6em; /* 16/10 */ + } +.linenos p.highlight { + background: #ffdd00; + } +.linenos p a { + text-decoration: none; + color: #999999; + } +.linenos p a:hover { + text-decoration: underline; + color: #999999; + } + +td.text { + width: 100%; + } +.text p { + margin: 0; + padding: 0 0 0 .5em; + border-left: 2px solid #ffffff; + white-space: nowrap; + } + +.text p.mis { + background: #ffdddd; + border-left: 2px solid #ff0000; + } +.text p.run, .text p.run.hide_par { + background: #ddffdd; + border-left: 2px solid #00ff00; + } +.text p.exc { + background: #eeeeee; + border-left: 2px solid #808080; + } +.text p.par, .text p.par.hide_run { + background: #ffffaa; + border-left: 2px solid #eeee99; + } +.text p.hide_run, .text p.hide_exc, .text p.hide_mis, .text p.hide_par, +.text p.hide_run.hide_par { + background: inherit; + } + +.text span.annotate { + font-family: georgia; + font-style: italic; + color: #666; + float: right; + padding-right: .5em; + } +.text p.hide_par span.annotate { + display: none; + } + +/* Syntax coloring */ +.text .com { + color: green; + font-style: italic; + line-height: 1px; + } +.text .key { + font-weight: bold; + line-height: 1px; + } +.text .str { + color: #000080; + } + +/* index styles */ +#index td, #index th { + text-align: right; + width: 5em; + padding: .25em .5em; + border-bottom: 1px solid #eee; + } +#index th { + font-style: italic; + color: #333; + border-bottom: 1px solid #ccc; + cursor: pointer; + } +#index th:hover { + background: #eee; + border-bottom: 1px solid #999; + } +#index td.left, #index th.left { + padding-left: 0; + } +#index td.right, #index th.right { + padding-right: 0; + } +#index th.headerSortDown, #index th.headerSortUp { + border-bottom: 1px solid #000; + } +#index td.name, #index th.name { + text-align: left; + width: auto; + } +#index td.name a { + text-decoration: none; + color: #000; + } +#index td.name a:hover { + text-decoration: underline; + color: #000; + } +#index tr.total { + } +#index tr.total td { + font-weight: bold; + border-top: 1px solid #ccc; + border-bottom: none; + } +#index tr.file:hover { + background: #eeeeee; + } diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py new file mode 100644 index 00000000..4218536d --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py @@ -0,0 +1,85 @@ +"""Miscellaneous stuff for Coverage.""" + +def nice_pair(pair): + """Make a nice string representation of a pair of numbers. + + If the numbers are equal, just return the number, otherwise return the pair + with a dash between them, indicating the range. + + """ + start, end = pair + if start == end: + return "%d" % start + else: + return "%d-%d" % (start, end) + + +def format_lines(statements, lines): + """Nicely format a list of line numbers. + + Format a list of line numbers for printing by coalescing groups of lines as + long as the lines represent consecutive statements. This will coalesce + even if there are gaps between statements. + + For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and + `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14". + + """ + pairs = [] + i = 0 + j = 0 + start = None + while i < len(statements) and j < len(lines): + if statements[i] == lines[j]: + if start == None: + start = lines[j] + end = lines[j] + j += 1 + elif start: + pairs.append((start, end)) + start = None + i += 1 + if start: + pairs.append((start, end)) + ret = ', '.join(map(nice_pair, pairs)) + return ret + + +def expensive(fn): + """A decorator to cache the result of an expensive operation. + + Only applies to methods with no arguments. + + """ + attr = "_cache_" + fn.__name__ + def _wrapped(self): + """Inner fn that checks the cache.""" + if not hasattr(self, attr): + setattr(self, attr, fn(self)) + return getattr(self, attr) + return _wrapped + + +def bool_or_none(b): + """Return bool(b), but preserve None.""" + if b is None: + return None + else: + return bool(b) + + +class CoverageException(Exception): + """An exception specific to Coverage.""" + pass + +class NoSource(CoverageException): + """Used to indicate we couldn't find the source for a module.""" + pass + +class ExceptionDuringRun(CoverageException): + """An exception happened while running customer code. + + Construct it with three arguments, the values from `sys.exc_info`. + + """ + pass diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py new file mode 100644 index 00000000..ae618ce5 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py @@ -0,0 +1,783 @@ +"""Code parsing for Coverage.""" + +import glob, opcode, os, re, sys, token, tokenize + +from coverage.backward import set, sorted, StringIO # pylint: disable-msg=W0622 +from coverage.bytecode import ByteCodes, CodeObjects +from coverage.misc import nice_pair, CoverageException, NoSource, expensive + + +class CodeParser(object): + """Parse code to find executable lines, excluded lines, etc.""" + + def __init__(self, text=None, filename=None, exclude=None): + """ + Source can be provided as `text`, the text itself, or `filename`, from + which text will be read. Excluded lines are those that match + `exclude`, a regex. + + """ + assert text or filename, "CodeParser needs either text or filename" + self.filename = filename or "" + self.text = text + if not self.text: + try: + sourcef = open(self.filename, 'rU') + self.text = sourcef.read() + sourcef.close() + except IOError: + _, err, _ = sys.exc_info() + raise NoSource( + "No source for code: %r: %s" % (self.filename, err) + ) + self.text = self.text.replace('\r\n', '\n') + + self.exclude = exclude + + self.show_tokens = False + + # The text lines of the parsed code. + self.lines = self.text.split('\n') + + # The line numbers of excluded lines of code. + self.excluded = set() + + # The line numbers of docstring lines. + self.docstrings = set() + + # The line numbers of class definitions. + self.classdefs = set() + + # A dict mapping line numbers to (lo,hi) for multi-line statements. + self.multiline = {} + + # The line numbers that start statements. + self.statement_starts = set() + + # Lazily-created ByteParser + self._byte_parser = None + + def _get_byte_parser(self): + """Create a ByteParser on demand.""" + if not self._byte_parser: + self._byte_parser = \ + ByteParser(text=self.text, filename=self.filename) + return self._byte_parser + byte_parser = property(_get_byte_parser) + + def _raw_parse(self): + """Parse the source to find the interesting facts about its lines. + + A handful of member fields are updated. + + """ + # Find lines which match an exclusion pattern. + if self.exclude: + re_exclude = re.compile(self.exclude) + for i, ltext in enumerate(self.lines): + if re_exclude.search(ltext): + self.excluded.add(i+1) + + # Tokenize, to find excluded suites, to find docstrings, and to find + # multi-line statements. + indent = 0 + exclude_indent = 0 + excluding = False + prev_toktype = token.INDENT + first_line = None + empty = True + + tokgen = tokenize.generate_tokens(StringIO(self.text).readline) + for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: + if self.show_tokens: # pragma: no cover + print("%10s %5s %-20r %r" % ( + tokenize.tok_name.get(toktype, toktype), + nice_pair((slineno, elineno)), ttext, ltext + )) + if toktype == token.INDENT: + indent += 1 + elif toktype == token.DEDENT: + indent -= 1 + elif toktype == token.NAME and ttext == 'class': + # Class definitions look like branches in the byte code, so + # we need to exclude them. The simplest way is to note the + # lines with the 'class' keyword. + self.classdefs.add(slineno) + elif toktype == token.OP and ttext == ':': + if not excluding and elineno in self.excluded: + # Start excluding a suite. We trigger off of the colon + # token so that the #pragma comment will be recognized on + # the same line as the colon. + exclude_indent = indent + excluding = True + elif toktype == token.STRING and prev_toktype == token.INDENT: + # Strings that are first on an indented line are docstrings. + # (a trick from trace.py in the stdlib.) This works for + # 99.9999% of cases. For the rest (!) see: + # http://stackoverflow.com/questions/1769332/x/1769794#1769794 + for i in range(slineno, elineno+1): + self.docstrings.add(i) + elif toktype == token.NEWLINE: + if first_line is not None and elineno != first_line: + # We're at the end of a line, and we've ended on a + # different line than the first line of the statement, + # so record a multi-line range. + rng = (first_line, elineno) + for l in range(first_line, elineno+1): + self.multiline[l] = rng + first_line = None + + if ttext.strip() and toktype != tokenize.COMMENT: + # A non-whitespace token. + empty = False + if first_line is None: + # The token is not whitespace, and is the first in a + # statement. + first_line = slineno + # Check whether to end an excluded suite. + if excluding and indent <= exclude_indent: + excluding = False + if excluding: + self.excluded.add(elineno) + + prev_toktype = toktype + + # Find the starts of the executable statements. + if not empty: + self.statement_starts.update(self.byte_parser._find_statements()) + + def first_line(self, line): + """Return the first line number of the statement including `line`.""" + rng = self.multiline.get(line) + if rng: + first_line = rng[0] + else: + first_line = line + return first_line + + def first_lines(self, lines, ignore=None): + """Map the line numbers in `lines` to the correct first line of the + statement. + + Skip any line mentioned in `ignore`. + + Returns a sorted list of the first lines. + + """ + ignore = ignore or [] + lset = set() + for l in lines: + if l in ignore: + continue + new_l = self.first_line(l) + if new_l not in ignore: + lset.add(new_l) + return sorted(lset) + + def parse_source(self): + """Parse source text to find executable lines, excluded lines, etc. + + Return values are 1) a sorted list of executable line numbers, and + 2) a sorted list of excluded line numbers. + + Reported line numbers are normalized to the first line of multi-line + statements. + + """ + self._raw_parse() + + excluded_lines = self.first_lines(self.excluded) + ignore = excluded_lines + list(self.docstrings) + lines = self.first_lines(self.statement_starts, ignore) + + return lines, excluded_lines + + def arcs(self): + """Get information about the arcs available in the code. + + Returns a sorted list of line number pairs. Line numbers have been + normalized to the first line of multiline statements. + + """ + all_arcs = [] + for l1, l2 in self.byte_parser._all_arcs(): + fl1 = self.first_line(l1) + fl2 = self.first_line(l2) + if fl1 != fl2: + all_arcs.append((fl1, fl2)) + return sorted(all_arcs) + arcs = expensive(arcs) + + def exit_counts(self): + """Get a mapping from line numbers to count of exits from that line. + + Excluded lines are excluded. + + """ + excluded_lines = self.first_lines(self.excluded) + exit_counts = {} + for l1, l2 in self.arcs(): + if l1 < 0: + # Don't ever report -1 as a line number + continue + if l1 in excluded_lines: + # Don't report excluded lines as line numbers. + continue + if l2 in excluded_lines: + # Arcs to excluded lines shouldn't count. + continue + if l1 not in exit_counts: + exit_counts[l1] = 0 + exit_counts[l1] += 1 + + # Class definitions have one extra exit, so remove one for each: + for l in self.classdefs: + # Ensure key is there: classdefs can include excluded lines. + if l in exit_counts: + exit_counts[l] -= 1 + + return exit_counts + exit_counts = expensive(exit_counts) + + +## Opcodes that guide the ByteParser. + +def _opcode(name): + """Return the opcode by name from the opcode module.""" + return opcode.opmap[name] + +def _opcode_set(*names): + """Return a set of opcodes by the names in `names`.""" + s = set() + for name in names: + try: + s.add(_opcode(name)) + except KeyError: + pass + return s + +# Opcodes that leave the code object. +OPS_CODE_END = _opcode_set('RETURN_VALUE') + +# Opcodes that unconditionally end the code chunk. +OPS_CHUNK_END = _opcode_set( + 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'RETURN_VALUE', 'RAISE_VARARGS', + 'BREAK_LOOP', 'CONTINUE_LOOP', + ) + +# Opcodes that unconditionally begin a new code chunk. By starting new chunks +# with unconditional jump instructions, we neatly deal with jumps to jumps +# properly. +OPS_CHUNK_BEGIN = _opcode_set('JUMP_ABSOLUTE', 'JUMP_FORWARD') + +# Opcodes that push a block on the block stack. +OPS_PUSH_BLOCK = _opcode_set( + 'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_WITH' + ) + +# Block types for exception handling. +OPS_EXCEPT_BLOCKS = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') + +# Opcodes that pop a block from the block stack. +OPS_POP_BLOCK = _opcode_set('POP_BLOCK') + +# Opcodes that have a jump destination, but aren't really a jump. +OPS_NO_JUMP = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') + +# Individual opcodes we need below. +OP_BREAK_LOOP = _opcode('BREAK_LOOP') +OP_END_FINALLY = _opcode('END_FINALLY') +OP_COMPARE_OP = _opcode('COMPARE_OP') +COMPARE_EXCEPTION = 10 # just have to get this const from the code. +OP_LOAD_CONST = _opcode('LOAD_CONST') +OP_RETURN_VALUE = _opcode('RETURN_VALUE') + + +class ByteParser(object): + """Parse byte codes to understand the structure of code.""" + + def __init__(self, code=None, text=None, filename=None): + if code: + self.code = code + else: + if not text: + assert filename, "If no code or text, need a filename" + sourcef = open(filename, 'rU') + text = sourcef.read() + sourcef.close() + + try: + # Python 2.3 and 2.4 don't like partial last lines, so be sure + # the text ends nicely for them. + self.code = compile(text + '\n', filename, "exec") + except SyntaxError: + _, synerr, _ = sys.exc_info() + raise CoverageException( + "Couldn't parse '%s' as Python source: '%s' at line %d" % + (filename, synerr.msg, synerr.lineno) + ) + + # Alternative Python implementations don't always provide all the + # attributes on code objects that we need to do the analysis. + for attr in ['co_lnotab', 'co_firstlineno', 'co_consts', 'co_code']: + if not hasattr(self.code, attr): + raise CoverageException( + "This implementation of Python doesn't support code " + "analysis.\n" + "Run coverage.py under CPython for this command." + ) + + def child_parsers(self): + """Iterate over all the code objects nested within this one. + + The iteration includes `self` as its first value. + + """ + return map(lambda c: ByteParser(code=c), CodeObjects(self.code)) + + # Getting numbers from the lnotab value changed in Py3.0. + if sys.version_info >= (3, 0): + def _lnotab_increments(self, lnotab): + """Return a list of ints from the lnotab bytes in 3.x""" + return list(lnotab) + else: + def _lnotab_increments(self, lnotab): + """Return a list of ints from the lnotab string in 2.x""" + return [ord(c) for c in lnotab] + + def _bytes_lines(self): + """Map byte offsets to line numbers in `code`. + + Uses co_lnotab described in Python/compile.c to map byte offsets to + line numbers. Returns a list: [(b0, l0), (b1, l1), ...] + + """ + # Adapted from dis.py in the standard library. + byte_increments = self._lnotab_increments(self.code.co_lnotab[0::2]) + line_increments = self._lnotab_increments(self.code.co_lnotab[1::2]) + + bytes_lines = [] + last_line_num = None + line_num = self.code.co_firstlineno + byte_num = 0 + for byte_incr, line_incr in zip(byte_increments, line_increments): + if byte_incr: + if line_num != last_line_num: + bytes_lines.append((byte_num, line_num)) + last_line_num = line_num + byte_num += byte_incr + line_num += line_incr + if line_num != last_line_num: + bytes_lines.append((byte_num, line_num)) + return bytes_lines + + def _find_statements(self): + """Find the statements in `self.code`. + + Return a set of line numbers that start statements. Recurses into all + code objects reachable from `self.code`. + + """ + stmts = set() + for bp in self.child_parsers(): + # Get all of the lineno information from this code. + for _, l in bp._bytes_lines(): + stmts.add(l) + return stmts + + def _disassemble(self): # pragma: no cover + """Disassemble code, for ad-hoc experimenting.""" + + import dis + + for bp in self.child_parsers(): + print("\n%s: " % bp.code) + dis.dis(bp.code) + print("Bytes lines: %r" % bp._bytes_lines()) + + print("") + + def _split_into_chunks(self): + """Split the code object into a list of `Chunk` objects. + + Each chunk is only entered at its first instruction, though there can + be many exits from a chunk. + + Returns a list of `Chunk` objects. + + """ + + # The list of chunks so far, and the one we're working on. + chunks = [] + chunk = None + bytes_lines_map = dict(self._bytes_lines()) + + # The block stack: loops and try blocks get pushed here for the + # implicit jumps that can occur. + # Each entry is a tuple: (block type, destination) + block_stack = [] + + # Some op codes are followed by branches that should be ignored. This + # is a count of how many ignores are left. + ignore_branch = 0 + + # We have to handle the last two bytecodes specially. + ult = penult = None + + for bc in ByteCodes(self.code.co_code): + # Maybe have to start a new chunk + if bc.offset in bytes_lines_map: + # Start a new chunk for each source line number. + if chunk: + chunk.exits.add(bc.offset) + chunk = Chunk(bc.offset, bytes_lines_map[bc.offset]) + chunks.append(chunk) + elif bc.op in OPS_CHUNK_BEGIN: + # Jumps deserve their own unnumbered chunk. This fixes + # problems with jumps to jumps getting confused. + if chunk: + chunk.exits.add(bc.offset) + chunk = Chunk(bc.offset) + chunks.append(chunk) + + if not chunk: + chunk = Chunk(bc.offset) + chunks.append(chunk) + + # Look at the opcode + if bc.jump_to >= 0 and bc.op not in OPS_NO_JUMP: + if ignore_branch: + # Someone earlier wanted us to ignore this branch. + ignore_branch -= 1 + else: + # The opcode has a jump, it's an exit for this chunk. + chunk.exits.add(bc.jump_to) + + if bc.op in OPS_CODE_END: + # The opcode can exit the code object. + chunk.exits.add(-self.code.co_firstlineno) + if bc.op in OPS_PUSH_BLOCK: + # The opcode adds a block to the block_stack. + block_stack.append((bc.op, bc.jump_to)) + if bc.op in OPS_POP_BLOCK: + # The opcode pops a block from the block stack. + block_stack.pop() + if bc.op in OPS_CHUNK_END: + # This opcode forces the end of the chunk. + if bc.op == OP_BREAK_LOOP: + # A break is implicit: jump where the top of the + # block_stack points. + chunk.exits.add(block_stack[-1][1]) + chunk = None + if bc.op == OP_END_FINALLY: + if block_stack: + # A break that goes through a finally will jump to whatever + # block is on top of the stack. + chunk.exits.add(block_stack[-1][1]) + # For the finally clause we need to find the closest exception + # block, and use its jump target as an exit. + for iblock in range(len(block_stack)-1, -1, -1): + if block_stack[iblock][0] in OPS_EXCEPT_BLOCKS: + chunk.exits.add(block_stack[iblock][1]) + break + if bc.op == OP_COMPARE_OP and bc.arg == COMPARE_EXCEPTION: + # This is an except clause. We want to overlook the next + # branch, so that except's don't count as branches. + ignore_branch += 1 + + penult = ult + ult = bc + + if chunks: + # The last two bytecodes could be a dummy "return None" that + # shouldn't be counted as real code. Every Python code object seems + # to end with a return, and a "return None" is inserted if there + # isn't an explicit return in the source. + if ult and penult: + if penult.op == OP_LOAD_CONST and ult.op == OP_RETURN_VALUE: + if self.code.co_consts[penult.arg] is None: + # This is "return None", but is it dummy? A real line + # would be a last chunk all by itself. + if chunks[-1].byte != penult.offset: + ex = -self.code.co_firstlineno + # Split the last chunk + last_chunk = chunks[-1] + last_chunk.exits.remove(ex) + last_chunk.exits.add(penult.offset) + chunk = Chunk(penult.offset) + chunk.exits.add(ex) + chunks.append(chunk) + + # Give all the chunks a length. + chunks[-1].length = bc.next_offset - chunks[-1].byte + for i in range(len(chunks)-1): + chunks[i].length = chunks[i+1].byte - chunks[i].byte + + return chunks + + def _arcs(self): + """Find the executable arcs in the code. + + Returns a set of pairs, (from,to). From and to are integer line + numbers. If from is < 0, then the arc is an entrance into the code + object. If to is < 0, the arc is an exit from the code object. + + """ + chunks = self._split_into_chunks() + + # A map from byte offsets to chunks jumped into. + byte_chunks = dict([(c.byte, c) for c in chunks]) + + # Build a map from byte offsets to actual lines reached. + byte_lines = {} + bytes_to_add = set([c.byte for c in chunks]) + + while bytes_to_add: + byte_to_add = bytes_to_add.pop() + if byte_to_add in byte_lines or byte_to_add < 0: + continue + + # Which lines does this chunk lead to? + bytes_considered = set() + bytes_to_consider = [byte_to_add] + lines = set() + + while bytes_to_consider: + byte = bytes_to_consider.pop() + bytes_considered.add(byte) + + # Find chunk for byte + try: + ch = byte_chunks[byte] + except KeyError: + for ch in chunks: + if ch.byte <= byte < ch.byte+ch.length: + break + else: + # No chunk for this byte! + raise Exception("Couldn't find chunk @ %d" % byte) + byte_chunks[byte] = ch + + if ch.line: + lines.add(ch.line) + else: + for ex in ch.exits: + if ex < 0: + lines.add(ex) + elif ex not in bytes_considered: + bytes_to_consider.append(ex) + + bytes_to_add.update(ch.exits) + + byte_lines[byte_to_add] = lines + + # Figure out for each chunk where the exits go. + arcs = set() + for chunk in chunks: + if chunk.line: + for ex in chunk.exits: + if ex < 0: + exit_lines = [ex] + else: + exit_lines = byte_lines[ex] + for exit_line in exit_lines: + if chunk.line != exit_line: + arcs.add((chunk.line, exit_line)) + for line in byte_lines[0]: + arcs.add((-1, line)) + + return arcs + + def _all_chunks(self): + """Returns a list of `Chunk` objects for this code and its children. + + See `_split_into_chunks` for details. + + """ + chunks = [] + for bp in self.child_parsers(): + chunks.extend(bp._split_into_chunks()) + + return chunks + + def _all_arcs(self): + """Get the set of all arcs in this code object and its children. + + See `_arcs` for details. + + """ + arcs = set() + for bp in self.child_parsers(): + arcs.update(bp._arcs()) + + return arcs + + +class Chunk(object): + """A sequence of bytecodes with a single entrance. + + To analyze byte code, we have to divide it into chunks, sequences of byte + codes such that each basic block has only one entrance, the first + instruction in the block. + + This is almost the CS concept of `basic block`_, except that we're willing + to have many exits from a chunk, and "basic block" is a more cumbersome + term. + + .. _basic block: http://en.wikipedia.org/wiki/Basic_block + + An exit < 0 means the chunk can leave the code (return). The exit is + the negative of the starting line number of the code block. + + """ + def __init__(self, byte, line=0): + self.byte = byte + self.line = line + self.length = 0 + self.exits = set() + + def __repr__(self): + return "<%d+%d @%d %r>" % ( + self.byte, self.length, self.line, list(self.exits) + ) + + +class AdHocMain(object): # pragma: no cover + """An ad-hoc main for code parsing experiments.""" + + def main(self, args): + """A main function for trying the code from the command line.""" + + from optparse import OptionParser + + parser = OptionParser() + parser.add_option( + "-c", action="store_true", dest="chunks", + help="Show basic block chunks" + ) + parser.add_option( + "-d", action="store_true", dest="dis", + help="Disassemble" + ) + parser.add_option( + "-R", action="store_true", dest="recursive", + help="Recurse to find source files" + ) + parser.add_option( + "-s", action="store_true", dest="source", + help="Show analyzed source" + ) + parser.add_option( + "-t", action="store_true", dest="tokens", + help="Show tokens" + ) + + options, args = parser.parse_args() + if options.recursive: + if args: + root = args[0] + else: + root = "." + for root, _, _ in os.walk(root): + for f in glob.glob(root + "/*.py"): + self.adhoc_one_file(options, f) + else: + self.adhoc_one_file(options, args[0]) + + def adhoc_one_file(self, options, filename): + """Process just one file.""" + + if options.dis or options.chunks: + try: + bp = ByteParser(filename=filename) + except CoverageException: + _, err, _ = sys.exc_info() + print("%s" % (err,)) + return + + if options.dis: + print("Main code:") + bp._disassemble() + + if options.chunks: + chunks = bp._all_chunks() + if options.recursive: + print("%6d: %s" % (len(chunks), filename)) + else: + print("Chunks: %r" % chunks) + arcs = bp._all_arcs() + print("Arcs: %r" % sorted(arcs)) + + if options.source or options.tokens: + cp = CodeParser(filename=filename, exclude=r"no\s*cover") + cp.show_tokens = options.tokens + cp._raw_parse() + + if options.source: + if options.chunks: + arc_width, arc_chars = self.arc_ascii_art(arcs) + else: + arc_width, arc_chars = 0, {} + + exit_counts = cp.exit_counts() + + for i, ltext in enumerate(cp.lines): + lineno = i+1 + m0 = m1 = m2 = m3 = a = ' ' + if lineno in cp.statement_starts: + m0 = '-' + exits = exit_counts.get(lineno, 0) + if exits > 1: + m1 = str(exits) + if lineno in cp.docstrings: + m2 = '"' + if lineno in cp.classdefs: + m2 = 'C' + if lineno in cp.excluded: + m3 = 'x' + a = arc_chars.get(lineno, '').ljust(arc_width) + print("%4d %s%s%s%s%s %s" % + (lineno, m0, m1, m2, m3, a, ltext) + ) + + def arc_ascii_art(self, arcs): + """Draw arcs as ascii art. + + Returns a width of characters needed to draw all the arcs, and a + dictionary mapping line numbers to ascii strings to draw for that line. + + """ + arc_chars = {} + for lfrom, lto in sorted(arcs): + if lfrom < 0: + arc_chars[lto] = arc_chars.get(lto, '') + 'v' + elif lto < 0: + arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^' + else: + if lfrom == lto - 1: + # Don't show obvious arcs. + continue + if lfrom < lto: + l1, l2 = lfrom, lto + else: + l1, l2 = lto, lfrom + w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)]) + for l in range(l1, l2+1): + if l == lfrom: + ch = '<' + elif l == lto: + ch = '>' + else: + ch = '|' + arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch + arc_width = 0 + + if arc_chars: + arc_width = max([len(a) for a in arc_chars.values()]) + else: + arc_width = 0 + + return arc_width, arc_chars + +if __name__ == '__main__': + AdHocMain().main(sys.argv[1:]) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py new file mode 100644 index 00000000..60b87932 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py @@ -0,0 +1,108 @@ +"""Better tokenizing for coverage.py.""" + +import keyword, re, token, tokenize +from coverage.backward import StringIO # pylint: disable-msg=W0622 + +def phys_tokens(toks): + """Return all physical tokens, even line continuations. + + tokenize.generate_tokens() doesn't return a token for the backslash that + continues lines. This wrapper provides those tokens so that we can + re-create a faithful representation of the original source. + + Returns the same values as generate_tokens() + + """ + last_line = None + last_lineno = -1 + last_ttype = None + for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: + if last_lineno != elineno: + if last_line and last_line[-2:] == "\\\n": + # We are at the beginning of a new line, and the last line + # ended with a backslash. We probably have to inject a + # backslash token into the stream. Unfortunately, there's more + # to figure out. This code:: + # + # usage = """\ + # HEY THERE + # """ + # + # triggers this condition, but the token text is:: + # + # '"""\\\nHEY THERE\n"""' + # + # so we need to figure out if the backslash is already in the + # string token or not. + inject_backslash = True + if last_ttype == tokenize.COMMENT: + # Comments like this \ + # should never result in a new token. + inject_backslash = False + elif ttype == token.STRING: + if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\': + # It's a multiline string and the first line ends with + # a backslash, so we don't need to inject another. + inject_backslash = False + if inject_backslash: + # Figure out what column the backslash is in. + ccol = len(last_line.split("\n")[-2]) - 1 + # Yield the token, with a fake token type. + yield ( + 99999, "\\\n", + (slineno, ccol), (slineno, ccol+2), + last_line + ) + last_line = ltext + last_ttype = ttype + yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext + last_lineno = elineno + + +def source_token_lines(source): + """Generate a series of lines, one for each line in `source`. + + Each line is a list of pairs, each pair is a token:: + + [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ] + + Each pair has a token class, and the token text. + + If you concatenate all the token texts, and then join them with newlines, + you should have your original `source` back, with two differences: + trailing whitespace is not preserved, and a final line with no newline + is indistinguishable from a final line with a newline. + + """ + ws_tokens = [token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL] + line = [] + col = 0 + source = source.expandtabs(8).replace('\r\n', '\n') + tokgen = tokenize.generate_tokens(StringIO(source).readline) + for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen): + mark_start = True + for part in re.split('(\n)', ttext): + if part == '\n': + yield line + line = [] + col = 0 + mark_end = False + elif part == '': + mark_end = False + elif ttype in ws_tokens: + mark_end = False + else: + if mark_start and scol > col: + line.append(("ws", " " * (scol - col))) + mark_start = False + tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3] + if ttype == token.NAME and keyword.iskeyword(ttext): + tok_class = "key" + line.append((tok_class, part)) + mark_end = True + scol = 0 + if mark_end: + col = ecol + + if line: + yield line diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py new file mode 100644 index 00000000..0fb353a2 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py @@ -0,0 +1,83 @@ +"""Reporter foundation for Coverage.""" + +import fnmatch, os +from coverage.codeunit import code_unit_factory +from coverage.misc import CoverageException, NoSource + +class Reporter(object): + """A base class for all reporters.""" + + def __init__(self, coverage, ignore_errors=False): + """Create a reporter. + + `coverage` is the coverage instance. `ignore_errors` controls how + skittish the reporter will be during file processing. + + """ + self.coverage = coverage + self.ignore_errors = ignore_errors + + # The code units to report on. Set by find_code_units. + self.code_units = [] + + # The directory into which to place the report, used by some derived + # classes. + self.directory = None + + def find_code_units(self, morfs, config): + """Find the code units we'll report on. + + `morfs` is a list of modules or filenames. `config` is a + CoverageConfig instance. + + """ + morfs = morfs or self.coverage.data.measured_files() + file_locator = self.coverage.file_locator + self.code_units = code_unit_factory(morfs, file_locator) + + if config.include: + patterns = [file_locator.abs_file(p) for p in config.include] + filtered = [] + for cu in self.code_units: + for pattern in patterns: + if fnmatch.fnmatch(cu.filename, pattern): + filtered.append(cu) + break + self.code_units = filtered + + if config.omit: + patterns = [file_locator.abs_file(p) for p in config.omit] + filtered = [] + for cu in self.code_units: + for pattern in patterns: + if fnmatch.fnmatch(cu.filename, pattern): + break + else: + filtered.append(cu) + self.code_units = filtered + + self.code_units.sort() + + def report_files(self, report_fn, morfs, config, directory=None): + """Run a reporting function on a number of morfs. + + `report_fn` is called for each relative morf in `morfs`. + + `config` is a CoverageConfig instance. + + """ + self.find_code_units(morfs, config) + + if not self.code_units: + raise CoverageException("No data to report.") + + self.directory = directory + if self.directory and not os.path.exists(self.directory): + os.makedirs(self.directory) + + for cu in self.code_units: + try: + report_fn(cu, self.coverage._analyze(cu)) + except NoSource: + if not self.ignore_errors: + raise diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py new file mode 100644 index 00000000..85071fe3 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py @@ -0,0 +1,233 @@ +"""Results of coverage measurement.""" + +import os + +from coverage.backward import set, sorted # pylint: disable-msg=W0622 +from coverage.misc import format_lines, NoSource +from coverage.parser import CodeParser + + +class Analysis(object): + """The results of analyzing a code unit.""" + + def __init__(self, cov, code_unit): + self.coverage = cov + self.code_unit = code_unit + + self.filename = self.code_unit.filename + ext = os.path.splitext(self.filename)[1] + source = None + if ext == '.py': + if not os.path.exists(self.filename): + source = self.coverage.file_locator.get_zip_data(self.filename) + if not source: + raise NoSource("No source for code: %r" % self.filename) + + self.parser = CodeParser( + text=source, filename=self.filename, + exclude=self.coverage.exclude_re + ) + self.statements, self.excluded = self.parser.parse_source() + + # Identify missing statements. + executed = self.coverage.data.executed_lines(self.filename) + exec1 = self.parser.first_lines(executed) + self.missing = sorted(set(self.statements) - set(exec1)) + + if self.coverage.data.has_arcs(): + n_branches = self.total_branches() + mba = self.missing_branch_arcs() + n_missing_branches = sum([len(v) for v in mba.values()]) + else: + n_branches = n_missing_branches = 0 + + self.numbers = Numbers( + n_files=1, + n_statements=len(self.statements), + n_excluded=len(self.excluded), + n_missing=len(self.missing), + n_branches=n_branches, + n_missing_branches=n_missing_branches, + ) + + def missing_formatted(self): + """The missing line numbers, formatted nicely. + + Returns a string like "1-2, 5-11, 13-14". + + """ + return format_lines(self.statements, self.missing) + + def has_arcs(self): + """Were arcs measured in this result?""" + return self.coverage.data.has_arcs() + + def arc_possibilities(self): + """Returns a sorted list of the arcs in the code.""" + return self.parser.arcs() + + def arcs_executed(self): + """Returns a sorted list of the arcs actually executed in the code.""" + executed = self.coverage.data.executed_arcs(self.filename) + m2fl = self.parser.first_line + executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed] + return sorted(executed) + + def arcs_missing(self): + """Returns a sorted list of the arcs in the code not executed.""" + possible = self.arc_possibilities() + executed = self.arcs_executed() + missing = [p for p in possible if p not in executed] + return sorted(missing) + + def arcs_unpredicted(self): + """Returns a sorted list of the executed arcs missing from the code.""" + possible = self.arc_possibilities() + executed = self.arcs_executed() + # Exclude arcs here which connect a line to itself. They can occur + # in executed data in some cases. This is where they can cause + # trouble, and here is where it's the least burden to remove them. + unpredicted = [ + e for e in executed + if e not in possible and e[0] != e[1] + ] + return sorted(unpredicted) + + def branch_lines(self): + """Returns a list of line numbers that have more than one exit.""" + exit_counts = self.parser.exit_counts() + return [l1 for l1,count in exit_counts.items() if count > 1] + + def total_branches(self): + """How many total branches are there?""" + exit_counts = self.parser.exit_counts() + return sum([count for count in exit_counts.values() if count > 1]) + + def missing_branch_arcs(self): + """Return arcs that weren't executed from branch lines. + + Returns {l1:[l2a,l2b,...], ...} + + """ + missing = self.arcs_missing() + branch_lines = set(self.branch_lines()) + mba = {} + for l1, l2 in missing: + if l1 in branch_lines: + if l1 not in mba: + mba[l1] = [] + mba[l1].append(l2) + return mba + + def branch_stats(self): + """Get stats about branches. + + Returns a dict mapping line numbers to a tuple: + (total_exits, taken_exits). + """ + + exit_counts = self.parser.exit_counts() + missing_arcs = self.missing_branch_arcs() + stats = {} + for lnum in self.branch_lines(): + exits = exit_counts[lnum] + try: + missing = len(missing_arcs[lnum]) + except KeyError: + missing = 0 + stats[lnum] = (exits, exits - missing) + return stats + + +class Numbers(object): + """The numerical results of measuring coverage. + + This holds the basic statistics from `Analysis`, and is used to roll + up statistics across files. + + """ + # A global to determine the precision on coverage percentages, the number + # of decimal places. + _precision = 0 + _near0 = 1.0 # These will change when _precision is changed. + _near100 = 99.0 + + def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0, + n_branches=0, n_missing_branches=0 + ): + self.n_files = n_files + self.n_statements = n_statements + self.n_excluded = n_excluded + self.n_missing = n_missing + self.n_branches = n_branches + self.n_missing_branches = n_missing_branches + + def set_precision(cls, precision): + """Set the number of decimal places used to report percentages.""" + assert 0 <= precision < 10 + cls._precision = precision + cls._near0 = 1.0 / 10**precision + cls._near100 = 100.0 - cls._near0 + set_precision = classmethod(set_precision) + + def _get_n_executed(self): + """Returns the number of executed statements.""" + return self.n_statements - self.n_missing + n_executed = property(_get_n_executed) + + def _get_n_executed_branches(self): + """Returns the number of executed branches.""" + return self.n_branches - self.n_missing_branches + n_executed_branches = property(_get_n_executed_branches) + + def _get_pc_covered(self): + """Returns a single percentage value for coverage.""" + if self.n_statements > 0: + pc_cov = (100.0 * (self.n_executed + self.n_executed_branches) / + (self.n_statements + self.n_branches)) + else: + pc_cov = 100.0 + return pc_cov + pc_covered = property(_get_pc_covered) + + def _get_pc_covered_str(self): + """Returns the percent covered, as a string, without a percent sign. + + The important thing here is that "0" only be returned when it's truly + zero, and "100" only be returned when it's truly 100. + + """ + pc = self.pc_covered + if 0 < pc < self._near0: + pc = self._near0 + elif self._near100 < pc < 100: + pc = self._near100 + else: + pc = round(pc, self._precision) + return "%.*f" % (self._precision, pc) + pc_covered_str = property(_get_pc_covered_str) + + def pc_str_width(cls): + """How many characters wide can pc_covered_str be?""" + width = 3 # "100" + if cls._precision > 0: + width += 1 + cls._precision + return width + pc_str_width = classmethod(pc_str_width) + + def __add__(self, other): + nums = Numbers() + nums.n_files = self.n_files + other.n_files + nums.n_statements = self.n_statements + other.n_statements + nums.n_excluded = self.n_excluded + other.n_excluded + nums.n_missing = self.n_missing + other.n_missing + nums.n_branches = self.n_branches + other.n_branches + nums.n_missing_branches = (self.n_missing_branches + + other.n_missing_branches) + return nums + + def __radd__(self, other): + # Implementing 0+Numbers allows us to sum() a list of Numbers. + if other == 0: + return self + raise NotImplemented diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py new file mode 100644 index 00000000..599ae782 --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py @@ -0,0 +1,81 @@ +"""Summary reporting""" + +import sys + +from coverage.report import Reporter +from coverage.results import Numbers + + +class SummaryReporter(Reporter): + """A reporter for writing the summary report.""" + + def __init__(self, coverage, show_missing=True, ignore_errors=False): + super(SummaryReporter, self).__init__(coverage, ignore_errors) + self.show_missing = show_missing + self.branches = coverage.data.has_arcs() + + def report(self, morfs, outfile=None, config=None): + """Writes a report summarizing coverage statistics per module. + + `outfile` is a file object to write the summary to. `config` is a + CoverageConfig instance. + + """ + self.find_code_units(morfs, config) + + # Prepare the formatting strings + max_name = max([len(cu.name) for cu in self.code_units] + [5]) + fmt_name = "%%- %ds " % max_name + fmt_err = "%s %s: %s\n" + header = (fmt_name % "Name") + " Stmts Miss" + fmt_coverage = fmt_name + "%6d %6d" + if self.branches: + header += " Branch BrPart" + fmt_coverage += " %6d %6d" + width100 = Numbers.pc_str_width() + header += "%*s" % (width100+4, "Cover") + fmt_coverage += "%%%ds%%%%" % (width100+3,) + if self.show_missing: + header += " Missing" + fmt_coverage += " %s" + rule = "-" * len(header) + "\n" + header += "\n" + fmt_coverage += "\n" + + if not outfile: + outfile = sys.stdout + + # Write the header + outfile.write(header) + outfile.write(rule) + + total = Numbers() + + for cu in self.code_units: + try: + analysis = self.coverage._analyze(cu) + nums = analysis.numbers + args = (cu.name, nums.n_statements, nums.n_missing) + if self.branches: + args += (nums.n_branches, nums.n_missing_branches) + args += (nums.pc_covered_str,) + if self.show_missing: + args += (analysis.missing_formatted(),) + outfile.write(fmt_coverage % args) + total += nums + except KeyboardInterrupt: # pragma: no cover + raise + except: + if not self.ignore_errors: + typ, msg = sys.exc_info()[:2] + outfile.write(fmt_err % (cu.name, typ.__name__, msg)) + + if total.n_files > 1: + outfile.write(rule) + args = ("TOTAL", total.n_statements, total.n_missing) + if self.branches: + args += (total.n_branches, total.n_missing_branches) + args += (total.pc_covered_str,) + if self.show_missing: + args += ("",) + outfile.write(fmt_coverage % args) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py new file mode 100644 index 00000000..c39e061e --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py @@ -0,0 +1,166 @@ +"""A simple Python template renderer, for a nano-subset of Django syntax.""" + +# Coincidentally named the same as http://code.activestate.com/recipes/496702/ + +import re, sys + +class Templite(object): + """A simple template renderer, for a nano-subset of Django syntax. + + Supported constructs are extended variable access:: + + {{var.modifer.modifier|filter|filter}} + + loops:: + + {% for var in list %}...{% endfor %} + + and ifs:: + + {% if var %}...{% endif %} + + Comments are within curly-hash markers:: + + {# This will be ignored #} + + Construct a Templite with the template text, then use `render` against a + dictionary context to create a finished string. + + """ + def __init__(self, text, *contexts): + """Construct a Templite with the given `text`. + + `contexts` are dictionaries of values to use for future renderings. + These are good for filters and global values. + + """ + self.text = text + self.context = {} + for context in contexts: + self.context.update(context) + + # Split the text to form a list of tokens. + toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text) + + # Parse the tokens into a nested list of operations. Each item in the + # list is a tuple with an opcode, and arguments. They'll be + # interpreted by TempliteEngine. + # + # When parsing an action tag with nested content (if, for), the current + # ops list is pushed onto ops_stack, and the parsing continues in a new + # ops list that is part of the arguments to the if or for op. + ops = [] + ops_stack = [] + for tok in toks: + if tok.startswith('{{'): + # Expression: ('exp', expr) + ops.append(('exp', tok[2:-2].strip())) + elif tok.startswith('{#'): + # Comment: ignore it and move on. + continue + elif tok.startswith('{%'): + # Action tag: split into words and parse further. + words = tok[2:-2].strip().split() + if words[0] == 'if': + # If: ('if', (expr, body_ops)) + if_ops = [] + assert len(words) == 2 + ops.append(('if', (words[1], if_ops))) + ops_stack.append(ops) + ops = if_ops + elif words[0] == 'for': + # For: ('for', (varname, listexpr, body_ops)) + assert len(words) == 4 and words[2] == 'in' + for_ops = [] + ops.append(('for', (words[1], words[3], for_ops))) + ops_stack.append(ops) + ops = for_ops + elif words[0].startswith('end'): + # Endsomething. Pop the ops stack + ops = ops_stack.pop() + assert ops[-1][0] == words[0][3:] + else: + raise SyntaxError("Don't understand tag %r" % words) + else: + ops.append(('lit', tok)) + + assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0] + self.ops = ops + + def render(self, context=None): + """Render this template by applying it to `context`. + + `context` is a dictionary of values to use in this rendering. + + """ + # Make the complete context we'll use. + ctx = dict(self.context) + if context: + ctx.update(context) + + # Run it through an engine, and return the result. + engine = _TempliteEngine(ctx) + engine.execute(self.ops) + return "".join(engine.result) + + +class _TempliteEngine(object): + """Executes Templite objects to produce strings.""" + def __init__(self, context): + self.context = context + self.result = [] + + def execute(self, ops): + """Execute `ops` in the engine. + + Called recursively for the bodies of if's and loops. + + """ + for op, args in ops: + if op == 'lit': + self.result.append(args) + elif op == 'exp': + try: + self.result.append(str(self.evaluate(args))) + except: + exc_class, exc, _ = sys.exc_info() + new_exc = exc_class("Couldn't evaluate {{ %s }}: %s" + % (args, exc)) + raise new_exc + elif op == 'if': + expr, body = args + if self.evaluate(expr): + self.execute(body) + elif op == 'for': + var, lis, body = args + vals = self.evaluate(lis) + for val in vals: + self.context[var] = val + self.execute(body) + else: + raise AssertionError("TempliteEngine doesn't grok op %r" % op) + + def evaluate(self, expr): + """Evaluate an expression. + + `expr` can have pipes and dots to indicate data access and filtering. + + """ + if "|" in expr: + pipes = expr.split("|") + value = self.evaluate(pipes[0]) + for func in pipes[1:]: + value = self.evaluate(func)(value) + elif "." in expr: + dots = expr.split('.') + value = self.evaluate(dots[0]) + for dot in dots[1:]: + try: + value = getattr(value, dot) + except AttributeError: + value = value[dot] + if hasattr(value, '__call__'): + value = value() + else: + value = self.context[expr] + return value diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py new file mode 100644 index 00000000..a6f2aa9e --- /dev/null +++ b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'tracer.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so new file mode 100755 index 0000000000000000000000000000000000000000..9f34cf3ec1dba6d24b9b7c334a962adc87f58cc6 GIT binary patch literal 15256 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 literal 0 HcmV?d00001 diff --git a/files.txt b/files.txt new file mode 100644 index 00000000..80e76291 --- /dev/null +++ b/files.txt @@ -0,0 +1 @@ +/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/oauth2-1.5.170-py2.7.egg diff --git a/mock-0.7.0-py2.7.egg b/mock-0.7.0-py2.7.egg new file mode 100644 index 0000000000000000000000000000000000000000..a96ba5a17e8f537853458472c33bdd5608cd2cf7 GIT binary patch literal 24549 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 literal 0 HcmV?d00001 diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..3b9b3dcf 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -648,7 +648,7 @@ def request(self, uri, method="GET", body='', headers=None, DEFAULT_POST_CONTENT_TYPE) is_form_encoded = \ - headers.get('Content-Type') == 'application/x-www-form-urlencoded' + ((headers.get('Content-Type') == 'application/x-www-form-urlencoded') and (method == "POST")) if is_form_encoded and body: parameters = parse_qs(body) From e4d4ab946c24e1f3cb748a373856b6bae5623258 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Mon, 9 May 2011 23:21:32 -0400 Subject: [PATCH 2/2] Modified PUT implementation so that it conforms to the OAuth spec. Fixes #70 --- build/lib/oauth2/__init__.py | 860 ----------- build/lib/oauth2/_version.py | 18 - build/lib/oauth2/clients/__init__.py | 0 build/lib/oauth2/clients/imap.py | 40 - build/lib/oauth2/clients/smtp.py | 41 - build/lib/tests/__init__.py | 0 build/lib/tests/test_oauth.py | 1309 ----------------- .../EGG-INFO/PKG-INFO | 35 - .../EGG-INFO/SOURCES.txt | 43 - .../EGG-INFO/dependency_links.txt | 1 - .../EGG-INFO/entry_points.txt | 3 - .../EGG-INFO/native_libs.txt | 1 - .../EGG-INFO/not-zip-safe | 1 - .../EGG-INFO/top_level.txt | 1 - .../coverage/__init__.py | 88 -- .../coverage/__main__.py | 3 - .../coverage/annotate.py | 101 -- .../coverage/backward.py | 73 - .../coverage/bytecode.py | 81 - .../coverage/cmdline.py | 661 --------- .../coverage/codeunit.py | 117 -- .../coverage/collector.py | 292 ---- .../coverage/config.py | 118 -- .../coverage/control.py | 624 -------- .../coverage/data.py | 261 ---- .../coverage/execfile.py | 72 - .../coverage/files.py | 132 -- .../coverage/html.py | 183 --- .../coverage/htmlfiles/coverage_html.js | 87 -- .../coverage/htmlfiles/index.html | 81 - .../coverage/htmlfiles/jquery-1.3.2.min.js | 19 - .../htmlfiles/jquery.tablesorter.min.js | 2 - .../coverage/htmlfiles/pyfile.html | 61 - .../coverage/htmlfiles/style.css | 230 --- .../coverage/misc.py | 85 -- .../coverage/parser.py | 783 ---------- .../coverage/phystokens.py | 108 -- .../coverage/report.py | 83 -- .../coverage/results.py | 233 --- .../coverage/summary.py | 81 - .../coverage/templite.py | 166 --- .../coverage/tracer.py | 7 - .../coverage/tracer.so | Bin 15256 -> 0 bytes .../coverage/xmlreport.py | 147 -- dist/oauth2-1.5.170-py2.7.egg | Bin 52228 -> 0 bytes files.txt | 1 - mock-0.7.0-py2.7.egg | Bin 24549 -> 0 bytes 47 files changed, 7333 deletions(-) delete mode 100644 build/lib/oauth2/__init__.py delete mode 100644 build/lib/oauth2/_version.py delete mode 100644 build/lib/oauth2/clients/__init__.py delete mode 100644 build/lib/oauth2/clients/imap.py delete mode 100644 build/lib/oauth2/clients/smtp.py delete mode 100644 build/lib/tests/__init__.py delete mode 100644 build/lib/tests/test_oauth.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/coverage_html.js delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/pyfile.html delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py delete mode 100755 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so delete mode 100644 coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/xmlreport.py delete mode 100644 dist/oauth2-1.5.170-py2.7.egg delete mode 100644 files.txt delete mode 100644 mock-0.7.0-py2.7.egg diff --git a/build/lib/oauth2/__init__.py b/build/lib/oauth2/__init__.py deleted file mode 100644 index 3b9b3dcf..00000000 --- a/build/lib/oauth2/__init__.py +++ /dev/null @@ -1,860 +0,0 @@ -""" -The MIT License - -Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import base64 -import urllib -import time -import random -import urlparse -import hmac -import binascii -import httplib2 - -try: - from urlparse import parse_qs - parse_qs # placate pyflakes -except ImportError: - # fall back for Python 2.5 - from cgi import parse_qs - -try: - from hashlib import sha1 - sha = sha1 -except ImportError: - # hashlib was added in Python 2.5 - import sha - -import _version - -__version__ = _version.__version__ - -OAUTH_VERSION = '1.0' # Hi Blaine! -HTTP_METHOD = 'GET' -SIGNATURE_METHOD = 'PLAINTEXT' - - -class Error(RuntimeError): - """Generic exception class.""" - - def __init__(self, message='OAuth error occurred.'): - self._message = message - - @property - def message(self): - """A hack to get around the deprecation errors in 2.6.""" - return self._message - - def __str__(self): - return self._message - - -class MissingSignature(Error): - pass - - -def build_authenticate_header(realm=''): - """Optional WWW-Authenticate header (401 error)""" - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - - -def build_xoauth_string(url, consumer, token=None): - """Build an XOAUTH string for use in SMTP/IMPA authentication.""" - request = Request.from_consumer_and_token(consumer, token, - "GET", url) - - signing_method = SignatureMethod_HMAC_SHA1() - request.sign_request(signing_method, consumer, token) - - params = [] - for k, v in sorted(request.iteritems()): - if v is not None: - params.append('%s="%s"' % (k, escape(v))) - - return "%s %s %s" % ("GET", url, ','.join(params)) - - -def to_unicode(s): - """ Convert to unicode, raise exception with instructive error - message if s is not unicode, ascii, or utf-8. """ - if not isinstance(s, unicode): - if not isinstance(s, str): - raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s)) - try: - s = s.decode('utf-8') - except UnicodeDecodeError, le: - raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,)) - return s - -def to_utf8(s): - return to_unicode(s).encode('utf-8') - -def to_unicode_if_string(s): - if isinstance(s, basestring): - return to_unicode(s) - else: - return s - -def to_utf8_if_string(s): - if isinstance(s, basestring): - return to_utf8(s) - else: - return s - -def to_unicode_optional_iterator(x): - """ - Raise TypeError if x is a str containing non-utf8 bytes or if x is - an iterable which contains such a str. - """ - if isinstance(x, basestring): - return to_unicode(x) - - try: - l = list(x) - except TypeError, e: - assert 'is not iterable' in str(e) - return x - else: - return [ to_unicode(e) for e in l ] - -def to_utf8_optional_iterator(x): - """ - Raise TypeError if x is a str or if x is an iterable which - contains a str. - """ - if isinstance(x, basestring): - return to_utf8(x) - - try: - l = list(x) - except TypeError, e: - assert 'is not iterable' in str(e) - return x - else: - return [ to_utf8_if_string(e) for e in l ] - -def escape(s): - """Escape a URL including any /.""" - return urllib.quote(s.encode('utf-8'), safe='~') - -def generate_timestamp(): - """Get seconds since epoch (UTC).""" - return int(time.time()) - - -def generate_nonce(length=8): - """Generate pseudorandom number.""" - return ''.join([str(random.randint(0, 9)) for i in range(length)]) - - -def generate_verifier(length=8): - """Generate pseudorandom number.""" - return ''.join([str(random.randint(0, 9)) for i in range(length)]) - - -class Consumer(object): - """A consumer of OAuth-protected services. - - The OAuth consumer is a "third-party" service that wants to access - protected resources from an OAuth service provider on behalf of an end - user. It's kind of the OAuth client. - - Usually a consumer must be registered with the service provider by the - developer of the consumer software. As part of that process, the service - provider gives the consumer a *key* and a *secret* with which the consumer - software can identify itself to the service. The consumer will include its - key in each request to identify itself, but will use its secret only when - signing requests, to prove that the request is from that particular - registered consumer. - - Once registered, the consumer can then use its consumer credentials to ask - the service provider for a request token, kicking off the OAuth - authorization process. - """ - - key = None - secret = None - - def __init__(self, key, secret): - self.key = key - self.secret = secret - - if self.key is None or self.secret is None: - raise ValueError("Key and secret must be set.") - - def __str__(self): - data = {'oauth_consumer_key': self.key, - 'oauth_consumer_secret': self.secret} - - return urllib.urlencode(data) - - -class Token(object): - """An OAuth credential used to request authorization or a protected - resource. - - Tokens in OAuth comprise a *key* and a *secret*. The key is included in - requests to identify the token being used, but the secret is used only in - the signature, to prove that the requester is who the server gave the - token to. - - When first negotiating the authorization, the consumer asks for a *request - token* that the live user authorizes with the service provider. The - consumer then exchanges the request token for an *access token* that can - be used to access protected resources. - """ - - key = None - secret = None - callback = None - callback_confirmed = None - verifier = None - - def __init__(self, key, secret): - self.key = key - self.secret = secret - - if self.key is None or self.secret is None: - raise ValueError("Key and secret must be set.") - - def set_callback(self, callback): - self.callback = callback - self.callback_confirmed = 'true' - - def set_verifier(self, verifier=None): - if verifier is not None: - self.verifier = verifier - else: - self.verifier = generate_verifier() - - def get_callback_url(self): - if self.callback and self.verifier: - # Append the oauth_verifier. - parts = urlparse.urlparse(self.callback) - scheme, netloc, path, params, query, fragment = parts[:6] - if query: - query = '%s&oauth_verifier=%s' % (query, self.verifier) - else: - query = 'oauth_verifier=%s' % self.verifier - return urlparse.urlunparse((scheme, netloc, path, params, - query, fragment)) - return self.callback - - def to_string(self): - """Returns this token as a plain string, suitable for storage. - - The resulting string includes the token's secret, so you should never - send or store this string where a third party can read it. - """ - - data = { - 'oauth_token': self.key, - 'oauth_token_secret': self.secret, - } - - if self.callback_confirmed is not None: - data['oauth_callback_confirmed'] = self.callback_confirmed - return urllib.urlencode(data) - - @staticmethod - def from_string(s): - """Deserializes a token from a string like one returned by - `to_string()`.""" - - if not len(s): - raise ValueError("Invalid parameter string.") - - params = parse_qs(s, keep_blank_values=False) - if not len(params): - raise ValueError("Invalid parameter string.") - - try: - key = params['oauth_token'][0] - except Exception: - raise ValueError("'oauth_token' not found in OAuth request.") - - try: - secret = params['oauth_token_secret'][0] - except Exception: - raise ValueError("'oauth_token_secret' not found in " - "OAuth request.") - - token = Token(key, secret) - try: - token.callback_confirmed = params['oauth_callback_confirmed'][0] - except KeyError: - pass # 1.0, no callback confirmed. - return token - - def __str__(self): - return self.to_string() - - -def setter(attr): - name = attr.__name__ - - def getter(self): - try: - return self.__dict__[name] - except KeyError: - raise AttributeError(name) - - def deleter(self): - del self.__dict__[name] - - return property(getter, attr, deleter) - - -class Request(dict): - - """The parameters and information for an HTTP request, suitable for - authorizing with OAuth credentials. - - When a consumer wants to access a service's protected resources, it does - so using a signed HTTP request identifying itself (the consumer) with its - key, and providing an access token authorized by the end user to access - those resources. - - """ - - version = OAUTH_VERSION - - def __init__(self, method=HTTP_METHOD, url=None, parameters=None, - body='', is_form_encoded=False): - if url is not None: - self.url = to_unicode(url) - self.method = method - if parameters is not None: - for k, v in parameters.iteritems(): - k = to_unicode(k) - v = to_unicode_optional_iterator(v) - self[k] = v - self.body = body - self.is_form_encoded = is_form_encoded - - - @setter - def url(self, value): - self.__dict__['url'] = value - if value is not None: - scheme, netloc, path, params, query, fragment = urlparse.urlparse(value) - - # Exclude default port numbers. - if scheme == 'http' and netloc[-3:] == ':80': - netloc = netloc[:-3] - elif scheme == 'https' and netloc[-4:] == ':443': - netloc = netloc[:-4] - if scheme not in ('http', 'https'): - raise ValueError("Unsupported URL %s (%s)." % (value, scheme)) - - # Normalized URL excludes params, query, and fragment. - self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None)) - else: - self.normalized_url = None - self.__dict__['url'] = None - - @setter - def method(self, value): - self.__dict__['method'] = value.upper() - - def _get_timestamp_nonce(self): - return self['oauth_timestamp'], self['oauth_nonce'] - - def get_nonoauth_parameters(self): - """Get any non-OAuth parameters.""" - return dict([(k, v) for k, v in self.iteritems() - if not k.startswith('oauth_')]) - - def to_header(self, realm=''): - """Serialize as a header for an HTTPAuth request.""" - oauth_params = ((k, v) for k, v in self.items() - if k.startswith('oauth_')) - stringy_params = ((k, escape(str(v))) for k, v in oauth_params) - header_params = ('%s="%s"' % (k, v) for k, v in stringy_params) - params_header = ', '.join(header_params) - - auth_header = 'OAuth realm="%s"' % realm - if params_header: - auth_header = "%s, %s" % (auth_header, params_header) - - return {'Authorization': auth_header} - - def to_postdata(self): - """Serialize as post data for a POST request.""" - d = {} - for k, v in self.iteritems(): - d[k.encode('utf-8')] = to_utf8_optional_iterator(v) - - # tell urlencode to deal with sequence values and map them correctly - # to resulting querystring. for example self["k"] = ["v1", "v2"] will - # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D - return urllib.urlencode(d, True).replace('+', '%20') - - def to_url(self): - """Serialize as a URL for a GET request.""" - base_url = urlparse.urlparse(self.url) - try: - query = base_url.query - except AttributeError: - # must be python <2.5 - query = base_url[4] - query = parse_qs(query) - for k, v in self.items(): - query.setdefault(k, []).append(v) - - try: - scheme = base_url.scheme - netloc = base_url.netloc - path = base_url.path - params = base_url.params - fragment = base_url.fragment - except AttributeError: - # must be python <2.5 - scheme = base_url[0] - netloc = base_url[1] - path = base_url[2] - params = base_url[3] - fragment = base_url[5] - - url = (scheme, netloc, path, params, - urllib.urlencode(query, True), fragment) - return urlparse.urlunparse(url) - - def get_parameter(self, parameter): - ret = self.get(parameter) - if ret is None: - raise Error('Parameter not found: %s' % parameter) - - return ret - - def get_normalized_parameters(self): - """Return a string that contains the parameters that must be signed.""" - items = [] - for key, value in self.iteritems(): - if key == 'oauth_signature': - continue - # 1.0a/9.1.1 states that kvp must be sorted by key, then by value, - # so we unpack sequence values into multiple items for sorting. - if isinstance(value, basestring): - items.append((to_utf8_if_string(key), to_utf8(value))) - else: - try: - value = list(value) - except TypeError, e: - assert 'is not iterable' in str(e) - items.append((to_utf8_if_string(key), to_utf8_if_string(value))) - else: - items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value) - - # Include any query string parameters from the provided URL - query = urlparse.urlparse(self.url)[4] - - url_items = self._split_url_string(query).items() - url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ] - items.extend(url_items) - - items.sort() - encoded_str = urllib.urlencode(items) - # Encode signature parameters per Oauth Core 1.0 protocol - # spec draft 7, section 3.6 - # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6) - # Spaces must be encoded with "%20" instead of "+" - return encoded_str.replace('+', '%20').replace('%7E', '~') - - def sign_request(self, signature_method, consumer, token): - """Set the signature parameter to the result of sign.""" - - if not self.is_form_encoded: - # according to - # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html - # section 4.1.1 "OAuth Consumers MUST NOT include an - # oauth_body_hash parameter on requests with form-encoded - # request bodies." - self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest()) - - if 'oauth_consumer_key' not in self: - self['oauth_consumer_key'] = consumer.key - - if token and 'oauth_token' not in self: - self['oauth_token'] = token.key - - self['oauth_signature_method'] = signature_method.name - self['oauth_signature'] = signature_method.sign(self, consumer, token) - - @classmethod - def make_timestamp(cls): - """Get seconds since epoch (UTC).""" - return str(int(time.time())) - - @classmethod - def make_nonce(cls): - """Generate pseudorandom number.""" - return str(random.randint(0, 100000000)) - - @classmethod - def from_request(cls, http_method, http_url, headers=None, parameters=None, - query_string=None): - """Combines multiple parameter sources.""" - if parameters is None: - parameters = {} - - # Headers - if headers and 'Authorization' in headers: - auth_header = headers['Authorization'] - # Check that the authorization header is OAuth. - if auth_header[:6] == 'OAuth ': - auth_header = auth_header[6:] - try: - # Get the parameters from the header. - header_params = cls._split_header(auth_header) - parameters.update(header_params) - except: - raise Error('Unable to parse OAuth parameters from ' - 'Authorization header.') - - # GET or POST query string. - if query_string: - query_params = cls._split_url_string(query_string) - parameters.update(query_params) - - # URL parameters. - param_str = urlparse.urlparse(http_url)[4] # query - url_params = cls._split_url_string(param_str) - parameters.update(url_params) - - if parameters: - return cls(http_method, http_url, parameters) - - return None - - @classmethod - def from_consumer_and_token(cls, consumer, token=None, - http_method=HTTP_METHOD, http_url=None, parameters=None, - body='', is_form_encoded=False): - if not parameters: - parameters = {} - - defaults = { - 'oauth_consumer_key': consumer.key, - 'oauth_timestamp': cls.make_timestamp(), - 'oauth_nonce': cls.make_nonce(), - 'oauth_version': cls.version, - } - - defaults.update(parameters) - parameters = defaults - - if token: - parameters['oauth_token'] = token.key - if token.verifier: - parameters['oauth_verifier'] = token.verifier - - return Request(http_method, http_url, parameters, body=body, - is_form_encoded=is_form_encoded) - - @classmethod - def from_token_and_callback(cls, token, callback=None, - http_method=HTTP_METHOD, http_url=None, parameters=None): - - if not parameters: - parameters = {} - - parameters['oauth_token'] = token.key - - if callback: - parameters['oauth_callback'] = callback - - return cls(http_method, http_url, parameters) - - @staticmethod - def _split_header(header): - """Turn Authorization: header into parameters.""" - params = {} - parts = header.split(',') - for param in parts: - # Ignore realm parameter. - if param.find('realm') > -1: - continue - # Remove whitespace. - param = param.strip() - # Split key-value. - param_parts = param.split('=', 1) - # Remove quotes and unescape the value. - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) - return params - - @staticmethod - def _split_url_string(param_str): - """Turn URL string into parameters.""" - parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) - return parameters - - -class Client(httplib2.Http): - """OAuthClient is a worker to attempt to execute a request.""" - - def __init__(self, consumer, token=None, cache=None, timeout=None, - proxy_info=None): - - if consumer is not None and not isinstance(consumer, Consumer): - raise ValueError("Invalid consumer.") - - if token is not None and not isinstance(token, Token): - raise ValueError("Invalid token.") - - self.consumer = consumer - self.token = token - self.method = SignatureMethod_HMAC_SHA1() - - httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info) - - def set_signature_method(self, method): - if not isinstance(method, SignatureMethod): - raise ValueError("Invalid signature method.") - - self.method = method - - def request(self, uri, method="GET", body='', headers=None, - redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None): - DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded' - - if not isinstance(headers, dict): - headers = {} - - if method == "POST": - headers['Content-Type'] = headers.get('Content-Type', - DEFAULT_POST_CONTENT_TYPE) - - is_form_encoded = \ - ((headers.get('Content-Type') == 'application/x-www-form-urlencoded') and (method == "POST")) - - if is_form_encoded and body: - parameters = parse_qs(body) - else: - parameters = None - - req = Request.from_consumer_and_token(self.consumer, - token=self.token, http_method=method, http_url=uri, - parameters=parameters, body=body, is_form_encoded=is_form_encoded) - - req.sign_request(self.method, self.consumer, self.token) - - schema, rest = urllib.splittype(uri) - if rest.startswith('//'): - hierpart = '//' - else: - hierpart = '' - host, rest = urllib.splithost(rest) - - realm = schema + ':' + hierpart + host - - if is_form_encoded: - body = req.to_postdata() - elif method == "GET": - uri = req.to_url() - else: - headers.update(req.to_header(realm=realm)) - - return httplib2.Http.request(self, uri, method=method, body=body, - headers=headers, redirections=redirections, - connection_type=connection_type) - - -class Server(object): - """A skeletal implementation of a service provider, providing protected - resources to requests from authorized consumers. - - This class implements the logic to check requests for authorization. You - can use it with your web server or web framework to protect certain - resources with OAuth. - """ - - timestamp_threshold = 300 # In seconds, five minutes. - version = OAUTH_VERSION - signature_methods = None - - def __init__(self, signature_methods=None): - self.signature_methods = signature_methods or {} - - def add_signature_method(self, signature_method): - self.signature_methods[signature_method.name] = signature_method - return self.signature_methods - - def verify_request(self, request, consumer, token): - """Verifies an api call and checks all the parameters.""" - - self._check_version(request) - self._check_signature(request, consumer, token) - parameters = request.get_nonoauth_parameters() - return parameters - - def build_authenticate_header(self, realm=''): - """Optional support for the authenticate header.""" - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - - def _check_version(self, request): - """Verify the correct version of the request for this server.""" - version = self._get_version(request) - if version and version != self.version: - raise Error('OAuth version %s not supported.' % str(version)) - - def _get_version(self, request): - """Return the version of the request for this server.""" - try: - version = request.get_parameter('oauth_version') - except: - version = OAUTH_VERSION - - return version - - def _get_signature_method(self, request): - """Figure out the signature with some defaults.""" - try: - signature_method = request.get_parameter('oauth_signature_method') - except: - signature_method = SIGNATURE_METHOD - - try: - # Get the signature method object. - signature_method = self.signature_methods[signature_method] - except: - signature_method_names = ', '.join(self.signature_methods.keys()) - raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) - - return signature_method - - def _get_verifier(self, request): - return request.get_parameter('oauth_verifier') - - def _check_signature(self, request, consumer, token): - timestamp, nonce = request._get_timestamp_nonce() - self._check_timestamp(timestamp) - signature_method = self._get_signature_method(request) - - try: - signature = request.get_parameter('oauth_signature') - except: - raise MissingSignature('Missing oauth_signature.') - - # Validate the signature. - valid = signature_method.check(request, consumer, token, signature) - - if not valid: - key, base = signature_method.signing_base(request, consumer, token) - - raise Error('Invalid signature. Expected signature base ' - 'string: %s' % base) - - def _check_timestamp(self, timestamp): - """Verify that timestamp is recentish.""" - timestamp = int(timestamp) - now = int(time.time()) - lapsed = now - timestamp - if lapsed > self.timestamp_threshold: - raise Error('Expired timestamp: given %d and now %s has a ' - 'greater difference than threshold %d' % (timestamp, now, - self.timestamp_threshold)) - - -class SignatureMethod(object): - """A way of signing requests. - - The OAuth protocol lets consumers and service providers pick a way to sign - requests. This interface shows the methods expected by the other `oauth` - modules for signing requests. Subclass it and implement its methods to - provide a new way to sign requests. - """ - - def signing_base(self, request, consumer, token): - """Calculates the string that needs to be signed. - - This method returns a 2-tuple containing the starting key for the - signing and the message to be signed. The latter may be used in error - messages to help clients debug their software. - - """ - raise NotImplementedError - - def sign(self, request, consumer, token): - """Returns the signature for the given request, based on the consumer - and token also provided. - - You should use your implementation of `signing_base()` to build the - message to sign. Otherwise it may be less useful for debugging. - - """ - raise NotImplementedError - - def check(self, request, consumer, token, signature): - """Returns whether the given signature is the correct signature for - the given consumer and token signing the given request.""" - built = self.sign(request, consumer, token) - return built == signature - - -class SignatureMethod_HMAC_SHA1(SignatureMethod): - name = 'HMAC-SHA1' - - def signing_base(self, request, consumer, token): - if not hasattr(request, 'normalized_url') or request.normalized_url is None: - raise ValueError("Base URL for request is not set.") - - sig = ( - escape(request.method), - escape(request.normalized_url), - escape(request.get_normalized_parameters()), - ) - - key = '%s&' % escape(consumer.secret) - if token: - key += escape(token.secret) - raw = '&'.join(sig) - return key, raw - - def sign(self, request, consumer, token): - """Builds the base signature string.""" - key, raw = self.signing_base(request, consumer, token) - - hashed = hmac.new(key, raw, sha) - - # Calculate the digest base 64. - return binascii.b2a_base64(hashed.digest())[:-1] - - -class SignatureMethod_PLAINTEXT(SignatureMethod): - - name = 'PLAINTEXT' - - def signing_base(self, request, consumer, token): - """Concatenates the consumer key and secret with the token's - secret.""" - sig = '%s&' % escape(consumer.secret) - if token: - sig = sig + escape(token.secret) - return sig, sig - - def sign(self, request, consumer, token): - key, raw = self.signing_base(request, consumer, token) - return raw diff --git a/build/lib/oauth2/_version.py b/build/lib/oauth2/_version.py deleted file mode 100644 index 0d74e056..00000000 --- a/build/lib/oauth2/_version.py +++ /dev/null @@ -1,18 +0,0 @@ -# This is the version of this source code. - -manual_verstr = "1.5" - - - -auto_build_num = "170" - - - -verstr = manual_verstr + "." + auto_build_num -try: - from pyutil.version_class import Version as pyutil_Version - __version__ = pyutil_Version(verstr) -except (ImportError, ValueError): - # Maybe there is no pyutil installed. - from distutils.version import LooseVersion as distutils_Version - __version__ = distutils_Version(verstr) diff --git a/build/lib/oauth2/clients/__init__.py b/build/lib/oauth2/clients/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/build/lib/oauth2/clients/imap.py b/build/lib/oauth2/clients/imap.py deleted file mode 100644 index 68b7cd8c..00000000 --- a/build/lib/oauth2/clients/imap.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -The MIT License - -Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import oauth2 -import imaplib - - -class IMAP4_SSL(imaplib.IMAP4_SSL): - """IMAP wrapper for imaplib.IMAP4_SSL that implements XOAUTH.""" - - def authenticate(self, url, consumer, token): - if consumer is not None and not isinstance(consumer, oauth2.Consumer): - raise ValueError("Invalid consumer.") - - if token is not None and not isinstance(token, oauth2.Token): - raise ValueError("Invalid token.") - - imaplib.IMAP4_SSL.authenticate(self, 'XOAUTH', - lambda x: oauth2.build_xoauth_string(url, consumer, token)) diff --git a/build/lib/oauth2/clients/smtp.py b/build/lib/oauth2/clients/smtp.py deleted file mode 100644 index 3e7bf0b0..00000000 --- a/build/lib/oauth2/clients/smtp.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -The MIT License - -Copyright (c) 2007-2010 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -import oauth2 -import smtplib -import base64 - - -class SMTP(smtplib.SMTP): - """SMTP wrapper for smtplib.SMTP that implements XOAUTH.""" - - def authenticate(self, url, consumer, token): - if consumer is not None and not isinstance(consumer, oauth2.Consumer): - raise ValueError("Invalid consumer.") - - if token is not None and not isinstance(token, oauth2.Token): - raise ValueError("Invalid token.") - - self.docmd('AUTH', 'XOAUTH %s' % \ - base64.b64encode(oauth2.build_xoauth_string(url, consumer, token))) diff --git a/build/lib/tests/__init__.py b/build/lib/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/build/lib/tests/test_oauth.py b/build/lib/tests/test_oauth.py deleted file mode 100644 index 099e5794..00000000 --- a/build/lib/tests/test_oauth.py +++ /dev/null @@ -1,1309 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -The MIT License - -Copyright (c) 2009 Vic Fryzel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" -import sys -import os -import unittest -import oauth2 as oauth -import random -import time -import urllib -import urlparse -from types import ListType -import mock -import httplib2 - -# Fix for python2.5 compatibility -try: - from urlparse import parse_qs, parse_qsl -except ImportError: - from cgi import parse_qs, parse_qsl - - -sys.path[0:0] = [os.path.join(os.path.dirname(__file__), ".."),] - - -class TestError(unittest.TestCase): - def test_message(self): - try: - raise oauth.Error - except oauth.Error, e: - self.assertEqual(e.message, 'OAuth error occurred.') - - msg = 'OMG THINGS BROKE!!!!' - try: - raise oauth.Error(msg) - except oauth.Error, e: - self.assertEqual(e.message, msg) - - def test_str(self): - try: - raise oauth.Error - except oauth.Error, e: - self.assertEquals(str(e), 'OAuth error occurred.') - -class TestGenerateFunctions(unittest.TestCase): - def test_build_auth_header(self): - header = oauth.build_authenticate_header() - self.assertEqual(header['WWW-Authenticate'], 'OAuth realm=""') - self.assertEqual(len(header), 1) - realm = 'http://example.myrealm.com/' - header = oauth.build_authenticate_header(realm) - self.assertEqual(header['WWW-Authenticate'], 'OAuth realm="%s"' % - realm) - self.assertEqual(len(header), 1) - - def test_build_xoauth_string(self): - consumer = oauth.Consumer('consumer_token', 'consumer_secret') - token = oauth.Token('user_token', 'user_secret') - url = "https://mail.google.com/mail/b/joe@example.com/imap/" - xoauth_string = oauth.build_xoauth_string(url, consumer, token) - - method, oauth_url, oauth_string = xoauth_string.split(' ') - - self.assertEqual("GET", method) - self.assertEqual(url, oauth_url) - - returned = {} - parts = oauth_string.split(',') - for part in parts: - var, val = part.split('=') - returned[var] = val.strip('"') - - self.assertEquals('HMAC-SHA1', returned['oauth_signature_method']) - self.assertEquals('user_token', returned['oauth_token']) - self.assertEquals('consumer_token', returned['oauth_consumer_key']) - self.assertTrue('oauth_signature' in returned, 'oauth_signature') - - def test_escape(self): - string = 'http://whatever.com/~someuser/?test=test&other=other' - self.assert_('~' in oauth.escape(string)) - string = '../../../../../../../etc/passwd' - self.assert_('../' not in oauth.escape(string)) - - def test_gen_nonce(self): - nonce = oauth.generate_nonce() - self.assertEqual(len(nonce), 8) - nonce = oauth.generate_nonce(20) - self.assertEqual(len(nonce), 20) - - def test_gen_verifier(self): - verifier = oauth.generate_verifier() - self.assertEqual(len(verifier), 8) - verifier = oauth.generate_verifier(16) - self.assertEqual(len(verifier), 16) - - def test_gen_timestamp(self): - exp = int(time.time()) - now = oauth.generate_timestamp() - self.assertEqual(exp, now) - -class TestConsumer(unittest.TestCase): - def setUp(self): - self.key = 'my-key' - self.secret = 'my-secret' - self.consumer = oauth.Consumer(key=self.key, secret=self.secret) - - def test_init(self): - self.assertEqual(self.consumer.key, self.key) - self.assertEqual(self.consumer.secret, self.secret) - - def test_basic(self): - self.assertRaises(ValueError, lambda: oauth.Consumer(None, None)) - self.assertRaises(ValueError, lambda: oauth.Consumer('asf', None)) - self.assertRaises(ValueError, lambda: oauth.Consumer(None, 'dasf')) - - def test_str(self): - res = dict(parse_qsl(str(self.consumer))) - self.assertTrue('oauth_consumer_key' in res) - self.assertTrue('oauth_consumer_secret' in res) - self.assertEquals(res['oauth_consumer_key'], self.consumer.key) - self.assertEquals(res['oauth_consumer_secret'], self.consumer.secret) - -class TestToken(unittest.TestCase): - def setUp(self): - self.key = 'my-key' - self.secret = 'my-secret' - self.token = oauth.Token(self.key, self.secret) - - def test_basic(self): - self.assertRaises(ValueError, lambda: oauth.Token(None, None)) - self.assertRaises(ValueError, lambda: oauth.Token('asf', None)) - self.assertRaises(ValueError, lambda: oauth.Token(None, 'dasf')) - - def test_init(self): - self.assertEqual(self.token.key, self.key) - self.assertEqual(self.token.secret, self.secret) - self.assertEqual(self.token.callback, None) - self.assertEqual(self.token.callback_confirmed, None) - self.assertEqual(self.token.verifier, None) - - def test_set_callback(self): - self.assertEqual(self.token.callback, None) - self.assertEqual(self.token.callback_confirmed, None) - cb = 'http://www.example.com/my-callback' - self.token.set_callback(cb) - self.assertEqual(self.token.callback, cb) - self.assertEqual(self.token.callback_confirmed, 'true') - self.token.set_callback(None) - self.assertEqual(self.token.callback, None) - # TODO: The following test should probably not pass, but it does - # To fix this, check for None and unset 'true' in set_callback - # Additionally, should a confirmation truly be done of the callback? - self.assertEqual(self.token.callback_confirmed, 'true') - - def test_set_verifier(self): - self.assertEqual(self.token.verifier, None) - v = oauth.generate_verifier() - self.token.set_verifier(v) - self.assertEqual(self.token.verifier, v) - self.token.set_verifier() - self.assertNotEqual(self.token.verifier, v) - self.token.set_verifier('') - self.assertEqual(self.token.verifier, '') - - def test_get_callback_url(self): - self.assertEqual(self.token.get_callback_url(), None) - - self.token.set_verifier() - self.assertEqual(self.token.get_callback_url(), None) - - cb = 'http://www.example.com/my-callback?save=1&return=true' - v = oauth.generate_verifier() - self.token.set_callback(cb) - self.token.set_verifier(v) - url = self.token.get_callback_url() - verifier_str = '&oauth_verifier=%s' % v - self.assertEqual(url, '%s%s' % (cb, verifier_str)) - - cb = 'http://www.example.com/my-callback-no-query' - v = oauth.generate_verifier() - self.token.set_callback(cb) - self.token.set_verifier(v) - url = self.token.get_callback_url() - verifier_str = '?oauth_verifier=%s' % v - self.assertEqual(url, '%s%s' % (cb, verifier_str)) - - def test_to_string(self): - string = 'oauth_token_secret=%s&oauth_token=%s' % (self.secret, - self.key) - self.assertEqual(self.token.to_string(), string) - - self.token.set_callback('http://www.example.com/my-callback') - string += '&oauth_callback_confirmed=true' - self.assertEqual(self.token.to_string(), string) - - def _compare_tokens(self, new): - self.assertEqual(self.token.key, new.key) - self.assertEqual(self.token.secret, new.secret) - # TODO: What about copying the callback to the new token? - # self.assertEqual(self.token.callback, new.callback) - self.assertEqual(self.token.callback_confirmed, - new.callback_confirmed) - # TODO: What about copying the verifier to the new token? - # self.assertEqual(self.token.verifier, new.verifier) - - def test_to_string(self): - tok = oauth.Token('tooken', 'seecret') - self.assertEqual(str(tok), 'oauth_token_secret=seecret&oauth_token=tooken') - - def test_from_string(self): - self.assertRaises(ValueError, lambda: oauth.Token.from_string('')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('blahblahblah')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('blah=blah')) - - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=asfdasf')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=asfdasf')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=&oauth_token_secret=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=tooken%26oauth_token_secret=seecret')) - - string = self.token.to_string() - new = oauth.Token.from_string(string) - self._compare_tokens(new) - - self.token.set_callback('http://www.example.com/my-callback') - string = self.token.to_string() - new = oauth.Token.from_string(string) - self._compare_tokens(new) - -class ReallyEqualMixin: - def failUnlessReallyEqual(self, a, b, msg=None): - self.failUnlessEqual(a, b, msg=msg) - self.failUnlessEqual(type(a), type(b), msg="a :: %r, b :: %r, %r" % (a, b, msg)) - -class TestFuncs(unittest.TestCase): - def test_to_unicode(self): - self.failUnlessRaises(TypeError, oauth.to_unicode, '\xae') - self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, '\xae') - self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, ['\xae']) - - self.failUnlessEqual(oauth.to_unicode(':-)'), u':-)') - self.failUnlessEqual(oauth.to_unicode(u'\u00ae'), u'\u00ae') - self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), u'\u00ae') - self.failUnlessEqual(oauth.to_unicode_optional_iterator([':-)']), [u':-)']) - self.failUnlessEqual(oauth.to_unicode_optional_iterator([u'\u00ae']), [u'\u00ae']) - -class TestRequest(unittest.TestCase, ReallyEqualMixin): - def test_setter(self): - url = "http://example.com" - method = "GET" - req = oauth.Request(method) - self.assertTrue(not hasattr(req, 'url') or req.url is None) - self.assertTrue(not hasattr(req, 'normalized_url') or req.normalized_url is None) - - def test_deleter(self): - url = "http://example.com" - method = "GET" - req = oauth.Request(method, url) - - try: - del req.url - url = req.url - self.fail("AttributeError should have been raised on empty url.") - except AttributeError: - pass - except Exception, e: - self.fail(str(e)) - - def test_url(self): - url1 = "http://example.com:80/foo.php" - url2 = "https://example.com:443/foo.php" - exp1 = "http://example.com/foo.php" - exp2 = "https://example.com/foo.php" - method = "GET" - - req = oauth.Request(method, url1) - self.assertEquals(req.normalized_url, exp1) - self.assertEquals(req.url, url1) - - req = oauth.Request(method, url2) - self.assertEquals(req.normalized_url, exp2) - self.assertEquals(req.url, url2) - - def test_bad_url(self): - request = oauth.Request() - try: - request.url = "ftp://example.com" - self.fail("Invalid URL scheme was accepted.") - except ValueError: - pass - - def test_unset_consumer_and_token(self): - consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') - token = oauth.Token('my_key', 'my_secret') - request = oauth.Request("GET", "http://example.com/fetch.php") - request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, - token) - - self.assertEquals(consumer.key, request['oauth_consumer_key']) - self.assertEquals(token.key, request['oauth_token']) - - def test_no_url_set(self): - consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') - token = oauth.Token('my_key', 'my_secret') - request = oauth.Request() - - try: - try: - request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), - consumer, token) - except TypeError: - self.fail("Signature method didn't check for a normalized URL.") - except ValueError: - pass - - def test_url_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" - normalized_url = urlparse.urlunparse(urlparse.urlparse(url)[:3] + (None, None, None)) - method = "GET" - - req = oauth.Request(method, url) - self.assertEquals(req.url, url) - self.assertEquals(req.normalized_url, normalized_url) - - def test_get_parameter(self): - url = "http://example.com" - method = "GET" - params = {'oauth_consumer' : 'asdf'} - req = oauth.Request(method, url, parameters=params) - - self.assertEquals(req.get_parameter('oauth_consumer'), 'asdf') - self.assertRaises(oauth.Error, req.get_parameter, 'blah') - - def test_get_nonoauth_parameters(self): - - oauth_params = { - 'oauth_consumer': 'asdfasdfasdf' - } - - other_params = { - u'foo': u'baz', - u'bar': u'foo', - u'multi': [u'FOO',u'BAR'], - u'uni_utf8': u'\xae', - u'uni_unicode': u'\u00ae', - u'uni_unicode_2': u'åÅøØ', - } - - params = oauth_params - params.update(other_params) - - req = oauth.Request("GET", "http://example.com", params) - self.assertEquals(other_params, req.get_nonoauth_parameters()) - - def test_to_header(self): - realm = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", realm, params) - header, value = req.to_header(realm).items()[0] - - parts = value.split('OAuth ') - vars = parts[1].split(', ') - self.assertTrue(len(vars), (len(params) + 1)) - - res = {} - for v in vars: - var, val = v.split('=') - res[var] = urllib.unquote(val.strip('"')) - - self.assertEquals(realm, res['realm']) - del res['realm'] - - self.assertTrue(len(res), len(params)) - - for key, val in res.items(): - self.assertEquals(val, params.get(key)) - - def test_to_postdata_nonascii(self): - realm = "http://sp.example.com/" - - params = { - 'nonasciithing': u'q\xbfu\xe9 ,aasp u?..a.s', - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", realm, params) - - self.failUnlessReallyEqual(req.to_postdata(), 'nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s&oauth_nonce=4572616e48616d6d65724c61686176&oauth_timestamp=137131200&oauth_consumer_key=0685bd9184jfhq22&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=ad180jjd733klru7&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D') - - def test_to_postdata(self): - realm = "http://sp.example.com/" - - params = { - 'multi': ['FOO','BAR'], - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", realm, params) - - flat = [('multi','FOO'),('multi','BAR')] - del params['multi'] - flat.extend(params.items()) - kf = lambda x: x[0] - self.assertEquals(sorted(flat, key=kf), sorted(parse_qsl(req.to_postdata()), key=kf)) - - def test_to_url(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", url, params) - exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params))) - res = urlparse.urlparse(req.to_url()) - self.assertEquals(exp.scheme, res.scheme) - self.assertEquals(exp.netloc, res.netloc) - self.assertEquals(exp.path, res.path) - - a = parse_qs(exp.query) - b = parse_qs(res.query) - self.assertEquals(a, b) - - def test_to_url_with_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", url, params) - # Note: the url above already has query parameters, so append new ones with & - exp = urlparse.urlparse("%s&%s" % (url, urllib.urlencode(params))) - res = urlparse.urlparse(req.to_url()) - self.assertEquals(exp.scheme, res.scheme) - self.assertEquals(exp.netloc, res.netloc) - self.assertEquals(exp.path, res.path) - - a = parse_qs(exp.query) - b = parse_qs(res.query) - self.assertTrue('alt' in b) - self.assertTrue('max-contacts' in b) - self.assertEquals(b['alt'], ['json']) - self.assertEquals(b['max-contacts'], ['10']) - self.assertEquals(a, b) - - def test_signature_base_string_nonascii_nonutf8(self): - consumer = oauth.Consumer('consumer_token', 'consumer_secret') - - url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\u2766,+CA' - req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - - url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\xe2\x9d\xa6,+CA' - req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - - url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' - req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - - url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' - req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - - def test_signature_base_string_with_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - req = oauth.Request("GET", url, params) - self.assertEquals(req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') - self.assertEquals(req.url, 'https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10') - normalized_params = parse_qsl(req.get_normalized_parameters()) - self.assertTrue(len(normalized_params), len(params) + 2) - normalized_params = dict(normalized_params) - for key, value in params.iteritems(): - if key == 'oauth_signature': - continue - self.assertEquals(value, normalized_params[key]) - self.assertEquals(normalized_params['alt'], 'json') - self.assertEquals(normalized_params['max-contacts'], '10') - - def test_get_normalized_parameters_empty(self): - url = "http://sp.example.com/?empty=" - - req = oauth.Request("GET", url) - - res = req.get_normalized_parameters() - - expected='empty=' - - self.assertEquals(expected, res) - - def test_get_normalized_parameters_duplicate(self): - url = "http://example.com/v2/search/videos?oauth_nonce=79815175&oauth_timestamp=1295397962&oauth_consumer_key=mykey&oauth_signature_method=HMAC-SHA1&q=car&oauth_version=1.0&offset=10&oauth_signature=spWLI%2FGQjid7sQVd5%2FarahRxzJg%3D" - - req = oauth.Request("GET", url) - - res = req.get_normalized_parameters() - - expected='oauth_consumer_key=mykey&oauth_nonce=79815175&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1295397962&oauth_version=1.0&offset=10&q=car' - - self.assertEquals(expected, res) - - def test_get_normalized_parameters_from_url(self): - # example copied from - # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js - # which in turns says that it was copied from - # http://oauth.net/core/1.0/#sig_base_example . - url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" - - req = oauth.Request("GET", url) - - res = req.get_normalized_parameters() - - expected = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original' - - self.assertEquals(expected, res) - - def test_signing_base(self): - # example copied from - # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js - # which in turns says that it was copied from - # http://oauth.net/core/1.0/#sig_base_example . - url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" - - req = oauth.Request("GET", url) - - sm = oauth.SignatureMethod_HMAC_SHA1() - - consumer = oauth.Consumer('dpf43f3p2l4k3l03', 'foo') - key, raw = sm.signing_base(req, consumer, None) - - expected = 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal' - self.assertEquals(expected, raw) - - def test_get_normalized_parameters(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'multi': ['FOO','BAR', u'\u00ae', '\xc2\xae'], - 'multi_same': ['FOO','FOO'], - 'uni_utf8_bytes': '\xc2\xae', - 'uni_unicode_object': u'\u00ae' - } - - req = oauth.Request("GET", url, params) - - res = req.get_normalized_parameters() - - expected='multi=BAR&multi=FOO&multi=%C2%AE&multi=%C2%AE&multi_same=FOO&multi_same=FOO&oauth_consumer_key=0685bd9184jfhq22&oauth_nonce=4572616e48616d6d65724c61686176&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131200&oauth_token=ad180jjd733klru7&oauth_version=1.0&uni_unicode_object=%C2%AE&uni_utf8_bytes=%C2%AE' - - self.assertEquals(expected, res) - - def test_get_normalized_parameters_ignores_auth_signature(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_signature': "some-random-signature-%d" % random.randint(1000, 2000), - 'oauth_token': "ad180jjd733klru7", - } - - req = oauth.Request("GET", url, params) - - res = req.get_normalized_parameters() - - self.assertNotEquals(urllib.urlencode(sorted(params.items())), res) - - foo = params.copy() - del foo["oauth_signature"] - self.assertEqual(urllib.urlencode(sorted(foo.items())), res) - - def test_set_signature_method(self): - consumer = oauth.Consumer('key', 'secret') - client = oauth.Client(consumer) - - class Blah: - pass - - try: - client.set_signature_method(Blah()) - self.fail("Client.set_signature_method() accepted invalid method.") - except ValueError: - pass - - m = oauth.SignatureMethod_HMAC_SHA1() - client.set_signature_method(m) - self.assertEquals(m, client.method) - - def test_get_normalized_string_escapes_spaces_properly(self): - url = "http://sp.example.com/" - params = { - "some_random_data": random.randint(100, 1000), - "data": "This data with a random number (%d) has spaces!" % random.randint(1000, 2000), - } - - req = oauth.Request("GET", url, params) - res = req.get_normalized_parameters() - expected = urllib.urlencode(sorted(params.items())).replace('+', '%20') - self.assertEqual(expected, res) - - @mock.patch('oauth2.Request.make_timestamp') - @mock.patch('oauth2.Request.make_nonce') - def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): - mock_make_nonce.return_value = 5 - mock_make_timestamp.return_value = 6 - - tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") - con = oauth.Consumer(key="con-test-key", secret="con-test-secret") - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_token': tok.key, - 'oauth_consumer_key': con.key - } - - # If someone passes a sequence of bytes which is not ascii for - # url, we'll raise an exception as early as possible. - url = "http://sp.example.com/\x92" # It's actually cp1252-encoding... - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) - - # And if they pass an unicode, then we'll use it. - url = u'http://sp.example.com/\u2019' - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'cMzvCkhvLL57+sTIxLITTHfkqZk=') - - # And if it is a utf-8-encoded-then-percent-encoded non-ascii - # thing, we'll decode it and use it. - url = "http://sp.example.com/%E2%80%99" - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'yMLKOyNKC/DkyhUOb8DLSvceEWE=') - - # Same thing with the params. - url = "http://sp.example.com/" - - # If someone passes a sequence of bytes which is not ascii in - # params, we'll raise an exception as early as possible. - params['non_oauth_thing'] = '\xae', # It's actually cp1252-encoding... - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) - - # And if they pass a unicode, then we'll use it. - params['non_oauth_thing'] = u'\u2019' - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], '0GU50m0v60CVDB5JnoBXnvvvKx4=') - - # And if it is a utf-8-encoded non-ascii thing, we'll decode - # it and use it. - params['non_oauth_thing'] = '\xc2\xae' - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') - - - # Also if there are non-utf8 bytes in the query args. - url = "http://sp.example.com/?q=\x92" # cp1252 - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) - - def test_request_hash_of_body(self): - tok = oauth.Token(key="token", secret="tok-test-secret") - con = oauth.Consumer(key="consumer", secret="con-test-secret") - - # Example 1a from Appendix A.1 of - # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html - # Except that we get a differetn result than they do. - - params = { - 'oauth_version': "1.0", - 'oauth_token': tok.key, - 'oauth_nonce': 10288510250934, - 'oauth_timestamp': 1236874155, - 'oauth_consumer_key': con.key - } - - url = u"http://www.example.com/resource" - req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual(req['oauth_signature'], 't+MX8l/0S8hdbVQL99nD0X1fPnM=') - # oauth-bodyhash.html A.1 has - # '08bUFF%2Fjmp59mWB7cSgCYBUpJ0U%3D', but I don't see how that - # is possible. - - # Example 1b - params = { - 'oauth_version': "1.0", - 'oauth_token': tok.key, - 'oauth_nonce': 10369470270925, - 'oauth_timestamp': 1236874236, - 'oauth_consumer_key': con.key - } - - req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual(req['oauth_signature'], 'CTFmrqJIGT7NsWJ42OrujahTtTc=') - - # Appendix A.2 - params = { - 'oauth_version': "1.0", - 'oauth_token': tok.key, - 'oauth_nonce': 8628868109991, - 'oauth_timestamp': 1238395022, - 'oauth_consumer_key': con.key - } - - req = oauth.Request(method="GET", url=url, parameters=params, is_form_encoded=False) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], '2jmj7l5rSw0yVb/vlWAYkK/YBwk=') - self.failUnlessReallyEqual(req['oauth_signature'], 'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') - - - def test_sign_request(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200" - } - - tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") - con = oauth.Consumer(key="con-test-key", secret="con-test-secret") - - params['oauth_token'] = tok.key - params['oauth_consumer_key'] = con.key - req = oauth.Request(method="GET", url=url, parameters=params) - - methods = { - 'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), - 'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT() - } - - for exp, method in methods.items(): - req.sign_request(method, con, tok) - self.assertEquals(req['oauth_signature_method'], method.name) - self.assertEquals(req['oauth_signature'], exp) - - # Also if there are non-ascii chars in the URL. - url = "http://sp.example.com/\xe2\x80\x99" # utf-8 bytes - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') - - url = u'http://sp.example.com/\u2019' # Python unicode object - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') - - # Also if there are non-ascii chars in the query args. - url = "http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') - - url = u'http://sp.example.com/?q=\u2019' # Python unicode object - req = oauth.Request(method="GET", url=url, parameters=params) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') - - def test_from_request(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - req = oauth.Request("GET", url, params) - headers = req.to_header() - - # Test from the headers - req = oauth.Request.from_request("GET", url, headers) - self.assertEquals(req.method, "GET") - self.assertEquals(req.url, url) - - self.assertEquals(params, req.copy()) - - # Test with bad OAuth headers - bad_headers = { - 'Authorization' : 'OAuth this is a bad header' - } - - self.assertRaises(oauth.Error, oauth.Request.from_request, "GET", - url, bad_headers) - - # Test getting from query string - qs = urllib.urlencode(params) - req = oauth.Request.from_request("GET", url, query_string=qs) - - exp = parse_qs(qs, keep_blank_values=False) - for k, v in exp.iteritems(): - exp[k] = urllib.unquote(v[0]) - - self.assertEquals(exp, req.copy()) - - # Test that a boned from_request() call returns None - req = oauth.Request.from_request("GET", url) - self.assertEquals(None, req) - - def test_from_token_and_callback(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': "137131200", - 'oauth_consumer_key': "0685bd9184jfhq22", - 'oauth_signature_method': "HMAC-SHA1", - 'oauth_token': "ad180jjd733klru7", - 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", - } - - tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") - req = oauth.Request.from_token_and_callback(tok) - self.assertFalse('oauth_callback' in req) - self.assertEquals(req['oauth_token'], tok.key) - - req = oauth.Request.from_token_and_callback(tok, callback=url) - self.assertTrue('oauth_callback' in req) - self.assertEquals(req['oauth_callback'], url) - - def test_from_consumer_and_token(self): - url = "http://sp.example.com/" - - tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") - tok.set_verifier('this_is_a_test_verifier') - con = oauth.Consumer(key="con-test-key", secret="con-test-secret") - req = oauth.Request.from_consumer_and_token(con, token=tok, - http_method="GET", http_url=url) - - self.assertEquals(req['oauth_token'], tok.key) - self.assertEquals(req['oauth_consumer_key'], con.key) - self.assertEquals(tok.verifier, req['oauth_verifier']) - -class SignatureMethod_Bad(oauth.SignatureMethod): - name = "BAD" - - def signing_base(self, request, consumer, token): - return "" - - def sign(self, request, consumer, token): - return "invalid-signature" - - -class TestServer(unittest.TestCase): - def setUp(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': "1.0", - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': int(time.time()), - 'bar': 'blerg', - 'multi': ['FOO','BAR'], - 'foo': 59 - } - - self.consumer = oauth.Consumer(key="consumer-key", - secret="consumer-secret") - self.token = oauth.Token(key="token-key", secret="token-secret") - - params['oauth_token'] = self.token.key - params['oauth_consumer_key'] = self.consumer.key - self.request = oauth.Request(method="GET", url=url, parameters=params) - - signature_method = oauth.SignatureMethod_HMAC_SHA1() - self.request.sign_request(signature_method, self.consumer, self.token) - - def test_init(self): - server = oauth.Server(signature_methods={'HMAC-SHA1' : oauth.SignatureMethod_HMAC_SHA1()}) - self.assertTrue('HMAC-SHA1' in server.signature_methods) - self.assertTrue(isinstance(server.signature_methods['HMAC-SHA1'], - oauth.SignatureMethod_HMAC_SHA1)) - - server = oauth.Server() - self.assertEquals(server.signature_methods, {}) - - def test_add_signature_method(self): - server = oauth.Server() - res = server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - self.assertTrue(len(res) == 1) - self.assertTrue('HMAC-SHA1' in res) - self.assertTrue(isinstance(res['HMAC-SHA1'], - oauth.SignatureMethod_HMAC_SHA1)) - - res = server.add_signature_method(oauth.SignatureMethod_PLAINTEXT()) - self.assertTrue(len(res) == 2) - self.assertTrue('PLAINTEXT' in res) - self.assertTrue(isinstance(res['PLAINTEXT'], - oauth.SignatureMethod_PLAINTEXT)) - - def test_verify_request(self): - server = oauth.Server() - server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - - parameters = server.verify_request(self.request, self.consumer, - self.token) - - self.assertTrue('bar' in parameters) - self.assertTrue('foo' in parameters) - self.assertTrue('multi' in parameters) - self.assertEquals(parameters['bar'], 'blerg') - self.assertEquals(parameters['foo'], 59) - self.assertEquals(parameters['multi'], ['FOO','BAR']) - - def test_build_authenticate_header(self): - server = oauth.Server() - headers = server.build_authenticate_header('example.com') - self.assertTrue('WWW-Authenticate' in headers) - self.assertEquals('OAuth realm="example.com"', - headers['WWW-Authenticate']) - - def test_no_version(self): - url = "http://sp.example.com/" - - params = { - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': int(time.time()), - 'bar': 'blerg', - 'multi': ['FOO','BAR'], - 'foo': 59 - } - - self.consumer = oauth.Consumer(key="consumer-key", - secret="consumer-secret") - self.token = oauth.Token(key="token-key", secret="token-secret") - - params['oauth_token'] = self.token.key - params['oauth_consumer_key'] = self.consumer.key - self.request = oauth.Request(method="GET", url=url, parameters=params) - - signature_method = oauth.SignatureMethod_HMAC_SHA1() - self.request.sign_request(signature_method, self.consumer, self.token) - - server = oauth.Server() - server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - - parameters = server.verify_request(self.request, self.consumer, - self.token) - - def test_invalid_version(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': '222.9922', - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': int(time.time()), - 'bar': 'blerg', - 'multi': ['foo','bar'], - 'foo': 59 - } - - consumer = oauth.Consumer(key="consumer-key", - secret="consumer-secret") - token = oauth.Token(key="token-key", secret="token-secret") - - params['oauth_token'] = token.key - params['oauth_consumer_key'] = consumer.key - request = oauth.Request(method="GET", url=url, parameters=params) - - signature_method = oauth.SignatureMethod_HMAC_SHA1() - request.sign_request(signature_method, consumer, token) - - server = oauth.Server() - server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - - self.assertRaises(oauth.Error, server.verify_request, request, consumer, token) - - def test_invalid_signature_method(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': '1.0', - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': int(time.time()), - 'bar': 'blerg', - 'multi': ['FOO','BAR'], - 'foo': 59 - } - - consumer = oauth.Consumer(key="consumer-key", - secret="consumer-secret") - token = oauth.Token(key="token-key", secret="token-secret") - - params['oauth_token'] = token.key - params['oauth_consumer_key'] = consumer.key - request = oauth.Request(method="GET", url=url, parameters=params) - - signature_method = SignatureMethod_Bad() - request.sign_request(signature_method, consumer, token) - - server = oauth.Server() - server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - - self.assertRaises(oauth.Error, server.verify_request, request, - consumer, token) - - def test_missing_signature(self): - url = "http://sp.example.com/" - - params = { - 'oauth_version': '1.0', - 'oauth_nonce': "4572616e48616d6d65724c61686176", - 'oauth_timestamp': int(time.time()), - 'bar': 'blerg', - 'multi': ['FOO','BAR'], - 'foo': 59 - } - - consumer = oauth.Consumer(key="consumer-key", - secret="consumer-secret") - token = oauth.Token(key="token-key", secret="token-secret") - - params['oauth_token'] = token.key - params['oauth_consumer_key'] = consumer.key - request = oauth.Request(method="GET", url=url, parameters=params) - - signature_method = oauth.SignatureMethod_HMAC_SHA1() - request.sign_request(signature_method, consumer, token) - del request['oauth_signature'] - - server = oauth.Server() - server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - - self.assertRaises(oauth.MissingSignature, server.verify_request, - request, consumer, token) - - -# Request Token: http://oauth-sandbox.sevengoslings.net/request_token -# Auth: http://oauth-sandbox.sevengoslings.net/authorize -# Access Token: http://oauth-sandbox.sevengoslings.net/access_token -# Two-legged: http://oauth-sandbox.sevengoslings.net/two_legged -# Three-legged: http://oauth-sandbox.sevengoslings.net/three_legged -# Key: bd37aed57e15df53 -# Secret: 0e9e6413a9ef49510a4f68ed02cd -class TestClient(unittest.TestCase): -# oauth_uris = { -# 'request_token': '/request_token.php', -# 'access_token': '/access_token.php' -# } - - oauth_uris = { - 'request_token': '/request_token', - 'authorize': '/authorize', - 'access_token': '/access_token', - 'two_legged': '/two_legged', - 'three_legged': '/three_legged' - } - - consumer_key = 'bd37aed57e15df53' - consumer_secret = '0e9e6413a9ef49510a4f68ed02cd' - host = 'http://oauth-sandbox.sevengoslings.net' - - def setUp(self): - self.consumer = oauth.Consumer(key=self.consumer_key, - secret=self.consumer_secret) - - self.body = { - 'foo': 'bar', - 'bar': 'foo', - 'multi': ['FOO','BAR'], - 'blah': 599999 - } - - def _uri(self, type): - uri = self.oauth_uris.get(type) - if uri is None: - raise KeyError("%s is not a valid OAuth URI type." % type) - - return "%s%s" % (self.host, uri) - - def create_simple_multipart_data(self, data): - boundary = '---Boundary-%d' % random.randint(1,1000) - crlf = '\r\n' - items = [] - for key, value in data.iteritems(): - items += [ - '--'+boundary, - 'Content-Disposition: form-data; name="%s"'%str(key), - '', - str(value), - ] - items += ['', '--'+boundary+'--', ''] - content_type = 'multipart/form-data; boundary=%s' % boundary - return content_type, crlf.join(items) - - def test_init(self): - class Blah(): - pass - - try: - client = oauth.Client(Blah()) - self.fail("Client.__init__() accepted invalid Consumer.") - except ValueError: - pass - - consumer = oauth.Consumer('token', 'secret') - try: - client = oauth.Client(consumer, Blah()) - self.fail("Client.__init__() accepted invalid Token.") - except ValueError: - pass - - def test_access_token_get(self): - """Test getting an access token via GET.""" - client = oauth.Client(self.consumer, None) - resp, content = client.request(self._uri('request_token'), "GET") - - self.assertEquals(int(resp['status']), 200) - - def test_access_token_post(self): - """Test getting an access token via POST.""" - client = oauth.Client(self.consumer, None) - resp, content = client.request(self._uri('request_token'), "POST") - - self.assertEquals(int(resp['status']), 200) - - res = dict(parse_qsl(content)) - self.assertTrue('oauth_token' in res) - self.assertTrue('oauth_token_secret' in res) - - def _two_legged(self, method): - client = oauth.Client(self.consumer, None) - - return client.request(self._uri('two_legged'), method, - body=urllib.urlencode(self.body)) - - def test_two_legged_post(self): - """A test of a two-legged OAuth POST request.""" - resp, content = self._two_legged("POST") - - self.assertEquals(int(resp['status']), 200) - - def test_two_legged_get(self): - """A test of a two-legged OAuth GET request.""" - resp, content = self._two_legged("GET") - self.assertEquals(int(resp['status']), 200) - - @mock.patch('httplib2.Http.request') - def test_multipart_post_does_not_alter_body(self, mockHttpRequest): - random_result = random.randint(1,100) - - data = { - 'rand-%d'%random.randint(1,100):random.randint(1,100), - } - content_type, body = self.create_simple_multipart_data(data) - - client = oauth.Client(self.consumer, None) - uri = self._uri('two_legged') - - def mockrequest(cl, ur, **kw): - self.failUnless(cl is client) - self.failUnless(ur is uri) - self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) - self.failUnlessEqual(kw['body'], body) - self.failUnlessEqual(kw['connection_type'], None) - self.failUnlessEqual(kw['method'], 'POST') - self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) - self.failUnless(isinstance(kw['headers'], dict)) - - return random_result - - mockHttpRequest.side_effect = mockrequest - - result = client.request(uri, 'POST', headers={'Content-Type':content_type}, body=body) - self.assertEqual(result, random_result) - - @mock.patch('httplib2.Http.request') - def test_url_with_query_string(self, mockHttpRequest): - uri = 'http://example.com/foo/bar/?show=thundercats&character=snarf' - client = oauth.Client(self.consumer, None) - random_result = random.randint(1,100) - - def mockrequest(cl, ur, **kw): - self.failUnless(cl is client) - self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) - self.failUnlessEqual(kw['body'], '') - self.failUnlessEqual(kw['connection_type'], None) - self.failUnlessEqual(kw['method'], 'GET') - self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) - self.failUnless(isinstance(kw['headers'], dict)) - - req = oauth.Request.from_consumer_and_token(self.consumer, None, - http_method='GET', http_url=uri, parameters={}) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) - expected = parse_qsl(urlparse.urlparse(req.to_url()).query) - actual = parse_qsl(urlparse.urlparse(ur).query) - self.failUnlessEqual(len(expected), len(actual)) - actual = dict(actual) - for key, value in expected: - if key not in ('oauth_signature', 'oauth_nonce', 'oauth_timestamp'): - self.failUnlessEqual(actual[key], value) - - return random_result - - mockHttpRequest.side_effect = mockrequest - - client.request(uri, 'GET') - - @mock.patch('httplib2.Http.request') - @mock.patch('oauth2.Request.from_consumer_and_token') - def test_multiple_values_for_a_key(self, mockReqConstructor, mockHttpRequest): - client = oauth.Client(self.consumer, None) - - request = oauth.Request("GET", "http://example.com/fetch.php", parameters={'multi': ['1', '2']}) - mockReqConstructor.return_value = request - - client.request('http://whatever', 'POST', body='multi=1&multi=2') - - self.failUnlessEqual(mockReqConstructor.call_count, 1) - self.failUnlessEqual(mockReqConstructor.call_args[1]['parameters'], {'multi': ['1', '2']}) - - self.failUnless('multi=1' in mockHttpRequest.call_args[1]['body']) - self.failUnless('multi=2' in mockHttpRequest.call_args[1]['body']) - -if __name__ == "__main__": - unittest.main() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO deleted file mode 100644 index 4201c268..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/PKG-INFO +++ /dev/null @@ -1,35 +0,0 @@ -Metadata-Version: 1.0 -Name: coverage -Version: 3.4 -Summary: Code coverage measurement for Python -Home-page: http://nedbatchelder.com/code/coverage -Author: Ned Batchelder and others -Author-email: ned@nedbatchelder.com -License: BSD -Description: Coverage.py measures code coverage, typically during test execution. It uses - the code analysis tools and tracing hooks provided in the Python standard - library to determine which lines are executable, and which have been executed. - - Coverage.py runs on Pythons 2.3 through 3.2. - - Documentation is at `nedbatchelder.com `_. Code repository and issue - tracker are at `bitbucket.org `_. - - New in 3.2: Branch coverage! - - New in 3.3: .coveragerc files. - - New in 3.4: Better control over source to measure, and unexecuted files - can be reported. - -Keywords: code coverage testing -Platform: UNKNOWN -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Topic :: Software Development :: Quality Assurance -Classifier: Topic :: Software Development :: Testing -Classifier: Development Status :: 5 - Production/Stable diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt deleted file mode 100644 index a18ab1d1..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/SOURCES.txt +++ /dev/null @@ -1,43 +0,0 @@ -AUTHORS.txt -CHANGES.txt -MANIFEST.in -README.txt -distribute_setup.py -ez_setup.py -setup.cfg -setup.py -coverage/__init__.py -coverage/__main__.py -coverage/annotate.py -coverage/backward.py -coverage/bytecode.py -coverage/cmdline.py -coverage/codeunit.py -coverage/collector.py -coverage/config.py -coverage/control.py -coverage/data.py -coverage/execfile.py -coverage/files.py -coverage/html.py -coverage/misc.py -coverage/parser.py -coverage/phystokens.py -coverage/report.py -coverage/results.py -coverage/summary.py -coverage/templite.py -coverage/tracer.c -coverage/xmlreport.py -coverage.egg-info/PKG-INFO -coverage.egg-info/SOURCES.txt -coverage.egg-info/dependency_links.txt -coverage.egg-info/entry_points.txt -coverage.egg-info/not-zip-safe -coverage.egg-info/top_level.txt -coverage/htmlfiles/coverage_html.js -coverage/htmlfiles/index.html -coverage/htmlfiles/jquery-1.3.2.min.js -coverage/htmlfiles/jquery.tablesorter.min.js -coverage/htmlfiles/pyfile.html -coverage/htmlfiles/style.css \ No newline at end of file diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt deleted file mode 100644 index d82bb0a8..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -coverage = coverage:main - diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt deleted file mode 100644 index 89d7cb13..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/native_libs.txt +++ /dev/null @@ -1 +0,0 @@ -coverage/tracer.so diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe deleted file mode 100644 index 8b137891..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt deleted file mode 100644 index 4ebc8aea..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/EGG-INFO/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -coverage diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py deleted file mode 100644 index 65bc003f..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__init__.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Code coverage measurement for Python. - -Ned Batchelder -http://nedbatchelder.com/code/coverage - -""" - -__version__ = "3.4" # see detailed history in CHANGES.txt - -__url__ = "http://nedbatchelder.com/code/coverage" -if 'b' in __version__: - # For beta, use a version-specific URL. - __url__ += "/" + __version__ - -from coverage.control import coverage, process_startup -from coverage.data import CoverageData -from coverage.cmdline import main, CoverageScript -from coverage.misc import CoverageException - - -# Module-level functions. The original API to this module was based on -# functions defined directly in the module, with a singleton of the coverage() -# class. That design hampered programmability, so the current api uses -# explicitly-created coverage objects. But for backward compatibility, here we -# define the top-level functions to create the singleton when they are first -# called. - -# Singleton object for use with module-level functions. The singleton is -# created as needed when one of the module-level functions is called. -_the_coverage = None - -def _singleton_method(name): - """Return a function to the `name` method on a singleton `coverage` object. - - The singleton object is created the first time one of these functions is - called. - - """ - def wrapper(*args, **kwargs): - """Singleton wrapper around a coverage method.""" - global _the_coverage - if not _the_coverage: - _the_coverage = coverage(auto_data=True) - return getattr(_the_coverage, name)(*args, **kwargs) - return wrapper - - -# Define the module-level functions. -use_cache = _singleton_method('use_cache') -start = _singleton_method('start') -stop = _singleton_method('stop') -erase = _singleton_method('erase') -exclude = _singleton_method('exclude') -analysis = _singleton_method('analysis') -analysis2 = _singleton_method('analysis2') -report = _singleton_method('report') -annotate = _singleton_method('annotate') - - -# COPYRIGHT AND LICENSE -# -# Copyright 2001 Gareth Rees. All rights reserved. -# Copyright 2004-2010 Ned Batchelder. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the -# distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py deleted file mode 100644 index af5fa9f6..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Coverage.py's main entrypoint.""" -from coverage.cmdline import main -main() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py deleted file mode 100644 index a556d853..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/annotate.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Source file annotation for Coverage.""" - -import os, re - -from coverage.report import Reporter - -class AnnotateReporter(Reporter): - """Generate annotated source files showing line coverage. - - This reporter creates annotated copies of the measured source files. Each - .py file is copied as a .py,cover file, with a left-hand margin annotating - each line:: - - > def h(x): - - if 0: #pragma: no cover - - pass - > if x == 1: - ! a = 1 - > else: - > a = 2 - - > h(2) - - Executed lines use '>', lines not executed use '!', lines excluded from - consideration use '-'. - - """ - - def __init__(self, coverage, ignore_errors=False): - super(AnnotateReporter, self).__init__(coverage, ignore_errors) - self.directory = None - - blank_re = re.compile(r"\s*(#|$)") - else_re = re.compile(r"\s*else\s*:\s*(#|$)") - - def report(self, morfs, config, directory=None): - """Run the report. - - See `coverage.report()` for arguments. - - """ - self.report_files(self.annotate_file, morfs, config, directory) - - def annotate_file(self, cu, analysis): - """Annotate a single file. - - `cu` is the CodeUnit for the file to annotate. - - """ - if not cu.relative: - return - - filename = cu.filename - source = cu.source_file() - if self.directory: - dest_file = os.path.join(self.directory, cu.flat_rootname()) - dest_file += ".py,cover" - else: - dest_file = filename + ",cover" - dest = open(dest_file, 'w') - - statements = analysis.statements - missing = analysis.missing - excluded = analysis.excluded - - lineno = 0 - i = 0 - j = 0 - covered = True - while True: - line = source.readline() - if line == '': - break - lineno += 1 - while i < len(statements) and statements[i] < lineno: - i += 1 - while j < len(missing) and missing[j] < lineno: - j += 1 - if i < len(statements) and statements[i] == lineno: - covered = j >= len(missing) or missing[j] > lineno - if self.blank_re.match(line): - dest.write(' ') - elif self.else_re.match(line): - # Special logic for lines containing only 'else:'. - if i >= len(statements) and j >= len(missing): - dest.write('! ') - elif i >= len(statements) or j >= len(missing): - dest.write('> ') - elif statements[i] == missing[j]: - dest.write('! ') - else: - dest.write('> ') - elif lineno in excluded: - dest.write('- ') - elif covered: - dest.write('> ') - else: - dest.write('! ') - dest.write(line) - source.close() - dest.close() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py deleted file mode 100644 index 425bcc6e..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/backward.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Add things to old Pythons so I can pretend they are newer.""" - -# This file does lots of tricky stuff, so disable a bunch of lintisms. -# pylint: disable-msg=F0401,W0611,W0622 -# F0401: Unable to import blah -# W0611: Unused import blah -# W0622: Redefining built-in blah - -import os, sys - -# Python 2.3 doesn't have `set` -try: - set = set # new in 2.4 -except NameError: - from sets import Set as set - -# Python 2.3 doesn't have `sorted`. -try: - sorted = sorted -except NameError: - def sorted(iterable): - """A 2.3-compatible implementation of `sorted`.""" - lst = list(iterable) - lst.sort() - return lst - -# Pythons 2 and 3 differ on where to get StringIO -try: - from cStringIO import StringIO - BytesIO = StringIO -except ImportError: - from io import StringIO, BytesIO - -# What's a string called? -try: - string_class = basestring -except NameError: - string_class = str - -# Where do pickles come from? -try: - import cPickle as pickle -except ImportError: - import pickle - -# range or xrange? -try: - range = xrange -except NameError: - range = range - -# Exec is a statement in Py2, a function in Py3 -if sys.version_info >= (3, 0): - def exec_code_object(code, global_map): - """A wrapper around exec().""" - exec(code, global_map) -else: - # OK, this is pretty gross. In Py2, exec was a statement, but that will - # be a syntax error if we try to put it in a Py3 file, even if it is never - # executed. So hide it inside an evaluated string literal instead. - eval( - compile( - "def exec_code_object(code, global_map):\n" - " exec code in global_map\n", - "", "exec" - ) - ) - -# ConfigParser was renamed to the more-standard configparser -try: - import configparser -except ImportError: - import ConfigParser as configparser diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py deleted file mode 100644 index ab522d6c..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/bytecode.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Bytecode manipulation for coverage.py""" - -import opcode, sys, types - -class ByteCode(object): - """A single bytecode.""" - def __init__(self): - self.offset = -1 - self.op = -1 - self.arg = -1 - self.next_offset = -1 - self.jump_to = -1 - - -class ByteCodes(object): - """Iterator over byte codes in `code`. - - Returns `ByteCode` objects. - - """ - def __init__(self, code): - self.code = code - self.offset = 0 - - if sys.version_info >= (3, 0): - def __getitem__(self, i): - return self.code[i] - else: - def __getitem__(self, i): - return ord(self.code[i]) - - def __iter__(self): - return self - - def __next__(self): - if self.offset >= len(self.code): - raise StopIteration - - bc = ByteCode() - bc.op = self[self.offset] - bc.offset = self.offset - - next_offset = self.offset+1 - if bc.op >= opcode.HAVE_ARGUMENT: - bc.arg = self[self.offset+1] + 256*self[self.offset+2] - next_offset += 2 - - label = -1 - if bc.op in opcode.hasjrel: - label = next_offset + bc.arg - elif bc.op in opcode.hasjabs: - label = bc.arg - bc.jump_to = label - - bc.next_offset = self.offset = next_offset - return bc - - next = __next__ # Py2k uses an old-style non-dunder name. - - -class CodeObjects(object): - """Iterate over all the code objects in `code`.""" - def __init__(self, code): - self.stack = [code] - - def __iter__(self): - return self - - def __next__(self): - if self.stack: - # We're going to return the code object on the stack, but first - # push its children for later returning. - code = self.stack.pop() - for c in code.co_consts: - if isinstance(c, types.CodeType): - self.stack.append(c) - return code - - raise StopIteration - - next = __next__ diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py deleted file mode 100644 index e5d6bb84..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/cmdline.py +++ /dev/null @@ -1,661 +0,0 @@ -"""Command-line support for Coverage.""" - -import optparse, re, sys, traceback - -from coverage.backward import sorted # pylint: disable-msg=W0622 -from coverage.execfile import run_python_file -from coverage.misc import CoverageException, ExceptionDuringRun - - -class Opts(object): - """A namespace class for individual options we'll build parsers from.""" - - append = optparse.make_option( - '-a', '--append', action='store_false', dest="erase_first", - help="Append coverage data to .coverage, otherwise it is started " - "clean with each run." - ) - branch = optparse.make_option( - '', '--branch', action='store_true', - help="Measure branch coverage in addition to statement coverage." - ) - directory = optparse.make_option( - '-d', '--directory', action='store', - metavar="DIR", - help="Write the output files to DIR." - ) - help = optparse.make_option( - '-h', '--help', action='store_true', - help="Get help on this command." - ) - ignore_errors = optparse.make_option( - '-i', '--ignore-errors', action='store_true', - help="Ignore errors while reading source files." - ) - include = optparse.make_option( - '', '--include', action='store', - metavar="PAT1,PAT2,...", - help="Include files only when their filename path matches one of " - "these patterns. Usually needs quoting on the command line." - ) - pylib = optparse.make_option( - '-L', '--pylib', action='store_true', - help="Measure coverage even inside the Python installed library, " - "which isn't done by default." - ) - show_missing = optparse.make_option( - '-m', '--show-missing', action='store_true', - help="Show line numbers of statements in each module that weren't " - "executed." - ) - old_omit = optparse.make_option( - '-o', '--omit', action='store', - metavar="PAT1,PAT2,...", - help="Omit files when their filename matches one of these patterns. " - "Usually needs quoting on the command line." - ) - omit = optparse.make_option( - '', '--omit', action='store', - metavar="PAT1,PAT2,...", - help="Omit files when their filename matches one of these patterns. " - "Usually needs quoting on the command line." - ) - output_xml = optparse.make_option( - '-o', '', action='store', dest="outfile", - metavar="OUTFILE", - help="Write the XML report to this file. Defaults to 'coverage.xml'" - ) - parallel_mode = optparse.make_option( - '-p', '--parallel-mode', action='store_true', - help="Append the machine name, process id and random number to the " - ".coverage data file name to simplify collecting data from " - "many processes." - ) - rcfile = optparse.make_option( - '', '--rcfile', action='store', - help="Specify configuration file. Defaults to '.coveragerc'" - ) - source = optparse.make_option( - '', '--source', action='store', metavar="SRC1,SRC2,...", - help="A list of packages or directories of code to be measured." - ) - timid = optparse.make_option( - '', '--timid', action='store_true', - help="Use a simpler but slower trace method. Try this if you get " - "seemingly impossible results!" - ) - version = optparse.make_option( - '', '--version', action='store_true', - help="Display version information and exit." - ) - - -class CoverageOptionParser(optparse.OptionParser, object): - """Base OptionParser for coverage. - - Problems don't exit the program. - Defaults are initialized for all options. - - """ - - def __init__(self, *args, **kwargs): - super(CoverageOptionParser, self).__init__( - add_help_option=False, *args, **kwargs - ) - self.set_defaults( - actions=[], - branch=None, - directory=None, - help=None, - ignore_errors=None, - include=None, - omit=None, - parallel_mode=None, - pylib=None, - rcfile=True, - show_missing=None, - source=None, - timid=None, - erase_first=None, - version=None, - ) - - self.disable_interspersed_args() - self.help_fn = self.help_noop - - def help_noop(self, error=None, topic=None, parser=None): - """No-op help function.""" - pass - - class OptionParserError(Exception): - """Used to stop the optparse error handler ending the process.""" - pass - - def parse_args(self, args=None, options=None): - """Call optparse.parse_args, but return a triple: - - (ok, options, args) - - """ - try: - options, args = \ - super(CoverageOptionParser, self).parse_args(args, options) - except self.OptionParserError: - return False, None, None - return True, options, args - - def error(self, msg): - """Override optparse.error so sys.exit doesn't get called.""" - self.help_fn(msg) - raise self.OptionParserError - - -class ClassicOptionParser(CoverageOptionParser): - """Command-line parser for coverage.py classic arguments.""" - - def __init__(self): - super(ClassicOptionParser, self).__init__() - - self.add_action('-a', '--annotate', 'annotate') - self.add_action('-b', '--html', 'html') - self.add_action('-c', '--combine', 'combine') - self.add_action('-e', '--erase', 'erase') - self.add_action('-r', '--report', 'report') - self.add_action('-x', '--execute', 'execute') - - self.add_options([ - Opts.directory, - Opts.help, - Opts.ignore_errors, - Opts.pylib, - Opts.show_missing, - Opts.old_omit, - Opts.parallel_mode, - Opts.timid, - Opts.version, - ]) - - def add_action(self, dash, dashdash, action_code): - """Add a specialized option that is the action to execute.""" - option = self.add_option(dash, dashdash, action='callback', - callback=self._append_action - ) - option.action_code = action_code - - def _append_action(self, option, opt_unused, value_unused, parser): - """Callback for an option that adds to the `actions` list.""" - parser.values.actions.append(option.action_code) - - -class CmdOptionParser(CoverageOptionParser): - """Parse one of the new-style commands for coverage.py.""" - - def __init__(self, action, options=None, defaults=None, usage=None, - cmd=None, description=None - ): - """Create an OptionParser for a coverage command. - - `action` is the slug to put into `options.actions`. - `options` is a list of Option's for the command. - `defaults` is a dict of default value for options. - `usage` is the usage string to display in help. - `cmd` is the command name, if different than `action`. - `description` is the description of the command, for the help text. - - """ - if usage: - usage = "%prog " + usage - super(CmdOptionParser, self).__init__( - prog="coverage %s" % (cmd or action), - usage=usage, - description=description, - ) - self.set_defaults(actions=[action], **(defaults or {})) - if options: - self.add_options(options) - self.cmd = cmd or action - - def __eq__(self, other): - # A convenience equality, so that I can put strings in unit test - # results, and they will compare equal to objects. - return (other == "" % self.cmd) - -GLOBAL_ARGS = [ - Opts.rcfile, - Opts.help, - ] - -CMDS = { - 'annotate': CmdOptionParser("annotate", - [ - Opts.directory, - Opts.ignore_errors, - Opts.omit, - Opts.include, - ] + GLOBAL_ARGS, - usage = "[options] [modules]", - description = "Make annotated copies of the given files, marking " - "statements that are executed with > and statements that are " - "missed with !." - ), - - 'combine': CmdOptionParser("combine", GLOBAL_ARGS, - usage = " ", - description = "Combine data from multiple coverage files collected " - "with 'run -p'. The combined results are written to a single " - "file representing the union of the data." - ), - - 'debug': CmdOptionParser("debug", GLOBAL_ARGS, - usage = "", - description = "Display information on the internals of coverage.py, " - "for diagnosing problems. " - "Topics are 'data' to show a summary of the collected data, " - "or 'sys' to show installation information." - ), - - 'erase': CmdOptionParser("erase", GLOBAL_ARGS, - usage = " ", - description = "Erase previously collected coverage data." - ), - - 'help': CmdOptionParser("help", GLOBAL_ARGS, - usage = "[command]", - description = "Describe how to use coverage.py" - ), - - 'html': CmdOptionParser("html", - [ - Opts.directory, - Opts.ignore_errors, - Opts.omit, - Opts.include, - ] + GLOBAL_ARGS, - usage = "[options] [modules]", - description = "Create an HTML report of the coverage of the files. " - "Each file gets its own page, with the source decorated to show " - "executed, excluded, and missed lines." - ), - - 'report': CmdOptionParser("report", - [ - Opts.ignore_errors, - Opts.omit, - Opts.include, - Opts.show_missing, - ] + GLOBAL_ARGS, - usage = "[options] [modules]", - description = "Report coverage statistics on modules." - ), - - 'run': CmdOptionParser("execute", - [ - Opts.append, - Opts.branch, - Opts.pylib, - Opts.parallel_mode, - Opts.timid, - Opts.source, - Opts.omit, - Opts.include, - ] + GLOBAL_ARGS, - defaults = {'erase_first': True}, - cmd = "run", - usage = "[options] [program options]", - description = "Run a Python program, measuring code execution." - ), - - 'xml': CmdOptionParser("xml", - [ - Opts.ignore_errors, - Opts.omit, - Opts.include, - Opts.output_xml, - ] + GLOBAL_ARGS, - cmd = "xml", - defaults = {'outfile': 'coverage.xml'}, - usage = "[options] [modules]", - description = "Generate an XML report of coverage results." - ), - } - - -OK, ERR = 0, 1 - - -class CoverageScript(object): - """The command-line interface to Coverage.""" - - def __init__(self, _covpkg=None, _run_python_file=None, _help_fn=None): - # _covpkg is for dependency injection, so we can test this code. - if _covpkg: - self.covpkg = _covpkg - else: - import coverage - self.covpkg = coverage - - # _run_python_file is for dependency injection also. - self.run_python_file = _run_python_file or run_python_file - - # _help_fn is for dependency injection. - self.help_fn = _help_fn or self.help - - self.coverage = None - - def help(self, error=None, topic=None, parser=None): - """Display an error message, or the named topic.""" - assert error or topic or parser - if error: - print(error) - print("Use 'coverage help' for help.") - elif parser: - print(parser.format_help().strip()) - else: - # Parse out the topic we want from HELP_TOPICS - topic_list = re.split("(?m)^=+ (\w+) =+$", HELP_TOPICS) - topics = dict(zip(topic_list[1::2], topic_list[2::2])) - help_msg = topics.get(topic, '').strip() - if help_msg: - print(help_msg % self.covpkg.__dict__) - else: - print("Don't know topic %r" % topic) - - def command_line(self, argv): - """The bulk of the command line interface to Coverage. - - `argv` is the argument list to process. - - Returns 0 if all is well, 1 if something went wrong. - - """ - # Collect the command-line options. - - if not argv: - self.help_fn(topic='minimum_help') - return OK - - # The command syntax we parse depends on the first argument. Classic - # syntax always starts with an option. - classic = argv[0].startswith('-') - if classic: - parser = ClassicOptionParser() - else: - parser = CMDS.get(argv[0]) - if not parser: - self.help_fn("Unknown command: '%s'" % argv[0]) - return ERR - argv = argv[1:] - - parser.help_fn = self.help_fn - ok, options, args = parser.parse_args(argv) - if not ok: - return ERR - - # Handle help. - if options.help: - if classic: - self.help_fn(topic='help') - else: - self.help_fn(parser=parser) - return OK - - if "help" in options.actions: - if args: - for a in args: - parser = CMDS.get(a) - if parser: - self.help_fn(parser=parser) - else: - self.help_fn(topic=a) - else: - self.help_fn(topic='help') - return OK - - # Handle version. - if options.version: - self.help_fn(topic='version') - return OK - - # Check for conflicts and problems in the options. - for i in ['erase', 'execute']: - for j in ['annotate', 'html', 'report', 'combine']: - if (i in options.actions) and (j in options.actions): - self.help_fn("You can't specify the '%s' and '%s' " - "options at the same time." % (i, j)) - return ERR - - if not options.actions: - self.help_fn( - "You must specify at least one of -e, -x, -c, -r, -a, or -b." - ) - return ERR - args_allowed = ( - 'execute' in options.actions or - 'annotate' in options.actions or - 'html' in options.actions or - 'debug' in options.actions or - 'report' in options.actions or - 'xml' in options.actions - ) - if not args_allowed and args: - self.help_fn("Unexpected arguments: %s" % " ".join(args)) - return ERR - - if 'execute' in options.actions and not args: - self.help_fn("Nothing to do.") - return ERR - - # Listify the list options. - source = unshell_list(options.source) - omit = unshell_list(options.omit) - include = unshell_list(options.include) - - # Do something. - self.coverage = self.covpkg.coverage( - data_suffix = options.parallel_mode, - cover_pylib = options.pylib, - timid = options.timid, - branch = options.branch, - config_file = options.rcfile, - source = source, - omit = omit, - include = include, - ) - - if 'debug' in options.actions: - if not args: - self.help_fn("What information would you like: data, sys?") - return ERR - for info in args: - if info == 'sys': - print("-- sys ----------------------------------------") - for label, info in self.coverage.sysinfo(): - if info == []: - info = "-none-" - if isinstance(info, list): - print("%15s:" % label) - for e in info: - print("%15s %s" % ("", e)) - else: - print("%15s: %s" % (label, info)) - elif info == 'data': - print("-- data ---------------------------------------") - self.coverage.load() - print("path: %s" % self.coverage.data.filename) - print("has_arcs: %r" % self.coverage.data.has_arcs()) - summary = self.coverage.data.summary(fullpath=True) - if summary: - filenames = sorted(summary.keys()) - print("\n%d files:" % len(filenames)) - for f in filenames: - print("%s: %d lines" % (f, summary[f])) - else: - print("No data collected") - else: - self.help_fn("Don't know what you mean by %r" % info) - return ERR - return OK - - if 'erase' in options.actions or options.erase_first: - self.coverage.erase() - else: - self.coverage.load() - - if 'execute' in options.actions: - # Run the script. - self.coverage.start() - try: - self.run_python_file(args[0], args) - finally: - self.coverage.stop() - self.coverage.save() - - if 'combine' in options.actions: - self.coverage.combine() - self.coverage.save() - - # Remaining actions are reporting, with some common options. - report_args = dict( - morfs = args, - ignore_errors = options.ignore_errors, - omit = omit, - include = include, - ) - - if 'report' in options.actions: - self.coverage.report( - show_missing=options.show_missing, **report_args) - if 'annotate' in options.actions: - self.coverage.annotate( - directory=options.directory, **report_args) - if 'html' in options.actions: - self.coverage.html_report( - directory=options.directory, **report_args) - if 'xml' in options.actions: - outfile = options.outfile - self.coverage.xml_report(outfile=outfile, **report_args) - - return OK - - -def unshell_list(s): - """Turn a command-line argument into a list.""" - if not s: - return None - if sys.platform == 'win32': - # When running coverage as coverage.exe, some of the behavior - # of the shell is emulated: wildcards are expanded into a list of - # filenames. So you have to single-quote patterns on the command - # line, but (not) helpfully, the single quotes are included in the - # argument, so we have to strip them off here. - s = s.strip("'") - return s.split(',') - - -HELP_TOPICS = r""" - -== classic ==================================================================== -Coverage.py version %(__version__)s -Measure, collect, and report on code coverage in Python programs. - -Usage: - -coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] - Execute the module, passing the given command-line arguments, collecting - coverage data. With the -p option, include the machine name and process - id in the .coverage file name. With -L, measure coverage even inside the - Python installed library, which isn't done by default. With --timid, use a - simpler but slower trace method. - -coverage -e - Erase collected coverage data. - -coverage -c - Combine data from multiple coverage files (as created by -p option above) - and store it into a single file representing the union of the coverage. - -coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...] - Report on the statement coverage for the given files. With the -m - option, show line numbers of the statements that weren't executed. - -coverage -b -d DIR [-i] [-o DIR,...] [FILE1 FILE2 ...] - Create an HTML report of the coverage of the given files. Each file gets - its own page, with the file listing decorated to show executed, excluded, - and missed lines. - -coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...] - Make annotated copies of the given files, marking statements that - are executed with > and statements that are missed with !. - --d DIR - Write output files for -b or -a to this directory. - --i Ignore errors while reporting or annotating. - --o DIR,... - Omit reporting or annotating files when their filename path starts with - a directory listed in the omit list. - e.g. coverage -i -r -o c:\python25,lib\enthought\traits - -Coverage data is saved in the file .coverage by default. Set the -COVERAGE_FILE environment variable to save it somewhere else. - -== help ======================================================================= -Coverage.py, version %(__version__)s -Measure, collect, and report on code coverage in Python programs. - -usage: coverage [options] [args] - -Commands: - annotate Annotate source files with execution information. - combine Combine a number of data files. - erase Erase previously collected coverage data. - help Get help on using coverage.py. - html Create an HTML report. - report Report coverage stats on modules. - run Run a Python program and measure code execution. - xml Create an XML report of coverage results. - -Use "coverage help " for detailed help on any command. -Use "coverage help classic" for help on older command syntax. -For more information, see %(__url__)s - -== minimum_help =============================================================== -Code coverage for Python. Use 'coverage help' for help. - -== version ==================================================================== -Coverage.py, version %(__version__)s. %(__url__)s - -""" - - -def main(argv=None): - """The main entrypoint to Coverage. - - This is installed as the script entrypoint. - - """ - if argv is None: - argv = sys.argv[1:] - try: - status = CoverageScript().command_line(argv) - except ExceptionDuringRun: - # An exception was caught while running the product code. The - # sys.exc_info() return tuple is packed into an ExceptionDuringRun - # exception. - _, err, _ = sys.exc_info() - traceback.print_exception(*err.args) - status = ERR - except CoverageException: - # A controlled error inside coverage.py: print the message to the user. - _, err, _ = sys.exc_info() - print(err) - status = ERR - except SystemExit: - # The user called `sys.exit()`. Exit with their argument, if any. - _, err, _ = sys.exc_info() - if err.args: - status = err.args[0] - else: - status = None - return status diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py deleted file mode 100644 index dfc4560d..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/codeunit.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Code unit (module) handling for Coverage.""" - -import glob, os - -from coverage.backward import string_class, StringIO -from coverage.misc import CoverageException - - -def code_unit_factory(morfs, file_locator): - """Construct a list of CodeUnits from polymorphic inputs. - - `morfs` is a module or a filename, or a list of same. - - `file_locator` is a FileLocator that can help resolve filenames. - - Returns a list of CodeUnit objects. - - """ - # Be sure we have a list. - if not isinstance(morfs, (list, tuple)): - morfs = [morfs] - - # On Windows, the shell doesn't expand wildcards. Do it here. - globbed = [] - for morf in morfs: - if isinstance(morf, string_class) and ('?' in morf or '*' in morf): - globbed.extend(glob.glob(morf)) - else: - globbed.append(morf) - morfs = globbed - - code_units = [CodeUnit(morf, file_locator) for morf in morfs] - - return code_units - - -class CodeUnit(object): - """Code unit: a filename or module. - - Instance attributes: - - `name` is a human-readable name for this code unit. - `filename` is the os path from which we can read the source. - `relative` is a boolean. - - """ - def __init__(self, morf, file_locator): - self.file_locator = file_locator - - if hasattr(morf, '__file__'): - f = morf.__file__ - else: - f = morf - # .pyc files should always refer to a .py instead. - if f.endswith('.pyc'): - f = f[:-1] - self.filename = self.file_locator.canonical_filename(f) - - if hasattr(morf, '__name__'): - n = modname = morf.__name__ - self.relative = True - else: - n = os.path.splitext(morf)[0] - rel = self.file_locator.relative_filename(n) - if os.path.isabs(n): - self.relative = (rel != n) - else: - self.relative = True - n = rel - modname = None - self.name = n - self.modname = modname - - def __repr__(self): - return "" % (self.name, self.filename) - - # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all - # of them defined. - - def __lt__(self, other): return self.name < other.name - def __le__(self, other): return self.name <= other.name - def __eq__(self, other): return self.name == other.name - def __ne__(self, other): return self.name != other.name - def __gt__(self, other): return self.name > other.name - def __ge__(self, other): return self.name >= other.name - - def flat_rootname(self): - """A base for a flat filename to correspond to this code unit. - - Useful for writing files about the code where you want all the files in - the same directory, but need to differentiate same-named files from - different directories. - - For example, the file a/b/c.py might return 'a_b_c' - - """ - if self.modname: - return self.modname.replace('.', '_') - else: - root = os.path.splitdrive(self.name)[1] - return root.replace('\\', '_').replace('/', '_').replace('.', '_') - - def source_file(self): - """Return an open file for reading the source of the code unit.""" - if os.path.exists(self.filename): - # A regular text file: open it. - return open(self.filename) - - # Maybe it's in a zip file? - source = self.file_locator.get_zip_data(self.filename) - if source is not None: - return StringIO(source) - - # Couldn't find source. - raise CoverageException( - "No source for code %r." % self.filename - ) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py deleted file mode 100644 index decdba9f..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/collector.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Raw data collector for Coverage.""" - -import sys, threading - -try: - # Use the C extension code when we can, for speed. - from coverage.tracer import Tracer -except ImportError: - # Couldn't import the C extension, maybe it isn't built. - Tracer = None - - -class PyTracer(object): - """Python implementation of the raw data tracer.""" - - # Because of poor implementations of trace-function-manipulating tools, - # the Python trace function must be kept very simple. In particular, there - # must be only one function ever set as the trace function, both through - # sys.settrace, and as the return value from the trace function. Put - # another way, the trace function must always return itself. It cannot - # swap in other functions, or return None to avoid tracing a particular - # frame. - # - # The trace manipulator that introduced this restriction is DecoratorTools, - # which sets a trace function, and then later restores the pre-existing one - # by calling sys.settrace with a function it found in the current frame. - # - # Systems that use DecoratorTools (or similar trace manipulations) must use - # PyTracer to get accurate results. The command-line --timid argument is - # used to force the use of this tracer. - - def __init__(self): - self.data = None - self.should_trace = None - self.should_trace_cache = None - self.cur_file_data = None - self.last_line = 0 - self.data_stack = [] - self.last_exc_back = None - self.last_exc_firstlineno = 0 - self.arcs = False - - def _trace(self, frame, event, arg_unused): - """The trace function passed to sys.settrace.""" - - #print "trace event: %s %r @%d" % ( - # event, frame.f_code.co_filename, frame.f_lineno) - - if self.last_exc_back: - if frame == self.last_exc_back: - # Someone forgot a return event. - if self.arcs and self.cur_file_data: - pair = (self.last_line, -self.last_exc_firstlineno) - self.cur_file_data[pair] = None - self.cur_file_data, self.last_line = self.data_stack.pop() - self.last_exc_back = None - - if event == 'call': - # Entering a new function context. Decide if we should trace - # in this file. - self.data_stack.append((self.cur_file_data, self.last_line)) - filename = frame.f_code.co_filename - tracename = self.should_trace_cache.get(filename) - if tracename is None: - tracename = self.should_trace(filename, frame) - self.should_trace_cache[filename] = tracename - if tracename: - if tracename not in self.data: - self.data[tracename] = {} - self.cur_file_data = self.data[tracename] - else: - self.cur_file_data = None - # Set the last_line to -1 because the next arc will be entering a - # code block, indicated by (-1, n). - self.last_line = -1 - elif event == 'line': - # Record an executed line. - if self.cur_file_data is not None: - if self.arcs: - #print "lin", self.last_line, frame.f_lineno - self.cur_file_data[(self.last_line, frame.f_lineno)] = None - else: - #print "lin", frame.f_lineno - self.cur_file_data[frame.f_lineno] = None - self.last_line = frame.f_lineno - elif event == 'return': - if self.arcs and self.cur_file_data: - first = frame.f_code.co_firstlineno - self.cur_file_data[(self.last_line, -first)] = None - # Leaving this function, pop the filename stack. - self.cur_file_data, self.last_line = self.data_stack.pop() - elif event == 'exception': - #print "exc", self.last_line, frame.f_lineno - self.last_exc_back = frame.f_back - self.last_exc_firstlineno = frame.f_code.co_firstlineno - return self._trace - - def start(self): - """Start this Tracer. - - Return a Python function suitable for use with sys.settrace(). - - """ - sys.settrace(self._trace) - return self._trace - - def stop(self): - """Stop this Tracer.""" - sys.settrace(None) - - def get_stats(self): - """Return a dictionary of statistics, or None.""" - return None - - -class Collector(object): - """Collects trace data. - - Creates a Tracer object for each thread, since they track stack - information. Each Tracer points to the same shared data, contributing - traced data points. - - When the Collector is started, it creates a Tracer for the current thread, - and installs a function to create Tracers for each new thread started. - When the Collector is stopped, all active Tracers are stopped. - - Threads started while the Collector is stopped will never have Tracers - associated with them. - - """ - - # The stack of active Collectors. Collectors are added here when started, - # and popped when stopped. Collectors on the stack are paused when not - # the top, and resumed when they become the top again. - _collectors = [] - - def __init__(self, should_trace, timid, branch): - """Create a collector. - - `should_trace` is a function, taking a filename, and returning a - canonicalized filename, or False depending on whether the file should - be traced or not. - - If `timid` is true, then a slower simpler trace function will be - used. This is important for some environments where manipulation of - tracing functions make the faster more sophisticated trace function not - operate properly. - - If `branch` is true, then branches will be measured. This involves - collecting data on which statements followed each other (arcs). Use - `get_arc_data` to get the arc data. - - """ - self.should_trace = should_trace - self.branch = branch - self.reset() - - if timid: - # Being timid: use the simple Python trace function. - self._trace_class = PyTracer - else: - # Being fast: use the C Tracer if it is available, else the Python - # trace function. - self._trace_class = Tracer or PyTracer - - def __repr__(self): - return "" % id(self) - - def tracer_name(self): - """Return the class name of the tracer we're using.""" - return self._trace_class.__name__ - - def reset(self): - """Clear collected data, and prepare to collect more.""" - # A dictionary mapping filenames to dicts with linenumber keys, - # or mapping filenames to dicts with linenumber pairs as keys. - self.data = {} - - # A cache of the results from should_trace, the decision about whether - # to trace execution in a file. A dict of filename to (filename or - # False). - self.should_trace_cache = {} - - # Our active Tracers. - self.tracers = [] - - def _start_tracer(self): - """Start a new Tracer object, and store it in self.tracers.""" - tracer = self._trace_class() - tracer.data = self.data - tracer.arcs = self.branch - tracer.should_trace = self.should_trace - tracer.should_trace_cache = self.should_trace_cache - fn = tracer.start() - self.tracers.append(tracer) - return fn - - # The trace function has to be set individually on each thread before - # execution begins. Ironically, the only support the threading module has - # for running code before the thread main is the tracing function. So we - # install this as a trace function, and the first time it's called, it does - # the real trace installation. - - def _installation_trace(self, frame_unused, event_unused, arg_unused): - """Called on new threads, installs the real tracer.""" - # Remove ourselves as the trace function - sys.settrace(None) - # Install the real tracer. - fn = self._start_tracer() - # Invoke the real trace function with the current event, to be sure - # not to lose an event. - if fn: - fn = fn(frame_unused, event_unused, arg_unused) - # Return the new trace function to continue tracing in this scope. - return fn - - def start(self): - """Start collecting trace information.""" - if self._collectors: - self._collectors[-1].pause() - self._collectors.append(self) - #print >>sys.stderr, "Started: %r" % self._collectors - # Install the tracer on this thread. - self._start_tracer() - # Install our installation tracer in threading, to jump start other - # threads. - threading.settrace(self._installation_trace) - - def stop(self): - """Stop collecting trace information.""" - #print >>sys.stderr, "Stopping: %r" % self._collectors - assert self._collectors - assert self._collectors[-1] is self - - self.pause() - self.tracers = [] - - # Remove this Collector from the stack, and resume the one underneath - # (if any). - self._collectors.pop() - if self._collectors: - self._collectors[-1].resume() - - def pause(self): - """Pause tracing, but be prepared to `resume`.""" - for tracer in self.tracers: - tracer.stop() - stats = tracer.get_stats() - if stats: - print("\nCoverage.py tracer stats:") - for k in sorted(stats.keys()): - print("%16s: %s" % (k, stats[k])) - threading.settrace(None) - - def resume(self): - """Resume tracing after a `pause`.""" - for tracer in self.tracers: - tracer.start() - threading.settrace(self._installation_trace) - - def get_line_data(self): - """Return the line data collected. - - Data is { filename: { lineno: None, ...}, ...} - - """ - if self.branch: - # If we were measuring branches, then we have to re-build the dict - # to show line data. - line_data = {} - for f, arcs in self.data.items(): - line_data[f] = ldf = {} - for l1, _ in list(arcs.keys()): - if l1: - ldf[l1] = None - return line_data - else: - return self.data - - def get_arc_data(self): - """Return the arc data collected. - - Data is { filename: { (l1, l2): None, ...}, ...} - - Note that no data is collected or returned if the Collector wasn't - created with `branch` true. - - """ - if self.branch: - return self.data - else: - return {} diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py deleted file mode 100644 index 1f6a879f..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/config.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Config file for coverage.py""" - -import os -from coverage.backward import configparser # pylint: disable-msg=W0622 - - -class CoverageConfig(object): - """Coverage.py configuration. - - The attributes of this class are the various settings that control the - operation of coverage.py. - - """ - - def __init__(self): - """Initialize the configuration attributes to their defaults.""" - # Defaults for [run] - self.branch = False - self.cover_pylib = False - self.data_file = ".coverage" - self.parallel = False - self.timid = False - self.source = None - - # Defaults for [report] - self.exclude_list = ['(?i)# *pragma[: ]*no *cover'] - self.ignore_errors = False - self.omit = None - self.include = None - self.precision = 0 - - # Defaults for [html] - self.html_dir = "htmlcov" - - # Defaults for [xml] - self.xml_output = "coverage.xml" - - def from_environment(self, env_var): - """Read configuration from the `env_var` environment variable.""" - # Timidity: for nose users, read an environment variable. This is a - # cheap hack, since the rest of the command line arguments aren't - # recognized, but it solves some users' problems. - env = os.environ.get(env_var, '') - if env: - self.timid = ('--timid' in env) - - def from_args(self, **kwargs): - """Read config values from `kwargs`.""" - for k, v in kwargs.items(): - if v is not None: - setattr(self, k, v) - - def from_file(self, *files): - """Read configuration from .rc files. - - Each argument in `files` is a file name to read. - - """ - cp = configparser.RawConfigParser() - cp.read(files) - - # [run] - if cp.has_option('run', 'branch'): - self.branch = cp.getboolean('run', 'branch') - if cp.has_option('run', 'cover_pylib'): - self.cover_pylib = cp.getboolean('run', 'cover_pylib') - if cp.has_option('run', 'data_file'): - self.data_file = cp.get('run', 'data_file') - if cp.has_option('run', 'parallel'): - self.parallel = cp.getboolean('run', 'parallel') - if cp.has_option('run', 'timid'): - self.timid = cp.getboolean('run', 'timid') - if cp.has_option('run', 'source'): - self.source = self.get_list(cp, 'run', 'source') - if cp.has_option('run', 'omit'): - self.omit = self.get_list(cp, 'run', 'omit') - if cp.has_option('run', 'include'): - self.include = self.get_list(cp, 'run', 'include') - - # [report] - if cp.has_option('report', 'exclude_lines'): - # exclude_lines is a list of lines, leave out the blank ones. - exclude_list = cp.get('report', 'exclude_lines') - self.exclude_list = list(filter(None, exclude_list.split('\n'))) - if cp.has_option('report', 'ignore_errors'): - self.ignore_errors = cp.getboolean('report', 'ignore_errors') - if cp.has_option('report', 'omit'): - self.omit = self.get_list(cp, 'report', 'omit') - if cp.has_option('report', 'include'): - self.include = self.get_list(cp, 'report', 'include') - if cp.has_option('report', 'precision'): - self.precision = cp.getint('report', 'precision') - - # [html] - if cp.has_option('html', 'directory'): - self.html_dir = cp.get('html', 'directory') - - # [xml] - if cp.has_option('xml', 'output'): - self.xml_output = cp.get('xml', 'output') - - def get_list(self, cp, section, option): - """Read a list of strings from the ConfigParser `cp`. - - The value of `section` and `option` is treated as a comma- and newline- - separated list of strings. Each value is stripped of whitespace. - - Returns the list of strings. - - """ - value_list = cp.get(section, option) - values = [] - for value_line in value_list.split('\n'): - for value in value_line.split(','): - value = value.strip() - if value: - values.append(value) - return values diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py deleted file mode 100644 index 28c8850f..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/control.py +++ /dev/null @@ -1,624 +0,0 @@ -"""Core control stuff for Coverage.""" - -import atexit, os, random, socket, sys - -from coverage.annotate import AnnotateReporter -from coverage.backward import string_class -from coverage.codeunit import code_unit_factory, CodeUnit -from coverage.collector import Collector -from coverage.config import CoverageConfig -from coverage.data import CoverageData -from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher -from coverage.files import find_python_files -from coverage.html import HtmlReporter -from coverage.misc import CoverageException, bool_or_none -from coverage.results import Analysis, Numbers -from coverage.summary import SummaryReporter -from coverage.xmlreport import XmlReporter - -class coverage(object): - """Programmatic access to Coverage. - - To use:: - - from coverage import coverage - - cov = coverage() - cov.start() - #.. blah blah (run your code) blah blah .. - cov.stop() - cov.html_report(directory='covhtml') - - """ - def __init__(self, data_file=None, data_suffix=None, cover_pylib=None, - auto_data=False, timid=None, branch=None, config_file=True, - source=None, omit=None, include=None): - """ - `data_file` is the base name of the data file to use, defaulting to - ".coverage". `data_suffix` is appended (with a dot) to `data_file` to - create the final file name. If `data_suffix` is simply True, then a - suffix is created with the machine and process identity included. - - `cover_pylib` is a boolean determining whether Python code installed - with the Python interpreter is measured. This includes the Python - standard library and any packages installed with the interpreter. - - If `auto_data` is true, then any existing data file will be read when - coverage measurement starts, and data will be saved automatically when - measurement stops. - - If `timid` is true, then a slower and simpler trace function will be - used. This is important for some environments where manipulation of - tracing functions breaks the faster trace function. - - If `branch` is true, then branch coverage will be measured in addition - to the usual statement coverage. - - `config_file` determines what config file to read. If it is a string, - it is the name of the config file to read. If it is True, then a - standard file is read (".coveragerc"). If it is False, then no file is - read. - - `source` is a list of file paths or package names. Only code located - in the trees indicated by the file paths or package names will be - measured. - - `include` and `omit` are lists of filename patterns. Files that match - `include` will be measured, files that match `omit` will not. - - """ - from coverage import __version__ - - # Build our configuration from a number of sources: - # 1: defaults: - self.config = CoverageConfig() - - # 2: from the coveragerc file: - if config_file: - if config_file is True: - config_file = ".coveragerc" - try: - self.config.from_file(config_file) - except ValueError: - _, err, _ = sys.exc_info() - raise CoverageException( - "Couldn't read config file %s: %s" % (config_file, err) - ) - - # 3: from environment variables: - self.config.from_environment('COVERAGE_OPTIONS') - env_data_file = os.environ.get('COVERAGE_FILE') - if env_data_file: - self.config.data_file = env_data_file - - # 4: from constructor arguments: - self.config.from_args( - data_file=data_file, cover_pylib=cover_pylib, timid=timid, - branch=branch, parallel=bool_or_none(data_suffix), - source=source, omit=omit, include=include - ) - - self.auto_data = auto_data - self.atexit_registered = False - - self.exclude_re = "" - self._compile_exclude() - - self.file_locator = FileLocator() - - # The source argument can be directories or package names. - self.source = [] - self.source_pkgs = [] - for src in self.config.source or []: - if os.path.exists(src): - self.source.append(self.file_locator.canonical_filename(src)) - else: - self.source_pkgs.append(src) - - self.omit = self._abs_files(self.config.omit) - self.include = self._abs_files(self.config.include) - - self.collector = Collector( - self._should_trace, timid=self.config.timid, - branch=self.config.branch - ) - - # Suffixes are a bit tricky. We want to use the data suffix only when - # collecting data, not when combining data. So we save it as - # `self.run_suffix` now, and promote it to `self.data_suffix` if we - # find that we are collecting data later. - if data_suffix or self.config.parallel: - if not isinstance(data_suffix, string_class): - # if data_suffix=True, use .machinename.pid.random - data_suffix = True - else: - data_suffix = None - self.data_suffix = None - self.run_suffix = data_suffix - - # Create the data file. We do this at construction time so that the - # data file will be written into the directory where the process - # started rather than wherever the process eventually chdir'd to. - self.data = CoverageData( - basename=self.config.data_file, - collector="coverage v%s" % __version__ - ) - - # The dirs for files considered "installed with the interpreter". - self.pylib_dirs = [] - if not self.config.cover_pylib: - # Look at where some standard modules are located. That's the - # indication for "installed with the interpreter". In some - # environments (virtualenv, for example), these modules may be - # spread across a few locations. Look at all the candidate modules - # we've imported, and take all the different ones. - for m in (atexit, os, random, socket): - if hasattr(m, "__file__"): - m_dir = self._canonical_dir(m.__file__) - if m_dir not in self.pylib_dirs: - self.pylib_dirs.append(m_dir) - - # To avoid tracing the coverage code itself, we skip anything located - # where we are. - self.cover_dir = self._canonical_dir(__file__) - - # The matchers for _should_trace, created when tracing starts. - self.source_match = None - self.pylib_match = self.cover_match = None - self.include_match = self.omit_match = None - - # Only _harvest_data once per measurement cycle. - self._harvested = False - - # Set the reporting precision. - Numbers.set_precision(self.config.precision) - - # When tearing down the coverage object, modules can become None. - # Saving the modules as object attributes avoids problems, but it is - # quite ad-hoc which modules need to be saved and which references - # need to use the object attributes. - self.socket = socket - self.os = os - self.random = random - - def _canonical_dir(self, f): - """Return the canonical directory of the file `f`.""" - return os.path.split(self.file_locator.canonical_filename(f))[0] - - def _source_for_file(self, filename): - """Return the source file for `filename`.""" - if not filename.endswith(".py"): - if filename[-4:-1] == ".py": - filename = filename[:-1] - return filename - - def _should_trace(self, filename, frame): - """Decide whether to trace execution in `filename` - - This function is called from the trace function. As each new file name - is encountered, this function determines whether it is traced or not. - - Returns a canonicalized filename if it should be traced, False if it - should not. - - """ - if os is None: - return False - - if filename.startswith('<'): - # Lots of non-file execution is represented with artificial - # filenames like "", "", or - # "". Don't ever trace these executions, since we - # can't do anything with the data later anyway. - return False - - if filename.endswith(".html"): - # Jinja and maybe other templating systems compile templates into - # Python code, but use the template filename as the filename in - # the compiled code. Of course, those filenames are useless later - # so don't bother collecting. TODO: How should we really separate - # out good file extensions from bad? - return False - - self._check_for_packages() - - # Compiled Python files have two filenames: frame.f_code.co_filename is - # the filename at the time the .pyc was compiled. The second name - # is __file__, which is where the .pyc was actually loaded from. Since - # .pyc files can be moved after compilation (for example, by being - # installed), we look for __file__ in the frame and prefer it to the - # co_filename value. - dunder_file = frame.f_globals.get('__file__') - if dunder_file: - filename = self._source_for_file(dunder_file) - canonical = self.file_locator.canonical_filename(filename) - - # If the user specified source, then that's authoritative about what to - # measure. If they didn't, then we have to exclude the stdlib and - # coverage.py directories. - if self.source_match: - if not self.source_match.match(canonical): - return False - else: - # If we aren't supposed to trace installed code, then check if this - # is near the Python standard library and skip it if so. - if self.pylib_match and self.pylib_match.match(canonical): - return False - - # We exclude the coverage code itself, since a little of it will be - # measured otherwise. - if self.cover_match and self.cover_match.match(canonical): - return False - - # Check the file against the include and omit patterns. - if self.include_match and not self.include_match.match(canonical): - return False - if self.omit_match and self.omit_match.match(canonical): - return False - - return canonical - - # To log what should_trace returns, change this to "if 1:" - if 0: - _real_should_trace = _should_trace - def _should_trace(self, filename, frame): # pylint: disable-msg=E0102 - """A logging decorator around the real _should_trace function.""" - ret = self._real_should_trace(filename, frame) - print("should_trace: %r -> %r" % (filename, ret)) - return ret - - def _warn(self, msg): - """Use `msg` as a warning.""" - sys.stderr.write("Coverage.py warning: " + msg + "\n") - - def _abs_files(self, files): - """Return a list of absolute file names for the names in `files`.""" - files = files or [] - return [self.file_locator.abs_file(f) for f in files] - - def _check_for_packages(self): - """Update the source_match matcher with latest imported packages.""" - # Our self.source_pkgs attribute is a list of package names we want to - # measure. Each time through here, we see if we've imported any of - # them yet. If so, we add its file to source_match, and we don't have - # to look for that package any more. - if self.source_pkgs: - found = [] - for pkg in self.source_pkgs: - try: - mod = sys.modules[pkg] - except KeyError: - continue - - found.append(pkg) - - try: - pkg_file = mod.__file__ - except AttributeError: - self._warn("Module %s has no python source." % pkg) - else: - d, f = os.path.split(pkg_file) - if f.startswith('__init__.'): - # This is actually a package, return the directory. - pkg_file = d - else: - pkg_file = self._source_for_file(pkg_file) - pkg_file = self.file_locator.canonical_filename(pkg_file) - self.source.append(pkg_file) - self.source_match.add(pkg_file) - - for pkg in found: - self.source_pkgs.remove(pkg) - - def use_cache(self, usecache): - """Control the use of a data file (incorrectly called a cache). - - `usecache` is true or false, whether to read and write data on disk. - - """ - self.data.usefile(usecache) - - def load(self): - """Load previously-collected coverage data from the data file.""" - self.collector.reset() - self.data.read() - - def start(self): - """Start measuring code coverage.""" - if self.run_suffix: - # Calling start() means we're running code, so use the run_suffix - # as the data_suffix when we eventually save the data. - self.data_suffix = self.run_suffix - if self.auto_data: - self.load() - # Save coverage data when Python exits. - if not self.atexit_registered: - atexit.register(self.save) - self.atexit_registered = True - - # Create the matchers we need for _should_trace - if self.source or self.source_pkgs: - self.source_match = TreeMatcher(self.source) - else: - if self.cover_dir: - self.cover_match = TreeMatcher([self.cover_dir]) - if self.pylib_dirs: - self.pylib_match = TreeMatcher(self.pylib_dirs) - if self.include: - self.include_match = FnmatchMatcher(self.include) - if self.omit: - self.omit_match = FnmatchMatcher(self.omit) - - self._harvested = False - self.collector.start() - - def stop(self): - """Stop measuring code coverage.""" - self.collector.stop() - self._harvest_data() - - def erase(self): - """Erase previously-collected coverage data. - - This removes the in-memory data collected in this session as well as - discarding the data file. - - """ - self.collector.reset() - self.data.erase() - - def clear_exclude(self): - """Clear the exclude list.""" - self.config.exclude_list = [] - self.exclude_re = "" - - def exclude(self, regex): - """Exclude source lines from execution consideration. - - `regex` is a regular expression. Lines matching this expression are - not considered executable when reporting code coverage. A list of - regexes is maintained; this function adds a new regex to the list. - Matching any of the regexes excludes a source line. - - """ - self.config.exclude_list.append(regex) - self._compile_exclude() - - def _compile_exclude(self): - """Build the internal usable form of the exclude list.""" - self.exclude_re = "(" + ")|(".join(self.config.exclude_list) + ")" - - def get_exclude_list(self): - """Return the list of excluded regex patterns.""" - return self.config.exclude_list - - def save(self): - """Save the collected coverage data to the data file.""" - data_suffix = self.data_suffix - if data_suffix is True: - # If data_suffix was a simple true value, then make a suffix with - # plenty of distinguishing information. We do this here in - # `save()` at the last minute so that the pid will be correct even - # if the process forks. - data_suffix = "%s.%s.%06d" % ( - self.socket.gethostname(), self.os.getpid(), - self.random.randint(0, 99999) - ) - - self._harvest_data() - self.data.write(suffix=data_suffix) - - def combine(self): - """Combine together a number of similarly-named coverage data files. - - All coverage data files whose name starts with `data_file` (from the - coverage() constructor) will be read, and combined together into the - current measurements. - - """ - self.data.combine_parallel_data() - - def _harvest_data(self): - """Get the collected data and reset the collector. - - Also warn about various problems collecting data. - - """ - if not self._harvested: - self.data.add_line_data(self.collector.get_line_data()) - self.data.add_arc_data(self.collector.get_arc_data()) - self.collector.reset() - - # If there are still entries in the source_pkgs list, then we never - # encountered those packages. - for pkg in self.source_pkgs: - self._warn("Source module %s was never encountered." % pkg) - - # Find out if we got any data. - summary = self.data.summary() - if not summary: - self._warn("No data was collected.") - - # Find files that were never executed at all. - for src in self.source: - for py_file in find_python_files(src): - self.data.touch_file(py_file) - - self._harvested = True - - # Backward compatibility with version 1. - def analysis(self, morf): - """Like `analysis2` but doesn't return excluded line numbers.""" - f, s, _, m, mf = self.analysis2(morf) - return f, s, m, mf - - def analysis2(self, morf): - """Analyze a module. - - `morf` is a module or a filename. It will be analyzed to determine - its coverage statistics. The return value is a 5-tuple: - - * The filename for the module. - * A list of line numbers of executable statements. - * A list of line numbers of excluded statements. - * A list of line numbers of statements not run (missing from - execution). - * A readable formatted string of the missing line numbers. - - The analysis uses the source file itself and the current measured - coverage data. - - """ - analysis = self._analyze(morf) - return ( - analysis.filename, analysis.statements, analysis.excluded, - analysis.missing, analysis.missing_formatted() - ) - - def _analyze(self, it): - """Analyze a single morf or code unit. - - Returns an `Analysis` object. - - """ - if not isinstance(it, CodeUnit): - it = code_unit_factory(it, self.file_locator)[0] - - return Analysis(self, it) - - def report(self, morfs=None, show_missing=True, ignore_errors=None, - file=None, # pylint: disable-msg=W0622 - omit=None, include=None - ): - """Write a summary report to `file`. - - Each module in `morfs` is listed, with counts of statements, executed - statements, missing statements, and a list of lines missed. - - `include` is a list of filename patterns. Modules whose filenames - match those patterns will be included in the report. Modules matching - `omit` will not be included in the report. - - """ - self.config.from_args( - ignore_errors=ignore_errors, omit=omit, include=include - ) - reporter = SummaryReporter( - self, show_missing, self.config.ignore_errors - ) - reporter.report(morfs, outfile=file, config=self.config) - - def annotate(self, morfs=None, directory=None, ignore_errors=None, - omit=None, include=None): - """Annotate a list of modules. - - Each module in `morfs` is annotated. The source is written to a new - file, named with a ",cover" suffix, with each line prefixed with a - marker to indicate the coverage of the line. Covered lines have ">", - excluded lines have "-", and missing lines have "!". - - See `coverage.report()` for other arguments. - - """ - self.config.from_args( - ignore_errors=ignore_errors, omit=omit, include=include - ) - reporter = AnnotateReporter(self, self.config.ignore_errors) - reporter.report(morfs, config=self.config, directory=directory) - - def html_report(self, morfs=None, directory=None, ignore_errors=None, - omit=None, include=None): - """Generate an HTML report. - - See `coverage.report()` for other arguments. - - """ - self.config.from_args( - ignore_errors=ignore_errors, omit=omit, include=include, - html_dir=directory, - ) - reporter = HtmlReporter(self, self.config.ignore_errors) - reporter.report(morfs, config=self.config) - - def xml_report(self, morfs=None, outfile=None, ignore_errors=None, - omit=None, include=None): - """Generate an XML report of coverage results. - - The report is compatible with Cobertura reports. - - Each module in `morfs` is included in the report. `outfile` is the - path to write the file to, "-" will write to stdout. - - See `coverage.report()` for other arguments. - - """ - self.config.from_args( - ignore_errors=ignore_errors, omit=omit, include=include, - xml_output=outfile, - ) - file_to_close = None - if self.config.xml_output: - if self.config.xml_output == '-': - outfile = sys.stdout - else: - outfile = open(self.config.xml_output, "w") - file_to_close = outfile - try: - reporter = XmlReporter(self, self.config.ignore_errors) - reporter.report(morfs, outfile=outfile, config=self.config) - finally: - if file_to_close: - file_to_close.close() - - def sysinfo(self): - """Return a list of (key, value) pairs showing internal information.""" - - import coverage as covmod - import platform, re - - info = [ - ('version', covmod.__version__), - ('coverage', covmod.__file__), - ('cover_dir', self.cover_dir), - ('pylib_dirs', self.pylib_dirs), - ('tracer', self.collector.tracer_name()), - ('data_path', self.data.filename), - ('python', sys.version.replace('\n', '')), - ('platform', platform.platform()), - ('cwd', os.getcwd()), - ('path', sys.path), - ('environment', [ - ("%s = %s" % (k, v)) for k, v in os.environ.items() - if re.search("^COV|^PY", k) - ]), - ] - return info - - -def process_startup(): - """Call this at Python startup to perhaps measure coverage. - - If the environment variable COVERAGE_PROCESS_START is defined, coverage - measurement is started. The value of the variable is the config file - to use. - - There are two ways to configure your Python installation to invoke this - function when Python starts: - - #. Create or append to sitecustomize.py to add these lines:: - - import coverage - coverage.process_startup() - - #. Create a .pth file in your Python installation containing:: - - import coverage; coverage.process_startup() - - """ - cps = os.environ.get("COVERAGE_PROCESS_START") - if cps: - cov = coverage(config_file=cps, auto_data=True) - if os.environ.get("COVERAGE_COVERAGE"): - # Measuring coverage within coverage.py takes yet more trickery. - cov.cover_dir = "Please measure coverage.py!" - cov.start() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py deleted file mode 100644 index 3d750c42..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/data.py +++ /dev/null @@ -1,261 +0,0 @@ -"""Coverage data for Coverage.""" - -import os - -from coverage.backward import pickle, sorted # pylint: disable-msg=W0622 - - -class CoverageData(object): - """Manages collected coverage data, including file storage. - - The data file format is a pickled dict, with these keys: - - * collector: a string identifying the collecting software - - * lines: a dict mapping filenames to sorted lists of line numbers - executed: - { 'file1': [17,23,45], 'file2': [1,2,3], ... } - - * arcs: a dict mapping filenames to sorted lists of line number pairs: - { 'file1': [(17,23), (17,25), (25,26)], ... } - - """ - - def __init__(self, basename=None, collector=None): - """Create a CoverageData. - - `basename` is the name of the file to use for storing data. - - `collector` is a string describing the coverage measurement software. - - """ - self.collector = collector or 'unknown' - - self.use_file = True - - # Construct the filename that will be used for data file storage, if we - # ever do any file storage. - self.filename = basename or ".coverage" - self.filename = os.path.abspath(self.filename) - - # A map from canonical Python source file name to a dictionary in - # which there's an entry for each line number that has been - # executed: - # - # { - # 'filename1.py': { 12: None, 47: None, ... }, - # ... - # } - # - self.lines = {} - - # A map from canonical Python source file name to a dictionary with an - # entry for each pair of line numbers forming an arc: - # - # { - # 'filename1.py': { (12,14): None, (47,48): None, ... }, - # ... - # } - # - self.arcs = {} - - self.os = os - self.sorted = sorted - self.pickle = pickle - - def usefile(self, use_file=True): - """Set whether or not to use a disk file for data.""" - self.use_file = use_file - - def read(self): - """Read coverage data from the coverage data file (if it exists).""" - if self.use_file: - self.lines, self.arcs = self._read_file(self.filename) - else: - self.lines, self.arcs = {}, {} - - def write(self, suffix=None): - """Write the collected coverage data to a file. - - `suffix` is a suffix to append to the base file name. This can be used - for multiple or parallel execution, so that many coverage data files - can exist simultaneously. A dot will be used to join the base name and - the suffix. - - """ - if self.use_file: - filename = self.filename - if suffix: - filename += "." + suffix - self.write_file(filename) - - def erase(self): - """Erase the data, both in this object, and from its file storage.""" - if self.use_file: - if self.filename and os.path.exists(self.filename): - os.remove(self.filename) - self.lines = {} - self.arcs = {} - - def line_data(self): - """Return the map from filenames to lists of line numbers executed.""" - return dict( - [(f, self.sorted(lmap.keys())) for f, lmap in self.lines.items()] - ) - - def arc_data(self): - """Return the map from filenames to lists of line number pairs.""" - return dict( - [(f, self.sorted(amap.keys())) for f, amap in self.arcs.items()] - ) - - def write_file(self, filename): - """Write the coverage data to `filename`.""" - - # Create the file data. - data = {} - - data['lines'] = self.line_data() - arcs = self.arc_data() - if arcs: - data['arcs'] = arcs - - if self.collector: - data['collector'] = self.collector - - # Write the pickle to the file. - fdata = open(filename, 'wb') - try: - self.pickle.dump(data, fdata, 2) - finally: - fdata.close() - - def read_file(self, filename): - """Read the coverage data from `filename`.""" - self.lines, self.arcs = self._read_file(filename) - - def raw_data(self, filename): - """Return the raw pickled data from `filename`.""" - fdata = open(filename, 'rb') - try: - data = pickle.load(fdata) - finally: - fdata.close() - return data - - def _read_file(self, filename): - """Return the stored coverage data from the given file. - - Returns two values, suitable for assigning to `self.lines` and - `self.arcs`. - - """ - lines = {} - arcs = {} - try: - data = self.raw_data(filename) - if isinstance(data, dict): - # Unpack the 'lines' item. - lines = dict([ - (f, dict.fromkeys(linenos, None)) - for f, linenos in data.get('lines', {}).items() - ]) - # Unpack the 'arcs' item. - arcs = dict([ - (f, dict.fromkeys(arcpairs, None)) - for f, arcpairs in data.get('arcs', {}).items() - ]) - except Exception: - pass - return lines, arcs - - def combine_parallel_data(self): - """Combine a number of data files together. - - Treat `self.filename` as a file prefix, and combine the data from all - of the data files starting with that prefix plus a dot. - - """ - data_dir, local = os.path.split(self.filename) - localdot = local + '.' - for f in os.listdir(data_dir or '.'): - if f.startswith(localdot): - full_path = os.path.join(data_dir, f) - new_lines, new_arcs = self._read_file(full_path) - for filename, file_data in new_lines.items(): - self.lines.setdefault(filename, {}).update(file_data) - for filename, file_data in new_arcs.items(): - self.arcs.setdefault(filename, {}).update(file_data) - if f != local: - os.remove(full_path) - - def add_line_data(self, line_data): - """Add executed line data. - - `line_data` is { filename: { lineno: None, ... }, ...} - - """ - for filename, linenos in line_data.items(): - self.lines.setdefault(filename, {}).update(linenos) - - def add_arc_data(self, arc_data): - """Add measured arc data. - - `arc_data` is { filename: { (l1,l2): None, ... }, ...} - - """ - for filename, arcs in arc_data.items(): - self.arcs.setdefault(filename, {}).update(arcs) - - def touch_file(self, filename): - """Ensure that `filename` appears in the data, empty if needed.""" - self.lines.setdefault(filename, {}) - - def measured_files(self): - """A list of all files that had been measured.""" - return list(self.lines.keys()) - - def executed_lines(self, filename): - """A map containing all the line numbers executed in `filename`. - - If `filename` hasn't been collected at all (because it wasn't executed) - then return an empty map. - - """ - return self.lines.get(filename) or {} - - def executed_arcs(self, filename): - """A map containing all the arcs executed in `filename`.""" - return self.arcs.get(filename) or {} - - def summary(self, fullpath=False): - """Return a dict summarizing the coverage data. - - Keys are based on the filenames, and values are the number of executed - lines. If `fullpath` is true, then the keys are the full pathnames of - the files, otherwise they are the basenames of the files. - - """ - summ = {} - if fullpath: - filename_fn = lambda f: f - else: - filename_fn = self.os.path.basename - for filename, lines in self.lines.items(): - summ[filename_fn(filename)] = len(lines) - return summ - - def has_arcs(self): - """Does this data have arcs?""" - return bool(self.arcs) - - -if __name__ == '__main__': - # Ad-hoc: show the raw data in a data file. - import pprint, sys - covdata = CoverageData() - if sys.argv[1:]: - fname = sys.argv[1] - else: - fname = covdata.filename - pprint.pprint(covdata.raw_data(fname)) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py deleted file mode 100644 index 333163f8..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/execfile.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Execute files of Python code.""" - -import imp, os, sys - -from coverage.backward import exec_code_object -from coverage.misc import NoSource, ExceptionDuringRun - - -try: - # In Py 2.x, the builtins were in __builtin__ - BUILTINS = sys.modules['__builtin__'] -except KeyError: - # In Py 3.x, they're in builtins - BUILTINS = sys.modules['builtins'] - - -def run_python_file(filename, args): - """Run a python file as if it were the main program on the command line. - - `filename` is the path to the file to execute, it need not be a .py file. - `args` is the argument array to present as sys.argv, including the first - element representing the file being executed. - - """ - # Create a module to serve as __main__ - old_main_mod = sys.modules['__main__'] - main_mod = imp.new_module('__main__') - sys.modules['__main__'] = main_mod - main_mod.__file__ = filename - main_mod.__builtins__ = BUILTINS - - # Set sys.argv and the first path element properly. - old_argv = sys.argv - old_path0 = sys.path[0] - sys.argv = args - sys.path[0] = os.path.dirname(filename) - - try: - # Open the source file. - try: - source = open(filename, 'rU').read() - except IOError: - raise NoSource("No file to run: %r" % filename) - - # We have the source. `compile` still needs the last line to be clean, - # so make sure it is, then compile a code object from it. - if source[-1] != '\n': - source += '\n' - code = compile(source, filename, "exec") - - # Execute the source file. - try: - exec_code_object(code, main_mod.__dict__) - except SystemExit: - # The user called sys.exit(). Just pass it along to the upper - # layers, where it will be handled. - raise - except: - # Something went wrong while executing the user code. - # Get the exc_info, and pack them into an exception that we can - # throw up to the outer loop. We peel two layers off the traceback - # so that the coverage.py code doesn't appear in the final printed - # traceback. - typ, err, tb = sys.exc_info() - raise ExceptionDuringRun(typ, err, tb.tb_next.tb_next) - finally: - # Restore the old __main__ - sys.modules['__main__'] = old_main_mod - - # Restore the old argv and path - sys.argv = old_argv - sys.path[0] = old_path0 diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py deleted file mode 100644 index 9a8ac564..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/files.py +++ /dev/null @@ -1,132 +0,0 @@ -"""File wrangling.""" - -import fnmatch, os, sys - -class FileLocator(object): - """Understand how filenames work.""" - - def __init__(self): - # The absolute path to our current directory. - self.relative_dir = self.abs_file(os.curdir) + os.sep - - # Cache of results of calling the canonical_filename() method, to - # avoid duplicating work. - self.canonical_filename_cache = {} - - def abs_file(self, filename): - """Return the absolute normalized form of `filename`.""" - return os.path.normcase(os.path.abspath(os.path.realpath(filename))) - - def relative_filename(self, filename): - """Return the relative form of `filename`. - - The filename will be relative to the current directory when the - `FileLocator` was constructed. - - """ - if filename.startswith(self.relative_dir): - filename = filename.replace(self.relative_dir, "") - return filename - - def canonical_filename(self, filename): - """Return a canonical filename for `filename`. - - An absolute path with no redundant components and normalized case. - - """ - if filename not in self.canonical_filename_cache: - f = filename - if os.path.isabs(f) and not os.path.exists(f): - if self.get_zip_data(f) is None: - f = os.path.basename(f) - if not os.path.isabs(f): - for path in [os.curdir] + sys.path: - if path is None: - continue - g = os.path.join(path, f) - if os.path.exists(g): - f = g - break - cf = self.abs_file(f) - self.canonical_filename_cache[filename] = cf - return self.canonical_filename_cache[filename] - - def get_zip_data(self, filename): - """Get data from `filename` if it is a zip file path. - - Returns the string data read from the zip file, or None if no zip file - could be found or `filename` isn't in it. The data returned will be - an empty string if the file is empty. - - """ - import zipimport - markers = ['.zip'+os.sep, '.egg'+os.sep] - for marker in markers: - if marker in filename: - parts = filename.split(marker) - try: - zi = zipimport.zipimporter(parts[0]+marker[:-1]) - except zipimport.ZipImportError: - continue - try: - data = zi.get_data(parts[1]) - except IOError: - continue - if sys.version_info >= (3, 0): - data = data.decode('utf8') # TODO: How to do this properly? - return data - return None - - -class TreeMatcher(object): - """A matcher for files in a tree.""" - def __init__(self, directories): - self.dirs = directories[:] - - def __repr__(self): - return "" % self.dirs - - def add(self, directory): - """Add another directory to the list we match for.""" - self.dirs.append(directory) - - def match(self, fpath): - """Does `fpath` indicate a file in one of our trees?""" - for d in self.dirs: - if fpath.startswith(d): - if fpath == d: - # This is the same file! - return True - if fpath[len(d)] == os.sep: - # This is a file in the directory - return True - return False - - -class FnmatchMatcher(object): - """A matcher for files by filename pattern.""" - def __init__(self, pats): - self.pats = pats[:] - - def __repr__(self): - return "" % self.pats - - def match(self, fpath): - """Does `fpath` match one of our filename patterns?""" - for pat in self.pats: - if fnmatch.fnmatch(fpath, pat): - return True - return False - - -def find_python_files(dirname): - """Yield all of the importable Python files in `dirname`, recursively.""" - for dirpath, dirnames, filenames in os.walk(dirname, topdown=True): - if '__init__.py' not in filenames: - # If a directory doesn't have __init__.py, then it isn't - # importable and neither are its files - del dirnames[:] - continue - for filename in filenames: - if fnmatch.fnmatch(filename, "*.py"): - yield os.path.join(dirpath, filename) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py deleted file mode 100644 index dac4ff96..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/html.py +++ /dev/null @@ -1,183 +0,0 @@ -"""HTML reporting for Coverage.""" - -import os, re, shutil - -from coverage import __url__, __version__ # pylint: disable-msg=W0611 -from coverage.misc import CoverageException -from coverage.phystokens import source_token_lines -from coverage.report import Reporter -from coverage.templite import Templite - -# Disable pylint msg W0612, because a bunch of variables look unused, but -# they're accessed in a Templite context via locals(). -# pylint: disable-msg=W0612 - -def data_filename(fname): - """Return the path to a data file of ours.""" - return os.path.join(os.path.split(__file__)[0], fname) - -def data(fname): - """Return the contents of a data file of ours.""" - return open(data_filename(fname)).read() - - -class HtmlReporter(Reporter): - """HTML reporting.""" - - def __init__(self, coverage, ignore_errors=False): - super(HtmlReporter, self).__init__(coverage, ignore_errors) - self.directory = None - self.source_tmpl = Templite(data("htmlfiles/pyfile.html"), globals()) - - self.files = [] - self.arcs = coverage.data.has_arcs() - - def report(self, morfs, config=None): - """Generate an HTML report for `morfs`. - - `morfs` is a list of modules or filenames. `config` is a - CoverageConfig instance. - - """ - assert config.html_dir, "must provide a directory for html reporting" - - # Process all the files. - self.report_files(self.html_file, morfs, config, config.html_dir) - - if not self.files: - raise CoverageException("No data to report.") - - # Write the index file. - self.index_file() - - # Create the once-per-directory files. - for static in [ - "style.css", "coverage_html.js", - "jquery-1.3.2.min.js", "jquery.tablesorter.min.js" - ]: - shutil.copyfile( - data_filename("htmlfiles/" + static), - os.path.join(self.directory, static) - ) - - def html_file(self, cu, analysis): - """Generate an HTML file for one source file.""" - - source = cu.source_file().read() - - nums = analysis.numbers - - missing_branch_arcs = analysis.missing_branch_arcs() - n_par = 0 # accumulated below. - arcs = self.arcs - - # These classes determine which lines are highlighted by default. - c_run = "run hide_run" - c_exc = "exc" - c_mis = "mis" - c_par = "par " + c_run - - lines = [] - - for lineno, line in enumerate(source_token_lines(source)): - lineno += 1 # 1-based line numbers. - # Figure out how to mark this line. - line_class = [] - annotate_html = "" - annotate_title = "" - if lineno in analysis.statements: - line_class.append("stm") - if lineno in analysis.excluded: - line_class.append(c_exc) - elif lineno in analysis.missing: - line_class.append(c_mis) - elif self.arcs and lineno in missing_branch_arcs: - line_class.append(c_par) - n_par += 1 - annlines = [] - for b in missing_branch_arcs[lineno]: - if b < 0: - annlines.append("exit") - else: - annlines.append(str(b)) - annotate_html = "   ".join(annlines) - if len(annlines) > 1: - annotate_title = "no jumps to these line numbers" - elif len(annlines) == 1: - annotate_title = "no jump to this line number" - elif lineno in analysis.statements: - line_class.append(c_run) - - # Build the HTML for the line - html = [] - for tok_type, tok_text in line: - if tok_type == "ws": - html.append(escape(tok_text)) - else: - tok_html = escape(tok_text) or ' ' - html.append( - "%s" % (tok_type, tok_html) - ) - - lines.append({ - 'html': ''.join(html), - 'number': lineno, - 'class': ' '.join(line_class) or "pln", - 'annotate': annotate_html, - 'annotate_title': annotate_title, - }) - - # Write the HTML page for this file. - html_filename = cu.flat_rootname() + ".html" - html_path = os.path.join(self.directory, html_filename) - html = spaceless(self.source_tmpl.render(locals())) - fhtml = open(html_path, 'w') - fhtml.write(html) - fhtml.close() - - # Save this file's information for the index file. - self.files.append({ - 'nums': nums, - 'par': n_par, - 'html_filename': html_filename, - 'cu': cu, - }) - - def index_file(self): - """Write the index.html file for this report.""" - index_tmpl = Templite(data("htmlfiles/index.html"), globals()) - - files = self.files - arcs = self.arcs - - totals = sum([f['nums'] for f in files]) - - fhtml = open(os.path.join(self.directory, "index.html"), "w") - fhtml.write(index_tmpl.render(locals())) - fhtml.close() - - -# Helpers for templates and generating HTML - -def escape(t): - """HTML-escape the text in `t`.""" - return (t - # Convert HTML special chars into HTML entities. - .replace("&", "&").replace("<", "<").replace(">", ">") - .replace("'", "'").replace('"', """) - # Convert runs of spaces: "......" -> " . . ." - .replace(" ", "  ") - # To deal with odd-length runs, convert the final pair of spaces - # so that "....." -> " .  ." - .replace(" ", "  ") - ) - -def spaceless(html): - """Squeeze out some annoying extra space from an HTML string. - - Nicely-formatted templates mean lots of extra space in the result. - Get rid of some. - - """ - html = re.sub(">\s+

\n

-1) { - cookies = document.cookie.split(";"); - for (var i=0; i < cookies.length; i++) { - parts = cookies[i].split("=") - - if ($.trim(parts[0]) == cookie_name && parts[1]) { - sort_list = eval("[[" + parts[1] + "]]"); - break; - } - } - } - - // Create a new widget which exists only to save and restore - // the sort order: - $.tablesorter.addWidget({ - id: "persistentSort", - - // Format is called by the widget before displaying: - format: function(table) { - if (table.config.sortList.length == 0 && sort_list.length > 0) { - // This table hasn't been sorted before - we'll use - // our stored settings: - $(table).trigger('sorton', [sort_list]); - } - else { - // This is not the first load - something has - // already defined sorting so we'll just update - // our stored value to match: - sort_list = table.config.sortList; - } - } - }); - - // Configure our tablesorter to handle the variable number of - // columns produced depending on report options: - var headers = {}; - var col_count = $("table.index > thead > tr > th").length; - - headers[0] = { sorter: 'text' }; - for (var i = 1; i < col_count-1; i++) { - headers[i] = { sorter: 'digit' }; - } - headers[col_count-1] = { sorter: 'percent' }; - - // Enable the table sorter: - $("table.index").tablesorter({ - widgets: ['persistentSort'], - headers: headers - }); - - // Watch for page unload events so we can save the final sort settings: - $(window).unload(function() { - document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/" - }); -} - -// -- pyfile stuff -- - -function pyfile_ready($) { - // If we're directed to a particular line number, highlight the line. - var frag = location.hash; - if (frag.length > 2 && frag[1] == 'n') { - $(frag).addClass('highlight'); - } -} - -function toggle_lines(btn, cls) { - btn = $(btn); - var hide = "hide_"+cls; - if (btn.hasClass(hide)) { - $("#source ."+cls).removeClass(hide); - btn.removeClass(hide); - } - else { - $("#source ."+cls).addClass(hide); - btn.addClass(hide); - } -} diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html deleted file mode 100644 index bec2584f..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Coverage report - - - - - - - - -

- -
- - - {# The title='' attr doesn't work in Safari. #} - - - - - - {% if arcs %} - - - {% endif %} - - - - {# HTML syntax requires thead, tfoot, tbody #} - - - - - - - {% if arcs %} - - - {% endif %} - - - - - {% for file in files %} - - - - - - {% if arcs %} - - - {% endif %} - - - {% endfor %} - -
Modulestatementsmissingexcludedbranchespartialcoverage
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_missing_branches}}{{totals.pc_covered_str}}%
{{file.cu.name}}{{file.nums.n_statements}}{{file.nums.n_missing}}{{file.nums.n_excluded}}{{file.nums.n_branches}}{{file.nums.n_missing_branches}}{{file.nums.pc_covered_str}}%
-
- -
- - - diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js deleted file mode 100644 index b1ae21d8..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery-1.3.2.min.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js deleted file mode 100644 index 64c70071..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/jquery.tablesorter.min.js +++ /dev/null @@ -1,2 +0,0 @@ - -(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i - - - - {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #} - {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #} - - Coverage for {{cu.name|escape}}: {{nums.pc_covered_str}}% - - - - - - - - - -
- - - - - -
- {% for line in lines %} -

{{line.number}}

- {% endfor %} -
- {% for line in lines %} -

{% if line.annotate %}{{line.annotate}}{% endif %}{{line.html}} 

- {% endfor %} -
-
- - - - - diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css deleted file mode 100644 index 9a06a2b4..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/htmlfiles/style.css +++ /dev/null @@ -1,230 +0,0 @@ -/* CSS styles for Coverage. */ -/* Page-wide styles */ -html, body, h1, h2, h3, p, td, th { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-weight: inherit; - font-style: inherit; - font-size: 100%; - font-family: inherit; - vertical-align: baseline; - } - -/* Set baseline grid to 16 pt. */ -body { - font-family: georgia, serif; - font-size: 1em; - } - -html>body { - font-size: 16px; - } - -/* Set base font size to 12/16 */ -p { - font-size: .75em; /* 12/16 */ - line-height: 1.3333em; /* 16/12 */ - } - -table { - border-collapse: collapse; - } - -a.nav { - text-decoration: none; - color: inherit; - } -a.nav:hover { - text-decoration: underline; - color: inherit; - } - -/* Page structure */ -#header { - background: #f8f8f8; - width: 100%; - border-bottom: 1px solid #eee; - } - -#source { - padding: 1em; - font-family: "courier new", monospace; - } - -#indexfile #footer { - margin: 1em 3em; - } - -#pyfile #footer { - margin: 1em 1em; - } - -#footer .content { - padding: 0; - font-size: 85%; - font-family: verdana, sans-serif; - color: #666666; - font-style: italic; - } - -#index { - margin: 1em 0 0 3em; - } - -/* Header styles */ -#header .content { - padding: 1em 3em; - } - -h1 { - font-size: 1.25em; -} - -h2.stats { - margin-top: .5em; - font-size: 1em; -} -.stats span { - border: 1px solid; - padding: .1em .25em; - margin: 0 .1em; - cursor: pointer; - border-color: #999 #ccc #ccc #999; -} -.stats span.hide_run, .stats span.hide_exc, -.stats span.hide_mis, .stats span.hide_par, -.stats span.par.hide_run.hide_par { - border-color: #ccc #999 #999 #ccc; -} -.stats span.par.hide_run { - border-color: #999 #ccc #ccc #999; -} - -/* Source file styles */ -.linenos p { - text-align: right; - margin: 0; - padding: 0 .5em; - color: #999999; - font-family: verdana, sans-serif; - font-size: .625em; /* 10/16 */ - line-height: 1.6em; /* 16/10 */ - } -.linenos p.highlight { - background: #ffdd00; - } -.linenos p a { - text-decoration: none; - color: #999999; - } -.linenos p a:hover { - text-decoration: underline; - color: #999999; - } - -td.text { - width: 100%; - } -.text p { - margin: 0; - padding: 0 0 0 .5em; - border-left: 2px solid #ffffff; - white-space: nowrap; - } - -.text p.mis { - background: #ffdddd; - border-left: 2px solid #ff0000; - } -.text p.run, .text p.run.hide_par { - background: #ddffdd; - border-left: 2px solid #00ff00; - } -.text p.exc { - background: #eeeeee; - border-left: 2px solid #808080; - } -.text p.par, .text p.par.hide_run { - background: #ffffaa; - border-left: 2px solid #eeee99; - } -.text p.hide_run, .text p.hide_exc, .text p.hide_mis, .text p.hide_par, -.text p.hide_run.hide_par { - background: inherit; - } - -.text span.annotate { - font-family: georgia; - font-style: italic; - color: #666; - float: right; - padding-right: .5em; - } -.text p.hide_par span.annotate { - display: none; - } - -/* Syntax coloring */ -.text .com { - color: green; - font-style: italic; - line-height: 1px; - } -.text .key { - font-weight: bold; - line-height: 1px; - } -.text .str { - color: #000080; - } - -/* index styles */ -#index td, #index th { - text-align: right; - width: 5em; - padding: .25em .5em; - border-bottom: 1px solid #eee; - } -#index th { - font-style: italic; - color: #333; - border-bottom: 1px solid #ccc; - cursor: pointer; - } -#index th:hover { - background: #eee; - border-bottom: 1px solid #999; - } -#index td.left, #index th.left { - padding-left: 0; - } -#index td.right, #index th.right { - padding-right: 0; - } -#index th.headerSortDown, #index th.headerSortUp { - border-bottom: 1px solid #000; - } -#index td.name, #index th.name { - text-align: left; - width: auto; - } -#index td.name a { - text-decoration: none; - color: #000; - } -#index td.name a:hover { - text-decoration: underline; - color: #000; - } -#index tr.total { - } -#index tr.total td { - font-weight: bold; - border-top: 1px solid #ccc; - border-bottom: none; - } -#index tr.file:hover { - background: #eeeeee; - } diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py deleted file mode 100644 index 4218536d..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/misc.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Miscellaneous stuff for Coverage.""" - -def nice_pair(pair): - """Make a nice string representation of a pair of numbers. - - If the numbers are equal, just return the number, otherwise return the pair - with a dash between them, indicating the range. - - """ - start, end = pair - if start == end: - return "%d" % start - else: - return "%d-%d" % (start, end) - - -def format_lines(statements, lines): - """Nicely format a list of line numbers. - - Format a list of line numbers for printing by coalescing groups of lines as - long as the lines represent consecutive statements. This will coalesce - even if there are gaps between statements. - - For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and - `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14". - - """ - pairs = [] - i = 0 - j = 0 - start = None - while i < len(statements) and j < len(lines): - if statements[i] == lines[j]: - if start == None: - start = lines[j] - end = lines[j] - j += 1 - elif start: - pairs.append((start, end)) - start = None - i += 1 - if start: - pairs.append((start, end)) - ret = ', '.join(map(nice_pair, pairs)) - return ret - - -def expensive(fn): - """A decorator to cache the result of an expensive operation. - - Only applies to methods with no arguments. - - """ - attr = "_cache_" + fn.__name__ - def _wrapped(self): - """Inner fn that checks the cache.""" - if not hasattr(self, attr): - setattr(self, attr, fn(self)) - return getattr(self, attr) - return _wrapped - - -def bool_or_none(b): - """Return bool(b), but preserve None.""" - if b is None: - return None - else: - return bool(b) - - -class CoverageException(Exception): - """An exception specific to Coverage.""" - pass - -class NoSource(CoverageException): - """Used to indicate we couldn't find the source for a module.""" - pass - -class ExceptionDuringRun(CoverageException): - """An exception happened while running customer code. - - Construct it with three arguments, the values from `sys.exc_info`. - - """ - pass diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py deleted file mode 100644 index ae618ce5..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/parser.py +++ /dev/null @@ -1,783 +0,0 @@ -"""Code parsing for Coverage.""" - -import glob, opcode, os, re, sys, token, tokenize - -from coverage.backward import set, sorted, StringIO # pylint: disable-msg=W0622 -from coverage.bytecode import ByteCodes, CodeObjects -from coverage.misc import nice_pair, CoverageException, NoSource, expensive - - -class CodeParser(object): - """Parse code to find executable lines, excluded lines, etc.""" - - def __init__(self, text=None, filename=None, exclude=None): - """ - Source can be provided as `text`, the text itself, or `filename`, from - which text will be read. Excluded lines are those that match - `exclude`, a regex. - - """ - assert text or filename, "CodeParser needs either text or filename" - self.filename = filename or "" - self.text = text - if not self.text: - try: - sourcef = open(self.filename, 'rU') - self.text = sourcef.read() - sourcef.close() - except IOError: - _, err, _ = sys.exc_info() - raise NoSource( - "No source for code: %r: %s" % (self.filename, err) - ) - self.text = self.text.replace('\r\n', '\n') - - self.exclude = exclude - - self.show_tokens = False - - # The text lines of the parsed code. - self.lines = self.text.split('\n') - - # The line numbers of excluded lines of code. - self.excluded = set() - - # The line numbers of docstring lines. - self.docstrings = set() - - # The line numbers of class definitions. - self.classdefs = set() - - # A dict mapping line numbers to (lo,hi) for multi-line statements. - self.multiline = {} - - # The line numbers that start statements. - self.statement_starts = set() - - # Lazily-created ByteParser - self._byte_parser = None - - def _get_byte_parser(self): - """Create a ByteParser on demand.""" - if not self._byte_parser: - self._byte_parser = \ - ByteParser(text=self.text, filename=self.filename) - return self._byte_parser - byte_parser = property(_get_byte_parser) - - def _raw_parse(self): - """Parse the source to find the interesting facts about its lines. - - A handful of member fields are updated. - - """ - # Find lines which match an exclusion pattern. - if self.exclude: - re_exclude = re.compile(self.exclude) - for i, ltext in enumerate(self.lines): - if re_exclude.search(ltext): - self.excluded.add(i+1) - - # Tokenize, to find excluded suites, to find docstrings, and to find - # multi-line statements. - indent = 0 - exclude_indent = 0 - excluding = False - prev_toktype = token.INDENT - first_line = None - empty = True - - tokgen = tokenize.generate_tokens(StringIO(self.text).readline) - for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen: - if self.show_tokens: # pragma: no cover - print("%10s %5s %-20r %r" % ( - tokenize.tok_name.get(toktype, toktype), - nice_pair((slineno, elineno)), ttext, ltext - )) - if toktype == token.INDENT: - indent += 1 - elif toktype == token.DEDENT: - indent -= 1 - elif toktype == token.NAME and ttext == 'class': - # Class definitions look like branches in the byte code, so - # we need to exclude them. The simplest way is to note the - # lines with the 'class' keyword. - self.classdefs.add(slineno) - elif toktype == token.OP and ttext == ':': - if not excluding and elineno in self.excluded: - # Start excluding a suite. We trigger off of the colon - # token so that the #pragma comment will be recognized on - # the same line as the colon. - exclude_indent = indent - excluding = True - elif toktype == token.STRING and prev_toktype == token.INDENT: - # Strings that are first on an indented line are docstrings. - # (a trick from trace.py in the stdlib.) This works for - # 99.9999% of cases. For the rest (!) see: - # http://stackoverflow.com/questions/1769332/x/1769794#1769794 - for i in range(slineno, elineno+1): - self.docstrings.add(i) - elif toktype == token.NEWLINE: - if first_line is not None and elineno != first_line: - # We're at the end of a line, and we've ended on a - # different line than the first line of the statement, - # so record a multi-line range. - rng = (first_line, elineno) - for l in range(first_line, elineno+1): - self.multiline[l] = rng - first_line = None - - if ttext.strip() and toktype != tokenize.COMMENT: - # A non-whitespace token. - empty = False - if first_line is None: - # The token is not whitespace, and is the first in a - # statement. - first_line = slineno - # Check whether to end an excluded suite. - if excluding and indent <= exclude_indent: - excluding = False - if excluding: - self.excluded.add(elineno) - - prev_toktype = toktype - - # Find the starts of the executable statements. - if not empty: - self.statement_starts.update(self.byte_parser._find_statements()) - - def first_line(self, line): - """Return the first line number of the statement including `line`.""" - rng = self.multiline.get(line) - if rng: - first_line = rng[0] - else: - first_line = line - return first_line - - def first_lines(self, lines, ignore=None): - """Map the line numbers in `lines` to the correct first line of the - statement. - - Skip any line mentioned in `ignore`. - - Returns a sorted list of the first lines. - - """ - ignore = ignore or [] - lset = set() - for l in lines: - if l in ignore: - continue - new_l = self.first_line(l) - if new_l not in ignore: - lset.add(new_l) - return sorted(lset) - - def parse_source(self): - """Parse source text to find executable lines, excluded lines, etc. - - Return values are 1) a sorted list of executable line numbers, and - 2) a sorted list of excluded line numbers. - - Reported line numbers are normalized to the first line of multi-line - statements. - - """ - self._raw_parse() - - excluded_lines = self.first_lines(self.excluded) - ignore = excluded_lines + list(self.docstrings) - lines = self.first_lines(self.statement_starts, ignore) - - return lines, excluded_lines - - def arcs(self): - """Get information about the arcs available in the code. - - Returns a sorted list of line number pairs. Line numbers have been - normalized to the first line of multiline statements. - - """ - all_arcs = [] - for l1, l2 in self.byte_parser._all_arcs(): - fl1 = self.first_line(l1) - fl2 = self.first_line(l2) - if fl1 != fl2: - all_arcs.append((fl1, fl2)) - return sorted(all_arcs) - arcs = expensive(arcs) - - def exit_counts(self): - """Get a mapping from line numbers to count of exits from that line. - - Excluded lines are excluded. - - """ - excluded_lines = self.first_lines(self.excluded) - exit_counts = {} - for l1, l2 in self.arcs(): - if l1 < 0: - # Don't ever report -1 as a line number - continue - if l1 in excluded_lines: - # Don't report excluded lines as line numbers. - continue - if l2 in excluded_lines: - # Arcs to excluded lines shouldn't count. - continue - if l1 not in exit_counts: - exit_counts[l1] = 0 - exit_counts[l1] += 1 - - # Class definitions have one extra exit, so remove one for each: - for l in self.classdefs: - # Ensure key is there: classdefs can include excluded lines. - if l in exit_counts: - exit_counts[l] -= 1 - - return exit_counts - exit_counts = expensive(exit_counts) - - -## Opcodes that guide the ByteParser. - -def _opcode(name): - """Return the opcode by name from the opcode module.""" - return opcode.opmap[name] - -def _opcode_set(*names): - """Return a set of opcodes by the names in `names`.""" - s = set() - for name in names: - try: - s.add(_opcode(name)) - except KeyError: - pass - return s - -# Opcodes that leave the code object. -OPS_CODE_END = _opcode_set('RETURN_VALUE') - -# Opcodes that unconditionally end the code chunk. -OPS_CHUNK_END = _opcode_set( - 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'RETURN_VALUE', 'RAISE_VARARGS', - 'BREAK_LOOP', 'CONTINUE_LOOP', - ) - -# Opcodes that unconditionally begin a new code chunk. By starting new chunks -# with unconditional jump instructions, we neatly deal with jumps to jumps -# properly. -OPS_CHUNK_BEGIN = _opcode_set('JUMP_ABSOLUTE', 'JUMP_FORWARD') - -# Opcodes that push a block on the block stack. -OPS_PUSH_BLOCK = _opcode_set( - 'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_WITH' - ) - -# Block types for exception handling. -OPS_EXCEPT_BLOCKS = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') - -# Opcodes that pop a block from the block stack. -OPS_POP_BLOCK = _opcode_set('POP_BLOCK') - -# Opcodes that have a jump destination, but aren't really a jump. -OPS_NO_JUMP = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') - -# Individual opcodes we need below. -OP_BREAK_LOOP = _opcode('BREAK_LOOP') -OP_END_FINALLY = _opcode('END_FINALLY') -OP_COMPARE_OP = _opcode('COMPARE_OP') -COMPARE_EXCEPTION = 10 # just have to get this const from the code. -OP_LOAD_CONST = _opcode('LOAD_CONST') -OP_RETURN_VALUE = _opcode('RETURN_VALUE') - - -class ByteParser(object): - """Parse byte codes to understand the structure of code.""" - - def __init__(self, code=None, text=None, filename=None): - if code: - self.code = code - else: - if not text: - assert filename, "If no code or text, need a filename" - sourcef = open(filename, 'rU') - text = sourcef.read() - sourcef.close() - - try: - # Python 2.3 and 2.4 don't like partial last lines, so be sure - # the text ends nicely for them. - self.code = compile(text + '\n', filename, "exec") - except SyntaxError: - _, synerr, _ = sys.exc_info() - raise CoverageException( - "Couldn't parse '%s' as Python source: '%s' at line %d" % - (filename, synerr.msg, synerr.lineno) - ) - - # Alternative Python implementations don't always provide all the - # attributes on code objects that we need to do the analysis. - for attr in ['co_lnotab', 'co_firstlineno', 'co_consts', 'co_code']: - if not hasattr(self.code, attr): - raise CoverageException( - "This implementation of Python doesn't support code " - "analysis.\n" - "Run coverage.py under CPython for this command." - ) - - def child_parsers(self): - """Iterate over all the code objects nested within this one. - - The iteration includes `self` as its first value. - - """ - return map(lambda c: ByteParser(code=c), CodeObjects(self.code)) - - # Getting numbers from the lnotab value changed in Py3.0. - if sys.version_info >= (3, 0): - def _lnotab_increments(self, lnotab): - """Return a list of ints from the lnotab bytes in 3.x""" - return list(lnotab) - else: - def _lnotab_increments(self, lnotab): - """Return a list of ints from the lnotab string in 2.x""" - return [ord(c) for c in lnotab] - - def _bytes_lines(self): - """Map byte offsets to line numbers in `code`. - - Uses co_lnotab described in Python/compile.c to map byte offsets to - line numbers. Returns a list: [(b0, l0), (b1, l1), ...] - - """ - # Adapted from dis.py in the standard library. - byte_increments = self._lnotab_increments(self.code.co_lnotab[0::2]) - line_increments = self._lnotab_increments(self.code.co_lnotab[1::2]) - - bytes_lines = [] - last_line_num = None - line_num = self.code.co_firstlineno - byte_num = 0 - for byte_incr, line_incr in zip(byte_increments, line_increments): - if byte_incr: - if line_num != last_line_num: - bytes_lines.append((byte_num, line_num)) - last_line_num = line_num - byte_num += byte_incr - line_num += line_incr - if line_num != last_line_num: - bytes_lines.append((byte_num, line_num)) - return bytes_lines - - def _find_statements(self): - """Find the statements in `self.code`. - - Return a set of line numbers that start statements. Recurses into all - code objects reachable from `self.code`. - - """ - stmts = set() - for bp in self.child_parsers(): - # Get all of the lineno information from this code. - for _, l in bp._bytes_lines(): - stmts.add(l) - return stmts - - def _disassemble(self): # pragma: no cover - """Disassemble code, for ad-hoc experimenting.""" - - import dis - - for bp in self.child_parsers(): - print("\n%s: " % bp.code) - dis.dis(bp.code) - print("Bytes lines: %r" % bp._bytes_lines()) - - print("") - - def _split_into_chunks(self): - """Split the code object into a list of `Chunk` objects. - - Each chunk is only entered at its first instruction, though there can - be many exits from a chunk. - - Returns a list of `Chunk` objects. - - """ - - # The list of chunks so far, and the one we're working on. - chunks = [] - chunk = None - bytes_lines_map = dict(self._bytes_lines()) - - # The block stack: loops and try blocks get pushed here for the - # implicit jumps that can occur. - # Each entry is a tuple: (block type, destination) - block_stack = [] - - # Some op codes are followed by branches that should be ignored. This - # is a count of how many ignores are left. - ignore_branch = 0 - - # We have to handle the last two bytecodes specially. - ult = penult = None - - for bc in ByteCodes(self.code.co_code): - # Maybe have to start a new chunk - if bc.offset in bytes_lines_map: - # Start a new chunk for each source line number. - if chunk: - chunk.exits.add(bc.offset) - chunk = Chunk(bc.offset, bytes_lines_map[bc.offset]) - chunks.append(chunk) - elif bc.op in OPS_CHUNK_BEGIN: - # Jumps deserve their own unnumbered chunk. This fixes - # problems with jumps to jumps getting confused. - if chunk: - chunk.exits.add(bc.offset) - chunk = Chunk(bc.offset) - chunks.append(chunk) - - if not chunk: - chunk = Chunk(bc.offset) - chunks.append(chunk) - - # Look at the opcode - if bc.jump_to >= 0 and bc.op not in OPS_NO_JUMP: - if ignore_branch: - # Someone earlier wanted us to ignore this branch. - ignore_branch -= 1 - else: - # The opcode has a jump, it's an exit for this chunk. - chunk.exits.add(bc.jump_to) - - if bc.op in OPS_CODE_END: - # The opcode can exit the code object. - chunk.exits.add(-self.code.co_firstlineno) - if bc.op in OPS_PUSH_BLOCK: - # The opcode adds a block to the block_stack. - block_stack.append((bc.op, bc.jump_to)) - if bc.op in OPS_POP_BLOCK: - # The opcode pops a block from the block stack. - block_stack.pop() - if bc.op in OPS_CHUNK_END: - # This opcode forces the end of the chunk. - if bc.op == OP_BREAK_LOOP: - # A break is implicit: jump where the top of the - # block_stack points. - chunk.exits.add(block_stack[-1][1]) - chunk = None - if bc.op == OP_END_FINALLY: - if block_stack: - # A break that goes through a finally will jump to whatever - # block is on top of the stack. - chunk.exits.add(block_stack[-1][1]) - # For the finally clause we need to find the closest exception - # block, and use its jump target as an exit. - for iblock in range(len(block_stack)-1, -1, -1): - if block_stack[iblock][0] in OPS_EXCEPT_BLOCKS: - chunk.exits.add(block_stack[iblock][1]) - break - if bc.op == OP_COMPARE_OP and bc.arg == COMPARE_EXCEPTION: - # This is an except clause. We want to overlook the next - # branch, so that except's don't count as branches. - ignore_branch += 1 - - penult = ult - ult = bc - - if chunks: - # The last two bytecodes could be a dummy "return None" that - # shouldn't be counted as real code. Every Python code object seems - # to end with a return, and a "return None" is inserted if there - # isn't an explicit return in the source. - if ult and penult: - if penult.op == OP_LOAD_CONST and ult.op == OP_RETURN_VALUE: - if self.code.co_consts[penult.arg] is None: - # This is "return None", but is it dummy? A real line - # would be a last chunk all by itself. - if chunks[-1].byte != penult.offset: - ex = -self.code.co_firstlineno - # Split the last chunk - last_chunk = chunks[-1] - last_chunk.exits.remove(ex) - last_chunk.exits.add(penult.offset) - chunk = Chunk(penult.offset) - chunk.exits.add(ex) - chunks.append(chunk) - - # Give all the chunks a length. - chunks[-1].length = bc.next_offset - chunks[-1].byte - for i in range(len(chunks)-1): - chunks[i].length = chunks[i+1].byte - chunks[i].byte - - return chunks - - def _arcs(self): - """Find the executable arcs in the code. - - Returns a set of pairs, (from,to). From and to are integer line - numbers. If from is < 0, then the arc is an entrance into the code - object. If to is < 0, the arc is an exit from the code object. - - """ - chunks = self._split_into_chunks() - - # A map from byte offsets to chunks jumped into. - byte_chunks = dict([(c.byte, c) for c in chunks]) - - # Build a map from byte offsets to actual lines reached. - byte_lines = {} - bytes_to_add = set([c.byte for c in chunks]) - - while bytes_to_add: - byte_to_add = bytes_to_add.pop() - if byte_to_add in byte_lines or byte_to_add < 0: - continue - - # Which lines does this chunk lead to? - bytes_considered = set() - bytes_to_consider = [byte_to_add] - lines = set() - - while bytes_to_consider: - byte = bytes_to_consider.pop() - bytes_considered.add(byte) - - # Find chunk for byte - try: - ch = byte_chunks[byte] - except KeyError: - for ch in chunks: - if ch.byte <= byte < ch.byte+ch.length: - break - else: - # No chunk for this byte! - raise Exception("Couldn't find chunk @ %d" % byte) - byte_chunks[byte] = ch - - if ch.line: - lines.add(ch.line) - else: - for ex in ch.exits: - if ex < 0: - lines.add(ex) - elif ex not in bytes_considered: - bytes_to_consider.append(ex) - - bytes_to_add.update(ch.exits) - - byte_lines[byte_to_add] = lines - - # Figure out for each chunk where the exits go. - arcs = set() - for chunk in chunks: - if chunk.line: - for ex in chunk.exits: - if ex < 0: - exit_lines = [ex] - else: - exit_lines = byte_lines[ex] - for exit_line in exit_lines: - if chunk.line != exit_line: - arcs.add((chunk.line, exit_line)) - for line in byte_lines[0]: - arcs.add((-1, line)) - - return arcs - - def _all_chunks(self): - """Returns a list of `Chunk` objects for this code and its children. - - See `_split_into_chunks` for details. - - """ - chunks = [] - for bp in self.child_parsers(): - chunks.extend(bp._split_into_chunks()) - - return chunks - - def _all_arcs(self): - """Get the set of all arcs in this code object and its children. - - See `_arcs` for details. - - """ - arcs = set() - for bp in self.child_parsers(): - arcs.update(bp._arcs()) - - return arcs - - -class Chunk(object): - """A sequence of bytecodes with a single entrance. - - To analyze byte code, we have to divide it into chunks, sequences of byte - codes such that each basic block has only one entrance, the first - instruction in the block. - - This is almost the CS concept of `basic block`_, except that we're willing - to have many exits from a chunk, and "basic block" is a more cumbersome - term. - - .. _basic block: http://en.wikipedia.org/wiki/Basic_block - - An exit < 0 means the chunk can leave the code (return). The exit is - the negative of the starting line number of the code block. - - """ - def __init__(self, byte, line=0): - self.byte = byte - self.line = line - self.length = 0 - self.exits = set() - - def __repr__(self): - return "<%d+%d @%d %r>" % ( - self.byte, self.length, self.line, list(self.exits) - ) - - -class AdHocMain(object): # pragma: no cover - """An ad-hoc main for code parsing experiments.""" - - def main(self, args): - """A main function for trying the code from the command line.""" - - from optparse import OptionParser - - parser = OptionParser() - parser.add_option( - "-c", action="store_true", dest="chunks", - help="Show basic block chunks" - ) - parser.add_option( - "-d", action="store_true", dest="dis", - help="Disassemble" - ) - parser.add_option( - "-R", action="store_true", dest="recursive", - help="Recurse to find source files" - ) - parser.add_option( - "-s", action="store_true", dest="source", - help="Show analyzed source" - ) - parser.add_option( - "-t", action="store_true", dest="tokens", - help="Show tokens" - ) - - options, args = parser.parse_args() - if options.recursive: - if args: - root = args[0] - else: - root = "." - for root, _, _ in os.walk(root): - for f in glob.glob(root + "/*.py"): - self.adhoc_one_file(options, f) - else: - self.adhoc_one_file(options, args[0]) - - def adhoc_one_file(self, options, filename): - """Process just one file.""" - - if options.dis or options.chunks: - try: - bp = ByteParser(filename=filename) - except CoverageException: - _, err, _ = sys.exc_info() - print("%s" % (err,)) - return - - if options.dis: - print("Main code:") - bp._disassemble() - - if options.chunks: - chunks = bp._all_chunks() - if options.recursive: - print("%6d: %s" % (len(chunks), filename)) - else: - print("Chunks: %r" % chunks) - arcs = bp._all_arcs() - print("Arcs: %r" % sorted(arcs)) - - if options.source or options.tokens: - cp = CodeParser(filename=filename, exclude=r"no\s*cover") - cp.show_tokens = options.tokens - cp._raw_parse() - - if options.source: - if options.chunks: - arc_width, arc_chars = self.arc_ascii_art(arcs) - else: - arc_width, arc_chars = 0, {} - - exit_counts = cp.exit_counts() - - for i, ltext in enumerate(cp.lines): - lineno = i+1 - m0 = m1 = m2 = m3 = a = ' ' - if lineno in cp.statement_starts: - m0 = '-' - exits = exit_counts.get(lineno, 0) - if exits > 1: - m1 = str(exits) - if lineno in cp.docstrings: - m2 = '"' - if lineno in cp.classdefs: - m2 = 'C' - if lineno in cp.excluded: - m3 = 'x' - a = arc_chars.get(lineno, '').ljust(arc_width) - print("%4d %s%s%s%s%s %s" % - (lineno, m0, m1, m2, m3, a, ltext) - ) - - def arc_ascii_art(self, arcs): - """Draw arcs as ascii art. - - Returns a width of characters needed to draw all the arcs, and a - dictionary mapping line numbers to ascii strings to draw for that line. - - """ - arc_chars = {} - for lfrom, lto in sorted(arcs): - if lfrom < 0: - arc_chars[lto] = arc_chars.get(lto, '') + 'v' - elif lto < 0: - arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^' - else: - if lfrom == lto - 1: - # Don't show obvious arcs. - continue - if lfrom < lto: - l1, l2 = lfrom, lto - else: - l1, l2 = lto, lfrom - w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)]) - for l in range(l1, l2+1): - if l == lfrom: - ch = '<' - elif l == lto: - ch = '>' - else: - ch = '|' - arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch - arc_width = 0 - - if arc_chars: - arc_width = max([len(a) for a in arc_chars.values()]) - else: - arc_width = 0 - - return arc_width, arc_chars - -if __name__ == '__main__': - AdHocMain().main(sys.argv[1:]) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py deleted file mode 100644 index 60b87932..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/phystokens.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Better tokenizing for coverage.py.""" - -import keyword, re, token, tokenize -from coverage.backward import StringIO # pylint: disable-msg=W0622 - -def phys_tokens(toks): - """Return all physical tokens, even line continuations. - - tokenize.generate_tokens() doesn't return a token for the backslash that - continues lines. This wrapper provides those tokens so that we can - re-create a faithful representation of the original source. - - Returns the same values as generate_tokens() - - """ - last_line = None - last_lineno = -1 - last_ttype = None - for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks: - if last_lineno != elineno: - if last_line and last_line[-2:] == "\\\n": - # We are at the beginning of a new line, and the last line - # ended with a backslash. We probably have to inject a - # backslash token into the stream. Unfortunately, there's more - # to figure out. This code:: - # - # usage = """\ - # HEY THERE - # """ - # - # triggers this condition, but the token text is:: - # - # '"""\\\nHEY THERE\n"""' - # - # so we need to figure out if the backslash is already in the - # string token or not. - inject_backslash = True - if last_ttype == tokenize.COMMENT: - # Comments like this \ - # should never result in a new token. - inject_backslash = False - elif ttype == token.STRING: - if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\': - # It's a multiline string and the first line ends with - # a backslash, so we don't need to inject another. - inject_backslash = False - if inject_backslash: - # Figure out what column the backslash is in. - ccol = len(last_line.split("\n")[-2]) - 1 - # Yield the token, with a fake token type. - yield ( - 99999, "\\\n", - (slineno, ccol), (slineno, ccol+2), - last_line - ) - last_line = ltext - last_ttype = ttype - yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext - last_lineno = elineno - - -def source_token_lines(source): - """Generate a series of lines, one for each line in `source`. - - Each line is a list of pairs, each pair is a token:: - - [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ] - - Each pair has a token class, and the token text. - - If you concatenate all the token texts, and then join them with newlines, - you should have your original `source` back, with two differences: - trailing whitespace is not preserved, and a final line with no newline - is indistinguishable from a final line with a newline. - - """ - ws_tokens = [token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL] - line = [] - col = 0 - source = source.expandtabs(8).replace('\r\n', '\n') - tokgen = tokenize.generate_tokens(StringIO(source).readline) - for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen): - mark_start = True - for part in re.split('(\n)', ttext): - if part == '\n': - yield line - line = [] - col = 0 - mark_end = False - elif part == '': - mark_end = False - elif ttype in ws_tokens: - mark_end = False - else: - if mark_start and scol > col: - line.append(("ws", " " * (scol - col))) - mark_start = False - tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3] - if ttype == token.NAME and keyword.iskeyword(ttext): - tok_class = "key" - line.append((tok_class, part)) - mark_end = True - scol = 0 - if mark_end: - col = ecol - - if line: - yield line diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py deleted file mode 100644 index 0fb353a2..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/report.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Reporter foundation for Coverage.""" - -import fnmatch, os -from coverage.codeunit import code_unit_factory -from coverage.misc import CoverageException, NoSource - -class Reporter(object): - """A base class for all reporters.""" - - def __init__(self, coverage, ignore_errors=False): - """Create a reporter. - - `coverage` is the coverage instance. `ignore_errors` controls how - skittish the reporter will be during file processing. - - """ - self.coverage = coverage - self.ignore_errors = ignore_errors - - # The code units to report on. Set by find_code_units. - self.code_units = [] - - # The directory into which to place the report, used by some derived - # classes. - self.directory = None - - def find_code_units(self, morfs, config): - """Find the code units we'll report on. - - `morfs` is a list of modules or filenames. `config` is a - CoverageConfig instance. - - """ - morfs = morfs or self.coverage.data.measured_files() - file_locator = self.coverage.file_locator - self.code_units = code_unit_factory(morfs, file_locator) - - if config.include: - patterns = [file_locator.abs_file(p) for p in config.include] - filtered = [] - for cu in self.code_units: - for pattern in patterns: - if fnmatch.fnmatch(cu.filename, pattern): - filtered.append(cu) - break - self.code_units = filtered - - if config.omit: - patterns = [file_locator.abs_file(p) for p in config.omit] - filtered = [] - for cu in self.code_units: - for pattern in patterns: - if fnmatch.fnmatch(cu.filename, pattern): - break - else: - filtered.append(cu) - self.code_units = filtered - - self.code_units.sort() - - def report_files(self, report_fn, morfs, config, directory=None): - """Run a reporting function on a number of morfs. - - `report_fn` is called for each relative morf in `morfs`. - - `config` is a CoverageConfig instance. - - """ - self.find_code_units(morfs, config) - - if not self.code_units: - raise CoverageException("No data to report.") - - self.directory = directory - if self.directory and not os.path.exists(self.directory): - os.makedirs(self.directory) - - for cu in self.code_units: - try: - report_fn(cu, self.coverage._analyze(cu)) - except NoSource: - if not self.ignore_errors: - raise diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py deleted file mode 100644 index 85071fe3..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/results.py +++ /dev/null @@ -1,233 +0,0 @@ -"""Results of coverage measurement.""" - -import os - -from coverage.backward import set, sorted # pylint: disable-msg=W0622 -from coverage.misc import format_lines, NoSource -from coverage.parser import CodeParser - - -class Analysis(object): - """The results of analyzing a code unit.""" - - def __init__(self, cov, code_unit): - self.coverage = cov - self.code_unit = code_unit - - self.filename = self.code_unit.filename - ext = os.path.splitext(self.filename)[1] - source = None - if ext == '.py': - if not os.path.exists(self.filename): - source = self.coverage.file_locator.get_zip_data(self.filename) - if not source: - raise NoSource("No source for code: %r" % self.filename) - - self.parser = CodeParser( - text=source, filename=self.filename, - exclude=self.coverage.exclude_re - ) - self.statements, self.excluded = self.parser.parse_source() - - # Identify missing statements. - executed = self.coverage.data.executed_lines(self.filename) - exec1 = self.parser.first_lines(executed) - self.missing = sorted(set(self.statements) - set(exec1)) - - if self.coverage.data.has_arcs(): - n_branches = self.total_branches() - mba = self.missing_branch_arcs() - n_missing_branches = sum([len(v) for v in mba.values()]) - else: - n_branches = n_missing_branches = 0 - - self.numbers = Numbers( - n_files=1, - n_statements=len(self.statements), - n_excluded=len(self.excluded), - n_missing=len(self.missing), - n_branches=n_branches, - n_missing_branches=n_missing_branches, - ) - - def missing_formatted(self): - """The missing line numbers, formatted nicely. - - Returns a string like "1-2, 5-11, 13-14". - - """ - return format_lines(self.statements, self.missing) - - def has_arcs(self): - """Were arcs measured in this result?""" - return self.coverage.data.has_arcs() - - def arc_possibilities(self): - """Returns a sorted list of the arcs in the code.""" - return self.parser.arcs() - - def arcs_executed(self): - """Returns a sorted list of the arcs actually executed in the code.""" - executed = self.coverage.data.executed_arcs(self.filename) - m2fl = self.parser.first_line - executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed] - return sorted(executed) - - def arcs_missing(self): - """Returns a sorted list of the arcs in the code not executed.""" - possible = self.arc_possibilities() - executed = self.arcs_executed() - missing = [p for p in possible if p not in executed] - return sorted(missing) - - def arcs_unpredicted(self): - """Returns a sorted list of the executed arcs missing from the code.""" - possible = self.arc_possibilities() - executed = self.arcs_executed() - # Exclude arcs here which connect a line to itself. They can occur - # in executed data in some cases. This is where they can cause - # trouble, and here is where it's the least burden to remove them. - unpredicted = [ - e for e in executed - if e not in possible and e[0] != e[1] - ] - return sorted(unpredicted) - - def branch_lines(self): - """Returns a list of line numbers that have more than one exit.""" - exit_counts = self.parser.exit_counts() - return [l1 for l1,count in exit_counts.items() if count > 1] - - def total_branches(self): - """How many total branches are there?""" - exit_counts = self.parser.exit_counts() - return sum([count for count in exit_counts.values() if count > 1]) - - def missing_branch_arcs(self): - """Return arcs that weren't executed from branch lines. - - Returns {l1:[l2a,l2b,...], ...} - - """ - missing = self.arcs_missing() - branch_lines = set(self.branch_lines()) - mba = {} - for l1, l2 in missing: - if l1 in branch_lines: - if l1 not in mba: - mba[l1] = [] - mba[l1].append(l2) - return mba - - def branch_stats(self): - """Get stats about branches. - - Returns a dict mapping line numbers to a tuple: - (total_exits, taken_exits). - """ - - exit_counts = self.parser.exit_counts() - missing_arcs = self.missing_branch_arcs() - stats = {} - for lnum in self.branch_lines(): - exits = exit_counts[lnum] - try: - missing = len(missing_arcs[lnum]) - except KeyError: - missing = 0 - stats[lnum] = (exits, exits - missing) - return stats - - -class Numbers(object): - """The numerical results of measuring coverage. - - This holds the basic statistics from `Analysis`, and is used to roll - up statistics across files. - - """ - # A global to determine the precision on coverage percentages, the number - # of decimal places. - _precision = 0 - _near0 = 1.0 # These will change when _precision is changed. - _near100 = 99.0 - - def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0, - n_branches=0, n_missing_branches=0 - ): - self.n_files = n_files - self.n_statements = n_statements - self.n_excluded = n_excluded - self.n_missing = n_missing - self.n_branches = n_branches - self.n_missing_branches = n_missing_branches - - def set_precision(cls, precision): - """Set the number of decimal places used to report percentages.""" - assert 0 <= precision < 10 - cls._precision = precision - cls._near0 = 1.0 / 10**precision - cls._near100 = 100.0 - cls._near0 - set_precision = classmethod(set_precision) - - def _get_n_executed(self): - """Returns the number of executed statements.""" - return self.n_statements - self.n_missing - n_executed = property(_get_n_executed) - - def _get_n_executed_branches(self): - """Returns the number of executed branches.""" - return self.n_branches - self.n_missing_branches - n_executed_branches = property(_get_n_executed_branches) - - def _get_pc_covered(self): - """Returns a single percentage value for coverage.""" - if self.n_statements > 0: - pc_cov = (100.0 * (self.n_executed + self.n_executed_branches) / - (self.n_statements + self.n_branches)) - else: - pc_cov = 100.0 - return pc_cov - pc_covered = property(_get_pc_covered) - - def _get_pc_covered_str(self): - """Returns the percent covered, as a string, without a percent sign. - - The important thing here is that "0" only be returned when it's truly - zero, and "100" only be returned when it's truly 100. - - """ - pc = self.pc_covered - if 0 < pc < self._near0: - pc = self._near0 - elif self._near100 < pc < 100: - pc = self._near100 - else: - pc = round(pc, self._precision) - return "%.*f" % (self._precision, pc) - pc_covered_str = property(_get_pc_covered_str) - - def pc_str_width(cls): - """How many characters wide can pc_covered_str be?""" - width = 3 # "100" - if cls._precision > 0: - width += 1 + cls._precision - return width - pc_str_width = classmethod(pc_str_width) - - def __add__(self, other): - nums = Numbers() - nums.n_files = self.n_files + other.n_files - nums.n_statements = self.n_statements + other.n_statements - nums.n_excluded = self.n_excluded + other.n_excluded - nums.n_missing = self.n_missing + other.n_missing - nums.n_branches = self.n_branches + other.n_branches - nums.n_missing_branches = (self.n_missing_branches + - other.n_missing_branches) - return nums - - def __radd__(self, other): - # Implementing 0+Numbers allows us to sum() a list of Numbers. - if other == 0: - return self - raise NotImplemented diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py deleted file mode 100644 index 599ae782..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/summary.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Summary reporting""" - -import sys - -from coverage.report import Reporter -from coverage.results import Numbers - - -class SummaryReporter(Reporter): - """A reporter for writing the summary report.""" - - def __init__(self, coverage, show_missing=True, ignore_errors=False): - super(SummaryReporter, self).__init__(coverage, ignore_errors) - self.show_missing = show_missing - self.branches = coverage.data.has_arcs() - - def report(self, morfs, outfile=None, config=None): - """Writes a report summarizing coverage statistics per module. - - `outfile` is a file object to write the summary to. `config` is a - CoverageConfig instance. - - """ - self.find_code_units(morfs, config) - - # Prepare the formatting strings - max_name = max([len(cu.name) for cu in self.code_units] + [5]) - fmt_name = "%%- %ds " % max_name - fmt_err = "%s %s: %s\n" - header = (fmt_name % "Name") + " Stmts Miss" - fmt_coverage = fmt_name + "%6d %6d" - if self.branches: - header += " Branch BrPart" - fmt_coverage += " %6d %6d" - width100 = Numbers.pc_str_width() - header += "%*s" % (width100+4, "Cover") - fmt_coverage += "%%%ds%%%%" % (width100+3,) - if self.show_missing: - header += " Missing" - fmt_coverage += " %s" - rule = "-" * len(header) + "\n" - header += "\n" - fmt_coverage += "\n" - - if not outfile: - outfile = sys.stdout - - # Write the header - outfile.write(header) - outfile.write(rule) - - total = Numbers() - - for cu in self.code_units: - try: - analysis = self.coverage._analyze(cu) - nums = analysis.numbers - args = (cu.name, nums.n_statements, nums.n_missing) - if self.branches: - args += (nums.n_branches, nums.n_missing_branches) - args += (nums.pc_covered_str,) - if self.show_missing: - args += (analysis.missing_formatted(),) - outfile.write(fmt_coverage % args) - total += nums - except KeyboardInterrupt: # pragma: no cover - raise - except: - if not self.ignore_errors: - typ, msg = sys.exc_info()[:2] - outfile.write(fmt_err % (cu.name, typ.__name__, msg)) - - if total.n_files > 1: - outfile.write(rule) - args = ("TOTAL", total.n_statements, total.n_missing) - if self.branches: - args += (total.n_branches, total.n_missing_branches) - args += (total.pc_covered_str,) - if self.show_missing: - args += ("",) - outfile.write(fmt_coverage % args) diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py deleted file mode 100644 index c39e061e..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/templite.py +++ /dev/null @@ -1,166 +0,0 @@ -"""A simple Python template renderer, for a nano-subset of Django syntax.""" - -# Coincidentally named the same as http://code.activestate.com/recipes/496702/ - -import re, sys - -class Templite(object): - """A simple template renderer, for a nano-subset of Django syntax. - - Supported constructs are extended variable access:: - - {{var.modifer.modifier|filter|filter}} - - loops:: - - {% for var in list %}...{% endfor %} - - and ifs:: - - {% if var %}...{% endif %} - - Comments are within curly-hash markers:: - - {# This will be ignored #} - - Construct a Templite with the template text, then use `render` against a - dictionary context to create a finished string. - - """ - def __init__(self, text, *contexts): - """Construct a Templite with the given `text`. - - `contexts` are dictionaries of values to use for future renderings. - These are good for filters and global values. - - """ - self.text = text - self.context = {} - for context in contexts: - self.context.update(context) - - # Split the text to form a list of tokens. - toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text) - - # Parse the tokens into a nested list of operations. Each item in the - # list is a tuple with an opcode, and arguments. They'll be - # interpreted by TempliteEngine. - # - # When parsing an action tag with nested content (if, for), the current - # ops list is pushed onto ops_stack, and the parsing continues in a new - # ops list that is part of the arguments to the if or for op. - ops = [] - ops_stack = [] - for tok in toks: - if tok.startswith('{{'): - # Expression: ('exp', expr) - ops.append(('exp', tok[2:-2].strip())) - elif tok.startswith('{#'): - # Comment: ignore it and move on. - continue - elif tok.startswith('{%'): - # Action tag: split into words and parse further. - words = tok[2:-2].strip().split() - if words[0] == 'if': - # If: ('if', (expr, body_ops)) - if_ops = [] - assert len(words) == 2 - ops.append(('if', (words[1], if_ops))) - ops_stack.append(ops) - ops = if_ops - elif words[0] == 'for': - # For: ('for', (varname, listexpr, body_ops)) - assert len(words) == 4 and words[2] == 'in' - for_ops = [] - ops.append(('for', (words[1], words[3], for_ops))) - ops_stack.append(ops) - ops = for_ops - elif words[0].startswith('end'): - # Endsomething. Pop the ops stack - ops = ops_stack.pop() - assert ops[-1][0] == words[0][3:] - else: - raise SyntaxError("Don't understand tag %r" % words) - else: - ops.append(('lit', tok)) - - assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0] - self.ops = ops - - def render(self, context=None): - """Render this template by applying it to `context`. - - `context` is a dictionary of values to use in this rendering. - - """ - # Make the complete context we'll use. - ctx = dict(self.context) - if context: - ctx.update(context) - - # Run it through an engine, and return the result. - engine = _TempliteEngine(ctx) - engine.execute(self.ops) - return "".join(engine.result) - - -class _TempliteEngine(object): - """Executes Templite objects to produce strings.""" - def __init__(self, context): - self.context = context - self.result = [] - - def execute(self, ops): - """Execute `ops` in the engine. - - Called recursively for the bodies of if's and loops. - - """ - for op, args in ops: - if op == 'lit': - self.result.append(args) - elif op == 'exp': - try: - self.result.append(str(self.evaluate(args))) - except: - exc_class, exc, _ = sys.exc_info() - new_exc = exc_class("Couldn't evaluate {{ %s }}: %s" - % (args, exc)) - raise new_exc - elif op == 'if': - expr, body = args - if self.evaluate(expr): - self.execute(body) - elif op == 'for': - var, lis, body = args - vals = self.evaluate(lis) - for val in vals: - self.context[var] = val - self.execute(body) - else: - raise AssertionError("TempliteEngine doesn't grok op %r" % op) - - def evaluate(self, expr): - """Evaluate an expression. - - `expr` can have pipes and dots to indicate data access and filtering. - - """ - if "|" in expr: - pipes = expr.split("|") - value = self.evaluate(pipes[0]) - for func in pipes[1:]: - value = self.evaluate(func)(value) - elif "." in expr: - dots = expr.split('.') - value = self.evaluate(dots[0]) - for dot in dots[1:]: - try: - value = getattr(value, dot) - except AttributeError: - value = value[dot] - if hasattr(value, '__call__'): - value = value() - else: - value = self.context[expr] - return value diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py deleted file mode 100644 index a6f2aa9e..00000000 --- a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.py +++ /dev/null @@ -1,7 +0,0 @@ -def __bootstrap__(): - global __bootstrap__, __loader__, __file__ - import sys, pkg_resources, imp - __file__ = pkg_resources.resource_filename(__name__,'tracer.so') - __loader__ = None; del __bootstrap__, __loader__ - imp.load_dynamic(__name__,__file__) -__bootstrap__() diff --git a/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so b/coverage-3.4-py2.7-macosx-10.5-x86_64.egg/coverage/tracer.so deleted file mode 100755 index 9f34cf3ec1dba6d24b9b7c334a962adc87f58cc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15256 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 diff --git a/files.txt b/files.txt deleted file mode 100644 index 80e76291..00000000 --- a/files.txt +++ /dev/null @@ -1 +0,0 @@ -/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/oauth2-1.5.170-py2.7.egg diff --git a/mock-0.7.0-py2.7.egg b/mock-0.7.0-py2.7.egg deleted file mode 100644 index a96ba5a17e8f537853458472c33bdd5608cd2cf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24549 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