while from_date <= to_date:
            data = {}
            data['uniqid'] = search['uniqid']
            data['zip'] = zipcode
            data['year'] = from_date.year
            data['month'] = from_date.month
            data['day'] = from_date.day
            if dist and len(dist) > 0:
                wdata = self.search_weather_data(dist, data)
                data.update(wdata)
            results.append(data)
            from_date += timedelta(days=1)

import os
import urllib
import re
import time
import math
import sqlite3
import sys
import gzip
import logging
import csv

from datetime import datetime, timedelta
from pkg_resources import resource_filename

try:
    from urllib.request import urlopen
    from urllib.error import HTTPError
except ImportError:
    from urllib2 import urlopen, HTTPError

"""Constants
"""
nauticalMilePerLat = 60.00721
nauticalMilePerLongitude = 60.10793
rad = math.pi / 180.0
metersPerNauticalMile = 1852

STATION_INFO_COLS = ["sid", "type", "name", "lat", "lon", "nth", "distance"]

USAF_WBAN_DATA = [('TEMP', 24, 30), ('DEWP', 35, 41), ('SLP', 46, 52),
                  ('STP', 57, 63), ('VISIB', 68, 73), ('AWND', 78, 83),
                  ('MXSPD', 88, 93), ('GUST', 95, 100), ('TMAX', 102, 108),
                  ('MAXF', 108, 109), ('TMIN', 110, 116), ('MINF', 116, 117),
                  ('PRCP', 118, 123), ('PRCPF', 123, 124), ('SNWD', 125, 130),
                  ('FRSHTT', 132, 138)]

def download(url, local):
    print("Downloading '{:s}'...".format(url))
    start = time.time()
    response = urlopen(url)
    content = response.read()
    with open(local, 'wb') as f:
        f.write(content)
    elapse = time.time() - start
    print("Elapse time: {:f} seconds".format(elapse))

def build_ghcn_database(args, year, dbname):
    conn = None
    try:
        local = os.path.join(args.dbpath, "{:s}.csv.gz".format(year))
        if not os.path.exists(local):
            if not os.path.exists(args.dbpath):
                os.makedirs(args.dbpath)
            url = "ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/daily/by_year/{:s}.csv.gz".format(year)
            download(url, local)
        conn = sqlite3.connect(dbname)
        c = conn.cursor()
        c.execute("""CREATE TABLE IF NOT EXISTS `ghcn_{:s}` (
`id` VARCHAR(12) NOT NULL,
`date` VARCHAR(8) NOT NULL,
`element` VARCHAR(4) NULL,
`value` VARCHAR(6) NULL,
`m_flag` VARCHAR(1) NULL,
`q_flag` VARCHAR(1) NULL,
`s_flag` VARCHAR(1) NULL,
`obs_time` VARCHAR(4) NULL)""".format(year))

        c.execute("""create index if not exists idx_id_time
on ghcn_{:s} (id, date)""".format(year))

        c.execute("PRAGMA journal_mode = OFF")
        c.execute("PRAGMA synchronous = OFF")
        # Fastest way if no memory limit
        c.execute("PRAGMA cache_size = 1000000")
        print("Importing...")
        start = time.time()
        with gzip.open(local, 'rb') as f:
            reader = csv.reader(f)
            c.executemany("""INSERT OR IGNORE INTO ghcn_{:s}
(id, date, element, value, m_flag, q_flag, s_flag, obs_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""".format(year), reader)
        elapse = time.time() - start
        print("Import time: {:f} seconds".format(elapse))
    except Exception as e:
        logging.error(str(e))
        raise
    finally:
        if conn:
            print("Commiting...")
            start = time.time()
            conn.commit()
            conn.close()
            elapse = time.time() - start
            print("Commit time: {:f} seconds".format(elapse))

class WeatherByZip(object):

    not_found_list = []

    def get_stations(self):
        """Returns all stations from database
        """
        conn = sqlite3.connect(self.args.zip2ws_db)
        c = conn.cursor()
        c.execute("select rowid, id, name, lat, lon, type from stations")
        stations = []
        for r in c:
            stations.append(r)
        conn.close()
        return stations

    def __init__(self, args):
        self.args = args
        try:
            with open(args.columns, 'rb') as f:
                self.output_columns = [r.strip() for r in f.readlines()
                                       if r[0] != '#']
        except:
            args.columns = resource_filename(__name__, args.columns)
            with open(args.columns, 'rb') as f:
                self.output_columns = [r.strip() for r in f.readlines()
                                       if r[0] != '#']

        self.stations = self.get_stations()
        logging.info('Number of Stations = {:d}'.format(len(self.stations)))

    def search_weather_data(self, dist, data):
        year = "{:04d}" .format(data['year'])
        month = "{:02d}".format(data['month'])
        day = "{:02d}".format(data['day'])
        logging.debug("Date: {:s}/{:s}/{:s}".format(year, month, day))
        found = 0
        nth = 0
        raw = []
        values = {}
        current_year = ""
        orders = self.output_columns
        for s in dist:
            if self.args.nth > 0 and nth >= self.args.nth:
                logging.info("Reach maximum n-th stations: {:d}".format(nth))
                return values
            if self.args.distance > 0 and s[0] >= self.args.distance * 1000:
                logging.info("Reach maximum distance: {:d}"
                             .format(self.args.distance))
                return values
            nth += 1
            values['nth'] = nth
            sid = s[1]
            stype = s[2]
            values['sid'] = sid
            values['type'] = stype
            values['name'] = s[3]
            values['lat'] = s[4]
            values['lon'] = s[5]
            values['distance'] = s[0]
            if stype == 'GHCND':
                if not self.args.uses_sqlite:
                    datadir = './data/ghcn-daily/all/'
                    if not os.path.exists(datadir):
                        os.makedirs(datadir)
                    datafile = datadir + '{:s}.dly'.format(sid)
                    urlfile = ('http://example.com/'
                               'all/{:s}.dly'.format(sid))
                    if not self.download_data_file(urlfile, datafile):
                        continue
                    search = sid + year + month
                    match = False
                    with open(datafile, 'rb') as df:
                        for l in df:
                            if (l[:17] == search):
                                element = l[17:21]
                                offset = 21 + int(day) * 8 - 8
                                value = l[offset:offset + 5]
                                mflag = l[offset + 5:offset + 6]
                                qflag = l[offset + 6:offset + 7]
                                sflag = l[offset + 7:offset + 8]
                                logging.debug("<{:s}> <{:s}> <{:s}> <{:s}>"
                                              " <{:s}>".format(element, value,
                                                               mflag, qflag,
                                                               sflag))
                                match = True
                                if value != '-9999' and (element in orders):
                                    if not values.get(element):
                                        values[element] = value
                                        found += 1
                                        raw.append(l)
                            elif match:
                                break
                else:
                    if current_year != year:
                        current_year = year
                        dbname = os.path.join(self.args.dbpath,
                                              "ghcn_{:s}.sqlite3".format(year))
                        if not os.path.exists(dbname):
                            build_ghcn_database(self.args, year, dbname)
                        conn2 = sqlite3.connect(dbname)
                        c2 = conn2.cursor()
                        # FIXME: try to optimize with these options
                        c2.execute("PRAGMA journal_mode = MEMORY")
                        c2.execute("PRAGMA synchronous = OFF")
                        c2.execute("PRAGMA temp_store = MEMORY")
                        c2.execute("PRAGMA cache_size = 500000")
                    ghcn_values = self.get_ghcn_data(c2, sid, year, month, day)
                    for element in ghcn_values:
                        value = ghcn_values[element]
                        if value != '-9999' and (element in orders):
                            if not values.get(element):
                                found += 1
                                values[element] = value
            elif stype == 'USAF-WBAN':
                datadir = './data/gsod/{:s}/'.format(year)
                if not os.path.exists(datadir):
                    os.makedirs(datadir)
                datafile = datadir + '{:s}-{:s}.op.gz'.format(sid, year)
                urlfile = ('http://example.com/'
                           '{:s}/{:s}-{:s}.op.gz'.format(year, sid, year))
                if not self.download_data_file(urlfile, datafile):
                    continue
                search = year + month + day
                with gzip.open(datafile, 'rb') as df:
                    for l in df:
                        if l[14:22] == search:
                            match = False
                            for d in USAF_WBAN_DATA:
                                element = d[0]
                                value = l[d[1]:d[2]]
                                if element in ['TMAX', 'TMIN']:
                                    if value != '9999.9':
                                        value = self.f2c(float(value)) * 10
                                elif element in ['AWND']:
                                    if value != '999.9':
                                        value = self.kn2ms(float(value)) * 10
                                if (not values.get(element) and
                                   (element in orders)):
                                    found += 1
                                    values[element] = value
                                    match = True
                            if match:
                                raw.append(l)
                            break
            if found >= len(orders):
                break
        logging.debug(values)
        # For debug only
        return values

    def search(self, search):
        zipcode = '0' * (5 - len(search['zip'])) + search['zip']

        logging.info("Search for: '{:s}'".format(zipcode))

        from_date = datetime(year=search['from.year'],
                             month=search['from.month'],
                             day=search['from.day'])
        to_date = datetime(year=search['to.year'],
                           month=search['to.month'],
                           day=search['to.day'])
        columns = (['uniqid', 'zip', 'year', 'month', 'day'] +
                   STATION_INFO_COLS + self.output_columns)
        results = []
        conn = sqlite3.connect(self.args.zip2ws_db)
        c = conn.cursor()
        c.execute('select zipcode, lat, lon, gm_lat, gm_lon, rowid from zip'
                  ' where zipcode = ?', (zipcode,))
        r = c.fetchone()
        dist = self.sort_stations(zipcode, r, self.stations)

        return results

    def download_data_file(self, url, file):
        """Download weather data file from server if it not exist in local directory
        """
        if url in self.not_found_list:
            logging.warn("This URL no data on server {:s}".format(url))
            return False
        if not os.path.exists(file):
            retry = 0
            while True:
                try:
                    logging.info("Downloading '{:s}'".format(url))
                    urllib.urlretrieve(url, file)
                    return True
                except Exception as e:
                    m = re.match(".*\s(\d\d\d)\s.*", str(e))
                    if m:
                        logging.warn("FTP server error code = {:s}"
                                     .format(m.group(1)))
                        if m.group(1) == '550':
                            self.not_found_list.append(url)
                            return False
                    logging.warn("Unknown FTP server error = {:s}"
                                 .format(str(e)))
                    if retry < 5:
                        retry += 1
                        logging.info("Retry #{:d}: waiting...({:d}s)"
                                     .format(retry, retry * 10))
                        time.sleep(retry * 10)
                    else:
                        logging.warn("Cannot download data from URL = {:s}"
                                     .format(url))
                        return False
        return True

    def metersGeoDistance(self, lat1, lon1, lat2, lon2):
        """Returns calculate distance between two lat lons in meters
        """
        yDistance = (lat2 - lat1) * nauticalMilePerLat
        xDistance = ((math.cos(lat1 * rad) + math.cos(lat2 * rad)) *
                     (lon2 - lon1) * (nauticalMilePerLongitude / 2))

        distance = math.sqrt(yDistance**2 + xDistance**2)

        return distance * metersPerNauticalMile

    def f2c(self, f):
        """Convert Fahrenheit to Celsius
        """
        return (f - 32) * 5.0/9.0

    def kn2ms(self, kn):
        """Convert Knots to m/s
        """
        return 0.51444 * kn

    def sort_stations(self, zipcode, r, stations):
        """Returns sorted stations list by distance
        """
        if r is None:
            logging.warn("zipcode = {:s} not found".format(zipcode))
            return None
        lat1 = r[3]
        if lat1 is None:
            lat1 = r[1]
        lon1 = r[4]
        if lon1 is None:
            lon1 = r[2]
        if lat1 == '' or lon1 == '':
            logging.warn("not lat/lon for zipcode = {:s}".format(zipcode))
            return None
        lat1 = float(lat1)
        lon1 = float(lon1)
        dist = []
        for s in stations:
            id = s[1]
            name = s[2]
            stype = s[5]
            if s[3] is None or s[4] is None:
                continue
            lat2 = float(s[3])
            lon2 = float(s[4])
            try:
                distance = self.metersGeoDistance(lat1, lon1, lat2, lon2)
            except:
                distance = sys.maxint
            dist.append((int(distance), id, stype, name, lat2, lon2))
        return sorted(dist)

    def get_ghcn_data(self, cursor, sid, year, month, day):
        cursor.execute("select * from ghcn_{:s} where id = ? and date = ?"
                       .format(year), (sid, '{:02d}{:02d}{:02d}'
                                       .format(int(year), int(month),
                                               int(day))))
        values = {}
        for r in cursor:
            values[r[2]] = r[3]
        return values


