Skip to content

Commit 3d4b6b5

Browse files
authored
Add files via upload
1 parent fa250e7 commit 3d4b6b5

File tree

7 files changed

+768
-0
lines changed

7 files changed

+768
-0
lines changed

__init__.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# urllib3/__init__.py
2+
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3+
#
4+
# This module is part of urllib3 and is released under
5+
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6+
7+
"""
8+
urllib3 - Thread-safe connection pooling and re-using.
9+
"""
10+
11+
__author__ = 'Andrey Petrov ([email protected])'
12+
__license__ = 'MIT'
13+
__version__ = '1.1'
14+
15+
16+
from .connectionpool import (
17+
HTTPConnectionPool,
18+
HTTPSConnectionPool,
19+
connection_from_url,
20+
get_host,
21+
make_headers)
22+
23+
24+
from .exceptions import (
25+
HTTPError,
26+
MaxRetryError,
27+
SSLError,
28+
TimeoutError)
29+
30+
from .poolmanager import PoolManager, ProxyManager, proxy_from_url
31+
from .response import HTTPResponse
32+
from .filepost import encode_multipart_formdata
33+
34+
35+
# Set default logging handler to avoid "No handler found" warnings.
36+
import logging
37+
try:
38+
from logging import NullHandler
39+
except ImportError:
40+
class NullHandler(logging.Handler):
41+
def emit(self, record):
42+
pass
43+
44+
logging.getLogger(__name__).addHandler(NullHandler())
45+
46+
# ... Clean up.
47+
del logging
48+
del NullHandler

_collections.py

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# urllib3/_collections.py
2+
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3+
#
4+
# This module is part of urllib3 and is released under
5+
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6+
7+
from collections import deque
8+
9+
from threading import RLock
10+
11+
__all__ = ['RecentlyUsedContainer']
12+
13+
14+
class AccessEntry(object):
15+
__slots__ = ('key', 'is_valid')
16+
17+
def __init__(self, key, is_valid=True):
18+
self.key = key
19+
self.is_valid = is_valid
20+
21+
22+
class RecentlyUsedContainer(dict):
23+
"""
24+
Provides a dict-like that maintains up to ``maxsize`` keys while throwing
25+
away the least-recently-used keys beyond ``maxsize``.
26+
"""
27+
28+
# If len(self.access_log) exceeds self._maxsize * CLEANUP_FACTOR, then we
29+
# will attempt to cleanup the invalidated entries in the access_log
30+
# datastructure during the next 'get' operation.
31+
CLEANUP_FACTOR = 10
32+
33+
def __init__(self, maxsize=10):
34+
self._maxsize = maxsize
35+
36+
self._container = {}
37+
38+
# We use a deque to to store our keys ordered by the last access.
39+
self.access_log = deque()
40+
self.access_log_lock = RLock()
41+
42+
# We look up the access log entry by the key to invalidate it so we can
43+
# insert a new authorative entry at the head without having to dig and
44+
# find the old entry for removal immediately.
45+
self.access_lookup = {}
46+
47+
# Trigger a heap cleanup when we get past this size
48+
self.access_log_limit = maxsize * self.CLEANUP_FACTOR
49+
50+
def _invalidate_entry(self, key):
51+
"If exists: Invalidate old entry and return it."
52+
old_entry = self.access_lookup.get(key)
53+
if old_entry:
54+
old_entry.is_valid = False
55+
56+
return old_entry
57+
58+
def _push_entry(self, key):
59+
"Push entry onto our access log, invalidate the old entry if exists."
60+
self._invalidate_entry(key)
61+
62+
new_entry = AccessEntry(key)
63+
self.access_lookup[key] = new_entry
64+
65+
self.access_log_lock.acquire()
66+
self.access_log.appendleft(new_entry)
67+
self.access_log_lock.release()
68+
69+
def _prune_entries(self, num):
70+
"Pop entries from our access log until we popped ``num`` valid ones."
71+
while num > 0:
72+
self.access_log_lock.acquire()
73+
p = self.access_log.pop()
74+
self.access_log_lock.release()
75+
76+
if not p.is_valid:
77+
continue # Invalidated entry, skip
78+
79+
dict.pop(self, p.key, None)
80+
self.access_lookup.pop(p.key, None)
81+
num -= 1
82+
83+
def _prune_invalidated_entries(self):
84+
"Rebuild our access_log without the invalidated entries."
85+
self.access_log_lock.acquire()
86+
self.access_log = deque(e for e in self.access_log if e.is_valid)
87+
self.access_log_lock.release()
88+
89+
def _get_ordered_access_keys(self):
90+
"Return ordered access keys for inspection. Used for testing."
91+
self.access_log_lock.acquire()
92+
r = [e.key for e in self.access_log if e.is_valid]
93+
self.access_log_lock.release()
94+
95+
return r
96+
97+
def __getitem__(self, key):
98+
item = dict.get(self, key)
99+
100+
if not item:
101+
raise KeyError(key)
102+
103+
# Insert new entry with new high priority, also implicitly invalidates
104+
# the old entry.
105+
self._push_entry(key)
106+
107+
if len(self.access_log) > self.access_log_limit:
108+
# Heap is getting too big, try to clean up any tailing invalidated
109+
# entries.
110+
self._prune_invalidated_entries()
111+
112+
return item
113+
114+
def __setitem__(self, key, item):
115+
# Add item to our container and access log
116+
dict.__setitem__(self, key, item)
117+
self._push_entry(key)
118+
119+
# Discard invalid and excess entries
120+
self._prune_entries(len(self) - self._maxsize)
121+
122+
def __delitem__(self, key):
123+
self._invalidate_entry(key)
124+
self.access_lookup.pop(key, None)
125+
dict.__delitem__(self, key)
126+
127+
def get(self, key, default=None):
128+
try:
129+
return self[key]
130+
except KeyError:
131+
return default

