Compare commits

...

14 Commits

Author SHA1 Message Date
b7c047102d v2.0.12 2014-09-30 10:17:52 -07:00
d0bfd04602 upgrade external wakatime package to v2.1.1 2014-09-30 10:17:18 -07:00
101ab38c70 v2.0.11 2014-09-30 09:28:34 -07:00
8632c4ff08 upgrade wakatime package to v2.1.0 2014-09-30 09:27:35 -07:00
80556d0cbf update screenshot 2014-09-15 16:30:19 -07:00
253728545c v2.0.10 2014-08-29 12:50:55 -07:00
49d9b1d7dc upgrade external wakatime package to v2.0.8 2014-08-29 12:49:51 -07:00
8574abe012 v2.0.9 2014-08-27 03:32:35 -07:00
6b6f60d8e8 upgrade wakatime-cli to v2.0.7 to fix #25 2014-08-27 03:31:14 -07:00
986e592d1e v2.0.8 2014-08-07 08:30:44 -07:00
6ec3b171e1 v2.0.7 2014-07-25 02:36:42 -07:00
bcfb9862af upgrade wakatime package to v2.0.5 2014-07-25 02:32:01 -07:00
85cf9f4eb5 v2.0.6 2014-07-25 01:02:27 -07:00
d2a996e845 upgrade wakatime package to v2.0.4 to prevent logging namespace conflicts 2014-07-25 01:01:39 -07:00
17 changed files with 243 additions and 88 deletions

View File

@ -3,6 +3,55 @@ History
-------
2.0.12 (2014-09-30)
++++++++++++++++++
- upgrade external wakatime package to v2.1.1
- fix bug where binary file opened as utf-8
2.0.11 (2014-09-30)
++++++++++++++++++
- upgrade external wakatime package to v2.1.0
- python3 compatibility changes
2.0.10 (2014-08-29)
++++++++++++++++++
- upgrade external wakatime package to v2.0.8
- supress output from svn command
2.0.9 (2014-08-27)
++++++++++++++++++
- upgrade external wakatime package to v2.0.7
- fix support for subversion projects on Mac OS X
2.0.8 (2014-08-07)
++++++++++++++++++
- upgrade external wakatime package to v2.0.6
- fix unicode bug by encoding json POST data
2.0.7 (2014-07-25)
++++++++++++++++++
- upgrade external wakatime package to v2.0.5
- option in .wakatime.cfg to obfuscate file names
2.0.6 (2014-07-25)
++++++++++++++++++
- upgrade external wakatime package to v2.0.4
- use unique logger namespace to prevent collisions in shared plugin environments
2.0.5 (2014-06-18)
++++++++++++++++++

View File

