You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			562 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			562 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
"""
 | 
						|
requests.cookies
 | 
						|
~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
 | 
						|
 | 
						|
requests.utils imports from here, so be careful with imports.
 | 
						|
"""
 | 
						|
 | 
						|
import calendar
 | 
						|
import copy
 | 
						|
import time
 | 
						|
 | 
						|
from ._internal_utils import to_native_string
 | 
						|
from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse
 | 
						|
 | 
						|
try:
 | 
						|
    import threading
 | 
						|
except ImportError:
 | 
						|
    import dummy_threading as threading
 | 
						|
 | 
						|
 | 
						|
class MockRequest:
 | 
						|
    """Wraps a `requests.Request` to mimic a `urllib2.Request`.
 | 
						|
 | 
						|
    The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
 | 
						|
    manage cookie policies, i.e., determine whether a cookie can be set, given the
 | 
						|
    domains of the request and the cookie.
 | 
						|
 | 
						|
    The original request object is read-only. The client is responsible for collecting
 | 
						|
    the new headers via `get_new_headers()` and interpreting them appropriately. You
 | 
						|
    probably want `get_cookie_header`, defined below.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, request):
 | 
						|
        self._r = request
 | 
						|
        self._new_headers = {}
 | 
						|
        self.type = urlparse(self._r.url).scheme
 | 
						|
 | 
						|
    def get_type(self):
 | 
						|
        return self.type
 | 
						|
 | 
						|
    def get_host(self):
 | 
						|
        return urlparse(self._r.url).netloc
 | 
						|
 | 
						|
    def get_origin_req_host(self):
 | 
						|
        return self.get_host()
 | 
						|
 | 
						|
    def get_full_url(self):
 | 
						|
        # Only return the response's URL if the user hadn't set the Host
 | 
						|
        # header
 | 
						|
        if not self._r.headers.get("Host"):
 | 
						|
            return self._r.url
 | 
						|
        # If they did set it, retrieve it and reconstruct the expected domain
 | 
						|
        host = to_native_string(self._r.headers["Host"], encoding="utf-8")
 | 
						|
        parsed = urlparse(self._r.url)
 | 
						|
        # Reconstruct the URL as we expect it
 | 
						|
        return urlunparse(
 | 
						|
            [
 | 
						|
                parsed.scheme,
 | 
						|
                host,
 | 
						|
                parsed.path,
 | 
						|
                parsed.params,
 | 
						|
                parsed.query,
 | 
						|
                parsed.fragment,
 | 
						|
            ]
 | 
						|
        )
 | 
						|
 | 
						|
    def is_unverifiable(self):
 | 
						|
        return True
 | 
						|
 | 
						|
    def has_header(self, name):
 | 
						|
        return name in self._r.headers or name in self._new_headers
 | 
						|
 | 
						|
    def get_header(self, name, default=None):
 | 
						|
        return self._r.headers.get(name, self._new_headers.get(name, default))
 | 
						|
 | 
						|
    def add_header(self, key, val):
 | 
						|
        """cookiejar has no legitimate use for this method; add it back if you find one."""
 | 
						|
        raise NotImplementedError(
 | 
						|
            "Cookie headers should be added with add_unredirected_header()"
 | 
						|
        )
 | 
						|
 | 
						|
    def add_unredirected_header(self, name, value):
 | 
						|
        self._new_headers[name] = value
 | 
						|
 | 
						|
    def get_new_headers(self):
 | 
						|
        return self._new_headers
 | 
						|
 | 
						|
    @property
 | 
						|
    def unverifiable(self):
 | 
						|
        return self.is_unverifiable()
 | 
						|
 | 
						|
    @property
 | 
						|
    def origin_req_host(self):
 | 
						|
        return self.get_origin_req_host()
 | 
						|
 | 
						|
    @property
 | 
						|
    def host(self):
 | 
						|
        return self.get_host()
 | 
						|
 | 
						|
 | 
						|
class MockResponse:
 | 
						|
    """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
 | 
						|
 | 
						|
    ...what? Basically, expose the parsed HTTP headers from the server response
 | 
						|
    the way `http.cookiejar` expects to see them.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, headers):
 | 
						|
        """Make a MockResponse for `cookiejar` to read.
 | 
						|
 | 
						|
        :param headers: a httplib.HTTPMessage or analogous carrying the headers
 | 
						|
        """
 | 
						|
        self._headers = headers
 | 
						|
 | 
						|
    def info(self):
 | 
						|
        return self._headers
 | 
						|
 | 
						|
    def getheaders(self, name):
 | 
						|
        self._headers.getheaders(name)
 | 
						|
 | 
						|
 | 
						|
