Compare commits

..

25 Commits
8.4.0 ... 9.0.1

Author SHA1 Message Date
3504096082 v9.0.1 2019-11-24 07:49:27 -08:00
5990947706 changes for v9.0.1 2019-11-24 07:48:58 -08:00
2246e31244 upgrade wakatime-cli to v13.0.2 2019-11-24 07:46:13 -08:00
b55fe702d3 v9.0.0 2019-06-23 19:03:26 -07:00
e0fbbb50bb changes for v9.0.0 2019-06-23 19:03:08 -07:00
32c0cb5a97 upgrade wakatime-cli to v12.0.0 2019-06-23 18:56:57 -07:00
67d8b0d24f v8.7.0 2019-05-29 06:44:07 -07:00
b8b2f4944b changes for v8.7.0 2019-05-29 06:43:54 -07:00
a20161164c prevent creating user settings file when api key found from common config 2019-05-29 06:42:38 -07:00
405211bb07 v8.6.1 2019-05-28 20:54:57 -07:00
ffc879c4eb changes for v8.6.1 2019-05-28 20:54:43 -07:00
1e23919694 fix import path of parseConfigFile 2019-05-28 20:53:58 -07:00
b2086a3cd2 v8.6.0 2019-05-27 12:41:14 -07:00
005b07520c changes for v8.6.0 2019-05-27 12:40:55 -07:00
60608bd322 Skip prompting for api key when found from common config file 2019-05-27 12:39:48 -07:00
cde8f8f1de v8.5.0 2019-05-10 09:45:11 -07:00
4adfca154c changes for v8.5.0 2019-05-10 09:44:57 -07:00
f7b3924a30 use wakatime-cli to fetch today coding time 2019-05-10 09:43:30 -07:00
db00024455 remove clock icon from status bar 2019-05-10 09:18:38 -07:00
9a6be7ca4e v8.4.2 2019-05-07 18:44:24 -07:00
1ea9b2a761 changes for v8.4.2 2019-05-07 18:43:57 -07:00
bd5e87e030 upgrade wakatime-cli to v11.0.0 2019-05-07 18:42:31 -07:00
0256ff4a6a v8.4.1 2019-05-01 10:57:50 -07:00
9d170b3276 changes for v8.4.1 2019-05-01 10:57:38 -07:00
c54e575210 use api subdomain for fetching summaries 2019-05-01 10:57:11 -07:00
12 changed files with 442 additions and 105 deletions

View File

