From 0dc3c0252f08e772d43a7a075a015d44502d77dc Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Sun, 6 Mar 2016 12:47:51 -0800 Subject: [PATCH] upgrade wakatime-cli to v4.1.11 --- plugin/packages/wakatime/__about__.py | 2 +- plugin/packages/wakatime/constants.py | 1 + plugin/packages/wakatime/logger.py | 9 +- plugin/packages/wakatime/main.py | 136 ++++++++++-------- .../wakatime/packages/requests/__init__.py | 2 +- .../wakatime/packages/requests/adapters.py | 2 +- .../wakatime/packages/requests/auth.py | 18 +++ .../wakatime/packages/requests/cookies.py | 6 + .../wakatime/packages/requests/sessions.py | 20 +-- plugin/packages/wakatime/projects/git.py | 4 +- .../packages/wakatime/projects/mercurial.py | 4 +- .../projects/wakatime_project_file.py | 4 +- plugin/packages/wakatime/session_cache.py | 10 +- plugin/packages/wakatime/stats.py | 2 +- 14 files changed, 139 insertions(+), 81 deletions(-) diff --git a/plugin/packages/wakatime/__about__.py b/plugin/packages/wakatime/__about__.py index 70d1a94..04db754 100644 --- a/plugin/packages/wakatime/__about__.py +++ b/plugin/packages/wakatime/__about__.py @@ -1,7 +1,7 @@ __title__ = 'wakatime' __description__ = 'Common interface to the WakaTime api.' __url__ = 'https://github.com/wakatime/wakatime' -__version_info__ = ('4', '1', '10') +__version_info__ = ('4', '1', '11') __version__ = '.'.join(__version_info__) __author__ = 'Alan Hamlett' __author_email__ = 'alan@wakatime.com' diff --git a/plugin/packages/wakatime/constants.py b/plugin/packages/wakatime/constants.py index c88f835..135039e 100644 --- a/plugin/packages/wakatime/constants.py +++ b/plugin/packages/wakatime/constants.py @@ -13,3 +13,4 @@ SUCCESS = 0 API_ERROR = 102 CONFIG_FILE_PARSE_ERROR = 103 +AUTH_ERROR = 104 diff --git a/plugin/packages/wakatime/logger.py b/plugin/packages/wakatime/logger.py index 13d9454..78b38ee 100644 --- a/plugin/packages/wakatime/logger.py +++ b/plugin/packages/wakatime/logger.py @@ -73,7 +73,14 @@ class JsonFormatter(logging.Formatter): def traceback_formatter(*args, **kwargs): - logging.getLogger('WakaTime').error(traceback.format_exc()) + if 'level' in kwargs and (kwargs['level'].lower() == 'warn' or kwargs['level'].lower() == 'warning'): + logging.getLogger('WakaTime').warning(traceback.format_exc()) + elif 'level' in kwargs and kwargs['level'].lower() == 'info': + logging.getLogger('WakaTime').info(traceback.format_exc()) + elif 'level' in kwargs and kwargs['level'].lower() == 'debug': + logging.getLogger('WakaTime').debug(traceback.format_exc()) + else: + logging.getLogger('WakaTime').error(traceback.format_exc()) def set_log_level(logger, args): diff --git a/plugin/packages/wakatime/main.py b/plugin/packages/wakatime/main.py index ff94e3e..232f4ea 100644 --- a/plugin/packages/wakatime/main.py +++ b/plugin/packages/wakatime/main.py @@ -25,12 +25,18 @@ try: except ImportError: # pragma: nocover import configparser -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages')) +pwd = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.dirname(pwd)) +sys.path.insert(0, os.path.join(pwd, 'packages')) from .__about__ import __version__ from .compat import u, open, is_py3 -from .constants import SUCCESS, API_ERROR, CONFIG_FILE_PARSE_ERROR +from .constants import ( + API_ERROR, + AUTH_ERROR, + CONFIG_FILE_PARSE_ERROR, + SUCCESS, +) from .logger import setup_logging from .offlinequeue import Queue from .packages import argparse @@ -123,12 +129,14 @@ def parseArguments(): '"url", "domain", or "app"; defaults to file.') parser.add_argument('--proxy', dest='proxy', help='optional https proxy url; for example: '+ - 'https://user:pass@localhost:8080') + 'https://user:pass@localhost:8080') parser.add_argument('--project', dest='project', help='optional project name') parser.add_argument('--alternate-project', dest='alternate_project', - help='optional alternate project name; auto-discovered project takes priority') - parser.add_argument('--hostname', dest='hostname', help='hostname of current machine.') + help='optional alternate project name; auto-discovered project '+ + 'takes priority') + parser.add_argument('--hostname', dest='hostname', help='hostname of '+ + 'current machine.') parser.add_argument('--disableoffline', dest='offline', action='store_false', help='disables offline time logging instead of queuing logged time') @@ -285,10 +293,14 @@ def get_user_agent(plugin): return user_agent -def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, entity=None, - timestamp=None, isWrite=None, plugin=None, offline=None, entity_type='file', - hidefilenames=None, proxy=None, api_url=None, timeout=None, **kwargs): +def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, + entity=None, timestamp=None, isWrite=None, plugin=None, + offline=None, entity_type='file', hidefilenames=None, + proxy=None, api_url=None, timeout=None, **kwargs): """Sends heartbeat as POST request to WakaTime api server. + + Returns `SUCCESS` when heartbeat was sent, otherwise returns an + error code constant. """ if not api_url: @@ -333,7 +345,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, 'Authorization': auth, } if hostname: - headers['X-Machine-Name'] = hostname + headers['X-Machine-Name'] = u(hostname) proxies = {} if proxy: proxies['https'] = proxy @@ -353,7 +365,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, response = None try: response = session.post(api_url, data=request_body, headers=headers, - proxies=proxies, timeout=timeout) + proxies=proxies, timeout=timeout) except RequestException: exception_data = { sys.exc_info()[0].__name__: u(sys.exc_info()[1]), @@ -368,40 +380,74 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, else: log.error(exception_data) else: - response_code = response.status_code if response is not None else None - response_content = response.text if response is not None else None - if response_code == requests.codes.created or response_code == requests.codes.accepted: + code = response.status_code if response is not None else None + content = response.text if response is not None else None + if code == requests.codes.created or code == requests.codes.accepted: log.debug({ - 'response_code': response_code, + 'response_code': code, }) session_cache.save(session) - return True + return SUCCESS if offline: - if response_code != 400: + if code != 400: queue = Queue() queue.push(data, json.dumps(stats), plugin) - if response_code == 401: + if code == 401: log.error({ - 'response_code': response_code, - 'response_content': response_content, + 'response_code': code, + 'response_content': content, }) + session_cache.delete() + return AUTH_ERROR elif log.isEnabledFor(logging.DEBUG): log.warn({ - 'response_code': response_code, - 'response_content': response_content, + 'response_code': code, + 'response_content': content, }) else: log.error({ - 'response_code': response_code, - 'response_content': response_content, + 'response_code': code, + 'response_content': content, }) else: log.error({ - 'response_code': response_code, - 'response_content': response_content, + 'response_code': code, + 'response_content': content, }) session_cache.delete() - return False + return API_ERROR + + +def sync_offline_heartbeats(args, hostname): + """Sends all heartbeats which were cached in the offline Queue.""" + + queue = Queue() + while True: + heartbeat = queue.pop() + if heartbeat is None: + break + status = send_heartbeat( + project=heartbeat['project'], + entity=heartbeat['entity'], + timestamp=heartbeat['time'], + branch=heartbeat['branch'], + hostname=hostname, + stats=json.loads(heartbeat['stats']), + key=args.key, + isWrite=heartbeat['is_write'], + plugin=heartbeat['plugin'], + offline=args.offline, + hidefilenames=args.hidefilenames, + entity_type=heartbeat['type'], + proxy=args.proxy, + api_url=args.api_url, + timeout=args.timeout, + ) + if status != SUCCESS: + if status == AUTH_ERROR: + return AUTH_ERROR + break + return SUCCESS def execute(argv=None): @@ -438,37 +484,15 @@ def execute(argv=None): kwargs['project'] = project kwargs['branch'] = branch kwargs['stats'] = stats - kwargs['hostname'] = args.hostname or socket.gethostname() + hostname = args.hostname or socket.gethostname() + kwargs['hostname'] = hostname kwargs['timeout'] = args.timeout - if send_heartbeat(**kwargs): - queue = Queue() - while True: - heartbeat = queue.pop() - if heartbeat is None: - break - sent = send_heartbeat( - project=heartbeat['project'], - entity=heartbeat['entity'], - timestamp=heartbeat['time'], - branch=heartbeat['branch'], - hostname=kwargs['hostname'], - stats=json.loads(heartbeat['stats']), - key=args.key, - isWrite=heartbeat['is_write'], - plugin=heartbeat['plugin'], - offline=args.offline, - hidefilenames=args.hidefilenames, - entity_type=heartbeat['type'], - proxy=args.proxy, - api_url=args.api_url, - timeout=args.timeout, - ) - if not sent: - break - return SUCCESS - - return API_ERROR + status = send_heartbeat(**kwargs) + if status == SUCCESS: + return sync_offline_heartbeats(args, hostname) + else: + return status else: log.debug('File does not exist; ignoring this heartbeat.') diff --git a/plugin/packages/wakatime/packages/requests/__init__.py b/plugin/packages/wakatime/packages/requests/__init__.py index bd5b5b9..1218d43 100644 --- a/plugin/packages/wakatime/packages/requests/__init__.py +++ b/plugin/packages/wakatime/packages/requests/__init__.py @@ -46,7 +46,7 @@ __version__ = '2.9.1' __build__ = 0x020901 __author__ = 'Kenneth Reitz' __license__ = 'Apache 2.0' -__copyright__ = 'Copyright 2015 Kenneth Reitz' +__copyright__ = 'Copyright 2016 Kenneth Reitz' # Attempt to enable urllib3's SNI support, if possible try: diff --git a/plugin/packages/wakatime/packages/requests/adapters.py b/plugin/packages/wakatime/packages/requests/adapters.py index 6266d5b..4f2b23c 100644 --- a/plugin/packages/wakatime/packages/requests/adapters.py +++ b/plugin/packages/wakatime/packages/requests/adapters.py @@ -65,7 +65,7 @@ class HTTPAdapter(BaseAdapter): :param pool_connections: The number of urllib3 connection pools to cache. :param pool_maxsize: The maximum number of connections to save in the pool. - :param int max_retries: The maximum number of retries each connection + :param max_retries: The maximum number of retries each connection should attempt. Note, this applies only to failed DNS lookups, socket connections and connection timeouts, never to requests where data has made it to the server. By default, Requests does not retry failed diff --git a/plugin/packages/wakatime/packages/requests/auth.py b/plugin/packages/wakatime/packages/requests/auth.py index 2af55fb..edf4c8d 100644 --- a/plugin/packages/wakatime/packages/requests/auth.py +++ b/plugin/packages/wakatime/packages/requests/auth.py @@ -47,6 +47,15 @@ class HTTPBasicAuth(AuthBase): self.username = username self.password = password + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + def __call__(self, r): r.headers['Authorization'] = _basic_auth_str(self.username, self.password) return r @@ -221,3 +230,12 @@ class HTTPDigestAuth(AuthBase): self._thread_local.num_401_calls = 1 return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/plugin/packages/wakatime/packages/requests/cookies.py b/plugin/packages/wakatime/packages/requests/cookies.py index b85fd2b..eee5168 100644 --- a/plugin/packages/wakatime/packages/requests/cookies.py +++ b/plugin/packages/wakatime/packages/requests/cookies.py @@ -277,6 +277,12 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): dictionary[cookie.name] = cookie.value return dictionary + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + def __getitem__(self, name): """Dict-like __getitem__() for compatibility with client code. Throws exception if there are more than one cookie with name. In that case, diff --git a/plugin/packages/wakatime/packages/requests/sessions.py b/plugin/packages/wakatime/packages/requests/sessions.py index 9eaa36a..639668f 100644 --- a/plugin/packages/wakatime/packages/requests/sessions.py +++ b/plugin/packages/wakatime/packages/requests/sessions.py @@ -110,7 +110,7 @@ class SessionRedirectMixin(object): resp.raw.read(decode_content=False) if i >= self.max_redirects: - raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects) + raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) # Release the connection back into the pool. resp.close() @@ -553,19 +553,21 @@ class Session(SessionRedirectMixin): if not isinstance(request, PreparedRequest): raise ValueError('You can only send PreparedRequests.') - checked_urls = set() - while request.url in self.redirect_cache: - checked_urls.add(request.url) - new_url = self.redirect_cache.get(request.url) - if new_url in checked_urls: - break - request.url = new_url - # Set up variables needed for resolve_redirects and dispatching of hooks allow_redirects = kwargs.pop('allow_redirects', True) stream = kwargs.get('stream') hooks = request.hooks + # Resolve URL in redirect cache, if available. + if allow_redirects: + checked_urls = set() + while request.url in self.redirect_cache: + checked_urls.add(request.url) + new_url = self.redirect_cache.get(request.url) + if new_url in checked_urls: + break + request.url = new_url + # Get the appropriate adapter to use adapter = self.get_adapter(url=request.url) diff --git a/plugin/packages/wakatime/projects/git.py b/plugin/packages/wakatime/projects/git.py index 091f5c9..fb4f2ac 100644 --- a/plugin/packages/wakatime/projects/git.py +++ b/plugin/packages/wakatime/projects/git.py @@ -44,9 +44,9 @@ class Git(BaseProject): with open(head, 'r', encoding=sys.getfilesystemencoding()) as fh: return u(fh.readline().strip().rsplit('/', 1)[-1]) except: - log.traceback() + log.traceback('warn') except IOError: # pragma: nocover - log.traceback() + log.traceback('warn') return None def _project_base(self): diff --git a/plugin/packages/wakatime/projects/mercurial.py b/plugin/packages/wakatime/projects/mercurial.py index 399edd5..4369ddb 100644 --- a/plugin/packages/wakatime/projects/mercurial.py +++ b/plugin/packages/wakatime/projects/mercurial.py @@ -42,9 +42,9 @@ class Mercurial(BaseProject): with open(branch_file, 'r', encoding=sys.getfilesystemencoding()) as fh: return u(fh.readline().strip().rsplit('/', 1)[-1]) except: - log.traceback() + log.traceback('warn') except IOError: # pragma: nocover - log.traceback() + log.traceback('warn') return u('default') def _find_hg_config_dir(self, path): diff --git a/plugin/packages/wakatime/projects/wakatime_project_file.py b/plugin/packages/wakatime/projects/wakatime_project_file.py index 76fc7d2..8ec4ccf 100644 --- a/plugin/packages/wakatime/projects/wakatime_project_file.py +++ b/plugin/packages/wakatime/projects/wakatime_project_file.py @@ -41,9 +41,9 @@ class WakaTimeProjectFile(BaseProject): self._project_name = u(fh.readline().strip()) self._project_branch = u(fh.readline().strip()) except: - log.traceback() + log.traceback('warn') except IOError: # pragma: nocover - log.traceback() + log.traceback('warn') return True return False diff --git a/plugin/packages/wakatime/session_cache.py b/plugin/packages/wakatime/session_cache.py index 8804748..e20beae 100644 --- a/plugin/packages/wakatime/session_cache.py +++ b/plugin/packages/wakatime/session_cache.py @@ -57,7 +57,7 @@ class SessionCache(object): conn.commit() conn.close() except: # pragma: nocover - log.traceback() + log.traceback('debug') def get(self): @@ -72,7 +72,7 @@ class SessionCache(object): try: conn, c = self.connect() except: - log.traceback() + log.traceback('debug') return requests.session() session = None @@ -83,12 +83,12 @@ class SessionCache(object): if row is not None: session = pickle.loads(row[0]) except: # pragma: nocover - log.traceback() + log.traceback('debug') try: conn.close() except: # pragma: nocover - log.traceback() + log.traceback('debug') return session if session is not None else requests.session() @@ -105,4 +105,4 @@ class SessionCache(object): conn.commit() conn.close() except: - log.traceback() + log.traceback('debug') diff --git a/plugin/packages/wakatime/stats.py b/plugin/packages/wakatime/stats.py index 2606fa4..81fb283 100644 --- a/plugin/packages/wakatime/stats.py +++ b/plugin/packages/wakatime/stats.py @@ -191,5 +191,5 @@ def get_file_contents(file_name): with open(file_name, 'r', encoding=sys.getfilesystemencoding()) as fh: text = fh.read(512000) except: - log.traceback() + log.traceback('debug') return text