Merge mozilla-central to inbound. a=merge CLOSED TREE
authorTiberius Oros <toros@mozilla.com>
Mon, 08 Oct 2018 19:23:04 +0300
changeset 495762 6ea2076a39bc76c553d699bbe1ef1fa7e9e781d1
parent 495743 113ba8a072a6cb6074fe176fe84965bbd7a1970d (current diff)
parent 495703 c291143e24019097d087f9307e59b49facaf90cb (diff)
child 495763 163c65f9c522229c0bbc3a73055499c8b406189a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java
mobile/android/base/java/org/mozilla/gecko/promotion/HomeScreenPrompt.java
mobile/android/services/src/main/res/layout/homescreen_prompt.xml
testing/config/telemetry_tests_requirements.txt
toolkit/components/telemetry/build_scripts/python_mozparsers/__init__.py
toolkit/components/telemetry/build_scripts/python_mozparsers/parse_events.py
toolkit/components/telemetry/build_scripts/python_mozparsers/parse_histograms.py
toolkit/components/telemetry/build_scripts/python_mozparsers/parse_scalars.py
toolkit/components/telemetry/build_scripts/python_mozparsers/shared_telemetry_utils.py
--- a/Pipfile
+++ b/Pipfile
@@ -5,15 +5,16 @@ name = "pypi"
 
 [dev-packages]
 
 [packages]
 attrs = "==18.1.0"
 blessings = "==1.7"
 jsmin = "==2.1.0"
 json-e = "==2.7.0"
+pip-tools = "==3.0.0"
 pipenv = "==2018.5.18"
 pytest = "==3.6.2"
 python-hglib = "==2.4"
 requests = "==2.9.1"
 six = "==1.10.0"
 virtualenv = "==15.2.0"
 voluptuous = "==0.11.5"
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,12 +1,12 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "2cbc7cd293a86ec63eb82c051c79a7900da7e4abea1df7d3a3ab0b03cafe3273"
+            "sha256": "e756c316803705f9230eb8dd3b53fd9a9aa0a146c7387e3caffb668e0f7ea223"
         },
         "pipfile-spec": 6,
         "requires": {},
         "sources": [
             {
                 "name": "pypi",
                 "url": "https://pypi.org/simple",
                 "verify_ssl": true
@@ -40,16 +40,23 @@
         },
         "certifi": {
             "hashes": [
                 "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
                 "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
             ],
             "version": "==2018.4.16"
         },
+        "click": {
+            "hashes": [
+                "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
+                "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
+            ],
+            "version": "==7.0"
+        },
         "funcsigs": {
             "hashes": [
                 "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
                 "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
             ],
             "markers": "python_version < '3.0'",
             "version": "==1.0.2"
         },
@@ -70,16 +77,24 @@
         "more-itertools": {
             "hashes": [
                 "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
                 "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
                 "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
             ],
             "version": "==4.3.0"
         },
+        "pip-tools": {
+            "hashes": [
+                "sha256:4a94997602848f77ff02f660c0fcdfeaf316924ebb236c865f9742ce212aa6f9",
+                "sha256:e45e5198ce3799068642ebb0e7c9be5520bcff944c0186f79c1199a2759c970a"
+            ],
+            "index": "pypi",
+            "version": "==3.0.0"
+        },
         "pipenv": {
             "hashes": [
                 "sha256:04b9a8b02a3ff12a5502b335850cfdb192adcfd1d6bbdb7a7c47cae9ab9ddece",
                 "sha256:e96d5bfa6822a17b2200d455aa5f9002c14361c50df1b1e51921479d7c09e741"
             ],
             "index": "pypi",
             "version": "==2018.5.18"
         },
--- a/dom/media/webaudio/AudioEventTimeline.cpp
+++ b/dom/media/webaudio/AudioEventTimeline.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioEventTimeline.h"
-#include "MediaStreamGraph.h"
+#include "AudioNodeStream.h"
 
 #include "mozilla/ErrorResult.h"
 
 static float LinearInterpolate(double t0, float v0, double t1, float v1, double t)
 {
   return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
 }
 
@@ -71,17 +71,17 @@ AudioTimelineEvent::AudioTimelineEvent(T
   mTime = aTime;
   if (aType == AudioTimelineEvent::SetValueCurve) {
     SetCurveParams(aCurve, aCurveLength);
   } else {
     mValue = aValue;
   }
 }
 
-AudioTimelineEvent::AudioTimelineEvent(MediaStream* aStream)
+AudioTimelineEvent::AudioTimelineEvent(AudioNodeStream* aStream)
   : mType(Stream)
   , mCurve(nullptr)
   , mStream(aStream)
   , mTimeConstant(0.0)
   , mDuration(0.0)
 #ifdef DEBUG
   , mTimeIsInTicks(false)
 #endif
--- a/dom/media/webaudio/AudioEventTimeline.h
+++ b/dom/media/webaudio/AudioEventTimeline.h
@@ -15,17 +15,17 @@
 
 #include "MainThreadUtils.h"
 #include "nsTArray.h"
 #include "math.h"
 #include "WebAudioUtils.h"
 
 namespace mozilla {
 
-class MediaStream;
+class AudioNodeStream;
 
 namespace dom {
 
 struct AudioTimelineEvent final
 {
   enum Type : uint32_t
   {
     SetValue,
@@ -40,17 +40,17 @@ struct AudioTimelineEvent final
 
   AudioTimelineEvent(Type aType,
                      double aTime,
                      float aValue,
                      double aTimeConstant = 0.0,
                      double aDuration = 0.0,
                      const float* aCurve = nullptr,
                      uint32_t aCurveLength = 0);
-  explicit AudioTimelineEvent(MediaStream* aStream);
+  explicit AudioTimelineEvent(AudioNodeStream* aStream);
   AudioTimelineEvent(const AudioTimelineEvent& rhs);
   ~AudioTimelineEvent();
 
   template <class TimeType>
   TimeType Time() const;
 
   void SetTimeInTicks(int64_t aTimeInTicks)
   {
@@ -76,17 +76,17 @@ struct AudioTimelineEvent final
     uint32_t mCurveLength;
   };
   // mCurve contains a buffer of SetValueCurve samples.  We sample the
   // values in the buffer depending on how far along we are in time.
   // If we're at time T and the event has started as time T0 and has a
   // duration of D, we sample the buffer at floor(mCurveLength*(T-T0)/D)
   // if T<T0+D, and just take the last sample in the buffer otherwise.
   float* mCurve;
-  RefPtr<MediaStream> mStream;
+  RefPtr<AudioNodeStream> mStream;
   double mTimeConstant;
   double mDuration;
 #ifdef DEBUG
   bool mTimeIsInTicks;
 #endif
 
 private:
   // This member is accessed using the `Time` method, for safety.
--- a/dom/media/webaudio/AudioParam.cpp
+++ b/dom/media/webaudio/AudioParam.cpp
@@ -87,28 +87,26 @@ AudioParam::DisconnectFromGraphAndDestro
 MediaStream*
 AudioParam::Stream()
 {
   if (mStream) {
     return mStream;
   }
 
   AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
-  RefPtr<AudioNodeStream> stream =
-    AudioNodeStream::Create(mNode->Context(), engine,
-                            AudioNodeStream::NO_STREAM_FLAGS,
-                            mNode->Context()->Graph());
+  mStream = AudioNodeStream::Create(mNode->Context(), engine,
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    mNode->Context()->Graph());
 
   // Force the input to have only one channel, and make it down-mix using
   // the speaker rules if needed.
-  stream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
+  mStream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit,
+                                          ChannelInterpretation::Speakers);
   // Mark as an AudioParam helper stream
-  stream->SetAudioParamHelperStream();
-
-  mStream = stream.forget();
+  mStream->SetAudioParamHelperStream();
 
   // Setup the AudioParam's stream as an input to the owner AudioNode's stream
   AudioNodeStream* nodeStream = mNode->GetStream();
   if (nodeStream) {
     mNodeStreamPort =
       nodeStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
   }
 
@@ -180,18 +178,17 @@ float
 AudioParamTimeline::AudioNodeInputValue(size_t aCounter) const
 {
   MOZ_ASSERT(mStream);
 
   // If we have a chunk produced by the AudioNode inputs to the AudioParam,
   // get its value now.  We use aCounter to tell us which frame of the last
   // AudioChunk to look at.
   float audioNodeInputValue = 0.0f;
-  const AudioBlock& lastAudioNodeChunk =
-    static_cast<AudioNodeStream*>(mStream.get())->LastChunks()[0];
+  const AudioBlock& lastAudioNodeChunk = mStream->LastChunks()[0];
   if (!lastAudioNodeChunk.IsNull()) {
     MOZ_ASSERT(lastAudioNodeChunk.GetDuration() == WEBAUDIO_BLOCK_SIZE);
     audioNodeInputValue =
       static_cast<const float*>(lastAudioNodeChunk.mChannelData[0])[aCounter];
     audioNodeInputValue *= lastAudioNodeChunk.mVolume;
   }
 
   return audioNodeInputValue;
--- a/dom/media/webaudio/AudioParamTimeline.h
+++ b/dom/media/webaudio/AudioParamTimeline.h
@@ -3,49 +3,50 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef AudioParamTimeline_h_
 #define AudioParamTimeline_h_
 
 #include "AudioEventTimeline.h"
+#include "AudioNodeStream.h"
 #include "mozilla/ErrorResult.h"
-#include "MediaStreamGraph.h"
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 namespace dom {
 
 // This helper class is used to represent the part of the AudioParam
 // class that gets sent to AudioNodeEngine instances.  In addition to
 // AudioEventTimeline methods, it holds a pointer to an optional
-// MediaStream which represents the AudioNode inputs to the AudioParam.
-// This MediaStream is managed by the AudioParam subclass on the main
+// AudioNodeStream which represents the AudioNode inputs to the AudioParam.
+// This AudioNodeStream is managed by the AudioParam subclass on the main
 // thread, and can only be obtained from the AudioNodeEngine instances
 // consuming this class.
 class AudioParamTimeline : public AudioEventTimeline
 {
   typedef AudioEventTimeline BaseClass;
 
 public:
   explicit AudioParamTimeline(float aDefaultValue)
     : BaseClass(aDefaultValue)
   {
   }
 
-  MediaStream* Stream() const
+  AudioNodeStream* Stream() const
   {
     return mStream;
   }
 
   bool HasSimpleValue() const
   {
-    return BaseClass::HasSimpleValue() && !mStream;
+    return BaseClass::HasSimpleValue() &&
+      (!mStream || mStream->LastChunks()[0].IsNull());
   }
 
   template<class TimeType>
   float GetValueAtTime(TimeType aTime)
   {
     return GetValueAtTime(aTime, 0);
   }
 
@@ -93,17 +94,17 @@ public:
   }
 
 
 private:
   float AudioNodeInputValue(size_t aCounter) const;
 
 protected:
   // This is created lazily when needed.
-  RefPtr<MediaStream> mStream;
+  RefPtr<AudioNodeStream> mStream;
 };
 
 template<> inline float
 AudioParamTimeline::GetValueAtTime(double aTime, size_t aCounter)
 {
   MOZ_ASSERT(!aCounter);
 
   // Getting an AudioParam value on an AudioNode does not consider input from
--- a/mobile/android/app/src/test/resources/experiments.json
+++ b/mobile/android/app/src/test/resources/experiments.json
@@ -49,34 +49,16 @@
           "appId": "^org.mozilla.fennec|^org.mozilla.firefox_beta$"
         },
         "buckets": {
           "min": "0",
           "max": "100"
         }
       },
     {
-      "name": "promote-add-to-homescreen",
-      "buckets": {
-        "max": "100",
-        "min": "50"
-      },
-      "last_modified": 1467705654772,
-      "values": {
-        "lastVisitMaximumAgeMs": 600000,
-        "minimumTotalVisits": 5,
-        "lastVisitMinimumAgeMs": 30000
-      },
-      "id": "20d278d7-0d35-4811-8f01-bf24e31ba51b",
-      "match": {
-        "appId": "^org.mozilla.fennec|^org.mozilla.firefox_beta$"
-      },
-      "schema": 1467705310595
-    },
-    {
       "name": "offline-cache",
       "buckets": {
         "max": "100",
         "min": "0"
       },
       "last_modified": 1467705429859,
       "id": "9f1ea043-c1d8-48ba-802d-aeabaf667afe",
       "match": {
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -206,20 +206,16 @@
         <activity android:name="org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt"
                   android:launchMode="singleTop"
                   android:theme="@style/OverlayActivity" />
 
         <activity android:name="org.mozilla.gecko.promotion.SimpleHelperUI"
                   android:launchMode="singleTop"
                   android:theme="@style/OverlayActivity" />
 
-        <activity android:name="org.mozilla.gecko.promotion.HomeScreenPrompt"
-                  android:launchMode="singleTop"
-                  android:theme="@style/OverlayActivity" />
-
         <!-- The main reason for the Tab Queue build flag is to not mess with the VIEW intent filter
              before the rest of the plumbing is in place -->
 
         <service android:name="org.mozilla.gecko.tabqueue.TabQueueService" />
 
         <activity android:name="org.mozilla.gecko.tabqueue.TabQueuePrompt"
                   android:launchMode="singleTop"
                   android:theme="@style/OverlayActivity" />
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -113,17 +113,16 @@ import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
 import org.mozilla.gecko.mma.MmaDelegate;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
-import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
 import org.mozilla.gecko.promotion.ReaderViewBookmarkPromotion;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.reader.ReadingListHelper;
 import org.mozilla.gecko.reader.SavedReaderViewHelper;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.search.SearchEngineManager;
@@ -313,17 +312,16 @@ public class BrowserApp extends GeckoApp
     // race by determining if the web content should be hidden at the animation's end.
     private boolean mHideWebContentOnAnimationEnd;
 
     private final DynamicToolbar mDynamicToolbar = new DynamicToolbar();
 
     private final TelemetryCorePingDelegate mTelemetryCorePingDelegate = new TelemetryCorePingDelegate();
 
     private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
-            new AddToHomeScreenPromotion(),
             new ScreenshotDelegate(),
             new BookmarkStateChangeDelegate(),
             new ReaderViewBookmarkPromotion(),
             new PostUpdateHandler(),
             mTelemetryCorePingDelegate,
             new OfflineTabStatusDelegate(),
             new AdjustBrowserAppDelegate(mTelemetryCorePingDelegate)
     ));
--- a/mobile/android/base/java/org/mozilla/gecko/Experiments.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Experiments.java
@@ -23,19 +23,16 @@ public class Experiments {
     private static final String LOGTAG = "GeckoExperiments";
 
     // Show a system notification linking to a "What's New" page on app update.
     public static final String WHATSNEW_NOTIFICATION = "whatsnew-notification";
 
     // Synchronizing the catalog of downloadable content from Kinto
     public static final String DOWNLOAD_CONTENT_CATALOG_SYNC = "download-content-catalog-sync";
 
-    // Promotion for "Add to homescreen"
-    public static final String PROMOTE_ADD_TO_HOMESCREEN = "promote-add-to-homescreen";
-
     // Promotion to bookmark reader-view items after entering reader view three times (Bug 1247689)
     public static final String TRIPLE_READERVIEW_BOOKMARK_PROMPT = "triple-readerview-bookmark-prompt";
 
     // Play HLS videos in a VideoView (Bug 1313391)
     public static final String HLS_VIDEO_PLAYBACK = "hls-video-playback";
 
     // Show AddOns menu-item in top level menu
     public static final String TOP_ADDONS_MENU = "top-addons-menu";
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.promotion;
-
-import android.app.Activity;
-import android.content.Context;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import org.mozilla.gecko.switchboard.SwitchBoard;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.AboutPages;
-import org.mozilla.gecko.BrowserApp;
-import org.mozilla.gecko.GeckoProfile;
-import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
-import org.mozilla.gecko.db.BrowserContract;
-import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.db.UrlAnnotations;
-import org.mozilla.gecko.delegates.TabsTrayVisibilityAwareDelegate;
-import org.mozilla.gecko.Experiments;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import java.lang.ref.WeakReference;
-
-import ch.boye.httpclientandroidlib.util.TextUtils;
-
-/**
- * Promote "Add to home screen" if user visits website often.
- */
-public class AddToHomeScreenPromotion extends TabsTrayVisibilityAwareDelegate implements Tabs.OnTabsChangedListener {
-    public static class URLHistory {
-        public final long visits;
-        public final long lastVisit;
-
-        private URLHistory(long visits, long lastVisit) {
-            this.visits = visits;
-            this.lastVisit = lastVisit;
-        }
-    }
-
-    private static final String LOGTAG = "GeckoPromoteShortcut";
-
-    private static final String EXPERIMENT_MINIMUM_TOTAL_VISITS = "minimumTotalVisits";
-    private static final String EXPERIMENT_LAST_VISIT_MINIMUM_AGE = "lastVisitMinimumAgeMs";
-    private static final String EXPERIMENT_LAST_VISIT_MAXIMUM_AGE = "lastVisitMaximumAgeMs";
-
-    private WeakReference<Activity> activityReference;
-    private boolean isEnabled;
-    private int minimumVisits;
-    private int lastVisitMinimumAgeMs;
-    private int lastVisitMaximumAgeMs;
-
-    @CallSuper
-    @Override
-    public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
-        super.onCreate(browserApp, savedInstanceState);
-        activityReference = new WeakReference<Activity>(browserApp);
-
-        initializeExperiment(browserApp);
-    }
-
-    @Override
-    public void onResume(BrowserApp browserApp) {
-        Tabs.registerOnTabsChangedListener(this);
-    }
-
-    @Override
-    public void onPause(BrowserApp browserApp) {
-        Tabs.unregisterOnTabsChangedListener(this);
-    }
-
-    private void initializeExperiment(Context context) {
-        if (!SwitchBoard.isInExperiment(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN)) {
-            Log.v(LOGTAG, "Experiment not enabled");
-            // Experiment is not enabled. No need to try to read values.
-            return;
-        }
-
-        JSONObject values = SwitchBoard.getExperimentValuesFromJson(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
-        if (values == null) {
-            // We didn't get any values for this experiment. Let's disable it instead of picking default
-            // values that might be bad.
-            return;
-        }
-
-        try {
-            initializeWithValues(
-                    values.getInt(EXPERIMENT_MINIMUM_TOTAL_VISITS),
-                    values.getInt(EXPERIMENT_LAST_VISIT_MINIMUM_AGE),
-                    values.getInt(EXPERIMENT_LAST_VISIT_MAXIMUM_AGE));
-        } catch (JSONException e) {
-            Log.w(LOGTAG, "Could not read experiment values", e);
-        }
-    }
-
-    private void initializeWithValues(int minimumVisits, int lastVisitMinimumAgeMs, int lastVisitMaximumAgeMs) {
-        this.isEnabled = true;
-
-        this.minimumVisits = minimumVisits;
-        this.lastVisitMinimumAgeMs = lastVisitMinimumAgeMs;
-        this.lastVisitMaximumAgeMs = lastVisitMaximumAgeMs;
-    }
-
-    @Override
-    public void onTabChanged(final Tab tab, Tabs.TabEvents msg, String data) {
-        if (tab == null) {
-            return;
-        }
-
-        if (!Tabs.getInstance().isSelectedTab(tab)) {
-            // We only ever want to show this promotion for the current tab.
-            return;
-        }
-
-        if (Tabs.TabEvents.LOADED != msg) {
-            return;
-        }
-
-        if (tab.isPrivate()) {
-            // Never show the prompt for private browsing tabs.
-            return;
-        }
-
-        if (isTabsTrayVisible()) {
-            // We only want to show this prompt if this tab is in the foreground and not on top
-            // of the tabs tray.
-            return;
-        }
-
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                maybeShowPromotionForUrl(tab.getURL(), tab.getTitle());
-            }
-        });
-    }
-
-    private void maybeShowPromotionForUrl(String url, String title) {
-        if (!isEnabled) {
-            return;
-        }
-
-        final Context context = activityReference.get();
-        if (context == null) {
-            return;
-        }
-
-        if (!shouldShowPromotion(context, url, title)) {
-            return;
-        }
-
-        HomeScreenPrompt.show(context, url, title);
-    }
-
-    private boolean shouldShowPromotion(Context context, String url, String title) {
-        if (TextUtils.isEmpty(url) || TextUtils.isEmpty(title)) {
-            // We require an URL and a title for the shortcut.
-            return false;
-        }
-
-        if (AboutPages.isAboutPage(url)) {
-            // No promotion for our internal sites.
-            return false;
-        }
-
-        if (!url.startsWith("https://")) {
-            // Only promote websites that are served over HTTPS.
-            return false;
-        }
-
-        URLHistory history = getHistoryForURL(context, url);
-        if (history == null) {
-            // There's no history for this URL yet or we can't read it right now. Just ignore.
-            return false;
-        }
-
-        if (history.visits < minimumVisits) {
-            // This URL has not been visited often enough.
-            return false;
-        }
-
-        if (history.lastVisit > System.currentTimeMillis() - lastVisitMinimumAgeMs) {
-            // The last visit is too new. Do not show promotion. This is mostly to avoid that the
-            // promotion shows up for a quick refreshs and in the worst case the last visit could
-            // be the current visit (race).
-            return false;
-        }
-
-        if (history.lastVisit < System.currentTimeMillis() - lastVisitMaximumAgeMs) {
-            // The last visit is to old. Do not show promotion.
-            return false;
-        }
-
-        if (hasAcceptedOrDeclinedHomeScreenShortcut(context, url)) {
-            // The user has already created a shortcut in the past or actively declined to create one.
-            // Let's not ask again for this url - We do not want to be annoying.
-            return false;
-        }
-
-        return true;
-    }
-
-    protected boolean hasAcceptedOrDeclinedHomeScreenShortcut(Context context, String url) {
-        final UrlAnnotations urlAnnotations = BrowserDB.from(context).getUrlAnnotations();
-        return urlAnnotations.hasAcceptedOrDeclinedHomeScreenShortcut(context.getContentResolver(), url);
-    }
-
-    @Nullable
-    public static URLHistory getHistoryForURL(Context context, String url) {
-        final GeckoProfile profile = GeckoProfile.get(context);
-        final BrowserDB browserDB = BrowserDB.from(profile);
-
-        Cursor cursor = null;
-        try {
-            cursor = browserDB.getHistoryForURL(context.getContentResolver(), url);
-
-            if (cursor == null) {
-                return null;
-            }
-
-            if (cursor.moveToFirst()) {
-                return new URLHistory(
-                    cursor.getInt(cursor.getColumnIndex(BrowserContract.History.VISITS)),
-                    cursor.getLong(cursor.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)));
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-
-        return null;
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/promotion/HomeScreenPrompt.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.promotion;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.mozilla.gecko.GeckoApplication;
-import org.mozilla.gecko.GeckoProfile;
-import org.mozilla.gecko.Locales;
-import org.mozilla.gecko.R;
-import org.mozilla.gecko.Telemetry;
-import org.mozilla.gecko.TelemetryContract;
-import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.db.UrlAnnotations;
-import org.mozilla.gecko.icons.IconCallback;
-import org.mozilla.gecko.icons.IconResponse;
-import org.mozilla.gecko.icons.Icons;
-import org.mozilla.gecko.Experiments;
-import org.mozilla.gecko.util.ActivityUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-
-/**
- * Prompt to promote adding the current website to the home screen.
- */
-public class HomeScreenPrompt extends Locales.LocaleAwareActivity implements IconCallback {
-    private static final String EXTRA_TITLE = "title";
-    private static final String EXTRA_URL = "url";
-
-    private static final String TELEMETRY_EXTRA = "home_screen_promotion";
-
-    private View containerView;
-    private ImageView iconView;
-    private String title;
-    private String url;
-    private boolean isAnimating;
-    private boolean hasAccepted;
-    private boolean hasDeclined;
-
-    public static void show(Context context, String url, String title) {
-        Intent intent = new Intent(context, HomeScreenPrompt.class);
-        intent.putExtra(EXTRA_TITLE, title);
-        intent.putExtra(EXTRA_URL, url);
-        context.startActivity(intent);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        fetchDataFromIntent();
-        setupViews();
-        loadShortcutIcon();
-
-        slideIn();
-
-        Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
-
-        // Technically this isn't triggered by a "service". But it's also triggered by a background task and without
-        // actual user interaction.
-        Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.SERVICE, TELEMETRY_EXTRA);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
-    }
-
-    private void fetchDataFromIntent() {
-        final Bundle extras = getIntent().getExtras();
-
-        title = extras.getString(EXTRA_TITLE);
-        url = extras.getString(EXTRA_URL);
-    }
-
-    private void setupViews() {
-        setContentView(R.layout.homescreen_prompt);
-
-        ((TextView) findViewById(R.id.title)).setText(title);
-
-        Uri uri = Uri.parse(url);
-        ((TextView) findViewById(R.id.host)).setText(uri.getHost());
-
-        containerView = findViewById(R.id.container);
-        iconView = (ImageView) findViewById(R.id.icon);
-
-        findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                hasAccepted = true;
-
-                addToHomeScreen();
-            }
-        });
-
-        findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onDecline();
-            }
-        });
-    }
-
-    private void addToHomeScreen() {
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                GeckoApplication.createBrowserShortcut(title, url);
-
-                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, TELEMETRY_EXTRA);
-
-                ActivityUtils.goToHomeScreen(HomeScreenPrompt.this);
-
-                finish();
-            }
-        });
-    }
-
-    private void loadShortcutIcon() {
-        Icons.with(this)
-                .pageUrl(url)
-                .skipNetwork()
-                .skipMemory()
-                .forLauncherIcon()
-                .build()
-                .execute(this);
-    }
-
-    private void slideIn() {
-        containerView.setTranslationY(500);
-        containerView.setAlpha(0);
-
-        final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
-        translateAnimator.setDuration(400);
-
-        final Animator alphaAnimator = ObjectAnimator.ofFloat(containerView, "alpha", 1);
-        alphaAnimator.setStartDelay(200);
-        alphaAnimator.setDuration(600);
-
-        final AnimatorSet set = new AnimatorSet();
-        set.playTogether(alphaAnimator, translateAnimator);
-        set.setStartDelay(400);
-
-        set.start();
-    }
-
-    /**
-     * Remember that the user rejected creating a home screen shortcut for this URL.
-     */
-    private void rememberRejection() {
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                final UrlAnnotations urlAnnotations = BrowserDB.from(HomeScreenPrompt.this).getUrlAnnotations();
-                urlAnnotations.insertHomeScreenShortcut(getContentResolver(), url, false);
-            }
-        });
-    }
-
-    private void slideOut() {
-        if (isAnimating) {
-            return;
-        }
-
-        isAnimating = true;
-
-        ObjectAnimator animator = ObjectAnimator.ofFloat(containerView, "translationY", containerView.getHeight());
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                finish();
-            }
-
-        });
-        animator.start();
-    }
-
-    @Override
-    public void finish() {
-        super.finish();
-
-        // Don't perform an activity-dismiss animation.
-        overridePendingTransition(0, 0);
-    }
-
-    @Override
-    public void onBackPressed() {
-        onDecline();
-    }
-
-    private void onDecline() {
-        if (hasDeclined || hasAccepted) {
-            return;
-        }
-
-        rememberRejection();
-        slideOut();
-
-        // Technically not always an action triggered by the "back" button but with the same effect: Finishing this
-        // activity and going back to the previous one.
-        Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, TELEMETRY_EXTRA);
-
-        hasDeclined = true;
-    }
-
-    /**
-     * User clicked outside of the prompt.
-     */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        onDecline();
-
-        return true;
-    }
-
-    @Override
-    public void onIconResponse(IconResponse response) {
-        iconView.setImageBitmap(response.getBitmap());
-    }
-}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -830,18 +830,16 @@ just addresses the organization to follo
 <!-- LOCALIZATION NOTE(corrupt_apk): This notification is shown if corruption has been detected on startup and the user has to reinstall Firefox -->
 <!ENTITY corrupt_apk "Unable to open &brandShortName;. Please reinstall and try again.">
 
 <!-- LOCALIZATION NOTE (whatsnew_notification_title, whatsnew_notification_summary): These strings
      are used for a system notification that's shown to users after the app updates. -->
 <!ENTITY whatsnew_notification_title "&brandShortName; is up to date">
 <!ENTITY whatsnew_notification_summary "Find out what\'s new in this version">
 
-<!ENTITY promotion_add_page_shortcut "Add page shortcut">
-
 <!ENTITY helper_first_offline_bookmark_title "Read offline">
 <!ENTITY helper_first_offline_bookmark_message "Find your Reader View items in Bookmarks, even offline.">
 <!ENTITY helper_first_offline_bookmark_button "Go to Bookmarks">
 
 <!ENTITY helper_triple_readerview_open_title "Available offline">
 <!ENTITY helper_triple_readerview_open_message "Bookmark Reader View items to read them offline.">
 <!ENTITY helper_triple_readerview_open_button "Add to Bookmarks">
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -603,18 +603,16 @@
   <string name="corrupt_apk">&corrupt_apk;</string>
   <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/honeycomb -->
 
   <string name="whatsnew_notification_title">&whatsnew_notification_title;</string>
   <string name="whatsnew_notification_summary">&whatsnew_notification_summary;</string>
   <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/new-android -->
   <string name="whatsnew_notification_url">https://support.mozilla.org/1/mobile/&formatS1;/&formatS2;/&formatS3;/new-android</string>
 
-  <string name="promotion_add_page_shortcut">&promotion_add_page_shortcut;</string>
-
   <string name="helper_first_offline_bookmark_title">&helper_first_offline_bookmark_title;</string>
   <string name="helper_first_offline_bookmark_message">&helper_first_offline_bookmark_message;</string>
   <string name="helper_first_offline_bookmark_button">&helper_first_offline_bookmark_button;</string>
 
   <string name="helper_triple_readerview_open_title">&helper_triple_readerview_open_title;</string>
   <string name="helper_triple_readerview_open_message">&helper_triple_readerview_open_message;</string>
   <string name="helper_triple_readerview_open_button">&helper_triple_readerview_open_button;</string>
 