return get_ctxnet(num_classes=num_classes, model_name="ctxnet_cityscapes", **kwargs)

"""
    ContextNet for image segmentation, implemented in PyTorch.
    Original paper: 'ContextNet: Exploring Context and Detail for Semantic Segmentation in Real-time,'
    http://example.com.
"""

__all__ = ['ContextNet', 'ctxnet_cityscapes']

import os
import torch
import torch.nn as nn
from .common import conv1x1, conv1x1_block, conv3x3_block, dwconv3x3_block, dwsconv3x3_block, InterpolationBlock

class CtxShallowNet(nn.Module):
    """
    ContextNet specific shallow net (spatial detail encoder).

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    mid1_channels : int
        Number of middle #1 channels.
    mid2_channels : int
        Number of middle #2 channels.
    out_channels : int
        Number of output channels.
    """
    def __init__(self,
                 in_channels,
                 mid1_channels,
                 mid2_channels,
                 out_channels):
        super(CtxShallowNet, self).__init__()
        self.conv1 = conv3x3_block(
            in_channels=in_channels,
            out_channels=mid1_channels,
            stride=2,
            padding=0)
        self.conv2 = dwsconv3x3_block(
            in_channels=mid1_channels,
            out_channels=mid2_channels,
            stride=2)
        self.conv3 = dwsconv3x3_block(
            in_channels=mid2_channels,
            out_channels=out_channels,
            stride=2)
        self.conv4 = dwsconv3x3_block(
            in_channels=out_channels,
            out_channels=out_channels)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        return x

class LinearBottleneck(nn.Module):
    """
    So-called 'Linear Bottleneck' layer (from MobileNetV2). It is used as a CtxDeepNet encoder unit.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    out_channels : int
        Number of output channels.
    stride : int or tuple/list of 2 int
        Strides of the second convolution layer.
    expansion : bool
        Whether do expansion of channels.
    """
    def __init__(self,
                 in_channels,
                 out_channels,
                 stride,
                 expansion):
        super(LinearBottleneck, self).__init__()
        self.residual = (in_channels == out_channels) and (stride == 1)
        mid_channels = in_channels * 6 if expansion else in_channels

        self.block = nn.Sequential(
            conv1x1_block(
                in_channels=in_channels,
                out_channels=mid_channels),
            dwconv3x3_block(
                in_channels=mid_channels,
                out_channels=mid_channels,
                stride=stride),
            conv1x1_block(
                in_channels=mid_channels,
                out_channels=out_channels,
                activation=None),
        )

    def forward(self, x):
        if self.residual:
            identity = x

        x = self.block(x)

        if self.residual:
            x = x + identity
        return x

class CtxDeepNet(nn.Module):
    """
    ContextNet specific deep net (regular encoder).

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    init_block_channels : int
        Number of channels for init block.
    """
    def __init__(self,
                 in_channels,
                 init_block_channels):
        super(CtxDeepNet, self).__init__()
        layers = [1, 1, 3, 3, 2, 2]
        channels_per_layers = [32, 32, 48, 64, 96, 128]
        downsample = [0, 0, 1, 1, 0, 0]

        self.features = nn.Sequential()
        self.features.add_module("init_block", conv3x3_block(
            in_channels=in_channels,
            out_channels=init_block_channels,
            stride=2,
            padding=0))
        in_channels = init_block_channels
        for i, out_channels in enumerate(channels_per_layers):
            stage = nn.Sequential()
            expansion = (i != 0)
            for j in range(layers[i]):
                stride = 2 if (j == 0) and (downsample[i] == 1) else 1
                stage.add_module("unit{}".format(j + 1), LinearBottleneck(
                    in_channels=in_channels,
                    out_channels=out_channels,
                    stride=stride,
                    expansion=expansion))
                in_channels = out_channels
            self.features.add_module("stage{}".format(i + 1), stage)

    def forward(self, x):
        x = self.features(x)
        return x