@ -29,5 +29,5 @@ Heads Up! For Sublime Text 2 on Windows & Linux, WakaTime depends on [Python](ht
Screen Shots
------------
![Project Overview](https://wakatime.com/static/img/ScreenShots/Screen Shot 2013-10-26 at 5.04.01 PM.png)
![Project Overview](https://wakatime.com/static/img/ScreenShots/ScreenShot_2014-09-15.png)

View File

@ -6,7 +6,7 @@ License: BSD, see LICENSE for more details.
Website: https://wakatime.com/
==========================================================="""
__version__ = '2.0.5'
__version__ = '2.0.12'
import sublime
import sublime_plugin

View File

@ -36,3 +36,4 @@ nosetests.xml
virtualenv
venv
.DS_Store

View File

@ -3,6 +3,48 @@ History
-------
2.1.1 (2014-09-30)
++++++++++++++++++
- fix bug where binary file opened as utf-8
2.1.0 (2014-09-30)
++++++++++++++++++
- python3 compatibility changes
2.0.8 (2014-08-29)
++++++++++++++++++
- supress output from svn command
2.0.7 (2014-08-27)
++++++++++++++++++
- find svn binary location from common install directories
2.0.6 (2014-08-07)
++++++++++++++++++
- encode json data as str when passing to urllib
2.0.5 (2014-07-25)
++++++++++++++++++
- option in .wakatime.cfg to obfuscate file names
2.0.4 (2014-07-25)
++++++++++++++++++
- use unique logger namespace to prevent collisions in shared plugin environments
2.0.3 (2014-06-18)
++++++++++++++++++

View File

@ -13,7 +13,7 @@
from __future__ import print_function
__title__ = 'wakatime'
__version__ = '2.0.3'
__version__ = '2.1.1'
__author__ = 'Alan Hamlett'
__license__ = 'BSD'
__copyright__ = 'Copyright 2014 Alan Hamlett'
@ -40,6 +40,7 @@ except ImportError:
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages'))
from .compat import u, open, is_py2, is_py3
from .queue import Queue
from .log import setup_logging
from .project import find_project
@ -52,7 +53,7 @@ except:
from .packages import tzlocal3
log = logging.getLogger(__name__)
log = logging.getLogger('WakaTime')
class FileAction(argparse.Action):
@ -77,7 +78,7 @@ def upgradeConfigFile(configFile):
'ignore': [],
}
with open(oldConfig) as fh:
with open(oldConfig, 'r', encoding='utf-8') as fh:
for line in fh.readlines():
line = line.split('=', 1)
if len(line) == 2 and line[0].strip() and line[1].strip():
@ -86,7 +87,7 @@ def upgradeConfigFile(configFile):
else:
configs[line[0].strip()] = line[1].strip()
with open(configFile, 'w') as fh:
with open(configFile, 'w', encoding='utf-8') as fh:
fh.write("[settings]\n")
for name, value in configs.items():
if isinstance(value, list):
@ -114,7 +115,7 @@ def parseConfigFile(configFile):
configs = configparser.SafeConfigParser()
try:
with open(configFile) as fh:
with open(configFile, 'r', encoding='utf-8') as fh:
try:
configs.readfp(fh)
except configparser.Error:
@ -122,7 +123,7 @@ def parseConfigFile(configFile):
return None
except IOError:
if not os.path.isfile(configFile):
print('Error: Could not read from config file ~/.wakatime.conf')
print('Error: Could not read from config file ~/.wakatime.cfg')
return configs
@ -161,6 +162,9 @@ def parseArguments(argv):
parser.add_argument('--disableoffline', dest='offline',
action='store_false',
help='disables offline time logging instead of queuing logged time')
parser.add_argument('--hidefilenames', dest='hidefilenames',
action='store_true',
help='obfuscate file names; will not send file names to api')
parser.add_argument('--ignore', dest='ignore', action='append',
help='filename patterns to ignore; POSIX regex syntax; can be used more than once')
parser.add_argument('--logfile', dest='logfile',
@ -203,6 +207,8 @@ def parseArguments(argv):
pass
if args.offline and configs.has_option('settings', 'offline'):
args.offline = configs.getboolean('settings', 'offline')
if not args.hidefilenames and configs.has_option('settings', 'hidefilenames'):
args.hidefilenames = configs.getboolean('settings', 'hidefilenames')
if not args.verbose and configs.has_option('settings', 'verbose'):
args.verbose = configs.getboolean('settings', 'verbose')
if not args.verbose and configs.has_option('settings', 'debug'):
@ -221,7 +227,10 @@ def should_ignore(fileName, patterns):
if compiled.search(fileName):
return pattern
except re.error as ex:
log.warning('Regex error (%s) for ignore pattern: %s' % (str(ex), pattern))
log.warning(u('Regex error ({msg}) for ignore pattern: {pattern}').format(
msg=str(ex),
pattern=pattern,
))
except TypeError:
pass
return False
@ -230,21 +239,34 @@ def should_ignore(fileName, patterns):
def get_user_agent(plugin):
ver = sys.version_info
python_version = '%d.%d.%d.%s.%d' % (ver[0], ver[1], ver[2], ver[3], ver[4])
user_agent = 'wakatime/%s (%s) Python%s' % (__version__,
platform.platform(), python_version)
user_agent = u('wakatime/{ver} ({platform}) Python{py_ver}').format(
ver=__version__,
platform=platform.platform(),
py_ver=python_version,
)
if plugin:
user_agent = user_agent+' '+plugin
user_agent = u('{user_agent} {plugin}').format(
user_agent=user_agent,
plugin=plugin,
)
return user_agent
def send_action(project=None, branch=None, stats=None, key=None, targetFile=None,
timestamp=None, isWrite=None, plugin=None, offline=None, **kwargs):
timestamp=None, isWrite=None, plugin=None, offline=None,
hidefilenames=None, **kwargs):
url = 'https://wakatime.com/api/v1/actions'
log.debug('Sending action to api at %s' % url)
log.debug('Sending heartbeat to api at %s' % url)
data = {
'time': timestamp,
'file': targetFile,
}
if hidefilenames and targetFile is not None:
data['file'] = data['file'].rsplit('/', 1)[-1].rsplit('\\', 1)[-1]
if len(data['file'].strip('.').split('.', 1)) > 1:
data['file'] = u('HIDDEN.{ext}').format(ext=data['file'].strip('.').rsplit('.', 1)[-1])
else:
data['file'] = u('HIDDEN')
if stats.get('lines'):
data['lines'] = stats['lines']
if stats.get('language'):
@ -258,16 +280,17 @@ def send_action(project=None, branch=None, stats=None, key=None, targetFile=None
log.debug(data)
# setup api request
request = Request(url=url, data=str.encode(json.dumps(data)))
request_body = json.dumps(data)
request = Request(url=url, data=str.encode(request_body) if is_py3 else request_body)
request.add_header('User-Agent', get_user_agent(plugin))
request.add_header('Content-Type', 'application/json')
auth = 'Basic %s' % bytes.decode(base64.b64encode(str.encode(key)))
auth = u('Basic {key}').format(key=u(base64.b64encode(str.encode(key) if is_py3 else key)))
request.add_header('Authorization', auth)
# add Olson timezone to request
tz = tzlocal.get_localzone()
if tz:
request.add_header('TimeZone', str(tz.zone))
request.add_header('TimeZone', u(tz.zone))
# log time to api
response = None
@ -276,7 +299,7 @@ def send_action(project=None, branch=None, stats=None, key=None, targetFile=None
except HTTPError as exc:
exception_data = {
'response_code': exc.getcode(),
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
}
if log.isEnabledFor(logging.DEBUG):
exception_data['traceback'] = traceback.format_exc()
@ -289,7 +312,7 @@ def send_action(project=None, branch=None, stats=None, key=None, targetFile=None
log.error(exception_data)
except:
exception_data = {
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
}
if log.isEnabledFor(logging.DEBUG):
exception_data['traceback'] = traceback.format_exc()
@ -339,7 +362,9 @@ def main(argv=None):
ignore = should_ignore(args.targetFile, args.ignore)
if ignore is not False:
log.debug('File ignored because matches pattern: %s' % ignore)
log.debug(u('File ignored because matches pattern: {pattern}').format(
pattern=ignore,
))
return 0
if os.path.isfile(args.targetFile):
@ -364,9 +389,16 @@ def main(argv=None):
action = queue.pop()
if action is None:
break
if not send_action(project=action['project'], targetFile=action['file'], timestamp=action['time'],
branch=action['branch'], stats={'language': action['language'], 'lines': action['lines']},
key=args.key, isWrite=action['is_write'], plugin=action['plugin'], offline=args.offline):
sent = send_action(project=action['project'],
targetFile=action['file'],
timestamp=action['time'],
branch=action['branch'],
stats={'language': action['language'], 'lines': action['lines']},
key=args.key, isWrite=action['is_write'],
plugin=action['plugin'],
offline=args.offline,
hidefilenames=args.hidefilenames)
if not sent:
break
return 0 # success

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
wakatime.compat
~~~~~~~~~~~~~~~
For working with Python2 and Python3.
:copyright: (c) 2014 Alan Hamlett.
:license: BSD, see LICENSE for more details.
"""
import codecs
import io
import sys
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
if is_py2:
def u(text):
if isinstance(text, str):
return text.decode('utf-8')
return unicode(text)
open = codecs.open
basestring = basestring
elif is_py3:
def u(text):
if isinstance(text, bytes):
return text.decode('utf-8')
return str(text)
open = open
basestring = (str, bytes)

View File

@ -14,6 +14,7 @@ import os
import sys
from .packages import simplejson as json
from .compat import u
try:
from collections import OrderedDict
except ImportError:
@ -30,7 +31,7 @@ class CustomEncoder(json.JSONEncoder):
encoded = super(CustomEncoder, self).default(obj)
except UnicodeDecodeError:
encoding = sys.getfilesystemencoding()
obj = obj.decode(encoding, 'ignore').encode('utf-8')
obj = u(obj)
encoded = super(CustomEncoder, self).default(obj)
return encoded
@ -73,7 +74,7 @@ def set_log_level(logger, args):
def setup_logging(args, version):
logger = logging.getLogger()
logger = logging.getLogger('WakaTime')
set_log_level(logger, args)
if len(logger.handlers) > 0:
formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')

View File

@ -19,7 +19,7 @@ from .projects.subversion import Subversion
from .projects.wakatime import WakaTime
log = logging.getLogger(__name__)
log = logging.getLogger('WakaTime')
# List of plugin classes to find a project for the current file path.

View File

@ -13,7 +13,7 @@ import logging
import os
log = logging.getLogger(__name__)
log = logging.getLogger('WakaTime')
class BaseProject(object):

View File

@ -13,16 +13,10 @@ import logging
import os
from .base import BaseProject
from ..compat import u, open
log = logging.getLogger(__name__)
# str is unicode in Python3
try:
unicode
except NameError:
unicode = str
log = logging.getLogger('WakaTime')
class Git(BaseProject):
@ -34,7 +28,7 @@ class Git(BaseProject):
def name(self):
base = self._project_base()
if base:
return unicode(os.path.basename(base))
return u(os.path.basename(base))
return None
def branch(self):
@ -42,8 +36,8 @@ class Git(BaseProject):
if base:
head = os.path.join(self._project_base(), '.git', 'HEAD')
try:
with open(head) as fh:
return unicode(fh.readline().strip().rsplit('/', 1)[-1])
with open(head, 'r', encoding='utf-8') as fh:
return u(fh.readline().strip().rsplit('/', 1)[-1])
except IOError:
pass
return None

View File

@ -13,16 +13,10 @@ import logging
import os
from .base import BaseProject
from ..compat import u, open
log = logging.getLogger(__name__)
# str is unicode in Python3
try:
unicode
except NameError:
unicode = str
log = logging.getLogger('WakaTime')
class Mercurial(BaseProject):
@ -33,18 +27,18 @@ class Mercurial(BaseProject):
def name(self):
if self.configDir:
return unicode(os.path.basename(os.path.dirname(self.configDir)))
return u(os.path.basename(os.path.dirname(self.configDir)))
return None
def branch(self):
if self.configDir:
branch_file = os.path.join(self.configDir, 'branch')
try:
with open(branch_file) as fh:
return unicode(fh.readline().strip().rsplit('/', 1)[-1])
with open(branch_file, 'r', encoding='utf-8') as fh:
return u(fh.readline().strip().rsplit('/', 1)[-1])
except IOError:
pass
return unicode('default')
return u('default')
def _find_hg_config_dir(self, path):
path = os.path.realpath(path)

View File

@ -24,16 +24,10 @@ import logging
import os
from .base import BaseProject
from ..compat import u
log = logging.getLogger(__name__)
# str is unicode in Python3
try:
unicode
except NameError:
unicode = str
log = logging.getLogger('WakaTime')
class ProjectMap(BaseProject):
@ -68,5 +62,5 @@ class ProjectMap(BaseProject):
def name(self):
if self.project:
return unicode(self.project)
return u(self.project)
return None

View File

@ -15,42 +15,56 @@ import platform
from subprocess import Popen, PIPE
from .base import BaseProject
from ..compat import u, open
try:
from collections import OrderedDict
except ImportError:
from ..packages.ordereddict import OrderedDict
log = logging.getLogger(__name__)
# str is unicode in Python3
try:
unicode
except NameError:
unicode = str
log = logging.getLogger('WakaTime')
class Subversion(BaseProject):
binary_location = None
def process(self):
return self._find_project_base(self.path)
def name(self):
return unicode(self.info['Repository Root'].split('/')[-1])
return u(self.info['Repository Root'].split('/')[-1])
def branch(self):
if self.base:
unicode(os.path.basename(self.base))
u(os.path.basename(self.base))
return None
def _find_binary(self):
if self.binary_location:
return self.binary_location
locations = [
'svn',
'/usr/bin/svn',
'/usr/local/bin/svn',
]
for location in locations:
with open(os.devnull, 'wb') as DEVNULL:
try:
Popen([location, '--version'], stdout=DEVNULL, stderr=DEVNULL)
self.binary_location = location
return location
except:
pass
self.binary_location = 'svn'
return 'svn'
def _get_info(self, path):
info = OrderedDict()
stdout = None
try:
os.environ['LANG'] = 'en_US'
stdout, stderr = Popen([
'svn', 'info', os.path.realpath(path)
self._find_binary(), 'info', os.path.realpath(path)
], stdout=PIPE, stderr=PIPE).communicate()
except OSError:
pass

View File

@ -15,16 +15,10 @@ import logging
import os
from .base import BaseProject
from ..compat import u, open
log = logging.getLogger(__name__)
# str is unicode in Python3
try:
unicode
except NameError:
unicode = str
log = logging.getLogger('WakaTime')
class WakaTime(BaseProject):
@ -37,9 +31,9 @@ class WakaTime(BaseProject):
if self.config:
try:
with open(self.config) as fh:
self._project_name = unicode(fh.readline().strip())
self._project_branch = unicode(fh.readline().strip())
with open(self.config, 'r', encoding='utf-8') as fh:
self._project_name = u(fh.readline().strip())
self._project_branch = u(fh.readline().strip())
except IOError as e:
log.exception("Exception:")

View File

@ -22,7 +22,7 @@ except ImportError:
HAS_SQL = False
log = logging.getLogger(__name__)
log = logging.getLogger('WakaTime')
class Queue(object):

View File

@ -13,6 +13,8 @@ import logging
import os
import sys
from .compat import u, open
if sys.version_info[0] == 2:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages', 'pygments2'))
else:
@ -20,7 +22,7 @@ else:
from pygments.lexers import guess_lexer_for_filename
log = logging.getLogger(__name__)
log = logging.getLogger('WakaTime')
# force file name extensions to be recognized as a certain language
@ -49,12 +51,12 @@ def guess_language(file_name):
return language
lexer = None
try:
with open(file_name) as f:
lexer = guess_lexer_for_filename(file_name, f.read(512000))
with open(file_name, 'r', encoding='utf-8') as fh:
lexer = guess_lexer_for_filename(file_name, fh.read(512000))
except:
pass
if lexer:
return translate_language(str(lexer.name))
return translate_language(u(lexer.name))
else:
return None
@ -77,8 +79,8 @@ def translate_language(language):
def number_lines_in_file(file_name):
lines = 0
try:
with open(file_name) as f:
for line in f:
with open(file_name, 'r', encoding='utf-8') as fh:
for line in fh:
lines += 1
except IOError:
return None