def extract_cookies_to_jar(jar, request, response):
 | 
						|
    """Extract the cookies from the response into a CookieJar.
 | 
						|
 | 
						|
    :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
 | 
						|
    :param request: our own requests.Request object
 | 
						|
    :param response: urllib3.HTTPResponse object
 | 
						|
    """
 | 
						|
    if not (hasattr(response, "_original_response") and response._original_response):
 | 
						|
        return
 | 
						|
    # the _original_response field is the wrapped httplib.HTTPResponse object,
 | 
						|
    req = MockRequest(request)
 | 
						|
    # pull out the HTTPMessage with the headers and put it in the mock:
 | 
						|
    res = MockResponse(response._original_response.msg)
 | 
						|
    jar.extract_cookies(res, req)
 | 
						|
 | 
						|
 | 
						|
def get_cookie_header(jar, request):
 | 
						|
    """
 | 
						|
    Produce an appropriate Cookie header string to be sent with `request`, or None.
 | 
						|
 | 
						|
    :rtype: str
 | 
						|
    """
 | 
						|
    r = MockRequest(request)
 | 
						|
    jar.add_cookie_header(r)
 | 
						|
    return r.get_new_headers().get("Cookie")
 | 
						|
 | 
						|
 | 
						|
def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
 | 
						|
    """Unsets a cookie by name, by default over all domains and paths.
 | 
						|
 | 
						|
    Wraps CookieJar.clear(), is O(n).
 | 
						|
    """
 | 
						|
    clearables = []
 | 
						|
    for cookie in cookiejar:
 | 
						|
        if cookie.name != name:
 | 
						|
            continue
 | 
						|
        if domain is not None and domain != cookie.domain:
 | 
						|
            continue
 | 
						|
        if path is not None and path != cookie.path:
 | 
						|
            continue
 | 
						|
        clearables.append((cookie.domain, cookie.path, cookie.name))
 | 
						|
 | 
						|
    for domain, path, name in clearables:
 | 
						|
        cookiejar.clear(domain, path, name)
 | 
						|
 | 
						|
 | 
						|
class CookieConflictError(RuntimeError):
 | 
						|
    """There are two cookies that meet the criteria specified in the cookie jar.
 | 
						|
    Use .get and .set and include domain and path args in order to be more specific.
 | 
						|
    """
 | 
						|
 | 
						|
 | 
						|