class FeatureFusion(nn.Module):
    """
    ContextNet specific feature fusion block.

    Parameters:
    ----------
    in_channels_high : int
        Number of input channels for x_high.
    in_channels_low : int
        Number of input channels for x_low.
    out_channels : int
        Number of output channels.
    """
    def __init__(self,
                 in_channels_high,
                 in_channels_low,
                 out_channels):
        super(FeatureFusion, self).__init__()
        self.conv_high = conv1x1_block(
            in_channels=in_channels_high,
            out_channels=out_channels,
            bias=True,
            activation=None)
        self.up = InterpolationBlock(
            scale_factor=4,
            align_corners=True)
        self.dw_conv_low = dwconv3x3_block(
            in_channels=in_channels_low,
            out_channels=out_channels)
        self.pw_conv_low = conv1x1_block(
            in_channels=out_channels,
            out_channels=out_channels,
            bias=True,
            activation=None)
        self.activ = nn.ReLU(True)

    def forward(self, x_high, x_low):
        x_high = self.conv_high(x_high)

        x_low = self.up(x_low)
        x_low = self.dw_conv_low(x_low)
        x_low = self.pw_conv_low(x_low)

        out = x_high + x_low
        out = self.activ(out)
        return out

class CtxHead(nn.Module):
    """
    ContextNet specific head/classifier block.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    num_classes : int
        Number of output channels/classes.
    """
    def __init__(self,
                 in_channels,
                 num_classes):
        super(CtxHead, self).__init__()
        self.conv1 = dwsconv3x3_block(
            in_channels=in_channels,
            out_channels=in_channels)
        self.conv2 = dwsconv3x3_block(
            in_channels=in_channels,
            out_channels=in_channels)
        self.dropout = nn.Dropout(p=0.1, inplace=False)
        self.conv3 = conv1x1(
            in_channels=in_channels,
            out_channels=num_classes,
            bias=True)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.dropout(x)
        x = self.conv3(x)
        return x

class CtxAuxHead(nn.Module):
    """
    ContextNet specific auxiliary head/classifier block.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    num_classes : int
        Number of output channels/classes.
    """
    def __init__(self,
                 in_channels,
                 mid_channels,
                 num_classes):
        super(CtxAuxHead, self).__init__()
        self.conv1 = conv3x3_block(
            in_channels=in_channels,
            out_channels=mid_channels)
        self.dropout = nn.Dropout(p=0.1, inplace=False)
        self.conv2 = conv1x1(
            in_channels=mid_channels,
            out_channels=num_classes,
            bias=True)

    def forward(self, x):
        x = self.conv1(x)
        x = self.dropout(x)
        x = self.conv2(x)
        return x

class ContextNet(nn.Module):
    """
    ContextNet model from 'ContextNet: Exploring Context and Detail for Semantic Segmentation in Real-time,'
    https://arxiv.org/abs/1805.04554.

    Parameters:
    ----------
    aux : bool, default False
        Whether to output an auxiliary result.
    fixed_size : bool, default False
        Whether to expect fixed spatial size of input image.
    in_channels : int, default 3
        Number of input channels.
    in_size : tuple of two ints, default (1024, 2048)
        Spatial size of the expected input image.
    num_classes : int, default 19
        Number of segmentation classes.
    """
    def __init__(self,
                 aux=False,
                 fixed_size=False,
                 in_channels=3,
                 in_size=(1024, 2048),
                 num_classes=19):
        super(ContextNet, self).__init__()
        assert (aux is not None)
        assert (fixed_size is not None)
        assert ((in_size[0] % 8 == 0) and (in_size[1] % 8 == 0))
        self.in_size = in_size
        self.num_classes = num_classes
        self.aux = aux
        self.fixed_size = fixed_size

        self.features_high = CtxShallowNet(
            in_channels=in_channels,
            mid1_channels=32,
            mid2_channels=64,
            out_channels=128)
        self.down = InterpolationBlock(
            scale_factor=4,
            align_corners=True,
            up=False)
        self.features_low = CtxDeepNet(
            in_channels=in_channels,
            init_block_channels=32)
        self.fusion = FeatureFusion(
            in_channels_high=128,
            in_channels_low=128,
            out_channels=128)
        self.head = CtxHead(
            in_channels=128,
            num_classes=num_classes)
        self.up = InterpolationBlock(
            scale_factor=8,
            align_corners=True)

        if self.aux:
            self.aux_head = CtxAuxHead(
                in_channels=128,
                mid_channels=32,
                num_classes=num_classes)

    def forward(self, x):
        x_high = self.features_high(x)

        x_low = self.down(x)
        x_low = self.features_low(x_low)

        x = self.fusion(x_high, x_low)
        x = self.head(x)
        x = self.up(x)

        if self.aux:
            y = self.aux_head(x_high)
            y = self.up(y)
            return x, y
        else:
            return x

def get_ctxnet(model_name=None,
               pretrained=False,
               root=os.path.join("~", ".torch", "models"),
               **kwargs):
    """
    Create ContextNet model with specific parameters.

    Parameters:
    ----------
    model_name : str or None, default None
        Model name for loading pretrained model.
    pretrained : bool, default False
        Whether to load the pretrained weights for model.
    root : str, default '~/.torch/models'
        Location for keeping the model parameters.
    """
    net = ContextNet(
        **kwargs)

    if pretrained:
        if (model_name is None) or (not model_name):
            raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.")
        from .model_store import download_model
        download_model(
            net=net,
            model_name=model_name,
            local_model_store_dir_path=root)

    return net

def ctxnet_cityscapes(num_classes=19, **kwargs):
    """
    ContextNet model for Cityscapes from 'ContextNet: Exploring Context and Detail for Semantic Segmentation in
    Real-time,' https://arxiv.org/abs/1805.04554.

    Parameters:
    ----------
    num_classes : int, default 19
        Number of segmentation classes.
    pretrained : bool, default False
        Whether to load the pretrained weights for model.
    root : str, default '~/.torch/models'
        Location for keeping the model parameters.
    """

def _calc_width(net):
    import numpy as np
    net_params = filter(lambda p: p.requires_grad, net.parameters())
    weight_count = 0
    for param in net_params:
        weight_count += np.prod(param.size())
    return weight_count

def _test():
    pretrained = False
    aux = False
    fixed_size = True
    in_size = (1024, 2048)
    classes = 19

    models = [
        ctxnet_cityscapes,
    ]

    for model in models:

        net = model(pretrained=pretrained, in_size=in_size, aux=aux, fixed_size=fixed_size)

        net.eval()
        weight_count = _calc_width(net)
        print("m={}, {}".format(model.__name__, weight_count))
        if aux:
            assert (model != ctxnet_cityscapes or weight_count == 914118)
        else:
            assert (model != ctxnet_cityscapes or weight_count == 876563)

        batch = 4
        x = torch.randn(batch, 3, in_size[0], in_size[1])
        ys = net(x)
        y = ys[0] if aux else ys
        assert (tuple(y.size()) == (batch, classes, in_size[0], in_size[1]))
        if aux:
            assert (tuple(ys[1].size()) == (batch, classes, in_size[0], in_size[1]))

if __name__ == "__main__":
    _test()


maya.text(self._tasks, edit=True, label=" {0}".format(value))

import os
import webbrowser

from azurebatchmayaapi import MayaAPI as maya
import azurebatchutils as utils