@ -3,6 +3,70 @@ History
-------
9.0.1 (2019-11-24)
++++++++++++++++++
- Upgrade wakatime-cli to v13.0.2.
- Filter dependencies longer than 200 characters.
- Close sqlite connection even when error raised.
`wakatime#196 <https://github.com/wakatime/wakatime/issues/196>`_
- Detect ColdFusion as root language instead of HTML.
- New arguments for reading and writing ini config file.
- Today argument shows categories when available.
- Prevent unnecessarily debug log when syncing offline heartbeats.
- Support for Python 3.7.
9.0.0 (2019-06-23)
++++++++++++++++++
- New optional config option hide_branch_names.
`wakatime#183 <https://github.com/wakatime/wakatime/issues/183>`_
8.7.0 (2019-05-29)
++++++++++++++++++
- Prevent creating user sublime-settings file when api key already exists in
common wakatime.cfg file.
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
8.6.1 (2019-05-28)
++++++++++++++++++
- Fix parsing common wakatime.cfg file.
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
8.6.0 (2019-05-27)
++++++++++++++++++
- Prevent prompting for api key when found from config file.
`#98 <https://github.com/wakatime/sublime-wakatime/issues/98>`_
8.5.0 (2019-05-10)
++++++++++++++++++
- Remove clock icon from status bar.
- Use wakatime-cli to fetch status bar coding time.
8.4.2 (2019-05-07)
++++++++++++++++++
- Upgrade wakatime-cli to v11.0.0.
- Rename argument --show-time-today to --today.
- New argument --show-time-today for printing Today's coding time.
8.4.1 (2019-05-01)
++++++++++++++++++
- Use api subdomain for fetching today's coding activity.
8.4.0 (2019-05-01)
++++++++++++++++++
@ -14,7 +78,7 @@ History
- Upgrade wakatime-cli to v10.8.4.
- Use wakatime fork of certifi package.
`sublime-wakatime#95 <https://github.com/wakatime/sublime-wakatime/issues/95>`_
`#95 <https://github.com/wakatime/sublime-wakatime/issues/95>`_
8.3.5 (2019-04-30)
@ -33,11 +97,11 @@ History
- Detect C++ language from all C++ file extensions.
`vscode-wakatime#87 <https://github.com/wakatime/vscode-wakatime/issues/87>`_
- Add ssl_certs_file arg and config for custom ca bundles.
`#164 <https://github.com/wakatime/wakatime/issues/164>`_
`wakatime#164 <https://github.com/wakatime/wakatime/issues/164>`_
- Fix bug causing random project names when hide project names enabled.
`vscode-wakatime#162 <https://github.com/wakatime/vscode-wakatime/issues/61>`_
- Add support for UNC network shares without drive letter mapped on Winows.
`#162 <https://github.com/wakatime/wakatime/issues/162>`_
`wakatime#162 <https://github.com/wakatime/wakatime/issues/162>`_
8.3.3 (2018-12-19)

View File

