Compare commits

...

16 Commits

Author SHA1 Message Date
a290e5d86d v7.0.23 2017-09-14 17:54:06 -07:00
d5b922bb10 changes for 7.0.23 2017-09-14 17:53:51 -07:00
ec7b5e3530 add missing setting description 2017-09-14 17:51:22 -07:00
aa3f2e8af6 Merge pull request #76 from krishnaglick/master
Adding "Include" Functionality
2017-09-14 17:46:55 -07:00
f4e53cd682 Added 'include' functionality 2017-09-14 13:54:50 -04:00
aba72b0f1e v7.0.22 2017-06-08 00:21:33 -07:00
5b9d86a57d changes for v7.0.22 2017-06-08 00:21:15 -07:00
fa40874635 upgrade wakatime-cli to v8.0.3 2017-06-08 00:20:10 -07:00
6d4a4cf9eb link to FAQ from troubleshooting section 2017-05-26 07:40:14 -07:00
f628b8dd11 v7.0.21 2017-05-24 23:55:36 -07:00
f932ee9fc6 changes for v7.0.21 2017-05-24 23:55:17 -07:00
2f14009279 Upgrade wakatime-cli to v8.0.2 2017-05-24 23:53:28 -07:00
453d96bf9c use unicode arrow 2017-05-02 19:24:54 -07:00
9de153f156 use unicode arrow 2017-04-15 10:13:42 -07:00
dcc782338d v7.0.20 2017-04-10 19:04:00 -07:00
9d0dba988a remove sub-list install instructions 2017-04-10 19:03:05 -07:00
13 changed files with 244 additions and 78 deletions

View File

@ -13,3 +13,4 @@ Patches and Suggestions
- Jimmy Selgen Nielsen <jimmy.selgen@gmail.com>
- Patrik Kernstock <info@pkern.at>
- Krishna Glick <krishnaglick@gmail.com>

View File

@ -3,6 +3,37 @@ History
-------
7.0.23 (2017-09-14)
++++++++++++++++++
- Add "include" setting to bypass ignored files. #89
7.0.22 (2017-06-08)
++++++++++++++++++
- Upgrade wakatime-cli to v8.0.3.
- Improve Matlab language detection.
7.0.21 (2017-05-24)
++++++++++++++++++
- Upgrade wakatime-cli to v8.0.2.
- Only treat proxy string as NTLM proxy after unable to connect with HTTPS and
SOCKS proxy.
- Support running automated tests on Linux, OS X, and Windows.
- Ability to disable SSL cert verification. wakatime/wakatime#90
- Disable line count stats for files larger than 2MB to improve performance.
- Print error saying Python needs upgrading when requests can't be imported.
7.0.20 (2017-04-10)
++++++++++++++++++
- Fix install instructions formatting.
7.0.19 (2017-04-10)
++++++++++++++++++

View File