deleted file mode 100644
--- a/mobile/android/services/src/main/res/layout/homescreen_prompt.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false">
-
-    <RelativeLayout
-        android:id="@+id/container"
-        android:layout_width="@dimen/overlay_prompt_container_width"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom|center"
-        android:background="@android:color/white"
-        android:clickable="true"
-        android:orientation="vertical">
-
-        <ImageView
-            android:id="@+id/close"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentEnd="true"
-            android:layout_marginLeft="10dp"
-            android:layout_marginStart="10dp"
-            android:layout_marginRight="30dp"
-            android:layout_marginEnd="30dp"
-            android:layout_marginTop="30dp"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:padding="6dp"
-            android:src="@drawable/tab_close_active" />
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="6dp"
-            android:layout_marginLeft="30dp"
-            android:layout_marginStart="30dp"
-            android:layout_marginTop="30dp"
-            android:layout_toLeftOf="@id/close"
-            android:layout_toStartOf="@id/close"
-            android:fontFamily="sans-serif-light"
-            android:textColor="@color/text_and_tabs_tray_grey"
-            android:textSize="20sp"
-            tools:text="The Pokedex" />
-
-        <TextView
-            android:id="@+id/host"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/title"
-            android:layout_marginBottom="20dp"
-            android:layout_marginLeft="30dp"
-            android:layout_marginStart="30dp"
-            android:layout_marginRight="30dp"
-            android:layout_marginEnd="30dp"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:textColor="@color/placeholder_grey"
-            android:textSize="16sp"
-            tools:text="pokedex.org" />
-
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="50dp"
-            android:layout_height="50dp"
-            android:layout_below="@id/host"
-            android:layout_marginBottom="20dp"
-            android:layout_marginLeft="30dp"
-            android:layout_marginStart="30dp"
-            android:src="@drawable/icon" />
-
-        <Button
-            android:id="@+id/add"
-            style="@style/Widget.BaseButton"
-            android:layout_width="wrap_content"
-            android:layout_height="50dp"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentEnd="true"
-            android:layout_below="@id/host"
-            android:layout_marginBottom="20dp"
-            android:layout_marginLeft="100dp"
-            android:layout_marginStart="100dp"
-            android:layout_marginRight="30dp"
-            android:layout_marginEnd="30dp"
-            android:background="@drawable/button_background_action_photon_round"
-            android:paddingLeft="16dp"
-            android:paddingStart="16dp"
-            android:paddingRight="16dp"
-            android:paddingEnd="16dp"
-            android:text="@string/promotion_add_page_shortcut"
-            android:maxLines="2"
-            android:ellipsize="end"
-            android:textColor="@android:color/white"
-            android:textSize="16sp" />
-
-    </RelativeLayout>
-</merge>
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -416,17 +416,17 @@ Http2Decompressor::DecodeHeaderBlock(con
   mHeaderScheme.Truncate();
   mHeaderPath.Truncate();
   mHeaderMethod.Truncate();
   mSeenNonColonHeader = false;
   mIsPush = isPush;
 
   nsresult rv = NS_OK;
   nsresult softfail_rv = NS_OK;
-  while (NS_SUCCEEDED(rv) && (mOffset < datalen)) {
+  while (NS_SUCCEEDED(rv) && (mOffset < mDataLen)) {
     bool modifiesTable = true;
     if (mData[mOffset] & 0x80) {
       rv = DoIndexed();
       LOG(("Decompressor state after indexed"));
     } else if (mData[mOffset] & 0x40) {
       rv = DoLiteralWithIncremental();
       LOG(("Decompressor state after literal with incremental"));
     } else if (mData[mOffset] & 0x20) {
@@ -698,16 +698,21 @@ Http2Decompressor::CopyStringFromInput(u
   mOffset += bytes;
   return NS_OK;
 }
 
 nsresult
 Http2Decompressor::DecodeFinalHuffmanCharacter(const HuffmanIncomingTable *table,
                                                uint8_t &c, uint8_t &bitsLeft)
 {
+  MOZ_ASSERT(mOffset <= mDataLen);
+  if (mOffset > mDataLen) {
+    NS_WARNING("DecodeFinalHuffmanCharacter would read beyond end of buffer");
+    return NS_ERROR_FAILURE;
+  }
   uint8_t mask = (1 << bitsLeft) - 1;
   uint8_t idx = mData[mOffset - 1] & mask;
   idx <<= (8 - bitsLeft);
   // Don't update bitsLeft yet, because we need to check that value against the
   // number of bits used by our encoding later on. We'll update when we are sure
   // how many bits we've actually used.
 
   if (table->IndexHasANextTable(idx)) {
@@ -735,16 +740,17 @@ Http2Decompressor::DecodeFinalHuffmanCha
   bitsLeft -= entry->mPrefixLen;
 
   return NS_OK;
 }
 
 uint8_t
 Http2Decompressor::ExtractByte(uint8_t bitsLeft, uint32_t &bytesConsumed)
 {
+  MOZ_DIAGNOSTIC_ASSERT(mOffset < mDataLen);
   uint8_t rv;
 
   if (bitsLeft) {
     // Need to extract bitsLeft bits from the previous byte, and 8 - bitsLeft
     // bits from the current byte
     uint8_t mask = (1 << bitsLeft) - 1;
     rv = (mData[mOffset - 1] & mask) << (8 - bitsLeft);
     rv |= (mData[mOffset] & ~mask) >> bitsLeft;
@@ -764,18 +770,18 @@ Http2Decompressor::ExtractByte(uint8_t b
 nsresult
 Http2Decompressor::DecodeHuffmanCharacter(const HuffmanIncomingTable *table,
                                           uint8_t &c, uint32_t &bytesConsumed,
                                           uint8_t &bitsLeft)
 {
   uint8_t idx = ExtractByte(bitsLeft, bytesConsumed);
 
   if (table->IndexHasANextTable(idx)) {
-    if (bytesConsumed >= mDataLen) {
-      if (!bitsLeft || (bytesConsumed > mDataLen)) {
+    if (mOffset >= mDataLen) {
+      if (!bitsLeft || (mOffset > mDataLen)) {
         // TODO - does this get me into trouble in the new world?
         // No info left in input to try to consume, we're done
         LOG(("DecodeHuffmanCharacter all out of bits to consume, can't chain"));
         return NS_ERROR_FAILURE;
       }
 
       // We might get lucky here!
       return DecodeFinalHuffmanCharacter(table->NextTable(idx), c, bitsLeft);
@@ -906,16 +912,23 @@ Http2Decompressor::DoLiteralInternal(nsA
 
   // first let's get the name
   uint32_t index;
   nsresult rv = DecodeInteger(namePrefixLen, index);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  // sanity check
+  if (mOffset >= mDataLen) {
+    NS_WARNING("Http2 Decompressor ran out of data");
+    // This is session-fatal
+    return NS_ERROR_FAILURE;
+  }
+
   bool isHuffmanEncoded;
 
   if (!index) {
     // name is embedded as a literal
     uint32_t nameLen;
     isHuffmanEncoded = mData[mOffset] & (1 << 7);
     rv = DecodeInteger(7, nameLen);
     if (NS_SUCCEEDED(rv)) {
@@ -933,16 +946,23 @@ Http2Decompressor::DoLiteralInternal(nsA
     rv = CopyHeaderString(index - 1, name);
     LOG(("Http2Decompressor::DoLiteralInternal indexed name %d %s",
          index, name.BeginReading()));
   }
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  // sanity check
+  if (mOffset >= mDataLen) {
+    NS_WARNING("Http2 Decompressor ran out of data");
+    // This is session-fatal
+    return NS_ERROR_FAILURE;
+  }
+
   // now the value
   uint32_t valueLen;
   isHuffmanEncoded = mData[mOffset] & (1 << 7);
   rv = DecodeInteger(7, valueLen);
   if (NS_SUCCEEDED(rv)) {
     if (isHuffmanEncoded) {
       rv = CopyHuffmanStringFromInput(valueLen, value);
     } else {
new file mode 100644
--- /dev/null
+++ b/testing/config/marionette_source_requirements.txt
@@ -0,0 +1,17 @@
+# These dependencies are required for running marionette based tests from a source checkout
+
+-r mozbase_source_requirements.txt
+
+../web-platform/tests/tools/wptserve
+../web-platform/tests/tools/third_party/certifi
+../web-platform/tests/tools/third_party/enum
+../web-platform/tests/tools/third_party/h2
+../web-platform/tests/tools/third_party/hyperframe
+../web-platform/tests/tools/third_party/hpack
+
+../marionette/client
+../marionette/harness
+../marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py
+
+# Allows to use the Puppeteer page object model for Firefox
+../marionette/puppeteer/firefox/
rename from testing/config/telemetry_tests_requirements.txt
rename to testing/config/telemetry_tests_source_requirements.txt
--- a/testing/config/telemetry_tests_requirements.txt
+++ b/testing/config/telemetry_tests_source_requirements.txt
@@ -1,9 +1,5 @@
--r mozbase_requirements.txt
-
-../marionette/client/
-../marionette/harness/
+# These dependencies are required for running telemetry tests from a source checkout
 
-# Allows to use the Puppeteer page object model for Firefox
-../marionette/puppeteer/firefox/
+-r marionette_source_requirements.txt
 
-../../toolkit/components/telemetry/tests/marionette/harness
\ No newline at end of file
+../../toolkit/components/telemetry/tests/marionette/harness
--- a/testing/marionette/harness/requirements.txt
+++ b/testing/marionette/harness/requirements.txt
@@ -7,9 +7,9 @@ mozinfo >= 0.8
 mozlog >= 3.0
 moznetwork >= 0.21
 mozprocess >= 0.9
 mozprofile >= 0.7
 mozrunner >= 7.0.1
 moztest >= 0.8
 mozversion >= 1.1
 six
-wptserve >= 1.3.0
+wptserve >= 2.0.0
--- a/testing/mozbase/moztest/moztest/resolve.py
+++ b/testing/mozbase/moztest/moztest/resolve.py
@@ -448,18 +448,24 @@ class TestMetadata(object):
     def add_wpt_manifest_data(self):
         if self._wpt_loaded:
             return
 
         wpt_path = os.path.join(self._srcdir, "testing", "web-platform")
         sys.path = [wpt_path] + sys.path
 
         import manifestupdate
+        # Set up a logger that will drop all the output
+        import logging
+        logger = logging.getLogger("manifestupdate")
+        logger.propogate = False
+
         manifests = manifestupdate.run(self._srcdir, self._objdir, rebuild=False, download=True,
-                                       config_path=None, rewrite_config=True, update=True)
+                                       config_path=None, rewrite_config=True, update=True,
+                                       logger=logger)
         if not manifests:
             print("Loading wpt manifest failed")
             return
 
         for manifest, data in manifests.iteritems():
             tests_root = data["tests_path"]
             for test_type, path, tests in manifest:
                 full_path = os.path.join(tests_root, path)
--- a/testing/mozharness/scripts/telemetry/telemetry_client.py
+++ b/testing/mozharness/scripts/telemetry/telemetry_client.py
@@ -97,19 +97,22 @@ class TelemetryTests(TestingMixin, VCSTo
         self.test_url = self.config.get('test_url')
 
         if not self.test_url and not self.test_packages_url:
             self.fatal(
                 'You must use --test-url, or --test-packages-url')
 
     @PreScriptAction('create-virtualenv')
     def _pre_create_virtualenv(self, action):
-
-        requirements = os.path.join(GECKO_SRCDIR, 'testing',
-                                    'config', 'telemetry_tests_requirements.txt')
+        requirements = os.path.join(
+            GECKO_SRCDIR,
+            'testing',
+            'config',
+            'telemetry_tests_source_requirements.txt',
+        )
         self.register_virtualenv_module(requirements=[requirements], two_pass=True)
 
     def query_abs_dirs(self):
         if self.abs_dirs:
             return self.abs_dirs
 
         abs_dirs = super(TelemetryTests, self).query_abs_dirs()
 
@@ -174,17 +177,17 @@ class TelemetryTests(TestingMixin, VCSTo
             env['GCOV_PREFIX'] = self.gcov_dir
 
         return_code = self.run_command(cmd,
                                        cwd=dirs['abs_work_dir'],
                                        output_timeout=300,
                                        output_parser=parser,
                                        env=env)
 
-        tbpl_status, log_level = parser.evaluate_parser(return_code)
+        tbpl_status, log_level, _ = parser.evaluate_parser(return_code)
         self.record_status(tbpl_status, level=log_level)
 
         return return_code
 
     @PreScriptAction('run-tests')
     def _pre_run_tests(self, action):
         if not self.installer_path and not self.installer_url:
             self.critical('Please specify an installer via --installer-path or --installer-url.')
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/CHANGES.rst
@@ -0,0 +1,635 @@
+Click Changelog
+===============
+
+
+Version 7.0
+-----------
+
+Released 2018-09-25
+
+-   Drop support for Python 2.6 and 3.3. (`#967`_, `#976`_)
+-   Wrap ``click.Choice``'s missing message. (`#202`_, `#1000`_)
+-   Add native ZSH autocompletion support. (`#323`_, `#865`_)
+-   Document that ANSI color info isn't parsed from bytearrays in
+    Python 2. (`#334`_)
+-   Document byte-stripping behavior of ``CliRunner``. (`#334`_,
+    `#1010`_)
+-   Usage errors now hint at the ``--help`` option. (`#393`_, `#557`_)
+-   Implement streaming pager. (`#409`_, `#889`_)
+-   Extract bar formatting to its own method. (`#414`_)
+-   Add ``DateTime`` type for converting input in given date time
+    formats. (`#423`_)
+-   ``secho``'s first argument can now be ``None``, like in ``echo``.
+    (`#424`_)
+-   Fixes a ``ZeroDivisionError`` in ``ProgressBar.make_step``, when the
+    arg passed to the first call of ``ProgressBar.update`` is 0.
+    (`#447`_, `#1012`_)
+-   Show progressbar only if total execution time is visible. (`#487`_)
+-   Added the ability to hide commands and options from help. (`#500`_)
+-   Document that options can be ``required=True``. (`#514`_, `#1022`_)
+-   Non-standalone calls to ``Context.exit`` return the exit code,
+    rather than calling ``sys.exit``. (`#533`_, `#667`_, `#1098`_)
+-   ``click.getchar()`` returns Unicode in Python 3 on Windows,
+    consistent with other platforms. (`#537`_, `#821`_, `#822`_,
+    `#1088`_, `#1108`_)
+-   Added ``FloatRange`` type. (`#538`_, `#553`_)
+-   Added support for bash completion of ``type=click.Choice`` for
+    ``Options`` and ``Arguments``. (`#535`_, `#681`_)
+-   Only allow one positional arg for ``Argument`` parameter
+    declaration. (`#568`_, `#574`_, `#1014`_)
+-   Add ``case_sensitive=False`` as an option to Choice. (`#569`_)
+-   ``click.getchar()`` correctly raises ``KeyboardInterrupt`` on "^C"
+    and ``EOFError`` on "^D" on Linux. (`#583`_, `#1115`_)
+-   Fix encoding issue with ``click.getchar(echo=True)`` on Linux.
+    (`#1115`_)
+-   ``param_hint`` in errors now derived from param itself. (`#598`_,
+    `#704`_, `#709`_)
+-   Add a test that ensures that when an argument is formatted into a
+    usage error, its metavar is used, not its name. (`#612`_)
+-   Allow setting ``prog_name`` as extra in ``CliRunner.invoke``.
+    (`#616`_, `#999`_)
+-   Help text taken from docstrings truncates at the ``\f`` form feed
+    character, useful for hiding Sphinx-style parameter documentation.
+    (`#629`_, `#1091`_)
+-   ``launch`` now works properly under Cygwin. (`#650`_)
+-   Update progress after iteration. (`#651`_, `#706`_)
+-   ``CliRunner.invoke`` now may receive ``args`` as a string
+    representing a Unix shell command. (`#664`_)
+-   Make ``Argument.make_metavar()`` default to type metavar. (`#675`_)
+-   Add documentation for ``ignore_unknown_options``. (`#684`_)
+-   Add bright colors support for ``click.style`` and fix the reset
+    option for parameters ``fg`` and ``bg``. (`#703`_, `#809`_)
+-   Add ``show_envvar`` for showing environment variables in help.
+    (`#710`_)
+-   Avoid ``BrokenPipeError`` during interpreter shutdown when stdout or
+    stderr is a closed pipe. (`#712`_, `#1106`_)
+-   Document customizing option names. (`#725`_, `#1016`_)
+-   Disable ``sys._getframes()`` on Python interpreters that don't
+    support it. (`#728`_)
+-   Fix bug in test runner when calling ``sys.exit`` with ``None``.
+    (`#739`_)
+-   Clarify documentation on command line options. (`#741`_, `#1003`_)
+-   Fix crash on Windows console. (`#744`_)
+-   Fix bug that caused bash completion to give improper completions on
+    chained commands. (`#754`_, `#774`_)
+-   Added support for dynamic bash completion from a user-supplied
+    callback. (`#755`_)
+-   Added support for bash completions containing spaces. (`#773`_)
+-   Allow autocompletion function to determine whether or not to return
+    completions that start with the incomplete argument. (`#790`_,
+    `#806`_)
+-   Fix option naming routine to match documentation and be
+    deterministic. (`#793`_, `#794`_)
+-   Fix path validation bug. (`#795`_, `#1020`_)
+-   Add test and documentation for ``Option`` naming: functionality.
+    (`#799`_)
+-   Update doc to match arg name for ``path_type``. (`#801`_)
+-   Raw strings added so correct escaping occurs. (`#807`_)
+-   Fix 16k character limit of ``click.echo`` on Windows. (`#816`_,
+    `#819`_)
+-   Overcome 64k character limit when writing to binary stream on
+    Windows 7. (`#825`_, `#830`_)
+-   Add bool conversion for "t" and "f". (`#842`_)
+-   ``NoSuchOption`` errors take ``ctx`` so that ``--help`` hint gets
+    printed in error output. (`#860`_)
+-   Fixed the behavior of Click error messages with regards to Unicode
+    on 2.x and 3.x. Message is now always Unicode and the str and
+    Unicode special methods work as you expect on that platform.
+    (`#862`_)
+-   Progress bar now uses stderr by default. (`#863`_)
+-   Add support for auto-completion documentation. (`#866`_, `#869`_)
+-   Allow ``CliRunner`` to separate stdout and stderr. (`#868`_)
+-   Fix variable precedence. (`#873`_, `#874`_)
+-   Fix invalid escape sequences. (`#877`_)
+-   Fix ``ResourceWarning`` that occurs during some tests. (`#878`_)
+-   When detecting a misconfigured locale, don't fail if the ``locale``
+    command fails. (`#880`_)
+-   Add ``case_sensitive=False`` as an option to ``Choice`` types.
+    (`#887`_)
+-   Force stdout/stderr writable. This works around issues with badly
+    patched standard streams like those from Jupyter. (`#918`_)
+-   Fix completion of subcommand options after last argument (`#919`_,
+    `#930`_)
+-   ``_AtomicFile`` now uses the ``realpath`` of the original filename
+    so that changing the working directory does not affect it.
+    (`#920`_)
+-   Fix incorrect completions when defaults are present (`#925`_,
+    `#930`_)
+-   Add copy option attrs so that custom classes can be re-used.
+    (`#926`_, `#994`_)
+-   "x" and "a" file modes now use stdout when file is ``"-"``.
+    (`#929`_)
+-   Fix missing comma in ``__all__`` list. (`#935`_)
+-   Clarify how parameters are named. (`#949`_, `#1009`_)
+-   Stdout is now automatically set to non blocking. (`#954`_)
+-   Do not set options twice. (`#962`_)
+-   Move ``fcntl`` import. (`#965`_)
+-   Fix Google App Engine ``ImportError``. (`#995`_)
+-   Better handling of help text for dynamic default option values.
+    (`#996`_)
+-   Fix ``get_winter_size()`` so it correctly returns ``(0,0)``.
+    (`#997`_)
+-   Add test case checking for custom param type. (`#1001`_)
+-   Allow short width to address cmd formatting. (`#1002`_)
+-   Add details about Python version support. (`#1004`_)
+-   Added deprecation flag to commands. (`#1005`_)
+-   Fixed issues where ``fd`` was undefined. (`#1007`_)
+-   Fix formatting for short help. (`#1008`_)
+-   Document how ``auto_envvar_prefix`` works with command groups.
+    (`#1011`_)
+-   Don't add newlines by default for progress bars. (`#1013`_)
+-   Use Python sorting order for ZSH completions. (`#1047`_, `#1059`_)
+-   Document that parameter names are converted to lowercase by default.
+    (`#1055`_)
+-   Subcommands that are named by the function now automatically have
+    the underscore replaced with a dash. If you register a function
+    named ``my_command`` it becomes ``my-command`` in the command line
+    interface.
+-   Hide hidden commands and options from completion. (`#1058`_,
+    `#1061`_)
+-   Fix absolute import blocking Click from being vendored into a
+    project on Windows. (`#1068`_, `#1069`_)
+-   Fix issue where a lowercase ``auto_envvar_prefix`` would not be
+    converted to uppercase. (`#1105`_)
+
+.. _#202: https://github.com/pallets/click/issues/202
+.. _#323: https://github.com/pallets/click/issues/323
+.. _#334: https://github.com/pallets/click/issues/334
+.. _#393: https://github.com/pallets/click/issues/393
+.. _#409: https://github.com/pallets/click/issues/409
+.. _#414: https://github.com/pallets/click/pull/414
+.. _#423: https://github.com/pallets/click/pull/423
+.. _#424: https://github.com/pallets/click/pull/424
+.. _#447: https://github.com/pallets/click/issues/447
+.. _#487: https://github.com/pallets/click/pull/487
+.. _#500: https://github.com/pallets/click/pull/500
+.. _#514: https://github.com/pallets/click/issues/514
+.. _#533: https://github.com/pallets/click/pull/533
+.. _#535: https://github.com/pallets/click/issues/535
+.. _#537: https://github.com/pallets/click/issues/537
+.. _#538: https://github.com/pallets/click/pull/538
+.. _#553: https://github.com/pallets/click/pull/553
+.. _#557: https://github.com/pallets/click/pull/557
+.. _#568: https://github.com/pallets/click/issues/568
+.. _#569: https://github.com/pallets/click/issues/569
+.. _#574: https://github.com/pallets/click/issues/574
+.. _#583: https://github.com/pallets/click/issues/583
+.. _#598: https://github.com/pallets/click/issues/598
+.. _#612: https://github.com/pallets/click/pull/612
+.. _#616: https://github.com/pallets/click/issues/616
+.. _#629: https://github.com/pallets/click/pull/629
+.. _#650: https://github.com/pallets/click/pull/650
+.. _#651: https://github.com/pallets/click/issues/651
+.. _#664: https://github.com/pallets/click/pull/664
+.. _#667: https://github.com/pallets/click/issues/667
+.. _#675: https://github.com/pallets/click/pull/675
+.. _#681: https://github.com/pallets/click/pull/681
+.. _#684: https://github.com/pallets/click/pull/684
+.. _#703: https://github.com/pallets/click/issues/703
+.. _#704: https://github.com/pallets/click/issues/704
+.. _#706: https://github.com/pallets/click/pull/706
+.. _#709: https://github.com/pallets/click/pull/709
+.. _#710: https://github.com/pallets/click/pull/710
+.. _#712: https://github.com/pallets/click/pull/712
+.. _#719: https://github.com/pallets/click/issues/719
+.. _#725: https://github.com/pallets/click/issues/725
+.. _#728: https://github.com/pallets/click/pull/728
+.. _#739: https://github.com/pallets/click/pull/739
+.. _#741: https://github.com/pallets/click/issues/741
+.. _#744: https://github.com/pallets/click/issues/744
+.. _#754: https://github.com/pallets/click/issues/754
+.. _#755: https://github.com/pallets/click/pull/755
+.. _#773: https://github.com/pallets/click/pull/773
+.. _#774: https://github.com/pallets/click/pull/774
+.. _#790: https://github.com/pallets/click/issues/790
+.. _#793: https://github.com/pallets/click/issues/793
+.. _#794: https://github.com/pallets/click/pull/794
+.. _#795: https://github.com/pallets/click/issues/795
+.. _#799: https://github.com/pallets/click/pull/799
+.. _#801: https://github.com/pallets/click/pull/801
+.. _#806: https://github.com/pallets/click/pull/806
+.. _#807: https://github.com/pallets/click/pull/807
+.. _#809: https://github.com/pallets/click/pull/809
+.. _#816: https://github.com/pallets/click/pull/816
+.. _#819: https://github.com/pallets/click/pull/819
+.. _#821: https://github.com/pallets/click/issues/821
+.. _#822: https://github.com/pallets/click/issues/822
+.. _#825: https://github.com/pallets/click/issues/825
+.. _#830: https://github.com/pallets/click/pull/830
+.. _#842: https://github.com/pallets/click/pull/842
+.. _#860: https://github.com/pallets/click/issues/860
+.. _#862: https://github.com/pallets/click/issues/862
+.. _#863: https://github.com/pallets/click/pull/863
+.. _#865: https://github.com/pallets/click/pull/865
+.. _#866: https://github.com/pallets/click/issues/866
+.. _#868: https://github.com/pallets/click/pull/868
+.. _#869: https://github.com/pallets/click/pull/869
+.. _#873: https://github.com/pallets/click/issues/873
+.. _#874: https://github.com/pallets/click/pull/874
+.. _#877: https://github.com/pallets/click/pull/877
+.. _#878: https://github.com/pallets/click/pull/878
+.. _#880: https://github.com/pallets/click/pull/880
+.. _#883: https://github.com/pallets/click/pull/883
+.. _#887: https://github.com/pallets/click/pull/887
+.. _#889: https://github.com/pallets/click/pull/889
+.. _#918: https://github.com/pallets/click/pull/918
+.. _#919: https://github.com/pallets/click/issues/919
+.. _#920: https://github.com/pallets/click/pull/920
+.. _#925: https://github.com/pallets/click/issues/925
+.. _#926: https://github.com/pallets/click/issues/926
+.. _#929: https://github.com/pallets/click/pull/929
+.. _#930: https://github.com/pallets/click/pull/930
+.. _#935: https://github.com/pallets/click/pull/935
+.. _#949: https://github.com/pallets/click/issues/949
+.. _#954: https://github.com/pallets/click/pull/954
+.. _#962: https://github.com/pallets/click/pull/962
+.. _#965: https://github.com/pallets/click/pull/965
+.. _#967: https://github.com/pallets/click/pull/967
+.. _#976: https://github.com/pallets/click/pull/976
+.. _#990: https://github.com/pallets/click/pull/990
+.. _#991: https://github.com/pallets/click/pull/991
+.. _#993: https://github.com/pallets/click/pull/993
+.. _#994: https://github.com/pallets/click/pull/994
+.. _#995: https://github.com/pallets/click/pull/995
+.. _#996: https://github.com/pallets/click/pull/996
+.. _#997: https://github.com/pallets/click/pull/997
+.. _#999: https://github.com/pallets/click/pull/999
+.. _#1000: https://github.com/pallets/click/pull/1000
+.. _#1001: https://github.com/pallets/click/pull/1001
+.. _#1002: https://github.com/pallets/click/pull/1002
+.. _#1003: https://github.com/pallets/click/pull/1003
+.. _#1004: https://github.com/pallets/click/pull/1004
+.. _#1005: https://github.com/pallets/click/pull/1005
+.. _#1007: https://github.com/pallets/click/pull/1007
+.. _#1008: https://github.com/pallets/click/pull/1008
+.. _#1009: https://github.com/pallets/click/pull/1009
+.. _#1010: https://github.com/pallets/click/pull/1010
+.. _#1011: https://github.com/pallets/click/pull/1011
+.. _#1012: https://github.com/pallets/click/pull/1012
+.. _#1013: https://github.com/pallets/click/pull/1013
+.. _#1014: https://github.com/pallets/click/pull/1014
+.. _#1016: https://github.com/pallets/click/pull/1016
+.. _#1020: https://github.com/pallets/click/pull/1020
+.. _#1022: https://github.com/pallets/click/pull/1022
+.. _#1027: https://github.com/pallets/click/pull/1027
+.. _#1047: https://github.com/pallets/click/pull/1047
+.. _#1055: https://github.com/pallets/click/pull/1055
+.. _#1058: https://github.com/pallets/click/pull/1058
+.. _#1059: https://github.com/pallets/click/pull/1059
+.. _#1061: https://github.com/pallets/click/pull/1061
+.. _#1068: https://github.com/pallets/click/issues/1068
+.. _#1069: https://github.com/pallets/click/pull/1069
+.. _#1088: https://github.com/pallets/click/issues/1088
+.. _#1091: https://github.com/pallets/click/pull/1091
+.. _#1098: https://github.com/pallets/click/pull/1098
+.. _#1105: https://github.com/pallets/click/pull/1105
+.. _#1106: https://github.com/pallets/click/pull/1106
+.. _#1108: https://github.com/pallets/click/pull/1108
+.. _#1115: https://github.com/pallets/click/pull/1115
+
+
+Version 6.7
+-----------
+
+(bugfix release; released on January 6th 2017)
+
+- Make ``click.progressbar`` work with ``codecs.open`` files. See #637.
+- Fix bug in bash completion with nested subcommands. See #639.
+- Fix test runner not saving caller env correctly. See #644.
+- Fix handling of SIGPIPE. See #626
+- Deal with broken Windows environments such as Google App Engine's. See #711.
+
+Version 6.6
+-----------
+
+(bugfix release; released on April 4th 2016)
+
+- Fix bug in ``click.Path`` where it would crash when passed a ``-``. See #551.
+
+Version 6.4
+-----------
+
+(bugfix release; released on March 24th 2016)
+
+- Fix bug in bash completion where click would discard one or more trailing
+  arguments. See #471.
+
+Version 6.3
+-----------
+
+(bugfix release; released on February 22 2016)
+
+- Fix argument checks for interpreter invoke with ``-m`` and ``-c``
+  on Windows.
+- Fixed a bug that cased locale detection to error out on Python 3.
+
+Version 6.2
+-----------
+
+(bugfix release, released on November 27th 2015)
+
+- Correct fix for hidden progress bars.
+
+Version 6.1
+-----------
+
+(bugfix release, released on November 27th 2015)
+
+- Resolved an issue with invisible progress bars no longer rendering.
+- Disable chain commands with subcommands as they were inherently broken.
+- Fix ``MissingParameter`` not working without parameters passed.
+
+Version 6.0
+-----------
+
+(codename "pow pow", released on November 24th 2015)
+
+- Optimized the progressbar rendering to not render when it did not
+  actually change.
+- Explicitly disallow ``nargs=-1`` with a set default.
+- The context is now closed before it's popped from the stack.
+- Added support for short aliases for the false flag on toggles.
+- Click will now attempt to aid you with debugging locale errors
+  better by listing with the help of the OS what locales are
+  available.
+- Click used to return byte strings on Python 2 in some unit-testing
+  situations.  This has been fixed to correctly return unicode strings
+  now.
+- For Windows users on Python 2, Click will now handle Unicode more
+  correctly handle Unicode coming in from the system.  This also has
+  the disappointing side effect that filenames will now be always
+  unicode by default in the ``Path`` type which means that this can
+  introduce small bugs for code not aware of this.
+- Added a ``type`` parameter to ``Path`` to force a specific string type
+  on the value.
+- For users running Python on Windows the ``echo`` and ``prompt`` functions
+  now work with full unicode functionality in the Python windows console
+  by emulating an output stream.  This also applies to getting the
+  virtual output and input streams via ``click.get_text_stream(...)``.
+- Unittests now always force a certain virtual terminal width.
+- Added support for allowing dashes to indicate standard streams to the
+  ``Path`` type.
+- Multi commands in chain mode no longer propagate arguments left over
+  from parsing to the callbacks.  It's also now disallowed through an
+  exception when optional arguments are attached to multi commands if chain
+  mode is enabled.
+- Relaxed restriction that disallowed chained commands to have other
+  chained commands as child commands.
+- Arguments with positive nargs can now have defaults implemented.
+  Previously this configuration would often result in slightly unexpected
+  values be returned.
+
+Version 5.1
+-----------
+
+(bugfix release, released on 17th August 2015)
+
+- Fix a bug in ``pass_obj`` that would accidentally pass the context too.
+
+Version 5.0
+-----------
+
+(codename "tok tok", released on 16th August 2015)
+
+- Removed various deprecated functionality.
+- Atomic files now only accept the ``w`` mode.
+- Change the usage part of help output for very long commands to wrap
+  their arguments onto the next line, indented by 4 spaces.
+- Fix a bug where return code and error messages were incorrect when
+  using ``CliRunner``.
+- added ``get_current_context``.
+- added a ``meta`` dictionary to the context which is shared across the
+  linked list of contexts to allow click utilities to place state there.
+- introduced ``Context.scope``.
+- The ``echo`` function is now threadsafe: It calls the ``write`` method of the
+  underlying object only once.
+- ``prompt(hide_input=True)`` now prints a newline on ``^C``.
+- Click will now warn if users are using ``unicode_literals``.
+- Click will now ignore the ``PAGER`` environment variable if it is empty or
+  contains only whitespace.
+- The ``click-contrib`` GitHub organization was created.
+
+Version 4.1
+-----------
+
+(bugfix release, released on July 14th 2015)
+
+- Fix a bug where error messages would include a trailing ``None`` string.
+- Fix a bug where Click would crash on docstrings with trailing newlines.
+- Support streams with encoding set to ``None`` on Python 3 by barfing with
+  a better error.
+- Handle ^C in less-pager properly.
+- Handle return value of ``None`` from ``sys.getfilesystemencoding``
+- Fix crash when writing to unicode files with ``click.echo``.
+- Fix type inference with multiple options.
+
+Version 4.0
+-----------
+
+(codename "zoom zoom", released on March 31st 2015)
+
+- Added ``color`` parameters to lots of interfaces that directly or indirectly
+  call into echoing.  This previously was always autodetection (with the
+  exception of the ``echo_via_pager`` function).  Now you can forcefully
+  enable or disable it, overriding the auto detection of Click.
+- Added an ``UNPROCESSED`` type which does not perform any type changes which
+  simplifies text handling on 2.x / 3.x in some special advanced usecases.
+- Added ``NoSuchOption`` and ``BadOptionUsage`` exceptions for more generic
+  handling of errors.
+- Added support for handling of unprocessed options which can be useful in
+  situations where arguments are forwarded to underlying tools.
+- Added ``max_content_width`` parameter to the context which can be used to
+  change the maximum width of help output.  By default Click will not format
+  content for more than 80 characters width.
+- Added support for writing prompts to stderr.
+- Fix a bug when showing the default for multiple arguments.
+- Added support for custom subclasses to ``option`` and ``argument``.
+- Fix bug in ``clear()`` on Windows when colorama is installed.
+- Reject ``nargs=-1`` for options properly.  Options cannot be variadic.
+- Fixed an issue with bash completion not working properly for commands with
+  non ASCII characters or dashes.
+- Added a way to manually update the progressbar.
+- Changed the formatting of missing arguments.  Previously the internal
+  argument name was shown in error messages, now the metavar is shown if
+  passed.  In case an automated metavar is selected, it's stripped of
+  extra formatting first.
+
+Version 3.3
+-----------
+
+(bugfix release, released on September 8th 2014)
+
+- Fixed an issue with error reporting on Python 3 for invalid forwarding
+  of commands.
+
+Version 3.2
+-----------
+
+(bugfix release, released on August 22nd 2014)
+
+- Added missing ``err`` parameter forwarding to the ``secho`` function.
+- Fixed default parameters not being handled properly by the context
+  invoke method.  This is a backwards incompatible change if the function
+  was used improperly.  See :ref:`upgrade-to-3.2` for more information.
+- Removed the `invoked_subcommands` attribute largely.  It is not possible
+  to provide it to work error free due to how the parsing works so this
+  API has been deprecated.  See :ref:`upgrade-to-3.2` for more information.
+- Restored the functionality of `invoked_subcommand` which was broken as
+  a regression in 3.1.
+
+Version 3.1
+-----------
+
+(bugfix release, released on August 13th 2014)
+
+- Fixed a regression that caused contexts of subcommands to be
+  created before the parent command was invoked which was a
+  regression from earlier Click versions.
+
+Version 3.0
+-----------
+
+(codename "clonk clonk", released on August 12th 2014)
+
+- formatter now no longer attempts to accomodate for terminals
+  smaller than 50 characters.  If that happens it just assumes
+  a minimal width.
+- added a way to not swallow exceptions in the test system.
+- added better support for colors with pagers and ways to
+  override the autodetection.
+- the CLI runner's result object now has a traceback attached.
+- improved automatic short help detection to work better with
+  dots that do not terminate sentences.
+- when definining options without actual valid option strings
+  now, Click will give an error message instead of silently
+  passing.  This should catch situations where users wanted to
+  created arguments instead of options.
+- Restructured Click internally to support vendoring.
+- Added support for multi command chaining.
+- Added support for defaults on options with ``multiple`` and
+  options and arguments with ``nargs != 1``.
+- label passed to ``progressbar`` is no longer rendered with
+  whitespace stripped.
+- added a way to disable the standalone mode of the ``main``
+  method on a Click command to be able to handle errors better.
+- added support for returning values from command callbacks.
+- added simplifications for printing to stderr from ``echo``.
+- added result callbacks for groups.
+- entering a context multiple times defers the cleanup until
+  the last exit occurs.
+- added ``open_file``.
+
+Version 2.6
+-----------
+
+(bugfix release, released on August 11th 2014)
+
+- Fixed an issue where the wrapped streams on Python 3 would be reporting
+  incorrect values for seekable.
+
+Version 2.5
+-----------
+
+(bugfix release, released on July 28th 2014)
+
+- Fixed a bug with text wrapping on Python 3.
+
+Version 2.4
+-----------
+
+(bugfix release, released on July 4th 2014)
+
+- Corrected a bug in the change of the help option in 2.3.
+
+Version 2.3
+-----------
+
+(bugfix release, released on July 3rd 2014)
+
+- Fixed an incorrectly formatted help record for count options.
+- Add support for ansi code stripping on Windows if colorama
+  is not available.
+- restored the Click 1.0 handling of the help parameter for certain
+  edge cases.
+
+Version 2.2
+-----------
+
+(bugfix release, released on June 26th 2014)
+
+- fixed tty detection on PyPy.
+- fixed an issue that progress bars were not rendered when the
+  context manager was entered.
+
+Version 2.1
+-----------
+
+(bugfix release, released on June 14th 2014)
+
+- fixed the :func:`launch` function on windows.
+- improved the colorama support on windows to try hard to not
+  screw up the console if the application is interrupted.
+- fixed windows terminals incorrectly being reported to be 80
+  characters wide instead of 79
+- use colorama win32 bindings if available to get the correct
+  dimensions of a windows terminal.
+- fixed an issue with custom function types on Python 3.
+- fixed an issue with unknown options being incorrectly reported
+  in error messages.
+
+Version 2.0
+-----------
+
+(codename "tap tap tap", released on June 6th 2014)
+
+- added support for opening stdin/stdout on Windows in
+  binary mode correctly.
+- added support for atomic writes to files by going through
+  a temporary file.
+- introduced :exc:`BadParameter` which can be used to easily perform
+  custom validation with the same error messages as in the type system.
+- added :func:`progressbar`; a function to show progress bars.
+- added :func:`get_app_dir`; a function to calculate the home folder
+  for configs.
+- Added transparent handling for ANSI codes into the :func:`echo`
+  function through ``colorama``.
+- Added :func:`clear` function.
+- Breaking change: parameter callbacks now get the parameter object
+  passed as second argument.  There is legacy support for old callbacks
+  which will warn but still execute the script.
+- Added :func:`style`, :func:`unstyle` and :func:`secho` for ANSI
+  styles.
+- Added an :func:`edit` function that invokes the default editor.
+- Added an :func:`launch` function that launches browsers and applications.
+- nargs of -1 for arguments can now be forced to be a single item through
+  the required flag.  It defaults to not required.
+- setting a default for arguments now implicitly makes it non required.
+- changed "yN" / "Yn" to "y/N" and "Y/n" in confirmation prompts.
+- added basic support for bash completion.
+- added :func:`getchar` to fetch a single character from the terminal.
+- errors now go to stderr as intended.
+- fixed various issues with more exotic parameter formats like DOS/Windows
+  style arguments.
+- added :func:`pause` which works similar to the Windows ``pause`` cmd
+  built-in but becomes an automatic noop if the application is not run
+  through a terminal.
+- added a bit of extra information about missing choice parameters.
+- changed how the help function is implemented to allow global overriding
+  of the help option.
+- added support for token normalization to implement case insensitive handling.
+- added support for providing defaults for context settings.
+
+Version 1.1
+-----------
+
+(bugfix release, released on May 23rd 2014)
+
+- fixed a bug that caused text files in Python 2 to not accept
+  native strings.
+
+Version 1.0
+-----------
+
+(no codename, released on May 21st 2014)
+
+- Initial release.
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/CONTRIBUTING.rst
@@ -0,0 +1,61 @@
+==========================
+How to contribute to Click
+==========================
+
+Thanks for considering contributing to Click.
+
+Support questions
+=================
+
+Please, don't use the issue tracker for this. Check whether the
+``#pocoo`` IRC channel on Freenode can help with your issue. If your problem
+is not strictly Click-specific, ``#python`` on Freenode is generally more
+active.  `StackOverflow <https://stackoverflow.com/>`_ is also worth
+considering.
+
+Reporting issues
+================
+
+- Under which versions of Python does this happen? This is even more important
+  if your issue is encoding related.
+
+- Under which versions of Click does this happen? Check if this issue is fixed
+  in the repository.
+
+Submitting patches
+==================
+
+- Include tests if your patch is supposed to solve a bug, and explain clearly
+  under which circumstances the bug happens. Make sure the test fails without
+  your patch.
+
+- Try to follow `PEP8 <http://legacy.python.org/dev/peps/pep-0008/>`_, but you
+  may ignore the line-length-limit if following it would make the code uglier.
+
+- For features: Consider whether your feature would be a better fit for an
+  `external package <https://click.palletsprojects.com/en/7.x/contrib/>`_
+
+- For docs and bug fixes: Submit against the latest maintenance branch instead of master!
+
+Running the testsuite
+---------------------
+
+You probably want to set up a `virtualenv
+<https://virtualenv.readthedocs.io/en/latest/index.html>`_.
+
+The minimal requirement for running the testsuite is ``py.test``.  You can
+install it with::
+
+    pip install pytest
+
+Then you can run the testsuite with::
+
+    py.test
+
+For a more isolated test environment, you can also install ``tox`` instead of
+``pytest``. You can install it with::
+
+    pip install tox
+
+The ``tox`` command will then run all tests against multiple combinations of
+Python versions and dependency versions.
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/LICENSE.rst
@@ -0,0 +1,39 @@
+Copyright © 2014 by the Pallets team.
+
+Some rights reserved.
+
+Redistribution and use in source and binary forms of the software as
+well as documentation, with or without modification, are permitted
+provided that the following conditions are met:
+
+-   Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+-   Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+-   Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+----
+
+Click uses parts of optparse written by Gregory P. Ward and maintained
+by the Python Software Foundation. This is limited to code in parser.py.
+
+Copyright © 2001-2006 Gregory P. Ward. All rights reserved.
+Copyright © 2002-2006 Python Software Foundation. All rights reserved.
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/MANIFEST.in
@@ -0,0 +1,11 @@
+include CHANGES.rst
+include CONTRIBUTING.rst
+include LICENSE.rst
+include README.rst
+include tox.ini
+graft artwork
+graft docs
+prune docs/_build
+graft examples
+graft tests
+global-exclude *.py[co] .DS_Store
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/PKG-INFO
@@ -0,0 +1,119 @@
+Metadata-Version: 1.2
+Name: Click
+Version: 7.0
+Summary: Composable command line interface toolkit
+Home-page: https://palletsprojects.com/p/click/
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets Team
+Maintainer-email: contact@palletsprojects.com
+License: BSD
+Project-URL: Documentation, https://click.palletsprojects.com/
+Project-URL: Code, https://github.com/pallets/click
+Project-URL: Issue tracker, https://github.com/pallets/click/issues
+Description: \$ click\_
+        ==========
+        
+        Click is a Python package for creating beautiful command line interfaces
+        in a composable way with as little code as necessary. It's the "Command
+        Line Interface Creation Kit". It's highly configurable but comes with
+        sensible defaults out of the box.
+        
+        It aims to make the process of writing command line tools quick and fun
+        while also preventing any frustration caused by the inability to
+        implement an intended CLI API.
+        
+        Click in three points:
+        
+        -   Arbitrary nesting of commands
+        -   Automatic help page generation
+        -   Supports lazy loading of subcommands at runtime
+        
+        
+        Installing
+        ----------
+        
+        Install and update using `pip`_:
+        
+        .. code-block:: text
+        
+            $ pip install click
+        
+        Click supports Python 3.4 and newer, Python 2.7, and PyPy.
+        
+        .. _pip: https://pip.pypa.io/en/stable/quickstart/
+        
+        
+        A Simple Example
+        ----------------
+        
+        What does it look like? Here is an example of a simple Click program:
+        
+        .. code-block:: python
+        
+            import click
+            
+            @click.command()
+            @click.option("--count", default=1, help="Number of greetings.")
+            @click.option("--name", prompt="Your name",
+                          help="The person to greet.")
+            def hello(count, name):
+                """Simple program that greets NAME for a total of COUNT times."""
+                for _ in range(count):
+                    click.echo("Hello, %s!" % name)
+            
+            if __name__ == '__main__':
+                hello()
+        
+        And what it looks like when run:
+        
+        .. code-block:: text
+        
+            $ python hello.py --count=3
+            Your name: Click
+            Hello, Click!
+            Hello, Click!
+            Hello, Click!
+        
+        
+        Donate
+        ------
+        
+        The Pallets organization develops and supports Click and other popular
+        packages. In order to grow the community of contributors and users, and
+        allow the maintainers to devote more time to the projects, `please
+        donate today`_.
+        
+        .. _please donate today: https://palletsprojects.com/donate
+        
+        
+        Links
+        -----
+        
+        *   Website: https://palletsprojects.com/p/click/
+        *   Documentation: https://click.palletsprojects.com/
+        *   License: `BSD <https://github.com/pallets/click/blob/master/LICENSE.rst>`_
+        *   Releases: https://pypi.org/project/click/
+        *   Code: https://github.com/pallets/click
+        *   Issue tracker: https://github.com/pallets/click/issues
+        *   Test status:
+        
+            *   Linux, Mac: https://travis-ci.org/pallets/click
+            *   Windows: https://ci.appveyor.com/project/pallets/click
+        
+        *   Test coverage: https://codecov.io/gh/pallets/click
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/README.rst
@@ -0,0 +1,91 @@
+\$ click\_
+==========
+
+Click is a Python package for creating beautiful command line interfaces
+in a composable way with as little code as necessary. It's the "Command
+Line Interface Creation Kit". It's highly configurable but comes with
+sensible defaults out of the box.
+
+It aims to make the process of writing command line tools quick and fun
+while also preventing any frustration caused by the inability to
+implement an intended CLI API.
+
+Click in three points:
+
+-   Arbitrary nesting of commands
+-   Automatic help page generation
+-   Supports lazy loading of subcommands at runtime
+
+
+Installing
+----------
+
+Install and update using `pip`_:
+
+.. code-block:: text
+
+    $ pip install click
+
+Click supports Python 3.4 and newer, Python 2.7, and PyPy.
+
+.. _pip: https://pip.pypa.io/en/stable/quickstart/
+
+
+A Simple Example
+----------------
+
+What does it look like? Here is an example of a simple Click program:
+
+.. code-block:: python
+
+    import click
+    
+    @click.command()
+    @click.option("--count", default=1, help="Number of greetings.")
+    @click.option("--name", prompt="Your name",
+                  help="The person to greet.")
+    def hello(count, name):
+        """Simple program that greets NAME for a total of COUNT times."""
+        for _ in range(count):
+            click.echo("Hello, %s!" % name)
+    
+    if __name__ == '__main__':
+        hello()
+
+And what it looks like when run:
+
+.. code-block:: text
+
+    $ python hello.py --count=3
+    Your name: Click
+    Hello, Click!
+    Hello, Click!
+    Hello, Click!
+
+
+Donate
+------
+
+The Pallets organization develops and supports Click and other popular
+packages. In order to grow the community of contributors and users, and
+allow the maintainers to devote more time to the projects, `please
+donate today`_.
+
+.. _please donate today: https://palletsprojects.com/donate
+
+
+Links
+-----
+
+*   Website: https://palletsprojects.com/p/click/
+*   Documentation: https://click.palletsprojects.com/
+*   License: `BSD <https://github.com/pallets/click/blob/master/LICENSE.rst>`_
+*   Releases: https://pypi.org/project/click/
+*   Code: https://github.com/pallets/click
+*   Issue tracker: https://github.com/pallets/click/issues
+*   Test status:
+
+    *   Linux, Mac: https://travis-ci.org/pallets/click
+    *   Windows: https://ci.appveyor.com/project/pallets/click
+
+*   Test coverage: https://codecov.io/gh/pallets/click
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/artwork/logo.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="237.36929"
+   height="110.7928"
+   id="svg4837"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   sodipodi:docname="New document 8">
+  <defs
+     id="defs4839" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="259.76814"
+     inkscape:cy="40.769955"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="10"
+     fit-margin-left="10"
+     fit-margin-right="10"
+     fit-margin-bottom="10"
+     inkscape:window-width="1676"
+     inkscape:window-height="1006"
+     inkscape:window-x="4"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata4842">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-255.43458,-207.38101)">
+    <path
+       style="fill:#000000"
+       d="m 466.33424,306.48462 0,-1.6892 3.16724,0 3.16723,0 0,1.6892 0,1.68919 -3.16723,0 -3.16724,0 0,-1.68919 z m -3.37839,-5.06759 0,-3.37839 1.47804,0 1.47805,0 0,3.37839 0,3.37839 -1.47805,0 -1.47804,0 0,-3.37839 z m 10.13516,0 0,-3.37839 1.47804,0 1.47805,0 0,3.37839 0,3.37839 -1.47805,0 -1.47804,0 0,-3.37839 z m -30.82778,-28.92744 0,-28.92744 1.68919,0 c 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 1.6892,1.6892 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 1.6892,1.6892 1.68919,0 1.68919,0 1.68919,1.68919 0,1.6892 0,1.6892 -1.68919,1.6892 -1.6892,0 -1.6892,0 -1.6892,-1.6892 0,-1.68919 0,-1.68919 -1.68919,-1.68919 l -1.6892,0 0,23.85986 0,23.85985 1.6892,0 c 1.68919,0 1.68919,0 1.68919,1.6892 l 0,1.68919 -3.37839,0 -3.37838,0 0,-28.92744 z m 7.00029,25.22951 c -0.13394,-0.13393 -0.24352,-0.87696 -0.24352,-1.65118 0,-1.36423 0.0529,-1.40766 1.71469,-1.40766 l 1.71468,0 -0.13106,1.58362 c -0.12024,1.45279 -0.24178,1.5892 -1.47117,1.65118 -0.73706,0.0372 -1.44968,-0.042 -1.58362,-0.17596 z m 10.14204,-0.0392 c -0.13814,-0.22352 -0.20454,-1.7545 -0.14756,-3.40219 l 0.10361,-2.99581 1.60373,0 1.60374,0 -0.12334,3.27282 -0.12334,3.27281 -1.33284,0.12938 c -0.73305,0.0712 -1.44586,-0.0535 -1.584,-0.27702 z m 10.30707,-3.03247 0,-3.36553 1.47803,0 1.47805,0 0,3.30997 0,3.30996 -1.47805,0.0556 -1.47803,0.0556 0,-3.36553 z m -17.03271,-0.26872 c -0.15484,-0.15483 -0.28153,-0.91497 -0.28153,-1.68919 0,-1.36073 0.0563,-1.40766 1.68919,-1.40766 1.6892,0 1.6892,0 1.6892,1.6892 0,1.63289 -0.0469,1.68919 -1.40766,1.68919 -0.77422,0 -1.53436,-0.12669 -1.6892,-0.28154 z m 3.09686,-4.99719 c 0,-1.44286 0.0402,-1.47804 1.68919,-1.47804 1.64898,0 1.68919,0.0352 1.68919,1.47804 0,1.44285 -0.0402,1.47805 -1.68919,1.47805 -1.64898,0 -1.68919,-0.0352 -1.68919,-1.47805 z m 10.55746,-1.68919 0,-3.16724 6.75677,0 6.75678,0 0,-1.68919 c 0,-1.64898 0.0351,-1.6892 1.47804,-1.6892 l 1.47804,0 0,3.15652 0,3.15651 -6.6512,0.1163 -6.65119,0.1163 -0.13108,1.58361 c -0.12394,1.49763 -0.20994,1.58363 -1.58361,1.58363 l -1.45255,0 0,-3.16724 z m 10.13516,-8.44597 c 0,-1.40766 0.0704,-1.47804 1.47804,-1.47804 1.40766,0 1.47805,0.0704 1.47805,1.47804 0,1.40766 -0.0704,1.47804 -1.47805,1.47804 -1.40766,0 -1.47804,-0.0704 -1.47804,-1.47804 z m -3.37839,-3.37839 c 0,-1.40766 0.0704,-1.47804 1.47804,-1.47804 1.40767,0 1.47805,0.0704 1.47805,1.47804 0,1.40767 -0.0704,1.47805 -1.47805,1.47805 -1.40765,0 -1.47804,-0.0704 -1.47804,-1.47805 z m -3.37838,-3.37839 c 0,-1.40765 0.0704,-1.47804 1.47803,-1.47804 1.40767,0 1.47805,0.0704 1.47805,1.47804 0,1.40767 -0.0704,1.47805 -1.47805,1.47805 -1.40765,0 -1.47803,-0.0704 -1.47803,-1.47805 z m -3.37839,-3.48871 c 0,-1.52897 0.0553,-1.58931 1.47804,-1.61346 1.43567,-0.0244 1.47804,0.0211 1.47804,1.58838 0,1.56241 -0.0467,1.61345 -1.47804,1.61345 -1.42721,0 -1.47804,-0.0546 -1.47804,-1.58837 z m -3.37839,-3.47921 c 0,-1.64897 0.0351,-1.68919 1.47804,-1.68919 1.44285,0 1.47805,0.0402 1.47805,1.68919 0,1.64898 -0.0352,1.6892 -1.47805,1.6892 -1.44285,0 -1.47804,-0.0403 -1.47804,-1.6892 z m -3.45076,-3.37839 c 0.15328,-1.60738 0.23052,-1.68919 1.59478,-1.68919 1.3911,0 1.43368,0.0502 1.43368,1.68919 0,1.67253 -0.0157,1.6892 -1.59478,1.6892 l -1.59477,0 0.16109,-1.6892 z m -3.30601,-3.37838 c 0,-1.64898 0.0351,-1.6892 1.47804,-1.6892 1.44285,0 1.47804,0.0402 1.47804,1.6892 0,1.64897 -0.0352,1.68919 -1.47804,1.68919 -1.44286,0 -1.47804,-0.0402 -1.47804,-1.68919 z m -3.72061,-3.27282 c 0.12395,-1.49762 0.21706,-1.59073 1.71468,-1.71468 l 1.58363,-0.13107 0,1.71469 0,1.71468 -1.71469,0 -1.71468,0 0.13106,-1.58362 z"
+       id="path4856"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#000000"
+       d="m 271.34527,247.46047 c 0,-1.32826 -0.94153,-2.12734 -3.05297,-2.59109 -4.04056,-0.88746 -3.19986,-3.08429 1.01754,-2.65894 9.32239,0.94022 10.81407,-3.50307 2.60795,-7.76831 -2.71012,-1.40862 -5.36388,-3.27897 -5.89726,-4.15634 -1.47604,-2.42802 -0.0246,-6.42458 2.80076,-7.7119 1.48697,-0.67751 2.52398,-1.98054 2.52398,-3.17144 0,-1.13017 0.66136,-2.02144 1.5,-2.02144 0.825,0 1.5,0.85122 1.5,1.89159 0,1.19765 0.94321,2.12832 2.57104,2.53688 3.91591,0.98283 2.39946,3.10499 -1.95093,2.73019 -6.99788,-0.60291 -8.93527,3.8291 -2.87011,6.5657 9.0905,4.10163 11.015,9.73021 4.5,13.16112 -1.2375,0.65169 -2.25,2.06905 -2.25,3.1497 0,1.08065 -0.675,1.96482 -1.5,1.96482 -0.825,0 -1.5,-0.86425 -1.5,-1.92054 z m 38.357,-3.07104 c -4.57947,-1.84804 -6.77791,-8.48491 -4.32736,-13.06381 2.11183,-3.94599 10.30093,-6.28597 13.9414,-3.98367 2.2687,1.43477 0.36183,2.48777 -3.94232,2.177 -6.96682,-0.50302 -10.61631,6.27447 -5.91184,10.97894 1.71218,1.71218 2.95483,2.02243 6.5,1.62284 3.13626,-0.35351 4.38312,-0.13272 4.38312,0.77613 0,2.4062 -6.21813,3.27822 -10.643,1.49257 z m 21.1997,-1.23093 c -1.16379,-1.66155 -1.5567,-4.81653 -1.5567,-12.5 l 0,-10.27749 -2.5,0 c -1.55556,0 -2.5,-0.56667 -2.5,-1.5 0,-1.08333 1.11111,-1.5 4,-1.5 l 4,0 0.0153,11.25 c 0.008,6.1875 0.41056,11.87411 0.89363,12.63691 0.54214,0.85607 1.88505,1.19446 3.50856,0.88411 2.94677,-0.56331 4.58181,0.98192 2.46061,2.32547 -2.5829,1.63598 -6.70984,0.98184 -8.32141,-1.319 z m 20.4433,0.22251 c -1.55556,-1.55556 -2,-3.33333 -2,-8 0,-5.73333 -0.11111,-6 -2.5,-6 -1.55556,0 -2.5,-0.56667 -2.5,-1.5 0,-1.08333 1.11111,-1.5 4,-1.5 l 4,0 0.0153,6.75 c 0.0183,8.04891 0.82623,9.70461 4.40219,9.02102 2.94677,-0.56331 4.58181,0.98192 2.46061,2.32547 -2.3358,1.47948 -5.78176,0.99986 -7.87811,-1.09649 z m 18.357,1.00842 c -4.57947,-1.84804 -6.77791,-8.48491 -4.32736,-13.06381 2.11183,-3.94599 10.30093,-6.28597 13.9414,-3.98367 2.2687,1.43477 0.36183,2.48777 -3.94232,2.177 -6.96682,-0.50302 -10.61631,6.27447 -5.91184,10.97894 1.71218,1.71218 2.95483,2.02243 6.5,1.62284 3.13626,-0.35351 4.38312,-0.13272 4.38312,0.77613 0,2.4062 -6.21813,3.27822 -10.643,1.49257 z m 15.86478,-12.67646 c 0.20032,-9.84119 0.6282,-13.78431 1.52822,-14.08333 0.9358,-0.31091 1.25628,1.80502 1.275,8.41804 l 0.025,8.83333 4.01111,-4.25 c 2.32594,-2.46446 4.92367,-4.25 6.18321,-4.25 1.8968,0 1.6121,0.57059 -2.24643,4.50214 l -4.41851,4.50213 4.92271,4.99787 c 3.84161,3.90023 4.49929,4.99786 2.99467,4.99786 -1.06044,0 -4.08102,-2.12058 -6.71241,-4.71241 l -4.78435,-4.71241 0,4.71241 c 0,3.56082 -0.37346,4.71241 -1.52822,4.71241 -1.30999,0 -1.48849,-1.95179 -1.25,-13.66804 z m -37.85759,-9.82527 c -0.34636,-0.90262 -0.15025,-2.12063 0.43581,-2.70669 1.52361,-1.52361 4.41041,-0.13109 4.01242,1.93549 -0.4433,2.30188 -3.64766,2.85743 -4.44823,0.7712 z"
+       id="path4887"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 411.32164,243.39982 c 7.38153,0.15924 14.76525,0.0821 22.14736,0.0136 -3.6814,3.83663 -9.42739,1.45689 -14.06576,2.07921 -3.69684,-0.007 -7.3939,0.0151 -11.09,0.0923 1.0029,-0.72825 2.00505,-1.45754 3.0084,-2.18514 z"
+       id="path4889"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/__init__.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+"""
+click
+~~~~~
+
+Click is a simple Python module inspired by the stdlib optparse to make
+writing command line scripts fun. Unlike other modules, it's based
+around a simple API that does not come with too much magic and is
+composable.
+
+:copyright: © 2014 by the Pallets team.
+:license: BSD, see LICENSE.rst for more details.
+"""
+
+# Core classes
+from .core import Context, BaseCommand, Command, MultiCommand, Group, \
+     CommandCollection, Parameter, Option, Argument
+
+# Globals
+from .globals import get_current_context
+
+# Decorators
+from .decorators import pass_context, pass_obj, make_pass_decorator, \
+     command, group, argument, option, confirmation_option, \
+     password_option, version_option, help_option
+
+# Types
+from .types import ParamType, File, Path, Choice, IntRange, Tuple, \
+     DateTime, STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED, FloatRange
+
+# Utilities
+from .utils import echo, get_binary_stream, get_text_stream, open_file, \
+     format_filename, get_app_dir, get_os_args
+
+# Terminal functions
+from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \
+     progressbar, clear, style, unstyle, secho, edit, launch, getchar, \
+     pause
+
+# Exceptions
+from .exceptions import ClickException, UsageError, BadParameter, \
+     FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \
+     MissingParameter
+
+# Formatting
+from .formatting import HelpFormatter, wrap_text
+
+# Parsing
+from .parser import OptionParser
+
+
+__all__ = [
+    # Core classes
+    'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group',
+    'CommandCollection', 'Parameter', 'Option', 'Argument',
+
+    # Globals
+    'get_current_context',
+
+    # Decorators
+    'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group',
+    'argument', 'option', 'confirmation_option', 'password_option',
+    'version_option', 'help_option',
+
+    # Types
+    'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple',
+    'DateTime', 'STRING', 'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED',
+    'FloatRange',
+
+    # Utilities
+    'echo', 'get_binary_stream', 'get_text_stream', 'open_file',
+    'format_filename', 'get_app_dir', 'get_os_args',
+
+    # Terminal functions
+    'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager',
+    'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch',
+    'getchar', 'pause',
+
+    # Exceptions
+    'ClickException', 'UsageError', 'BadParameter', 'FileError',
+    'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage',
+    'MissingParameter',
+
+    # Formatting
+    'HelpFormatter', 'wrap_text',
+
+    # Parsing
+    'OptionParser',
+]
+
+
+# Controls if click should emit the warning about the use of unicode
+# literals.
+disable_unicode_literals_warning = False
+
+
+__version__ = '7.0'
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_bashcomplete.py
@@ -0,0 +1,293 @@
+import copy
+import os
+import re
+
+from .utils import echo
+from .parser import split_arg_string
+from .core import MultiCommand, Option, Argument
+from .types import Choice
+
+try:
+    from collections import abc
+except ImportError:
+    import collections as abc
+
+WORDBREAK = '='
+
+# Note, only BASH version 4.4 and later have the nosort option.
+COMPLETION_SCRIPT_BASH = '''
+%(complete_func)s() {
+    local IFS=$'\n'
+    COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\
+                   COMP_CWORD=$COMP_CWORD \\
+                   %(autocomplete_var)s=complete $1 ) )
+    return 0
+}
+
+%(complete_func)setup() {
+    local COMPLETION_OPTIONS=""
+    local BASH_VERSION_ARR=(${BASH_VERSION//./ })
+    # Only BASH version 4.4 and later have the nosort option.
+    if [ ${BASH_VERSION_ARR[0]} -gt 4 ] || ([ ${BASH_VERSION_ARR[0]} -eq 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ]); then
+        COMPLETION_OPTIONS="-o nosort"
+    fi
+
+    complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s
+}
+
+%(complete_func)setup
+'''
+
+COMPLETION_SCRIPT_ZSH = '''
+%(complete_func)s() {
+    local -a completions
+    local -a completions_with_descriptions
+    local -a response
+    response=("${(@f)$( env COMP_WORDS=\"${words[*]}\" \\
+                        COMP_CWORD=$((CURRENT-1)) \\
+                        %(autocomplete_var)s=\"complete_zsh\" \\
+                        %(script_names)s )}")
+
+    for key descr in ${(kv)response}; do
+      if [[ "$descr" == "_" ]]; then
+          completions+=("$key")
+      else
+          completions_with_descriptions+=("$key":"$descr")
+      fi
+    done
+
+    if [ -n "$completions_with_descriptions" ]; then
+        _describe -V unsorted completions_with_descriptions -U -Q
+    fi
+
+    if [ -n "$completions" ]; then
+        compadd -U -V unsorted -Q -a completions
+    fi
+    compstate[insert]="automenu"
+}
+
+compdef %(complete_func)s %(script_names)s
+'''
+
+_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]')
+
+
+def get_completion_script(prog_name, complete_var, shell):
+    cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_'))
+    script = COMPLETION_SCRIPT_ZSH if shell == 'zsh' else COMPLETION_SCRIPT_BASH
+    return (script % {
+        'complete_func': '_%s_completion' % cf_name,
+        'script_names': prog_name,
+        'autocomplete_var': complete_var,
+    }).strip() + ';'
+
+
+def resolve_ctx(cli, prog_name, args):
+    """
+    Parse into a hierarchy of contexts. Contexts are connected through the parent variable.
+    :param cli: command definition
+    :param prog_name: the program that is running
+    :param args: full list of args
+    :return: the final context/command parsed
+    """
+    ctx = cli.make_context(prog_name, args, resilient_parsing=True)
+    args = ctx.protected_args + ctx.args
+    while args:
+        if isinstance(ctx.command, MultiCommand):
+            if not ctx.command.chain:
+                cmd_name, cmd, args = ctx.command.resolve_command(ctx, args)
+                if cmd is None:
+                    return ctx
+                ctx = cmd.make_context(cmd_name, args, parent=ctx,
+                                       resilient_parsing=True)
+                args = ctx.protected_args + ctx.args
+            else:
+                # Walk chained subcommand contexts saving the last one.
+                while args:
+                    cmd_name, cmd, args = ctx.command.resolve_command(ctx, args)
+                    if cmd is None:
+                        return ctx
+                    sub_ctx = cmd.make_context(cmd_name, args, parent=ctx,
+                                               allow_extra_args=True,
+                                               allow_interspersed_args=False,
+                                               resilient_parsing=True)
+                    args = sub_ctx.args
+                ctx = sub_ctx
+                args = sub_ctx.protected_args + sub_ctx.args
+        else:
+            break
+    return ctx
+
+
+def start_of_option(param_str):
+    """
+    :param param_str: param_str to check
+    :return: whether or not this is the start of an option declaration (i.e. starts "-" or "--")
+    """
+    return param_str and param_str[:1] == '-'
+
+
+def is_incomplete_option(all_args, cmd_param):
+    """
+    :param all_args: the full original list of args supplied
+    :param cmd_param: the current command paramter
+    :return: whether or not the last option declaration (i.e. starts "-" or "--") is incomplete and
+    corresponds to this cmd_param. In other words whether this cmd_param option can still accept
+    values
+    """
+    if not isinstance(cmd_param, Option):
+        return False
+    if cmd_param.is_flag:
+        return False
+    last_option = None
+    for index, arg_str in enumerate(reversed([arg for arg in all_args if arg != WORDBREAK])):
+        if index + 1 > cmd_param.nargs:
+            break
+        if start_of_option(arg_str):
+            last_option = arg_str
+
+    return True if last_option and last_option in cmd_param.opts else False
+
+
+def is_incomplete_argument(current_params, cmd_param):
+    """
+    :param current_params: the current params and values for this argument as already entered
+    :param cmd_param: the current command parameter
+    :return: whether or not the last argument is incomplete and corresponds to this cmd_param. In
+    other words whether or not the this cmd_param argument can still accept values
+    """
+    if not isinstance(cmd_param, Argument):
+        return False
+    current_param_values = current_params[cmd_param.name]
+    if current_param_values is None:
+        return True
+    if cmd_param.nargs == -1:
+        return True
+    if isinstance(current_param_values, abc.Iterable) \
+            and cmd_param.nargs > 1 and len(current_param_values) < cmd_param.nargs:
+        return True
+    return False
+
+
+def get_user_autocompletions(ctx, args, incomplete, cmd_param):
+    """
+    :param ctx: context associated with the parsed command
+    :param args: full list of args
+    :param incomplete: the incomplete text to autocomplete
+    :param cmd_param: command definition
+    :return: all the possible user-specified completions for the param
+    """
+    results = []
+    if isinstance(cmd_param.type, Choice):
+        # Choices don't support descriptions.
+        results = [(c, None)
+                   for c in cmd_param.type.choices if str(c).startswith(incomplete)]
+    elif cmd_param.autocompletion is not None:
+        dynamic_completions = cmd_param.autocompletion(ctx=ctx,
+                                                       args=args,
+                                                       incomplete=incomplete)
+        results = [c if isinstance(c, tuple) else (c, None)
+                   for c in dynamic_completions]
+    return results
+
+
+def get_visible_commands_starting_with(ctx, starts_with):
+    """
+    :param ctx: context associated with the parsed command
+    :starts_with: string that visible commands must start with.
+    :return: all visible (not hidden) commands that start with starts_with.
+    """
+    for c in ctx.command.list_commands(ctx):
+        if c.startswith(starts_with):
+            command = ctx.command.get_command(ctx, c)
+            if not command.hidden:
+                yield command
+
+
+def add_subcommand_completions(ctx, incomplete, completions_out):
+    # Add subcommand completions.
+    if isinstance(ctx.command, MultiCommand):
+        completions_out.extend(
+            [(c.name, c.get_short_help_str()) for c in get_visible_commands_starting_with(ctx, incomplete)])
+
+    # Walk up the context list and add any other completion possibilities from chained commands
+    while ctx.parent is not None:
+        ctx = ctx.parent
+        if isinstance(ctx.command, MultiCommand) and ctx.command.chain:
+            remaining_commands = [c for c in get_visible_commands_starting_with(ctx, incomplete)
+                                  if c.name not in ctx.protected_args]
+            completions_out.extend([(c.name, c.get_short_help_str()) for c in remaining_commands])
+
+
+def get_choices(cli, prog_name, args, incomplete):
+    """
+    :param cli: command definition
+    :param prog_name: the program that is running
+    :param args: full list of args
+    :param incomplete: the incomplete text to autocomplete
+    :return: all the possible completions for the incomplete
+    """
+    all_args = copy.deepcopy(args)
+
+    ctx = resolve_ctx(cli, prog_name, args)
+    if ctx is None:
+        return []
+
+    # In newer versions of bash long opts with '='s are partitioned, but it's easier to parse
+    # without the '='
+    if start_of_option(incomplete) and WORDBREAK in incomplete:
+        partition_incomplete = incomplete.partition(WORDBREAK)
+        all_args.append(partition_incomplete[0])
+        incomplete = partition_incomplete[2]
+    elif incomplete == WORDBREAK:
+        incomplete = ''
+
+    completions = []
+    if start_of_option(incomplete):
+        # completions for partial options
+        for param in ctx.command.params:
+            if isinstance(param, Option) and not param.hidden:
+                param_opts = [param_opt for param_opt in param.opts +
+                              param.secondary_opts if param_opt not in all_args or param.multiple]
+                completions.extend([(o, param.help) for o in param_opts if o.startswith(incomplete)])
+        return completions
+    # completion for option values from user supplied values
+    for param in ctx.command.params:
+        if is_incomplete_option(all_args, param):
+            return get_user_autocompletions(ctx, all_args, incomplete, param)
+    # completion for argument values from user supplied values
+    for param in ctx.command.params:
+        if is_incomplete_argument(ctx.params, param):
+            return get_user_autocompletions(ctx, all_args, incomplete, param)
+
+    add_subcommand_completions(ctx, incomplete, completions)
+    # Sort before returning so that proper ordering can be enforced in custom types.
+    return sorted(completions)
+
+
+def do_complete(cli, prog_name, include_descriptions):
+    cwords = split_arg_string(os.environ['COMP_WORDS'])
+    cword = int(os.environ['COMP_CWORD'])
+    args = cwords[1:cword]
+    try:
+        incomplete = cwords[cword]
+    except IndexError:
+        incomplete = ''
+
+    for item in get_choices(cli, prog_name, args, incomplete):
+        echo(item[0])
+        if include_descriptions:
+            # ZSH has trouble dealing with empty array parameters when returned from commands, so use a well defined character '_' to indicate no description is present.
+            echo(item[1] if item[1] else '_')
+
+    return True
+
+
+def bashcomplete(cli, prog_name, complete_var, complete_instr):
+    if complete_instr.startswith('source'):
+        shell = 'zsh' if complete_instr == 'source_zsh' else 'bash'
+        echo(get_completion_script(prog_name, complete_var, shell))
+        return True
+    elif complete_instr == 'complete' or complete_instr == 'complete_zsh':
+        return do_complete(cli, prog_name, complete_instr == 'complete_zsh')
+    return False
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_compat.py
@@ -0,0 +1,703 @@
+import re
+import io
+import os
+import sys
+import codecs
+from weakref import WeakKeyDictionary
+
+
+PY2 = sys.version_info[0] == 2
+CYGWIN = sys.platform.startswith('cygwin')
+# Determine local App Engine environment, per Google's own suggestion
+APP_ENGINE = ('APPENGINE_RUNTIME' in os.environ and
+              'Development/' in os.environ['SERVER_SOFTWARE'])
+WIN = sys.platform.startswith('win') and not APP_ENGINE
+DEFAULT_COLUMNS = 80
+
+
+_ansi_re = re.compile(r'\033\[((?:\d|;)*)([a-zA-Z])')
+
+
+def get_filesystem_encoding():
+    return sys.getfilesystemencoding() or sys.getdefaultencoding()
+
+
+def _make_text_stream(stream, encoding, errors,
+                      force_readable=False, force_writable=False):
+    if encoding is None:
+        encoding = get_best_encoding(stream)
+    if errors is None:
+        errors = 'replace'
+    return _NonClosingTextIOWrapper(stream, encoding, errors,
+                                    line_buffering=True,
+                                    force_readable=force_readable,
+                                    force_writable=force_writable)
+
+
+def is_ascii_encoding(encoding):
+    """Checks if a given encoding is ascii."""
+    try:
+        return codecs.lookup(encoding).name == 'ascii'
+    except LookupError:
+        return False
+
+
+def get_best_encoding(stream):
+    """Returns the default stream encoding if not found."""
+    rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding()
+    if is_ascii_encoding(rv):
+        return 'utf-8'
+    return rv
+
+
+class _NonClosingTextIOWrapper(io.TextIOWrapper):
+
+    def __init__(self, stream, encoding, errors,
+                 force_readable=False, force_writable=False, **extra):
+        self._stream = stream = _FixupStream(stream, force_readable,
+                                             force_writable)
+        io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra)
+
+    # The io module is a place where the Python 3 text behavior
+    # was forced upon Python 2, so we need to unbreak
+    # it to look like Python 2.
+    if PY2:
+        def write(self, x):
+            if isinstance(x, str) or is_bytes(x):
+                try:
+                    self.flush()
+                except Exception:
+                    pass
+                return self.buffer.write(str(x))
+            return io.TextIOWrapper.write(self, x)
+
+        def writelines(self, lines):
+            for line in lines:
+                self.write(line)
+
+    def __del__(self):
+        try:
+            self.detach()
+        except Exception:
+            pass
+
+    def isatty(self):
+        # https://bitbucket.org/pypy/pypy/issue/1803
+        return self._stream.isatty()
+
+
+class _FixupStream(object):
+    """The new io interface needs more from streams than streams
+    traditionally implement.  As such, this fix-up code is necessary in
+    some circumstances.
+
+    The forcing of readable and writable flags are there because some tools
+    put badly patched objects on sys (one such offender are certain version
+    of jupyter notebook).
+    """
+
+    def __init__(self, stream, force_readable=False, force_writable=False):
+        self._stream = stream
+        self._force_readable = force_readable
+        self._force_writable = force_writable
+
+    def __getattr__(self, name):
+        return getattr(self._stream, name)
+
+    def read1(self, size):
+        f = getattr(self._stream, 'read1', None)
+        if f is not None:
+            return f(size)
+        # We only dispatch to readline instead of read in Python 2 as we
+        # do not want cause problems with the different implementation
+        # of line buffering.
+        if PY2:
+            return self._stream.readline(size)
+        return self._stream.read(size)
+
+    def readable(self):
+        if self._force_readable:
+            return True
+        x = getattr(self._stream, 'readable', None)
+        if x is not None:
+            return x()
+        try:
+            self._stream.read(0)
+        except Exception:
+            return False
+        return True
+
+    def writable(self):
+        if self._force_writable:
+            return True
+        x = getattr(self._stream, 'writable', None)
+        if x is not None:
+            return x()
+        try:
+            self._stream.write('')
+        except Exception:
+            try:
+                self._stream.write(b'')
+            except Exception:
+                return False
+        return True
+
+    def seekable(self):
+        x = getattr(self._stream, 'seekable', None)
+        if x is not None:
+            return x()
+        try:
+            self._stream.seek(self._stream.tell())
+        except Exception:
+            return False
+        return True
+
+
+if PY2:
+    text_type = unicode
+    bytes = str
+    raw_input = raw_input
+    string_types = (str, unicode)
+    int_types = (int, long)
+    iteritems = lambda x: x.iteritems()
+    range_type = xrange
+
+    def is_bytes(x):
+        return isinstance(x, (buffer, bytearray))
+
+    _identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
+
+    # For Windows, we need to force stdout/stdin/stderr to binary if it's
+    # fetched for that.  This obviously is not the most correct way to do
+    # it as it changes global state.  Unfortunately, there does not seem to
+    # be a clear better way to do it as just reopening the file in binary
+    # mode does not change anything.
+    #
+    # An option would be to do what Python 3 does and to open the file as
+    # binary only, patch it back to the system, and then use a wrapper
+    # stream that converts newlines.  It's not quite clear what's the
+    # correct option here.
+    #
+    # This code also lives in _winconsole for the fallback to the console
+    # emulation stream.
+    #
+    # There are also Windows environments where the `msvcrt` module is not
+    # available (which is why we use try-catch instead of the WIN variable
+    # here), such as the Google App Engine development server on Windows. In
+    # those cases there is just nothing we can do.
+    def set_binary_mode(f):
+        return f
+
+    try:
+        import msvcrt
+    except ImportError:
+        pass
+    else:
+        def set_binary_mode(f):
+            try:
+                fileno = f.fileno()
+            except Exception:
+                pass
+            else:
+                msvcrt.setmode(fileno, os.O_BINARY)
+            return f
+
+    try:
+        import fcntl
+    except ImportError:
+        pass
+    else:
+        def set_binary_mode(f):
+            try:
+                fileno = f.fileno()
+            except Exception:
+                pass
+            else:
+                flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
+                fcntl.fcntl(fileno, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
+            return f
+
+    def isidentifier(x):
+        return _identifier_re.search(x) is not None
+
+    def get_binary_stdin():
+        return set_binary_mode(sys.stdin)
+
+    def get_binary_stdout():
+        _wrap_std_stream('stdout')
+        return set_binary_mode(sys.stdout)
+
+    def get_binary_stderr():
+        _wrap_std_stream('stderr')
+        return set_binary_mode(sys.stderr)
+
+    def get_text_stdin(encoding=None, errors=None):
+        rv = _get_windows_console_stream(sys.stdin, encoding, errors)
+        if rv is not None:
+            return rv
+        return _make_text_stream(sys.stdin, encoding, errors,
+                                 force_readable=True)
+
+    def get_text_stdout(encoding=None, errors=None):
+        _wrap_std_stream('stdout')
+        rv = _get_windows_console_stream(sys.stdout, encoding, errors)
+        if rv is not None:
+            return rv
+        return _make_text_stream(sys.stdout, encoding, errors,
+                                 force_writable=True)
+
+    def get_text_stderr(encoding=None, errors=None):
+        _wrap_std_stream('stderr')
+        rv = _get_windows_console_stream(sys.stderr, encoding, errors)
+        if rv is not None:
+            return rv
+        return _make_text_stream(sys.stderr, encoding, errors,
+                                 force_writable=True)
+
+    def filename_to_ui(value):
+        if isinstance(value, bytes):
+            value = value.decode(get_filesystem_encoding(), 'replace')
+        return value
+else:
+    import io
+    text_type = str
+    raw_input = input
+    string_types = (str,)
+    int_types = (int,)
+    range_type = range
+    isidentifier = lambda x: x.isidentifier()
+    iteritems = lambda x: iter(x.items())
+
+    def is_bytes(x):
+        return isinstance(x, (bytes, memoryview, bytearray))
+
+    def _is_binary_reader(stream, default=False):
+        try:
+            return isinstance(stream.read(0), bytes)
+        except Exception:
+            return default
+            # This happens in some cases where the stream was already
+            # closed.  In this case, we assume the default.
+
+    def _is_binary_writer(stream, default=False):
+        try:
+            stream.write(b'')
+        except Exception:
+            try:
+                stream.write('')
+                return False
+            except Exception:
+                pass
+            return default
+        return True
+
+    def _find_binary_reader(stream):
+        # We need to figure out if the given stream is already binary.
+        # This can happen because the official docs recommend detaching
+        # the streams to get binary streams.  Some code might do this, so
+        # we need to deal with this case explicitly.
+        if _is_binary_reader(stream, False):
+            return stream
+
+        buf = getattr(stream, 'buffer', None)
+
+        # Same situation here; this time we assume that the buffer is
+        # actually binary in case it's closed.
+        if buf is not None and _is_binary_reader(buf, True):
+            return buf
+
+    def _find_binary_writer(stream):
+        # We need to figure out if the given stream is already binary.
+        # This can happen because the official docs recommend detatching
+        # the streams to get binary streams.  Some code might do this, so
+        # we need to deal with this case explicitly.
+        if _is_binary_writer(stream, False):
+            return stream
+
+        buf = getattr(stream, 'buffer', None)
+
+        # Same situation here; this time we assume that the buffer is
+        # actually binary in case it's closed.
+        if buf is not None and _is_binary_writer(buf, True):
+            return buf
+
+    def _stream_is_misconfigured(stream):
+        """A stream is misconfigured if its encoding is ASCII."""
+        # If the stream does not have an encoding set, we assume it's set
+        # to ASCII.  This appears to happen in certain unittest
+        # environments.  It's not quite clear what the correct behavior is
+        # but this at least will force Click to recover somehow.
+        return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii')
+
+    def _is_compatible_text_stream(stream, encoding, errors):
+        stream_encoding = getattr(stream, 'encoding', None)
+        stream_errors = getattr(stream, 'errors', None)
+
+        # Perfect match.
+        if stream_encoding == encoding and stream_errors == errors:
+            return True
+
+        # Otherwise, it's only a compatible stream if we did not ask for
+        # an encoding.
+        if encoding is None:
+            return stream_encoding is not None
+
+        return False
+
+    def _force_correct_text_reader(text_reader, encoding, errors,
+                                   force_readable=False):
+        if _is_binary_reader(text_reader, False):
+            binary_reader = text_reader
+        else:
+            # If there is no target encoding set, we need to verify that the
+            # reader is not actually misconfigured.
+            if encoding is None and not _stream_is_misconfigured(text_reader):
+                return text_reader
+
+            if _is_compatible_text_stream(text_reader, encoding, errors):
+                return text_reader
+
+            # If the reader has no encoding, we try to find the underlying
+            # binary reader for it.  If that fails because the environment is
+            # misconfigured, we silently go with the same reader because this
+            # is too common to happen.  In that case, mojibake is better than
+            # exceptions.
+            binary_reader = _find_binary_reader(text_reader)
+            if binary_reader is None:
+                return text_reader
+
+        # At this point, we default the errors to replace instead of strict
+        # because nobody handles those errors anyways and at this point
+        # we're so fundamentally fucked that nothing can repair it.
+        if errors is None:
+            errors = 'replace'
+        return _make_text_stream(binary_reader, encoding, errors,
+                                 force_readable=force_readable)
+
+    def _force_correct_text_writer(text_writer, encoding, errors,
+                                   force_writable=False):
+        if _is_binary_writer(text_writer, False):
+            binary_writer = text_writer
+        else:
+            # If there is no target encoding set, we need to verify that the
+            # writer is not actually misconfigured.
+            if encoding is None and not _stream_is_misconfigured(text_writer):
+                return text_writer
+
+            if _is_compatible_text_stream(text_writer, encoding, errors):
+                return text_writer
+
+            # If the writer has no encoding, we try to find the underlying
+            # binary writer for it.  If that fails because the environment is
+            # misconfigured, we silently go with the same writer because this
+            # is too common to happen.  In that case, mojibake is better than
+            # exceptions.
+            binary_writer = _find_binary_writer(text_writer)
+            if binary_writer is None:
+                return text_writer
+
+        # At this point, we default the errors to replace instead of strict
+        # because nobody handles those errors anyways and at this point
+        # we're so fundamentally fucked that nothing can repair it.
+        if errors is None:
+            errors = 'replace'
+        return _make_text_stream(binary_writer, encoding, errors,
+                                 force_writable=force_writable)
+
+    def get_binary_stdin():
+        reader = _find_binary_reader(sys.stdin)
+        if reader is None:
+            raise RuntimeError('Was not able to determine binary '
+                               'stream for sys.stdin.')
+        return reader
+
+    def get_binary_stdout():
+        writer = _find_binary_writer(sys.stdout)
+        if writer is None:
+            raise RuntimeError('Was not able to determine binary '
+                               'stream for sys.stdout.')
+        return writer
+
+    def get_binary_stderr():
+        writer = _find_binary_writer(sys.stderr)
+        if writer is None:
+            raise RuntimeError('Was not able to determine binary '
+                               'stream for sys.stderr.')
+        return writer
+
+    def get_text_stdin(encoding=None, errors=None):
+        rv = _get_windows_console_stream(sys.stdin, encoding, errors)
+        if rv is not None:
+            return rv
+        return _force_correct_text_reader(sys.stdin, encoding, errors,
+                                          force_readable=True)
+
+    def get_text_stdout(encoding=None, errors=None):
+        rv = _get_windows_console_stream(sys.stdout, encoding, errors)
+        if rv is not None:
+            return rv
+        return _force_correct_text_writer(sys.stdout, encoding, errors,
+                                          force_writable=True)
+
+    def get_text_stderr(encoding=None, errors=None):
+        rv = _get_windows_console_stream(sys.stderr, encoding, errors)
+        if rv is not None:
+            return rv
+        return _force_correct_text_writer(sys.stderr, encoding, errors,
+                                          force_writable=True)
+
+    def filename_to_ui(value):
+        if isinstance(value, bytes):
+            value = value.decode(get_filesystem_encoding(), 'replace')
+        else:
+            value = value.encode('utf-8', 'surrogateescape') \
+                .decode('utf-8', 'replace')
+        return value
+
+
+def get_streerror(e, default=None):
+    if hasattr(e, 'strerror'):
+        msg = e.strerror
+    else:
+        if default is not None:
+            msg = default
+        else:
+            msg = str(e)
+    if isinstance(msg, bytes):
+        msg = msg.decode('utf-8', 'replace')
+    return msg
+
+
+def open_stream(filename, mode='r', encoding=None, errors='strict',
+                atomic=False):
+    # Standard streams first.  These are simple because they don't need
+    # special handling for the atomic flag.  It's entirely ignored.
+    if filename == '-':
+        if any(m in mode for m in ['w', 'a', 'x']):
+            if 'b' in mode:
+                return get_binary_stdout(), False
+            return get_text_stdout(encoding=encoding, errors=errors), False
+        if 'b' in mode:
+            return get_binary_stdin(), False
+        return get_text_stdin(encoding=encoding, errors=errors), False
+
+    # Non-atomic writes directly go out through the regular open functions.
+    if not atomic:
+        if encoding is None:
+            return open(filename, mode), True
+        return io.open(filename, mode, encoding=encoding, errors=errors), True
+
+    # Some usability stuff for atomic writes
+    if 'a' in mode:
+        raise ValueError(
+            'Appending to an existing file is not supported, because that '
+            'would involve an expensive `copy`-operation to a temporary '
+            'file. Open the file in normal `w`-mode and copy explicitly '
+            'if that\'s what you\'re after.'
+        )
+    if 'x' in mode:
+        raise ValueError('Use the `overwrite`-parameter instead.')
+    if 'w' not in mode:
+        raise ValueError('Atomic writes only make sense with `w`-mode.')
+
+    # Atomic writes are more complicated.  They work by opening a file
+    # as a proxy in the same folder and then using the fdopen
+    # functionality to wrap it in a Python file.  Then we wrap it in an
+    # atomic file that moves the file over on close.
+    import tempfile
+    fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename),
+                                        prefix='.__atomic-write')
+
+    if encoding is not None:
+        f = io.open(fd, mode, encoding=encoding, errors=errors)
+    else:
+        f = os.fdopen(fd, mode)
+
+    return _AtomicFile(f, tmp_filename, os.path.realpath(filename)), True
+
+
+# Used in a destructor call, needs extra protection from interpreter cleanup.
+if hasattr(os, 'replace'):
+    _replace = os.replace
+    _can_replace = True
+else:
+    _replace = os.rename
+    _can_replace = not WIN
+
+
+class _AtomicFile(object):
+
+    def __init__(self, f, tmp_filename, real_filename):
+        self._f = f
+        self._tmp_filename = tmp_filename
+        self._real_filename = real_filename
+        self.closed = False
+
+    @property
+    def name(self):
+        return self._real_filename
+
+    def close(self, delete=False):
+        if self.closed:
+            return
+        self._f.close()
+        if not _can_replace:
+            try:
+                os.remove(self._real_filename)
+            except OSError:
+                pass
+        _replace(self._tmp_filename, self._real_filename)
+        self.closed = True
+
+    def __getattr__(self, name):
+        return getattr(self._f, name)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        self.close(delete=exc_type is not None)
+
+    def __repr__(self):
+        return repr(self._f)
+
+
+auto_wrap_for_ansi = None
+colorama = None
+get_winterm_size = None
+
+
+def strip_ansi(value):
+    return _ansi_re.sub('', value)
+
+
+def should_strip_ansi(stream=None, color=None):
+    if color is None:
+        if stream is None:
+            stream = sys.stdin
+        return not isatty(stream)
+    return not color
+
+
+# If we're on Windows, we provide transparent integration through
+# colorama.  This will make ANSI colors through the echo function
+# work automatically.
+if WIN:
+    # Windows has a smaller terminal
+    DEFAULT_COLUMNS = 79
+
+    from ._winconsole import _get_windows_console_stream, _wrap_std_stream
+
+    def _get_argv_encoding():
+        import locale
+        return locale.getpreferredencoding()
+
+    if PY2:
+        def raw_input(prompt=''):
+            sys.stderr.flush()
+            if prompt:
+                stdout = _default_text_stdout()
+                stdout.write(prompt)
+            stdin = _default_text_stdin()
+            return stdin.readline().rstrip('\r\n')
+
+    try:
+        import colorama
+    except ImportError:
+        pass
+    else:
+        _ansi_stream_wrappers = WeakKeyDictionary()
+
+        def auto_wrap_for_ansi(stream, color=None):
+            """This function wraps a stream so that calls through colorama
+            are issued to the win32 console API to recolor on demand.  It
+            also ensures to reset the colors if a write call is interrupted
+            to not destroy the console afterwards.
+            """
+            try:
+                cached = _ansi_stream_wrappers.get(stream)
+            except Exception:
+                cached = None
+            if cached is not None:
+                return cached
+            strip = should_strip_ansi(stream, color)
+            ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip)
+            rv = ansi_wrapper.stream
+            _write = rv.write
+
+            def _safe_write(s):
+                try:
+                    return _write(s)
+                except:
+                    ansi_wrapper.reset_all()
+                    raise
+
+            rv.write = _safe_write
+            try:
+                _ansi_stream_wrappers[stream] = rv
+            except Exception:
+                pass
+            return rv
+
+        def get_winterm_size():
+            win = colorama.win32.GetConsoleScreenBufferInfo(
+                colorama.win32.STDOUT).srWindow
+            return win.Right - win.Left, win.Bottom - win.Top
+else:
+    def _get_argv_encoding():
+        return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding()
+
+    _get_windows_console_stream = lambda *x: None
+    _wrap_std_stream = lambda *x: None
+
+
+def term_len(x):
+    return len(strip_ansi(x))
+
+
+def isatty(stream):
+    try:
+        return stream.isatty()
+    except Exception:
+        return False
+
+
+def _make_cached_stream_func(src_func, wrapper_func):
+    cache = WeakKeyDictionary()
+    def func():
+        stream = src_func()
+        try:
+            rv = cache.get(stream)
+        except Exception:
+            rv = None
+        if rv is not None:
+            return rv
+        rv = wrapper_func()
+        try:
+            stream = src_func()  # In case wrapper_func() modified the stream
+            cache[stream] = rv
+        except Exception:
+            pass
+        return rv
+    return func
+
+
+_default_text_stdin = _make_cached_stream_func(
+    lambda: sys.stdin, get_text_stdin)
+_default_text_stdout = _make_cached_stream_func(
+    lambda: sys.stdout, get_text_stdout)
+_default_text_stderr = _make_cached_stream_func(
+    lambda: sys.stderr, get_text_stderr)
+
+
+binary_streams = {
+    'stdin': get_binary_stdin,
+    'stdout': get_binary_stdout,
+    'stderr': get_binary_stderr,
+}
+
+text_streams = {
+    'stdin': get_text_stdin,
+    'stdout': get_text_stdout,
+    'stderr': get_text_stderr,
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_termui_impl.py
@@ -0,0 +1,621 @@
+# -*- coding: utf-8 -*-
+"""
+click._termui_impl
+~~~~~~~~~~~~~~~~~~
+
+This module contains implementations for the termui module. To keep the
+import time of Click down, some infrequently used functionality is
+placed in this module and only imported as needed.
+
+:copyright: © 2014 by the Pallets team.
+:license: BSD, see LICENSE.rst for more details.
+"""
+
+import os
+import sys
+import time
+import math
+import contextlib
+from ._compat import _default_text_stdout, range_type, PY2, isatty, \
+     open_stream, strip_ansi, term_len, get_best_encoding, WIN, int_types, \
+     CYGWIN
+from .utils import echo
+from .exceptions import ClickException
+
+
+if os.name == 'nt':
+    BEFORE_BAR = '\r'
+    AFTER_BAR = '\n'
+else:
+    BEFORE_BAR = '\r\033[?25l'
+    AFTER_BAR = '\033[?25h\n'
+
+
+def _length_hint(obj):
+    """Returns the length hint of an object."""
+    try:
+        return len(obj)
+    except (AttributeError, TypeError):
+        try:
+            get_hint = type(obj).__length_hint__
+        except AttributeError:
+            return None
+        try:
+            hint = get_hint(obj)
+        except TypeError:
+            return None
+        if hint is NotImplemented or \
+           not isinstance(hint, int_types) or \
+           hint < 0:
+            return None
+        return hint
+
+
+class ProgressBar(object):
+
+    def __init__(self, iterable, length=None, fill_char='#', empty_char=' ',
+                 bar_template='%(bar)s', info_sep='  ', show_eta=True,
+                 show_percent=None, show_pos=False, item_show_func=None,
+                 label=None, file=None, color=None, width=30):
+        self.fill_char = fill_char
+        self.empty_char = empty_char
+        self.bar_template = bar_template
+        self.info_sep = info_sep
+        self.show_eta = show_eta
+        self.show_percent = show_percent
+        self.show_pos = show_pos
+        self.item_show_func = item_show_func
+        self.label = label or ''
+        if file is None:
+            file = _default_text_stdout()
+        self.file = file
+        self.color = color
+        self.width = width
+        self.autowidth = width == 0
+
+        if length is None:
+            length = _length_hint(iterable)
+        if iterable is None:
+            if length is None:
+                raise TypeError('iterable or length is required')
+            iterable = range_type(length)
+        self.iter = iter(iterable)
+        self.length = length
+        self.length_known = length is not None
+        self.pos = 0
+        self.avg = []
+        self.start = self.last_eta = time.time()
+        self.eta_known = False
+        self.finished = False
+        self.max_width = None
+        self.entered = False
+        self.current_item = None
+        self.is_hidden = not isatty(self.file)
+        self._last_line = None
+        self.short_limit = 0.5
+
+    def __enter__(self):
+        self.entered = True
+        self.render_progress()
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        self.render_finish()
+
+    def __iter__(self):
+        if not self.entered:
+            raise RuntimeError('You need to use progress bars in a with block.')
+        self.render_progress()
+        return self.generator()
+
+    def is_fast(self):
+        return time.time() - self.start <= self.short_limit
+
+    def render_finish(self):
+        if self.is_hidden or self.is_fast():
+            return
+        self.file.write(AFTER_BAR)
+        self.file.flush()
+
+    @property
+    def pct(self):
+        if self.finished:
+            return 1.0
+        return min(self.pos / (float(self.length) or 1), 1.0)
+
+    @property
+    def time_per_iteration(self):
+        if not self.avg:
+            return 0.0
+        return sum(self.avg) / float(len(self.avg))
+
+    @property
+    def eta(self):
+        if self.length_known and not self.finished:
+            return self.time_per_iteration * (self.length - self.pos)
+        return 0.0
+
+    def format_eta(self):
+        if self.eta_known:
+            t = int(self.eta)
+            seconds = t % 60
+            t //= 60
+            minutes = t % 60
+            t //= 60
+            hours = t % 24
+            t //= 24
+            if t > 0:
+                days = t
+                return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds)
+            else:
+                return '%02d:%02d:%02d' % (hours, minutes, seconds)
+        return ''
+
+    def format_pos(self):
+        pos = str(self.pos)
+        if self.length_known:
+            pos += '/%s' % self.length
+        return pos
+
+    def format_pct(self):
+        return ('% 4d%%' % int(self.pct * 100))[1:]
+
+    def format_bar(self):
+        if self.length_known:
+            bar_length = int(self.pct * self.width)
+            bar = self.fill_char * bar_length
+            bar += self.empty_char * (self.width - bar_length)
+        elif self.finished:
+            bar = self.fill_char * self.width
+        else:
+            bar = list(self.empty_char * (self.width or 1))
+            if self.time_per_iteration != 0:
+                bar[int((math.cos(self.pos * self.time_per_iteration)
+                    / 2.0 + 0.5) * self.width)] = self.fill_char
+            bar = ''.join(bar)
+        return bar
+
+    def format_progress_line(self):
+        show_percent = self.show_percent
+
+        info_bits = []
+        if self.length_known and show_percent is None:
+            show_percent = not self.show_pos
+
+        if self.show_pos:
+            info_bits.append(self.format_pos())
+        if show_percent:
+            info_bits.append(self.format_pct())
+        if self.show_eta and self.eta_known and not self.finished:
+            info_bits.append(self.format_eta())
+        if self.item_show_func is not None:
+            item_info = self.item_show_func(self.current_item)
+            if item_info is not None:
+                info_bits.append(item_info)
+
+        return (self.bar_template % {
+            'label': self.label,
+            'bar': self.format_bar(),
+            'info': self.info_sep.join(info_bits)
+        }).rstrip()
+
+    def render_progress(self):
+        from .termui import get_terminal_size
+
+        if self.is_hidden:
+            return
+
+        buf = []
+        # Update width in case the terminal has been resized
+        if self.autowidth:
+            old_width = self.width
+            self.width = 0
+            clutter_length = term_len(self.format_progress_line())
+            new_width = max(0, get_terminal_size()[0] - clutter_length)
+            if new_width < old_width:
+                buf.append(BEFORE_BAR)
+                buf.append(' ' * self.max_width)
+                self.max_width = new_width
+            self.width = new_width
+
+        clear_width = self.width
+        if self.max_width is not None:
+            clear_width = self.max_width
+
+        buf.append(BEFORE_BAR)
+        line = self.format_progress_line()
+        line_len = term_len(line)
+        if self.max_width is None or self.max_width < line_len:
+            self.max_width = line_len
+
+        buf.append(line)
+        buf.append(' ' * (clear_width - line_len))
+        line = ''.join(buf)
+        # Render the line only if it changed.
+
+        if line != self._last_line and not self.is_fast():
+            self._last_line = line
+            echo(line, file=self.file, color=self.color, nl=False)
+            self.file.flush()
+
+    def make_step(self, n_steps):
+        self.pos += n_steps
+        if self.length_known and self.pos >= self.length:
+            self.finished = True
+
+        if (time.time() - self.last_eta) < 1.0:
+            return
+
+        self.last_eta = time.time()
+
+        # self.avg is a rolling list of length <= 7 of steps where steps are
+        # defined as time elapsed divided by the total progress through
+        # self.length.
+        if self.pos:
+            step = (time.time() - self.start) / self.pos
+        else:
+            step = time.time() - self.start
+
+        self.avg = self.avg[-6:] + [step]
+
+        self.eta_known = self.length_known
+
+    def update(self, n_steps):
+        self.make_step(n_steps)
+        self.render_progress()
+
+    def finish(self):
+        self.eta_known = 0
+        self.current_item = None
+        self.finished = True
+
+    def generator(self):
+        """
+        Returns a generator which yields the items added to the bar during
+        construction, and updates the progress bar *after* the yielded block
+        returns.
+        """
+        if not self.entered:
+            raise RuntimeError('You need to use progress bars in a with block.')
+
+        if self.is_hidden:
+            for rv in self.iter:
+                yield rv
+        else:
+            for rv in self.iter:
+                self.current_item = rv
+                yield rv
+                self.update(1)
+            self.finish()
+            self.render_progress()
+
+
+def pager(generator, color=None):
+    """Decide what method to use for paging through text."""
+    stdout = _default_text_stdout()
+    if not isatty(sys.stdin) or not isatty(stdout):
+        return _nullpager(stdout, generator, color)
+    pager_cmd = (os.environ.get('PAGER', None) or '').strip()
+    if pager_cmd:
+        if WIN:
+            return _tempfilepager(generator, pager_cmd, color)
+        return _pipepager(generator, pager_cmd, color)
+    if os.environ.get('TERM') in ('dumb', 'emacs'):
+        return _nullpager(stdout, generator, color)
+    if WIN or sys.platform.startswith('os2'):
+        return _tempfilepager(generator, 'more <', color)
+    if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
+        return _pipepager(generator, 'less', color)
+
+    import tempfile
+    fd, filename = tempfile.mkstemp()
+    os.close(fd)
+    try:
+        if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
+            return _pipepager(generator, 'more', color)
+        return _nullpager(stdout, generator, color)
+    finally:
+        os.unlink(filename)
+
+
+def _pipepager(generator, cmd, color):
+    """Page through text by feeding it to another program.  Invoking a
+    pager through this might support colors.
+    """
+    import subprocess
+    env = dict(os.environ)
+
+    # If we're piping to less we might support colors under the
+    # condition that
+    cmd_detail = cmd.rsplit('/', 1)[-1].split()
+    if color is None and cmd_detail[0] == 'less':
+        less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:])
+        if not less_flags:
+            env['LESS'] = '-R'
+            color = True
+        elif 'r' in less_flags or 'R' in less_flags:
+            color = True
+
+    c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
+                         env=env)
+    encoding = get_best_encoding(c.stdin)
+    try:
+        for text in generator:
+            if not color:
+                text = strip_ansi(text)
+
+            c.stdin.write(text.encode(encoding, 'replace'))
+    except (IOError, KeyboardInterrupt):
+        pass
+    else:
+        c.stdin.close()
+
+    # Less doesn't respect ^C, but catches it for its own UI purposes (aborting
+    # search or other commands inside less).
+    #
+    # That means when the user hits ^C, the parent process (click) terminates,
+    # but less is still alive, paging the output and messing up the terminal.
+    #
+    # If the user wants to make the pager exit on ^C, they should set
+    # `LESS='-K'`. It's not our decision to make.
+    while True:
+        try:
+            c.wait()
+        except KeyboardInterrupt:
+            pass
+        else:
+            break
+
+
+def _tempfilepager(generator, cmd, color):
+    """Page through text by invoking a program on a temporary file."""
+    import tempfile
+    filename = tempfile.mktemp()
+    # TODO: This never terminates if the passed generator never terminates.
+    text = "".join(generator)
+    if not color:
+        text = strip_ansi(text)
+    encoding = get_best_encoding(sys.stdout)
+    with open_stream(filename, 'wb')[0] as f:
+        f.write(text.encode(encoding))
+    try:
+        os.system(cmd + ' "' + filename + '"')
+    finally:
+        os.unlink(filename)
+
+
+def _nullpager(stream, generator, color):
+    """Simply print unformatted text.  This is the ultimate fallback."""
+    for text in generator:
+        if not color:
+            text = strip_ansi(text)
+        stream.write(text)
+
+
+class Editor(object):
+
+    def __init__(self, editor=None, env=None, require_save=True,
+                 extension='.txt'):
+        self.editor = editor
+        self.env = env
+        self.require_save = require_save
+        self.extension = extension
+
+    def get_editor(self):
+        if self.editor is not None:
+            return self.editor
+        for key in 'VISUAL', 'EDITOR':
+            rv = os.environ.get(key)
+            if rv:
+                return rv
+        if WIN:
+            return 'notepad'
+        for editor in 'vim', 'nano':
+            if os.system('which %s >/dev/null 2>&1' % editor) == 0:
+                return editor
+        return 'vi'
+
+    def edit_file(self, filename):
+        import subprocess
+        editor = self.get_editor()
+        if self.env:
+            environ = os.environ.copy()
+            environ.update(self.env)
+        else:
+            environ = None
+        try:
+            c = subprocess.Popen('%s "%s"' % (editor, filename),
+                                 env=environ, shell=True)
+            exit_code = c.wait()
+            if exit_code != 0:
+                raise ClickException('%s: Editing failed!' % editor)
+        except OSError as e:
+            raise ClickException('%s: Editing failed: %s' % (editor, e))
+
+    def edit(self, text):
+        import tempfile
+
+        text = text or ''
+        if text and not text.endswith('\n'):
+            text += '\n'
+
+        fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension)
+        try:
+            if WIN:
+                encoding = 'utf-8-sig'
+                text = text.replace('\n', '\r\n')
+            else:
+                encoding = 'utf-8'
+            text = text.encode(encoding)
+
+            f = os.fdopen(fd, 'wb')
+            f.write(text)
+            f.close()
+            timestamp = os.path.getmtime(name)
+
+            self.edit_file(name)
+
+            if self.require_save \
+               and os.path.getmtime(name) == timestamp:
+                return None
+
+            f = open(name, 'rb')
+            try:
+                rv = f.read()
+            finally:
+                f.close()
+            return rv.decode('utf-8-sig').replace('\r\n', '\n')
+        finally:
+            os.unlink(name)
+
+
+def open_url(url, wait=False, locate=False):
+    import subprocess
+
+    def _unquote_file(url):
+        try:
+            import urllib
+        except ImportError:
+            import urllib
+        if url.startswith('file://'):
+            url = urllib.unquote(url[7:])
+        return url
+
+    if sys.platform == 'darwin':
+        args = ['open']
+        if wait:
+            args.append('-W')
+        if locate:
+            args.append('-R')
+        args.append(_unquote_file(url))
+        null = open('/dev/null', 'w')
+        try:
+            return subprocess.Popen(args, stderr=null).wait()
+        finally:
+            null.close()
+    elif WIN:
+        if locate:
+            url = _unquote_file(url)
+            args = 'explorer /select,"%s"' % _unquote_file(
+                url.replace('"', ''))
+        else:
+            args = 'start %s "" "%s"' % (
+                wait and '/WAIT' or '', url.replace('"', ''))
+        return os.system(args)
+    elif CYGWIN:
+        if locate:
+            url = _unquote_file(url)
+            args = 'cygstart "%s"' % (os.path.dirname(url).replace('"', ''))
+        else:
+            args = 'cygstart %s "%s"' % (
+                wait and '-w' or '', url.replace('"', ''))
+        return os.system(args)
+
+    try:
+        if locate:
+            url = os.path.dirname(_unquote_file(url)) or '.'
+        else:
+            url = _unquote_file(url)
+        c = subprocess.Popen(['xdg-open', url])
+        if wait:
+            return c.wait()
+        return 0
+    except OSError:
+        if url.startswith(('http://', 'https://')) and not locate and not wait:
+            import webbrowser
+            webbrowser.open(url)
+            return 0
+        return 1
+
+
+def _translate_ch_to_exc(ch):
+    if ch == u'\x03':
+        raise KeyboardInterrupt()
+    if ch == u'\x04' and not WIN:  # Unix-like, Ctrl+D
+        raise EOFError()
+    if ch == u'\x1a' and WIN:      # Windows, Ctrl+Z
+        raise EOFError()
+
+
+if WIN:
+    import msvcrt
+
+    @contextlib.contextmanager
+    def raw_terminal():
+        yield
+
+    def getchar(echo):
+        # The function `getch` will return a bytes object corresponding to
+        # the pressed character. Since Windows 10 build 1803, it will also
+        # return \x00 when called a second time after pressing a regular key.
+        #
+        # `getwch` does not share this probably-bugged behavior. Moreover, it
+        # returns a Unicode object by default, which is what we want.
+        #
+        # Either of these functions will return \x00 or \xe0 to indicate
+        # a special key, and you need to call the same function again to get
+        # the "rest" of the code. The fun part is that \u00e0 is
+        # "latin small letter a with grave", so if you type that on a French
+        # keyboard, you _also_ get a \xe0.
+        # E.g., consider the Up arrow. This returns \xe0 and then \x48. The
+        # resulting Unicode string reads as "a with grave" + "capital H".
+        # This is indistinguishable from when the user actually types
+        # "a with grave" and then "capital H".
+        #
+        # When \xe0 is returned, we assume it's part of a special-key sequence
+        # and call `getwch` again, but that means that when the user types
+        # the \u00e0 character, `getchar` doesn't return until a second
+        # character is typed.
+        # The alternative is returning immediately, but that would mess up
+        # cross-platform handling of arrow keys and others that start with
+        # \xe0. Another option is using `getch`, but then we can't reliably
+        # read non-ASCII characters, because return values of `getch` are
+        # limited to the current 8-bit codepage.
+        #
+        # Anyway, Click doesn't claim to do this Right(tm), and using `getwch`
+        # is doing the right thing in more situations than with `getch`.
+        if echo:
+            func = msvcrt.getwche
+        else:
+            func = msvcrt.getwch
+
+        rv = func()
+        if rv in (u'\x00', u'\xe0'):
+            # \x00 and \xe0 are control characters that indicate special key,
+            # see above.
+            rv += func()
+        _translate_ch_to_exc(rv)
+        return rv
+else:
+    import tty
+    import termios
+
+    @contextlib.contextmanager
+    def raw_terminal():
+        if not isatty(sys.stdin):
+            f = open('/dev/tty')
+            fd = f.fileno()
+        else:
+            fd = sys.stdin.fileno()
+            f = None
+        try:
+            old_settings = termios.tcgetattr(fd)
+            try:
+                tty.setraw(fd)
+                yield fd
+            finally:
+                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
+                sys.stdout.flush()
+                if f is not None:
+                    f.close()
+        except termios.error:
+            pass
+
+    def getchar(echo):
+        with raw_terminal() as fd:
+            ch = os.read(fd, 32)
+            ch = ch.decode(get_best_encoding(sys.stdin), 'replace')
+            if echo and isatty(sys.stdout):
+                sys.stdout.write(ch)
+            _translate_ch_to_exc(ch)
+            return ch
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_textwrap.py
@@ -0,0 +1,38 @@
+import textwrap
+from contextlib import contextmanager
+
+
+class TextWrapper(textwrap.TextWrapper):
+
+    def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
+        space_left = max(width - cur_len, 1)
+
+        if self.break_long_words:
+            last = reversed_chunks[-1]
+            cut = last[:space_left]
+            res = last[space_left:]
+            cur_line.append(cut)
+            reversed_chunks[-1] = res
+        elif not cur_line:
+            cur_line.append(reversed_chunks.pop())
+
+    @contextmanager
+    def extra_indent(self, indent):
+        old_initial_indent = self.initial_indent
+        old_subsequent_indent = self.subsequent_indent
+        self.initial_indent += indent
+        self.subsequent_indent += indent
+        try:
+            yield
+        finally:
+            self.initial_indent = old_initial_indent
+            self.subsequent_indent = old_subsequent_indent
+
+    def indent_only(self, text):
+        rv = []
+        for idx, line in enumerate(text.splitlines()):
+            indent = self.initial_indent
+            if idx > 0:
+                indent = self.subsequent_indent
+            rv.append(indent + line)
+        return '\n'.join(rv)
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_unicodefun.py
@@ -0,0 +1,125 @@
+import os
+import sys
+import codecs
+
+from ._compat import PY2
+
+
+# If someone wants to vendor click, we want to ensure the
+# correct package is discovered.  Ideally we could use a
+# relative import here but unfortunately Python does not
+# support that.
+click = sys.modules[__name__.rsplit('.', 1)[0]]
+
+
+def _find_unicode_literals_frame():
+    import __future__
+    if not hasattr(sys, '_getframe'):  # not all Python implementations have it
+        return 0
+    frm = sys._getframe(1)
+    idx = 1
+    while frm is not None:
+        if frm.f_globals.get('__name__', '').startswith('click.'):
+            frm = frm.f_back
+            idx += 1
+        elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag:
+            return idx
+        else:
+            break
+    return 0
+
+
+def _check_for_unicode_literals():
+    if not __debug__:
+        return
+    if not PY2 or click.disable_unicode_literals_warning:
+        return
+    bad_frame = _find_unicode_literals_frame()
+    if bad_frame <= 0:
+        return
+    from warnings import warn
+    warn(Warning('Click detected the use of the unicode_literals '
+                 '__future__ import.  This is heavily discouraged '
+                 'because it can introduce subtle bugs in your '
+                 'code.  You should instead use explicit u"" literals '
+                 'for your unicode strings.  For more information see '
+                 'https://click.palletsprojects.com/python3/'),
+         stacklevel=bad_frame)
+
+
+def _verify_python3_env():
+    """Ensures that the environment is good for unicode on Python 3."""
+    if PY2:
+        return
+    try:
+        import locale
+        fs_enc = codecs.lookup(locale.getpreferredencoding()).name
+    except Exception:
+        fs_enc = 'ascii'
+    if fs_enc != 'ascii':
+        return
+
+    extra = ''
+    if os.name == 'posix':
+        import subprocess
+        try:
+            rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE,
+                                  stderr=subprocess.PIPE).communicate()[0]
+        except OSError:
+            rv = b''
+        good_locales = set()
+        has_c_utf8 = False
+
+        # Make sure we're operating on text here.
+        if isinstance(rv, bytes):
+            rv = rv.decode('ascii', 'replace')
+
+        for line in rv.splitlines():
+            locale = line.strip()
+            if locale.lower().endswith(('.utf-8', '.utf8')):
+                good_locales.add(locale)
+                if locale.lower() in ('c.utf8', 'c.utf-8'):
+                    has_c_utf8 = True
+
+        extra += '\n\n'
+        if not good_locales:
+            extra += (
+                'Additional information: on this system no suitable UTF-8\n'
+                'locales were discovered.  This most likely requires resolving\n'
+                'by reconfiguring the locale system.'
+            )
+        elif has_c_utf8:
+            extra += (
+                'This system supports the C.UTF-8 locale which is recommended.\n'
+                'You might be able to resolve your issue by exporting the\n'
+                'following environment variables:\n\n'
+                '    export LC_ALL=C.UTF-8\n'
+                '    export LANG=C.UTF-8'
+            )
+        else:
+            extra += (
+                'This system lists a couple of UTF-8 supporting locales that\n'
+                'you can pick from.  The following suitable locales were\n'
+                'discovered: %s'
+            ) % ', '.join(sorted(good_locales))
+
+        bad_locale = None
+        for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'):
+            if locale and locale.lower().endswith(('.utf-8', '.utf8')):
+                bad_locale = locale
+            if locale is not None:
+                break
+        if bad_locale is not None:
+            extra += (
+                '\n\nClick discovered that you exported a UTF-8 locale\n'
+                'but the locale system could not pick up from it because\n'
+                'it does not exist.  The exported locale is "%s" but it\n'
+                'is not supported'
+            ) % bad_locale
+
+    raise RuntimeError(
+        'Click will abort further execution because Python 3 was'
+        ' configured to use ASCII as encoding for the environment.'
+        ' Consult https://click.palletsprojects.com/en/7.x/python3/ for'
+        ' mitigation steps.' + extra
+    )
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/_winconsole.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+# This module is based on the excellent work by Adam Bartoš who
+# provided a lot of what went into the implementation here in
+# the discussion to issue1602 in the Python bug tracker.
+#
+# There are some general differences in regards to how this works
+# compared to the original patches as we do not need to patch
+# the entire interpreter but just work in our little world of
+# echo and prmopt.
+
+import io
+import os
+import sys
+import zlib
+import time
+import ctypes
+import msvcrt
+from ._compat import _NonClosingTextIOWrapper, text_type, PY2
+from ctypes import byref, POINTER, c_int, c_char, c_char_p, \
+     c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE
+try:
+    from ctypes import pythonapi
+    PyObject_GetBuffer = pythonapi.PyObject_GetBuffer
+    PyBuffer_Release = pythonapi.PyBuffer_Release
+except ImportError:
+    pythonapi = None
+from ctypes.wintypes import LPWSTR, LPCWSTR
+
+
+c_ssize_p = POINTER(c_ssize_t)
+
+kernel32 = windll.kernel32
+GetStdHandle = kernel32.GetStdHandle
+ReadConsoleW = kernel32.ReadConsoleW
+WriteConsoleW = kernel32.WriteConsoleW
+GetLastError = kernel32.GetLastError
+GetCommandLineW = WINFUNCTYPE(LPWSTR)(
+    ('GetCommandLineW', windll.kernel32))
+CommandLineToArgvW = WINFUNCTYPE(
+    POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(
+        ('CommandLineToArgvW', windll.shell32))
+
+
+STDIN_HANDLE = GetStdHandle(-10)
+STDOUT_HANDLE = GetStdHandle(-11)
+STDERR_HANDLE = GetStdHandle(-12)
+
+
+PyBUF_SIMPLE = 0
+PyBUF_WRITABLE = 1
+
+ERROR_SUCCESS = 0
+ERROR_NOT_ENOUGH_MEMORY = 8
+ERROR_OPERATION_ABORTED = 995
+
+STDIN_FILENO = 0
+STDOUT_FILENO = 1
+STDERR_FILENO = 2
+
+EOF = b'\x1a'
+MAX_BYTES_WRITTEN = 32767
+
+
+class Py_buffer(ctypes.Structure):
+    _fields_ = [
+        ('buf', c_void_p),
+        ('obj', py_object),
+        ('len', c_ssize_t),
+        ('itemsize', c_ssize_t),
+        ('readonly', c_int),
+        ('ndim', c_int),
+        ('format', c_char_p),
+        ('shape', c_ssize_p),
+        ('strides', c_ssize_p),
+        ('suboffsets', c_ssize_p),
+        ('internal', c_void_p)
+    ]
+
+    if PY2:
+        _fields_.insert(-1, ('smalltable', c_ssize_t * 2))
+
+
+# On PyPy we cannot get buffers so our ability to operate here is
+# serverly limited.
+if pythonapi is None:
+    get_buffer = None
+else:
+    def get_buffer(obj, writable=False):
+        buf = Py_buffer()
+        flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE
+        PyObject_GetBuffer(py_object(obj), byref(buf), flags)
+        try:
+            buffer_type = c_char * buf.len
+            return buffer_type.from_address(buf.buf)
+        finally:
+            PyBuffer_Release(byref(buf))
+
+
+class _WindowsConsoleRawIOBase(io.RawIOBase):
+
+    def __init__(self, handle):
+        self.handle = handle
+
+    def isatty(self):
+        io.RawIOBase.isatty(self)
+        return True
+
+
+class _WindowsConsoleReader(_WindowsConsoleRawIOBase):
+
+    def readable(self):
+        return True
+
+    def readinto(self, b):
+        bytes_to_be_read = len(b)
+        if not bytes_to_be_read:
+            return 0
+        elif bytes_to_be_read % 2:
+            raise ValueError('cannot read odd number of bytes from '
+                             'UTF-16-LE encoded console')
+
+        buffer = get_buffer(b, writable=True)
+        code_units_to_be_read = bytes_to_be_read // 2
+        code_units_read = c_ulong()
+
+        rv = ReadConsoleW(self.handle, buffer, code_units_to_be_read,
+                          byref(code_units_read), None)
+        if GetLastError() == ERROR_OPERATION_ABORTED:
+            # wait for KeyboardInterrupt
+            time.sleep(0.1)
+        if not rv:
+            raise OSError('Windows error: %s' % GetLastError())
+
+        if buffer[0] == EOF:
+            return 0
+        return 2 * code_units_read.value
+
+
+class _WindowsConsoleWriter(_WindowsConsoleRawIOBase):
+
+    def writable(self):
+        return True
+
+    @staticmethod
+    def _get_error_message(errno):
+        if errno == ERROR_SUCCESS:
+            return 'ERROR_SUCCESS'
+        elif errno == ERROR_NOT_ENOUGH_MEMORY:
+            return 'ERROR_NOT_ENOUGH_MEMORY'
+        return 'Windows error %s' % errno
+
+    def write(self, b):
+        bytes_to_be_written = len(b)
+        buf = get_buffer(b)
+        code_units_to_be_written = min(bytes_to_be_written,
+                                       MAX_BYTES_WRITTEN) // 2
+        code_units_written = c_ulong()
+
+        WriteConsoleW(self.handle, buf, code_units_to_be_written,
+                      byref(code_units_written), None)
+        bytes_written = 2 * code_units_written.value
+
+        if bytes_written == 0 and bytes_to_be_written > 0:
+            raise OSError(self._get_error_message(GetLastError()))
+        return bytes_written
+
+
+class ConsoleStream(object):
+
+    def __init__(self, text_stream, byte_stream):
+        self._text_stream = text_stream
+        self.buffer = byte_stream
+
+    @property
+    def name(self):
+        return self.buffer.name
+
+    def write(self, x):
+        if isinstance(x, text_type):
+            return self._text_stream.write(x)
+        try:
+            self.flush()
+        except Exception:
+            pass
+        return self.buffer.write(x)
+
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+
+    def __getattr__(self, name):
+        return getattr(self._text_stream, name)
+
+    def isatty(self):
+        return self.buffer.isatty()
+
+    def __repr__(self):
+        return '<ConsoleStream name=%r encoding=%r>' % (
+            self.name,
+            self.encoding,
+        )
+
+
+class WindowsChunkedWriter(object):
+    """
+    Wraps a stream (such as stdout), acting as a transparent proxy for all
+    attribute access apart from method 'write()' which we wrap to write in
+    limited chunks due to a Windows limitation on binary console streams.
+    """
+    def __init__(self, wrapped):
+        # double-underscore everything to prevent clashes with names of
+        # attributes on the wrapped stream object.
+        self.__wrapped = wrapped
+
+    def __getattr__(self, name):
+        return getattr(self.__wrapped, name)
+
+    def write(self, text):
+        total_to_write = len(text)
+        written = 0
+
+        while written < total_to_write:
+            to_write = min(total_to_write - written, MAX_BYTES_WRITTEN)
+            self.__wrapped.write(text[written:written+to_write])
+            written += to_write
+
+
+_wrapped_std_streams = set()
+
+
+def _wrap_std_stream(name):
+    # Python 2 & Windows 7 and below
+    if PY2 and sys.getwindowsversion()[:2] <= (6, 1) and name not in _wrapped_std_streams:
+        setattr(sys, name, WindowsChunkedWriter(getattr(sys, name)))
+        _wrapped_std_streams.add(name)
+
+
+def _get_text_stdin(buffer_stream):
+    text_stream = _NonClosingTextIOWrapper(
+        io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)),
+        'utf-16-le', 'strict', line_buffering=True)
+    return ConsoleStream(text_stream, buffer_stream)
+
+
+def _get_text_stdout(buffer_stream):
+    text_stream = _NonClosingTextIOWrapper(
+        io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)),
+        'utf-16-le', 'strict', line_buffering=True)
+    return ConsoleStream(text_stream, buffer_stream)
+
+
+def _get_text_stderr(buffer_stream):
+    text_stream = _NonClosingTextIOWrapper(
+        io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)),
+        'utf-16-le', 'strict', line_buffering=True)
+    return ConsoleStream(text_stream, buffer_stream)
+
+
+if PY2:
+    def _hash_py_argv():
+        return zlib.crc32('\x00'.join(sys.argv[1:]))
+
+    _initial_argv_hash = _hash_py_argv()
+
+    def _get_windows_argv():
+        argc = c_int(0)
+        argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))
+        argv = [argv_unicode[i] for i in range(0, argc.value)]
+
+        if not hasattr(sys, 'frozen'):
+            argv = argv[1:]
+            while len(argv) > 0:
+                arg = argv[0]
+                if not arg.startswith('-') or arg == '-':
+                    break
+                argv = argv[1:]
+                if arg.startswith(('-c', '-m')):
+                    break
+
+        return argv[1:]
+
+
+_stream_factories = {
+    0: _get_text_stdin,
+    1: _get_text_stdout,
+    2: _get_text_stderr,
+}
+
+
+def _get_windows_console_stream(f, encoding, errors):
+    if get_buffer is not None and \
+       encoding in ('utf-16-le', None) \
+       and errors in ('strict', None) and \
+       hasattr(f, 'isatty') and f.isatty():
+        func = _stream_factories.get(f.fileno())
+        if func is not None:
+            if not PY2:
+                f = getattr(f, 'buffer', None)
+                if f is None:
+                    return None
+            else:
+                # If we are on Python 2 we need to set the stream that we
+                # deal with to binary mode as otherwise the exercise if a
+                # bit moot.  The same problems apply as for
+                # get_binary_stdin and friends from _compat.
+                msvcrt.setmode(f.fileno(), os.O_BINARY)
+            return func(f)
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/core.py
@@ -0,0 +1,1856 @@
+import errno
+import inspect
+import os
+import sys
+from contextlib import contextmanager
+from itertools import repeat
+from functools import update_wrapper
+
+from .types import convert_type, IntRange, BOOL
+from .utils import PacifyFlushWrapper, make_str, make_default_short_help, \
+     echo, get_os_args
+from .exceptions import ClickException, UsageError, BadParameter, Abort, \
+     MissingParameter, Exit
+from .termui import prompt, confirm, style
+from .formatting import HelpFormatter, join_options
+from .parser import OptionParser, split_opt
+from .globals import push_context, pop_context
+
+from ._compat import PY2, isidentifier, iteritems, string_types
+from ._unicodefun import _check_for_unicode_literals, _verify_python3_env
+
+
+_missing = object()
+
+
+SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...'
+SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...'
+
+DEPRECATED_HELP_NOTICE = ' (DEPRECATED)'
+DEPRECATED_INVOKE_NOTICE = 'DeprecationWarning: ' + \
+                           'The command %(name)s is deprecated.'
+
+
+def _maybe_show_deprecated_notice(cmd):
+    if cmd.deprecated:
+        echo(style(DEPRECATED_INVOKE_NOTICE % {'name': cmd.name}, fg='red'), err=True)
+
+
+def fast_exit(code):
+    """Exit without garbage collection, this speeds up exit by about 10ms for
+    things like bash completion.
+    """
+    sys.stdout.flush()
+    sys.stderr.flush()
+    os._exit(code)
+
+
+def _bashcomplete(cmd, prog_name, complete_var=None):
+    """Internal handler for the bash completion support."""
+    if complete_var is None:
+        complete_var = '_%s_COMPLETE' % (prog_name.replace('-', '_')).upper()
+    complete_instr = os.environ.get(complete_var)
+    if not complete_instr:
+        return
+
+    from ._bashcomplete import bashcomplete
+    if bashcomplete(cmd, prog_name, complete_var, complete_instr):
+        fast_exit(1)
+
+
+def _check_multicommand(base_command, cmd_name, cmd, register=False):
+    if not base_command.chain or not isinstance(cmd, MultiCommand):
+        return
+    if register:
+        hint = 'It is not possible to add multi commands as children to ' \
+               'another multi command that is in chain mode'
+    else:
+        hint = 'Found a multi command as subcommand to a multi command ' \
+               'that is in chain mode.  This is not supported'
+    raise RuntimeError('%s.  Command "%s" is set to chain and "%s" was '
+                       'added as subcommand but it in itself is a '
+                       'multi command.  ("%s" is a %s within a chained '
+                       '%s named "%s").' % (
+                           hint, base_command.name, cmd_name,
+                           cmd_name, cmd.__class__.__name__,
+                           base_command.__class__.__name__,
+                           base_command.name))
+
+
+def batch(iterable, batch_size):
+    return list(zip(*repeat(iter(iterable), batch_size)))
+
+
+def invoke_param_callback(callback, ctx, param, value):
+    code = getattr(callback, '__code__', None)
+    args = getattr(code, 'co_argcount', 3)
+
+    if args < 3:
+        # This will become a warning in Click 3.0:
+        from warnings import warn
+        warn(Warning('Invoked legacy parameter callback "%s".  The new '
+                     'signature for such callbacks starting with '
+                     'click 2.0 is (ctx, param, value).'
+                     % callback), stacklevel=3)
+        return callback(ctx, value)
+    return callback(ctx, param, value)
+
+
+@contextmanager
+def augment_usage_errors(ctx, param=None):
+    """Context manager that attaches extra information to exceptions that
+    fly.
+    """
+    try:
+        yield
+    except BadParameter as e:
+        if e.ctx is None:
+            e.ctx = ctx
+        if param is not None and e.param is None:
+            e.param = param
+        raise
+    except UsageError as e:
+        if e.ctx is None:
+            e.ctx = ctx
+        raise
+
+
+def iter_params_for_processing(invocation_order, declaration_order):
+    """Given a sequence of parameters in the order as should be considered
+    for processing and an iterable of parameters that exist, this returns
+    a list in the correct order as they should be processed.
+    """
+    def sort_key(item):
+        try:
+            idx = invocation_order.index(item)
+        except ValueError:
+            idx = float('inf')
+        return (not item.is_eager, idx)
+
+    return sorted(declaration_order, key=sort_key)
+
+
+class Context(object):
+    """The context is a special internal object that holds state relevant
+    for the script execution at every single level.  It's normally invisible
+    to commands unless they opt-in to getting access to it.
+
+    The context is useful as it can pass internal objects around and can
+    control special execution features such as reading data from
+    environment variables.
+
+    A context can be used as context manager in which case it will call
+    :meth:`close` on teardown.
+
+    .. versionadded:: 2.0
+       Added the `resilient_parsing`, `help_option_names`,
+       `token_normalize_func` parameters.
+
+    .. versionadded:: 3.0
+       Added the `allow_extra_args` and `allow_interspersed_args`
+       parameters.
+
+    .. versionadded:: 4.0
+       Added the `color`, `ignore_unknown_options`, and
+       `max_content_width` parameters.
+
+    :param command: the command class for this context.
+    :param parent: the parent context.
+    :param info_name: the info name for this invocation.  Generally this
+                      is the most descriptive name for the script or
+                      command.  For the toplevel script it is usually
+                      the name of the script, for commands below it it's
+                      the name of the script.
+    :param obj: an arbitrary object of user data.
+    :param auto_envvar_prefix: the prefix to use for automatic environment
+                               variables.  If this is `None` then reading
+                               from environment variables is disabled.  This
+                               does not affect manually set environment
+                               variables which are always read.
+    :param default_map: a dictionary (like object) with default values
+                        for parameters.
+    :param terminal_width: the width of the terminal.  The default is
+                           inherit from parent context.  If no context
+                           defines the terminal width then auto
+                           detection will be applied.
+    :param max_content_width: the maximum width for content rendered by
+                              Click (this currently only affects help
+                              pages).  This defaults to 80 characters if
+                              not overridden.  In other words: even if the
+                              terminal is larger than that, Click will not
+                              format things wider than 80 characters by
+                              default.  In addition to that, formatters might
+                              add some safety mapping on the right.
+    :param resilient_parsing: if this flag is enabled then Click will
+                              parse without any interactivity or callback
+                              invocation.  Default values will also be
+                              ignored.  This is useful for implementing
+                              things such as completion support.
+    :param allow_extra_args: if this is set to `True` then extra arguments
+                             at the end will not raise an error and will be
+                             kept on the context.  The default is to inherit
+                             from the command.
+    :param allow_interspersed_args: if this is set to `False` then options
+                                    and arguments cannot be mixed.  The
+                                    default is to inherit from the command.
+    :param ignore_unknown_options: instructs click to ignore options it does
+                                   not know and keeps them for later
+                                   processing.
+    :param help_option_names: optionally a list of strings that define how
+                              the default help parameter is named.  The
+                              default is ``['--help']``.
+    :param token_normalize_func: an optional function that is used to
+                                 normalize tokens (options, choices,
+                                 etc.).  This for instance can be used to
+                                 implement case insensitive behavior.
+    :param color: controls if the terminal supports ANSI colors or not.  The
+                  default is autodetection.  This is only needed if ANSI
+                  codes are used in texts that Click prints which is by
+                  default not the case.  This for instance would affect
+                  help output.
+    """
+
+    def __init__(self, command, parent=None, info_name=None, obj=None,
+                 auto_envvar_prefix=None, default_map=None,
+                 terminal_width=None, max_content_width=None,
+                 resilient_parsing=False, allow_extra_args=None,
+                 allow_interspersed_args=None,
+                 ignore_unknown_options=None, help_option_names=None,
+                 token_normalize_func=None, color=None):
+        #: the parent context or `None` if none exists.
+        self.parent = parent
+        #: the :class:`Command` for this context.
+        self.command = command
+        #: the descriptive information name
+        self.info_name = info_name
+        #: the parsed parameters except if the value is hidden in which
+        #: case it's not remembered.
+        self.params = {}
+        #: the leftover arguments.
+        self.args = []
+        #: protected arguments.  These are arguments that are prepended
+        #: to `args` when certain parsing scenarios are encountered but
+        #: must be never propagated to another arguments.  This is used
+        #: to implement nested parsing.
+        self.protected_args = []
+        if obj is None and parent is not None:
+            obj = parent.obj
+        #: the user object stored.
+        self.obj = obj
+        self._meta = getattr(parent, 'meta', {})
+
+        #: A dictionary (-like object) with defaults for parameters.
+        if default_map is None \
+           and parent is not None \
+           and parent.default_map is not None:
+            default_map = parent.default_map.get(info_name)
+        self.default_map = default_map
+
+        #: This flag indicates if a subcommand is going to be executed. A
+        #: group callback can use this information to figure out if it's
+        #: being executed directly or because the execution flow passes
+        #: onwards to a subcommand. By default it's None, but it can be
+        #: the name of the subcommand to execute.
+        #:
+        #: If chaining is enabled this will be set to ``'*'`` in case
+        #: any commands are executed.  It is however not possible to
+        #: figure out which ones.  If you require this knowledge you
+        #: should use a :func:`resultcallback`.
+        self.invoked_subcommand = None
+
+        if terminal_width is None and parent is not None:
+            terminal_width = parent.terminal_width
+        #: The width of the terminal (None is autodetection).
+        self.terminal_width = terminal_width
+
+        if max_content_width is None and parent is not None:
+            max_content_width = parent.max_content_width
+        #: The maximum width of formatted content (None implies a sensible
+        #: default which is 80 for most things).
+        self.max_content_width = max_content_width
+
+        if allow_extra_args is None:
+            allow_extra_args = command.allow_extra_args
+        #: Indicates if the context allows extra args or if it should
+        #: fail on parsing.
+        #:
+        #: .. versionadded:: 3.0
+        self.allow_extra_args = allow_extra_args
+
+        if allow_interspersed_args is None:
+            allow_interspersed_args = command.allow_interspersed_args
+        #: Indicates if the context allows mixing of arguments and
+        #: options or not.
+        #:
+        #: .. versionadded:: 3.0
+        self.allow_interspersed_args = allow_interspersed_args
+
+        if ignore_unknown_options is None:
+            ignore_unknown_options = command.ignore_unknown_options
+        #: Instructs click to ignore options that a command does not
+        #: understand and will store it on the context for later
+        #: processing.  This is primarily useful for situations where you
+        #: want to call into external programs.  Generally this pattern is
+        #: strongly discouraged because it's not possibly to losslessly
+        #: forward all arguments.
+        #:
+        #: .. versionadded:: 4.0
+        self.ignore_unknown_options = ignore_unknown_options
+
+        if help_option_names is None:
+            if parent is not None:
+                help_option_names = parent.help_option_names
+            else:
+                help_option_names = ['--help']
+
+        #: The names for the help options.
+        self.help_option_names = help_option_names
+
+        if token_normalize_func is None and parent is not None:
+            token_normalize_func = parent.token_normalize_func
+
+        #: An optional normalization function for tokens.  This is
+        #: options, choices, commands etc.
+        self.token_normalize_func = token_normalize_func
+
+        #: Indicates if resilient parsing is enabled.  In that case Click
+        #: will do its best to not cause any failures and default values
+        #: will be ignored. Useful for completion.
+        self.resilient_parsing = resilient_parsing
+
+        # If there is no envvar prefix yet, but the parent has one and
+        # the command on this level has a name, we can expand the envvar
+        # prefix automatically.
+        if auto_envvar_prefix is None:
+            if parent is not None \
+               and parent.auto_envvar_prefix is not None and \
+               self.info_name is not None:
+                auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix,
+                                           self.info_name.upper())
+        else:
+            auto_envvar_prefix = auto_envvar_prefix.upper()
+        self.auto_envvar_prefix = auto_envvar_prefix
+
+        if color is None and parent is not None:
+            color = parent.color
+
+        #: Controls if styling output is wanted or not.
+        self.color = color
+
+        self._close_callbacks = []
+        self._depth = 0
+
+    def __enter__(self):
+        self._depth += 1
+        push_context(self)
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        self._depth -= 1
+        if self._depth == 0:
+            self.close()
+        pop_context()
+
+    @contextmanager
+    def scope(self, cleanup=True):
+        """This helper method can be used with the context object to promote
+        it to the current thread local (see :func:`get_current_context`).
+        The default behavior of this is to invoke the cleanup functions which
+        can be disabled by setting `cleanup` to `False`.  The cleanup
+        functions are typically used for things such as closing file handles.
+
+        If the cleanup is intended the context object can also be directly
+        used as a context manager.
+
+        Example usage::
+
+            with ctx.scope():
+                assert get_current_context() is ctx
+
+        This is equivalent::
+
+            with ctx:
+                assert get_current_context() is ctx
+
+        .. versionadded:: 5.0
+
+        :param cleanup: controls if the cleanup functions should be run or
+                        not.  The default is to run these functions.  In
+                        some situations the context only wants to be
+                        temporarily pushed in which case this can be disabled.
+                        Nested pushes automatically defer the cleanup.
+        """
+        if not cleanup:
+            self._depth += 1
+        try:
+            with self as rv:
+                yield rv
+        finally:
+            if not cleanup:
+                self._depth -= 1
+
+    @property
+    def meta(self):
+        """This is a dictionary which is shared with all the contexts
+        that are nested.  It exists so that click utilities can store some
+        state here if they need to.  It is however the responsibility of
+        that code to manage this dictionary well.
+
+        The keys are supposed to be unique dotted strings.  For instance
+        module paths are a good choice for it.  What is stored in there is
+        irrelevant for the operation of click.  However what is important is
+        that code that places data here adheres to the general semantics of
+        the system.
+
+        Example usage::
+
+            LANG_KEY = __name__ + '.lang'
+
+            def set_language(value):
+                ctx = get_current_context()
+                ctx.meta[LANG_KEY] = value
+
+            def get_language():
+                return get_current_context().meta.get(LANG_KEY, 'en_US')
+
+        .. versionadded:: 5.0
+        """
+        return self._meta
+
+    def make_formatter(self):
+        """Creates the formatter for the help and usage output."""
+        return HelpFormatter(width=self.terminal_width,
+                             max_width=self.max_content_width)
+
+    def call_on_close(self, f):
+        """This decorator remembers a function as callback that should be
+        executed when the context tears down.  This is most useful to bind
+        resource handling to the script execution.  For instance, file objects
+        opened by the :class:`File` type will register their close callbacks
+        here.
+
+        :param f: the function to execute on teardown.
+        """
+        self._close_callbacks.append(f)
+        return f
+
+    def close(self):
+        """Invokes all close callbacks."""
+        for cb in self._close_callbacks:
+            cb()
+        self._close_callbacks = []
+
+    @property
+    def command_path(self):
+        """The computed command path.  This is used for the ``usage``
+        information on the help page.  It's automatically created by
+        combining the info names of the chain of contexts to the root.
+        """
+        rv = ''
+        if self.info_name is not None:
+            rv = self.info_name
+        if self.parent is not None:
+            rv = self.parent.command_path + ' ' + rv
+        return rv.lstrip()
+
+    def find_root(self):
+        """Finds the outermost context."""
+        node = self
+        while node.parent is not None:
+            node = node.parent
+        return node
+
+    def find_object(self, object_type):
+        """Finds the closest object of a given type."""
+        node = self
+        while node is not None:
+            if isinstance(node.obj, object_type):
+                return node.obj
+            node = node.parent
+
+    def ensure_object(self, object_type):
+        """Like :meth:`find_object` but sets the innermost object to a
+        new instance of `object_type` if it does not exist.
+        """
+        rv = self.find_object(object_type)
+        if rv is None:
+            self.obj = rv = object_type()
+        return rv
+
+    def lookup_default(self, name):
+        """Looks up the default for a parameter name.  This by default
+        looks into the :attr:`default_map` if available.
+        """
+        if self.default_map is not None:
+            rv = self.default_map.get(name)
+            if callable(rv):
+                rv = rv()
+            return rv
+
+    def fail(self, message):
+        """Aborts the execution of the program with a specific error
+        message.
+
+        :param message: the error message to fail with.
+        """
+        raise UsageError(message, self)
+
+    def abort(self):
+        """Aborts the script."""
+        raise Abort()
+
+    def exit(self, code=0):
+        """Exits the application with a given exit code."""
+        raise Exit(code)
+
+    def get_usage(self):
+        """Helper method to get formatted usage string for the current
+        context and command.
+        """
+        return self.command.get_usage(self)
+
+    def get_help(self):
+        """Helper method to get formatted help page for the current
+        context and command.
+        """
+        return self.command.get_help(self)
+
+    def invoke(*args, **kwargs):
+        """Invokes a command callback in exactly the way it expects.  There
+        are two ways to invoke this method:
+
+        1.  the first argument can be a callback and all other arguments and
+            keyword arguments are forwarded directly to the function.
+        2.  the first argument is a click command object.  In that case all
+            arguments are forwarded as well but proper click parameters
+            (options and click arguments) must be keyword arguments and Click
+            will fill in defaults.
+
+        Note that before Click 3.2 keyword arguments were not properly filled
+        in against the intention of this code and no context was created.  For
+        more information about this change and why it was done in a bugfix
+        release see :ref:`upgrade-to-3.2`.
+        """
+        self, callback = args[:2]
+        ctx = self
+
+        # It's also possible to invoke another command which might or
+        # might not have a callback.  In that case we also fill
+        # in defaults and make a new context for this command.
+        if isinstance(callback, Command):
+            other_cmd = callback
+            callback = other_cmd.callback
+            ctx = Context(other_cmd, info_name=other_cmd.name, parent=self)
+            if callback is None:
+                raise TypeError('The given command does not have a '
+                                'callback that can be invoked.')
+
+            for param in other_cmd.params:
+                if param.name not in kwargs and param.expose_value:
+                    kwargs[param.name] = param.get_default(ctx)
+
+        args = args[2:]
+        with augment_usage_errors(self):
+            with ctx:
+                return callback(*args, **kwargs)
+
+    def forward(*args, **kwargs):
+        """Similar to :meth:`invoke` but fills in default keyword
+        arguments from the current context if the other command expects
+        it.  This cannot invoke callbacks directly, only other commands.
+        """
+        self, cmd = args[:2]
+
+        # It's also possible to invoke another command which might or
+        # might not have a callback.
+        if not isinstance(cmd, Command):
+            raise TypeError('Callback is not a command.')
+
+        for param in self.params:
+            if param not in kwargs:
+                kwargs[param] = self.params[param]
+
+        return self.invoke(cmd, **kwargs)
+
+
+class BaseCommand(object):
+    """The base command implements the minimal API contract of commands.
+    Most code will never use this as it does not implement a lot of useful
+    functionality but it can act as the direct subclass of alternative
+    parsing methods that do not depend on the Click parser.
+
+    For instance, this can be used to bridge Click and other systems like
+    argparse or docopt.
+
+    Because base commands do not implement a lot of the API that other
+    parts of Click take for granted, they are not supported for all
+    operations.  For instance, they cannot be used with the decorators
+    usually and they have no built-in callback system.
+
+    .. versionchanged:: 2.0
+       Added the `context_settings` parameter.
+
+    :param name: the name of the command to use unless a group overrides it.
+    :param context_settings: an optional dictionary with defaults that are
+                             passed to the context object.
+    """
+    #: the default for the :attr:`Context.allow_extra_args` flag.
+    allow_extra_args = False
+    #: the default for the :attr:`Context.allow_interspersed_args` flag.
+    allow_interspersed_args = True
+    #: the default for the :attr:`Context.ignore_unknown_options` flag.
+    ignore_unknown_options = False
+
+    def __init__(self, name, context_settings=None):
+        #: the name the command thinks it has.  Upon registering a command
+        #: on a :class:`Group` the group will default the command name
+        #: with this information.  You should instead use the
+        #: :class:`Context`\'s :attr:`~Context.info_name` attribute.
+        self.name = name
+        if context_settings is None:
+            context_settings = {}
+        #: an optional dictionary with defaults passed to the context.
+        self.context_settings = context_settings
+
+    def get_usage(self, ctx):
+        raise NotImplementedError('Base commands cannot get usage')
+
+    def get_help(self, ctx):
+        raise NotImplementedError('Base commands cannot get help')
+
+    def make_context(self, info_name, args, parent=None, **extra):
+        """This function when given an info name and arguments will kick
+        off the parsing and create a new :class:`Context`.  It does not
+        invoke the actual command callback though.
+
+        :param info_name: the info name for this invokation.  Generally this
+                          is the most descriptive name for the script or
+                          command.  For the toplevel script it's usually
+                          the name of the script, for commands below it it's
+                          the name of the script.
+        :param args: the arguments to parse as list of strings.
+        :param parent: the parent context if available.
+        :param extra: extra keyword arguments forwarded to the context
+                      constructor.
+        """
+        for key, value in iteritems(self.context_settings):
+            if key not in extra:
+                extra[key] = value
+        ctx = Context(self, info_name=info_name, parent=parent, **extra)
+        with ctx.scope(cleanup=False):
+            self.parse_args(ctx, args)
+        return ctx
+
+    def parse_args(self, ctx, args):
+        """Given a context and a list of arguments this creates the parser
+        and parses the arguments, then modifies the context as necessary.
+        This is automatically invoked by :meth:`make_context`.
+        """
+        raise NotImplementedError('Base commands do not know how to parse '
+                                  'arguments.')
+
+    def invoke(self, ctx):
+        """Given a context, this invokes the command.  The default
+        implementation is raising a not implemented error.
+        """
+        raise NotImplementedError('Base commands are not invokable by default')
+
+    def main(self, args=None, prog_name=None, complete_var=None,
+             standalone_mode=True, **extra):
+        """This is the way to invoke a script with all the bells and
+        whistles as a command line application.  This will always terminate
+        the application after a call.  If this is not wanted, ``SystemExit``
+        needs to be caught.
+
+        This method is also available by directly calling the instance of
+        a :class:`Command`.
+
+        .. versionadded:: 3.0
+           Added the `standalone_mode` flag to control the standalone mode.
+
+        :param args: the arguments that should be used for parsing.  If not
+                     provided, ``sys.argv[1:]`` is used.
+        :param prog_name: the program name that should be used.  By default
+                          the program name is constructed by taking the file
+                          name from ``sys.argv[0]``.
+        :param complete_var: the environment variable that controls the
+                             bash completion support.  The default is
+                             ``"_<prog_name>_COMPLETE"`` with prog_name in
+                             uppercase.
+        :param standalone_mode: the default behavior is to invoke the script
+                                in standalone mode.  Click will then
+                                handle exceptions and convert them into
+                                error messages and the function will never
+                                return but shut down the interpreter.  If
+                                this is set to `False` they will be
+                                propagated to the caller and the return
+                                value of this function is the return value
+                                of :meth:`invoke`.
+        :param extra: extra keyword arguments are forwarded to the context
+                      constructor.  See :class:`Context` for more information.
+        """
+        # If we are in Python 3, we will verify that the environment is
+        # sane at this point or reject further execution to avoid a
+        # broken script.
+        if not PY2:
+            _verify_python3_env()
+        else:
+            _check_for_unicode_literals()
+
+        if args is None:
+            args = get_os_args()
+        else:
+            args = list(args)
+
+        if prog_name is None:
+            prog_name = make_str(os.path.basename(
+                sys.argv and sys.argv[0] or __file__))
+
+        # Hook for the Bash completion.  This only activates if the Bash
+        # completion is actually enabled, otherwise this is quite a fast
+        # noop.
+        _bashcomplete(self, prog_name, complete_var)
+
+        try:
+            try:
+                with self.make_context(prog_name, args, **extra) as ctx:
+                    rv = self.invoke(ctx)
+                    if not standalone_mode:
+                        return rv
+                    # it's not safe to `ctx.exit(rv)` here!
+                    # note that `rv` may actually contain data like "1" which
+                    # has obvious effects
+                    # more subtle case: `rv=[None, None]` can come out of
+                    # chained commands which all returned `None` -- so it's not
+                    # even always obvious that `rv` indicates success/failure
+                    # by its truthiness/falsiness
+                    ctx.exit()
+            except (EOFError, KeyboardInterrupt):
+                echo(file=sys.stderr)
+                raise Abort()
+            except ClickException as e:
+                if not standalone_mode:
+                    raise
+                e.show()
+                sys.exit(e.exit_code)
+            except IOError as e:
+                if e.errno == errno.EPIPE:
+                    sys.stdout = PacifyFlushWrapper(sys.stdout)
+                    sys.stderr = PacifyFlushWrapper(sys.stderr)
+                    sys.exit(1)
+                else:
+                    raise
+        except Exit as e:
+            if standalone_mode:
+                sys.exit(e.exit_code)
+            else:
+                # in non-standalone mode, return the exit code
+                # note that this is only reached if `self.invoke` above raises
+                # an Exit explicitly -- thus bypassing the check there which
+                # would return its result
+                # the results of non-standalone execution may therefore be
+                # somewhat ambiguous: if there are codepaths which lead to
+                # `ctx.exit(1)` and to `return 1`, the caller won't be able to
+                # tell the difference between the two
+                return e.exit_code
+        except Abort:
+            if not standalone_mode:
+                raise
+            echo('Aborted!', file=sys.stderr)
+            sys.exit(1)
+
+    def __call__(self, *args, **kwargs):
+        """Alias for :meth:`main`."""
+        return self.main(*args, **kwargs)
+
+
+class Command(BaseCommand):
+    """Commands are the basic building block of command line interfaces in
+    Click.  A basic command handles command line parsing and might dispatch
+    more parsing to commands nested below it.
+
+    .. versionchanged:: 2.0
+       Added the `context_settings` parameter.
+
+    :param name: the name of the command to use unless a group overrides it.
+    :param context_settings: an optional dictionary with defaults that are
+                             passed to the context object.
+    :param callback: the callback to invoke.  This is optional.
+    :param params: the parameters to register with this command.  This can
+                   be either :class:`Option` or :class:`Argument` objects.
+    :param help: the help string to use for this command.
+    :param epilog: like the help string but it's printed at the end of the
+                   help page after everything else.
+    :param short_help: the short help to use for this command.  This is
+                       shown on the command listing of the parent command.
+    :param add_help_option: by default each command registers a ``--help``
+                            option.  This can be disabled by this parameter.
+    :param hidden: hide this command from help outputs.
+
+    :param deprecated: issues a message indicating that
+                             the command is deprecated.
+    """
+
+    def __init__(self, name, context_settings=None, callback=None,
+                 params=None, help=None, epilog=None, short_help=None,
+                 options_metavar='[OPTIONS]', add_help_option=True,
+                 hidden=False, deprecated=False):
+        BaseCommand.__init__(self, name, context_settings)
+        #: the callback to execute when the command fires.  This might be
+        #: `None` in which case nothing happens.
+        self.callback = callback
+        #: the list of parameters for this command in the order they
+        #: should show up in the help page and execute.  Eager parameters
+        #: will automatically be handled before non eager ones.
+        self.params = params or []
+        # if a form feed (page break) is found in the help text, truncate help
+        # text to the content preceding the first form feed
+        if help and '\f' in help:
+            help = help.split('\f', 1)[0]
+        self.help = help
+        self.epilog = epilog
+        self.options_metavar = options_metavar
+        self.short_help = short_help
+        self.add_help_option = add_help_option
+        self.hidden = hidden
+        self.deprecated = deprecated
+
+    def get_usage(self, ctx):
+        formatter = ctx.make_formatter()
+        self.format_usage(ctx, formatter)
+        return formatter.getvalue().rstrip('\n')
+
+    def get_params(self, ctx):
+        rv = self.params
+        help_option = self.get_help_option(ctx)
+        if help_option is not None:
+            rv = rv + [help_option]
+        return rv
+
+    def format_usage(self, ctx, formatter):
+        """Writes the usage line into the formatter."""
+        pieces = self.collect_usage_pieces(ctx)
+        formatter.write_usage(ctx.command_path, ' '.join(pieces))
+
+    def collect_usage_pieces(self, ctx):
+        """Returns all the pieces that go into the usage line and returns
+        it as a list of strings.
+        """
+        rv = [self.options_metavar]
+        for param in self.get_params(ctx):
+            rv.extend(param.get_usage_pieces(ctx))
+        return rv
+
+    def get_help_option_names(self, ctx):
+        """Returns the names for the help option."""
+        all_names = set(ctx.help_option_names)
+        for param in self.params:
+            all_names.difference_update(param.opts)
+            all_names.difference_update(param.secondary_opts)
+        return all_names
+
+    def get_help_option(self, ctx):
+        """Returns the help option object."""
+        help_options = self.get_help_option_names(ctx)
+        if not help_options or not self.add_help_option:
+            return
+
+        def show_help(ctx, param, value):
+            if value and not ctx.resilient_parsing:
+                echo(ctx.get_help(), color=ctx.color)
+                ctx.exit()
+        return Option(help_options, is_flag=True,
+                      is_eager=True, expose_value=False,
+                      callback=show_help,
+                      help='Show this message and exit.')
+
+    def make_parser(self, ctx):
+        """Creates the underlying option parser for this command."""
+        parser = OptionParser(ctx)
+        for param in self.get_params(ctx):
+            param.add_to_parser(parser, ctx)
+        return parser
+
+    def get_help(self, ctx):
+        """Formats the help into a string and returns it.  This creates a
+        formatter and will call into the following formatting methods:
+        """
+        formatter = ctx.make_formatter()
+        self.format_help(ctx, formatter)
+        return formatter.getvalue().rstrip('\n')
+
+    def get_short_help_str(self, limit=45):
+        """Gets short help for the command or makes it by shortening the long help string."""
+        return self.short_help or self.help and make_default_short_help(self.help, limit) or ''
+
+    def format_help(self, ctx, formatter):
+        """Writes the help into the formatter if it exists.
+
+        This calls into the following methods:
+
+        -   :meth:`format_usage`
+        -   :meth:`format_help_text`
+        -   :meth:`format_options`
+        -   :meth:`format_epilog`
+        """
+        self.format_usage(ctx, formatter)
+        self.format_help_text(ctx, formatter)
+        self.format_options(ctx, formatter)
+        self.format_epilog(ctx, formatter)
+
+    def format_help_text(self, ctx, formatter):
+        """Writes the help text to the formatter if it exists."""
+        if self.help:
+            formatter.write_paragraph()
+            with formatter.indentation():
+                help_text = self.help
+                if self.deprecated:
+                    help_text += DEPRECATED_HELP_NOTICE
+                formatter.write_text(help_text)
+        elif self.deprecated:
+            formatter.write_paragraph()
+            with formatter.indentation():
+                formatter.write_text(DEPRECATED_HELP_NOTICE)
+
+    def format_options(self, ctx, formatter):
+        """Writes all the options into the formatter if they exist."""
+        opts = []
+        for param in self.get_params(ctx):
+            rv = param.get_help_record(ctx)
+            if rv is not None:
+                opts.append(rv)
+
+        if opts:
+            with formatter.section('Options'):
+                formatter.write_dl(opts)
+
+    def format_epilog(self, ctx, formatter):
+        """Writes the epilog into the formatter if it exists."""
+        if self.epilog:
+            formatter.write_paragraph()
+            with formatter.indentation():
+                formatter.write_text(self.epilog)
+
+    def parse_args(self, ctx, args):
+        parser = self.make_parser(ctx)
+        opts, args, param_order = parser.parse_args(args=args)
+
+        for param in iter_params_for_processing(
+                param_order, self.get_params(ctx)):
+            value, args = param.handle_parse_result(ctx, opts, args)
+
+        if args and not ctx.allow_extra_args and not ctx.resilient_parsing:
+            ctx.fail('Got unexpected extra argument%s (%s)'
+                     % (len(args) != 1 and 's' or '',
+                        ' '.join(map(make_str, args))))
+
+        ctx.args = args
+        return args
+
+    def invoke(self, ctx):
+        """Given a context, this invokes the attached callback (if it exists)
+        in the right way.
+        """
+        _maybe_show_deprecated_notice(self)
+        if self.callback is not None:
+            return ctx.invoke(self.callback, **ctx.params)
+
+
+class MultiCommand(Command):
+    """A multi command is the basic implementation of a command that
+    dispatches to subcommands.  The most common version is the
+    :class:`Group`.
+
+    :param invoke_without_command: this controls how the multi command itself
+                                   is invoked.  By default it's only invoked
+                                   if a subcommand is provided.
+    :param no_args_is_help: this controls what happens if no arguments are
+                            provided.  This option is enabled by default if
+                            `invoke_without_command` is disabled or disabled
+                            if it's enabled.  If enabled this will add
+                            ``--help`` as argument if no arguments are
+                            passed.
+    :param subcommand_metavar: the string that is used in the documentation
+                               to indicate the subcommand place.
+    :param chain: if this is set to `True` chaining of multiple subcommands
+                  is enabled.  This restricts the form of commands in that
+                  they cannot have optional arguments but it allows
+                  multiple commands to be chained together.
+    :param result_callback: the result callback to attach to this multi
+                            command.
+    """
+    allow_extra_args = True
+    allow_interspersed_args = False
+
+    def __init__(self, name=None, invoke_without_command=False,
+                 no_args_is_help=None, subcommand_metavar=None,
+                 chain=False, result_callback=None, **attrs):
+        Command.__init__(self, name, **attrs)
+        if no_args_is_help is None:
+            no_args_is_help = not invoke_without_command
+        self.no_args_is_help = no_args_is_help
+        self.invoke_without_command = invoke_without_command
+        if subcommand_metavar is None:
+            if chain:
+                subcommand_metavar = SUBCOMMANDS_METAVAR
+            else:
+                subcommand_metavar = SUBCOMMAND_METAVAR
+        self.subcommand_metavar = subcommand_metavar
+        self.chain = chain
+        #: The result callback that is stored.  This can be set or
+        #: overridden with the :func:`resultcallback` decorator.
+        self.result_callback = result_callback
+
+        if self.chain:
+            for param in self.params:
+                if isinstance(param, Argument) and not param.required:
+                    raise RuntimeError('Multi commands in chain mode cannot '
+                                       'have optional arguments.')
+
+    def collect_usage_pieces(self, ctx):
+        rv = Command.collect_usage_pieces(self, ctx)
+        rv.append(self.subcommand_metavar)
+        return rv
+
+    def format_options(self, ctx, formatter):
+        Command.format_options(self, ctx, formatter)
+        self.format_commands(ctx, formatter)
+
+    def resultcallback(self, replace=False):
+        """Adds a result callback to the chain command.  By default if a
+        result callback is already registered this will chain them but
+        this can be disabled with the `replace` parameter.  The result
+        callback is invoked with the return value of the subcommand
+        (or the list of return values from all subcommands if chaining
+        is enabled) as well as the parameters as they would be passed
+        to the main callback.
+
+        Example::
+
+            @click.group()
+            @click.option('-i', '--input', default=23)
+            def cli(input):
+                return 42
+
+            @cli.resultcallback()
+            def process_result(result, input):
+                return result + input
+
+        .. versionadded:: 3.0
+
+        :param replace: if set to `True` an already existing result
+                        callback will be removed.
+        """
+        def decorator(f):
+            old_callback = self.result_callback
+            if old_callback is None or replace:
+                self.result_callback = f
+                return f
+            def function(__value, *args, **kwargs):
+                return f(old_callback(__value, *args, **kwargs),
+                         *args, **kwargs)
+            self.result_callback = rv = update_wrapper(function, f)
+            return rv
+        return decorator
+
+    def format_commands(self, ctx, formatter):
+        """Extra format methods for multi methods that adds all the commands
+        after the options.
+        """
+        commands = []
+        for subcommand in self.list_commands(ctx):
+            cmd = self.get_command(ctx, subcommand)
+            # What is this, the tool lied about a command.  Ignore it
+            if cmd is None:
+                continue
+            if cmd.hidden:
+                continue
+
+            commands.append((subcommand, cmd))
+
+        # allow for 3 times the default spacing
+        if len(commands):
+            limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands)
+
+            rows = []
+            for subcommand, cmd in commands:
+                help = cmd.get_short_help_str(limit)
+                rows.append((subcommand, help))
+
+            if rows:
+                with formatter.section('Commands'):
+                    formatter.write_dl(rows)
+
+    def parse_args(self, ctx, args):
+        if not args and self.no_args_is_help and not ctx.resilient_parsing:
+            echo(ctx.get_help(), color=ctx.color)
+            ctx.exit()
+
+        rest = Command.parse_args(self, ctx, args)
+        if self.chain:
+            ctx.protected_args = rest
+            ctx.args = []
+        elif rest:
+            ctx.protected_args, ctx.args = rest[:1], rest[1:]
+
+        return ctx.args
+
+    def invoke(self, ctx):
+        def _process_result(value):
+            if self.result_callback is not None:
+                value = ctx.invoke(self.result_callback, value,
+                                   **ctx.params)
+            return value
+
+        if not ctx.protected_args:
+            # If we are invoked without command the chain flag controls
+            # how this happens.  If we are not in chain mode, the return
+            # value here is the return value of the command.
+            # If however we are in chain mode, the return value is the
+            # return value of the result processor invoked with an empty
+            # list (which means that no subcommand actually was executed).
+            if self.invoke_without_command:
+                if not self.chain:
+                    return Command.invoke(self, ctx)
+                with ctx:
+                    Command.invoke(self, ctx)
+                    return _process_result([])
+            ctx.fail('Missing command.')
+
+        # Fetch args back out
+        args = ctx.protected_args + ctx.args
+        ctx.args = []
+        ctx.protected_args = []
+
+        # If we're not in chain mode, we only allow the invocation of a
+        # single command but we also inform the current context about the
+        # name of the command to invoke.
+        if not self.chain:
+            # Make sure the context is entered so we do not clean up
+            # resources until the result processor has worked.
+            with ctx:
+                cmd_name, cmd, args = self.resolve_command(ctx, args)
+                ctx.invoked_subcommand = cmd_name
+                Command.invoke(self, ctx)
+                sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
+                with sub_ctx:
+                    return _process_result(sub_ctx.command.invoke(sub_ctx))
+
+        # In chain mode we create the contexts step by step, but after the
+        # base command has been invoked.  Because at that point we do not
+        # know the subcommands yet, the invoked subcommand attribute is
+        # set to ``*`` to inform the command that subcommands are executed
+        # but nothing else.
+        with ctx:
+            ctx.invoked_subcommand = args and '*' or None
+            Command.invoke(self, ctx)
+
+            # Otherwise we make every single context and invoke them in a
+            # chain.  In that case the return value to the result processor
+            # is the list of all invoked subcommand's results.
+            contexts = []
+            while args:
+                cmd_name, cmd, args = self.resolve_command(ctx, args)
+                sub_ctx = cmd.make_context(cmd_name, args, parent=ctx,
+                                           allow_extra_args=True,
+                                           allow_interspersed_args=False)
+                contexts.append(sub_ctx)
+                args, sub_ctx.args = sub_ctx.args, []
+
+            rv = []
+            for sub_ctx in contexts:
+                with sub_ctx:
+                    rv.append(sub_ctx.command.invoke(sub_ctx))
+            return _process_result(rv)
+
+    def resolve_command(self, ctx, args):
+        cmd_name = make_str(args[0])
+        original_cmd_name = cmd_name
+
+        # Get the command
+        cmd = self.get_command(ctx, cmd_name)
+
+        # If we can't find the command but there is a normalization
+        # function available, we try with that one.
+        if cmd is None and ctx.token_normalize_func is not None:
+            cmd_name = ctx.token_normalize_func(cmd_name)
+            cmd = self.get_command(ctx, cmd_name)
+
+        # If we don't find the command we want to show an error message
+        # to the user that it was not provided.  However, there is
+        # something else we should do: if the first argument looks like
+        # an option we want to kick off parsing again for arguments to
+        # resolve things like --help which now should go to the main
+        # place.
+        if cmd is None and not ctx.resilient_parsing:
+            if split_opt(cmd_name)[0]:
+                self.parse_args(ctx, ctx.args)
+            ctx.fail('No such command "%s".' % original_cmd_name)
+
+        return cmd_name, cmd, args[1:]
+
+    def get_command(self, ctx, cmd_name):
+        """Given a context and a command name, this returns a
+        :class:`Command` object if it exists or returns `None`.
+        """
+        raise NotImplementedError()
+
+    def list_commands(self, ctx):
+        """Returns a list of subcommand names in the order they should
+        appear.
+        """
+        return []
+
+
+class Group(MultiCommand):
+    """A group allows a command to have subcommands attached.  This is the
+    most common way to implement nesting in Click.
+
+    :param commands: a dictionary of commands.
+    """
+
+    def __init__(self, name=None, commands=None, **attrs):
+        MultiCommand.__init__(self, name, **attrs)
+        #: the registered subcommands by their exported names.
+        self.commands = commands or {}
+
+    def add_command(self, cmd, name=None):
+        """Registers another :class:`Command` with this group.  If the name
+        is not provided, the name of the command is used.
+        """
+        name = name or cmd.name
+        if name is None:
+            raise TypeError('Command has no name.')
+        _check_multicommand(self, name, cmd, register=True)
+        self.commands[name] = cmd
+
+    def command(self, *args, **kwargs):
+        """A shortcut decorator for declaring and attaching a command to
+        the group.  This takes the same arguments as :func:`command` but
+        immediately registers the created command with this instance by
+        calling into :meth:`add_command`.
+        """
+        def decorator(f):
+            cmd = command(*args, **kwargs)(f)
+            self.add_command(cmd)
+            return cmd
+        return decorator
+
+    def group(self, *args, **kwargs):
+        """A shortcut decorator for declaring and attaching a group to
+        the group.  This takes the same arguments as :func:`group` but
+        immediately registers the created command with this instance by
+        calling into :meth:`add_command`.
+        """
+        def decorator(f):
+            cmd = group(*args, **kwargs)(f)
+            self.add_command(cmd)
+            return cmd
+        return decorator
+
+    def get_command(self, ctx, cmd_name):
+        return self.commands.get(cmd_name)
+
+    def list_commands(self, ctx):
+        return sorted(self.commands)
+
+
+class CommandCollection(MultiCommand):
+    """A command collection is a multi command that merges multiple multi
+    commands together into one.  This is a straightforward implementation
+    that accepts a list of different multi commands as sources and
+    provides all the commands for each of them.
+    """
+
+    def __init__(self, name=None, sources=None, **attrs):
+        MultiCommand.__init__(self, name, **attrs)
+        #: The list of registered multi commands.
+        self.sources = sources or []
+
+    def add_source(self, multi_cmd):
+        """Adds a new multi command to the chain dispatcher."""
+        self.sources.append(multi_cmd)
+
+    def get_command(self, ctx, cmd_name):
+        for source in self.sources:
+            rv = source.get_command(ctx, cmd_name)
+            if rv is not None:
+                if self.chain:
+                    _check_multicommand(self, cmd_name, rv)
+                return rv
+
+    def list_commands(self, ctx):
+        rv = set()
+        for source in self.sources:
+            rv.update(source.list_commands(ctx))
+        return sorted(rv)
+
+
+class Parameter(object):
+    r"""A parameter to a command comes in two versions: they are either
+    :class:`Option`\s or :class:`Argument`\s.  Other subclasses are currently
+    not supported by design as some of the internals for parsing are
+    intentionally not finalized.
+
+    Some settings are supported by both options and arguments.
+
+    .. versionchanged:: 2.0
+       Changed signature for parameter callback to also be passed the
+       parameter.  In Click 2.0, the old callback format will still work,
+       but it will raise a warning to give you change to migrate the
+       code easier.
+
+    :param param_decls: the parameter declarations for this option or
+                        argument.  This is a list of flags or argument
+                        names.
+    :param type: the type that should be used.  Either a :class:`ParamType`
+                 or a Python type.  The later is converted into the former
+                 automatically if supported.
+    :param required: controls if this is optional or not.
+    :param default: the default value if omitted.  This can also be a callable,
+                    in which case it's invoked when the default is needed
+                    without any arguments.
+    :param callback: a callback that should be executed after the parameter
+                     was matched.  This is called as ``fn(ctx, param,
+                     value)`` and needs to return the value.  Before Click
+                     2.0, the signature was ``(ctx, value)``.
+    :param nargs: the number of arguments to match.  If not ``1`` the return
+                  value is a tuple instead of single value.  The default for
+                  nargs is ``1`` (except if the type is a tuple, then it's
+                  the arity of the tuple).
+    :param metavar: how the value is represented in the help page.
+    :param expose_value: if this is `True` then the value is passed onwards
+                         to the command callback and stored on the context,
+                         otherwise it's skipped.
+    :param is_eager: eager values are processed before non eager ones.  This
+                     should not be set for arguments or it will inverse the
+                     order of processing.
+    :param envvar: a string or list of strings that are environment variables
+                   that should be checked.
+    """
+    param_type_name = 'parameter'
+
+    def __init__(self, param_decls=None, type=None, required=False,
+                 default=None, callback=None, nargs=None, metavar=None,
+                 expose_value=True, is_eager=False, envvar=None,
+                 autocompletion=None):
+        self.name, self.opts, self.secondary_opts = \
+            self._parse_decls(param_decls or (), expose_value)
+
+        self.type = convert_type(type, default)
+
+        # Default nargs to what the type tells us if we have that
+        # information available.
+        if nargs is None:
+            if self.type.is_composite:
+                nargs = self.type.arity
+            else:
+                nargs = 1
+
+        self.required = required
+        self.callback = callback
+        self.nargs = nargs
+        self.multiple = False
+        self.expose_value = expose_value
+        self.default = default
+        self.is_eager = is_eager
+        self.metavar = metavar
+        self.envvar = envvar
+        self.autocompletion = autocompletion
+
+    @property
+    def human_readable_name(self):
+        """Returns the human readable name of this parameter.  This is the
+        same as the name for options, but the metavar for arguments.
+        """
+        return self.name
+
+    def make_metavar(self):
+        if self.metavar is not None:
+            return self.metavar
+        metavar = self.type.get_metavar(self)
+        if metavar is None:
+            metavar = self.type.name.upper()
+        if self.nargs != 1:
+            metavar += '...'
+        return metavar
+
+    def get_default(self, ctx):
+        """Given a context variable this calculates the default value."""
+        # Otherwise go with the regular default.
+        if callable(self.default):
+            rv = self.default()
+        else:
+            rv = self.default
+        return self.type_cast_value(ctx, rv)
+
+    def add_to_parser(self, parser, ctx):
+        pass
+
+    def consume_value(self, ctx, opts):
+        value = opts.get(self.name)
+        if value is None:
+            value = self.value_from_envvar(ctx)
+        if value is None:
+            value = ctx.lookup_default(self.name)
+        return value
+
+    def type_cast_value(self, ctx, value):
+        """Given a value this runs it properly through the type system.
+        This automatically handles things like `nargs` and `multiple` as
+        well as composite types.
+        """
+        if self.type.is_composite:
+            if self.nargs <= 1:
+                raise TypeError('Attempted to invoke composite type '
+                                'but nargs has been set to %s.  This is '
+                                'not supported; nargs needs to be set to '
+                                'a fixed value > 1.' % self.nargs)
+            if self.multiple:
+                return tuple(self.type(x or (), self, ctx) for x in value or ())
+            return self.type(value or (), self, ctx)
+
+        def _convert(value, level):
+            if level == 0:
+                return self.type(value, self, ctx)
+            return tuple(_convert(x, level - 1) for x in value or ())
+        return _convert(value, (self.nargs != 1) + bool(self.multiple))
+
+    def process_value(self, ctx, value):
+        """Given a value and context this runs the logic to convert the
+        value as necessary.
+        """
+        # If the value we were given is None we do nothing.  This way
+        # code that calls this can easily figure out if something was
+        # not provided.  Otherwise it would be converted into an empty
+        # tuple for multiple invocations which is inconvenient.
+        if value is not None:
+            return self.type_cast_value(ctx, value)
+
+    def value_is_missing(self, value):
+        if value is None:
+            return True
+        if (self.nargs != 1 or self.multiple) and value == ():
+            return True
+        return False
+
+    def full_process_value(self, ctx, value):
+        value = self.process_value(ctx, value)
+
+        if value is None and not ctx.resilient_parsing:
+            value = self.get_default(ctx)
+
+        if self.required and self.value_is_missing(value):
+            raise MissingParameter(ctx=ctx, param=self)
+
+        return value
+
+    def resolve_envvar_value(self, ctx):
+        if self.envvar is None:
+            return
+        if isinstance(self.envvar, (tuple, list)):
+            for envvar in self.envvar:
+                rv = os.environ.get(envvar)
+                if rv is not None:
+                    return rv
+        else:
+            return os.environ.get(self.envvar)
+
+    def value_from_envvar(self, ctx):
+        rv = self.resolve_envvar_value(ctx)
+        if rv is not None and self.nargs != 1:
+            rv = self.type.split_envvar_value(rv)
+        return rv
+
+    def handle_parse_result(self, ctx, opts, args):
+        with augment_usage_errors(ctx, param=self):
+            value = self.consume_value(ctx, opts)
+            try:
+                value = self.full_process_value(ctx, value)
+            except Exception:
+                if not ctx.resilient_parsing:
+                    raise
+                value = None
+            if self.callback is not None:
+                try:
+                    value = invoke_param_callback(
+                        self.callback, ctx, self, value)
+                except Exception:
+                    if not ctx.resilient_parsing:
+                        raise
+
+        if self.expose_value:
+            ctx.params[self.name] = value
+        return value, args
+
+    def get_help_record(self, ctx):
+        pass
+
+    def get_usage_pieces(self, ctx):
+        return []
+
+    def get_error_hint(self, ctx):
+        """Get a stringified version of the param for use in error messages to
+        indicate which param caused the error.
+        """
+        hint_list = self.opts or [self.human_readable_name]
+        return ' / '.join('"%s"' % x for x in hint_list)
+
+
+class Option(Parameter):
+    """Options are usually optional values on the command line and
+    have some extra features that arguments don't have.
+
+    All other parameters are passed onwards to the parameter constructor.
+
+    :param show_default: controls if the default value should be shown on the
+                         help page. Normally, defaults are not shown. If this
+                         value is a string, it shows the string instead of the
+                         value. This is particularly useful for dynamic options.
+    :param show_envvar: controls if an environment variable should be shown on
+                        the help page.  Normally, environment variables
+                        are not shown.
+    :param prompt: if set to `True` or a non empty string then the user will be
+                   prompted for input.  If set to `True` the prompt will be the
+                   option name capitalized.
+    :param confirmation_prompt: if set then the value will need to be confirmed
+                                if it was prompted for.
+    :param hide_input: if this is `True` then the input on the prompt will be
+                       hidden from the user.  This is useful for password
+                       input.
+    :param is_flag: forces this option to act as a flag.  The default is
+                    auto detection.
+    :param flag_value: which value should be used for this flag if it's
+                       enabled.  This is set to a boolean automatically if
+                       the option string contains a slash to mark two options.
+    :param multiple: if this is set to `True` then the argument is accepted
+                     multiple times and recorded.  This is similar to ``nargs``
+                     in how it works but supports arbitrary number of
+                     arguments.
+    :param count: this flag makes an option increment an integer.
+    :param allow_from_autoenv: if this is enabled then the value of this
+                               parameter will be pulled from an environment
+                               variable in case a prefix is defined on the
+                               context.
+    :param help: the help string.
+    :param hidden: hide this option from help outputs.
+    """
+    param_type_name = 'option'
+
+    def __init__(self, param_decls=None, show_default=False,
+                 prompt=False, confirmation_prompt=False,
+                 hide_input=False, is_flag=None, flag_value=None,
+                 multiple=False, count=False, allow_from_autoenv=True,
+                 type=None, help=None, hidden=False, show_choices=True,
+                 show_envvar=False, **attrs):
+        default_is_missing = attrs.get('default', _missing) is _missing
+        Parameter.__init__(self, param_decls, type=type, **attrs)
+
+        if prompt is True:
+            prompt_text = self.name.replace('_', ' ').capitalize()
+        elif prompt is False:
+            prompt_text = None
+        else:
+            prompt_text = prompt
+        self.prompt = prompt_text
+        self.confirmation_prompt = confirmation_prompt
+        self.hide_input = hide_input
+        self.hidden = hidden
+
+        # Flags
+        if is_flag is None:
+            if flag_value is not None:
+                is_flag = True
+            else:
+                is_flag = bool(self.secondary_opts)
+        if is_flag and default_is_missing:
+            self.default = False
+        if flag_value is None:
+            flag_value = not self.default
+        self.is_flag = is_flag
+        self.flag_value = flag_value
+        if self.is_flag and isinstance(self.flag_value, bool) \
+           and type is None:
+            self.type = BOOL
+            self.is_bool_flag = True
+        else:
+            self.is_bool_flag = False
+
+        # Counting
+        self.count = count
+        if count:
+            if type is None:
+                self.type = IntRange(min=0)
+            if default_is_missing:
+                self.default = 0
+
+        self.multiple = multiple
+        self.allow_from_autoenv = allow_from_autoenv
+        self.help = help
+        self.show_default = show_default
+        self.show_choices = show_choices
+        self.show_envvar = show_envvar
+
+        # Sanity check for stuff we don't support
+        if __debug__:
+            if self.nargs < 0:
+                raise TypeError('Options cannot have nargs < 0')
+            if self.prompt and self.is_flag and not self.is_bool_flag:
+                raise TypeError('Cannot prompt for flags that are not bools.')
+            if not self.is_bool_flag and self.secondary_opts:
+                raise TypeError('Got secondary option for non boolean flag.')
+            if self.is_bool_flag and self.hide_input \
+               and self.prompt is not None:
+                raise TypeError('Hidden input does not work with boolean '
+                                'flag prompts.')
+            if self.count:
+                if self.multiple:
+                    raise TypeError('Options cannot be multiple and count '
+                                    'at the same time.')
+                elif self.is_flag:
+                    raise TypeError('Options cannot be count and flags at '
+                                    'the same time.')
+
+    def _parse_decls(self, decls, expose_value):
+        opts = []
+        secondary_opts = []
+        name = None
+        possible_names = []
+
+        for decl in decls:
+            if isidentifier(decl):
+                if name is not None:
+                    raise TypeError('Name defined twice')
+                name = decl
+            else:
+                split_char = decl[:1] == '/' and ';' or '/'
+                if split_char in decl:
+                    first, second = decl.split(split_char, 1)
+                    first = first.rstrip()
+                    if first:
+                        possible_names.append(split_opt(first))
+                        opts.append(first)
+                    second = second.lstrip()
+                    if second:
+                        secondary_opts.append(second.lstrip())
+                else:
+                    possible_names.append(split_opt(decl))
+                    opts.append(decl)
+
+        if name is None and possible_names:
+            possible_names.sort(key=lambda x: -len(x[0]))  # group long options first
+            name = possible_names[0][1].replace('-', '_').lower()
+            if not isidentifier(name):
+                name = None
+
+        if name is None:
+            if not expose_value:
+                return None, opts, secondary_opts
+            raise TypeError('Could not determine name for option')
+
+        if not opts and not secondary_opts:
+            raise TypeError('No options defined but a name was passed (%s). '
+                            'Did you mean to declare an argument instead '
+                            'of an option?' % name)
+
+        return name, opts, secondary_opts
+
+    def add_to_parser(self, parser, ctx):
+        kwargs = {
+            'dest': self.name,
+            'nargs': self.nargs,
+            'obj': self,
+        }
+
+        if self.multiple:
+            action = 'append'
+        elif self.count:
+            action = 'count'
+        else:
+            action = 'store'
+
+        if self.is_flag:
+            kwargs.pop('nargs', None)
+            if self.is_bool_flag and self.secondary_opts:
+                parser.add_option(self.opts, action=action + '_const',
+                                  const=True, **kwargs)
+                parser.add_option(self.secondary_opts, action=action +
+                                  '_const', const=False, **kwargs)
+            else:
+                parser.add_option(self.opts, action=action + '_const',
+                                  const=self.flag_value,
+                                  **kwargs)
+        else:
+            kwargs['action'] = action
+            parser.add_option(self.opts, **kwargs)
+
+    def get_help_record(self, ctx):
+        if self.hidden:
+            return
+        any_prefix_is_slash = []
+
+        def _write_opts(opts):
+            rv, any_slashes = join_options(opts)
+            if any_slashes:
+                any_prefix_is_slash[:] = [True]
+            if not self.is_flag and not self.count:
+                rv += ' ' + self.make_metavar()
+            return rv
+
+        rv = [_write_opts(self.opts)]
+        if self.secondary_opts:
+            rv.append(_write_opts(self.secondary_opts))
+
+        help = self.help or ''
+        extra = []
+        if self.show_envvar:
+            envvar = self.envvar
+            if envvar is None:
+                if self.allow_from_autoenv and \
+                    ctx.auto_envvar_prefix is not None:
+                    envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper())
+            if envvar is not None:
+              extra.append('env var: %s' % (
+                           ', '.join('%s' % d for d in envvar)
+                           if isinstance(envvar, (list, tuple))
+                           else envvar, ))
+        if self.default is not None and self.show_default:
+            if isinstance(self.show_default, string_types):
+                default_string = '({})'.format(self.show_default)
+            elif isinstance(self.default, (list, tuple)):
+                default_string = ', '.join('%s' % d for d in self.default)
+            elif inspect.isfunction(self.default):
+                default_string = "(dynamic)"
+            else:
+                default_string = self.default
+            extra.append('default: {}'.format(default_string))
+
+        if self.required:
+            extra.append('required')
+        if extra:
+            help = '%s[%s]' % (help and help + '  ' or '', '; '.join(extra))
+
+        return ((any_prefix_is_slash and '; ' or ' / ').join(rv), help)
+
+    def get_default(self, ctx):
+        # If we're a non boolean flag out default is more complex because
+        # we need to look at all flags in the same group to figure out
+        # if we're the the default one in which case we return the flag
+        # value as default.
+        if self.is_flag and not self.is_bool_flag:
+            for param in ctx.command.params:
+                if param.name == self.name and param.default:
+                    return param.flag_value
+            return None
+        return Parameter.get_default(self, ctx)
+
+    def prompt_for_value(self, ctx):
+        """This is an alternative flow that can be activated in the full
+        value processing if a value does not exist.  It will prompt the
+        user until a valid value exists and then returns the processed
+        value as result.
+        """
+        # Calculate the default before prompting anything to be stable.
+        default = self.get_default(ctx)
+
+        # If this is a prompt for a flag we need to handle this
+        # differently.
+        if self.is_bool_flag:
+            return confirm(self.prompt, default)
+
+        return prompt(self.prompt, default=default, type=self.type,
+                      hide_input=self.hide_input, show_choices=self.show_choices,
+                      confirmation_prompt=self.confirmation_prompt,
+                      value_proc=lambda x: self.process_value(ctx, x))
+
+    def resolve_envvar_value(self, ctx):
+        rv = Parameter.resolve_envvar_value(self, ctx)
+        if rv is not None:
+            return rv
+        if self.allow_from_autoenv and \
+           ctx.auto_envvar_prefix is not None:
+            envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper())
+            return os.environ.get(envvar)
+
+    def value_from_envvar(self, ctx):
+        rv = self.resolve_envvar_value(ctx)
+        if rv is None:
+            return None
+        value_depth = (self.nargs != 1) + bool(self.multiple)
+        if value_depth > 0 and rv is not None:
+            rv = self.type.split_envvar_value(rv)
+            if self.multiple and self.nargs != 1:
+                rv = batch(rv, self.nargs)
+        return rv
+
+    def full_process_value(self, ctx, value):
+        if value is None and self.prompt is not None \
+           and not ctx.resilient_parsing:
+            return self.prompt_for_value(ctx)
+        return Parameter.full_process_value(self, ctx, value)
+
+
+class Argument(Parameter):
+    """Arguments are positional parameters to a command.  They generally
+    provide fewer features than options but can have infinite ``nargs``
+    and are required by default.
+
+    All parameters are passed onwards to the parameter constructor.
+    """
+    param_type_name = 'argument'
+
+    def __init__(self, param_decls, required=None, **attrs):
+        if required is None:
+            if attrs.get('default') is not None:
+                required = False
+            else:
+                required = attrs.get('nargs', 1) > 0
+        Parameter.__init__(self, param_decls, required=required, **attrs)
+        if self.default is not None and self.nargs < 0:
+            raise TypeError('nargs=-1 in combination with a default value '
+                            'is not supported.')
+
+    @property
+    def human_readable_name(self):
+        if self.metavar is not None:
+            return self.metavar
+        return self.name.upper()
+
+    def make_metavar(self):
+        if self.metavar is not None:
+            return self.metavar
+        var = self.type.get_metavar(self)
+        if not var:
+            var = self.name.upper()
+        if not self.required:
+            var = '[%s]' % var
+        if self.nargs != 1:
+            var += '...'
+        return var
+
+    def _parse_decls(self, decls, expose_value):
+        if not decls:
+            if not expose_value:
+                return None, [], []
+            raise TypeError('Could not determine name for argument')
+        if len(decls) == 1:
+            name = arg = decls[0]
+            name = name.replace('-', '_').lower()
+        else:
+            raise TypeError('Arguments take exactly one '
+                            'parameter declaration, got %d' % len(decls))
+        return name, [arg], []
+
+    def get_usage_pieces(self, ctx):
+        return [self.make_metavar()]
+
+    def get_error_hint(self, ctx):
+        return '"%s"' % self.make_metavar()
+
+    def add_to_parser(self, parser, ctx):
+        parser.add_argument(dest=self.name, nargs=self.nargs,
+                            obj=self)
+
+
+# Circular dependency between decorators and core
+from .decorators import command, group
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/decorators.py
@@ -0,0 +1,311 @@
+import sys
+import inspect
+
+from functools import update_wrapper
+
+from ._compat import iteritems
+from ._unicodefun import _check_for_unicode_literals
+from .utils import echo
+from .globals import get_current_context
+
+
+def pass_context(f):
+    """Marks a callback as wanting to receive the current context
+    object as first argument.
+    """
+    def new_func(*args, **kwargs):
+        return f(get_current_context(), *args, **kwargs)
+    return update_wrapper(new_func, f)
+
+
+def pass_obj(f):
+    """Similar to :func:`pass_context`, but only pass the object on the
+    context onwards (:attr:`Context.obj`).  This is useful if that object
+    represents the state of a nested system.
+    """
+    def new_func(*args, **kwargs):
+        return f(get_current_context().obj, *args, **kwargs)
+    return update_wrapper(new_func, f)
+
+
+def make_pass_decorator(object_type, ensure=False):
+    """Given an object type this creates a decorator that will work
+    similar to :func:`pass_obj` but instead of passing the object of the
+    current context, it will find the innermost context of type
+    :func:`object_type`.
+
+    This generates a decorator that works roughly like this::
+
+        from functools import update_wrapper
+
+        def decorator(f):
+            @pass_context
+            def new_func(ctx, *args, **kwargs):
+                obj = ctx.find_object(object_type)
+                return ctx.invoke(f, obj, *args, **kwargs)
+            return update_wrapper(new_func, f)
+        return decorator
+
+    :param object_type: the type of the object to pass.
+    :param ensure: if set to `True`, a new object will be created and
+                   remembered on the context if it's not there yet.
+    """
+    def decorator(f):
+        def new_func(*args, **kwargs):
+            ctx = get_current_context()
+            if ensure:
+                obj = ctx.ensure_object(object_type)
+            else:
+                obj = ctx.find_object(object_type)
+            if obj is None:
+                raise RuntimeError('Managed to invoke callback without a '
+                                   'context object of type %r existing'
+                                   % object_type.__name__)
+            return ctx.invoke(f, obj, *args, **kwargs)
+        return update_wrapper(new_func, f)
+    return decorator
+
+
+def _make_command(f, name, attrs, cls):
+    if isinstance(f, Command):
+        raise TypeError('Attempted to convert a callback into a '
+                        'command twice.')
+    try:
+        params = f.__click_params__
+        params.reverse()
+        del f.__click_params__
+    except AttributeError:
+        params = []
+    help = attrs.get('help')
+    if help is None:
+        help = inspect.getdoc(f)
+        if isinstance(help, bytes):
+            help = help.decode('utf-8')
+    else:
+        help = inspect.cleandoc(help)
+    attrs['help'] = help
+    _check_for_unicode_literals()
+    return cls(name=name or f.__name__.lower().replace('_', '-'),
+               callback=f, params=params, **attrs)
+
+
+def command(name=None, cls=None, **attrs):
+    r"""Creates a new :class:`Command` and uses the decorated function as
+    callback.  This will also automatically attach all decorated
+    :func:`option`\s and :func:`argument`\s as parameters to the command.
+
+    The name of the command defaults to the name of the function.  If you
+    want to change that, you can pass the intended name as the first
+    argument.
+
+    All keyword arguments are forwarded to the underlying command class.
+
+    Once decorated the function turns into a :class:`Command` instance
+    that can be invoked as a command line utility or be attached to a
+    command :class:`Group`.
+
+    :param name: the name of the command.  This defaults to the function
+                 name with underscores replaced by dashes.
+    :param cls: the command class to instantiate.  This defaults to
+                :class:`Command`.
+    """
+    if cls is None:
+        cls = Command
+    def decorator(f):
+        cmd = _make_command(f, name, attrs, cls)
+        cmd.__doc__ = f.__doc__
+        return cmd
+    return decorator
+
+
+def group(name=None, **attrs):
+    """Creates a new :class:`Group` with a function as callback.  This
+    works otherwise the same as :func:`command` just that the `cls`
+    parameter is set to :class:`Group`.
+    """
+    attrs.setdefault('cls', Group)
+    return command(name, **attrs)
+
+
+def _param_memo(f, param):
+    if isinstance(f, Command):
+        f.params.append(param)
+    else:
+        if not hasattr(f, '__click_params__'):
+            f.__click_params__ = []
+        f.__click_params__.append(param)
+
+
+def argument(*param_decls, **attrs):
+    """Attaches an argument to the command.  All positional arguments are
+    passed as parameter declarations to :class:`Argument`; all keyword
+    arguments are forwarded unchanged (except ``cls``).
+    This is equivalent to creating an :class:`Argument` instance manually
+    and attaching it to the :attr:`Command.params` list.
+
+    :param cls: the argument class to instantiate.  This defaults to
+                :class:`Argument`.
+    """
+    def decorator(f):
+        ArgumentClass = attrs.pop('cls', Argument)
+        _param_memo(f, ArgumentClass(param_decls, **attrs))
+        return f
+    return decorator
+
+
+def option(*param_decls, **attrs):
+    """Attaches an option to the command.  All positional arguments are
+    passed as parameter declarations to :class:`Option`; all keyword
+    arguments are forwarded unchanged (except ``cls``).
+    This is equivalent to creating an :class:`Option` instance manually
+    and attaching it to the :attr:`Command.params` list.
+
+    :param cls: the option class to instantiate.  This defaults to
+                :class:`Option`.
+    """
+    def decorator(f):
+        # Issue 926, copy attrs, so pre-defined options can re-use the same cls=
+        option_attrs = attrs.copy()
+
+        if 'help' in option_attrs:
+            option_attrs['help'] = inspect.cleandoc(option_attrs['help'])
+        OptionClass = option_attrs.pop('cls', Option)
+        _param_memo(f, OptionClass(param_decls, **option_attrs))
+        return f
+    return decorator
+
+
+def confirmation_option(*param_decls, **attrs):
+    """Shortcut for confirmation prompts that can be ignored by passing
+    ``--yes`` as parameter.
+
+    This is equivalent to decorating a function with :func:`option` with
+    the following parameters::
+
+        def callback(ctx, param, value):
+            if not value:
+                ctx.abort()
+
+        @click.command()
+        @click.option('--yes', is_flag=True, callback=callback,
+                      expose_value=False, prompt='Do you want to continue?')
+        def dropdb():
+            pass
+    """
+    def decorator(f):
+        def callback(ctx, param, value):
+            if not value:
+                ctx.abort()
+        attrs.setdefault('is_flag', True)
+        attrs.setdefault('callback', callback)
+        attrs.setdefault('expose_value', False)
+        attrs.setdefault('prompt', 'Do you want to continue?')
+        attrs.setdefault('help', 'Confirm the action without prompting.')
+        return option(*(param_decls or ('--yes',)), **attrs)(f)
+    return decorator
+
+
+def password_option(*param_decls, **attrs):
+    """Shortcut for password prompts.
+
+    This is equivalent to decorating a function with :func:`option` with
+    the following parameters::
+
+        @click.command()
+        @click.option('--password', prompt=True, confirmation_prompt=True,
+                      hide_input=True)
+        def changeadmin(password):
+            pass
+    """
+    def decorator(f):
+        attrs.setdefault('prompt', True)
+        attrs.setdefault('confirmation_prompt', True)
+        attrs.setdefault('hide_input', True)
+        return option(*(param_decls or ('--password',)), **attrs)(f)
+    return decorator
+
+
+def version_option(version=None, *param_decls, **attrs):
+    """Adds a ``--version`` option which immediately ends the program
+    printing out the version number.  This is implemented as an eager
+    option that prints the version and exits the program in the callback.
+
+    :param version: the version number to show.  If not provided Click
+                    attempts an auto discovery via setuptools.
+    :param prog_name: the name of the program (defaults to autodetection)
+    :param message: custom message to show instead of the default
+                    (``'%(prog)s, version %(version)s'``)
+    :param others: everything else is forwarded to :func:`option`.
+    """
+    if version is None:
+        if hasattr(sys, '_getframe'):
+            module = sys._getframe(1).f_globals.get('__name__')
+        else:
+            module = ''
+
+    def decorator(f):
+        prog_name = attrs.pop('prog_name', None)
+        message = attrs.pop('message', '%(prog)s, version %(version)s')
+
+        def callback(ctx, param, value):
+            if not value or ctx.resilient_parsing:
+                return
+            prog = prog_name
+            if prog is None:
+                prog = ctx.find_root().info_name
+            ver = version
+            if ver is None:
+                try:
+                    import pkg_resources
+                except ImportError:
+                    pass
+                else:
+                    for dist in pkg_resources.working_set:
+                        scripts = dist.get_entry_map().get('console_scripts') or {}
+                        for script_name, entry_point in iteritems(scripts):
+                            if entry_point.module_name == module:
+                                ver = dist.version
+                                break
+                if ver is None:
+                    raise RuntimeError('Could not determine version')
+            echo(message % {
+                'prog': prog,
+                'version': ver,
+            }, color=ctx.color)
+            ctx.exit()
+
+        attrs.setdefault('is_flag', True)
+        attrs.setdefault('expose_value', False)
+        attrs.setdefault('is_eager', True)
+        attrs.setdefault('help', 'Show the version and exit.')
+        attrs['callback'] = callback
+        return option(*(param_decls or ('--version',)), **attrs)(f)
+    return decorator
+
+
+def help_option(*param_decls, **attrs):
+    """Adds a ``--help`` option which immediately ends the program
+    printing out the help page.  This is usually unnecessary to add as
+    this is added by default to all commands unless suppressed.
+
+    Like :func:`version_option`, this is implemented as eager option that
+    prints in the callback and exits.
+
+    All arguments are forwarded to :func:`option`.
+    """
+    def decorator(f):
+        def callback(ctx, param, value):
+            if value and not ctx.resilient_parsing:
+                echo(ctx.get_help(), color=ctx.color)
+                ctx.exit()
+        attrs.setdefault('is_flag', True)
+        attrs.setdefault('expose_value', False)
+        attrs.setdefault('help', 'Show this message and exit.')
+        attrs.setdefault('is_eager', True)
+        attrs['callback'] = callback
+        return option(*(param_decls or ('--help',)), **attrs)(f)
+    return decorator
+
+
+# Circular dependencies between core and decorators
+from .core import Command, Group, Argument, Option
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/exceptions.py
@@ -0,0 +1,235 @@
+from ._compat import PY2, filename_to_ui, get_text_stderr
+from .utils import echo
+
+
+def _join_param_hints(param_hint):
+    if isinstance(param_hint, (tuple, list)):
+        return ' / '.join('"%s"' % x for x in param_hint)
+    return param_hint
+
+
+class ClickException(Exception):
+    """An exception that Click can handle and show to the user."""
+
+    #: The exit code for this exception
+    exit_code = 1
+
+    def __init__(self, message):
+        ctor_msg = message
+        if PY2:
+            if ctor_msg is not None:
+                ctor_msg = ctor_msg.encode('utf-8')
+        Exception.__init__(self, ctor_msg)
+        self.message = message
+
+    def format_message(self):
+        return self.message
+
+    def __str__(self):
+        return self.message
+
+    if PY2:
+        __unicode__ = __str__
+
+        def __str__(self):
+            return self.message.encode('utf-8')
+
+    def show(self, file=None):
+        if file is None:
+            file = get_text_stderr()
+        echo('Error: %s' % self.format_message(), file=file)
+
+
+class UsageError(ClickException):
+    """An internal exception that signals a usage error.  This typically
+    aborts any further handling.
+
+    :param message: the error message to display.
+    :param ctx: optionally the context that caused this error.  Click will
+                fill in the context automatically in some situations.
+    """
+    exit_code = 2
+
+    def __init__(self, message, ctx=None):
+        ClickException.__init__(self, message)
+        self.ctx = ctx
+        self.cmd = self.ctx and self.ctx.command or None
+
+    def show(self, file=None):
+        if file is None:
+            file = get_text_stderr()
+        color = None
+        hint = ''
+        if (self.cmd is not None and
+                self.cmd.get_help_option(self.ctx) is not None):
+            hint = ('Try "%s %s" for help.\n'
+                    % (self.ctx.command_path, self.ctx.help_option_names[0]))
+        if self.ctx is not None:
+            color = self.ctx.color
+            echo(self.ctx.get_usage() + '\n%s' % hint, file=file, color=color)
+        echo('Error: %s' % self.format_message(), file=file, color=color)
+
+
+class BadParameter(UsageError):
+    """An exception that formats out a standardized error message for a
+    bad parameter.  This is useful when thrown from a callback or type as
+    Click will attach contextual information to it (for instance, which
+    parameter it is).
+
+    .. versionadded:: 2.0
+
+    :param param: the parameter object that caused this error.  This can
+                  be left out, and Click will attach this info itself
+                  if possible.
+    :param param_hint: a string that shows up as parameter name.  This
+                       can be used as alternative to `param` in cases
+                       where custom validation should happen.  If it is
+                       a string it's used as such, if it's a list then
+                       each item is quoted and separated.
+    """
+
+    def __init__(self, message, ctx=None, param=None,
+                 param_hint=None):
+        UsageError.__init__(self, message, ctx)
+        self.param = param
+        self.param_hint = param_hint
+
+    def format_message(self):
+        if self.param_hint is not None:
+            param_hint = self.param_hint
+        elif self.param is not None:
+            param_hint = self.param.get_error_hint(self.ctx)
+        else:
+            return 'Invalid value: %s' % self.message
+        param_hint = _join_param_hints(param_hint)
+
+        return 'Invalid value for %s: %s' % (param_hint, self.message)
+
+
+class MissingParameter(BadParameter):
+    """Raised if click required an option or argument but it was not
+    provided when invoking the script.
+
+    .. versionadded:: 4.0
+
+    :param param_type: a string that indicates the type of the parameter.
+                       The default is to inherit the parameter type from
+                       the given `param`.  Valid values are ``'parameter'``,
+                       ``'option'`` or ``'argument'``.
+    """
+
+    def __init__(self, message=None, ctx=None, param=None,
+                 param_hint=None, param_type=None):
+        BadParameter.__init__(self, message, ctx, param, param_hint)
+        self.param_type = param_type
+
+    def format_message(self):
+        if self.param_hint is not None:
+            param_hint = self.param_hint
+        elif self.param is not None:
+            param_hint = self.param.get_error_hint(self.ctx)
+        else:
+            param_hint = None
+        param_hint = _join_param_hints(param_hint)
+
+        param_type = self.param_type
+        if param_type is None and self.param is not None:
+            param_type = self.param.param_type_name
+
+        msg = self.message
+        if self.param is not None:
+            msg_extra = self.param.type.get_missing_message(self.param)
+            if msg_extra:
+                if msg:
+                    msg += '.  ' + msg_extra
+                else:
+                    msg = msg_extra
+
+        return 'Missing %s%s%s%s' % (
+            param_type,
+            param_hint and ' %s' % param_hint or '',
+            msg and '.  ' or '.',
+            msg or '',
+        )
+
+
+class NoSuchOption(UsageError):
+    """Raised if click attempted to handle an option that does not
+    exist.
+
+    .. versionadded:: 4.0
+    """
+
+    def __init__(self, option_name, message=None, possibilities=None,
+                 ctx=None):
+        if message is None:
+            message = 'no such option: %s' % option_name
+        UsageError.__init__(self, message, ctx)
+        self.option_name = option_name
+        self.possibilities = possibilities
+
+    def format_message(self):
+        bits = [self.message]
+        if self.possibilities:
+            if len(self.possibilities) == 1:
+                bits.append('Did you mean %s?' % self.possibilities[0])
+            else:
+                possibilities = sorted(self.possibilities)
+                bits.append('(Possible options: %s)' % ', '.join(possibilities))
+        return '  '.join(bits)
+
+
+class BadOptionUsage(UsageError):
+    """Raised if an option is generally supplied but the use of the option
+    was incorrect.  This is for instance raised if the number of arguments
+    for an option is not correct.
+
+    .. versionadded:: 4.0
+
+    :param option_name: the name of the option being used incorrectly.
+    """
+
+    def __init__(self, option_name, message, ctx=None):
+        UsageError.__init__(self, message, ctx)
+        self.option_name = option_name
+
+
+class BadArgumentUsage(UsageError):
+    """Raised if an argument is generally supplied but the use of the argument
+    was incorrect.  This is for instance raised if the number of values
+    for an argument is not correct.
+
+    .. versionadded:: 6.0
+    """
+
+    def __init__(self, message, ctx=None):
+        UsageError.__init__(self, message, ctx)
+
+
+class FileError(ClickException):
+    """Raised if a file cannot be opened."""
+
+    def __init__(self, filename, hint=None):
+        ui_filename = filename_to_ui(filename)
+        if hint is None:
+            hint = 'unknown error'
+        ClickException.__init__(self, hint)
+        self.ui_filename = ui_filename
+        self.filename = filename
+
+    def format_message(self):
+        return 'Could not open file %s: %s' % (self.ui_filename, self.message)
+
+
+class Abort(RuntimeError):
+    """An internal signalling exception that signals Click to abort."""
+
+
+class Exit(RuntimeError):
+    """An exception that indicates that the application should exit with some
+    status code.
+
+    :param code: the status code to exit with.
+    """
+    def __init__(self, code=0):
+        self.exit_code = code
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/formatting.py
@@ -0,0 +1,256 @@
+from contextlib import contextmanager
+from .termui import get_terminal_size
+from .parser import split_opt
+from ._compat import term_len
+
+
+# Can force a width.  This is used by the test system
+FORCED_WIDTH = None
+
+
+def measure_table(rows):
+    widths = {}
+    for row in rows:
+        for idx, col in enumerate(row):
+            widths[idx] = max(widths.get(idx, 0), term_len(col))
+    return tuple(y for x, y in sorted(widths.items()))
+
+
+def iter_rows(rows, col_count):
+    for row in rows:
+        row = tuple(row)
+        yield row + ('',) * (col_count - len(row))
+
+
+def wrap_text(text, width=78, initial_indent='', subsequent_indent='',
+              preserve_paragraphs=False):
+    """A helper function that intelligently wraps text.  By default, it
+    assumes that it operates on a single paragraph of text but if the
+    `preserve_paragraphs` parameter is provided it will intelligently
+    handle paragraphs (defined by two empty lines).
+
+    If paragraphs are handled, a paragraph can be prefixed with an empty
+    line containing the ``\\b`` character (``\\x08``) to indicate that
+    no rewrapping should happen in that block.
+
+    :param text: the text that should be rewrapped.
+    :param width: the maximum width for the text.
+    :param initial_indent: the initial indent that should be placed on the
+                           first line as a string.
+    :param subsequent_indent: the indent string that should be placed on
+                              each consecutive line.
+    :param preserve_paragraphs: if this flag is set then the wrapping will
+                                intelligently handle paragraphs.
+    """
+    from ._textwrap import TextWrapper
+    text = text.expandtabs()
+    wrapper = TextWrapper(width, initial_indent=initial_indent,
+                          subsequent_indent=subsequent_indent,
+                          replace_whitespace=False)
+    if not preserve_paragraphs:
+        return wrapper.fill(text)
+
+    p = []
+    buf = []
+    indent = None
+
+    def _flush_par():
+        if not buf:
+            return
+        if buf[0].strip() == '\b':
+            p.append((indent or 0, True, '\n'.join(buf[1:])))
+        else:
+            p.append((indent or 0, False, ' '.join(buf)))
+        del buf[:]
+
+    for line in text.splitlines():
+        if not line:
+            _flush_par()
+            indent = None
+        else:
+            if indent is None:
+                orig_len = term_len(line)
+                line = line.lstrip()
+                indent = orig_len - term_len(line)
+            buf.append(line)
+    _flush_par()
+
+    rv = []
+    for indent, raw, text in p:
+        with wrapper.extra_indent(' ' * indent):
+            if raw:
+                rv.append(wrapper.indent_only(text))
+            else:
+                rv.append(wrapper.fill(text))
+
+    return '\n\n'.join(rv)
+
+
+class HelpFormatter(object):
+    """This class helps with formatting text-based help pages.  It's
+    usually just needed for very special internal cases, but it's also
+    exposed so that developers can write their own fancy outputs.
+
+    At present, it always writes into memory.
+
+    :param indent_increment: the additional increment for each level.
+    :param width: the width for the text.  This defaults to the terminal
+                  width clamped to a maximum of 78.
+    """
+
+    def __init__(self, indent_increment=2, width=None, max_width=None):
+        self.indent_increment = indent_increment
+        if max_width is None:
+            max_width = 80
+        if width is None:
+            width = FORCED_WIDTH
+            if width is None:
+                width = max(min(get_terminal_size()[0], max_width) - 2, 50)
+        self.width = width
+        self.current_indent = 0
+        self.buffer = []
+
+    def write(self, string):
+        """Writes a unicode string into the internal buffer."""
+        self.buffer.append(string)
+
+    def indent(self):
+        """Increases the indentation."""
+        self.current_indent += self.indent_increment
+
+    def dedent(self):
+        """Decreases the indentation."""
+        self.current_indent -= self.indent_increment
+
+    def write_usage(self, prog, args='', prefix='Usage: '):
+        """Writes a usage line into the buffer.
+
+        :param prog: the program name.
+        :param args: whitespace separated list of arguments.
+        :param prefix: the prefix for the first line.
+        """
+        usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog)
+        text_width = self.width - self.current_indent
+
+        if text_width >= (term_len(usage_prefix) + 20):
+            # The arguments will fit to the right of the prefix.
+            indent = ' ' * term_len(usage_prefix)
+            self.write(wrap_text(args, text_width,
+                                 initial_indent=usage_prefix,
+                                 subsequent_indent=indent))
+        else:
+            # The prefix is too long, put the arguments on the next line.
+            self.write(usage_prefix)
+            self.write('\n')
+            indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4)
+            self.write(wrap_text(args, text_width,
+                                 initial_indent=indent,
+                                 subsequent_indent=indent))
+
+        self.write('\n')
+
+    def write_heading(self, heading):
+        """Writes a heading into the buffer."""
+        self.write('%*s%s:\n' % (self.current_indent, '', heading))
+
+    def write_paragraph(self):
+        """Writes a paragraph into the buffer."""
+        if self.buffer:
+            self.write('\n')
+
+    def write_text(self, text):
+        """Writes re-indented text into the buffer.  This rewraps and
+        preserves paragraphs.
+        """
+        text_width = max(self.width - self.current_indent, 11)
+        indent = ' ' * self.current_indent
+        self.write(wrap_text(text, text_width,
+                             initial_indent=indent,
+                             subsequent_indent=indent,
+                             preserve_paragraphs=True))
+        self.write('\n')
+
+    def write_dl(self, rows, col_max=30, col_spacing=2):
+        """Writes a definition list into the buffer.  This is how options
+        and commands are usually formatted.
+
+        :param rows: a list of two item tuples for the terms and values.
+        :param col_max: the maximum width of the first column.
+        :param col_spacing: the number of spaces between the first and
+                            second column.
+        """
+        rows = list(rows)
+        widths = measure_table(rows)
+        if len(widths) != 2:
+            raise TypeError('Expected two columns for definition list')
+
+        first_col = min(widths[0], col_max) + col_spacing
+
+        for first, second in iter_rows(rows, len(widths)):
+            self.write('%*s%s' % (self.current_indent, '', first))
+            if not second:
+                self.write('\n')
+                continue
+            if term_len(first) <= first_col - col_spacing:
+                self.write(' ' * (first_col - term_len(first)))
+            else:
+                self.write('\n')
+                self.write(' ' * (first_col + self.current_indent))
+
+            text_width = max(self.width - first_col - 2, 10)
+            lines = iter(wrap_text(second, text_width).splitlines())
+            if lines:
+                self.write(next(lines) + '\n')
+                for line in lines:
+                    self.write('%*s%s\n' % (
+                        first_col + self.current_indent, '', line))
+            else:
+                self.write('\n')
+
+    @contextmanager
+    def section(self, name):
+        """Helpful context manager that writes a paragraph, a heading,
+        and the indents.
+
+        :param name: the section name that is written as heading.
+        """
+        self.write_paragraph()
+        self.write_heading(name)
+        self.indent()
+        try:
+            yield
+        finally:
+            self.dedent()
+
+    @contextmanager
+    def indentation(self):
+        """A context manager that increases the indentation."""
+        self.indent()
+        try:
+            yield
+        finally:
+            self.dedent()
+
+    def getvalue(self):
+        """Returns the buffer contents."""
+        return ''.join(self.buffer)
+
+
+def join_options(options):
+    """Given a list of option strings this joins them in the most appropriate
+    way and returns them in the form ``(formatted_string,
+    any_prefix_is_slash)`` where the second item in the tuple is a flag that
+    indicates if any of the option prefixes was a slash.
+    """
+    rv = []
+    any_prefix_is_slash = False
+    for opt in options:
+        prefix = split_opt(opt)[0]
+        if prefix == '/':
+            any_prefix_is_slash = True
+        rv.append((len(prefix), opt))
+
+    rv.sort(key=lambda x: x[0])
+
+    rv = ', '.join(x[1] for x in rv)
+    return rv, any_prefix_is_slash
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/globals.py
@@ -0,0 +1,48 @@
+from threading import local
+
+
+_local = local()
+
+
+def get_current_context(silent=False):
+    """Returns the current click context.  This can be used as a way to
+    access the current context object from anywhere.  This is a more implicit
+    alternative to the :func:`pass_context` decorator.  This function is
+    primarily useful for helpers such as :func:`echo` which might be
+    interested in changing its behavior based on the current context.
+
+    To push the current context, :meth:`Context.scope` can be used.
+
+    .. versionadded:: 5.0
+
+    :param silent: is set to `True` the return value is `None` if no context
+                   is available.  The default behavior is to raise a
+                   :exc:`RuntimeError`.
+    """
+    try:
+        return getattr(_local, 'stack')[-1]
+    except (AttributeError, IndexError):
+        if not silent:
+            raise RuntimeError('There is no active click context.')
+
+
+def push_context(ctx):
+    """Pushes a new context to the current stack."""
+    _local.__dict__.setdefault('stack', []).append(ctx)
+
+
+def pop_context():
+    """Removes the top level from the stack."""
+    _local.stack.pop()
+
+
+def resolve_color_default(color=None):
+    """"Internal helper to get the default value of the color flag.  If a
+    value is passed it's returned unchanged, otherwise it's looked up from
+    the current context.
+    """
+    if color is not None:
+        return color
+    ctx = get_current_context(silent=True)
+    if ctx is not None:
+        return ctx.color
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/parser.py
@@ -0,0 +1,427 @@
+# -*- coding: utf-8 -*-
+"""
+click.parser
+~~~~~~~~~~~~
+
+This module started out as largely a copy paste from the stdlib's
+optparse module with the features removed that we do not need from
+optparse because we implement them in Click on a higher level (for
+instance type handling, help formatting and a lot more).
+
+The plan is to remove more and more from here over time.
+
+The reason this is a different module and not optparse from the stdlib
+is that there are differences in 2.x and 3.x about the error messages
+generated and optparse in the stdlib uses gettext for no good reason
+and might cause us issues.
+"""
+
+import re
+from collections import deque
+from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \
+     BadArgumentUsage
+
+
+def _unpack_args(args, nargs_spec):
+    """Given an iterable of arguments and an iterable of nargs specifications,
+    it returns a tuple with all the unpacked arguments at the first index
+    and all remaining arguments as the second.
+
+    The nargs specification is the number of arguments that should be consumed
+    or `-1` to indicate that this position should eat up all the remainders.
+
+    Missing items are filled with `None`.
+    """
+    args = deque(args)
+    nargs_spec = deque(nargs_spec)
+    rv = []
+    spos = None
+
+    def _fetch(c):
+        try:
+            if spos is None:
+                return c.popleft()
+            else:
+                return c.pop()
+        except IndexError:
+            return None
+
+    while nargs_spec:
+        nargs = _fetch(nargs_spec)
+        if nargs == 1:
+            rv.append(_fetch(args))
+        elif nargs > 1:
+            x = [_fetch(args) for _ in range(nargs)]
+            # If we're reversed, we're pulling in the arguments in reverse,
+            # so we need to turn them around.
+            if spos is not None:
+                x.reverse()
+            rv.append(tuple(x))
+        elif nargs < 0:
+            if spos is not None:
+                raise TypeError('Cannot have two nargs < 0')
+            spos = len(rv)
+            rv.append(None)
+
+    # spos is the position of the wildcard (star).  If it's not `None`,
+    # we fill it with the remainder.
+    if spos is not None:
+        rv[spos] = tuple(args)
+        args = []
+        rv[spos + 1:] = reversed(rv[spos + 1:])
+
+    return tuple(rv), list(args)
+
+
+def _error_opt_args(nargs, opt):
+    if nargs == 1:
+        raise BadOptionUsage(opt, '%s option requires an argument' % opt)
+    raise BadOptionUsage(opt, '%s option requires %d arguments' % (opt, nargs))
+
+
+def split_opt(opt):
+    first = opt[:1]
+    if first.isalnum():
+        return '', opt
+    if opt[1:2] == first:
+        return opt[:2], opt[2:]
+    return first, opt[1:]
+
+
+def normalize_opt(opt, ctx):
+    if ctx is None or ctx.token_normalize_func is None:
+        return opt
+    prefix, opt = split_opt(opt)
+    return prefix + ctx.token_normalize_func(opt)
+
+
+def split_arg_string(string):
+    """Given an argument string this attempts to split it into small parts."""
+    rv = []
+    for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
+                             r'|"([^"\\]*(?:\\.[^"\\]*)*)"'
+                             r'|\S+)\s*', string, re.S):
+        arg = match.group().strip()
+        if arg[:1] == arg[-1:] and arg[:1] in '"\'':
+            arg = arg[1:-1].encode('ascii', 'backslashreplace') \
+                .decode('unicode-escape')
+        try:
+            arg = type(string)(arg)
+        except UnicodeError:
+            pass
+        rv.append(arg)
+    return rv
+
+
+class Option(object):
+
+    def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None):
+        self._short_opts = []
+        self._long_opts = []
+        self.prefixes = set()
+
+        for opt in opts:
+            prefix, value = split_opt(opt)
+            if not prefix:
+                raise ValueError('Invalid start character for option (%s)'
+                                 % opt)
+            self.prefixes.add(prefix[0])
+            if len(prefix) == 1 and len(value) == 1:
+                self._short_opts.append(opt)
+            else:
+                self._long_opts.append(opt)
+                self.prefixes.add(prefix)
+
+        if action is None:
+            action = 'store'
+
+        self.dest = dest
+        self.action = action
+        self.nargs = nargs
+        self.const = const
+        self.obj = obj
+
+    @property
+    def takes_value(self):
+        return self.action in ('store', 'append')
+
+    def process(self, value, state):
+        if self.action == 'store':
+            state.opts[self.dest] = value
+        elif self.action == 'store_const':
+            state.opts[self.dest] = self.const
+        elif self.action == 'append':
+            state.opts.setdefault(self.dest, []).append(value)
+        elif self.action == 'append_const':
+            state.opts.setdefault(self.dest, []).append(self.const)
+        elif self.action == 'count':
+            state.opts[self.dest] = state.opts.get(self.dest, 0) + 1
+        else:
+            raise ValueError('unknown action %r' % self.action)
+        state.order.append(self.obj)
+
+
+class Argument(object):
+
+    def __init__(self, dest, nargs=1, obj=None):
+        self.dest = dest
+        self.nargs = nargs
+        self.obj = obj
+
+    def process(self, value, state):
+        if self.nargs > 1:
+            holes = sum(1 for x in value if x is None)
+            if holes == len(value):
+                value = None
+            elif holes != 0:
+                raise BadArgumentUsage('argument %s takes %d values'
+                                       % (self.dest, self.nargs))
+        state.opts[self.dest] = value
+        state.order.append(self.obj)
+
+
+class ParsingState(object):
+
+    def __init__(self, rargs):
+        self.opts = {}
+        self.largs = []
+        self.rargs = rargs
+        self.order = []
+
+
+class OptionParser(object):
+    """The option parser is an internal class that is ultimately used to
+    parse options and arguments.  It's modelled after optparse and brings
+    a similar but vastly simplified API.  It should generally not be used
+    directly as the high level Click classes wrap it for you.
+
+    It's not nearly as extensible as optparse or argparse as it does not
+    implement features that are implemented on a higher level (such as
+    types or defaults).
+
+    :param ctx: optionally the :class:`~click.Context` where this parser
+                should go with.
+    """
+
+    def __init__(self, ctx=None):
+        #: The :class:`~click.Context` for this parser.  This might be
+        #: `None` for some advanced use cases.
+        self.ctx = ctx
+        #: This controls how the parser deals with interspersed arguments.
+        #: If this is set to `False`, the parser will stop on the first
+        #: non-option.  Click uses this to implement nested subcommands
+        #: safely.
+        self.allow_interspersed_args = True
+        #: This tells the parser how to deal with unknown options.  By
+        #: default it will error out (which is sensible), but there is a
+        #: second mode where it will ignore it and continue processing
+        #: after shifting all the unknown options into the resulting args.
+        self.ignore_unknown_options = False
+        if ctx is not None:
+            self.allow_interspersed_args = ctx.allow_interspersed_args
+            self.ignore_unknown_options = ctx.ignore_unknown_options
+        self._short_opt = {}
+        self._long_opt = {}
+        self._opt_prefixes = set(['-', '--'])
+        self._args = []
+
+    def add_option(self, opts, dest, action=None, nargs=1, const=None,
+                   obj=None):
+        """Adds a new option named `dest` to the parser.  The destination
+        is not inferred (unlike with optparse) and needs to be explicitly
+        provided.  Action can be any of ``store``, ``store_const``,
+        ``append``, ``appnd_const`` or ``count``.
+
+        The `obj` can be used to identify the option in the order list
+        that is returned from the parser.
+        """
+        if obj is None:
+            obj = dest
+        opts = [normalize_opt(opt, self.ctx) for opt in opts]
+        option = Option(opts, dest, action=action, nargs=nargs,
+                        const=const, obj=obj)
+        self._opt_prefixes.update(option.prefixes)
+        for opt in option._short_opts:
+            self._short_opt[opt] = option
+        for opt in option._long_opts:
+            self._long_opt[opt] = option
+
+    def add_argument(self, dest, nargs=1, obj=None):
+        """Adds a positional argument named `dest` to the parser.
+
+        The `obj` can be used to identify the option in the order list
+        that is returned from the parser.
+        """
+        if obj is None:
+            obj = dest
+        self._args.append(Argument(dest=dest, nargs=nargs, obj=obj))
+
+    def parse_args(self, args):
+        """Parses positional arguments and returns ``(values, args, order)``
+        for the parsed options and arguments as well as the leftover
+        arguments if there are any.  The order is a list of objects as they
+        appear on the command line.  If arguments appear multiple times they
+        will be memorized multiple times as well.
+        """
+        state = ParsingState(args)
+        try:
+            self._process_args_for_options(state)
+            self._process_args_for_args(state)
+        except UsageError:
+            if self.ctx is None or not self.ctx.resilient_parsing:
+                raise
+        return state.opts, state.largs, state.order
+
+    def _process_args_for_args(self, state):
+        pargs, args = _unpack_args(state.largs + state.rargs,
+                                   [x.nargs for x in self._args])
+
+        for idx, arg in enumerate(self._args):
+            arg.process(pargs[idx], state)
+
+        state.largs = args
+        state.rargs = []
+
+    def _process_args_for_options(self, state):
+        while state.rargs:
+            arg = state.rargs.pop(0)
+            arglen = len(arg)
+            # Double dashes always handled explicitly regardless of what
+            # prefixes are valid.
+            if arg == '--':
+                return
+            elif arg[:1] in self._opt_prefixes and arglen > 1:
+                self._process_opts(arg, state)
+            elif self.allow_interspersed_args:
+                state.largs.append(arg)
+            else:
+                state.rargs.insert(0, arg)
+                return
+
+        # Say this is the original argument list:
+        # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
+        #                            ^
+        # (we are about to process arg(i)).
+        #
+        # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
+        # [arg0, ..., arg(i-1)] (any options and their arguments will have
+        # been removed from largs).
+        #
+        # The while loop will usually consume 1 or more arguments per pass.
+        # If it consumes 1 (eg. arg is an option that takes no arguments),
+        # then after _process_arg() is done the situation is:
+        #
+        #   largs = subset of [arg0, ..., arg(i)]
+        #   rargs = [arg(i+1), ..., arg(N-1)]
+        #
+        # If allow_interspersed_args is false, largs will always be
+        # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
+        # not a very interesting subset!
+
+    def _match_long_opt(self, opt, explicit_value, state):
+        if opt not in self._long_opt:
+            possibilities = [word for word in self._long_opt
+                             if word.startswith(opt)]
+            raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
+
+        option = self._long_opt[opt]
+        if option.takes_value:
+            # At this point it's safe to modify rargs by injecting the
+            # explicit value, because no exception is raised in this
+            # branch.  This means that the inserted value will be fully
+            # consumed.
+            if explicit_value is not None:
+                state.rargs.insert(0, explicit_value)
+
+            nargs = option.nargs
+            if len(state.rargs) < nargs:
+                _error_opt_args(nargs, opt)
+            elif nargs == 1:
+                value = state.rargs.pop(0)
+            else:
+                value = tuple(state.rargs[:nargs])
+                del state.rargs[:nargs]
+
+        elif explicit_value is not None:
+            raise BadOptionUsage(opt, '%s option does not take a value' % opt)
+
+        else:
+            value = None
+
+        option.process(value, state)
+
+    def _match_short_opt(self, arg, state):
+        stop = False
+        i = 1
+        prefix = arg[0]
+        unknown_options = []
+
+        for ch in arg[1:]:
+            opt = normalize_opt(prefix + ch, self.ctx)
+            option = self._short_opt.get(opt)
+            i += 1
+
+            if not option:
+                if self.ignore_unknown_options:
+                    unknown_options.append(ch)
+                    continue
+                raise NoSuchOption(opt, ctx=self.ctx)
+            if option.takes_value:
+                # Any characters left in arg?  Pretend they're the
+                # next arg, and stop consuming characters of arg.
+                if i < len(arg):
+                    state.rargs.insert(0, arg[i:])
+                    stop = True
+
+                nargs = option.nargs
+                if len(state.rargs) < nargs:
+                    _error_opt_args(nargs, opt)
+                elif nargs == 1:
+                    value = state.rargs.pop(0)
+                else:
+                    value = tuple(state.rargs[:nargs])
+                    del state.rargs[:nargs]
+
+            else:
+                value = None
+
+            option.process(value, state)
+
+            if stop:
+                break
+
+        # If we got any unknown options we re-combinate the string of the
+        # remaining options and re-attach the prefix, then report that
+        # to the state as new larg.  This way there is basic combinatorics
+        # that can be achieved while still ignoring unknown arguments.
+        if self.ignore_unknown_options and unknown_options:
+            state.largs.append(prefix + ''.join(unknown_options))
+
+    def _process_opts(self, arg, state):
+        explicit_value = None
+        # Long option handling happens in two parts.  The first part is
+        # supporting explicitly attached values.  In any case, we will try
+        # to long match the option first.
+        if '=' in arg:
+            long_opt, explicit_value = arg.split('=', 1)
+        else:
+            long_opt = arg
+        norm_long_opt = normalize_opt(long_opt, self.ctx)
+
+        # At this point we will match the (assumed) long option through
+        # the long option matching code.  Note that this allows options
+        # like "-foo" to be matched as long options.
+        try:
+            self._match_long_opt(norm_long_opt, explicit_value, state)
+        except NoSuchOption:
+            # At this point the long option matching failed, and we need
+            # to try with short options.  However there is a special rule
+            # which says, that if we have a two character options prefix
+            # (applies to "--foo" for instance), we do not dispatch to the
+            # short option code and will instead raise the no option
+            # error.
+            if arg[:2] not in self._opt_prefixes:
+                return self._match_short_opt(arg, state)
+            if not self.ignore_unknown_options:
+                raise
+            state.largs.append(arg)
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/termui.py
@@ -0,0 +1,606 @@
+import os
+import sys
+import struct
+import inspect
+import itertools
+
+from ._compat import raw_input, text_type, string_types, \
+     isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN
+from .utils import echo
+from .exceptions import Abort, UsageError
+from .types import convert_type, Choice, Path
+from .globals import resolve_color_default
+
+
+# The prompt functions to use.  The doc tools currently override these
+# functions to customize how they work.
+visible_prompt_func = raw_input
+
+_ansi_colors = {
+    'black': 30,
+    'red': 31,
+    'green': 32,
+    'yellow': 33,
+    'blue': 34,
+    'magenta': 35,
+    'cyan': 36,
+    'white': 37,
+    'reset': 39,
+    'bright_black': 90,
+    'bright_red': 91,
+    'bright_green': 92,
+    'bright_yellow': 93,
+    'bright_blue': 94,
+    'bright_magenta': 95,
+    'bright_cyan': 96,
+    'bright_white': 97,
+}
+_ansi_reset_all = '\033[0m'
+
+
+def hidden_prompt_func(prompt):
+    import getpass
+    return getpass.getpass(prompt)
+
+
+def _build_prompt(text, suffix, show_default=False, default=None, show_choices=True, type=None):
+    prompt = text
+    if type is not None and show_choices and isinstance(type, Choice):
+        prompt += ' (' + ", ".join(map(str, type.choices)) + ')'
+    if default is not None and show_default:
+        prompt = '%s [%s]' % (prompt, default)
+    return prompt + suffix
+
+
+def prompt(text, default=None, hide_input=False, confirmation_prompt=False,
+           type=None, value_proc=None, prompt_suffix=': ', show_default=True,
+           err=False, show_choices=True):
+    """Prompts a user for input.  This is a convenience function that can
+    be used to prompt a user for input later.
+
+    If the user aborts the input by sending a interrupt signal, this
+    function will catch it and raise a :exc:`Abort` exception.
+
+    .. versionadded:: 7.0
+       Added the show_choices parameter.
+
+    .. versionadded:: 6.0
+       Added unicode support for cmd.exe on Windows.
+
+    .. versionadded:: 4.0
+       Added the `err` parameter.
+
+    :param text: the text to show for the prompt.
+    :param default: the default value to use if no input happens.  If this
+                    is not given it will prompt until it's aborted.
+    :param hide_input: if this is set to true then the input value will
+                       be hidden.
+    :param confirmation_prompt: asks for confirmation for the value.
+    :param type: the type to use to check the value against.
+    :param value_proc: if this parameter is provided it's a function that
+                       is invoked instead of the type conversion to
+                       convert a value.
+    :param prompt_suffix: a suffix that should be added to the prompt.
+    :param show_default: shows or hides the default value in the prompt.
+    :param err: if set to true the file defaults to ``stderr`` instead of
+                ``stdout``, the same as with echo.
+    :param show_choices: Show or hide choices if the passed type is a Choice.
+                         For example if type is a Choice of either day or week,
+                         show_choices is true and text is "Group by" then the
+                         prompt will be "Group by (day, week): ".
+    """
+    result = None
+
+    def prompt_func(text):
+        f = hide_input and hidden_prompt_func or visible_prompt_func
+        try:
+            # Write the prompt separately so that we get nice
+            # coloring through colorama on Windows
+            echo(text, nl=False, err=err)
+            return f('')
+        except (KeyboardInterrupt, EOFError):
+            # getpass doesn't print a newline if the user aborts input with ^C.
+            # Allegedly this behavior is inherited from getpass(3).
+            # A doc bug has been filed at https://bugs.python.org/issue24711
+            if hide_input:
+                echo(None, err=err)
+            raise Abort()
+
+    if value_proc is None:
+        value_proc = convert_type(type, default)
+
+    prompt = _build_prompt(text, prompt_suffix, show_default, default, show_choices, type)
+
+    while 1:
+        while 1:
+            value = prompt_func(prompt)
+            if value:
+                break
+            elif default is not None:
+                if isinstance(value_proc, Path):
+                    # validate Path default value(exists, dir_okay etc.)
+                    value = default
+                    break
+                return default
+        try:
+            result = value_proc(value)
+        except UsageError as e:
+            echo('Error: %s' % e.message, err=err)
+            continue
+        if not confirmation_prompt:
+            return result
+        while 1:
+            value2 = prompt_func('Repeat for confirmation: ')
+            if value2:
+                break
+        if value == value2:
+            return result
+        echo('Error: the two entered values do not match', err=err)
+
+
+def confirm(text, default=False, abort=False, prompt_suffix=': ',
+            show_default=True, err=False):
+    """Prompts for confirmation (yes/no question).
+
+    If the user aborts the input by sending a interrupt signal this
+    function will catch it and raise a :exc:`Abort` exception.
+
+    .. versionadded:: 4.0
+       Added the `err` parameter.
+
+    :param text: the question to ask.
+    :param default: the default for the prompt.
+    :param abort: if this is set to `True` a negative answer aborts the
+                  exception by raising :exc:`Abort`.
+    :param prompt_suffix: a suffix that should be added to the prompt.
+    :param show_default: shows or hides the default value in the prompt.
+    :param err: if set to true the file defaults to ``stderr`` instead of
+                ``stdout``, the same as with echo.
+    """
+    prompt = _build_prompt(text, prompt_suffix, show_default,
+                           default and 'Y/n' or 'y/N')
+    while 1:
+        try:
+            # Write the prompt separately so that we get nice
+            # coloring through colorama on Windows
+            echo(prompt, nl=False, err=err)
+            value = visible_prompt_func('').lower().strip()
+        except (KeyboardInterrupt, EOFError):
+            raise Abort()
+        if value in ('y', 'yes'):
+            rv = True
+        elif value in ('n', 'no'):
+            rv = False
+        elif value == '':
+            rv = default
+        else:
+            echo('Error: invalid input', err=err)
+            continue
+        break
+    if abort and not rv:
+        raise Abort()
+    return rv
+
+
+def get_terminal_size():
+    """Returns the current size of the terminal as tuple in the form
+    ``(width, height)`` in columns and rows.
+    """
+    # If shutil has get_terminal_size() (Python 3.3 and later) use that
+    if sys.version_info >= (3, 3):
+        import shutil
+        shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None)
+        if shutil_get_terminal_size:
+            sz = shutil_get_terminal_size()
+            return sz.columns, sz.lines
+
+    # We provide a sensible default for get_winterm_size() when being invoked
+    # inside a subprocess. Without this, it would not provide a useful input.
+    if get_winterm_size is not None:
+        size = get_winterm_size()
+        if size == (0, 0):
+            return (79, 24)
+        else:
+            return size
+
+    def ioctl_gwinsz(fd):
+        try:
+            import fcntl
+            import termios
+            cr = struct.unpack(
+                'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+        except Exception:
+            return
+        return cr
+
+    cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2)
+    if not cr:
+        try:
+            fd = os.open(os.ctermid(), os.O_RDONLY)
+            try:
+                cr = ioctl_gwinsz(fd)
+            finally:
+                os.close(fd)
+        except Exception:
+            pass
+    if not cr or not cr[0] or not cr[1]:
+        cr = (os.environ.get('LINES', 25),
+              os.environ.get('COLUMNS', DEFAULT_COLUMNS))
+    return int(cr[1]), int(cr[0])
+
+
+def echo_via_pager(text_or_generator, color=None):
+    """This function takes a text and shows it via an environment specific
+    pager on stdout.
+
+    .. versionchanged:: 3.0
+       Added the `color` flag.
+
+    :param text_or_generator: the text to page, or alternatively, a
+                              generator emitting the text to page.
+    :param color: controls if the pager supports ANSI colors or not.  The
+                  default is autodetection.
+    """
+    color = resolve_color_default(color)
+
+    if inspect.isgeneratorfunction(text_or_generator):
+        i = text_or_generator()
+    elif isinstance(text_or_generator, string_types):
+        i = [text_or_generator]
+    else:
+        i = iter(text_or_generator)
+
+    # convert every element of i to a text type if necessary
+    text_generator = (el if isinstance(el, string_types) else text_type(el)
+                      for el in i)
+
+    from ._termui_impl import pager
+    return pager(itertools.chain(text_generator, "\n"), color)
+
+
+def progressbar(iterable=None, length=None, label=None, show_eta=True,
+                show_percent=None, show_pos=False,
+                item_show_func=None, fill_char='#', empty_char='-',
+                bar_template='%(label)s  [%(bar)s]  %(info)s',
+                info_sep='  ', width=36, file=None, color=None):
+    """This function creates an iterable context manager that can be used
+    to iterate over something while showing a progress bar.  It will
+    either iterate over the `iterable` or `length` items (that are counted
+    up).  While iteration happens, this function will print a rendered
+    progress bar to the given `file` (defaults to stdout) and will attempt
+    to calculate remaining time and more.  By default, this progress bar
+    will not be rendered if the file is not a terminal.
+
+    The context manager creates the progress bar.  When the context
+    manager is entered the progress bar is already displayed.  With every
+    iteration over the progress bar, the iterable passed to the bar is
+    advanced and the bar is updated.  When the context manager exits,
+    a newline is printed and the progress bar is finalized on screen.
+
+    No printing must happen or the progress bar will be unintentionally
+    destroyed.
+
+    Example usage::
+
+        with progressbar(items) as bar:
+            for item in bar:
+                do_something_with(item)
+
+    Alternatively, if no iterable is specified, one can manually update the
+    progress bar through the `update()` method instead of directly
+    iterating over the progress bar.  The update method accepts the number
+    of steps to increment the bar with::
+
+        with progressbar(length=chunks.total_bytes) as bar:
+            for chunk in chunks:
+                process_chunk(chunk)
+                bar.update(chunks.bytes)
+
+    .. versionadded:: 2.0
+
+    .. versionadded:: 4.0
+       Added the `color` parameter.  Added a `update` method to the
+       progressbar object.
+
+    :param iterable: an iterable to iterate over.  If not provided the length
+                     is required.
+    :param length: the number of items to iterate over.  By default the
+                   progressbar will attempt to ask the iterator about its
+                   length, which might or might not work.  If an iterable is
+                   also provided this parameter can be used to override the
+                   length.  If an iterable is not provided the progress bar
+                   will iterate over a range of that length.
+    :param label: the label to show next to the progress bar.
+    :param show_eta: enables or disables the estimated time display.  This is
+                     automatically disabled if the length cannot be
+                     determined.
+    :param show_percent: enables or disables the percentage display.  The
+                         default is `True` if the iterable has a length or
+                         `False` if not.
+    :param show_pos: enables or disables the absolute position display.  The
+                     default is `False`.
+    :param item_show_func: a function called with the current item which
+                           can return a string to show the current item
+                           next to the progress bar.  Note that the current
+                           item can be `None`!
+    :param fill_char: the character to use to show the filled part of the
+                      progress bar.
+    :param empty_char: the character to use to show the non-filled part of
+                       the progress bar.
+    :param bar_template: the format string to use as template for the bar.
+                         The parameters in it are ``label`` for the label,
+                         ``bar`` for the progress bar and ``info`` for the
+                         info section.
+    :param info_sep: the separator between multiple info items (eta etc.)
+    :param width: the width of the progress bar in characters, 0 means full
+                  terminal width
+    :param file: the file to write to.  If this is not a terminal then
+                 only the label is printed.
+    :param color: controls if the terminal supports ANSI colors or not.  The
+                  default is autodetection.  This is only needed if ANSI
+                  codes are included anywhere in the progress bar output
+                  which is not the case by default.
+    """
+    from ._termui_impl import ProgressBar
+    color = resolve_color_default(color)
+    return ProgressBar(iterable=iterable, length=length, show_eta=show_eta,
+                       show_percent=show_percent, show_pos=show_pos,
+                       item_show_func=item_show_func, fill_char=fill_char,
+                       empty_char=empty_char, bar_template=bar_template,
+                       info_sep=info_sep, file=file, label=label,
+                       width=width, color=color)
+
+
+def clear():
+    """Clears the terminal screen.  This will have the effect of clearing
+    the whole visible space of the terminal and moving the cursor to the
+    top left.  This does not do anything if not connected to a terminal.
+
+    .. versionadded:: 2.0
+    """
+    if not isatty(sys.stdout):
+        return
+    # If we're on Windows and we don't have colorama available, then we
+    # clear the screen by shelling out.  Otherwise we can use an escape
+    # sequence.
+    if WIN:
+        os.system('cls')
+    else:
+        sys.stdout.write('\033[2J\033[1;1H')
+
+
+def style(text, fg=None, bg=None, bold=None, dim=None, underline=None,
+          blink=None, reverse=None, reset=True):
+    """Styles a text with ANSI styles and returns the new string.  By
+    default the styling is self contained which means that at the end
+    of the string a reset code is issued.  This can be prevented by
+    passing ``reset=False``.
+
+    Examples::
+
+        click.echo(click.style('Hello World!', fg='green'))
+        click.echo(click.style('ATTENTION!', blink=True))
+        click.echo(click.style('Some things', reverse=True, fg='cyan'))
+
+    Supported color names:
+
+    * ``black`` (might be a gray)
+    * ``red``
+    * ``green``
+    * ``yellow`` (might be an orange)
+    * ``blue``
+    * ``magenta``
+    * ``cyan``
+    * ``white`` (might be light gray)
+    * ``bright_black``
+    * ``bright_red``
+    * ``bright_green``
+    * ``bright_yellow``
+    * ``bright_blue``
+    * ``bright_magenta``
+    * ``bright_cyan``
+    * ``bright_white``
+    * ``reset`` (reset the color code only)
+
+    .. versionadded:: 2.0
+
+    .. versionadded:: 7.0
+       Added support for bright colors.
+
+    :param text: the string to style with ansi codes.
+    :param fg: if provided this will become the foreground color.
+    :param bg: if provided this will become the background color.
+    :param bold: if provided this will enable or disable bold mode.
+    :param dim: if provided this will enable or disable dim mode.  This is
+                badly supported.
+    :param underline: if provided this will enable or disable underline.
+    :param blink: if provided this will enable or disable blinking.
+    :param reverse: if provided this will enable or disable inverse
+                    rendering (foreground becomes background and the
+                    other way round).
+    :param reset: by default a reset-all code is added at the end of the
+                  string which means that styles do not carry over.  This
+                  can be disabled to compose styles.
+    """
+    bits = []
+    if fg:
+        try:
+            bits.append('\033[%dm' % (_ansi_colors[fg]))
+        except KeyError:
+            raise TypeError('Unknown color %r' % fg)
+    if bg:
+        try:
+            bits.append('\033[%dm' % (_ansi_colors[bg] + 10))
+        except KeyError:
+            raise TypeError('Unknown color %r' % bg)
+    if bold is not None:
+        bits.append('\033[%dm' % (1 if bold else 22))
+    if dim is not None:
+        bits.append('\033[%dm' % (2 if dim else 22))
+    if underline is not None:
+        bits.append('\033[%dm' % (4 if underline else 24))
+    if blink is not None:
+        bits.append('\033[%dm' % (5 if blink else 25))
+    if reverse is not None:
+        bits.append('\033[%dm' % (7 if reverse else 27))
+    bits.append(text)
+    if reset:
+        bits.append(_ansi_reset_all)
+    return ''.join(bits)
+
+
+def unstyle(text):
+    """Removes ANSI styling information from a string.  Usually it's not
+    necessary to use this function as Click's echo function will
+    automatically remove styling if necessary.
+
+    .. versionadded:: 2.0
+
+    :param text: the text to remove style information from.
+    """
+    return strip_ansi(text)
+
+
+def secho(message=None, file=None, nl=True, err=False, color=None, **styles):
+    """This function combines :func:`echo` and :func:`style` into one
+    call.  As such the following two calls are the same::
+
+        click.secho('Hello World!', fg='green')
+        click.echo(click.style('Hello World!', fg='green'))
+
+    All keyword arguments are forwarded to the underlying functions
+    depending on which one they go with.
+
+    .. versionadded:: 2.0
+    """
+    if message is not None:
+        message = style(message, **styles)
+    return echo(message, file=file, nl=nl, err=err, color=color)
+
+
+def edit(text=None, editor=None, env=None, require_save=True,
+         extension='.txt', filename=None):
+    r"""Edits the given text in the defined editor.  If an editor is given
+    (should be the full path to the executable but the regular operating
+    system search path is used for finding the executable) it overrides
+    the detected editor.  Optionally, some environment variables can be
+    used.  If the editor is closed without changes, `None` is returned.  In
+    case a file is edited directly the return value is always `None` and
+    `require_save` and `extension` are ignored.
+
+    If the editor cannot be opened a :exc:`UsageError` is raised.
+
+    Note for Windows: to simplify cross-platform usage, the newlines are
+    automatically converted from POSIX to Windows and vice versa.  As such,
+    the message here will have ``\n`` as newline markers.
+
+    :param text: the text to edit.
+    :param editor: optionally the editor to use.  Defaults to automatic
+                   detection.
+    :param env: environment variables to forward to the editor.
+    :param require_save: if this is true, then not saving in the editor
+                         will make the return value become `None`.
+    :param extension: the extension to tell the editor about.  This defaults
+                      to `.txt` but changing this might change syntax
+                      highlighting.
+    :param filename: if provided it will edit this file instead of the
+                     provided text contents.  It will not use a temporary
+                     file as an indirection in that case.
+    """
+    from ._termui_impl import Editor
+    editor = Editor(editor=editor, env=env, require_save=require_save,
+                    extension=extension)
+    if filename is None:
+        return editor.edit(text)
+    editor.edit_file(filename)
+
+
+def launch(url, wait=False, locate=False):
+    """This function launches the given URL (or filename) in the default
+    viewer application for this file type.  If this is an executable, it
+    might launch the executable in a new session.  The return value is
+    the exit code of the launched application.  Usually, ``0`` indicates
+    success.
+
+    Examples::
+
+        click.launch('https://click.palletsprojects.com/')
+        click.launch('/my/downloaded/file', locate=True)
+
+    .. versionadded:: 2.0
+
+    :param url: URL or filename of the thing to launch.
+    :param wait: waits for the program to stop.
+    :param locate: if this is set to `True` then instead of launching the
+                   application associated with the URL it will attempt to
+                   launch a file manager with the file located.  This
+                   might have weird effects if the URL does not point to
+                   the filesystem.
+    """
+    from ._termui_impl import open_url
+    return open_url(url, wait=wait, locate=locate)
+
+
+# If this is provided, getchar() calls into this instead.  This is used
+# for unittesting purposes.
+_getchar = None
+
+
+def getchar(echo=False):
+    """Fetches a single character from the terminal and returns it.  This
+    will always return a unicode character and under certain rare
+    circumstances this might return more than one character.  The
+    situations which more than one character is returned is when for
+    whatever reason multiple characters end up in the terminal buffer or
+    standard input was not actually a terminal.
+
+    Note that this will always read from the terminal, even if something
+    is piped into the standard input.
+
+    Note for Windows: in rare cases when typing non-ASCII characters, this
+    function might wait for a second character and then return both at once.
+    This is because certain Unicode characters look like special-key markers.
+
+    .. versionadded:: 2.0
+
+    :param echo: if set to `True`, the character read will also show up on
+                 the terminal.  The default is to not show it.
+    """
+    f = _getchar
+    if f is None:
+        from ._termui_impl import getchar as f
+    return f(echo)
+
+
+def raw_terminal():
+    from ._termui_impl import raw_terminal as f
+    return f()
+
+
+def pause(info='Press any key to continue ...', err=False):
+    """This command stops execution and waits for the user to press any
+    key to continue.  This is similar to the Windows batch "pause"
+    command.  If the program is not run through a terminal, this command
+    will instead do nothing.
+
+    .. versionadded:: 2.0
+
+    .. versionadded:: 4.0
+       Added the `err` parameter.
+
+    :param info: the info string to print before pausing.
+    :param err: if set to message goes to ``stderr`` instead of
+                ``stdout``, the same as with echo.
+    """
+    if not isatty(sys.stdin) or not isatty(sys.stdout):
+        return
+    try:
+        if info:
+            echo(info, nl=False, err=err)
+        try:
+            getchar()
+        except (KeyboardInterrupt, EOFError):
+            pass
+    finally:
+        if info:
+            echo(err=err)
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/testing.py
@@ -0,0 +1,374 @@
+import os
+import sys
+import shutil
+import tempfile
+import contextlib
+import shlex
+
+from ._compat import iteritems, PY2, string_types
+
+
+# If someone wants to vendor click, we want to ensure the
+# correct package is discovered.  Ideally we could use a
+# relative import here but unfortunately Python does not
+# support that.
+clickpkg = sys.modules[__name__.rsplit('.', 1)[0]]
+
+
+if PY2:
+    from cStringIO import StringIO
+else:
+    import io
+    from ._compat import _find_binary_reader
+
+
+class EchoingStdin(object):
+
+    def __init__(self, input, output):
+        self._input = input
+        self._output = output
+
+    def __getattr__(self, x):
+        return getattr(self._input, x)
+
+    def _echo(self, rv):
+        self._output.write(rv)
+        return rv
+
+    def read(self, n=-1):
+        return self._echo(self._input.read(n))
+
+    def readline(self, n=-1):
+        return self._echo(self._input.readline(n))
+
+    def readlines(self):
+        return [self._echo(x) for x in self._input.readlines()]
+
+    def __iter__(self):
+        return iter(self._echo(x) for x in self._input)
+
+    def __repr__(self):
+        return repr(self._input)
+
+
+def make_input_stream(input, charset):
+    # Is already an input stream.
+    if hasattr(input, 'read'):
+        if PY2:
+            return input
+        rv = _find_binary_reader(input)
+        if rv is not None:
+            return rv
+        raise TypeError('Could not find binary reader for input stream.')
+
+    if input is None:
+        input = b''
+    elif not isinstance(input, bytes):
+        input = input.encode(charset)
+    if PY2:
+        return StringIO(input)
+    return io.BytesIO(input)
+
+
+class Result(object):
+    """Holds the captured result of an invoked CLI script."""
+
+    def __init__(self, runner, stdout_bytes, stderr_bytes, exit_code,
+                 exception, exc_info=None):
+        #: The runner that created the result
+        self.runner = runner
+        #: The standard output as bytes.
+        self.stdout_bytes = stdout_bytes
+        #: The standard error as bytes, or False(y) if not available
+        self.stderr_bytes = stderr_bytes
+        #: The exit code as integer.
+        self.exit_code = exit_code
+        #: The exception that happened if one did.
+        self.exception = exception
+        #: The traceback
+        self.exc_info = exc_info
+
+    @property
+    def output(self):
+        """The (standard) output as unicode string."""
+        return self.stdout
+
+    @property
+    def stdout(self):
+        """The standard output as unicode string."""
+        return self.stdout_bytes.decode(self.runner.charset, 'replace') \
+            .replace('\r\n', '\n')
+
+    @property
+    def stderr(self):
+        """The standard error as unicode string."""
+        if not self.stderr_bytes:
+            raise ValueError("stderr not separately captured")
+        return self.stderr_bytes.decode(self.runner.charset, 'replace') \
+            .replace('\r\n', '\n')
+
+
+    def __repr__(self):
+        return '<%s %s>' % (
+            type(self).__name__,
+            self.exception and repr(self.exception) or 'okay',
+        )
+
+
+class CliRunner(object):
+    """The CLI runner provides functionality to invoke a Click command line
+    script for unittesting purposes in a isolated environment.  This only
+    works in single-threaded systems without any concurrency as it changes the
+    global interpreter state.
+
+    :param charset: the character set for the input and output data.  This is
+                    UTF-8 by default and should not be changed currently as
+                    the reporting to Click only works in Python 2 properly.
+    :param env: a dictionary with environment variables for overriding.
+    :param echo_stdin: if this is set to `True`, then reading from stdin writes
+                       to stdout.  This is useful for showing examples in
+                       some circumstances.  Note that regular prompts
+                       will automatically echo the input.
+    :param mix_stderr: if this is set to `False`, then stdout and stderr are
+                       preserved as independent streams.  This is useful for
+                       Unix-philosophy apps that have predictable stdout and
+                       noisy stderr, such that each may be measured
+                       independently
+    """
+
+    def __init__(self, charset=None, env=None, echo_stdin=False,
+                 mix_stderr=True):
+        if charset is None:
+            charset = 'utf-8'
+        self.charset = charset
+        self.env = env or {}
+        self.echo_stdin = echo_stdin
+        self.mix_stderr = mix_stderr
+
+    def get_default_prog_name(self, cli):
+        """Given a command object it will return the default program name
+        for it.  The default is the `name` attribute or ``"root"`` if not
+        set.
+        """
+        return cli.name or 'root'
+
+    def make_env(self, overrides=None):
+        """Returns the environment overrides for invoking a script."""
+        rv = dict(self.env)
+        if overrides:
+            rv.update(overrides)
+        return rv
+
+    @contextlib.contextmanager
+    def isolation(self, input=None, env=None, color=False):
+        """A context manager that sets up the isolation for invoking of a
+        command line tool.  This sets up stdin with the given input data
+        and `os.environ` with the overrides from the given dictionary.
+        This also rebinds some internals in Click to be mocked (like the
+        prompt functionality).
+
+        This is automatically done in the :meth:`invoke` method.
+
+        .. versionadded:: 4.0
+           The ``color`` parameter was added.
+
+        :param input: the input stream to put into sys.stdin.
+        :param env: the environment overrides as dictionary.
+        :param color: whether the output should contain color codes. The
+                      application can still override this explicitly.
+        """
+        input = make_input_stream(input, self.charset)
+
+        old_stdin = sys.stdin
+        old_stdout = sys.stdout
+        old_stderr = sys.stderr
+        old_forced_width = clickpkg.formatting.FORCED_WIDTH
+        clickpkg.formatting.FORCED_WIDTH = 80
+
+        env = self.make_env(env)
+
+        if PY2:
+            bytes_output = StringIO()
+            if self.echo_stdin:
+                input = EchoingStdin(input, bytes_output)
+            sys.stdout = bytes_output
+            if not self.mix_stderr:
+                bytes_error = StringIO()
+                sys.stderr = bytes_error
+        else:
+            bytes_output = io.BytesIO()
+            if self.echo_stdin:
+                input = EchoingStdin(input, bytes_output)
+            input = io.TextIOWrapper(input, encoding=self.charset)
+            sys.stdout = io.TextIOWrapper(
+                bytes_output, encoding=self.charset)
+            if not self.mix_stderr:
+                bytes_error = io.BytesIO()
+                sys.stderr = io.TextIOWrapper(
+                    bytes_error, encoding=self.charset)
+
+        if self.mix_stderr:
+            sys.stderr = sys.stdout
+
+        sys.stdin = input
+
+        def visible_input(prompt=None):
+            sys.stdout.write(prompt or '')
+            val = input.readline().rstrip('\r\n')
+            sys.stdout.write(val + '\n')
+            sys.stdout.flush()
+            return val
+
+        def hidden_input(prompt=None):
+            sys.stdout.write((prompt or '') + '\n')
+            sys.stdout.flush()
+            return input.readline().rstrip('\r\n')
+
+        def _getchar(echo):
+            char = sys.stdin.read(1)
+            if echo:
+                sys.stdout.write(char)
+                sys.stdout.flush()
+            return char
+
+        default_color = color
+
+        def should_strip_ansi(stream=None, color=None):
+            if color is None:
+                return not default_color
+            return not color
+
+        old_visible_prompt_func = clickpkg.termui.visible_prompt_func
+        old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func
+        old__getchar_func = clickpkg.termui._getchar
+        old_should_strip_ansi = clickpkg.utils.should_strip_ansi
+        clickpkg.termui.visible_prompt_func = visible_input
+        clickpkg.termui.hidden_prompt_func = hidden_input
+        clickpkg.termui._getchar = _getchar
+        clickpkg.utils.should_strip_ansi = should_strip_ansi
+
+        old_env = {}
+        try:
+            for key, value in iteritems(env):
+                old_env[key] = os.environ.get(key)
+                if value is None:
+                    try:
+                        del os.environ[key]
+                    except Exception:
+                        pass
+                else:
+                    os.environ[key] = value
+            yield (bytes_output, not self.mix_stderr and bytes_error)
+        finally:
+            for key, value in iteritems(old_env):
+                if value is None:
+                    try:
+                        del os.environ[key]
+                    except Exception:
+                        pass
+                else:
+                    os.environ[key] = value
+            sys.stdout = old_stdout
+            sys.stderr = old_stderr
+            sys.stdin = old_stdin
+            clickpkg.termui.visible_prompt_func = old_visible_prompt_func
+            clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func
+            clickpkg.termui._getchar = old__getchar_func
+            clickpkg.utils.should_strip_ansi = old_should_strip_ansi
+            clickpkg.formatting.FORCED_WIDTH = old_forced_width
+
+    def invoke(self, cli, args=None, input=None, env=None,
+               catch_exceptions=True, color=False, mix_stderr=False, **extra):
+        """Invokes a command in an isolated environment.  The arguments are
+        forwarded directly to the command line script, the `extra` keyword
+        arguments are passed to the :meth:`~clickpkg.Command.main` function of
+        the command.
+
+        This returns a :class:`Result` object.
+
+        .. versionadded:: 3.0
+           The ``catch_exceptions`` parameter was added.
+
+        .. versionchanged:: 3.0
+           The result object now has an `exc_info` attribute with the
+           traceback if available.
+
+        .. versionadded:: 4.0
+           The ``color`` parameter was added.
+
+        :param cli: the command to invoke
+        :param args: the arguments to invoke. It may be given as an iterable
+                     or a string. When given as string it will be interpreted
+                     as a Unix shell command. More details at
+                     :func:`shlex.split`.
+        :param input: the input data for `sys.stdin`.
+        :param env: the environment overrides.
+        :param catch_exceptions: Whether to catch any other exceptions than
+                                 ``SystemExit``.
+        :param extra: the keyword arguments to pass to :meth:`main`.
+        :param color: whether the output should contain color codes. The
+                      application can still override this explicitly.
+        """
+        exc_info = None
+        with self.isolation(input=input, env=env, color=color) as outstreams:
+            exception = None
+            exit_code = 0
+
+            if isinstance(args, string_types):
+                args = shlex.split(args)
+
+            try:
+                prog_name = extra.pop("prog_name")
+            except KeyError:
+                prog_name = self.get_default_prog_name(cli)
+
+            try:
+                cli.main(args=args or (), prog_name=prog_name, **extra)
+            except SystemExit as e:
+                exc_info = sys.exc_info()
+                exit_code = e.code
+                if exit_code is None:
+                    exit_code = 0
+
+                if exit_code != 0:
+                    exception = e
+
+                if not isinstance(exit_code, int):
+                    sys.stdout.write(str(exit_code))
+                    sys.stdout.write('\n')
+                    exit_code = 1
+
+            except Exception as e:
+                if not catch_exceptions:
+                    raise
+                exception = e
+                exit_code = 1
+                exc_info = sys.exc_info()
+            finally:
+                sys.stdout.flush()
+                stdout = outstreams[0].getvalue()
+                stderr = outstreams[1] and outstreams[1].getvalue()
+
+        return Result(runner=self,
+                      stdout_bytes=stdout,
+                      stderr_bytes=stderr,
+                      exit_code=exit_code,
+                      exception=exception,
+                      exc_info=exc_info)
+
+    @contextlib.contextmanager
+    def isolated_filesystem(self):
+        """A context manager that creates a temporary folder and changes
+        the current working directory to it for isolated filesystem tests.
+        """
+        cwd = os.getcwd()
+        t = tempfile.mkdtemp()
+        os.chdir(t)
+        try:
+            yield t
+        finally:
+            os.chdir(cwd)
+            try:
+                shutil.rmtree(t)
+            except (OSError, IOError):
+                pass
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/types.py
@@ -0,0 +1,668 @@
+import os
+import stat
+from datetime import datetime
+
+from ._compat import open_stream, text_type, filename_to_ui, \
+    get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2
+from .exceptions import BadParameter
+from .utils import safecall, LazyFile
+
+
+class ParamType(object):
+    """Helper for converting values through types.  The following is
+    necessary for a valid type:
+
+    *   it needs a name
+    *   it needs to pass through None unchanged
+    *   it needs to convert from a string
+    *   it needs to convert its result type through unchanged
+        (eg: needs to be idempotent)
+    *   it needs to be able to deal with param and context being `None`.
+        This can be the case when the object is used with prompt
+        inputs.
+    """
+    is_composite = False
+
+    #: the descriptive name of this type
+    name = None
+
+    #: if a list of this type is expected and the value is pulled from a
+    #: string environment variable, this is what splits it up.  `None`
+    #: means any whitespace.  For all parameters the general rule is that
+    #: whitespace splits them up.  The exception are paths and files which
+    #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
+    #: Windows).
+    envvar_list_splitter = None
+
+    def __call__(self, value, param=None, ctx=None):
+        if value is not None:
+            return self.convert(value, param, ctx)
+
+    def get_metavar(self, param):
+        """Returns the metavar default for this param if it provides one."""
+
+    def get_missing_message(self, param):
+        """Optionally might return extra information about a missing
+        parameter.
+
+        .. versionadded:: 2.0
+        """
+
+    def convert(self, value, param, ctx):
+        """Converts the value.  This is not invoked for values that are
+        `None` (the missing value).
+        """
+        return value
+
+    def split_envvar_value(self, rv):
+        """Given a value from an environment variable this splits it up
+        into small chunks depending on the defined envvar list splitter.
+
+        If the splitter is set to `None`, which means that whitespace splits,
+        then leading and trailing whitespace is ignored.  Otherwise, leading
+        and trailing splitters usually lead to empty items being included.
+        """
+        return (rv or '').split(self.envvar_list_splitter)
+
+    def fail(self, message, param=None, ctx=None):
+        """Helper method to fail with an invalid value message."""
+        raise BadParameter(message, ctx=ctx, param=param)
+
+
+class CompositeParamType(ParamType):
+    is_composite = True
+
+    @property
+    def arity(self):
+        raise NotImplementedError()
+
+
+class FuncParamType(ParamType):
+
+    def __init__(self, func):
+        self.name = func.__name__
+        self.func = func
+
+    def convert(self, value, param, ctx):
+        try:
+            return self.func(value)
+        except ValueError:
+            try:
+                value = text_type(value)
+            except UnicodeError:
+                value = str(value).decode('utf-8', 'replace')
+            self.fail(value, param, ctx)
+
+
+class UnprocessedParamType(ParamType):
+    name = 'text'
+
+    def convert(self, value, param, ctx):
+        return value
+
+    def __repr__(self):
+        return 'UNPROCESSED'
+
+
+class StringParamType(ParamType):
+    name = 'text'
+
+    def convert(self, value, param, ctx):
+        if isinstance(value, bytes):
+            enc = _get_argv_encoding()
+            try:
+                value = value.decode(enc)
+            except UnicodeError:
+                fs_enc = get_filesystem_encoding()
+                if fs_enc != enc:
+                    try:
+                        value = value.decode(fs_enc)
+                    except UnicodeError:
+                        value = value.decode('utf-8', 'replace')
+            return value
+        return value
+
+    def __repr__(self):
+        return 'STRING'
+
+
+class Choice(ParamType):
+    """The choice type allows a value to be checked against a fixed set
+    of supported values. All of these values have to be strings.
+
+    You should only pass a list or tuple of choices. Other iterables
+    (like generators) may lead to surprising results.
+
+    See :ref:`choice-opts` for an example.
+
+    :param case_sensitive: Set to false to make choices case
+        insensitive. Defaults to true.
+    """
+
+    name = 'choice'
+
+    def __init__(self, choices, case_sensitive=True):
+        self.choices = choices
+        self.case_sensitive = case_sensitive
+
+    def get_metavar(self, param):
+        return '[%s]' % '|'.join(self.choices)
+
+    def get_missing_message(self, param):
+        return 'Choose from:\n\t%s.' % ',\n\t'.join(self.choices)
+
+    def convert(self, value, param, ctx):
+        # Exact match
+        if value in self.choices:
+            return value
+
+        # Match through normalization and case sensitivity
+        # first do token_normalize_func, then lowercase
+        # preserve original `value` to produce an accurate message in
+        # `self.fail`
+        normed_value = value
+        normed_choices = self.choices
+
+        if ctx is not None and \
+           ctx.token_normalize_func is not None:
+            normed_value = ctx.token_normalize_func(value)
+            normed_choices = [ctx.token_normalize_func(choice) for choice in
+                              self.choices]
+
+        if not self.case_sensitive:
+            normed_value = normed_value.lower()
+            normed_choices = [choice.lower() for choice in normed_choices]
+
+        if normed_value in normed_choices:
+            return normed_value
+
+        self.fail('invalid choice: %s. (choose from %s)' %
+                  (value, ', '.join(self.choices)), param, ctx)
+
+    def __repr__(self):
+        return 'Choice(%r)' % list(self.choices)
+
+
+class DateTime(ParamType):
+    """The DateTime type converts date strings into `datetime` objects.
+
+    The format strings which are checked are configurable, but default to some
+    common (non-timezone aware) ISO 8601 formats.
+
+    When specifying *DateTime* formats, you should only pass a list or a tuple.
+    Other iterables, like generators, may lead to surprising results.
+
+    The format strings are processed using ``datetime.strptime``, and this
+    consequently defines the format strings which are allowed.
+
+    Parsing is tried using each format, in order, and the first format which
+    parses successfully is used.
+
+    :param formats: A list or tuple of date format strings, in the order in
+                    which they should be tried. Defaults to
+                    ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``,
+                    ``'%Y-%m-%d %H:%M:%S'``.
+    """
+    name = 'datetime'
+
+    def __init__(self, formats=None):
+        self.formats = formats or [
+            '%Y-%m-%d',
+            '%Y-%m-%dT%H:%M:%S',
+            '%Y-%m-%d %H:%M:%S'
+        ]
+
+    def get_metavar(self, param):
+        return '[{}]'.format('|'.join(self.formats))
+
+    def _try_to_convert_date(self, value, format):
+        try:
+            return datetime.strptime(value, format)
+        except ValueError:
+            return None
+
+    def convert(self, value, param, ctx):
+        # Exact match
+        for format in self.formats:
+            dtime = self._try_to_convert_date(value, format)
+            if dtime:
+                return dtime
+
+        self.fail(
+            'invalid datetime format: {}. (choose from {})'.format(
+                value, ', '.join(self.formats)))
+
+    def __repr__(self):
+        return 'DateTime'
+
+
+class IntParamType(ParamType):
+    name = 'integer'
+
+    def convert(self, value, param, ctx):
+        try:
+            return int(value)
+        except (ValueError, UnicodeError):
+            self.fail('%s is not a valid integer' % value, param, ctx)
+
+    def __repr__(self):
+        return 'INT'
+
+
+class IntRange(IntParamType):
+    """A parameter that works similar to :data:`click.INT` but restricts
+    the value to fit into a range.  The default behavior is to fail if the
+    value falls outside the range, but it can also be silently clamped
+    between the two edges.
+
+    See :ref:`ranges` for an example.
+    """
+    name = 'integer range'
+
+    def __init__(self, min=None, max=None, clamp=False):
+        self.min = min
+        self.max = max
+        self.clamp = clamp
+
+    def convert(self, value, param, ctx):
+        rv = IntParamType.convert(self, value, param, ctx)
+        if self.clamp:
+            if self.min is not None and rv < self.min:
+                return self.min
+            if self.max is not None and rv > self.max:
+                return self.max
+        if self.min is not None and rv < self.min or \
+           self.max is not None and rv > self.max:
+            if self.min is None:
+                self.fail('%s is bigger than the maximum valid value '
+                          '%s.' % (rv, self.max), param, ctx)
+            elif self.max is None:
+                self.fail('%s is smaller than the minimum valid value '
+                          '%s.' % (rv, self.min), param, ctx)
+            else:
+                self.fail('%s is not in the valid range of %s to %s.'
+                          % (rv, self.min, self.max), param, ctx)
+        return rv
+
+    def __repr__(self):
+        return 'IntRange(%r, %r)' % (self.min, self.max)
+
+
+class FloatParamType(ParamType):
+    name = 'float'
+
+    def convert(self, value, param, ctx):
+        try:
+            return float(value)
+        except (UnicodeError, ValueError):
+            self.fail('%s is not a valid floating point value' %
+                      value, param, ctx)
+
+    def __repr__(self):
+        return 'FLOAT'
+
+
+class FloatRange(FloatParamType):
+    """A parameter that works similar to :data:`click.FLOAT` but restricts
+    the value to fit into a range.  The default behavior is to fail if the
+    value falls outside the range, but it can also be silently clamped
+    between the two edges.
+
+    See :ref:`ranges` for an example.
+    """
+    name = 'float range'
+
+    def __init__(self, min=None, max=None, clamp=False):
+        self.min = min
+        self.max = max
+        self.clamp = clamp
+
+    def convert(self, value, param, ctx):
+        rv = FloatParamType.convert(self, value, param, ctx)
+        if self.clamp:
+            if self.min is not None and rv < self.min:
+                return self.min
+            if self.max is not None and rv > self.max:
+                return self.max
+        if self.min is not None and rv < self.min or \
+           self.max is not None and rv > self.max:
+            if self.min is None:
+                self.fail('%s is bigger than the maximum valid value '
+                          '%s.' % (rv, self.max), param, ctx)
+            elif self.max is None:
+                self.fail('%s is smaller than the minimum valid value '
+                          '%s.' % (rv, self.min), param, ctx)
+            else:
+                self.fail('%s is not in the valid range of %s to %s.'
+                          % (rv, self.min, self.max), param, ctx)
+        return rv
+
+    def __repr__(self):
+        return 'FloatRange(%r, %r)' % (self.min, self.max)
+
+
+class BoolParamType(ParamType):
+    name = 'boolean'
+
+    def convert(self, value, param, ctx):
+        if isinstance(value, bool):
+            return bool(value)
+        value = value.lower()
+        if value in ('true', 't', '1', 'yes', 'y'):
+            return True
+        elif value in ('false', 'f', '0', 'no', 'n'):
+            return False
+        self.fail('%s is not a valid boolean' % value, param, ctx)
+
+    def __repr__(self):
+        return 'BOOL'
+
+
+class UUIDParameterType(ParamType):
+    name = 'uuid'
+
+    def convert(self, value, param, ctx):
+        import uuid
+        try:
+            if PY2 and isinstance(value, text_type):
+                value = value.encode('ascii')
+            return uuid.UUID(value)
+        except (UnicodeError, ValueError):
+            self.fail('%s is not a valid UUID value' % value, param, ctx)
+
+    def __repr__(self):
+        return 'UUID'
+
+
+class File(ParamType):
+    """Declares a parameter to be a file for reading or writing.  The file
+    is automatically closed once the context tears down (after the command
+    finished working).
+
+    Files can be opened for reading or writing.  The special value ``-``
+    indicates stdin or stdout depending on the mode.
+
+    By default, the file is opened for reading text data, but it can also be
+    opened in binary mode or for writing.  The encoding parameter can be used
+    to force a specific encoding.
+
+    The `lazy` flag controls if the file should be opened immediately or upon
+    first IO. The default is to be non-lazy for standard input and output
+    streams as well as files opened for reading, `lazy` otherwise. When opening a
+    file lazily for reading, it is still opened temporarily for validation, but
+    will not be held open until first IO. lazy is mainly useful when opening
+    for writing to avoid creating the file until it is needed.
+
+    Starting with Click 2.0, files can also be opened atomically in which
+    case all writes go into a separate file in the same folder and upon
+    completion the file will be moved over to the original location.  This
+    is useful if a file regularly read by other users is modified.
+
+    See :ref:`file-args` for more information.
+    """
+    name = 'filename'
+    envvar_list_splitter = os.path.pathsep
+
+    def __init__(self, mode='r', encoding=None, errors='strict', lazy=None,
+                 atomic=False):
+        self.mode = mode
+        self.encoding = encoding
+        self.errors = errors
+        self.lazy = lazy
+        self.atomic = atomic
+
+    def resolve_lazy_flag(self, value):
+        if self.lazy is not None:
+            return self.lazy
+        if value == '-':
+            return False
+        elif 'w' in self.mode:
+            return True
+        return False
+
+    def convert(self, value, param, ctx):
+        try:
+            if hasattr(value, 'read') or hasattr(value, 'write'):
+                return value
+
+            lazy = self.resolve_lazy_flag(value)
+
+            if lazy:
+                f = LazyFile(value, self.mode, self.encoding, self.errors,
+                             atomic=self.atomic)
+                if ctx is not None:
+                    ctx.call_on_close(f.close_intelligently)
+                return f
+
+            f, should_close = open_stream(value, self.mode,
+                                          self.encoding, self.errors,
+                                          atomic=self.atomic)
+            # If a context is provided, we automatically close the file
+            # at the end of the context execution (or flush out).  If a
+            # context does not exist, it's the caller's responsibility to
+            # properly close the file.  This for instance happens when the
+            # type is used with prompts.
+            if ctx is not None:
+                if should_close:
+                    ctx.call_on_close(safecall(f.close))
+                else:
+                    ctx.call_on_close(safecall(f.flush))
+            return f
+        except (IOError, OSError) as e:
+            self.fail('Could not open file: %s: %s' % (
+                filename_to_ui(value),
+                get_streerror(e),
+            ), param, ctx)
+
+
+class Path(ParamType):
+    """The path type is similar to the :class:`File` type but it performs
+    different checks.  First of all, instead of returning an open file
+    handle it returns just the filename.  Secondly, it can perform various
+    basic checks about what the file or directory should be.
+
+    .. versionchanged:: 6.0
+       `allow_dash` was added.
+
+    :param exists: if set to true, the file or directory needs to exist for
+                   this value to be valid.  If this is not required and a
+                   file does indeed not exist, then all further checks are
+                   silently skipped.
+    :param file_okay: controls if a file is a possible value.
+    :param dir_okay: controls if a directory is a possible value.
+    :param writable: if true, a writable check is performed.
+    :param readable: if true, a readable check is performed.
+    :param resolve_path: if this is true, then the path is fully resolved
+                         before the value is passed onwards.  This means
+                         that it's absolute and symlinks are resolved.  It
+                         will not expand a tilde-prefix, as this is
+                         supposed to be done by the shell only.
+    :param allow_dash: If this is set to `True`, a single dash to indicate
+                       standard streams is permitted.
+    :param path_type: optionally a string type that should be used to
+                      represent the path.  The default is `None` which
+                      means the return value will be either bytes or
+                      unicode depending on what makes most sense given the
+                      input data Click deals with.
+    """
+    envvar_list_splitter = os.path.pathsep
+
+    def __init__(self, exists=False, file_okay=True, dir_okay=True,
+                 writable=False, readable=True, resolve_path=False,
+                 allow_dash=False, path_type=None):
+        self.exists = exists
+        self.file_okay = file_okay
+        self.dir_okay = dir_okay
+        self.writable = writable
+        self.readable = readable
+        self.resolve_path = resolve_path
+        self.allow_dash = allow_dash
+        self.type = path_type
+
+        if self.file_okay and not self.dir_okay:
+            self.name = 'file'
+            self.path_type = 'File'
+        elif self.dir_okay and not self.file_okay:
+            self.name = 'directory'
+            self.path_type = 'Directory'
+        else:
+            self.name = 'path'
+            self.path_type = 'Path'
+
+    def coerce_path_result(self, rv):
+        if self.type is not None and not isinstance(rv, self.type):
+            if self.type is text_type:
+                rv = rv.decode(get_filesystem_encoding())
+            else:
+                rv = rv.encode(get_filesystem_encoding())
+        return rv
+
+    def convert(self, value, param, ctx):
+        rv = value
+
+        is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-')
+
+        if not is_dash:
+            if self.resolve_path:
+                rv = os.path.realpath(rv)
+
+            try:
+                st = os.stat(rv)
+            except OSError:
+                if not self.exists:
+                    return self.coerce_path_result(rv)
+                self.fail('%s "%s" does not exist.' % (
+                    self.path_type,
+                    filename_to_ui(value)
+                ), param, ctx)
+
+            if not self.file_okay and stat.S_ISREG(st.st_mode):
+                self.fail('%s "%s" is a file.' % (
+                    self.path_type,
+                    filename_to_ui(value)
+                ), param, ctx)
+            if not self.dir_okay and stat.S_ISDIR(st.st_mode):
+                self.fail('%s "%s" is a directory.' % (
+                    self.path_type,
+                    filename_to_ui(value)
+                ), param, ctx)
+            if self.writable and not os.access(value, os.W_OK):
+                self.fail('%s "%s" is not writable.' % (
+                    self.path_type,
+                    filename_to_ui(value)
+                ), param, ctx)
+            if self.readable and not os.access(value, os.R_OK):
+                self.fail('%s "%s" is not readable.' % (
+                    self.path_type,
+                    filename_to_ui(value)
+                ), param, ctx)
+
+        return self.coerce_path_result(rv)
+
+
+class Tuple(CompositeParamType):
+    """The default behavior of Click is to apply a type on a value directly.
+    This works well in most cases, except for when `nargs` is set to a fixed
+    count and different types should be used for different items.  In this
+    case the :class:`Tuple` type can be used.  This type can only be used
+    if `nargs` is set to a fixed number.
+
+    For more information see :ref:`tuple-type`.
+
+    This can be selected by using a Python tuple literal as a type.
+
+    :param types: a list of types that should be used for the tuple items.
+    """
+
+    def __init__(self, types):
+        self.types = [convert_type(ty) for ty in types]
+
+    @property
+    def name(self):
+        return "<" + " ".join(ty.name for ty in self.types) + ">"
+
+    @property
+    def arity(self):
+        return len(self.types)
+
+    def convert(self, value, param, ctx):
+        if len(value) != len(self.types):
+            raise TypeError('It would appear that nargs is set to conflict '
+                            'with the composite type arity.')
+        return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value))
+
+
+def convert_type(ty, default=None):
+    """Converts a callable or python ty into the most appropriate param
+    ty.
+    """
+    guessed_type = False
+    if ty is None and default is not None:
+        if isinstance(default, tuple):
+            ty = tuple(map(type, default))
+        else:
+            ty = type(default)
+        guessed_type = True
+
+    if isinstance(ty, tuple):
+        return Tuple(ty)
+    if isinstance(ty, ParamType):
+        return ty
+    if ty is text_type or ty is str or ty is None:
+        return STRING
+    if ty is int:
+        return INT
+    # Booleans are only okay if not guessed.  This is done because for
+    # flags the default value is actually a bit of a lie in that it
+    # indicates which of the flags is the one we want.  See get_default()
+    # for more information.
+    if ty is bool and not guessed_type:
+        return BOOL
+    if ty is float:
+        return FLOAT
+    if guessed_type:
+        return STRING
+
+    # Catch a common mistake
+    if __debug__:
+        try:
+            if issubclass(ty, ParamType):
+                raise AssertionError('Attempted to use an uninstantiated '
+                                     'parameter type (%s).' % ty)
+        except TypeError:
+            pass
+    return FuncParamType(ty)
+
+
+#: A dummy parameter type that just does nothing.  From a user's
+#: perspective this appears to just be the same as `STRING` but internally
+#: no string conversion takes place.  This is necessary to achieve the
+#: same bytes/unicode behavior on Python 2/3 in situations where you want
+#: to not convert argument types.  This is usually useful when working
+#: with file paths as they can appear in bytes and unicode.
+#:
+#: For path related uses the :class:`Path` type is a better choice but
+#: there are situations where an unprocessed type is useful which is why
+#: it is is provided.
+#:
+#: .. versionadded:: 4.0
+UNPROCESSED = UnprocessedParamType()
+
+#: A unicode string parameter type which is the implicit default.  This
+#: can also be selected by using ``str`` as type.
+STRING = StringParamType()
+
+#: An integer parameter.  This can also be selected by using ``int`` as
+#: type.
+INT = IntParamType()
+
+#: A floating point value parameter.  This can also be selected by using
+#: ``float`` as type.
+FLOAT = FloatParamType()
+
+#: A boolean parameter.  This is the default for boolean flags.  This can
+#: also be selected by using ``bool`` as a type.
+BOOL = BoolParamType()
+
+#: A UUID parameter.
+UUID = UUIDParameterType()
new file mode 100644
--- /dev/null
+++ b/third_party/python/Click/click/utils.py
@@ -0,0 +1,440 @@
+import os
+import sys
+
+from .globals import resolve_color_default
+
+from ._compat import text_type, open_stream, get_filesystem_encoding, \
+    get_streerror, string_types, PY2, binary_streams, text_streams, \
+    filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \
+    _default_text_stdout, _default_text_stderr, is_bytes, WIN
+
+if not PY2:
+    from ._compat import _find_binary_writer
+elif WIN:
+    from ._winconsole import _get_windows_argv, \
+         _hash_py_argv, _initial_argv_hash
+
+
+echo_native_types = string_types + (bytes, bytearray)
+
+
+def _posixify(name):
+    return '-'.join(name.split()).lower()
+
+
+def safecall(func):
+    """Wraps a function so that it swallows exceptions."""
+    def wrapper(*args, **kwargs):
+        try:
+            return func(*args, **kwargs)
+        except Exception:
+            pass
+    return wrapper
+
+
+def make_str(value):
+    """Converts a value into a valid string."""
+    if isinstance(value, bytes):
+        try:
+            return value.decode(get_filesystem_encoding())
+        except UnicodeError:
+            return value.decode('utf-8', 'replace')
+    return text_type(value)
+
+
+def make_default_short_help(help, max_length=45):
+    """Return a condensed version of help string."""
+    words = help.split()
+    total_length = 0
+    result = []
+    done = False
+
+    for word in words:
+        if word[-1:] == '.':
+            done = True
+        new_length = result and 1 + len(word) or len(word)
+        if total_length + new_length > max_length:
+            result.append('...')
+            done = True
+        else:
+            if result:
+                result.append(' ')
+            result.append(word)
+        if done:
+            break
+        total_length += new_length
+
+    return ''.join(result)
+
+
+class LazyFile(object):
+    """A lazy file works like a regular file but it does not fully open
+    the file but it does perform some basic checks early to see if the
+    filename parameter does make sense.  This is useful for safely opening
+    files for writing.
+    """
+
+    def __init__(self, filename, mode='r', encoding=None, errors='strict',
+                 atomic=False):
+        self.name = filename
+        self.mode = mode
+        self.encoding = encoding
+        self.errors = errors
+        self.atomic = atomic
+
+        if filename == '-':
+            self._f, self.should_close = open_stream(filename, mode,
+                                                     encoding, errors)
+        else:
+            if 'r' in mode:
+                # Open and close the file in case we're opening it for
+                # reading so that we can catch at least some errors in
+                # some cases early.
+                open(filename, mode).close()
+            self._f = None
+            self.should_close = True
+
+    def __getattr__(self, name):
+        return getattr(self.open(), name)
+
+    def __repr__(self):
+        if self._f is not None:
+            return repr(self._f)
+        return '<unopened file %r %s>' % (self.name, self.mode)
+
+    def open(self):
+        """Opens the file if it's not yet open.  This call might fail with
+        a :exc:`FileError`.  Not handling this error will produce an error
+        that Click shows.
+        """
+        if self._f is not None:
+            return self._f
+        try:
+            rv, self.should_close = open_stream(self.name, self.mode,
+                                                self.encoding,
+                                                self.errors,
+                                                atomic=self.atomic)
+        except (IOError, OSError) as e:
+            from .exceptions import FileError
+            raise FileError(self.name, hint=get_streerror(e))
+        self._f = rv
+        return rv
+
+    def close(self):
+        """Closes the underlying file, no matter what."""
+        if self._f is not None:
+            self._f.close()
+
+    def close_intelligently(self):
+        """This function only closes the file if it was opened by the lazy
+        file wrapper.  For instance this will never close stdin.
+        """
+        if self.should_close:
+            self.close()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        self.close_intelligently()
+
+    def __iter__(self):
+        self.open()
+        return iter(self._f)
+
+
+class KeepOpenFile(object):
+
+    def __init__(self, file):
+        self._file = file
+
+    def __getattr__(self, name):
+        return getattr(self._file, name)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        pass
+
+    def __repr__(self):
+        return repr(self._file)
+
+    def __iter__(self):
+        return iter(self._file)
+
+
+def echo(message=None, file=None, nl=True, err=False, color=None):
+    """Prints a message plus a newline to the given file or stdout.  On