class JobHistoryUI(object):
    """Class to create the 'Jobs' tab in the plug-in UI"""

    def __init__(self, base, frame):
        """Create 'Jobs' tab and add to UI frame.

        :param base: The base class for handling jobs monitoring functionality.
        :type base: :class:`.AzureBatchJobHistory`
        :param frame: The shared plug-in UI frame.
        :type frame: :class:`.AzureBatchUI`
        """
        self.base = base
        self.label = " Jobs "
        self.ready = False
        self.jobs_displayed = []
        self.page = maya.form_layout(enableBackground=True)

        with utils.Row(1, 1, 360, "center") as lbl:
            self.total = maya.text(label="", font="boldLabelFont")
        self.paging_display()
        with utils.ScrollLayout(
            v_scrollbar=3, h_scrollbar=0, height=478) as scroll:
            with utils.RowLayout(row_spacing=20) as sublayout:
                if not self.jobs_displayed:
                    self.empty_jobs = maya.text(
                        label="Loading job data...",
                        align='left',
                        font="boldLabelFont")
                self.jobs_layout = sublayout

        with utils.Row(1, 1, 355, "center", (1,"bottom",0)) as btn:
            self.refresh_button = utils.ProcButton(
                "Refresh", "Refreshing...", self.refresh)
        maya.form_layout(
            self.page, edit=True,
            attachForm=[(lbl, 'top', 0), (lbl, 'left', 5),
                        (lbl, 'right', 5),
                        (self.first_btn, 'left', 5),
                        (self.last_btn, 'right', 5),
                        (scroll, 'left', 5), (scroll, 'right', 5),
                        (btn, 'bottom', 5), (btn, 'left', 0),
                        (btn, 'right', 0)],
            attachControl=[(self.first_btn,"top",5, lbl),
                           (self.prev_btn,"top",5, lbl),
                           (self.last_btn,"top",5, lbl),
                           (self.next_btn,"top",5, lbl),
                           (scroll,"top",5, self.first_btn),
                           (scroll,"top",5, self.next_btn),
                           (scroll,"top",5, self.prev_btn),
                           (scroll,"top",5, self.last_btn),
                           (scroll, "bottom", 5, btn)],
            attachPosition=[(lbl, 'top', 5, 0),
                            (self.first_btn, 'right', 5, 25),
                            (self.prev_btn, 'left', 5, 25),
                            (self.prev_btn, 'right', 5, 50),
                            (self.next_btn, 'right', 5, 75),
                            (self.next_btn, 'left', 5, 50),
                            (self.last_btn, 'left', 5, 75),])
        frame.add_tab(self)
        self.is_logged_out()

    @property
    def num_jobs(self):
        """Total number of jobs. Retrieves contents of label."""
        return maya.text(self.total, query=True, label=True)

    @num_jobs.setter
    def num_jobs(self, value):
        """Total number of jobs. Sets contents of label."""
        maya.text(self.total, edit=True, label=value)

    @property
    def last_page(self):
        """Whether the tab is displaying the last page of jobs.
        Retrieves status of paging buttons.
        """
        return maya.button(self.next_btn, query=True, enable=True)

    @last_page.setter
    def last_page(self, value):
        """Whether the tab is displaying the last page of jobs.
        Sets status of paging buttons.
        """
        maya.button(self.next_btn, edit=True, enable=value)
        maya.button(self.last_btn, edit=True, enable=value)

    @property
    def first_page(self):
        """Whether the tab is displaying the first page of jobs.
        Retrieves status of paging buttons.
        """
        return maya.button(self.prev_btn, query=True, enable=True)

    @first_page.setter
    def first_page(self, value):
        """Whether the tab is displaying the first page of jobs.
        Sets status of paging buttons.
        """
        maya.button(self.first_btn, edit=True, enable=value)
        maya.button(self.prev_btn, edit=True, enable=value)

    def is_logged_in(self):
        """Called when the plug-in is authenticated. Enables UI."""
        maya.form_layout(self.page, edit=True, enable=True)

    def is_logged_out(self):
        """Called when the plug-in is logged out. Disables UI and resets
        whether that tab has been loaded for the first time.
        """
        maya.delete_ui(self.empty_jobs)

        self.base.job_selected(None)
        for i in self.jobs_displayed:
            i.remove()

        self.jobs_displayed = []
        self.empty_jobs = maya.text(
            label="No jobs to display", parent=self.jobs_layout)

        maya.form_layout(self.page, edit=True, enable=False)
        self.ready = False

    def prepare(self):
        """Called when the tab is loaded (clicked into) for the first time.
        Initiates the downloading of job details based on the selected page.
        Once loaded, remains so for the rest of the plug-in session unless
        logged out or manually refreshed.

        If loading the UI fails, the tab returns to a logged-out state.
        """
        if not self.ready:
            maya.refresh()
            try:
                self.refresh()
                self.is_logged_in()
                self.ready = True

            except Exception as exp:
                maya.error("Error starting Assets UI: {0}".format(exp))
                self.is_logged_out()
        maya.refresh()

    def paging_display(self):
        """Display the buttons for controlling the job paging."""
        self.first_btn = maya.button(
            label="<<", align="center", command=self.show_first_jobs)
        self.prev_btn = maya.button(
            label="<", align="center", command=self.show_prev_jobs)
        self.next_btn = maya.button(
            label=">", align="center", command=self.show_next_jobs)
        self.last_btn = maya.button(
            label=">>", align="center", command=self.show_last_jobs)

    def reload(self, *args):
        """Refresh Jobs tab. Command for refresh_button.
        Remove all existing UI elements and job details and re-build
        from scratch. This is also called to populate
        the tab for the first time.
        """
        self.refresh_button.start()
        maya.delete_ui(self.empty_jobs)
        self.base.job_selected(None)
        for i in self.jobs_displayed:
            i.remove()
        self.jobs_displayed = self.base.show_jobs()
        if not self.jobs_displayed:
            self.empty_jobs = maya.text(
                label="No jobs to display", parent=self.jobs_layout)
        self.refresh_button.finish()

    def refresh(self, *args):
        """Refresh Jobs tab. Command for refresh_button.
        Remove all existing UI elements and job details and re-build
        from scratch. This is also called to populate
        the tab for the first time.
        """
        self.refresh_button.start()
        maya.delete_ui(self.empty_jobs)

        self.base.job_selected(None)
        for i in self.jobs_displayed:
            i.remove()
        self.jobs_displayed = self.base.get_history()
        if not self.jobs_displayed:
            self.empty_jobs = maya.text(
                label="No jobs to display", parent=self.jobs_layout)
        self.refresh_button.finish()

    def disable(self, enabled):
        """Disable the tab from user interaction. Used during long running
        processes like refreshing the job data, and when plug-in is
        unauthenticated.

        :param bool enabled: Whether to enable the display. False will
         disable the display.
        """
        maya.form_layout(self.page, edit=True, enable=enabled)

    def create_job_entry(self, name, index):
        """Create new dropdown frame to represent a job entry.
        :returns: A :class:`.AzureBatchJobInfo` object.
        """
        frame = maya.frame_layout(label=name,
                                  collapsable=True,
                                  collapse=True,
                                  visible=True,
                                  parent=self.jobs_layout)
        return AzureBatchJobInfo(self.base, index, frame)

    def show_next_jobs(self, *args):
        """Display next set of jobs. Command for next_btn."""
        self.base.show_next_jobs()
        self.reload()

    def show_prev_jobs(self, *args):
        """Display previous set of jobs. Command for prev_btn."""
        self.base.show_prev_jobs()
        self.reload()

    def show_first_jobs(self, *args):
        """Display first jobs. Command for first_btn."""
        self.base.show_first_jobs()
        self.reload()

    def show_last_jobs(self, *args):
        """Display last jobs. Command for last_btn."""
        self.base.show_last_jobs()
        self.reload()