@ -8,7 +8,7 @@ Website: https://wakatime.com/
==========================================================="""
__version__ = '8.4.0'
__version__ = '9.0.1'
import sublime
@ -26,7 +26,6 @@ import threading
import traceback
import urllib
import webbrowser
from contextlib import closing
from subprocess import STDOUT, PIPE
from zipfile import ZipFile
try:
@ -41,11 +40,6 @@ try:
except ImportError:
import queue # py3
try:
import urllib2
except ImportError:
import urllib.request as urllib2
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
@ -53,8 +47,6 @@ is_win = platform.system() == 'Windows'
if is_py2:
STATUS_BAR_PREFIX = 'WakaTime'
def u(text):
if text is None:
return None
@ -78,8 +70,6 @@ if is_py2:
return unicode('')
elif is_py3:
STATUS_BAR_PREFIX = '🕘'
def u(text):
if text is None:
return None
@ -150,9 +140,45 @@ ERROR = 'ERROR'
# add wakatime package to path
sys.path.insert(0, os.path.join(PLUGIN_DIR, 'packages'))
try:
from wakatime.main import parseConfigFile
from wakatime.configs import parseConfigFile
except ImportError:
pass
def parseConfigFile():
return None
class ApiKey(object):
_key = None
def read(self):
if self._key:
return self._key
key = SETTINGS.get('api_key')
if key:
self._key = key
return self._key
try:
configs = parseConfigFile()
if configs:
if configs.has_option('settings', 'api_key'):
key = configs.get('settings', 'api_key')
if key:
self._key = key
return self._key
except:
pass
return self._key
def write(self, key):
global SETTINGS
self._key = key
SETTINGS.set('api_key', str(key))
sublime.save_settings(SETTINGS_FILE)
APIKEY = ApiKey()
def set_timeout(callback, seconds):
@ -214,7 +240,7 @@ def update_status_bar(status=None, debounced=False, msg=None):
set_timeout(lambda: update_status_bar(status, debounced=True), FETCH_TODAY_DEBOUNCE_SECONDS)
return
else:
msg = '{prefix}: {status}'.format(prefix=STATUS_BAR_PREFIX, status=status)
msg = 'WakaTime: {status}'.format(status=status)
if msg:
active_window = sublime.active_window()
@ -231,45 +257,63 @@ class FetchStatusBarCodingTime(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.api_key = SETTINGS.get('api_key', '')
self.debug = SETTINGS.get('debug')
self.api_key = APIKEY.read() or ''
self.proxy = SETTINGS.get('proxy')
self.python_binary = SETTINGS.get('python_binary')
def run(self):
if not self.api_key:
log(DEBUG, 'Missing WakaTime API key.')
return
url = 'https://wakatime.com/api/v1/users/current/summaries?start=today&end=today&api_key={}'.format(self.api_key)
python = self.python_binary
if not python or not python.strip():
python = python_binary()
if not python:
log(DEBUG, 'Missing Python.')
return
ua = 'sublime/%d sublime-wakatime/%s' % (ST_VERSION, __version__)
cmd = [
python,
API_CLIENT,
'--today',
'--key', str(bytes.decode(self.api_key.encode('utf8'))),
'--plugin', ua,
]
if self.debug:
cmd.append('--verbose')
if self.proxy:
cmd.extend(['--proxy', self.proxy])
log(DEBUG, ' '.join(obfuscate_apikey(cmd)))
try:
with closing(urllib2.urlopen(url)) as response:
grand_total_text = json.loads(u(response.read()))['data'][0]['grand_total']['text']
msg = '{} Today: {}'.format(STATUS_BAR_PREFIX, grand_total_text)
process = Popen(cmd, stdout=PIPE, stderr=STDOUT)
output, err = process.communicate()
output = u(output)
retcode = process.poll()
if not retcode and output:
msg = 'Today: {output}'.format(output=output)
update_status_bar(msg=msg)
else:
log(DEBUG, 'wakatime-core today exited with status: {0}'.format(retcode))
if output:
log(DEBUG, u('wakatime-core today output: {0}').format(output))
except:
pass
def prompt_api_key():
global SETTINGS
if SETTINGS.get('api_key'):
if APIKEY.read():
return True
default_key = ''
try:
configs = parseConfigFile()
if configs is not None:
if configs.has_option('settings', 'api_key'):
default_key = configs.get('settings', 'api_key')
except:
pass
window = sublime.active_window()
if window:
def got_key(text):
if text:
SETTINGS.set('api_key', str(text))
sublime.save_settings(SETTINGS_FILE)
window.show_input_panel('[WakaTime] Enter your wakatime.com api key:', default_key, got_key, None, None)
APIKEY.write(text)
window.show_input_panel('[WakaTime] Enter your wakatime.com api key:', '', got_key, None, None)
return True
else:
log(ERROR, 'Could not prompt for api key because no window found.')
@ -544,7 +588,7 @@ class SendHeartbeatsThread(threading.Thread):
threading.Thread.__init__(self)
self.debug = SETTINGS.get('debug')
self.api_key = SETTINGS.get('api_key', '')
self.api_key = APIKEY.read() or ''
self.ignore = SETTINGS.get('ignore', [])
self.include = SETTINGS.get('include', [])
self.hidefilenames = SETTINGS.get('hidefilenames')

View File

@ -1,7 +1,7 @@
__title__ = 'wakatime'
__description__ = 'Common interface to the WakaTime api.'
__url__ = 'https://github.com/wakatime/wakatime'
__version_info__ = ('10', '8', '4')
__version_info__ = ('13', '0', '2')
__version__ = '.'.join(__version_info__)
__author__ = 'Alan Hamlett'
__author_email__ = 'alan@wakatime.com'

View File

@ -163,6 +163,123 @@ def send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=False):
return AUTH_ERROR if code == 401 else API_ERROR
def get_time_today(args, use_ntlm_proxy=False):
"""Get coding time from WakaTime API for given time range.
Returns total time as string or `None` when unable to fetch summary from
the API. When verbose output enabled, returns error message when unable to
fetch summary.
"""
url = 'https://api.wakatime.com/api/v1/users/current/summaries'
timeout = args.timeout
if not timeout:
timeout = 60
api_key = u(base64.b64encode(str.encode(args.key) if is_py3 else args.key))
auth = u('Basic {api_key}').format(api_key=api_key)
headers = {
'User-Agent': get_user_agent(args.plugin),
'Accept': 'application/json',
'Authorization': auth,
}
session_cache = SessionCache()
session = session_cache.get()
should_try_ntlm = False
proxies = {}
if args.proxy:
if use_ntlm_proxy:
from .packages.requests_ntlm import HttpNtlmAuth
username = args.proxy.rsplit(':', 1)
password = ''
if len(username) == 2:
password = username[1]
username = username[0]
session.auth = HttpNtlmAuth(username, password, session)
else:
should_try_ntlm = '\\' in args.proxy
proxies['https'] = args.proxy
ssl_verify = not args.nosslverify
if args.ssl_certs_file and ssl_verify:
ssl_verify = args.ssl_certs_file
params = {
'start': 'today',
'end': 'today',
}
# send request to api
response, code = None, None
try:
response = session.get(url, params=params, headers=headers,
proxies=proxies, timeout=timeout,
verify=ssl_verify)
except RequestException:
if should_try_ntlm:
return get_time_today(args, use_ntlm_proxy=True)
session_cache.delete()
if log.isEnabledFor(logging.DEBUG):
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
'traceback': traceback.format_exc(),
}
log.error(exception_data)
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
return None, API_ERROR
except: # delete cached session when requests raises unknown exception
if should_try_ntlm:
return get_time_today(args, use_ntlm_proxy=True)
session_cache.delete()
if log.isEnabledFor(logging.DEBUG):
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
'traceback': traceback.format_exc(),
}
log.error(exception_data)
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
return None, API_ERROR
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.ok:
try:
summary = response.json()['data'][0]
if len(summary['categories']) > 1:
text = ', '.join(['{0} {1}'.format(x['text'], x['name'].lower()) for x in summary['categories']])
else:
text = summary['grand_total']['text']
session_cache.save(session)
return text, SUCCESS
except:
if log.isEnabledFor(logging.DEBUG):
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
'traceback': traceback.format_exc(),
}
log.error(exception_data)
return '{0}: {1}'.format(sys.exc_info()[0].__name__, u(sys.exc_info()[1])), API_ERROR
return None, API_ERROR
else:
if should_try_ntlm:
return get_time_today(args, use_ntlm_proxy=True)
session_cache.delete()
log.debug({
'response_code': code,
'response_text': content,
})
if log.isEnabledFor(logging.DEBUG):
return 'Error: {0}'.format(code), API_ERROR
return None, API_ERROR
def _process_server_results(heartbeats, code, content, results, args, configs):
log.debug({
'response_code': code,

View File

@ -19,8 +19,8 @@ import time
import traceback
from .__about__ import __version__
from .compat import basestring
from .configs import parseConfigFile
from .constants import AUTH_ERROR, DEFAULT_SYNC_OFFLINE_ACTIVITY
from .configs import getConfigFile, parseConfigFile
from .constants import AUTH_ERROR, DEFAULT_SYNC_OFFLINE_ACTIVITY, SUCCESS
from .packages import argparse
@ -62,6 +62,7 @@ def parse_arguments():
parser.add_argument('--file', dest='file', action=FileAction,
help=argparse.SUPPRESS)
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
metavar='API_KEY',
help='Your wakatime api key; uses api_key from ' +
'~/.wakatime.cfg by default.')
parser.add_argument('--write', dest='is_write', action='store_true',
@ -75,10 +76,11 @@ def parse_arguments():
help='Optional floating-point unix epoch timestamp. ' +
'Uses current time by default.')
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
metavar='INT',
help='Optional line number. This is the current ' +
'line being edited.')
parser.add_argument('--cursorpos', dest='cursorpos',
action=StoreWithoutQuotes,
metavar='INT', action=StoreWithoutQuotes,
help='Optional cursor position in the current file.')
parser.add_argument('--entity-type', dest='entity_type',
action=StoreWithoutQuotes,
@ -104,13 +106,13 @@ def parse_arguments():
'requests. By default, SSL certificates are ' +
'verified.')
parser.add_argument('--ssl-certs-file', dest='ssl_certs_file',
action=StoreWithoutQuotes,
metavar='FILE', action=StoreWithoutQuotes,
help='Override the bundled Python Requests CA certs ' +
'file. By default, uses certifi for ca certs.')
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
help='Optional project name.')
parser.add_argument('--alternate-project', dest='alternate_project',
action=StoreWithoutQuotes,
metavar='PROJECT', action=StoreWithoutQuotes,
help='Optional alternate project name. ' +
'Auto-discovered project takes priority.')
parser.add_argument('--alternate-language', dest='alternate_language',
@ -153,7 +155,12 @@ def parse_arguments():
'folder name as the project, a ' +
'.wakatime-project file is created with a ' +
'random project name.')
parser.add_argument('--hide-branch-names', dest='hide_branch_names',
action='store_true',
help='Obfuscate branch names. Will not send revision ' +
'control branch names to api.')
parser.add_argument('--exclude', dest='exclude', action='append',
metavar='PATH',
help='Filename patterns to exclude from logging. ' +
'POSIX regex syntax. Can be used more than once.')
parser.add_argument('--exclude-unknown-project',
@ -161,6 +168,7 @@ def parse_arguments():
help='When set, any activity where the project ' +
'cannot be detected will be ignored.')
parser.add_argument('--include', dest='include', action='append',
metavar='PATH',
help='Filename patterns to log. When used in ' +
'combination with --exclude, files matching ' +
'include will still be logged. POSIX regex ' +
@ -177,20 +185,21 @@ def parse_arguments():
help='Reads extra heartbeats from STDIN as a JSON ' +
'array until EOF.')
parser.add_argument('--log-file', dest='log_file',
action=StoreWithoutQuotes,
metavar='FILE', action=StoreWithoutQuotes,
help='Defaults to ~/.wakatime.log.')
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
help=argparse.SUPPRESS)
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
metavar='URL',
help='Heartbeats api url. For debugging with a ' +
'local server.')
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
help=argparse.SUPPRESS)
parser.add_argument('--timeout', dest='timeout', type=int,
action=StoreWithoutQuotes,
metavar='SECONDS', action=StoreWithoutQuotes,
help='Number of seconds to wait when sending ' +
'heartbeats to api. Defaults to 60 seconds.')
parser.add_argument('--sync-offline-activity',
parser.add_argument('--sync-offline-activity', metavar='AMOUNT',
dest='sync_offline_activity',
action=StoreWithoutQuotes,
help='Amount of offline activity to sync from your ' +
@ -201,8 +210,24 @@ def parse_arguments():
'online 5 offline heartbeats are synced. Can ' +
'be used without --entity to only sync offline ' +
'activity without generating new heartbeats.')
parser.add_argument('--today', dest='today',
action='store_true',
help='Prints dashboard time for Today, then exits.')
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
help='Defaults to ~/.wakatime.cfg.')
metavar='FILE', help='Defaults to ~/.wakatime.cfg.')
parser.add_argument('--config-section', dest='config_section',
metavar='SECTION', action=StoreWithoutQuotes,
help='Optional config section when reading or ' +
'writing a config key. Defaults to [settings].')
parser.add_argument('--config-read', dest='config_read',
metavar='KEY', action=StoreWithoutQuotes,
help='Prints value for the given config key, then ' +
'exits.')
parser.add_argument('--config-write', dest='config_write',
nargs=2, metavar=('KEY', 'VALUE'),
action=StoreWithoutQuotes,
help='Writes value to a config key, then exits. ' +
'Expects two arguments, key and value.')
parser.add_argument('--verbose', dest='verbose', action='store_true',
help='Turns on debug messages in log file.')
parser.add_argument('--version', action='version', version=__version__)
@ -210,13 +235,30 @@ def parse_arguments():
# parse command line arguments
args = parser.parse_args()
# parse ~/.wakatime.cfg file
configs = parseConfigFile(args.config)
if args.config_read:
section = args.config_section or 'settings'
key = args.config_read
print(configs.get(section, key))
raise SystemExit(SUCCESS)
if args.config_write:
section = args.config_section or 'settings'
key = args.config_write[0]
val = args.config_write[1]
if not configs.has_section(section):
configs.add_section(section)
configs.set(section, key, val)
with open(args.config or getConfigFile(), 'w', encoding='utf-8') as fh:
configs.write(fh)
raise SystemExit(SUCCESS)
# use current unix epoch timestamp by default
if not args.timestamp:
args.timestamp = time.time()
# parse ~/.wakatime.cfg file
configs = parseConfigFile(args.config)
# update args from configs
if not args.hostname:
if configs.has_option('settings', 'hostname'):
@ -245,7 +287,7 @@ def parse_arguments():
if not args.entity:
if args.file:
args.entity = args.file
elif not args.sync_offline_activity or args.sync_offline_activity == 'none':
elif (not args.sync_offline_activity or args.sync_offline_activity == 'none') and not args.today:
parser.error('argument --entity is required')
if not args.sync_offline_activity:
@ -291,8 +333,9 @@ def parse_arguments():
pass
if not args.exclude_unknown_project and configs.has_option('settings', 'exclude_unknown_project'):
args.exclude_unknown_project = configs.getboolean('settings', 'exclude_unknown_project')
boolean_or_list('hide_file_names', args, configs, alternative_names=['hide_filenames', 'hidefilenames'])
boolean_or_list('hide_project_names', args, configs, alternative_names=['hide_projectnames', 'hideprojectnames'])
_boolean_or_list('hide_file_names', args, configs, alternative_names=['hide_filenames', 'hidefilenames'])
_boolean_or_list('hide_project_names', args, configs, alternative_names=['hide_projectnames', 'hideprojectnames'])
_boolean_or_list('hide_branch_names', args, configs, alternative_names=['hide_branchnames', 'hidebranchnames'], default=None)
if args.offline_deprecated:
args.offline = False
if args.offline and configs.has_option('settings', 'offline'):
@ -337,7 +380,7 @@ def parse_arguments():
return args, configs
def boolean_or_list(config_name, args, configs, alternative_names=[]):
def _boolean_or_list(config_name, args, configs, alternative_names=[], default=[]):
"""Get a boolean or list of regexes from args and configs."""
# when argument flag present, set to wildcard regex
@ -346,7 +389,7 @@ def boolean_or_list(config_name, args, configs, alternative_names=[]):
setattr(args, config_name, ['.*'])
return
setattr(args, config_name, [])
setattr(args, config_name, default)
option = None
alternative_names.insert(0, config_name)
@ -358,7 +401,11 @@ def boolean_or_list(config_name, args, configs, alternative_names=[]):
if option is not None:
if option.strip().lower() == 'true':
setattr(args, config_name, ['.*'])
elif option.strip().lower() != 'false':
elif option.strip().lower() == 'false':
setattr(args, config_name, [])
else:
for pattern in option.split("\n"):
if pattern.strip() != '':
if not getattr(args, config_name):
setattr(args, config_name, [])
getattr(args, config_name).append(pattern)

View File

@ -131,5 +131,9 @@ class DependencyParser(object):
if self.parser:
plugin = self.parser(self.source_file, lexer=self.lexer)
dependencies = plugin.parse()
return list(filter(bool, set(dependencies)))
def filter_dependencies(dep):
return dep and len(dep) <= 200
return list(filter(filter_dependencies, set(dependencies)))
return []

View File

@ -43,7 +43,15 @@ class Heartbeat(object):
cursorpos = None
user_agent = None
_sensitive = ('dependencies', 'lines', 'lineno', 'cursorpos', 'branch')
_sensitive_when_hiding_filename = (
'dependencies',
'lines',
'lineno',
'cursorpos',
)
_sensitive_when_hiding_branch = (
'branch',
)
def __init__(self, data, args, configs, _clone=None):
if not data:
@ -141,21 +149,24 @@ class Heartbeat(object):
Returns a Heartbeat.
"""
if not self.args.hide_file_names:
return self
if self.entity is None:
return self
if self.type != 'file':
return self
if self.should_obfuscate_filename():
self._sanitize_metadata()
if self._should_obfuscate_filename():
self._sanitize_metadata(keys=self._sensitive_when_hiding_filename)
if self._should_obfuscate_branch(default=True):
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
extension = u(os.path.splitext(self.entity)[1])
self.entity = u('HIDDEN{0}').format(extension)
elif self.should_obfuscate_project():
self._sanitize_metadata()
self._sanitize_metadata(keys=self._sensitive_when_hiding_filename)
if self._should_obfuscate_branch(default=True):
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
elif self._should_obfuscate_branch():
self._sanitize_metadata(keys=self._sensitive_when_hiding_branch)
return self
@ -193,22 +204,6 @@ class Heartbeat(object):
is_write=self.is_write,
)
def should_obfuscate_filename(self):
"""Returns True if hide_file_names is true or the entity file path
matches one in the list of obfuscated file paths."""
for pattern in self.args.hide_file_names:
try:
compiled = re.compile(pattern, re.IGNORECASE)
if compiled.search(self.entity):
return True
except re.error as ex:
log.warning(u('Regex error ({msg}) for hide_file_names pattern: {pattern}').format(
msg=u(ex),
pattern=u(pattern),
))
return False
def should_obfuscate_project(self):
"""Returns True if hide_project_names is true or the entity file path
matches one in the list of obfuscated project paths."""
@ -223,6 +218,49 @@ class Heartbeat(object):
msg=u(ex),
pattern=u(pattern),
))
return False
def _should_obfuscate_filename(self):
"""Returns True if hide_file_names is true or the entity file path
matches one in the list of obfuscated file paths."""
for pattern in self.args.hide_file_names:
try:
compiled = re.compile(pattern, re.IGNORECASE)
if compiled.search(self.entity):
return True
except re.error as ex:
log.warning(u('Regex error ({msg}) for hide_file_names pattern: {pattern}').format(
msg=u(ex),
pattern=u(pattern),
))
return False
def _should_obfuscate_branch(self, default=False):
"""Returns True if hide_file_names is true or the entity file path
matches one in the list of obfuscated file paths."""
# when project names or file names are hidden and hide_branch_names is
# not set, we default to hiding branch names along with file/project.
if default and self.args.hide_branch_names is None:
return True
if not self.branch or not self.args.hide_branch_names:
return False
for pattern in self.args.hide_branch_names:
try:
compiled = re.compile(pattern, re.IGNORECASE)
if compiled.search(self.entity) or compiled.search(self.branch):
return True
except re.error as ex:
log.warning(u('Regex error ({msg}) for hide_branch_names pattern: {pattern}').format(
msg=u(ex),
pattern=u(pattern),
))
return False
def _unicode(self, value):
@ -333,8 +371,8 @@ class Heartbeat(object):
return False
return find_project_file(self.entity) is None
def _sanitize_metadata(self):
for key in self._sensitive:
def _sanitize_metadata(self, keys=[]):
for key in keys:
setattr(self, key, None)
def __repr__(self):

