diff --git a/packages/wakatime/__about__.py b/packages/wakatime/__about__.py index aa701ee..3a9cd2b 100644 --- a/packages/wakatime/__about__.py +++ b/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', '8') +__version_info__ = ('4', '1', '9') __version__ = '.'.join(__version_info__) __author__ = 'Alan Hamlett' __author_email__ = 'alan@wakatime.com' diff --git a/packages/wakatime/constants.py b/packages/wakatime/constants.py new file mode 100644 index 0000000..c88f835 --- /dev/null +++ b/packages/wakatime/constants.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" + wakatime.constants + ~~~~~~~~~~~~~~~~~~ + + Constant variable definitions. + + :copyright: (c) 2016 Alan Hamlett. + :license: BSD, see LICENSE for more details. +""" + + +SUCCESS = 0 +API_ERROR = 102 +CONFIG_FILE_PARSE_ERROR = 103 diff --git a/packages/wakatime/dependencies/go.py b/packages/wakatime/dependencies/go.py new file mode 100644 index 0000000..2231e70 --- /dev/null +++ b/packages/wakatime/dependencies/go.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" + wakatime.languages.go + ~~~~~~~~~~~~~~~~~~~~~ + + Parse dependencies from Go code. + + :copyright: (c) 2016 Alan Hamlett. + :license: BSD, see LICENSE for more details. +""" + +from . import TokenParser + + +class GoParser(TokenParser): + state = None + parens = 0 + aliases = 0 + exclude = [ + r'^"fmt"$', + ] + + def parse(self): + for index, token, content in self.tokens: + self._process_token(token, content) + return self.dependencies + + def _process_token(self, token, content): + if self.partial(token) == 'Namespace': + self._process_namespace(token, content) + elif self.partial(token) == 'Punctuation': + self._process_punctuation(token, content) + elif self.partial(token) == 'String': + self._process_string(token, content) + elif self.partial(token) == 'Text': + self._process_text(token, content) + elif self.partial(token) == 'Other': + self._process_other(token, content) + else: + self._process_misc(token, content) + + def _process_namespace(self, token, content): + self.state = content + self.parens = 0 + self.aliases = 0 + + def _process_string(self, token, content): + if self.state == 'import': + self.append(content, truncate=False) + + def _process_punctuation(self, token, content): + if content == '(': + self.parens += 1 + elif content == ')': + self.parens -= 1 + elif content == '.': + self.aliases += 1 + else: + self.state = None + + def _process_text(self, token, content): + if self.state == 'import': + if content == "\n" and self.parens <= 0: + self.state = None + self.parens = 0 + self.aliases = 0 + else: + self.state = None + + def _process_other(self, token, content): + if self.state == 'import': + self.aliases += 1 + else: + self.state = None + + def _process_misc(self, token, content): + self.state = None diff --git a/packages/wakatime/logger.py b/packages/wakatime/logger.py index 19f8581..13d9454 100644 --- a/packages/wakatime/logger.py +++ b/packages/wakatime/logger.py @@ -11,9 +11,10 @@ import logging import os -import sys +import traceback from .compat import u +from .packages.requests.packages import urllib3 try: from collections import OrderedDict # pragma: nocover except ImportError: # pragma: nocover @@ -70,8 +71,9 @@ class JsonFormatter(logging.Formatter): del data['plugin'] return CustomEncoder().encode(data) - def formatException(self, exc_info): - return sys.exec_info[2].format_exc() + +def traceback_formatter(*args, **kwargs): + logging.getLogger('WakaTime').error(traceback.format_exc()) def set_log_level(logger, args): @@ -82,6 +84,7 @@ def set_log_level(logger, args): def setup_logging(args, version): + urllib3.disable_warnings() logger = logging.getLogger('WakaTime') for handler in logger.handlers: logger.removeHandler(handler) @@ -102,6 +105,9 @@ def setup_logging(args, version): handler.setFormatter(formatter) logger.addHandler(handler) + # add custom traceback logging method + logger.traceback = traceback_formatter + warnings_formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z') warnings_formatter.setup( timestamp=args.timestamp, diff --git a/packages/wakatime/main.py b/packages/wakatime/main.py index 306ad77..5f3eaee 100644 --- a/packages/wakatime/main.py +++ b/packages/wakatime/main.py @@ -30,6 +30,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pac from .__about__ import __version__ from .compat import u, open, is_py3 +from .constants import SUCCESS, API_ERROR, CONFIG_FILE_PARSE_ERROR from .logger import setup_logging from .offlinequeue import Queue from .packages import argparse @@ -290,7 +291,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, """ if not api_url: - api_url = 'https://wakatime.com/api/v1/heartbeats' + api_url = 'https://api.wakatime.com/api/v1/heartbeats' if not timeout: timeout = 30 log.debug('Sending heartbeat to api at %s' % api_url) @@ -408,63 +409,68 @@ def execute(argv=None): args, configs = parseArguments() if configs is None: - return 103 # config file parsing error + return CONFIG_FILE_PARSE_ERROR setup_logging(args, __version__) - exclude = should_exclude(args.entity, args.include, args.exclude) - if exclude is not False: - log.debug(u('Skipping because matches exclude pattern: {pattern}').format( - pattern=u(exclude), - )) - return 0 + try: + exclude = should_exclude(args.entity, args.include, args.exclude) + if exclude is not False: + log.debug(u('Skipping because matches exclude pattern: {pattern}').format( + pattern=u(exclude), + )) + return SUCCESS - if args.entity_type != 'file' or os.path.isfile(args.entity): + if args.entity_type != 'file' or os.path.isfile(args.entity): - stats = get_file_stats(args.entity, entity_type=args.entity_type, - lineno=args.lineno, cursorpos=args.cursorpos) + stats = get_file_stats(args.entity, + entity_type=args.entity_type, + lineno=args.lineno, + cursorpos=args.cursorpos) - project = args.project or args.alternate_project - branch = None - if args.entity_type == 'file': - project, branch = get_project_info(configs, args) + project = args.project or args.alternate_project + branch = None + if args.entity_type == 'file': + project, branch = get_project_info(configs, args) - kwargs = vars(args) - kwargs['project'] = project - kwargs['branch'] = branch - kwargs['stats'] = stats - kwargs['hostname'] = args.hostname or socket.gethostname() - kwargs['timeout'] = args.timeout + kwargs = vars(args) + kwargs['project'] = project + kwargs['branch'] = branch + kwargs['stats'] = stats + kwargs['hostname'] = args.hostname or socket.gethostname() + 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 0 # success + 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 102 # api error + return API_ERROR - else: - log.debug('File does not exist; ignoring this heartbeat.') - return 0 + else: + log.debug('File does not exist; ignoring this heartbeat.') + return SUCCESS + except: + log.traceback() diff --git a/packages/wakatime/projects/git.py b/packages/wakatime/projects/git.py index 2e8b561..091f5c9 100644 --- a/packages/wakatime/projects/git.py +++ b/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.exception("Exception:") + log.traceback() except IOError: # pragma: nocover - log.exception("Exception:") + log.traceback() return None def _project_base(self): diff --git a/packages/wakatime/projects/mercurial.py b/packages/wakatime/projects/mercurial.py index 7cb3e5e..399edd5 100644 --- a/packages/wakatime/projects/mercurial.py +++ b/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.exception("Exception:") + log.traceback() except IOError: # pragma: nocover - log.exception("Exception:") + log.traceback() return u('default') def _find_hg_config_dir(self, path): diff --git a/packages/wakatime/projects/wakatime_project_file.py b/packages/wakatime/projects/wakatime_project_file.py index 2b9ada5..76fc7d2 100644 --- a/packages/wakatime/projects/wakatime_project_file.py +++ b/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.exception("Exception:") + log.traceback() except IOError: # pragma: nocover - log.exception("Exception:") + log.traceback() return True return False diff --git a/packages/wakatime/session_cache.py b/packages/wakatime/session_cache.py index ba519e9..8804748 100644 --- a/packages/wakatime/session_cache.py +++ b/packages/wakatime/session_cache.py @@ -14,7 +14,6 @@ import logging import os import pickle import sys -import traceback try: import sqlite3 @@ -58,7 +57,7 @@ class SessionCache(object): conn.commit() conn.close() except: # pragma: nocover - log.error(traceback.format_exc()) + log.traceback() def get(self): @@ -73,7 +72,7 @@ class SessionCache(object): try: conn, c = self.connect() except: - log.error(traceback.format_exc()) + log.traceback() return requests.session() session = None @@ -84,12 +83,12 @@ class SessionCache(object): if row is not None: session = pickle.loads(row[0]) except: # pragma: nocover - log.error(traceback.format_exc()) + log.traceback() try: conn.close() except: # pragma: nocover - log.error(traceback.format_exc()) + log.traceback() return session if session is not None else requests.session() @@ -106,4 +105,4 @@ class SessionCache(object): conn.commit() conn.close() except: - log.error(traceback.format_exc()) + log.traceback() diff --git a/packages/wakatime/stats.py b/packages/wakatime/stats.py index 07b9bb7..2606fa4 100644 --- a/packages/wakatime/stats.py +++ b/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.exception("Exception:") + log.traceback() return text