| Index: tools/telemetry/third_party/gsutilz/gslib/tab_complete.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/gslib/tab_complete.py b/tools/telemetry/third_party/gsutilz/gslib/tab_complete.py
|
| deleted file mode 100644
|
| index cd99a25d4a71e2be4a1c448ed35e6c305d1a565e..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/gsutilz/gslib/tab_complete.py
|
| +++ /dev/null
|
| @@ -1,337 +0,0 @@
|
| -# -*- coding: utf-8 -*-
|
| -# Copyright 2014 Google Inc. All Rights Reserved.
|
| -#
|
| -# Licensed under the Apache License, Version 2.0 (the "License");
|
| -# you may not use this file except in compliance with the License.
|
| -# You may obtain a copy of the License at
|
| -#
|
| -# http://www.apache.org/licenses/LICENSE-2.0
|
| -#
|
| -# Unless required by applicable law or agreed to in writing, software
|
| -# distributed under the License is distributed on an "AS IS" BASIS,
|
| -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -# See the License for the specific language governing permissions and
|
| -# limitations under the License.
|
| -"""Shell tab completion."""
|
| -
|
| -import itertools
|
| -import json
|
| -import threading
|
| -import time
|
| -
|
| -import boto
|
| -
|
| -from boto.gs.acl import CannedACLStrings
|
| -from gslib.storage_url import IsFileUrlString
|
| -from gslib.storage_url import StorageUrlFromString
|
| -from gslib.storage_url import StripOneSlash
|
| -from gslib.util import GetTabCompletionCacheFilename
|
| -from gslib.util import GetTabCompletionLogFilename
|
| -from gslib.wildcard_iterator import CreateWildcardIterator
|
| -
|
| -TAB_COMPLETE_CACHE_TTL = 15
|
| -
|
| -_TAB_COMPLETE_MAX_RESULTS = 1000
|
| -
|
| -_TIMEOUT_WARNING = """
|
| -Tab completion aborted (took >%ss), you may complete the command manually.
|
| -The timeout can be adjusted in the gsutil configuration file.
|
| -""".rstrip()
|
| -
|
| -
|
| -class CompleterType(object):
|
| - CLOUD_BUCKET = 'cloud_bucket'
|
| - CLOUD_OBJECT = 'cloud_object'
|
| - CLOUD_OR_LOCAL_OBJECT = 'cloud_or_local_object'
|
| - LOCAL_OBJECT = 'local_object'
|
| - LOCAL_OBJECT_OR_CANNED_ACL = 'local_object_or_canned_acl'
|
| - NO_OP = 'no_op'
|
| -
|
| -
|
| -class LocalObjectCompleter(object):
|
| - """Completer object for local files."""
|
| -
|
| - def __init__(self):
|
| - # This is only safe to import if argcomplete is present in the install
|
| - # (which happens for Cloud SDK installs), so import on usage, not on load.
|
| - # pylint: disable=g-import-not-at-top
|
| - from argcomplete.completers import FilesCompleter
|
| - self.files_completer = FilesCompleter()
|
| -
|
| - def __call__(self, prefix, **kwargs):
|
| - return self.files_completer(prefix, **kwargs)
|
| -
|
| -
|
| -class LocalObjectOrCannedACLCompleter(object):
|
| - """Completer object for local files and canned ACLs.
|
| -
|
| - Currently, only Google Cloud Storage canned ACL names are supported.
|
| - """
|
| -
|
| - def __init__(self):
|
| - self.local_object_completer = LocalObjectCompleter()
|
| -
|
| - def __call__(self, prefix, **kwargs):
|
| - local_objects = self.local_object_completer(prefix, **kwargs)
|
| - canned_acls = [acl for acl in CannedACLStrings if acl.startswith(prefix)]
|
| - return local_objects + canned_acls
|
| -
|
| -
|
| -class TabCompletionCache(object):
|
| - """Cache for tab completion results."""
|
| -
|
| - def __init__(self, prefix, results, timestamp, partial_results):
|
| - self.prefix = prefix
|
| - self.results = results
|
| - self.timestamp = timestamp
|
| - self.partial_results = partial_results
|
| -
|
| - @staticmethod
|
| - def LoadFromFile(filename):
|
| - """Instantiates the cache from a file.
|
| -
|
| - Args:
|
| - filename: The file to load.
|
| - Returns:
|
| - TabCompletionCache instance with loaded data or an empty cache
|
| - if the file cannot be loaded
|
| - """
|
| - try:
|
| - with open(filename, 'r') as fp:
|
| - cache_dict = json.loads(fp.read())
|
| - prefix = cache_dict['prefix']
|
| - results = cache_dict['results']
|
| - timestamp = cache_dict['timestamp']
|
| - partial_results = cache_dict['partial-results']
|
| - except Exception: # pylint: disable=broad-except
|
| - # Guarding against incompatible format changes in the cache file.
|
| - # Erring on the side of not breaking tab-completion in case of cache
|
| - # issues.
|
| - prefix = None
|
| - results = []
|
| - timestamp = 0
|
| - partial_results = False
|
| -
|
| - return TabCompletionCache(prefix, results, timestamp, partial_results)
|
| -
|
| - def GetCachedResults(self, prefix):
|
| - """Returns the cached results for prefix or None if not in cache."""
|
| - current_time = time.time()
|
| - if current_time - self.timestamp >= TAB_COMPLETE_CACHE_TTL:
|
| - return None
|
| -
|
| - results = None
|
| -
|
| - if prefix == self.prefix:
|
| - results = self.results
|
| - elif (not self.partial_results and prefix.startswith(self.prefix)
|
| - and prefix.count('/') == self.prefix.count('/')):
|
| - results = [x for x in self.results if x.startswith(prefix)]
|
| -
|
| - if results is not None:
|
| - # Update cache timestamp to make sure the cache entry does not expire if
|
| - # the user is performing multiple completions in a single
|
| - # bucket/subdirectory since we can answer these requests from the cache.
|
| - # e.g. gs://prefix<tab> -> gs://prefix-mid<tab> -> gs://prefix-mid-suffix
|
| - self.timestamp = time.time()
|
| - return results
|
| -
|
| - def UpdateCache(self, prefix, results, partial_results):
|
| - """Updates the in-memory cache with the results for the given prefix."""
|
| - self.prefix = prefix
|
| - self.results = results
|
| - self.partial_results = partial_results
|
| - self.timestamp = time.time()
|
| -
|
| - def WriteToFile(self, filename):
|
| - """Writes out the cache to the given file."""
|
| - json_str = json.dumps({
|
| - 'prefix': self.prefix,
|
| - 'results': self.results,
|
| - 'partial-results': self.partial_results,
|
| - 'timestamp': self.timestamp,
|
| - })
|
| -
|
| - try:
|
| - with open(filename, 'w') as fp:
|
| - fp.write(json_str)
|
| - except IOError:
|
| - pass
|
| -
|
| -
|
| -class CloudListingRequestThread(threading.Thread):
|
| - """Thread that performs a listing request for the given URL string."""
|
| -
|
| - def __init__(self, wildcard_url_str, gsutil_api):
|
| - """Instantiates Cloud listing request thread.
|
| -
|
| - Args:
|
| - wildcard_url_str: The URL to list.
|
| - gsutil_api: gsutil Cloud API instance to use.
|
| - """
|
| - super(CloudListingRequestThread, self).__init__()
|
| - self.daemon = True
|
| - self._wildcard_url_str = wildcard_url_str
|
| - self._gsutil_api = gsutil_api
|
| - self.results = None
|
| -
|
| - def run(self):
|
| - it = CreateWildcardIterator(
|
| - self._wildcard_url_str, self._gsutil_api).IterAll(
|
| - bucket_listing_fields=['name'])
|
| - self.results = [
|
| - str(c) for c in itertools.islice(it, _TAB_COMPLETE_MAX_RESULTS)]
|
| -
|
| -
|
| -class TimeoutError(Exception):
|
| - pass
|
| -
|
| -
|
| -class CloudObjectCompleter(object):
|
| - """Completer object for Cloud URLs."""
|
| -
|
| - def __init__(self, gsutil_api, bucket_only=False):
|
| - """Instantiates completer for Cloud URLs.
|
| -
|
| - Args:
|
| - gsutil_api: gsutil Cloud API instance to use.
|
| - bucket_only: Whether the completer should only match buckets.
|
| - """
|
| - self._gsutil_api = gsutil_api
|
| - self._bucket_only = bucket_only
|
| -
|
| - def _PerformCloudListing(self, wildcard_url, timeout):
|
| - """Perform a remote listing request for the given wildcard URL.
|
| -
|
| - Args:
|
| - wildcard_url: The wildcard URL to list.
|
| - timeout: Time limit for the request.
|
| - Returns:
|
| - Cloud resources matching the given wildcard URL.
|
| - Raises:
|
| - TimeoutError: If the listing does not finish within the timeout.
|
| - """
|
| - request_thread = CloudListingRequestThread(wildcard_url, self._gsutil_api)
|
| - request_thread.start()
|
| - request_thread.join(timeout)
|
| -
|
| - if request_thread.is_alive():
|
| - # This is only safe to import if argcomplete is present in the install
|
| - # (which happens for Cloud SDK installs), so import on usage, not on load.
|
| - # pylint: disable=g-import-not-at-top
|
| - import argcomplete
|
| - argcomplete.warn(_TIMEOUT_WARNING % timeout)
|
| - raise TimeoutError()
|
| -
|
| - results = request_thread.results
|
| -
|
| - return results
|
| -
|
| - def __call__(self, prefix, **kwargs):
|
| - if not prefix:
|
| - prefix = 'gs://'
|
| - elif IsFileUrlString(prefix):
|
| - return []
|
| -
|
| - wildcard_url = prefix + '*'
|
| - url = StorageUrlFromString(wildcard_url)
|
| - if self._bucket_only and not url.IsBucket():
|
| - return []
|
| -
|
| - timeout = boto.config.getint('GSUtil', 'tab_completion_timeout', 5)
|
| - if timeout == 0:
|
| - return []
|
| -
|
| - start_time = time.time()
|
| -
|
| - cache = TabCompletionCache.LoadFromFile(GetTabCompletionCacheFilename())
|
| - cached_results = cache.GetCachedResults(prefix)
|
| -
|
| - timing_log_entry_type = ''
|
| - if cached_results is not None:
|
| - results = cached_results
|
| - timing_log_entry_type = ' (from cache)'
|
| - else:
|
| - try:
|
| - results = self._PerformCloudListing(wildcard_url, timeout)
|
| - if self._bucket_only and len(results) == 1:
|
| - results = [StripOneSlash(results[0])]
|
| - partial_results = (len(results) == _TAB_COMPLETE_MAX_RESULTS)
|
| - cache.UpdateCache(prefix, results, partial_results)
|
| - except TimeoutError:
|
| - timing_log_entry_type = ' (request timeout)'
|
| - results = []
|
| -
|
| - cache.WriteToFile(GetTabCompletionCacheFilename())
|
| -
|
| - end_time = time.time()
|
| - num_results = len(results)
|
| - elapsed_seconds = end_time - start_time
|
| - _WriteTimingLog(
|
| - '%s results%s in %.2fs, %.2f results/second for prefix: %s\n' %
|
| - (num_results, timing_log_entry_type, elapsed_seconds,
|
| - num_results / elapsed_seconds, prefix))
|
| -
|
| - return results
|
| -
|
| -
|
| -class CloudOrLocalObjectCompleter(object):
|
| - """Completer object for Cloud URLs or local files.
|
| -
|
| - Invokes the Cloud object completer if the input looks like a Cloud URL and
|
| - falls back to local file completer otherwise.
|
| - """
|
| -
|
| - def __init__(self, gsutil_api):
|
| - self.cloud_object_completer = CloudObjectCompleter(gsutil_api)
|
| - self.local_object_completer = LocalObjectCompleter()
|
| -
|
| - def __call__(self, prefix, **kwargs):
|
| - if IsFileUrlString(prefix):
|
| - completer = self.local_object_completer
|
| - else:
|
| - completer = self.cloud_object_completer
|
| - return completer(prefix, **kwargs)
|
| -
|
| -
|
| -class NoOpCompleter(object):
|
| - """Completer that always returns 0 results."""
|
| -
|
| - def __call__(self, unused_prefix, **unused_kwargs):
|
| - return []
|
| -
|
| -
|
| -def MakeCompleter(completer_type, gsutil_api):
|
| - """Create a completer instance of the given type.
|
| -
|
| - Args:
|
| - completer_type: The type of completer to create.
|
| - gsutil_api: gsutil Cloud API instance to use.
|
| - Returns:
|
| - A completer instance.
|
| - Raises:
|
| - RuntimeError: if completer type is not supported.
|
| - """
|
| - if completer_type == CompleterType.CLOUD_OR_LOCAL_OBJECT:
|
| - return CloudOrLocalObjectCompleter(gsutil_api)
|
| - elif completer_type == CompleterType.LOCAL_OBJECT:
|
| - return LocalObjectCompleter()
|
| - elif completer_type == CompleterType.LOCAL_OBJECT_OR_CANNED_ACL:
|
| - return LocalObjectOrCannedACLCompleter()
|
| - elif completer_type == CompleterType.CLOUD_BUCKET:
|
| - return CloudObjectCompleter(gsutil_api, bucket_only=True)
|
| - elif completer_type == CompleterType.CLOUD_OBJECT:
|
| - return CloudObjectCompleter(gsutil_api)
|
| - elif completer_type == CompleterType.NO_OP:
|
| - return NoOpCompleter()
|
| - else:
|
| - raise RuntimeError(
|
| - 'Unknown completer "%s"' % completer_type)
|
| -
|
| -
|
| -def _WriteTimingLog(message):
|
| - """Write an entry to the tab completion timing log, if it's enabled."""
|
| - if boto.config.getbool('GSUtil', 'tab_completion_time_logs', False):
|
| - with open(GetTabCompletionLogFilename(), 'ab') as fp:
|
| - fp.write(message)
|
| -
|
|
|