Compare commits

...

22 Commits

Author SHA1 Message Date
fb303e048f v10.0.1 2020-12-28 18:54:50 -08:00
4f11222c2b changes for v10.0.1 2020-12-28 18:54:37 -08:00
72b72dc9f0 add sublime text to subtitle 2020-12-28 18:54:07 -08:00
1b07d0442b v10.0.0 2020-12-28 18:46:13 -08:00
fbd8e84ea1 changes for v10.0.0 2020-12-28 18:44:55 -08:00
01c0e7758e Partially revert "Use wakatime-cli standalone" to support both
standalone and Python source wakatime-cli versions.

This partially reverts commit d588451468.
2020-12-28 18:42:00 -08:00
28556de3b6 add back python binary location setting 2020-12-28 17:57:46 -08:00
809e43cfe5 fix link to api key 2020-12-28 17:49:31 -08:00
22ddbe27b0 link directly to wakatime install instructions 2020-12-28 17:44:00 -08:00
ddaf60b8b0 remove ide setting files from revision control 2020-07-30 15:53:04 -07:00
2e6a87c67e prevent executing wakatime-cli before it has been downloaded 2020-03-01 10:01:52 -08:00
01503b1c20 Merge pull request #99 from gandarez/standalone
Use wakatime-cli standalone
2020-03-01 10:57:14 -05:00
5a4ac9c11d check if cli is installed before sending heartbeat 2020-02-29 20:01:34 -05:00
fa0a3aacb5 add api_key into sublime-settings 2020-02-29 19:22:32 -05:00
d588451468 Use wakatime-cli standalone 2020-02-29 19:15:40 -05:00
483d8f596e v9.1.2 2020-02-13 09:24:11 -08:00
03f2d6d580 changes for v9.1.2 2020-02-13 09:23:58 -08:00
e1390d7647 Upgrade wakatime-cli to v13.0.7 2020-02-13 09:22:15 -08:00
c87bdd041c Upgrade wakatime-cli to v13.0.5 2020-02-11 21:26:01 -08:00
3a65395636 v9.1.1 2020-02-11 08:12:07 -08:00
0b2f3aa9a4 changes for v9.1.1 2020-02-11 08:11:52 -08:00
58ef2cd794 fix typo 2020-02-11 08:10:39 -08:00
455 changed files with 91138 additions and 188 deletions

View File

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

View File

@ -3,6 +3,34 @@ History
-------
10.0.1 (2020-12-28)
++++++++++++++++++
- Improve readme subtitle.
10.0.0 (2020-12-28)
++++++++++++++++++
- Support for standalone wakatime-cli, disabled by default.
9.1.2 (2020-02-13)
++++++++++++++++++
- Upgrade wakatime-cli to v13.0.7.
- Split bundled pygments library for Python 2.7+.
- Upgrade pygments for py27+ to v2.5.2 development master.
- Force requests to use bundled ca cert from certifi by default.
- Upgrade bundled certifi to v2019.11.28.
9.1.1 (2020-02-11)
++++++++++++++++++
- Fix typo in python detection on Windows platform.
9.1.0 (2020-02-09)
++++++++++++++++++

View File