exceptions.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# urllib3/exceptions.py
2+
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3+
#
4+
# This module is part of urllib3 and is released under
5+
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6+
7+
## Exceptions
8+
9+
class HTTPError(Exception):
10+
"Base exception used by this module."
11+
pass
12+
13+
14+
class SSLError(Exception):
15+
"Raised when SSL certificate fails in an HTTPS connection."
16+
pass
17+
18+
19+
class MaxRetryError(HTTPError):
20+
"Raised when the maximum number of retries is exceeded."
21+
def __init__(self, url):
22+
HTTPError.__init__(self, "Max retries exceeded for url: %s" % url)
23+
self.url = url
24+
25+
26+
class TimeoutError(HTTPError):
27+
"Raised when a socket timeout occurs."
28+
pass
29+
30+
31+
class HostChangedError(HTTPError):
32+
"Raised when an existing pool gets a request for a foreign host."
33+
def __init__(self, original_host, new_url, retries=3):
34+
HTTPError.__init__(self,
35+
"Connection pool with host '%s' tried to open a foreign host: %s" %
36+
(original_host, new_url))
37+
38+
self.original_host = original_host
39+
self.new_url = new_url
40+
self.retries = retries
41+
42+
43+
class EmptyPoolError(HTTPError):
44+
"Raised when a pool runs out of connections and no more are allowed."
45+
pass

filepost.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# urllib3/filepost.py
2+
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3+
#
4+
# This module is part of urllib3 and is released under
5+
# the MIT License: http://www.opensource.org/licenses/mit-license.php
6+
7+
import codecs
8+
import mimetools
9+
import mimetypes
10+
11+
try:
12+
from cStringIO import StringIO
13+
except ImportError:
14+
from StringIO import StringIO # pylint: disable-msg=W0404
15+
16+
17+
writer = codecs.lookup('utf-8')[3]
18+
19+
20+
def get_content_type(filename):
21+
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
22+
23+
24+
def encode_multipart_formdata(fields, boundary=None):
25+
"""
26+
Encode a dictionary of ``fields`` using the multipart/form-data mime format.
27+
28+
:param fields:
29+
Dictionary of fields. The key is treated as the field name, and the
30+
value as the body of the form-data. If the value is a tuple of two
31+
elements, then the first element is treated as the filename of the
32+
form-data section.
33+
34+
:param boundary:
35+
If not specified, then a random boundary will be generated using
36+
:func:`mimetools.choose_boundary`.
37+
"""
38+
body = StringIO()
39+
if boundary is None:
40+
boundary = mimetools.choose_boundary()
41+
42+
for fieldname, value in fields.iteritems():
43+
body.write('--%s\r\n' % (boundary))
44+
45+
if isinstance(value, tuple):
46+
filename, data = value
47+
writer(body).write('Content-Disposition: form-data; name="%s"; '
48+
'filename="%s"\r\n' % (fieldname, filename))
49+
body.write('Content-Type: %s\r\n\r\n' %
50+
(get_content_type(filename)))
51+
else:
52+
data = value
53+
writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
54+
% (fieldname))
55+
body.write('Content-Type: text/plain\r\n\r\n')
56+
57+
if isinstance(data, int):
58+
data = str(data) # Backwards compatibility
59+
60+
if isinstance(data, unicode):
61+
writer(body).write(data)
62+
else:
63+
body.write(data)
64+
65+
body.write('\r\n')
66+
67+
body.write('--%s--\r\n' % (boundary))
68+
69+
content_type = 'multipart/form-data; boundary=%s' % boundary
70+
71+
return body.getvalue(), content_type

0 commit comments

Comments
 (0)