class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
 | 
						|
    """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
 | 
						|
    interface.
 | 
						|
 | 
						|
    This is the CookieJar we create by default for requests and sessions that
 | 
						|
    don't specify one, since some clients may expect response.cookies and
 | 
						|
    session.cookies to support dict operations.
 | 
						|
 | 
						|
    Requests does not use the dict interface internally; it's just for
 | 
						|
    compatibility with external client code. All requests code should work
 | 
						|
    out of the box with externally provided instances of ``CookieJar``, e.g.
 | 
						|
    ``LWPCookieJar`` and ``FileCookieJar``.
 | 
						|
 | 
						|
    Unlike a regular CookieJar, this class is pickleable.
 | 
						|
 | 
						|
    .. warning:: dictionary operations that are normally O(1) may be O(n).
 | 
						|
    """
 | 
						|
 | 
						|
    def get(self, name, default=None, domain=None, path=None):
 | 
						|
        """Dict-like get() that also supports optional domain and path args in
 | 
						|
        order to resolve naming collisions from using one cookie jar over
 | 
						|
        multiple domains.
 | 
						|
 | 
						|
        .. warning:: operation is O(n), not O(1).
 | 
						|
        """
 | 
						|
        try:
 | 
						|
            return self._find_no_duplicates(name, domain, path)
 | 
						|
        except KeyError:
 | 
						|
            return default
 | 
						|
 | 
						|
    def set(self, name, value, **kwargs):
 | 
						|
        """Dict-like set() that also supports optional domain and path args in
 | 
						|
        order to resolve naming collisions from using one cookie jar over
 | 
						|
        multiple domains.
 | 
						|
        """
 | 
						|
        # support client code that unsets cookies by assignment of a None value:
 | 
						|
        if value is None:
 | 
						|
            remove_cookie_by_name(
 | 
						|
                self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
 | 
						|
            )
 | 
						|
            return
 | 
						|
 | 
						|
        if isinstance(value, Morsel):
 | 
						|
            c = morsel_to_cookie(value)
 | 
						|
        else:
 | 
						|
            c = create_cookie(name, value, **kwargs)
 | 
						|
        self.set_cookie(c)
 | 
						|
        return c
 | 
						|
 | 
						|
    def iterkeys(self):
 | 
						|
        """Dict-like iterkeys() that returns an iterator of names of cookies
 | 
						|
        from the jar.
 | 
						|
 | 
						|
        .. seealso:: itervalues() and iteritems().
 | 
						|
        """
 | 
						|
        for cookie in iter(self):
 | 
						|
            yield cookie.name
 | 
						|
 | 
						|
    def keys(self):
 | 
						|
        """Dict-like keys() that returns a list of names of cookies from the
 | 
						|
        jar.
 | 
						|
 | 
						|
        .. seealso:: values() and items().
 | 
						|
        """
 | 
						|
        return list(self.iterkeys())
 | 
						|
 | 
						|
    def itervalues(self):
 | 
						|
        """Dict-like itervalues() that returns an iterator of values of cookies
 | 
						|
        from the jar.
 | 
						|
 | 
						|
        .. seealso:: iterkeys() and iteritems().
 | 
						|
        """
 | 
						|
        for cookie in iter(self):
 | 
						|
            yield cookie.value
 | 
						|
 | 
						|
    def values(self):
 | 
						|
        """Dict-like values() that returns a list of values of cookies from the
 | 
						|
        jar.
 | 
						|
 | 
						|
        .. seealso:: keys() and items().
 | 
						|
        """
 | 
						|
        return list(self.itervalues())
 | 
						|
 | 
						|
    def iteritems(self):
 | 
						|
        """Dict-like iteritems() that returns an iterator of name-value tuples
 | 
						|
        from the jar.
 | 
						|
 | 
						|
        .. seealso:: iterkeys() and itervalues().
 | 
						|
        """
 | 
						|
        for cookie in iter(self):
 | 
						|
            yield cookie.name, cookie.value
 | 
						|
 | 
						|
    def items(self):
 | 
						|
        """Dict-like items() that returns a list of name-value tuples from the
 | 
						|
        jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
 | 
						|
        vanilla python dict of key value pairs.
 | 
						|
 | 
						|
        .. seealso:: keys() and values().
 | 
						|
        """
 | 
						|
        return list(self.iteritems())
 | 
						|
 | 
						|
    def list_domains(self):
 | 
						|
        """Utility method to list all the domains in the jar."""
 | 
						|
        domains = []
 | 
						|
        for cookie in iter(self):
 | 
						|
            if cookie.domain not in domains:
 | 
						|
                domains.append(cookie.domain)
 | 
						|
        return domains
 | 
						|
 | 
						|
    def list_paths(self):
 | 
						|
        """Utility method to list all the paths in the jar."""
 | 
						|
        paths = []
 | 
						|
        for cookie in iter(self):
 | 
						|
            if cookie.path not in paths:
 | 
						|
                paths.append(cookie.path)
 | 
						|
        return paths
 | 
						|
 | 
						|
    def multiple_domains(self):
 | 
						|
        """Returns True if there are multiple domains in the jar.
 | 
						|
        Returns False otherwise.
 | 
						|
 | 
						|
        :rtype: bool
 | 
						|
        """
 | 
						|
        domains = []
 | 
						|
        for cookie in iter(self):
 | 
						|
            if cookie.domain is not None and cookie.domain in domains:
 | 
						|
                return True
 | 
						|
            domains.append(cookie.domain)
 | 
						|
        return False  # there is only one domain in jar
 | 
						|
 | 
						|
    def get_dict(self, domain=None, path=None):
 | 
						|
        """Takes as an argument an optional domain and path and returns a plain
 | 
						|
        old Python dict of name-value pairs of cookies that meet the
 | 
						|
        requirements.
 | 
						|
 | 
						|
        :rtype: dict
 | 
						|
        """
 | 
						|
        dictionary = {}
 | 
						|
        for cookie in iter(self):
 | 
						|
            if (domain is None or cookie.domain == domain) and (
 | 
						|
                path is None or cookie.path == path
 | 
						|
            ):
 | 
						|
                dictionary[cookie.name] = cookie.value
 | 
						|
        return dictionary
 | 
						|
 | 
						|
    def __contains__(self, name):
 | 
						|
        try:
 | 
						|
            return super().__contains__(name)
 | 
						|
        except CookieConflictError:
 | 
						|
            return True
 | 
						|
 | 
						|
    def __getitem__(self, name):
 | 
						|
        """Dict-like __getitem__() for compatibility with client code. Throws
 | 
						|
        exception if there are more than one cookie with name. In that case,
 | 
						|
        use the more explicit get() method instead.
 | 
						|
 | 
						|
        .. warning:: operation is O(n), not O(1).
 | 
						|
        """
 | 
						|
        return self._find_no_duplicates(name)
 | 
						|
 | 
						|
    def __setitem__(self, name, value):
 | 
						|
        """Dict-like __setitem__ for compatibility with client code. Throws
 | 
						|
        exception if there is already a cookie of that name in the jar. In that
 | 
						|
        case, use the more explicit set() method instead.
 | 
						|
        """
 | 
						|
        self.set(name, value)
 | 
						|
 | 
						|
    def __delitem__(self, name):
 | 
						|
        """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
 | 
						|
        ``remove_cookie_by_name()``.
 | 
						|
        """
 | 
						|
        remove_cookie_by_name(self, name)
 | 
						|
 | 
						|
    def set_cookie(self, cookie, *args, **kwargs):
 | 
						|
        if (
 | 
						|
            hasattr(cookie.value, "startswith")
 | 
						|
            and cookie.value.startswith('"')
 | 
						|
            and cookie.value.endswith('"')
 | 
						|
        ):
 | 
						|
            cookie.value = cookie.value.replace('\\"', "")
 | 
						|
        return super().set_cookie(cookie, *args, **kwargs)
 | 
						|
 | 
						|
    def update(self, other):
 | 
						|
        """Updates this jar with cookies from another CookieJar or dict-like"""
 | 
						|
        if isinstance(other, cookielib.CookieJar):
 | 
						|
            for cookie in other:
 | 
						|
                self.set_cookie(copy.copy(cookie))
 | 
						|
        else:
 | 
						|
            super().update(other)
 | 
						|
 | 
						|
    def _find(self, name, domain=None, path=None):
 | 
						|
        """Requests uses this method internally to get cookie values.
 | 
						|
 | 
						|
        If there are conflicting cookies, _find arbitrarily chooses one.
 | 
						|
        See _find_no_duplicates if you want an exception thrown if there are
 | 
						|
        conflicting cookies.
 | 
						|
 | 
						|
        :param name: a string containing name of cookie
 | 
						|
        :param domain: (optional) string containing domain of cookie
 | 
						|
        :param path: (optional) string containing path of cookie
 | 
						|
        :return: cookie.value
 | 
						|
        """
 | 
						|
        for cookie in iter(self):
 | 
						|
            if cookie.name == name:
 | 
						|
                if domain is None or cookie.domain == domain:
 | 
						|
                    if path is None or cookie.path == path:
 | 
						|
                        return cookie.value
 | 
						|
 | 
						|
        raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
 | 
						|
 | 
						|
    def _find_no_duplicates(self, name, domain=None, path=None):
 | 
						|
        """Both ``__get_item__`` and ``get`` call this function: it's never
 | 
						|
        used elsewhere in Requests.
 | 
						|
 | 
						|
        :param name: a string containing name of cookie
 | 
						|
        :param domain: (optional) string containing domain of cookie
 | 
						|
        :param path: (optional) string containing path of cookie
 | 
						|
        :raises KeyError: if cookie is not found
 | 
						|
        :raises CookieConflictError: if there are multiple cookies
 | 
						|
            that match name and optionally domain and path
 | 
						|
        :return: cookie.value
 | 
						|
        """
 | 
						|
        toReturn = None
 | 
						|
        for cookie in iter(self):
 | 
						|
            if cookie.name == name:
 | 
						|
                if domain is None or cookie.domain == domain:
 | 
						|
                    if path is None or cookie.path == path:
 | 
						|
                        if toReturn is not None:
 | 
						|
                            # if there are multiple cookies that meet passed in criteria
 | 
						|
                            raise CookieConflictError(
 | 
						|
                                f"There are multiple cookies with name, {name!r}"
 | 
						|
                            )
 | 
						|
                        # we will eventually return this as long as no cookie conflict
 | 
						|
                        toReturn = cookie.value
 | 
						|
 | 
						|
        if toReturn:
 | 
						|
            return toReturn
 | 
						|
        raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
 | 
						|
 | 
						|
    def __getstate__(self):
 | 
						|
        """Unlike a normal CookieJar, this class is pickleable."""
 | 
						|
        state = self.__dict__.copy()
 | 
						|
        # remove the unpickleable RLock object
 | 
						|
        state.pop("_cookies_lock")
 | 
						|
        return state
 | 
						|
 | 
						|
    def __setstate__(self, state):
 | 
						|
        """Unlike a normal CookieJar, this class is pickleable."""
 | 
						|
        self.__dict__.update(state)
 | 
						|
        if "_cookies_lock" not in self.__dict__:
 | 
						|
            self._cookies_lock = threading.RLock()
 | 
						|
 | 
						|
    def copy(self):
 | 
						|
        """Return a copy of this RequestsCookieJar."""
 | 
						|
        new_cj = RequestsCookieJar()
 | 
						|
        new_cj.set_policy(self.get_policy())
 | 
						|
        new_cj.update(self)
 | 
						|
        return new_cj
 | 
						|
 | 
						|
    def get_policy(self):
 | 
						|
        """Return the CookiePolicy instance used."""
 | 
						|
        return self._policy
 | 
						|
 | 
						|
 | 
						|