class AzureBatchJobInfo(object):
    """Class to represent a single job reference."""

    def __init__(self, base, index, layout):
        """Create a new job reference.

        :param base: The base class for handling jobs monitoring functionality.
        :type base: :class:`.AzureBatchJobHistory`
        :param int index: The index of where this reference is displayed on
         the current page.
        :param layout: The layout on which the job details will be displayed.
        :type layout: :class:`.utils.FrameLayout`
        """
        self.base = base
        self.index = index
        self.layout = layout
        self.data_path = self.base.get_data_dir()
        self.selected_dir = utils.get_default_output_path()
        maya.frame_layout(
            layout,
            edit=True,
            collapseCommand=self.on_collapse,
            expandCommand=self.on_expand)
        self.job_details = maya.form_layout(parent=self.layout)
        maya.parent()
        self.content = []
        self.url = ""

    def set_label(self, value):
        """Set the label for the job frame layout.
        :param str value: The string to display as label.
        """
        maya.frame_layout(self.layout, edit=True, label=value)

    def set_status(self, value):
        """Set the buttons availability depending on the job status.
        :param str value: The status of the job.
        """
        maya.text(self._status, edit=True, label=" {0}".format(value))
        maya.icon_button(self.refresh_button, edit=True, enable=True)
        maya.icon_button(self.view_button, edit=True, enable=True)
        maya.icon_button(self.watch_button, edit=True, enable=True)
        maya.icon_button(self.cancel_button, edit=True, enable=(
            value in ["active", "enabling"]))
        maya.icon_button(self.delete_button, edit=True, enable=(
            value=='completed'))

    def get_status(self):
        """Get the status of the job."""
        return maya.text(self._status, query=True, label=True).lstrip()

    def set_progress(self, value):
        """Set the label for progress complete.
        :param int value: The percent complete.
        """
        maya.text(self._progress, edit=True, label=" {0}".format(value))

    def set_submission(self, value):
        """Set the label for date/time submitted.
        :param str value: The datetime string to format.
        """
        datetime = value.split('T')
        datetime[1] = datetime[1].split('.')[0]
        label = ' '.join(datetime)
        maya.text(self._submission, edit=True, label=" {0}".format(label))

    def set_tasks(self, value):
        """Set the label for number of tasks in the job.
        :param int value: The number of tasks.
        """

    def set_job(self, value):
        """Set the label for the job ID, and format the portal URL reference
        for the job with this ID.
        Except that with the current portal we have no way to directly link to a job.
        :param str value: The job ID.
        """
        maya.text_field(self._job, edit=True, text=value)
        self.url = "https://portal.azure.com"

    def set_pool(self, value):
        """Set the label for the pool ID that this job ran on.
        :param str value: The pool ID.
        """
        maya.text_field(self._pool, edit=True, text=value)

    def open_portal(self, *args):
        """Open the portal in a web browser to the details page
        for this job. Command for view_button
        """
        webbrowser.open(self.url, 2, True)

    def on_expand(self):
        """Command for the expanding of the job reference frame layout.
        Loads latest details for the specified job and populates UI.
        """
        with utils.Row(2, 2, (100,220), parent=self.job_details) as row:
            self.content.append(row)
            self.content.append(maya.text(label=""))
            self._thumbnail = maya.image()
            self.content.append(self._thumbnail)
        self._status, st_row = self.display_info("Status:   ")
        self._progress, pr_row = self.display_info("Progress:   ")
        self._submission, sb_row = self.display_info("Submitted:   ")
        self._tasks, tk_row = self.display_info("Task Count:   ")
        self._job, jb_row = self.display_data("Job ID:   ")
        self._pool, pl_row = self.display_data("Pool:   ")
        self._dir, dr_row = self.display_watcher()
        self.refresh_button = self.display_button(
            "btn_refresh.png", self.refresh, "Refresh")
        self.view_button = self.display_button(
            "btn_portal.png", self.open_portal,
            "View this job in the portal")
        self.watch_button = self.display_button(
            "btn_background.png", self.start_watcher,
            "Watch this job in the background")
        self.cancel_button = self.display_button(
            "btn_cancel.png", self.cancel_job, "Cancel this job")
        self.delete_button = self.display_button(
            "btn_delete.png",self.delete_job, "Delete job")
        maya.form_layout(
            self.job_details, edit=True,
            attachForm=[(row,"top",5), (row,"left",5), (row,"right",5),
                        (st_row,"left",5), (st_row,"right",5),
                        (pr_row,"left",5), (pr_row,"right",5),
                        (sb_row,"left",5), (sb_row,"right",5),
                        (tk_row,"left",5), (tk_row,"right",5),
                        (jb_row,"left",5), (jb_row,"right",5),
                        (pl_row,"left",5), (pl_row,"right",5),
                        (dr_row,"left",5), (dr_row,"right",5),
                        (self.refresh_button,"left",5),
                        (self.delete_button,"right",5),
                        (self.refresh_button,"bottom",5),
                        (self.view_button,"bottom",5),
                        (self.watch_button,"bottom",5),
                        (self.cancel_button,"bottom",5),
                        (self.delete_button,"bottom",5)],
           attachControl=[(st_row,"top",5, row),
                          (pr_row,"top",5, st_row),
                          (sb_row,"top",5, pr_row),
                          (tk_row,"top",5, sb_row),
                          (jb_row,"top",5, tk_row),
                          (pl_row,"top",5, jb_row),
                          (dr_row,"top",5, pl_row),
                          (self.refresh_button,"top",5, dr_row),
                          (self.view_button,"top",5, dr_row),
                          (self.watch_button,"top",5, dr_row),
                          (self.cancel_button,"top",5, dr_row),
                          (self.delete_button, "top", 5, dr_row)],
            attachPosition=[(self.refresh_button, 'right', 5, 20),
                            (self.view_button, 'left', 5, 20),
                            (self.view_button, 'right', 5, 40),
                            (self.watch_button, 'right', 5, 60),
                            (self.watch_button, 'left', 5, 40),
                            (self.cancel_button, 'left', 5, 60),
                            (self.cancel_button, 'right', 5, 80),
                            (self.delete_button, 'left', 5, 80)])
        self.base.job_selected(self)
        maya.execute(self.base.load_tasks)
        maya.execute(self.base.get_thumbnail)
        maya.refresh()

    def on_collapse(self):
        """Command for the collapsing of the job reference frame layout.
        Deletes all UI elements and resets currently selected job.
        This is called automatically when the user collapses the UI layout,
        or programmatically from the :func:`collapse` function.
        """
        self.base.job_selected(None)
        maya.parent(self.job_details)
        for element in self.content:
            maya.delete_ui(element, control=True)
        self.content = []

    def collapse(self):
        """Collapse the job frame. Initiates the on_collapse sequence."""
        maya.frame_layout(self.layout, edit=True, collapse=True)
        self.on_collapse()
        maya.refresh()

    def remove(self):
        """Delete the job reference frame layout."""
        maya.delete_ui(self.layout, control=True)

    def refresh(self):
        """Refresh the details of the specified job, and update the UI."""
        self.base.update_job(self.index)
        maya.execute(self.base.load_tasks)
        maya.execute(self.base.get_thumbnail)
        self.selected_dir = utils.get_default_output_path()
        maya.text_field(self._dir, edit=True, text=self.selected_dir)
        maya.refresh()

    def start_watcher(self, *args):
        """Start the background job watcher. Command for watch_button."""
        utils.JobWatcher(
            self.base.selected_job_id(), self.data_path, self.selected_dir)

    def select_dir(self, *args):
        """Selected directory for job watcher to download outputs to.
        Command for directory select symbol button.
        """
        cap = "Select target directory for job downloads."
        okCap = "Select Folder"
        new_dir = maya.file_select(fileMode=3, okCaption=okCap, caption=cap)
        if new_dir:
            self.selected_dir = new_dir[0]
        maya.text_field(self._dir, edit=True, text=self.selected_dir)

    def display_info(self, label):
        """Display text data as a label with a heading.
        :param str label: The text for the data heading.
        """
        with utils.Row(2, 2, (100,220), ("right","left"),
                       parent=self.job_details) as row:
            self.content.append(row)
            self.content.append(maya.text(label=label, align="right"))
            input = maya.text(align="left", label="")
            self.content.append(input)
        return input, row

    def display_data(self, label):
        """Display text data as a non-editable text field with heading.
        :param str label: The text for the data heading.
        """
        with utils.Row(2, 2, (100,220), ("right","left"),
                       parent=self.job_details) as row:
            self.content.append(row)
            self.content.append(maya.text(label=label, align="right"))
            input = maya.text_field(text="", editable=False)
            self.content.append(input)
        return input, row

    def display_button(self, icon, cmd, note):
        """Display a job function button.

        :param str icon: The name of an icon to display on the button.
        :param func cmd: The command to execute when the button is clicked.
        :param str note: The tool tip text for the button.
        :returns: The button object.
        """
        img = os.path.join(os.environ["AZUREBATCH_ICONS"], icon)
        btn = maya.icon_button(style='iconOnly', image=img, flat=0,
                               height=30,
                               command=cmd,
                               parent=self.job_details,
                               enable=False, annotation=note)
        self.content.append(btn)
        return btn

    def display_watcher(self):
        """Display output download directory selection elements for
        job watcher.
        :returns: Text field object for directory path.
        """
        with utils.Row(3, 2, (100,190,40), ("right","center","center"),
                       parent=self.job_details) as row:
            self.content.append(row)
            self.content.append(maya.text(label="Outputs:   ", align="right"))
            input = maya.text_field(text=self.selected_dir, editable=False)
            self.content.append(maya.symbol_button(
                image="SP_DirOpenIcon.png",
                command=self.select_dir,
                annotation="Select Download Directory"))
        return input, row

    def set_thumbnail(self, thumb, height):
        """Set thumbnail image.

        :param str thumb: The path to the thumbnail image.
        :param int height: The height of the image in pixels.
        """
        maya.image(self._thumbnail,
                   edit=True,
                   image=thumb,
                   height=height)

    def cancel_job(self, *args):
        """Cancel the specified job. Command for cancel_button."""
        self.base.cancel_job()

    def delete_job(self, *args):
        """Cancel the specified job. Command for cancel_button."""
        self.base.delete_job()