View File

@ -22,7 +22,7 @@ sys.path.insert(0, os.path.dirname(pwd))
sys.path.insert(0, os.path.join(pwd, 'packages'))
from .__about__ import __version__
from .api import send_heartbeats
from .api import send_heartbeats, get_time_today
from .arguments import parse_arguments
from .compat import u, json
from .constants import SUCCESS, UNKNOWN_ERROR, HEARTBEATS_PER_REQUEST
@ -38,17 +38,26 @@ def execute(argv=None):
if argv:
sys.argv = ['wakatime'] + argv
args, configs = parse_arguments()
try:
args, configs = parse_arguments()
except SystemExit as ex:
return ex.code
setup_logging(args, __version__)
if args.today:
text, retval = get_time_today(args)
if text:
print(text)
return retval
try:
heartbeats = []
hb = Heartbeat(vars(args), args, configs)
if hb:
heartbeats.append(hb)
else:
elif args.entity:
log.debug(hb.skip)
if args.extra_heartbeats:

View File

@ -60,9 +60,12 @@ class Queue(object):
}
c.execute('INSERT INTO {0} VALUES (:id,:heartbeat)'.format(self.table_name), data)
conn.commit()
conn.close()
except sqlite3.Error:
log.traceback()
try:
conn.close()
except: # pragma: nocover
pass
def pop(self):
if not HAS_SQL:

View File

@ -69,8 +69,6 @@ def get_project_info(configs, heartbeat, data):
project_name = data.get('project') or heartbeat.args.project
hide_project = heartbeat.should_obfuscate_project()
if hide_project and project_name is not None:
return project_name, None
if project_name is None or branch_name is None:
@ -78,24 +76,25 @@ def get_project_info(configs, heartbeat, data):
plugin_name = plugin_cls.__name__.lower()
plugin_configs = get_configs_for_plugin(plugin_name, configs)
project = plugin_cls(heartbeat.entity, configs=plugin_configs)
if project.process():
project_name = project_name or project.name()
if not hide_project:
project_name = project_name or project.name()
branch_name = branch_name or project.branch()
if hide_project:
branch_name = None
project_name = generate_project_name()
project_file = os.path.join(project.folder(), '.wakatime-project')
try:
with open(project_file, 'w') as fh:
fh.write(project_name)
except IOError:
project_name = None
break
if project_name is None and not hide_project:
project_name = data.get('alternate_project') or heartbeat.args.alternate_project
if project_name is None:
if not hide_project:
project_name = data.get('alternate_project') or heartbeat.args.alternate_project
else:
project_name = generate_project_name()
project_file = os.path.join(project.folder(), '.wakatime-project')
try:
with open(project_file, 'w') as fh:
fh.write(project_name)
except IOError:
project_name = None
return project_name, branch_name

View File

@ -55,9 +55,12 @@ class SessionCache(object):
}
c.execute('INSERT INTO {0} VALUES (:value)'.format(self.table_name), values)
conn.commit()
conn.close()
except: # pragma: nocover
log.traceback(logging.DEBUG)
try:
conn.close()
except: # pragma: nocover
pass
def get(self):
"""Returns a requests.Session object.
@ -87,7 +90,7 @@ class SessionCache(object):
try:
conn.close()
except: # pragma: nocover
log.traceback(logging.DEBUG)
pass
return session if session is not None else requests.session()
@ -101,9 +104,12 @@ class SessionCache(object):
conn, c = self.connect()
c.execute('DELETE FROM {0}'.format(self.table_name))
conn.commit()
conn.close()
except:
log.traceback(logging.DEBUG)
try:
conn.close()
except: # pragma: nocover
pass
def _get_db_file(self):
home = '~'

View File

@ -259,6 +259,12 @@ def get_lexer(language):
def use_root_language(language, lexer):
override = {
'Coldfusion HTML': 'ColdFusion',
}
if language in override:
return override[language]
if lexer and hasattr(lexer, 'root_lexer'):
return u(lexer.root_lexer.name)