def _copy_cookie_jar(jar):
 | 
						|
    if jar is None:
 | 
						|
        return None
 | 
						|
 | 
						|
    if hasattr(jar, "copy"):
 | 
						|
        # We're dealing with an instance of RequestsCookieJar
 | 
						|
        return jar.copy()
 | 
						|
    # We're dealing with a generic CookieJar instance
 | 
						|
    new_jar = copy.copy(jar)
 | 
						|
    new_jar.clear()
 | 
						|
    for cookie in jar:
 | 
						|
        new_jar.set_cookie(copy.copy(cookie))
 | 
						|
    return new_jar
 | 
						|
 | 
						|
 | 
						|
def create_cookie(name, value, **kwargs):
 | 
						|
    """Make a cookie from underspecified parameters.
 | 
						|
 | 
						|
    By default, the pair of `name` and `value` will be set for the domain ''
 | 
						|
    and sent on every request (this is sometimes called a "supercookie").
 | 
						|
    """
 | 
						|
    result = {
 | 
						|
        "version": 0,
 | 
						|
        "name": name,
 | 
						|
        "value": value,
 | 
						|
        "port": None,
 | 
						|
        "domain": "",
 | 
						|
        "path": "/",
 | 
						|
        "secure": False,
 | 
						|
        "expires": None,
 | 
						|
        "discard": True,
 | 
						|
        "comment": None,
 | 
						|
        "comment_url": None,
 | 
						|
        "rest": {"HttpOnly": None},
 | 
						|
        "rfc2109": False,
 | 
						|
    }
 | 
						|
 | 
						|
    badargs = set(kwargs) - set(result)
 | 
						|
    if badargs:
 | 
						|
        raise TypeError(
 | 
						|
            f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
 | 
						|
        )
 | 
						|
 | 
						|
    result.update(kwargs)
 | 
						|
    result["port_specified"] = bool(result["port"])
 | 
						|
    result["domain_specified"] = bool(result["domain"])
 | 
						|
    result["domain_initial_dot"] = result["domain"].startswith(".")
 | 
						|
    result["path_specified"] = bool(result["path"])
 | 
						|
 | 
						|
    return cookielib.Cookie(**result)
 | 
						|
 | 
						|
 | 
						|
