mirror of
https://github.com/wakatime/sublime-wakatime.git
synced 2023-08-10 21:13:02 +03:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
fc8c61fa3f | |||
aa30110343 | |||
b671856341 | |||
b801759cdf | |||
919064200b | |||
911b5656d7 |
13
HISTORY.rst
13
HISTORY.rst
@ -3,6 +3,19 @@ History
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
||||||
|
3.0.15 (2015-04-01)
|
||||||
|
+++++++++++++++++++
|
||||||
|
|
||||||
|
- obfuscate api key when logging to Sublime Text Console in debug mode
|
||||||
|
|
||||||
|
|
||||||
|
3.0.14 (2015-03-31)
|
||||||
|
+++++++++++++++++++
|
||||||
|
|
||||||
|
- always use external python binary because ST builtin python does not support checking SSL certs
|
||||||
|
- upgrade wakatime cli to v4.0.6
|
||||||
|
|
||||||
|
|
||||||
3.0.13 (2015-03-23)
|
3.0.13 (2015-03-23)
|
||||||
+++++++++++++++++++
|
+++++++++++++++++++
|
||||||
|
|
||||||
|
95
WakaTime.py
95
WakaTime.py
@ -6,7 +6,9 @@ License: BSD, see LICENSE for more details.
|
|||||||
Website: https://wakatime.com/
|
Website: https://wakatime.com/
|
||||||
==========================================================="""
|
==========================================================="""
|
||||||
|
|
||||||
__version__ = '3.0.13'
|
|
||||||
|
__version__ = '3.0.15'
|
||||||
|
|
||||||
|
|
||||||
import sublime
|
import sublime
|
||||||
import sublime_plugin
|
import sublime_plugin
|
||||||
@ -19,13 +21,14 @@ import time
|
|||||||
import threading
|
import threading
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os.path import expanduser, dirname, basename, realpath, join
|
from subprocess import Popen
|
||||||
|
|
||||||
|
|
||||||
# globals
|
# globals
|
||||||
ACTION_FREQUENCY = 2
|
ACTION_FREQUENCY = 2
|
||||||
ST_VERSION = int(sublime.version())
|
ST_VERSION = int(sublime.version())
|
||||||
PLUGIN_DIR = dirname(realpath(__file__))
|
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
API_CLIENT = '%s/packages/wakatime/cli.py' % PLUGIN_DIR
|
API_CLIENT = os.path.join(PLUGIN_DIR, 'packages', 'wakatime', 'cli.py')
|
||||||
SETTINGS_FILE = 'WakaTime.sublime-settings'
|
SETTINGS_FILE = 'WakaTime.sublime-settings'
|
||||||
SETTINGS = {}
|
SETTINGS = {}
|
||||||
LAST_ACTION = {
|
LAST_ACTION = {
|
||||||
@ -33,27 +36,16 @@ LAST_ACTION = {
|
|||||||
'file': None,
|
'file': None,
|
||||||
'is_write': False,
|
'is_write': False,
|
||||||
}
|
}
|
||||||
HAS_SSL = False
|
|
||||||
LOCK = threading.RLock()
|
LOCK = threading.RLock()
|
||||||
PYTHON_LOCATION = None
|
PYTHON_LOCATION = None
|
||||||
|
|
||||||
|
|
||||||
# add wakatime package to path
|
# add wakatime package to path
|
||||||
sys.path.insert(0, join(PLUGIN_DIR, 'packages'))
|
sys.path.insert(0, os.path.join(PLUGIN_DIR, 'packages'))
|
||||||
|
|
||||||
# check if we have SSL support
|
|
||||||
try:
|
try:
|
||||||
import ssl
|
from wakatime.base import parseConfigFile
|
||||||
import socket
|
except ImportError:
|
||||||
assert ssl
|
pass
|
||||||
assert socket.ssl
|
|
||||||
assert ssl.OPENSSL_VERSION
|
|
||||||
HAS_SSL = True
|
|
||||||
except (ImportError, AttributeError):
|
|
||||||
from subprocess import Popen
|
|
||||||
|
|
||||||
if HAS_SSL:
|
|
||||||
# import wakatime package so we can use built-in python
|
|
||||||
import wakatime
|
|
||||||
|
|
||||||
|
|
||||||
def createConfigFile():
|
def createConfigFile():
|
||||||
@ -81,7 +73,6 @@ def prompt_api_key():
|
|||||||
|
|
||||||
default_key = ''
|
default_key = ''
|
||||||
try:
|
try:
|
||||||
from wakatime.base import parseConfigFile
|
|
||||||
configs = parseConfigFile()
|
configs = parseConfigFile()
|
||||||
if configs is not None:
|
if configs is not None:
|
||||||
if configs.has_option('settings', 'api_key'):
|
if configs.has_option('settings', 'api_key'):
|
||||||
@ -123,7 +114,7 @@ def python_binary():
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
for path in glob.iglob('/python*'):
|
for path in glob.iglob('/python*'):
|
||||||
path = realpath(join(path, 'pythonw'))
|
path = os.path.realpath(os.path.join(path, 'pythonw'))
|
||||||
try:
|
try:
|
||||||
Popen([path, '--version'])
|
Popen([path, '--version'])
|
||||||
PYTHON_LOCATION = path
|
PYTHON_LOCATION = path
|
||||||
@ -133,6 +124,17 @@ def python_binary():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def obfuscate_apikey(cmd):
|
||||||
|
apikey_index = None
|
||||||
|
for num in range(len(cmd)):
|
||||||
|
if cmd[num] == '--key':
|
||||||
|
apikey_index = num + 1
|
||||||
|
break
|
||||||
|
if apikey_index is not None and apikey_index < len(cmd):
|
||||||
|
cmd[apikey_index] = '********-****-****-****-********' + cmd[apikey_index][-4:]
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
def enough_time_passed(now, last_time):
|
def enough_time_passed(now, last_time):
|
||||||
if now - last_time > ACTION_FREQUENCY * 60:
|
if now - last_time > ACTION_FREQUENCY * 60:
|
||||||
return True
|
return True
|
||||||
@ -151,10 +153,13 @@ def find_project_name_from_folders(folders):
|
|||||||
|
|
||||||
|
|
||||||
def handle_action(view, is_write=False):
|
def handle_action(view, is_write=False):
|
||||||
target_file = view.file_name()
|
window = view.window()
|
||||||
project = view.window().project_file_name() if hasattr(view.window(), 'project_file_name') else None
|
if window is not None:
|
||||||
thread = SendActionThread(target_file, view, is_write=is_write, project=project, folders=view.window().folders())
|
target_file = view.file_name()
|
||||||
thread.start()
|
project = window.project_file_name() if hasattr(window, 'project_file_name') else None
|
||||||
|
folders = window.folders()
|
||||||
|
thread = SendActionThread(target_file, view, is_write=is_write, project=project, folders=folders)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
class SendActionThread(threading.Thread):
|
class SendActionThread(threading.Thread):
|
||||||
@ -195,7 +200,7 @@ class SendActionThread(threading.Thread):
|
|||||||
if self.is_write:
|
if self.is_write:
|
||||||
cmd.append('--write')
|
cmd.append('--write')
|
||||||
if self.project:
|
if self.project:
|
||||||
self.project = basename(self.project).replace('.sublime-project', '', 1)
|
self.project = os.path.basename(self.project).replace('.sublime-project', '', 1)
|
||||||
if self.project:
|
if self.project:
|
||||||
cmd.extend(['--project', self.project])
|
cmd.extend(['--project', self.project])
|
||||||
elif self.folders:
|
elif self.folders:
|
||||||
@ -206,28 +211,18 @@ class SendActionThread(threading.Thread):
|
|||||||
cmd.extend(['--ignore', pattern])
|
cmd.extend(['--ignore', pattern])
|
||||||
if self.debug:
|
if self.debug:
|
||||||
cmd.append('--verbose')
|
cmd.append('--verbose')
|
||||||
if HAS_SSL:
|
if python_binary():
|
||||||
|
cmd.insert(0, python_binary())
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print('[WakaTime] %s' % ' '.join(cmd))
|
print('[WakaTime] %s' % ' '.join(obfuscate_apikey(cmd)))
|
||||||
code = wakatime.main(cmd)
|
if platform.system() == 'Windows':
|
||||||
if code != 0:
|
Popen(cmd, shell=False)
|
||||||
print('[WakaTime] Error: Response code %d from wakatime package.' % code)
|
|
||||||
else:
|
else:
|
||||||
self.sent()
|
with open(os.path.join(os.path.expanduser('~'), '.wakatime.log'), 'a') as stderr:
|
||||||
|
Popen(cmd, stderr=stderr)
|
||||||
|
self.sent()
|
||||||
else:
|
else:
|
||||||
python = python_binary()
|
print('[WakaTime] Error: Unable to find python binary.')
|
||||||
if python:
|
|
||||||
cmd.insert(0, python)
|
|
||||||
if self.debug:
|
|
||||||
print('[WakaTime] %s %s' % (python, ' '.join(cmd)))
|
|
||||||
if platform.system() == 'Windows':
|
|
||||||
Popen(cmd, shell=False)
|
|
||||||
else:
|
|
||||||
with open(join(expanduser('~'), '.wakatime.log'), 'a') as stderr:
|
|
||||||
Popen(cmd, stderr=stderr)
|
|
||||||
self.sent()
|
|
||||||
else:
|
|
||||||
print('[WakaTime] Error: Unable to find python binary.')
|
|
||||||
|
|
||||||
def sent(self):
|
def sent(self):
|
||||||
sublime.set_timeout(self.set_status_bar, 0)
|
sublime.set_timeout(self.set_status_bar, 0)
|
||||||
@ -250,11 +245,9 @@ def plugin_loaded():
|
|||||||
global SETTINGS
|
global SETTINGS
|
||||||
print('[WakaTime] Initializing WakaTime plugin v%s' % __version__)
|
print('[WakaTime] Initializing WakaTime plugin v%s' % __version__)
|
||||||
|
|
||||||
if not HAS_SSL:
|
if not python_binary():
|
||||||
python = python_binary()
|
sublime.error_message("Unable to find Python binary!\nWakaTime needs Python to work correctly.\n\nGo to https://www.python.org/downloads")
|
||||||
if not python:
|
return
|
||||||
sublime.error_message("Unable to find Python binary!\nWakaTime needs Python to work correctly.\n\nGo to https://www.python.org/downloads")
|
|
||||||
return
|
|
||||||
|
|
||||||
SETTINGS = sublime.load_settings(SETTINGS_FILE)
|
SETTINGS = sublime.load_settings(SETTINGS_FILE)
|
||||||
after_loaded()
|
after_loaded()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
__title__ = 'wakatime'
|
__title__ = 'wakatime'
|
||||||
__description__ = 'Common interface to the WakaTime api.'
|
__description__ = 'Common interface to the WakaTime api.'
|
||||||
__url__ = 'https://github.com/wakatime/wakatime'
|
__url__ = 'https://github.com/wakatime/wakatime'
|
||||||
__version_info__ = ('4', '0', '4')
|
__version_info__ = ('4', '0', '6')
|
||||||
__version__ = '.'.join(__version_info__)
|
__version__ = '.'.join(__version_info__)
|
||||||
__author__ = 'Alan Hamlett'
|
__author__ = 'Alan Hamlett'
|
||||||
__author_email__ = 'alan@wakatime.com'
|
__author_email__ = 'alan@wakatime.com'
|
||||||
|
@ -24,8 +24,9 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages'))
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages'))
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages', 'requests', 'packages'))
|
||||||
|
|
||||||
from .__about__ import __version__
|
from .__about__ import __version__
|
||||||
from .compat import u, open, is_py3
|
from .compat import u, open, is_py3
|
||||||
|
@ -24,6 +24,9 @@ except ImportError:
|
|||||||
class CustomEncoder(json.JSONEncoder):
|
class CustomEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
|
if isinstance(obj, bytes):
|
||||||
|
obj = bytes.decode(obj)
|
||||||
|
return json.dumps(obj)
|
||||||
try:
|
try:
|
||||||
encoded = super(CustomEncoder, self).default(obj)
|
encoded = super(CustomEncoder, self).default(obj)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
|
@ -42,7 +42,7 @@ is at <http://python-requests.org>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__title__ = 'requests'
|
__title__ = 'requests'
|
||||||
__version__ = '2.5.3'
|
__version__ = '2.6.0'
|
||||||
__build__ = 0x020503
|
__build__ = 0x020503
|
||||||
__author__ = 'Kenneth Reitz'
|
__author__ = 'Kenneth Reitz'
|
||||||
__license__ = 'Apache 2.0'
|
__license__ = 'Apache 2.0'
|
||||||
|
@ -11,10 +11,10 @@ and maintain connections.
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
from .models import Response
|
from .models import Response
|
||||||
from .packages.urllib3 import Retry
|
|
||||||
from .packages.urllib3.poolmanager import PoolManager, proxy_from_url
|
from .packages.urllib3.poolmanager import PoolManager, proxy_from_url
|
||||||
from .packages.urllib3.response import HTTPResponse
|
from .packages.urllib3.response import HTTPResponse
|
||||||
from .packages.urllib3.util import Timeout as TimeoutSauce
|
from .packages.urllib3.util import Timeout as TimeoutSauce
|
||||||
|
from .packages.urllib3.util.retry import Retry
|
||||||
from .compat import urlparse, basestring
|
from .compat import urlparse, basestring
|
||||||
from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers,
|
from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers,
|
||||||
prepend_scheme_if_needed, get_auth_from_url, urldefragauth)
|
prepend_scheme_if_needed, get_auth_from_url, urldefragauth)
|
||||||
|
@ -16,7 +16,6 @@ from . import sessions
|
|||||||
|
|
||||||
def request(method, url, **kwargs):
|
def request(method, url, **kwargs):
|
||||||
"""Constructs and sends a :class:`Request <Request>`.
|
"""Constructs and sends a :class:`Request <Request>`.
|
||||||
Returns :class:`Response <Response>` object.
|
|
||||||
|
|
||||||
:param method: method for the new :class:`Request` object.
|
:param method: method for the new :class:`Request` object.
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
@ -37,6 +36,8 @@ def request(method, url, **kwargs):
|
|||||||
:param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
|
:param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
|
||||||
:param stream: (optional) if ``False``, the response content will be immediately downloaded.
|
:param stream: (optional) if ``False``, the response content will be immediately downloaded.
|
||||||
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
|
|
||||||
Usage::
|
Usage::
|
||||||
|
|
||||||
@ -55,10 +56,12 @@ def request(method, url, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def get(url, **kwargs):
|
def get(url, **kwargs):
|
||||||
"""Sends a GET request. Returns :class:`Response` object.
|
"""Sends a GET request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs.setdefault('allow_redirects', True)
|
kwargs.setdefault('allow_redirects', True)
|
||||||
@ -66,10 +69,12 @@ def get(url, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def options(url, **kwargs):
|
def options(url, **kwargs):
|
||||||
"""Sends a OPTIONS request. Returns :class:`Response` object.
|
"""Sends a OPTIONS request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs.setdefault('allow_redirects', True)
|
kwargs.setdefault('allow_redirects', True)
|
||||||
@ -77,10 +82,12 @@ def options(url, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def head(url, **kwargs):
|
def head(url, **kwargs):
|
||||||
"""Sends a HEAD request. Returns :class:`Response` object.
|
"""Sends a HEAD request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs.setdefault('allow_redirects', False)
|
kwargs.setdefault('allow_redirects', False)
|
||||||
@ -88,44 +95,52 @@ def head(url, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def post(url, data=None, json=None, **kwargs):
|
def post(url, data=None, json=None, **kwargs):
|
||||||
"""Sends a POST request. Returns :class:`Response` object.
|
"""Sends a POST request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
||||||
:param json: (optional) json data to send in the body of the :class:`Request`.
|
:param json: (optional) json data to send in the body of the :class:`Request`.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return request('post', url, data=data, json=json, **kwargs)
|
return request('post', url, data=data, json=json, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def put(url, data=None, **kwargs):
|
def put(url, data=None, **kwargs):
|
||||||
"""Sends a PUT request. Returns :class:`Response` object.
|
"""Sends a PUT request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return request('put', url, data=data, **kwargs)
|
return request('put', url, data=data, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def patch(url, data=None, **kwargs):
|
def patch(url, data=None, **kwargs):
|
||||||
"""Sends a PATCH request. Returns :class:`Response` object.
|
"""Sends a PATCH request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return request('patch', url, data=data, **kwargs)
|
return request('patch', url, data=data, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def delete(url, **kwargs):
|
def delete(url, **kwargs):
|
||||||
"""Sends a DELETE request. Returns :class:`Response` object.
|
"""Sends a DELETE request.
|
||||||
|
|
||||||
:param url: URL for the new :class:`Request` object.
|
:param url: URL for the new :class:`Request` object.
|
||||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||||
|
:return: :class:`Response <Response>` object
|
||||||
|
:rtype: requests.Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return request('delete', url, **kwargs)
|
return request('delete', url, **kwargs)
|
||||||
|
@ -143,12 +143,13 @@ class RequestEncodingMixin(object):
|
|||||||
else:
|
else:
|
||||||
fn = guess_filename(v) or k
|
fn = guess_filename(v) or k
|
||||||
fp = v
|
fp = v
|
||||||
if isinstance(fp, str):
|
|
||||||
fp = StringIO(fp)
|
|
||||||
if isinstance(fp, (bytes, bytearray)):
|
|
||||||
fp = BytesIO(fp)
|
|
||||||
|
|
||||||
rf = RequestField(name=k, data=fp.read(),
|
if isinstance(fp, (str, bytes, bytearray)):
|
||||||
|
fdata = fp
|
||||||
|
else:
|
||||||
|
fdata = fp.read()
|
||||||
|
|
||||||
|
rf = RequestField(name=k, data=fdata,
|
||||||
filename=fn, headers=fh)
|
filename=fn, headers=fh)
|
||||||
rf.make_multipart(content_type=ft)
|
rf.make_multipart(content_type=ft)
|
||||||
new_fields.append(rf)
|
new_fields.append(rf)
|
||||||
@ -572,7 +573,11 @@ class Response(object):
|
|||||||
self.cookies = cookiejar_from_dict({})
|
self.cookies = cookiejar_from_dict({})
|
||||||
|
|
||||||
#: The amount of time elapsed between sending the request
|
#: The amount of time elapsed between sending the request
|
||||||
#: and the arrival of the response (as a timedelta)
|
#: and the arrival of the response (as a timedelta).
|
||||||
|
#: This property specifically measures the time taken between sending
|
||||||
|
#: the first byte of the request and finishing parsing the headers. It
|
||||||
|
#: is therefore unaffected by consuming the response content or the
|
||||||
|
#: value of the ``stream`` keyword argument.
|
||||||
self.elapsed = datetime.timedelta(0)
|
self.elapsed = datetime.timedelta(0)
|
||||||
|
|
||||||
#: The :class:`PreparedRequest <PreparedRequest>` object to which this
|
#: The :class:`PreparedRequest <PreparedRequest>` object to which this
|
||||||
|
@ -4,7 +4,7 @@ urllib3 - Thread-safe connection pooling and re-using.
|
|||||||
|
|
||||||
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
|
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__version__ = 'dev'
|
__version__ = '1.10.2'
|
||||||
|
|
||||||
|
|
||||||
from .connectionpool import (
|
from .connectionpool import (
|
||||||
|
@ -20,8 +20,6 @@ from .packages.six import iterkeys, itervalues, PY3
|
|||||||
__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict']
|
__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict']
|
||||||
|
|
||||||
|
|
||||||
MULTIPLE_HEADERS_ALLOWED = frozenset(['cookie', 'set-cookie', 'set-cookie2'])
|
|
||||||
|
|
||||||
_Null = object()
|
_Null = object()
|
||||||
|
|
||||||
|
|
||||||
@ -143,7 +141,10 @@ class HTTPHeaderDict(dict):
|
|||||||
def __init__(self, headers=None, **kwargs):
|
def __init__(self, headers=None, **kwargs):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
if headers is not None:
|
if headers is not None:
|
||||||
self.extend(headers)
|
if isinstance(headers, HTTPHeaderDict):
|
||||||
|
self._copy_from(headers)
|
||||||
|
else:
|
||||||
|
self.extend(headers)
|
||||||
if kwargs:
|
if kwargs:
|
||||||
self.extend(kwargs)
|
self.extend(kwargs)
|
||||||
|
|
||||||
@ -223,11 +224,8 @@ class HTTPHeaderDict(dict):
|
|||||||
vals.append(val)
|
vals.append(val)
|
||||||
else:
|
else:
|
||||||
# vals should be a tuple then, i.e. only one item so far
|
# vals should be a tuple then, i.e. only one item so far
|
||||||
if key_lower in MULTIPLE_HEADERS_ALLOWED:
|
# Need to convert the tuple to list for further extension
|
||||||
# Need to convert the tuple to list for further extension
|
_dict_setitem(self, key_lower, [vals[0], vals[1], val])
|
||||||
_dict_setitem(self, key_lower, [vals[0], vals[1], val])
|
|
||||||
else:
|
|
||||||
_dict_setitem(self, key_lower, new_vals)
|
|
||||||
|
|
||||||
def extend(*args, **kwargs):
|
def extend(*args, **kwargs):
|
||||||
"""Generic import function for any type of header-like object.
|
"""Generic import function for any type of header-like object.
|
||||||
@ -276,14 +274,17 @@ class HTTPHeaderDict(dict):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s)" % (type(self).__name__, dict(self.itermerged()))
|
return "%s(%s)" % (type(self).__name__, dict(self.itermerged()))
|
||||||
|
|
||||||
def copy(self):
|
def _copy_from(self, other):
|
||||||
clone = type(self)()
|
for key in other:
|
||||||
for key in self:
|
val = _dict_getitem(other, key)
|
||||||
val = _dict_getitem(self, key)
|
|
||||||
if isinstance(val, list):
|
if isinstance(val, list):
|
||||||
# Don't need to convert tuples
|
# Don't need to convert tuples
|
||||||
val = list(val)
|
val = list(val)
|
||||||
_dict_setitem(clone, key, val)
|
_dict_setitem(self, key, val)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
clone = type(self)()
|
||||||
|
clone._copy_from(self)
|
||||||
return clone
|
return clone
|
||||||
|
|
||||||
def iteritems(self):
|
def iteritems(self):
|
||||||
|
@ -157,3 +157,8 @@ class InsecureRequestWarning(SecurityWarning):
|
|||||||
class SystemTimeWarning(SecurityWarning):
|
class SystemTimeWarning(SecurityWarning):
|
||||||
"Warned when system time is suspected to be wrong"
|
"Warned when system time is suspected to be wrong"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InsecurePlatformWarning(SecurityWarning):
|
||||||
|
"Warned when certain SSL configuration is not available on a platform."
|
||||||
|
pass
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from binascii import hexlify, unhexlify
|
from binascii import hexlify, unhexlify
|
||||||
from hashlib import md5, sha1, sha256
|
from hashlib import md5, sha1, sha256
|
||||||
|
|
||||||
from ..exceptions import SSLError
|
from ..exceptions import SSLError, InsecurePlatformWarning
|
||||||
|
|
||||||
|
|
||||||
SSLContext = None
|
SSLContext = None
|
||||||
@ -10,6 +10,7 @@ create_default_context = None
|
|||||||
|
|
||||||
import errno
|
import errno
|
||||||
import ssl
|
import ssl
|
||||||
|
import warnings
|
||||||
|
|
||||||
try: # Test for SSL features
|
try: # Test for SSL features
|
||||||
from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
|
from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
|
||||||
@ -69,6 +70,14 @@ except ImportError:
|
|||||||
self.ciphers = cipher_suite
|
self.ciphers = cipher_suite
|
||||||
|
|
||||||
def wrap_socket(self, socket, server_hostname=None):
|
def wrap_socket(self, socket, server_hostname=None):
|
||||||
|
warnings.warn(
|
||||||
|
'A true SSLContext object is not available. This prevents '
|
||||||
|
'urllib3 from configuring SSL appropriately and may cause '
|
||||||
|
'certain SSL connections to fail. For more information, see '
|
||||||
|
'https://urllib3.readthedocs.org/en/latest/security.html'
|
||||||
|
'#insecureplatformwarning.',
|
||||||
|
InsecurePlatformWarning
|
||||||
|
)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'keyfile': self.keyfile,
|
'keyfile': self.keyfile,
|
||||||
'certfile': self.certfile,
|
'certfile': self.certfile,
|
||||||
|
@ -171,7 +171,10 @@ class SessionRedirectMixin(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
extract_cookies_to_jar(prepared_request._cookies, prepared_request, resp.raw)
|
# Extract any cookies sent on the response to the cookiejar
|
||||||
|
# in the new request. Because we've mutated our copied prepared
|
||||||
|
# request, use the old one that we haven't yet touched.
|
||||||
|
extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
|
||||||
prepared_request._cookies.update(self.cookies)
|
prepared_request._cookies.update(self.cookies)
|
||||||
prepared_request.prepare_cookies(prepared_request._cookies)
|
prepared_request.prepare_cookies(prepared_request._cookies)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user