@ -9,17 +9,15 @@ Installation
1. Install [Package Control](https://packagecontrol.io/installation).
2. Using [Package Control](https://packagecontrol.io/docs/usage):
2. In Sublime, press `ctrl+shift+p`(Windows, Linux) or `cmd+shift+p`(OS X).
a) Inside Sublime, press `ctrl+shift+p`(Windows, Linux) or `cmd+shift+p`(OS X).
3. Type `install`, then press `enter` with `Package Control: Install Package` selected.
b) Type `install`, then press `enter` with `Package Control: Install Package` selected.
4. Type `wakatime`, then press `enter` with the `WakaTime` plugin selected.
c) Type `wakatime`, then press `enter` with the `WakaTime` plugin selected.
5. Enter your [api key](https://wakatime.com/settings#apikey), then press `enter`.
3. Enter your [api key](https://wakatime.com/settings#apikey), then press `enter`.
4. Use Sublime and your coding activity will be displayed on your [WakaTime dashboard](https://wakatime.com).
6. Use Sublime and your coding activity will be displayed on your [WakaTime dashboard](https://wakatime.com).
Screen Shots
@ -35,7 +33,7 @@ In Sublime Text 2, if you get a warning message:
A plugin (WakaTime) may be making Sublime Text unresponsive by taking too long (0.017332s) in its on_modified callback.
To fix this, go to `Preferences > Settings - User` then add the following setting:
To fix this, go to `Preferences Settings - User` then add the following setting:
`"detect_slow_plugins": false`
@ -49,7 +47,13 @@ First, turn on debug mode in your `WakaTime.sublime-settings` file.
Add the line: `"debug": true`
Then, open your Sublime Console with `View -> Show Console` ( CTRL + \` ) to see the plugin executing the wakatime cli process when sending a heartbeat.
Then, open your Sublime Console with `View Show Console` ( CTRL + \` ) to see the plugin executing the wakatime cli process when sending a heartbeat.
Also, tail your `$HOME/.wakatime.log` file to debug wakatime cli problems.
For more general troubleshooting information, see [wakatime/wakatime#troubleshooting](https://github.com/wakatime/wakatime#troubleshooting).
The [How to Debug Plugins][how to debug] guide shows how to check when coding activity was last received from your editor using the [User Agents API][user agents api].
For more general troubleshooting info, see the [wakatime-cli Troubleshooting Section][wakatime-cli-help].
[wakatime-cli-help]: https://github.com/wakatime/wakatime#troubleshooting
[how to debug]: https://wakatime.com/faq#debug-plugins
[user agents api]: https://wakatime.com/developers#user_agents

View File

@ -7,7 +7,7 @@ Website: https://wakatime.com/
==========================================================="""
__version__ = '7.0.19'
__version__ = '7.0.23'
import sublime
@ -462,6 +462,7 @@ class SendHeartbeatsThread(threading.Thread):
self.debug = SETTINGS.get('debug')
self.api_key = SETTINGS.get('api_key', '')
self.ignore = SETTINGS.get('ignore', [])
self.include = SETTINGS.get('include', [])
self.hidefilenames = SETTINGS.get('hidefilenames')
self.proxy = SETTINGS.get('proxy')
@ -520,6 +521,8 @@ class SendHeartbeatsThread(threading.Thread):
cmd.extend(['--cursorpos', heartbeat['cursorpos']])
for pattern in self.ignore:
cmd.extend(['--exclude', pattern])
for pattern in self.include:
cmd.extend(['--include', pattern])
if self.debug:
cmd.append('--verbose')
if self.hidefilenames:

View File

@ -17,6 +17,10 @@
// POSIX regular expressions will not be logged.
"ignore": ["^/tmp/", "^/etc/", "^/var/(?!www/).*", "COMMIT_EDITMSG$", "PULLREQ_EDITMSG$", "MERGE_MSG$", "TAG_EDITMSG$"],
// Include files; Files (including absolute paths) that match one of these
// POSIX regular expressions will bypass your ignore setting.
"include": [".*"],
// Status bar message. Set to false to hide status bar message.
// Defaults to true.
"status_bar_message": true,

View File

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

View File

@ -76,6 +76,10 @@ def parseArguments():
'https://user:pass@host:port or '+
'socks5://user:pass@host:port or ' +
'domain\\user:pass')
parser.add_argument('--no-ssl-verify', dest='nosslverify',
action='store_true',
help='disables SSL certificate verification for HTTPS '+
'requests. By default, SSL certificates are verified.')
parser.add_argument('--project', dest='project',
help='optional project name')
parser.add_argument('--alternate-project', dest='alternate_project',
@ -214,6 +218,8 @@ def parseArguments():
'https://user:pass@host:port or ' +
'socks5://user:pass@host:port or ' +
'domain\\user:pass.')
if configs.has_option('settings', 'no_ssl_verify'):
args.nosslverify = configs.getboolean('settings', 'no_ssl_verify')
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'):

View File

@ -25,6 +25,22 @@ except ImportError:
from .packages import configparser
def getConfigFile():
"""Returns the config file location.
If $WAKATIME_HOME env varialbe is defined, returns
$WAKATIME_HOME/.wakatime.cfg, otherwise ~/.wakatime.cfg.
"""
fileName = '.wakatime.cfg'
home = os.environ.get('WAKATIME_HOME')
if home:
return os.path.join(os.path.expanduser(home), fileName)
return os.path.join(os.path.expanduser('~'), fileName)
def parseConfigFile(configFile=None):
"""Returns a configparser.SafeConfigParser instance with configs
read from the config file. Default location of the config file is
@ -32,13 +48,8 @@ def parseConfigFile(configFile=None):
"""
# get config file location from ENV
home = os.environ.get('WAKATIME_HOME')
if not configFile and home:
configFile = os.path.join(os.path.expanduser(home), '.wakatime.cfg')
# use default config file location
if not configFile:
configFile = os.path.join(os.path.expanduser('~'), '.wakatime.cfg')
configFile = getConfigFile()
configs = configparser.ConfigParser(delimiters=('='), strict=False)
try:

View File

@ -38,3 +38,15 @@ UNKNOWN_ERROR = 105
Exit code used when the JSON input from `--extra-heartbeats` is malformed.
"""
MALFORMED_HEARTBEAT_ERROR = 106
""" Connection Error
Exit code used when there was proxy or other problem connecting to the WakaTime
API servers.
"""
CONNECTION_ERROR = 107
""" Max file size supporting line number count stats.
Files larger than this in bytes will not have a line count stat for performance.
Default is 2MB.
"""
MAX_FILE_SIZE_SUPPORTED = 2000000

View File

@ -11,8 +11,8 @@
LANGUAGES = {
'typescript': 0.01,
'f#': 0.01,
'perl': 0.01,
'perl6': 0.01,
'f#': 0.01,
'typescript': 0.01,
}

View File

@ -34,8 +34,19 @@ from .constants import (
MALFORMED_HEARTBEAT_ERROR,
)
from .logger import setup_logging
log = logging.getLogger('WakaTime')
try:
from .packages import requests
except ImportError:
log.traceback(logging.ERROR)
print(traceback.format_exc())
log.error('Please upgrade Python to the latest version.')
print('Please upgrade Python to the latest version.')
sys.exit(UNKNOWN_ERROR)
from .offlinequeue import Queue
from .packages import requests
from .packages.requests.exceptions import RequestException
from .project import get_project_info
from .session_cache import SessionCache
@ -48,13 +59,11 @@ except (ImportError, SyntaxError): # pragma: nocover
from .packages import tzlocal
log = logging.getLogger('WakaTime')
def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
entity=None, timestamp=None, is_write=None, plugin=None,
offline=None, entity_type='file', hidefilenames=None,
proxy=None, api_url=None, timeout=None, **kwargs):
proxy=None, nosslverify=None, api_url=None, timeout=None,
use_ntlm_proxy=False, **kwargs):
"""Sends heartbeat as POST request to WakaTime api server.
Returns `SUCCESS` when heartbeat was sent, otherwise returns an
@ -126,9 +135,10 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
session_cache = SessionCache()
session = session_cache.get()
should_try_ntlm = False
proxies = {}
if proxy:
if '\\' in proxy:
if use_ntlm_proxy:
from .packages.requests_ntlm import HttpNtlmAuth
username = proxy.rsplit(':', 1)
password = ''
@ -137,37 +147,80 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
username = username[0]
session.auth = HttpNtlmAuth(username, password, session)
else:
should_try_ntlm = '\\' in proxy
proxies['https'] = proxy
# log time to api
# send request to api
response = None
try:
response = session.post(api_url, data=request_body, headers=headers,
proxies=proxies, timeout=timeout)
proxies=proxies, timeout=timeout,
verify=not nosslverify)
except RequestException:
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
}
if log.isEnabledFor(logging.DEBUG):
exception_data['traceback'] = traceback.format_exc()
if offline:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
if log.isEnabledFor(logging.DEBUG):
log.warn(exception_data)
if should_try_ntlm:
return send_heartbeat(
project=project,
entity=entity,
timestamp=timestamp,
branch=branch,
hostname=hostname,
stats=stats,
key=key,
is_write=is_write,
plugin=plugin,
offline=offline,
hidefilenames=hidefilenames,
entity_type=entity_type,
proxy=proxy,
api_url=api_url,
timeout=timeout,
use_ntlm_proxy=True,
)
else:
log.error(exception_data)
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
}
if log.isEnabledFor(logging.DEBUG):
exception_data['traceback'] = traceback.format_exc()
if offline:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
if log.isEnabledFor(logging.DEBUG):
log.warn(exception_data)
else:
log.error(exception_data)
except: # delete cached session when requests raises unknown exception
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
'traceback': traceback.format_exc(),
}
if offline:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
log.warn(exception_data)
session_cache.delete()
if should_try_ntlm:
return send_heartbeat(
project=project,
entity=entity,
timestamp=timestamp,
branch=branch,
hostname=hostname,
stats=stats,
key=key,
is_write=is_write,
plugin=plugin,
offline=offline,
hidefilenames=hidefilenames,
entity_type=entity_type,
proxy=proxy,
api_url=api_url,
timeout=timeout,
use_ntlm_proxy=True,
)
else:
exception_data = {
sys.exc_info()[0].__name__: u(sys.exc_info()[1]),
'traceback': traceback.format_exc(),
}
if offline:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
log.warn(exception_data)
session_cache.delete()
return API_ERROR
else:
code = response.status_code if response is not None else None
@ -178,32 +231,52 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
})
session_cache.save(session)
return SUCCESS
if offline:
if code != 400:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
if code == 401:
if should_try_ntlm:
return send_heartbeat(
project=project,
entity=entity,
timestamp=timestamp,
branch=branch,
hostname=hostname,
stats=stats,
key=key,
is_write=is_write,
plugin=plugin,
offline=offline,
hidefilenames=hidefilenames,
entity_type=entity_type,
proxy=proxy,
api_url=api_url,
timeout=timeout,
use_ntlm_proxy=True,
)
else:
if offline:
if code != 400:
queue = Queue()
queue.push(data, json.dumps(stats), plugin)
if code == 401:
log.error({
'response_code': code,
'response_content': content,
})
session_cache.delete()
return AUTH_ERROR
elif log.isEnabledFor(logging.DEBUG):
log.warn({
'response_code': code,
'response_content': content,
})
else:
log.error({
'response_code': code,
'response_content': content,
})
session_cache.delete()
return AUTH_ERROR
elif log.isEnabledFor(logging.DEBUG):
log.warn({
'response_code': code,
'response_content': content,
})
else:
log.error({
'response_code': code,
'response_content': content,
})
else:
log.error({
'response_code': code,
'response_content': content,
})
session_cache.delete()
return API_ERROR
@ -278,6 +351,7 @@ def process_heartbeat(args, configs, hostname, heartbeat):
heartbeat['offline'] = args.offline
heartbeat['hidefilenames'] = args.hidefilenames
heartbeat['proxy'] = args.proxy
heartbeat['nosslverify'] = args.nosslverify
heartbeat['api_url'] = args.api_url
return send_heartbeat(**heartbeat)

View File

@ -134,9 +134,9 @@ class MatlabLexer(RegexLexer):
}
def analyse_text(text):
if re.match('^\s*%', text, re.M): # comment
if re.search(r'^\s*%', text, re.M): # comment
return 0.2
elif re.match('^!\w+', text, re.M): # system cmd
elif re.search(r'^!\w+', text, re.M): # system cmd
return 0.2

View File

@ -15,6 +15,7 @@ import re
import sys
from .compat import u, open
from .constants import MAX_FILE_SIZE_SUPPORTED
from .dependencies import DependencyParser
from .language_priorities import LANGUAGES
@ -171,10 +172,7 @@ def get_language_from_extension(file_name):
if os.path.exists(u('{0}{1}').format(u(filepart), u('.c'))) or os.path.exists(u('{0}{1}').format(u(filepart), u('.C'))):
return 'C'
directory = os.path.dirname(file_name)
available_files = os.listdir(directory)
available_extensions = list(zip(*map(os.path.splitext, available_files)))[1]
available_extensions = [ext.lower() for ext in available_extensions]
available_extensions = extensions_in_same_folder(file_name)
if '.cpp' in available_extensions:
return 'C++'
if '.c' in available_extensions:
@ -184,6 +182,11 @@ def get_language_from_extension(file_name):
def number_lines_in_file(file_name):
try:
if os.path.getsize(file_name) > MAX_FILE_SIZE_SUPPORTED:
return None
except os.error:
pass
lines = 0
try:
with open(file_name, 'r', encoding='utf-8') as fh:
@ -294,7 +297,7 @@ def custom_pygments_guess_lexer_for_filename(_fn, _text, **options):
rv = lexer.analyse_text(_text)
if rv == 1.0:
return lexer(**options)
result.append((rv, customize_priority(lexer)))
result.append(customize_lexer_priority(_fn, rv, lexer))
def type_sort(t):
# sort by:
@ -302,16 +305,33 @@ def custom_pygments_guess_lexer_for_filename(_fn, _text, **options):
# - is primary filename pattern?
# - priority
# - last resort: class name
return (t[0], primary[t[1]], t[1].priority, t[1].__name__)
return (t[0], primary[t[2]], t[1], t[2].__name__)
result.sort(key=type_sort)
return result[-1][1](**options)
return result[-1][2](**options)
def customize_priority(lexer):
"""Return an integer priority for the given lexer object."""
def customize_lexer_priority(file_name, accuracy, lexer):
"""Customize lexer priority"""
priority = lexer.priority
lexer_name = lexer.name.lower().replace('sharp', '#')
if lexer_name in LANGUAGES:
lexer.priority = LANGUAGES[lexer_name]
return lexer
priority = LANGUAGES[lexer_name]
elif lexer_name == 'matlab':
available_extensions = extensions_in_same_folder(file_name)
if '.mat' in available_extensions:
priority = 0.06
return (accuracy, priority, lexer)
def extensions_in_same_folder(file_name):
"""Returns a list of file extensions from the same folder as file_name."""
directory = os.path.dirname(file_name)
files = os.listdir(directory)
extensions = list(zip(*map(os.path.splitext, files)))[1]
extensions = set([ext.lower() for ext in extensions])
return extensions