def morsel_to_cookie(morsel):
 | 
						|
    """Convert a Morsel object into a Cookie containing the one k/v pair."""
 | 
						|
 | 
						|
    expires = None
 | 
						|
    if morsel["max-age"]:
 | 
						|
        try:
 | 
						|
            expires = int(time.time() + int(morsel["max-age"]))
 | 
						|
        except ValueError:
 | 
						|
            raise TypeError(f"max-age: {morsel['max-age']} must be integer")
 | 
						|
    elif morsel["expires"]:
 | 
						|
        time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
 | 
						|
        expires = calendar.timegm(time.strptime(morsel["expires"], time_template))
 | 
						|
    return create_cookie(
 | 
						|
        comment=morsel["comment"],
 | 
						|
        comment_url=bool(morsel["comment"]),
 | 
						|
        discard=False,
 | 
						|
        domain=morsel["domain"],
 | 
						|
        expires=expires,
 | 
						|
        name=morsel.key,
 | 
						|
        path=morsel["path"],
 | 
						|
        port=None,
 | 
						|
        rest={"HttpOnly": morsel["httponly"]},
 | 
						|
        rfc2109=False,
 | 
						|
        secure=bool(morsel["secure"]),
 | 
						|
        value=morsel.value,
 | 
						|
        version=morsel["version"] or 0,
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
 | 
						|
    """Returns a CookieJar from a key/value dictionary.
 | 
						|
 | 
						|
    :param cookie_dict: Dict of key/values to insert into CookieJar.
 | 
						|
    :param cookiejar: (optional) A cookiejar to add the cookies to.
 | 
						|
    :param overwrite: (optional) If False, will not replace cookies
 | 
						|
        already in the jar with new ones.
 | 
						|
    :rtype: CookieJar
 | 
						|
    """
 | 
						|
    if cookiejar is None:
 | 
						|
        cookiejar = RequestsCookieJar()
 | 
						|
 | 
						|
    if cookie_dict is not None:
 | 
						|
        names_from_jar = [cookie.name for cookie in cookiejar]
 | 
						|
        for name in cookie_dict:
 | 
						|
            if overwrite or (name not in names_from_jar):
 | 
						|
                cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
 | 
						|
 | 
						|
    return cookiejar
 | 
						|
 | 
						|
 | 
						|
def merge_cookies(cookiejar, cookies):
 | 
						|
    """Add cookies to cookiejar and returns a merged CookieJar.
 | 
						|
 | 
						|
    :param cookiejar: CookieJar object to add the cookies to.
 | 
						|
    :param cookies: Dictionary or CookieJar object to be added.
 | 
						|
    :rtype: CookieJar
 | 
						|
    """
 | 
						|
    if not isinstance(cookiejar, cookielib.CookieJar):
 | 
						|
        raise ValueError("You can only merge into CookieJar")
 | 
						|
 | 
						|
    if isinstance(cookies, dict):
 | 
						|
        cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False)
 | 
						|
    elif isinstance(cookies, cookielib.CookieJar):
 | 
						|
        try:
 | 
						|
            cookiejar.update(cookies)
 | 
						|
        except AttributeError:
 | 
						|
            for cookie_in_jar in cookies:
 | 
						|
                cookiejar.set_cookie(cookie_in_jar)
 | 
						|
 | 
						|
    return cookiejar
 |