"""
requests.models
~~~~~~~~~~~~~~~

This module contains the primary objects that power Requests.
"""

import collections
import datetime
import sys

# Import encoding now, to avoid implicit import later.
# Implicit import within threads may cause LookupError when standard library is in a ZIP,
import encodings.idna

from urllib3.fields import RequestField
from urllib3.filepost import encode_multipart_formdata
from urllib3.util import parse_url
from urllib3.exceptions import (
    DecodeError, ReadTimeoutError, ProtocolError, LocationParseError)

from io import UnsupportedOperation
from .hooks import default_hooks
from .structures import CaseInsensitiveDict

from .auth import HTTPBasicAuth
from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar
from .exceptions import (
    HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError,
    ContentDecodingError, ConnectionError, StreamConsumedError)
from ._internal_utils import to_native_string, unicode_is_ascii
from .utils import (
    guess_filename, get_auth_from_url, requote_uri,
    stream_decode_response_unicode, to_key_val_list, parse_header_links,
    iter_slices, guess_json_utf, super_len, check_header_validity)
from .compat import (
    cookielib, urlunparse, urlsplit, urlencode, str, bytes,
    is_py2, chardet, builtin_str, basestring)
from .compat import json as complexjson
from .status_codes import codes

#: The set of HTTP status codes that indicate an automatically
REDIRECT_STATI = (
    codes.moved,               # 301
    codes.found,               # 302
    codes.other,               # 303
    codes.temporary_redirect,  # 307
    codes.permanent_redirect,  # 308
)

DEFAULT_REDIRECT_LIMIT = 30
CONTENT_CHUNK_SIZE = 10 * 1024
ITER_CHUNK_SIZE = 512

class RequestEncodingMixin(object):
    @property
    def path_url(self):
        """Build the path URL to use."""

        url = []

        p = urlsplit(self.url)

        path = p.path
        if not path:
            path = '/'

        url.append(path)

        query = p.query
        if query:
            url.append('?')
            url.append(query)

        return ''.join(url)

    @staticmethod
    def _encode_params(data):
        """Encode parameters in a piece of data.

        Will successfully encode parameters when passed as a dict or a list of
        2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
        if parameters are supplied as a dict.
        """

        if isinstance(data, (str, bytes)):
            return data
        elif hasattr(data, 'read'):
            return data
        elif hasattr(data, '__iter__'):
            result = []
            for k, vs in to_key_val_list(data):
                if isinstance(vs, basestring) or not hasattr(vs, '__iter__'):
                    vs = [vs]
                for v in vs:
                    if v is not None:
                        result.append(
                            (k.encode('utf-8') if isinstance(k, str) else k,
                             v.encode('utf-8') if isinstance(v, str) else v))
            return urlencode(result, doseq=True)
        else:
            return data

    @staticmethod
    def _encode_files(files, data):
        """Build the body for a multipart/form-data request.

        Will successfully encode files when passed as a dict or a list of
        tuples. Order is retained if data is a list of tuples but arbitrary
        if parameters are supplied as a dict.
        The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype)
        or 4-tuples (filename, fileobj, contentype, custom_headers).
        """
        if (not files):
            raise ValueError("Files must be provided.")
        elif isinstance(data, basestring):
            raise ValueError("Data must not be a string.")

        new_fields = []
        fields = to_key_val_list(data or {})
        files = to_key_val_list(files or {})

        for field, val in fields:
            if isinstance(val, basestring) or not hasattr(val, '__iter__'):
                val = [val]
            for v in val:
                if v is not None:
                    # Don't call str() on bytestrings: in Py3 it all goes wrong.
                    if not isinstance(v, bytes):
                        v = str(v)

                    new_fields.append(
                        (field.decode('utf-8') if isinstance(field, bytes) else field,
                         v.encode('utf-8') if isinstance(v, str) else v))

        for (k, v) in files:
            # support for explicit filename
            ft = None
            fh = None
            if isinstance(v, (tuple, list)):
                if len(v) == 2:
                    fn, fp = v
                elif len(v) == 3:
                    fn, fp, ft = v
                else:
                    fn, fp, ft, fh = v
            else:
                fn = guess_filename(v) or k
                fp = v

            if isinstance(fp, (str, bytes, bytearray)):
                fdata = fp
            else:
                fdata = fp.read()

            rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
            rf.make_multipart(content_type=ft)
            new_fields.append(rf)

        body, content_type = encode_multipart_formdata(new_fields)

        return body, content_type

class RequestHooksMixin(object):
    def register_hook(self, event, hook):
        """Properly register a hook."""

        if event not in self.hooks:
            raise ValueError('Unsupported event specified, with event name "%s"' % (event))

        if isinstance(hook, collections.Callable):
            self.hooks[event].append(hook)
        elif hasattr(hook, '__iter__'):
            self.hooks[event].extend(h for h in hook if isinstance(h, collections.Callable))

    def deregister_hook(self, event, hook):
        """Deregister a previously registered hook.
        Returns True if the hook existed, False if not.
        """

        try:
            self.hooks[event].remove(hook)
            return True
        except ValueError:
            return False

class Request(RequestHooksMixin):
    """A user-created :class:`Request <Request>` object.

    Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.

    :param method: HTTP method to use.
    :param url: URL to send.
    :param headers: dictionary of headers to send.
    :param files: dictionary of {filename: fileobject} files to multipart upload.
    :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place.
    :param json: json for the body to attach to the request (if files or data is not specified).
    :param params: dictionary of URL parameters to append to the URL.
    :param auth: Auth handler or (user, pass) tuple.
    :param cookies: dictionary or CookieJar of cookies to attach to this request.
    :param hooks: dictionary of callback hooks, for internal usage.

    Usage::

      >>> import requests
      >>> req = requests.Request('GET', 'http://example.com')
      >>> req.prepare()
      <PreparedRequest [GET]>
    """

    def __init__(self,
            method=None, url=None, headers=None, files=None, data=None,
            params=None, auth=None, cookies=None, hooks=None, json=None):

        # Default empty dicts for dict params.
        data = [] if data is None else data
        files = [] if files is None else files
        headers = {} if headers is None else headers
        params = {} if params is None else params
        hooks = {} if hooks is None else hooks

        self.hooks = default_hooks()
        for (k, v) in list(hooks.items()):
            self.register_hook(event=k, hook=v)

        self.method = method
        self.url = url
        self.headers = headers
        self.files = files
        self.data = data
        self.json = json
        self.params = params
        self.auth = auth
        self.cookies = cookies

    def __repr__(self):
        return '<Request [%s]>' % (self.method)

    def prepare(self):
        """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it."""
        p = PreparedRequest()
        p.prepare(
            method=self.method,
            url=self.url,
            headers=self.headers,
            files=self.files,
            data=self.data,
            json=self.json,
            params=self.params,
            auth=self.auth,
            cookies=self.cookies,
            hooks=self.hooks,
        )
        return p

