| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- # Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- #
- # Modifications made by Cloudera are:
- # Copyright (c) 2016 Cloudera, 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. A copy of
- # the License is located at
- #
- # http://aws.amazon.com/apache2.0/
- #
- # or in the "license" file accompanying this file. This file 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.
- import contextlib
- import datetime
- import functools
- import re
- import signal
- from ccscli import LIST_TYPE
- from ccscli import OBJECT_TYPE
- from ccscli.compat import OrderedDict
- import dateutil.parser
- from dateutil.tz import tzlocal
- from dateutil.tz import tzutc
- # These are chars that do not need to be urlencoded based on rfc2986, section 2.3.
- SAFE_CHARS = '-._~'
- def get_service_module_name(service_model):
- name = service_model.service_name
- name = name.replace('Cloudera', '')
- name = name.replace('CCS', '')
- name = re.sub('\W+', '', name)
- return name
- def json_encoder(obj):
- """JSON encoder that formats datetimes as ISO8601 format."""
- if isinstance(obj, datetime.datetime):
- return obj.isoformat()
- else:
- return obj
- class CachedProperty(object):
- def __init__(self, fget):
- self._fget = fget
- def __get__(self, obj, cls):
- if obj is None:
- return self
- else:
- computed_value = self._fget(obj)
- obj.__dict__[self._fget.__name__] = computed_value
- return computed_value
- def instance_cache(func):
- """Method decorator for caching method calls to a single instance.
- **This is not a general purpose caching decorator.**
- In order to use this, you *must* provide an ``_instance_cache``
- attribute on the instance.
- This decorator is used to cache method calls. The cache is only
- scoped to a single instance though such that multiple instances
- will maintain their own cache. In order to keep things simple,
- this decorator requires that you provide an ``_instance_cache``
- attribute on your instance.
- """
- func_name = func.__name__
- @functools.wraps(func)
- def _cache_guard(self, *args, **kwargs):
- cache_key = (func_name, args)
- if kwargs:
- kwarg_items = tuple(sorted(kwargs.items()))
- cache_key = (func_name, args, kwarg_items)
- result = self._instance_cache.get(cache_key)
- if result is not None:
- return result
- result = func(self, *args, **kwargs)
- self._instance_cache[cache_key] = result
- return result
- return _cache_guard
- def parse_timestamp(value):
- """Parse a timestamp into a datetime object.
- Supported formats:
- * iso8601
- * rfc822
- * epoch (value is an integer)
- This will return a ``datetime.datetime`` object.
- """
- if isinstance(value, (int, float)):
- # Possibly an epoch time.
- return datetime.datetime.fromtimestamp(value, tzlocal())
- else:
- try:
- return datetime.datetime.fromtimestamp(float(value), tzlocal())
- except (TypeError, ValueError):
- pass
- try:
- return dateutil.parser.parse(value)
- except (TypeError, ValueError) as e:
- raise ValueError('Invalid timestamp "%s": %s' % (value, e))
- def parse_to_aware_datetime(value):
- """Converted the passed in value to a datetime object with tzinfo.
- This function can be used to normalize all timestamp inputs. This
- function accepts a number of different types of inputs, but
- will always return a datetime.datetime object with time zone
- information.
- The input param ``value`` can be one of several types:
- * A datetime object (both naive and aware)
- * An integer representing the epoch time (can also be a string
- of the integer, i.e '0', instead of 0). The epoch time is
- considered to be UTC.
- * An iso8601 formatted timestamp. This does not need to be
- a complete timestamp, it can contain just the date portion
- without the time component.
- The returned value will be a datetime object that will have tzinfo.
- If no timezone info was provided in the input value, then UTC is
- assumed, not local time.
- """
- # This is a general purpose method that handles several cases of
- # converting the provided value to a string timestamp suitable to be
- # serialized to an http request. It can handle:
- # 1) A datetime.datetime object.
- if isinstance(value, datetime.datetime):
- datetime_obj = value
- else:
- # 2) A string object that's formatted as a timestamp.
- # We document this as being an iso8601 timestamp, although
- # parse_timestamp is a bit more flexible.
- datetime_obj = parse_timestamp(value)
- if datetime_obj.tzinfo is None:
- # I think a case would be made that if no time zone is provided,
- # we should use the local time. However, to restore backwards
- # compat, the previous behavior was to assume UTC, which is
- # what we're going to do here.
- datetime_obj = datetime_obj.replace(tzinfo=tzutc())
- else:
- datetime_obj = datetime_obj.astimezone(tzutc())
- return datetime_obj
- @contextlib.contextmanager
- def ignore_ctrl_c():
- original = signal.signal(signal.SIGINT, signal.SIG_IGN)
- try:
- yield
- finally:
- signal.signal(signal.SIGINT, original)
- def datetime2timestamp(dt, default_timezone=None):
- """Calculate the timestamp based on the given datetime instance.
- :type dt: datetime
- :param dt: A datetime object to be converted into timestamp
- :type default_timezone: tzinfo
- :param default_timezone: If it is provided as None, we treat it as tzutc().
- But it is only used when dt is a naive datetime.
- :returns: The timestamp
- """
- epoch = datetime.datetime(1970, 1, 1)
- if dt.tzinfo is None:
- if default_timezone is None:
- default_timezone = tzutc()
- dt = dt.replace(tzinfo=default_timezone)
- d = dt.replace(tzinfo=None) - dt.utcoffset() - epoch
- if hasattr(d, "total_seconds"):
- return d.total_seconds() # Works in Python 2.7+
- return (d.microseconds + (d.seconds + d.days * 24 * 3600) * 10**6) / 10**6
- class ArgumentGenerator(object):
- """Generate sample input based on a shape model.
- This class contains a ``generate_skeleton`` method that will take
- an input shape (created from ``ccscli.model``) and generate
- a sample dictionary corresponding to the input shape.
- The specific values used are place holder values. For strings an
- empty string is used, for numbers 0 or 0.0 is used. For datetime a date in
- RFC822 is used. The intended usage of this class is to generate the *shape*
- of the input object. In the future we might take the defaults from the
- model.
- This can be useful for operations that have complex input shapes.
- This allows a user to just fill in the necessary data instead of
- worrying about the specific object of the input arguments.
- Example usage::
- clidriver = CLIDriver
- ddb = clidriver.get_service_model('mastodon')
- arg_gen = ArgumentGenerator()
- sample_input = arg_gen.generate_skeleton(
- ddb.operation_model('createCluster').input_shape)
- print("Sample input for mastodon.createCluster: %s" % sample_input)
- """
- def __init__(self):
- pass
- def generate_skeleton(self, shape):
- if shape.type_name == OBJECT_TYPE:
- return self._generate_type_object(shape)
- elif shape.type_name == LIST_TYPE:
- return self._generate_type_array(shape)
- elif shape.type_name == 'string':
- return ''
- elif shape.type_name in ['integer']:
- return 0
- elif shape.type_name == 'number':
- return 0.0
- elif shape.type_name == 'boolean':
- return True
- elif shape.type_name == 'datetime':
- return 'Wed, 02 Oct 2002 13:00:00 GMT'
- else:
- raise Exception("Unknown shape type: %s" % shape.type_name)
- def _generate_type_object(self, shape):
- skeleton = OrderedDict()
- for member_name, member_shape in shape.members.items():
- skeleton[member_name] = self.generate_skeleton(member_shape)
- return skeleton
- def _generate_type_array(self, shape):
- # For list elements we've arbitrarily decided to return the first
- # element for the skeleton list.
- return [
- self.generate_skeleton(shape.member),
- ]
|