@ -2,7 +2,7 @@
[![Coding time tracker](https://wakatime.com/badge/github/wakatime/sublime-wakatime.svg)](https://wakatime.com/badge/github/wakatime/sublime-wakatime)
Metrics, insights, and time tracking automatically generated from your programming activity.
[WakaTime][wakatime] is an open source Sublime Text plugin for metrics, insights, and time tracking automatically generated from your programming activity.
## Installation
@ -50,7 +50,7 @@ Also, tail your `$HOME/.wakatime.log` file to debug wakatime cli problems.
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]: https://wakatime.com/sublime-text
[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

@ -8,7 +8,7 @@ Website: https://wakatime.com/
==========================================================="""
__version__ = '9.1.0'
__version__ = '10.0.1'
import sublime
@ -19,6 +19,8 @@ import json
import os
import platform
import re
import shutil
import ssl
import subprocess
import sys
import time
@ -28,6 +30,7 @@ import urllib
import webbrowser
from subprocess import STDOUT, PIPE
from zipfile import ZipFile
try:
import _winreg as winreg # py2
except ImportError:
@ -35,11 +38,21 @@ except ImportError:
import winreg # py3
except ImportError:
winreg = None
try:
import Queue as queue # py2
except ImportError:
import queue # py3
try:
import ConfigParser as configparser
except ImportError:
import configparser
try:
from urllib2 import urlretrieve
except ImportError:
from urllib.request import urlretrieve
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
@ -112,7 +125,10 @@ class Popen(subprocess.Popen):
# globals
ST_VERSION = int(sublime.version())
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
RESOURCES_FOLDER = os.path.join(os.getenv('APPDATA'), 'WakaTime') if is_win else os.path.join(os.path.expanduser('~'), '.wakatime')
API_CLIENT = os.path.join(PLUGIN_DIR, 'packages', 'wakatime', 'cli.py')
API_CLIENT_STANDALONE = os.path.join(RESOURCES_FOLDER, 'wakatime-cli', 'wakatime-cli' + ('.exe' if is_win else ''))
S3_HOST = 'https://wakatime-cli.s3-us-west-2.amazonaws.com'
SETTINGS_FILE = 'WakaTime.sublime-settings'
SETTINGS = {}
LAST_HEARTBEAT = {
@ -142,8 +158,33 @@ sys.path.insert(0, os.path.join(PLUGIN_DIR, 'packages'))
try:
from wakatime.configs import parseConfigFile
except ImportError:
def parseConfigFile():
return None
def _configFile(self):
home = os.environ.get('WAKATIME_HOME')
if home:
return os.path.join(os.path.expanduser(home), '.wakatime.cfg')
return os.path.join(os.path.expanduser('~'), '.wakatime.cfg')
def parseConfigFile(self):
"""Returns a configparser.SafeConfigParser instance with configs
read from the config file. Default location of the config file is
at ~/.wakatime.cfg.
"""
configFile = _configFile()
configs = configparser.SafeConfigParser()
try:
with open(configFile, 'r', encoding='utf-8') as fh:
try:
configs.readfp(fh)
return configs
except configparser.Error:
log(ERROR, traceback.format_exc())
return None
except IOError:
log(DEBUG, "Error: Could not read from config file {0}\n".format(configFile))
return None
class ApiKey(object):
@ -212,13 +253,6 @@ def log(lvl, message, *args, **kwargs):
set_timeout(lambda: log(lvl, message, *args, **kwargs), 0)
def resources_folder():
if is_win:
return os.path.join(os.getenv('APPDATA'), 'WakaTime')
else:
return os.path.join(os.path.expanduser('~'), '.wakatime')
def update_status_bar(status=None, debounced=False, msg=None):
"""Updates the status bar."""
global LAST_FETCH_TODAY_CODING_TIME, FETCH_TODAY_DEBOUNCE_COUNTER
@ -261,27 +295,37 @@ class FetchStatusBarCodingTime(threading.Thread):
self.api_key = APIKEY.read() or ''
self.proxy = SETTINGS.get('proxy')
self.python_binary = SETTINGS.get('python_binary')
self.standalone = SETTINGS.get('standalone')
def run(self):
if not self.api_key:
log(DEBUG, 'Missing WakaTime API key.')
return
python = self.python_binary
if not python or not python.strip():
python = python_binary()
if not python:
log(DEBUG, 'Missing Python.')
if not isCliInstalled():
return
ua = 'sublime/%d sublime-wakatime/%s' % (ST_VERSION, __version__)
cmd = [
python,
API_CLIENT,
cmd = []
if self.standalone:
cmd.append(API_CLIENT_STANDALONE)
else:
python = self.python_binary
if not python or not python.strip():
python = python_binary()
if not python:
log(DEBUG, 'Missing Python.')
return
cmd.extend([
python,
API_CLIENT,
])
cmd.extend([
'--today',
'--key', str(bytes.decode(self.api_key.encode('utf8'))),
'--plugin', ua,
]
])
if self.debug:
cmd.append('--verbose')
if self.proxy:
@ -326,7 +370,7 @@ def python_binary():
# look for python in PATH and common install locations
paths = [
os.path.join(resources_folder(), 'python'),
os.path.join(RESOURCES_FOLDER, 'python'),
None,
'/',
'/usr/local/bin/',
@ -341,10 +385,10 @@ def python_binary():
ver -= 1
continue
paths.append('\\python{ver}\\'.format(ver=ver))
paths.append.push('\\Python{ver}\\'.format(ver=ver))
paths.append('{appdata}\\Programs\Python{ver}\\'.format(appdata=appdata, ver=ver))
paths.append('{appdata}\\Programs\Python{ver}-32\\'.format(appdata=appdata, ver=ver))
paths.append('{appdata}\\Programs\Python{ver}-64\\'.format(appdata=appdata, ver=ver))
paths.append('\\Python{ver}\\'.format(ver=ver))
paths.append('{appdata}\\Programs\\Python{ver}\\'.format(appdata=appdata, ver=ver))
paths.append('{appdata}\\Programs\\Python{ver}-32\\'.format(appdata=appdata, ver=ver))
paths.append('{appdata}\\Programs\\Python{ver}-64\\'.format(appdata=appdata, ver=ver))
ver -= 1
for path in paths:
@ -569,6 +613,9 @@ def append_heartbeat(entity, timestamp, is_write, view, project, folders):
def process_queue(timestamp):
global LAST_HEARTBEAT_SENT_AT
if not isCliInstalled():
return
# Prevent sending heartbeats more often than SEND_BUFFER_SECONDS
now = int(time.time())
if timestamp != LAST_HEARTBEAT['time'] and LAST_HEARTBEAT_SENT_AT > now - SEND_BUFFER_SECONDS:
@ -609,6 +656,7 @@ class SendHeartbeatsThread(threading.Thread):
self.hidefilenames = SETTINGS.get('hidefilenames')
self.proxy = SETTINGS.get('proxy')
self.python_binary = SETTINGS.get('python_binary')
self.standalone = SETTINGS.get('standalone')
self.heartbeat = heartbeat
self.has_extra_heartbeats = False
@ -645,14 +693,10 @@ class SendHeartbeatsThread(threading.Thread):
return heartbeat
def send_heartbeats(self):
python = self.python_binary
if not python or not python.strip():
python = python_binary()
if python:
if self.standalone:
heartbeat = self.build_heartbeat(**self.heartbeat)
ua = 'sublime/%d sublime-wakatime/%s' % (ST_VERSION, __version__)
cmd = [
python,
API_CLIENT,
'--entity', heartbeat['entity'],
'--time', str('%f' % heartbeat['timestamp']),
@ -689,8 +733,7 @@ class SendHeartbeatsThread(threading.Thread):
log(DEBUG, ' '.join(obfuscate_apikey(cmd)))
try:
process = Popen(cmd, stdin=stdin, stdout=PIPE, stderr=STDOUT)
output, err = process.communicate(input=inp)
output = u(output)
output, _err = process.communicate(input=inp)
retcode = process.poll()
if (not retcode or retcode == 102) and not output:
self.sent()
@ -703,10 +746,69 @@ class SendHeartbeatsThread(threading.Thread):
except:
log(ERROR, u(sys.exc_info()[1]))
update_status_bar('Error')
else:
log(ERROR, 'Unable to find python binary.')
update_status_bar('Error')
python = self.python_binary
if not python or not python.strip():
python = python_binary()
if python:
heartbeat = self.build_heartbeat(**self.heartbeat)
ua = 'sublime/%d sublime-wakatime/%s' % (ST_VERSION, __version__)
cmd = [
python,
API_CLIENT,
'--entity', heartbeat['entity'],
'--time', str('%f' % heartbeat['timestamp']),
'--plugin', ua,
]
if self.api_key:
cmd.extend(['--key', str(bytes.decode(self.api_key.encode('utf8')))])
if heartbeat['is_write']:
cmd.append('--write')
if heartbeat.get('alternate_project'):
cmd.extend(['--alternate-project', heartbeat['alternate_project']])
if heartbeat.get('cursorpos') is not None:
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:
cmd.append('--hidefilenames')
if self.proxy:
cmd.extend(['--proxy', self.proxy])
if self.has_extra_heartbeats:
cmd.append('--extra-heartbeats')
stdin = PIPE
extra_heartbeats = json.dumps([self.build_heartbeat(**x) for x in self.extra_heartbeats])
inp = "{0}\n".format(extra_heartbeats).encode('utf-8')
else:
extra_heartbeats = None
stdin = None
inp = None
log(DEBUG, ' '.join(obfuscate_apikey(cmd)))
try:
process = Popen(cmd, stdin=stdin, stdout=PIPE, stderr=STDOUT)
output, err = process.communicate(input=inp)
output = u(output)
retcode = process.poll()
if (not retcode or retcode == 102) and not output:
self.sent()
else:
update_status_bar('Error')
if retcode:
log(DEBUG if retcode == 102 else ERROR, 'wakatime-core exited with status: {0}'.format(retcode))
if output:
log(ERROR, u('wakatime-core output: {0}').format(output))
except:
log(ERROR, u(sys.exc_info()[1]))
update_status_bar('Error')
else:
log(ERROR, 'Unable to find python binary.')
update_status_bar('Error')
def sent(self):
update_status_bar('OK')
@ -731,10 +833,10 @@ class DownloadPython(threading.Thread):
arch=arch,
)
if not os.path.exists(resources_folder()):
os.makedirs(resources_folder())
if not os.path.exists(RESOURCES_FOLDER):
os.makedirs(RESOURCES_FOLDER)
zip_file = os.path.join(resources_folder(), 'python.zip')
zip_file = os.path.join(RESOURCES_FOLDER, 'python.zip')
try:
urllib.urlretrieve(url, zip_file)
except AttributeError:
@ -742,7 +844,7 @@ class DownloadPython(threading.Thread):
log(INFO, 'Extracting Python...')
with contextlib.closing(ZipFile(zip_file)) as zf:
path = os.path.join(resources_folder(), 'python')
path = os.path.join(RESOURCES_FOLDER, 'python')
zf.extractall(path)
try:
@ -760,13 +862,20 @@ def plugin_loaded():
log(INFO, 'Initializing WakaTime plugin v%s' % __version__)
update_status_bar('Initializing...')
if not python_binary():
log(WARNING, 'Python binary not found.')
if is_win:
set_timeout(download_python, 0)
else:
sublime.error_message("Unable to find Python binary!\nWakaTime needs Python to work correctly.\n\nGo to https://www.python.org/downloads")
return
standalone = SETTINGS.get('standalone')
if standalone:
if not isCliLatest():
thread = DownloadCLI()
thread.start()
else:
if not python_binary():
log(WARNING, 'Python binary not found.')
if is_win:
set_timeout(download_python, 0)
else:
sublime.error_message("Unable to find Python binary!\nWakaTime needs Python to work correctly.\n\nGo to https://www.python.org/downloads")
return
after_loaded()
@ -800,3 +909,121 @@ class WakatimeDashboardCommand(sublime_plugin.ApplicationCommand):
def run(self):
webbrowser.open_new_tab('https://wakatime.com/dashboard')
class DownloadCLI(threading.Thread):
"""Non-blocking thread for downloading latest wakatime-cli from GitHub.
"""
def run(self):
log(INFO, 'Downloading wakatime-cli...')
if not os.path.exists(RESOURCES_FOLDER):
os.makedirs(RESOURCES_FOLDER)
try:
shutil.rmtree(os.path.join(RESOURCES_FOLDER, 'wakatime-cli'))
except:
pass
try:
url = self._getCliUrl()
zip_file = os.path.join(RESOURCES_FOLDER, 'wakatime-cli.zip')
download(url, zip_file)
log(INFO, 'Extracting wakatime-cli...')
with ZipFile(zip_file) as zf:
zf.extractall(RESOURCES_FOLDER)
try:
shutil.rmtree(os.path.join(RESOURCES_FOLDER, 'wakatime-cli.zip'))
except:
pass
except:
log(DEBUG, traceback.format_exc())
log(INFO, 'Finished extracting wakatime-cli.')
def _getCliUrl(self):
os = platform.system().lower().replace('darwin', 'mac')
arch = '64' if sys.maxsize > 2**32 else '32'
return '{host}/{os}-x86-{arch}/wakatime-cli.zip'.format(
host=S3_HOST,
os=os,
arch=arch,
)
def isCliInstalled():
return os.path.exists(API_CLIENT)
def isCliLatest():
if not isCliInstalled():
return False
args = [API_CLIENT, '--version']
stdout, stderr = Popen(args, stdout=PIPE, stderr=PIPE).communicate()
stdout = (stdout or b'') + (stderr or b'')
localVer = extractVersion(stdout.decode('utf-8'))
if not localVer:
return False
log(INFO, 'Current wakatime-cli version is %s' % localVer)
log(INFO, 'Checking for updates to wakatime-cli...')
remoteVer = getLatestCliVersion()
if not remoteVer:
return True
if remoteVer == localVer:
log(INFO, 'wakatime-cli is up to date.')
return True
log(INFO, 'Found an updated wakatime-cli v%s' % remoteVer)
return False
def getLatestCliVersion():
url = getCliVersionUrl()
try:
localFile = os.path.join(RESOURCES_FOLDER, 'current_version.txt')
download(url, localFile)
ver = None
with open(localFile) as fh:
ver = extractVersion(fh.read())
try:
shutil.rmtree(localFile)
except:
pass
return ver
except:
return None
def getCliVersionUrl():
os = platform.system().lower().replace('darwin', 'mac')
arch = '64' if sys.maxsize > 2**32 else '32'
return '{host}/{os}-x86-{arch}/current_version.txt'.format(
host=S3_HOST,
os=os,
arch=arch,
)
def extractVersion(text):
log(DEBUG, 'extracting version.')
pattern = re.compile(r"([0-9]+\.[0-9]+\.[0-9]+)")
match = pattern.search(text)
if match:
return match.group(1)
return None
def download(url, filePath):
try:
urlretrieve(url, filePath)
except IOError:
ssl._create_default_https_context = ssl._create_unverified_context
urlretrieve(url, filePath)

View File

@ -3,7 +3,7 @@
// This settings file will be overwritten when upgrading.
{
// Your api key from https://wakatime.com/#apikey
// Your api key from https://wakatime.com/api-key
// Set this in your User specific WakaTime.sublime-settings file.
"api_key": "",
@ -33,5 +33,8 @@
"hidefilenames": false,
// Python binary location. Uses python from your PATH by default.
"python_binary": ""
"python_binary": "",
// Use standalone compiled Python wakatime-cli (Will not work on ARM Macs)
"standalone": false
}

View File

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

View File

@ -22,6 +22,7 @@ from .offlinequeue import Queue
from .session_cache import SessionCache
from .utils import get_hostname, get_user_agent
from .packages import tzlocal
from .packages import certifi
log = logging.getLogger('WakaTime')
@ -101,16 +102,12 @@ def send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=False):
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
# send request to api
response, code = None, None
try:
response = session.post(api_url, data=request_body, headers=headers,
proxies=proxies, timeout=timeout,
verify=ssl_verify)
verify=_get_verify(args))
except RequestException:
if should_try_ntlm:
return send_heartbeats(heartbeats, args, configs, use_ntlm_proxy=True)
@ -204,10 +201,6 @@ def get_time_today(args, use_ntlm_proxy=False):
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',
@ -218,7 +211,7 @@ def get_time_today(args, use_ntlm_proxy=False):
try:
response = session.get(url, params=params, headers=headers,
proxies=proxies, timeout=timeout,
verify=ssl_verify)
verify=_get_verify(args))
except RequestException:
if should_try_ntlm:
return get_time_today(args, use_ntlm_proxy=True)
@ -282,6 +275,16 @@ def get_time_today(args, use_ntlm_proxy=False):
return None, API_ERROR
def _get_verify(args):
verify = not args.nosslverify
if verify:
if args.ssl_certs_file:
verify = args.ssl_certs_file
else:
verify = certifi.where()
return verify
def _process_server_results(heartbeats, code, content, results, args, configs):
log.debug({
'response_code': code,

View File

@ -39,6 +39,8 @@ class JavaParser(TokenParser):
self._process_attribute(token, content)
elif self.partial(token) == 'Operator':
self._process_operator(token, content)
elif self.partial(token) == 'Punctuation':
self._process_operator(token, content)
else:
self._process_other(token, content)

View File

@ -9,6 +9,58 @@
:license: BSD, see LICENSE for more details.
"""
"""
Token.Keyword.Namespace import None
Token.Text import
Token.Name.Namespace os import
Token.Operator , import-2
Token.Text import-2
Token.Name.Namespace sys import-2
Token.Text
import-2
Token.Keyword.Namespace import None
Token.Text import
Token.Name.Namespace django.forms.monstertruck import
Token.Text
import-2
Token.Keyword.Namespace import None
Token.Text import
Token.Name.Namespace os import
Token.Operator , import-2
Token.Text import-2
Token.Name.Namespace sys import-2
Token.Text
import-2
Token.Keyword.Namespace import None
Token.Text import
Token.Name.Namespace django import
Token.Name.Namespace . import-2
Token.Name.Namespace forms import-2
Token.Name.Namespace . import-2
Token.Name.Namespace monstertruck import-2
Token.Text
import-2
None Token.Keyword.Namespace import
import Token.Text
import Token.Name.Namespace django
import-2 Token.Name.Namespace .
import-2 Token.Name.Namespace forms
import-2 Token.Name.Namespace .
import-2 Token.Name.Namespace monstertruck
import-2 Token.Text
None Token.Keyword.Namespace from
from Token.Text
from Token.Name.Namespace app
from-2 Token.Text
"""
from . import TokenParser
@ -32,7 +84,7 @@ class PythonParser(TokenParser):
if self.partial(token) == 'Namespace':
self._process_namespace(token, content)
elif self.partial(token) == 'Operator':
self._process_operator(token, content)
self._process_punctuation(token, content)
elif self.partial(token) == 'Punctuation':
self._process_punctuation(token, content)
elif self.partial(token) == 'Text':
@ -43,21 +95,20 @@ class PythonParser(TokenParser):
def _process_namespace(self, token, content):
if self.state is None:
self.state = content
elif content == 'as':
self.nonpackage = True
else:
if content == 'as':
self.nonpackage = True
else:
self._process_import(token, content)
def _process_operator(self, token, content):
pass
self._process_import(token, content)
def _process_punctuation(self, token, content):
if content == '(':
self.parens += 1
self.nonpackage = False
elif content == ')':
self.parens -= 1
self.nonpackage = False
self.nonpackage = False
elif content == ',' and self.state == 'import-2':
self.state = 'import'
def _process_text(self, token, content):
if self.state is not None:
@ -72,12 +123,12 @@ class PythonParser(TokenParser):
if not self.nonpackage:
if self.state == 'from':
self.append(content, truncate=True, truncate_to=1)
self.state = 'from-2'
self.state = None
elif self.state == 'import':
self.append(content, truncate=True, truncate_to=1)
self.state = 'import-2'
elif self.state == 'import-2':
self.append(content, truncate=True, truncate_to=1)
pass
else:
self.state = None
self.nonpackage = False

View File

@ -23,7 +23,9 @@ sys.path.insert(0, os.path.join(pwd, 'packages'))
from .compat import is_py26
if not is_py26:
if is_py26:
sys.path.insert(0, os.path.join(pwd, 'packages', 'py26'))
else:
sys.path.insert(0, os.path.join(pwd, 'packages', 'py27'))
from .__about__ import __version__

View File

@ -1,3 +1,3 @@
from .core import where
__version__ = "2019.03.09"
__version__ = "2019.11.28"

View File

@ -1,2 +1,2 @@
from . import where
from certifi import where
print(where())

View File

@ -771,36 +771,6 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
# Issuer: CN=Class 2 Primary CA O=Certplus
# Subject: CN=Class 2 Primary CA O=Certplus
# Label: "Certplus Class 2 Primary CA"
# Serial: 177770208045934040241468760488327595043
# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b
# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb
# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb
-----BEGIN CERTIFICATE-----
MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
l7+ijrRU
-----END CERTIFICATE-----
# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co.
# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co.
# Label: "DST Root CA X3"
@ -1219,36 +1189,6 @@ t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----
# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
# Label: "Deutsche Telekom Root CA 2"
# Serial: 38
# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08
# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf
# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3
-----BEGIN CERTIFICATE-----
MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
Cm26OWMohpLzGITY+9HPBVZkVw==
-----END CERTIFICATE-----
# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
# Label: "Cybertrust Global Root"
@ -3453,46 +3393,6 @@ AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----
# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
# Label: "Certinomis - Root CA"
# Serial: 1
# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f
# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8
# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58
-----BEGIN CERTIFICATE-----
MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET
MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb
BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz
MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx
FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g
Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2
fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl
LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV
WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF
TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb
5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc
CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri
wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ
wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG
m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4
F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng
WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0
2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/
0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw
F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS
g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj
qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN
h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/
ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V
btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj
Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ
8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW
gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE=
-----END CERTIFICATE-----
# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
# Label: "OISTE WISeKey Global Root GB CA"
@ -4656,3 +4556,47 @@ L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa
LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG
mpv0
-----END CERTIFICATE-----
# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only
# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only
# Label: "Entrust Root Certification Authority - G4"
# Serial: 289383649854506086828220374796556676440
# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88
# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01
# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw
gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL
Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg
MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw
BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0
MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1
c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ
bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg
Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ
2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E
T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j
5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM
C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T
DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX
wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A
2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm
nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8
dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl
N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj
c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS
5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS
Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr
hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/
B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI
AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw
H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+
b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk
2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol
IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk
5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY
n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==
-----END CERTIFICATE-----

Some files were not shown because too many files have changed in this diff Show More