class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
    """The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
    containing the exact bytes that will be sent to the server.

    Generated from either a :class:`Request <Request>` object or manually.

    Usage::

      >>> import requests
      >>> req = requests.Request('GET', 'http://httpbin.org/get')
      >>> r = req.prepare()
      <PreparedRequest [GET]>

      >>> s = requests.Session()
      >>> s.send(r)
      <Response [200]>
    """

    def __init__(self):
        #: HTTP verb to send to the server.
        self.method = None
        #: HTTP URL to send the request to.
        self.url = None
        #: dictionary of HTTP headers.
        self.headers = None
        # The `CookieJar` used to create the Cookie header will be stored here
        # after prepare_cookies is called
        self._cookies = None
        #: request body to send to the server.
        self.body = None
        #: dictionary of callback hooks, for internal usage.
        self.hooks = default_hooks()
        #: integer denoting starting position of a readable file-like body.
        self._body_position = None

    def prepare(self,
            method=None, url=None, headers=None, files=None, data=None,
            params=None, auth=None, cookies=None, hooks=None, json=None):
        """Prepares the entire request with the given parameters."""

        self.prepare_method(method)
        self.prepare_url(url, params)
        self.prepare_headers(headers)
        self.prepare_cookies(cookies)
        self.prepare_body(data, files, json)
        self.prepare_auth(auth, url)

        # Note that prepare_auth must be last to enable authentication schemes
        # such as OAuth to work on a fully prepared request.

        # This MUST go after prepare_auth. Authenticators could add a hook
        self.prepare_hooks(hooks)

    def __repr__(self):
        return '<PreparedRequest [%s]>' % (self.method)

    def copy(self):
        p = PreparedRequest()
        p.method = self.method
        p.url = self.url
        p.headers = self.headers.copy() if self.headers is not None else None
        p._cookies = _copy_cookie_jar(self._cookies)
        p.body = self.body
        p.hooks = self.hooks
        p._body_position = self._body_position
        return p

    def prepare_method(self, method):
        """Prepares the given HTTP method."""
        self.method = method
        if self.method is not None:
            self.method = to_native_string(self.method.upper())

    @staticmethod
    def _get_idna_encoded_host(host):
        import idna

        try:
            host = idna.encode(host, uts46=True).decode('utf-8')
        except idna.IDNAError:
            raise UnicodeError
        return host

    def prepare_url(self, url, params):
        """Prepares the given HTTP URL."""
        #: Accept objects that have string representations.
        #: We're unable to blindly call unicode/str functions
        #: as this will include the bytestring indicator (b'')
        if isinstance(url, bytes):
            url = url.decode('utf8')
        else:
            url = unicode(url) if is_py2 else str(url)

        # Remove leading whitespaces from url
        url = url.lstrip()

        # Don't do any URL preparation for non-HTTP schemes like `mailto`,
        # `data` etc to work around exceptions from `url_parse`, which
        # handles RFC 3986 only.
        if ':' in url and not url.lower().startswith('http'):
            self.url = url
            return

        # Support for unicode domain names and paths.
        try:
            scheme, auth, host, port, path, query, fragment = parse_url(url)
        except LocationParseError as e:
            raise InvalidURL(*e.args)

        if not scheme:
            error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?")
            error = error.format(to_native_string(url, 'utf8'))

            raise MissingSchema(error)

        if not host:
            raise InvalidURL("Invalid URL %r: No host supplied" % url)

        # In general, we want to try IDNA encoding the hostname if the string contains
        # non-ASCII characters. This allows users to automatically get the correct IDNA
        # behaviour. For strings containing only ASCII characters, we need to also verify
        # it doesn't start with a wildcard (*), before allowing the unencoded hostname.
        if not unicode_is_ascii(host):
            try:
                host = self._get_idna_encoded_host(host)
            except UnicodeError:
                raise InvalidURL('URL has an invalid label.')
        elif host.startswith(u'*'):
            raise InvalidURL('URL has an invalid label.')

        # Carefully reconstruct the network location
        netloc = auth or ''
        if netloc:
            netloc += '@'
        netloc += host
        if port:
            netloc += ':' + str(port)

        # Bare domains aren't valid URLs.
        if not path:
            path = '/'

        if is_py2:
            if isinstance(scheme, str):
                scheme = scheme.encode('utf-8')
            if isinstance(netloc, str):
                netloc = netloc.encode('utf-8')
            if isinstance(path, str):
                path = path.encode('utf-8')
            if isinstance(query, str):
                query = query.encode('utf-8')
            if isinstance(fragment, str):
                fragment = fragment.encode('utf-8')

        if isinstance(params, (str, bytes)):
            params = to_native_string(params)

        enc_params = self._encode_params(params)
        if enc_params:
            if query:
                query = '%s&%s' % (query, enc_params)
            else:
                query = enc_params

        url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
        self.url = url

    def prepare_headers(self, headers):
        """Prepares the given HTTP headers."""

        self.headers = CaseInsensitiveDict()
        if headers:
            for header in headers.items():
                # Raise exception on invalid header value.
                check_header_validity(header)
                name, value = header
                self.headers[to_native_string(name)] = value

    def prepare_body(self, data, files, json=None):
        """Prepares the given HTTP body data."""

        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = 'application/json'
            body = complexjson.dumps(json)
            if not isinstance(body, bytes):
                body = body.encode('utf-8')

        is_stream = all([
            hasattr(data, '__iter__'),
            not isinstance(data, (basestring, list, tuple, collections.Mapping))
        ])

        try:
            length = super_len(data)
        except (TypeError, AttributeError, UnsupportedOperation):
            length = None

        if is_stream:
            body = data

            if getattr(body, 'tell', None) is not None:
                # Record the current file position before reading.
                # This will allow us to rewind a file in the event
                # of a redirect.
                try:
                    self._body_position = body.tell()
                except (IOError, OSError):
                    # This differentiates from None, allowing us to catch
                    # a failed `tell()` later when trying to rewind the body
                    self._body_position = object()

            if files:
                raise NotImplementedError('Streamed bodies and files are mutually exclusive.')

            if length:
                self.headers['Content-Length'] = builtin_str(length)
            else:
                self.headers['Transfer-Encoding'] = 'chunked'
        else:
            # Multi-part file uploads.
            if files:
                (body, content_type) = self._encode_files(files, data)
            else:
                if data:
                    body = self._encode_params(data)
                    if isinstance(data, basestring) or hasattr(data, 'read'):
                        content_type = None
                    else:
                        content_type = 'application/x-www-form-urlencoded'

            self.prepare_content_length(body)

            # Add content-type if it wasn't explicitly provided.
            if content_type and ('content-type' not in self.headers):
                self.headers['Content-Type'] = content_type

        self.body = body

    def prepare_content_length(self, body):
        """Prepare Content-Length header based on request method and body"""
        if body is not None:
            length = super_len(body)
            if length:
                # If length exists, set it. Otherwise, we fallback
                # to Transfer-Encoding: chunked.
                self.headers['Content-Length'] = builtin_str(length)
        elif self.method not in ('GET', 'HEAD') and self.headers.get('Content-Length') is None:
            # Set Content-Length to 0 for methods that can have a body
            # but don't provide one. (i.e. not GET or HEAD)
            self.headers['Content-Length'] = '0'

    def prepare_auth(self, auth, url=''):
        """Prepares the given HTTP auth data."""

        # If no Auth is explicitly provided, extract it from the URL first.
        if auth is None:
            url_auth = get_auth_from_url(self.url)
            auth = url_auth if any(url_auth) else None

        if auth:
            if isinstance(auth, tuple) and len(auth) == 2:
                # special-case basic HTTP auth
                auth = HTTPBasicAuth(*auth)

            # Allow auth to make its changes.
            r = auth(self)

            # Update self to reflect the auth changes.
            self.__dict__.update(r.__dict__)

            # Recompute Content-Length
            self.prepare_content_length(self.body)

    def prepare_cookies(self, cookies):
        """Prepares the given HTTP cookie data.

        This function eventually generates a ``Cookie`` header from the
        given cookies using cookielib. Due to cookielib's design, the header
        will not be regenerated if it already exists, meaning this function
        can only be called once for the life of the
        :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls
        to ``prepare_cookies`` will have no actual effect, unless the "Cookie"
        header is removed beforehand.
        """
        if isinstance(cookies, cookielib.CookieJar):
            self._cookies = cookies
        else:
            self._cookies = cookiejar_from_dict(cookies)

        cookie_header = get_cookie_header(self._cookies, self)
        if cookie_header is not None:
            self.headers['Cookie'] = cookie_header

    def prepare_hooks(self, hooks):
        """Prepares the given hooks."""
        # hooks can be passed as None to the prepare method and to this
        # method. To prevent iterating over None, simply use an empty list
        hooks = hooks or []
        for event in hooks:
            self.register_hook(event, hooks[event])

class Response(object):
    """The :class:`Response <Response>` object, which contains a
    server's response to an HTTP request.
    """

    __attrs__ = [
        '_content', 'status_code', 'headers', 'url', 'history',
        'encoding', 'reason', 'cookies', 'elapsed', 'request'
    ]

    def __init__(self):
        super(Response, self).__init__()

        self._content = False
        self._content_consumed = False
        self._next = None

        #: Integer Code of responded HTTP Status, e.g. 404 or 200.
        self.status_code = None

        #: Case-insensitive Dictionary of Response Headers.
        #: For example, ``headers['content-encoding']`` will return the
        #: value of a ``'Content-Encoding'`` response header.
        self.headers = CaseInsensitiveDict()

        #: File-like object representation of response (for advanced usage).
        #: Use of ``raw`` requires that ``stream=True`` be set on the request.
        # This requirement does not apply for use internally to Requests.
        self.raw = None

        #: Final URL location of Response.
        self.url = None

        #: Encoding to decode with when accessing r.text.
        self.encoding = None

        #: A list of :class:`Response <Response>` objects from
        #: the history of the Request. Any redirect responses will end
        #: up here. The list is sorted from the oldest to the most recent request.
        self.history = []

        #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
        self.reason = None

        #: A CookieJar of Cookies the server sent back.
        self.cookies = cookiejar_from_dict({})

        #: The amount of time elapsed between sending the request
        #: 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)

        #: The :class:`PreparedRequest <PreparedRequest>` object to which this
        self.request = None

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

    def __getstate__(self):
        # Consume everything; accessing the content attribute makes
        # sure the content has been fully read.
        if not self._content_consumed:
            self.content

        return dict(
            (attr, getattr(self, attr, None))
            for attr in self.__attrs__
        )

    def __setstate__(self, state):
        for name, value in state.items():
            setattr(self, name, value)

        # pickled objects do not have .raw
        setattr(self, '_content_consumed', True)
        setattr(self, 'raw', None)

    def __repr__(self):
        return '<Response [%s]>' % (self.status_code)

    def __bool__(self):
        """Returns True if :attr:`status_code` is less than 400.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code, is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """
        return self.ok

    def __nonzero__(self):
        """Returns True if :attr:`status_code` is less than 400.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code, is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """

    def __iter__(self):
        """Allows you to use a response as an iterator."""
        return self.iter_content(128)

    @property
    def ok(self):
        """Returns True if :attr:`status_code` is less than 400.

        This attribute checks if the status code of the response is between
        400 and 600 to see if there was a client error or a server error. If
        the status code, is between 200 and 400, this will return True. This
        is **not** a check to see if the response code is ``200 OK``.
        """
        try:
            self.raise_for_status()
        except HTTPError:
            return False
        return True

    @property
    def is_redirect(self):
        """True if this Response is a well-formed HTTP redirect that could have
        been processed automatically (by :meth:`Session.resolve_redirects`).
        """
        return ('location' in self.headers and self.status_code in REDIRECT_STATI)

    @property
    def is_permanent_redirect(self):
        """True if this Response one of the permanent versions of redirect."""
        return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect))

    @property
    def next(self):
        """Returns a PreparedRequest for the next request in a redirect chain, if there is one."""
        return self._next

    @property
    def apparent_encoding(self):
        """The apparent encoding, provided by the chardet library."""
        return chardet.detect(self.content)['encoding']

    def iter_content(self, chunk_size=1, decode_unicode=False):
        """Iterates over the response data.  When stream=True is set on the
        request, this avoids reading the content at once into memory for
        large responses.  The chunk size is the number of bytes it should
        read into memory.  This is not necessarily the length of each item
        returned as decoding can take place.

        chunk_size must be of type int or None. A value of None will
        function differently depending on the value of `stream`.
        stream=True will read data as it arrives in whatever size the
        chunks are received. If stream=False, data is returned as
        a single chunk.

        If decode_unicode is True, content will be decoded using the best
        available encoding based on the response.
        """

        def generate():
            # Special case for urllib3.
            if hasattr(self.raw, 'stream'):
                try:
                    for chunk in self.raw.stream(chunk_size, decode_content=True):
                        yield chunk
                except ProtocolError as e:
                    raise ChunkedEncodingError(e)
                except DecodeError as e:
                    raise ContentDecodingError(e)
                except ReadTimeoutError as e:
                    raise ConnectionError(e)
            else:
                # Standard file-like object.
                while True:
                    chunk = self.raw.read(chunk_size)
                    if not chunk:
                        break
                    yield chunk

            self._content_consumed = True

        if self._content_consumed and isinstance(self._content, bool):
            raise StreamConsumedError()
        elif chunk_size is not None and not isinstance(chunk_size, int):
            raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size))
        # simulate reading small chunks of the content
        reused_chunks = iter_slices(self._content, chunk_size)

        stream_chunks = generate()

        chunks = reused_chunks if self._content_consumed else stream_chunks

        if decode_unicode:
            chunks = stream_decode_response_unicode(chunks, self)

        return chunks

    def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None):
        """Iterates over the response data, one line at a time.  When
        stream=True is set on the request, this avoids reading the
        content at once into memory for large responses.

        .. note:: This method is not reentrant safe.
        """

        pending = None

        for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode):

            if pending is not None:
                chunk = pending + chunk

            if delimiter:
                lines = chunk.split(delimiter)
            else:
                lines = chunk.splitlines()

            if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
                pending = lines.pop()
            else:
                pending = None

            for line in lines:
                yield line

        if pending is not None:
            yield pending

    @property
    def content(self):
        """Content of the response, in bytes."""

        if self._content is False:
            # Read the contents.
            if self._content_consumed:
                raise RuntimeError(
                    'The content for this response was already consumed')

            if self.status_code == 0 or self.raw is None:
                self._content = None
            else:
                self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()

        self._content_consumed = True
        # don't need to release the connection; that's been handled by urllib3
        # since we exhausted the data.
        return self._content

    @property
    def text(self):
        """Content of the response, in unicode.

        If Response.encoding is None, encoding will be guessed using
        ``chardet``.

        The encoding of the response content is determined based solely on HTTP
        headers, following RFC 2616 to the letter. If you can take advantage of
        non-HTTP knowledge to make a better guess at the encoding, you should
        set ``r.encoding`` appropriately before accessing this property.
        """

        # Try charset from content-type
        content = None
        encoding = self.encoding

        if not self.content:
            return str('')

        # Fallback to auto-detected encoding.
        if self.encoding is None:
            encoding = self.apparent_encoding

        # Decode unicode from given encoding.
        try:
            content = str(self.content, encoding, errors='replace')
        except (LookupError, TypeError):
            # A LookupError is raised if the encoding was not found which could
            # indicate a misspelling or similar mistake.
            # A TypeError can be raised if encoding is None
            # So we try blindly encoding.
            content = str(self.content, errors='replace')

        return content

    def json(self, **kwargs):
        r"""Returns the json-encoded content of a response, if any.

        :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
        :raises ValueError: If the response body does not contain valid json.
        """

        if not self.encoding and self.content and len(self.content) > 3:
            # No encoding set. JSON RFC 4627 section 3 states we should expect
            # UTF-8, -16 or -32. Detect which one to use; If the detection or
            # decoding fails, fall back to `self.text` (using chardet to make
            # a best guess).
            encoding = guess_json_utf(self.content)
            if encoding is not None:
                try:
                    return complexjson.loads(
                        self.content.decode(encoding), **kwargs
                    )
                except UnicodeDecodeError:
                    # Wrong UTF codec detected; usually because it's not UTF-8
                    # but some other 8-bit codec.  This is an RFC violation,
                    # and the server didn't bother to tell us what codec *was*
                    pass
        return complexjson.loads(self.text, **kwargs)

    @property
    def links(self):
        """Returns the parsed header links of the response, if any."""

        header = self.headers.get('link')

        l = {}

        if header:
            links = parse_header_links(header)

            for link in links:
                key = link.get('rel') or link.get('url')
                l[key] = link

        return l

    def raise_for_status(self):
        """Raises stored :class:`HTTPError`, if one occurred."""

        http_error_msg = ''
        if isinstance(self.reason, bytes):
            # We attempt to decode utf-8 first because some servers
            # choose to localize their reason strings. If the string
            # isn't utf-8, we fall back to iso-8859-1 for all other
            try:
                reason = self.reason.decode('utf-8')
            except UnicodeDecodeError:
                reason = self.reason.decode('iso-8859-1')
        else:
            reason = self.reason

        if 400 <= self.status_code < 500:
            http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url)

        elif 500 <= self.status_code < 600:
            http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url)

        if http_error_msg:
            raise HTTPError(http_error_msg, response=self)

    def close(self):
        """Releases the connection back to the pool. Once this method has been
        called the underlying ``raw`` object must not be accessed again.

        *Note: Should not normally need to be called explicitly.*
        """
        if not self._content_consumed:
            self.raw.close()

        release_conn = getattr(self.raw, 'release_conn', None)
        if release_conn is not None:
            release_conn()