<commit_before>"""f90nml.py
Parse fortran namelist files into dicts of standard Python data types.
Contact: Marshall Ward <python@marshallward.org>
---
Distributed as part of f90nml, Copyright 2014 Marshall Ward
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
"""

from collections import OrderedDict
import itertools
import os
import shlex

__version__ = '0.3'

#---
def read(nml_fname, assume_kind_type=False, verbose=False):
    """Parse a Fortran 90 namelist file and store the contents in a ``dict``.

    >>> data_nml = f90nml.read('data.nml')"""

    nml_file = open(nml_fname, 'r')

    f90 = shlex.shlex(nml_file)
    f90.commenters = '!'
    f90.escapedquotes = '\'"'
    f90.wordchars += '.-+'      # Include floating point characters
    tokens = iter(f90)

    # Store groups in case-insensitive dictionary
    nmls = NmlDict()

    for t in tokens:

        # Ignore tokens outside of namelist groups
        while t != '&':
            t, prior_t = next(tokens), t

        # Current token is now '&'

        # Create the next namelist
        g_name = next(tokens)
        g_vars = NmlDict()

        v_name = None
        v_idx = None
        v_vals = []

        # Current token is either a variable name or finalizer (/, &)

        # Populate the namelist group
        while g_name:

            t, prior_t = next(tokens), t

            # Skip commas separating objects
            if t == ',':
                t, prior_t = next(tokens), t

            # Diagnostic testing
            if verbose:
                print('  tokens: {} {}'.format(prior_t, t))

            while v_name:

                t, prior_t = next(tokens), t

                if verbose:
                    print('    vstate: {} {} {}'.format(v_name, v_idx, v_vals))
                    print('    tokens: {} {}'.format(prior_t, t))

                # Parse the prior token value

                ## TODO: This is miserable, refactor this
                ## Skip variable names and end commas
                #if not (t == '=' or (t == '(' and not prior_t == '=')
                #        or (prior_t, t) == (',', '/')
                #        or (prior_t, t) == ('=', '/')
                #        or (prior_t, t) == (',', '&')
                #        or prior_t == ')'):

                # Save then deactivate the current variable
                # TODO: Add '%'
                if t in ('(', '=', '/', '&'):

                    if len(v_vals) == 0:
                        v_vals = None
                    elif len(v_vals) == 1:
                        v_vals = v_vals[0]

                    g_vars[v_name] = v_vals

                    v_name = None
                    v_vals = []

                else:
                    # Parse the variable string
                    if prior_t == '=':
                        continue
                    elif prior_t == ',':
                        next_value = None
                    else:
                        next_value, t = parse_f90val(tokens, t, prior_t)

                    if v_idx:

                        v_i = next(v_idx)

                        if v_name in g_vars:
                            v_vals = g_vars[v_name]
                            if type(v_vals) != list:
                                v_vals = [v_vals]

                        try:
                            # Default Fortran indexing starts at 1
                            v_vals[v_i - 1] = next_value
                        except IndexError:
                            # Expand list to accomodate out-of-range indices
                            size = len(v_vals)
                            v_vals.extend(None for i in range(size, v_i))
                            v_vals[v_i - 1] = next_value
                    else:
                        v_vals.append(next_value)

            # Set the next active variable
            # TODO: Add '%' to the list
            if t in ('=', '('):
                v_name = prior_t
                v_idx = None

                # Parse the indices of the current variable
                if t == '(':
                    v_name, v_indices, t = parse_f90idx(tokens, t, prior_t)

                    # TODO: Multidimensional support
                    # TODO: End index support (currently ignored)
                    i_s = 1 if not v_indices[0][0] else v_indices[0][0]
                    i_r = 1 if not v_indices[0][2] else v_indices[0][2]
                    v_idx = itertools.count(i_s, i_r)

            # Finalise namelist group
            if t in ('/', '&'):
                # Test for classic namelist finaliser
                if t == '&':
                    t, prior_t = next(tokens), t
                    assert t.lower() == 'end'

                # Append the grouplist to the namelist (including empty groups)
                nmls[g_name] = g_vars
                g_name, g_vars = None, None

    nml_file.close()

    return nmls

#---
def write(nml, nml_fname, force=False):
    """Output dict to a Fortran 90 namelist file."""

    if not force and os.path.isfile(nml_fname):
        raise IOError('File {} already exists.'.format(nml_fname))

    nml_file = open(nml_fname, 'w')

    for grp in nml.keys():
        nml_file.write('&{}\n'.format(grp))

        grp_vars = nml[grp]
        for v_name in grp_vars.keys():

            v_val = grp_vars[v_name]

            if type(v_val) == list:
                v_str = ', '.join([to_f90str(v) for v in v_val])
            else:
                v_str = to_f90str(v_val)

            nml_file.write('    {} = {}\n'.format(v_name, v_str))

        nml_file.write('/\n')

    nml_file.close()

#---
def to_f90str(value):
    """Convert primitive Python types to equivalent Fortran strings"""

    if type(value) is int:
        return str(value)
    elif type(value) is float:
        return str(value)
    elif type(value) is bool:
        return '.{}.'.format(str(value).lower())
    elif type(value) is complex:
        return '({}, {})'.format(value.real, value.imag)
    elif type(value) is str:
        return '\'{}\''.format(value)
    elif value is None:
        return ''
    else:
        raise ValueError('Type {} of {} cannot be converted to a Fortran type.'
                         ''.format(type(value), value))

#---
def parse_f90val(tokens, t, s, assume_kind_type=False):
    """Convert string repr of Fortran type to equivalent Python type."""
    assert type(s) is str

    # Construct the complex string
    if s == '(':
        s_re = t
        next(tokens)
        s_im = next(tokens)

        t = next(tokens)

        s = '({}, {})'.format(s_re, s_im)

    recast_funcs = [int, f90float, f90complex, f90bool, f90str]

    for f90type in recast_funcs:
        try:
            if assume_kind_type and f90type == int:
                value = f90type(s.rpartition('_')[0])
            elif assume_kind_type and f90type in (f90float, f90complex):
                value = f90type(s, assume_kind_type)
            else:
                value = f90type(s)
            return value, t
        except ValueError:
            continue

    # If all test failed, then raise ValueError
    raise ValueError('Could not convert {} to a Python data type.'.format(s))

#---
def f90float(s, assume_kind_type=False):
    """Convert string repr of Fortran floating point to Python double"""

    if assume_kind_type:
        s, _, _ = s.rpartition('_')

    return float(s.lower().replace('d', 'e'))

#---
def f90complex(s, assume_kind_type=False):
    """Convert string repr of Fortran complex to Python complex."""
    assert type(s) == str

    if s[0] == '(' and s[-1] == ')' and len(s.split(',')) == 2:
        s_re, s_im = s[1:-1].split(',', 1)

        # NOTE: Failed float(str) will raise ValueError
        return complex(f90float(s_re, assume_kind_type),
                       f90float(s_im, assume_kind_type))
    else:
        raise ValueError('{} must be in complex number form (x, y)'.format(s))

#---
def f90bool(s):
    """Convert string repr of Fortran logical to Python logical."""
    assert type(s) == str

    try:
        s_bool = s[1].lower() if s.startswith('.') else s[0].lower()
    except IndexError:
        raise ValueError('{} is not a valid logical constant.'.format(s))

    if s_bool == 't':
        return True
    elif s_bool == 'f':
        return False
    else:
        raise ValueError('{} is not a valid logical constant.'.format(s))

#---
def f90str(s):
    """Convert string repr of Fortran string to Python string."""
    assert type(s) == str

    f90quotes = ["'", '"']

    if s[0] in f90quotes and s[-1] in f90quotes:
        return s[1:-1]

    raise ValueError

#---
def parse_f90idx(tokens, t, prior_t):
    """Parse Fortran vector indices into a tuple of Python indices."""

    idx_end = (',', ')')

    v_name = prior_t
    v_indices = []
    i_start = i_end = i_stride = None

    # Start index
    t = next(tokens)
    try:
        i_start = int(t)
        t = next(tokens)
    except ValueError:
        if t in idx_end:
            raise ValueError('{} index cannot be empty.'
                             ''.format(v_name))
        elif not t == ':':
            raise

    # End index
    if t == ':':
        t = next(tokens)
        try:
            i_end = int(t)
            t = next(tokens)
        except ValueError:
            if t == ':':
                raise ValueError('{} end index cannot be implicit '
                                 'when using stride.'
                                 ''.format(v_name))
            elif not t in idx_end:
                raise
    elif t in idx_end:
        # Replace index with single-index range
        if i_start:
            i_end = i_start

    # Stride index
    if t == ':':
        t = next(tokens)
        try:
            i_stride = int(t)
        except ValueError:
            if t == ')':
                raise ValueError('{} stride index cannot be '
                                 'implicit.'.format(v_name))
            else:
                raise

        if i_stride == 0:
            raise ValueError('{} stride index cannot be zero.'
                             ''.format(v_name))

        t = next(tokens)

    if not t in idx_end:
        raise ValueError('{} index did not terminate '
                         'correctly.'.format(v_name))

    idx_triplet = (i_start, i_end, i_stride)
    v_indices.append((idx_triplet))
    t = next(tokens)

    return v_name, v_indices, t

#---
class NmlDict(OrderedDict):
    """Case-insensitive Python dict"""
    def __setitem__(self, key, value):
        super(NmlDict, self).__setitem__(key.lower(), value)

    def __getitem__(self, key):
        return super(NmlDict, self).__getitem__(key.lower())

    def write(self, path, force=False):
        """Wrapper to the ``write`` method"""
        write(self, path, force)
<commit_msg>Happy case works<commit_after>"""f90nml.py
Parse fortran namelist files into dicts of standard Python data types.
Contact: Marshall Ward <python@marshallward.org>
---
Distributed as part of f90nml, Copyright 2014 Marshall Ward
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
"""

from collections import OrderedDict
import itertools
import os
import shlex

__version__ = '0.3'

#---
def read(nml_fname, assume_kind_type=False, verbose=False):
    """Parse a Fortran 90 namelist file and store the contents in a ``dict``.

    >>> data_nml = f90nml.read('data.nml')"""

    nml_file = open(nml_fname, 'r')

    f90lex = shlex.shlex(nml_file)
    f90lex.commenters = '!'
    f90lex.escapedquotes = '\'"'
    f90lex.wordchars += '.-+'      # Include floating point characters
    tokens = iter(f90lex)

    # Store groups in case-insensitive dictionary
    nmls = NmlDict()

    for t in tokens:

        # Ignore tokens outside of namelist groups
        while t != '&':
            t, prior_t = next(tokens), t

        # Current token is now '&'

        # Create the next namelist
        g_name = next(tokens)
        g_vars = NmlDict()

        v_name = None
        v_idx = None
        v_vals = []

        # Current token is either a variable name or finalizer (/, &)

        # Populate the namelist group
        while g_name:

            t, prior_t = next(tokens), t

            # Skip commas separating objects
            if t == ',':
                t, prior_t = next(tokens), t

            # Diagnostic testing
            if verbose:
                print('  tokens: {} {}'.format(prior_t, t))

            while v_name:

                t, prior_t = next(tokens), t

                if verbose:
                    print('    vstate: {} {} {}'.format(v_name, v_idx, v_vals))
                    print('    tokens: {} {}'.format(prior_t, t))

                # Parse the prior token value
                # TODO: Add '%'
                if not t in ('(', '='):
                    # Parse the variable string
                    if prior_t == '=':
                        continue
                    elif prior_t == ',':
                        next_value = None
                    else:
                        next_value, t = parse_f90val(tokens, t, prior_t)

                    if v_idx:

                        v_i = next(v_idx)

                        if v_name in g_vars:
                            v_vals = g_vars[v_name]
                            if type(v_vals) != list:
                                v_vals = [v_vals]

                        try:
                            # Default Fortran indexing starts at 1
                            v_vals[v_i - 1] = next_value
                        except IndexError:
                            # Expand list to accomodate out-of-range indices
                            size = len(v_vals)
                            v_vals.extend(None for i in range(size, v_i))
                            v_vals[v_i - 1] = next_value
                    else:
                        v_vals.append(next_value)

                # Save then deactivate the current variable
                # TODO: Add '%'
                if t in ('(', '=', '/', '&'):

                    if len(v_vals) == 0:
                        v_vals = None
                    elif len(v_vals) == 1:
                        v_vals = v_vals[0]

                    g_vars[v_name] = v_vals

                    v_name = None
                    v_vals = []

            # Set the next active variable
            # TODO: Add '%' to the list
            if t in ('=', '('):
                v_name = prior_t
                v_idx = None

                # Parse the indices of the current variable
                if t == '(':
                    v_name, v_indices, t = parse_f90idx(tokens, t, prior_t)

                    # TODO: Multidimensional support
                    # TODO: End index support (currently ignored)
                    i_s = 1 if not v_indices[0][0] else v_indices[0][0]
                    i_r = 1 if not v_indices[0][2] else v_indices[0][2]
                    v_idx = itertools.count(i_s, i_r)

            # Finalise namelist group
            if t in ('/', '&'):
                # Test for classic namelist finaliser
                if t == '&':
                    t, prior_t = next(tokens), t
                    assert t.lower() == 'end'

                # Append the grouplist to the namelist (including empty groups)
                nmls[g_name] = g_vars
                g_name, g_vars = None, None

    nml_file.close()

    return nmls

#---
def write(nml, nml_fname, force=False):
    """Output dict to a Fortran 90 namelist file."""

    if not force and os.path.isfile(nml_fname):
        raise IOError('File {} already exists.'.format(nml_fname))

    nml_file = open(nml_fname, 'w')

    for grp in nml.keys():
        nml_file.write('&{}\n'.format(grp))

        grp_vars = nml[grp]
        for v_name in grp_vars.keys():

            v_val = grp_vars[v_name]

            if type(v_val) == list:
                v_str = ', '.join([to_f90str(v) for v in v_val])
            else:
                v_str = to_f90str(v_val)

            nml_file.write('    {} = {}\n'.format(v_name, v_str))

        nml_file.write('/\n')

    nml_file.close()

#---
def to_f90str(value):
    """Convert primitive Python types to equivalent Fortran strings"""

    if type(value) is int:
        return str(value)
    elif type(value) is float:
        return str(value)
    elif type(value) is bool:
        return '.{}.'.format(str(value).lower())
    elif type(value) is complex:
        return '({}, {})'.format(value.real, value.imag)
    elif type(value) is str:
        return '\'{}\''.format(value)
    elif value is None:
        return ''
    else:
        raise ValueError('Type {} of {} cannot be converted to a Fortran type.'
                         ''.format(type(value), value))

#---
def parse_f90val(tokens, t, s, assume_kind_type=False):
    """Convert string repr of Fortran type to equivalent Python type."""
    assert type(s) is str

    # Construct the complex string
    if s == '(':
        s_re = t
        next(tokens)
        s_im = next(tokens)

        # Bypass the right parenthesis
        t = next(tokens)
        assert t == ')'

        t = next(tokens)

        s = '({}, {})'.format(s_re, s_im)

    recast_funcs = [int, f90float, f90complex, f90bool, f90str]

    for f90type in recast_funcs:
        try:
            if assume_kind_type and f90type == int:
                value = f90type(s.rpartition('_')[0])
            elif assume_kind_type and f90type in (f90float, f90complex):
                value = f90type(s, assume_kind_type)
            else:
                value = f90type(s)
            return value, t
        except ValueError:
            continue

    # If all test failed, then raise ValueError
    raise ValueError('Could not convert {} to a Python data type.'.format(s))

#---
def f90float(s, assume_kind_type=False):
    """Convert string repr of Fortran floating point to Python double"""

    if assume_kind_type:
        s, _, _ = s.rpartition('_')

    return float(s.lower().replace('d', 'e'))

#---
def f90complex(s, assume_kind_type=False):
    """Convert string repr of Fortran complex to Python complex."""
    assert type(s) == str

    if s[0] == '(' and s[-1] == ')' and len(s.split(',')) == 2:
        s_re, s_im = s[1:-1].split(',', 1)

        # NOTE: Failed float(str) will raise ValueError
        return complex(f90float(s_re, assume_kind_type),
                       f90float(s_im, assume_kind_type))
    else:
        raise ValueError('{} must be in complex number form (x, y)'.format(s))

#---
def f90bool(s):
    """Convert string repr of Fortran logical to Python logical."""
    assert type(s) == str

    try:
        s_bool = s[1].lower() if s.startswith('.') else s[0].lower()
    except IndexError:
        raise ValueError('{} is not a valid logical constant.'.format(s))

    if s_bool == 't':
        return True
    elif s_bool == 'f':
        return False
    else:
        raise ValueError('{} is not a valid logical constant.'.format(s))

#---
def f90str(s):
    """Convert string repr of Fortran string to Python string."""
    assert type(s) == str

    f90quotes = ["'", '"']

    if s[0] in f90quotes and s[-1] in f90quotes:
        return s[1:-1]

    raise ValueError

#---
def parse_f90idx(tokens, t, prior_t):
    """Parse Fortran vector indices into a tuple of Python indices."""

    idx_end = (',', ')')

    v_name = prior_t
    v_indices = []
    i_start = i_end = i_stride = None

    # Start index
    t = next(tokens)
    try:
        i_start = int(t)
        t = next(tokens)
    except ValueError:
        if t in idx_end:
            raise ValueError('{} index cannot be empty.'
                             ''.format(v_name))
        elif not t == ':':
            raise

    # End index
    if t == ':':
        t = next(tokens)
        try:
            i_end = int(t)
            t = next(tokens)
        except ValueError:
            if t == ':':
                raise ValueError('{} end index cannot be implicit '
                                 'when using stride.'
                                 ''.format(v_name))
            elif not t in idx_end:
                raise
    elif t in idx_end:
        # Replace index with single-index range
        if i_start:
            i_end = i_start

    # Stride index
    if t == ':':
        t = next(tokens)
        try:
            i_stride = int(t)
        except ValueError:
            if t == ')':
                raise ValueError('{} stride index cannot be '
                                 'implicit.'.format(v_name))
            else:
                raise

        if i_stride == 0:
            raise ValueError('{} stride index cannot be zero.'
                             ''.format(v_name))

        t = next(tokens)

    if not t in idx_end:
        raise ValueError('{} index did not terminate '
                         'correctly.'.format(v_name))

    idx_triplet = (i_start, i_end, i_stride)
    v_indices.append((idx_triplet))
    t = next(tokens)

    return v_name, v_indices, t

#---
class NmlDict(OrderedDict):
    """Case-insensitive Python dict"""
    def __setitem__(self, key, value):
        super(NmlDict, self).__setitem__(key.lower(), value)

    def __getitem__(self, key):
        return super(NmlDict, self).__getitem__(key.lower())

    def write(self, path, force=False):
        """Wrapper to the ``write`` method"""
        write(self, path, force)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
.. :doctest:

"""

import datetime
import time

from zope.interface import classProvides, implements
from zope.component import queryUtility

from .interfaces import ITime, IClock
from .timezone import UTC, TzLocal
from sact.epoch.utils import datetime_to_timestamp

def round_date(date):
    """Round a timedelta to the last minute (remove seconds)

    Setup:

     >>> from sact.epoch import round_date, Time

    Nothing to do:

     >>> round_date(Time(2010, 1, 1, 1, 1, 0))
     <Time 2010-01-01 01:01:00+00:00>

    Round to 1 minute when we have 1 minute and 30 seconds:

     >>> round_date(Time(2010, 1, 1, 1, 1, 30))
     <Time 2010-01-01 01:01:00+00:00>

    """
    assert(isinstance(date, Time))

    if date.second:
        return date - datetime.timedelta(seconds=date.second)
    return date

class Clock(object):
    """Time Factory

    Will only serve the current time.

    Usage
    =====

    >>> from sact.epoch.clock import Clock
    >>> c = Clock()

    We can use property 'ts' to get the timestamp and it should change very
    accurately at each call:

    >>> c.ts < c.ts
    True

    If we need a full object we should use:

    >>> c.time
    <Time ...>

    """

    implements(IClock)

    @property
    def time(self):
        """Should return later a Time object"""

        return Time.utcfromtimestamp(self.ts)

    @property
    def ts(self):
        return time.time()

class ManageableClock(Clock):
    """Creates a manageable time object

    Can be used to control what time it is. Start/Stop method can
    start/stop time, and wait/set will alter current time.

    Usage
    =====

    >>> from sact.epoch.clock import ManageableClock
    >>> mc = ManageableClock()

    Stopping time
    -------------

    >>> mc.stop()
    >>> t1 = mc.ts
    >>> t2 = mc.ts
    >>> assert t1 == t2, 'time %r should be equal to %r and it isn\'t' \
    ...              % (t1, t2)
    >>> mc.is_running
    False

    Stoping while running should do nothing:

    >>> mc.stop()
    >>> mc.is_running
    False

    Restarting time
    ---------------

    >>> mc.start()
    >>> t1 = mc.ts
    >>> t2 = mc.ts
    >>> assert t1 != t2, 'time %r should NOT be equal to %r and it is' \
    ...              % (t1, t2)
    >>> mc.is_running
    True

    Restarting while running should do nothing:

    >>> mc.start()
    >>> mc.is_running
    True

    >>> t3 = mc.ts
    >>> assert t1 < t3, \
    ...    'time %r should be superior to %r and it isn\'t' \
    ...    % (t3, t1)

    Setting time
    ------------

    >>> mc.stop()
    >>> mc.ts = 0
    >>> mc.ts
    0
    >>> mc.start()
    >>> ts = mc.ts
    >>> assert ts > 0, \
    ...    'clock should have been running and thus timestamp should be greater than 0.' \
    ...    'It was %r.' % ts

    Altering time
    -------------

    >>> mc.stop()
    >>> mc.ts = 20
    >>> mc.ts += 10
    >>> mc.ts
    30
    >>> mc.start()
    >>> ts = mc.ts
    >>> assert ts > 30, \
    ...    'clock should have been running and thus timestamp should be greater than 30.' \
    ...    'It was %r.' % ts

    Setting time should not stop the clock if it was running:

    >>> mc.ts = 20
    >>> mc.is_running
    True

    Altering with wait
    ------------------

    >>> mc.stop()
    >>> mc.ts = 0
    >>> mc.wait(minutes=5)
    >>> mc.ts
    300
    >>> mc.start()
    >>> mc.wait(minutes=5)
    >>> ts = mc.ts
    >>> assert ts > 600, \
    ...    'clock should have been running and thus timestamp should be greater than 600.' \
    ...    'It was %r.' % ts

    """

    implements(IClock)

    def __init__(self):
        self.delta = 0
        self._ft = None ## freezed time

    def start(self):
        if self.is_running:
            return
        ## use _ft to calculate the time delta
        self.delta = self.ts - self.delta - self._ft
        self._ft = None

    def stop(self):
        ## save real current time
        self._ft = self.ts - self.delta

    @property
    def is_running(self):
        return self._ft is None

    def get_ts(self):
        if not self.is_running:
            return self._ft
        return time.time() + self.delta

    def set_ts(self, value):
        self.delta = value - time.time()
        # don't forget to update self._ft
        if self._ft is not None:
            self._ft = value

    ts = property(get_ts, set_ts)

    def wait(self, timedelta=None, **kwargs):
        """Provide a convenient shortcut to alter the current time

        timedelta can be an int/float or a timedelta objet from timedelta

        """
        if timedelta is None:
            timedelta = datetime.timedelta(**kwargs)

        if isinstance(timedelta, datetime.timedelta):
            secs = timedelta.days * 86400 + timedelta.seconds
        else:
            secs = int(timedelta)

        self.ts += secs

DefaultClock = Clock()

class Time(datetime.datetime):
    """Time Factory

    Usage
    =====

    This is quite straightforward to use:

    >>> from sact.epoch.clock import Time
    >>> Time.now()
    <Time ...+00:00>

    Notice that it has a timezone information set.

    We can give a better view thanks to a manageable clock
    as time reference:

    >>> from sact.epoch.clock import ManageableClock
    >>> clock = ManageableClock()

    We will stop the time to epoch:

    >>> clock.stop()
    >>> clock.ts = 0

    Let's set it as reference:

    >>> test.ZCA.registerUtility(clock)

    Now, let's set our TzTest as local timezone, remember it has 5 minute
    difference to UTC:

    >>> from sact.epoch import TzTest
    >>> from sact.epoch.interfaces import ITimeZone

    >>> test.ZCA.registerUtility(TzTest(), ITimeZone, name='local')

    Here is the result of each function:

    >>> Time.now()
    <Time 1970-01-01 00:00:00+00:00>

    >>> Time.now_lt()
    <Time 1970-01-01 00:05:00+00:05>

    Please note that there are 5 minutes of diff to UTC

    Instanciation
    =============

    >>> Time(1980, 01, 01)
    <Time 1980-01-01 00:00:00+00:00>

    Instanciate a Time from a datetime

    >>> from datetime import datetime
    >>> d = datetime(1970, 01, 01, tzinfo=TzTest())
    >>> t = Time(d)
    >>> t
    <Time 1970-01-01 00:00:00+00:05>

    >>> Time(t)
    <Time 1970-01-01 00:00:00+00:05>

    """

    classProvides(ITime)

    def __new__(cls, *args, **kwargs):
        if len(args) and isinstance(args[0], datetime.datetime):
            return Time.from_datetime(args[0])

        if 'tzinfo' not in kwargs and len(args) < 8:
            # XXXjballet: to test
            kwargs['tzinfo'] = UTC()
        return super(Time, cls).__new__(cls, *args, **kwargs)

    def __repr__(self):
        return "<Time %s>" % self

    @classmethod
    def from_datetime(cls, dt):
        """Convert a datetime object with timezone to a Time object

        This method provides a handy way to convert datetime objects to Time
        objects:

        >>> import datetime
        >>> from sact.epoch import UTC
        >>> dt = datetime.datetime(2000, 1, 1, tzinfo=UTC())
        >>> Time.from_datetime(dt)
        <Time 2000-01-01 00:00:00+00:00>

        The provided datetime should contain a timezone information or the
        conversion will fail:

        >>> Time.from_datetime(datetime.datetime.now())
        Traceback (most recent call last):
        ...
        ValueError: no timezone set for ...

        """

        if dt.tzinfo is None:
            raise ValueError("no timezone set for %r" % dt)

        return cls(dt.year, dt.month, dt.day, dt.hour,
                   dt.minute, dt.second, dt.microsecond,
                   dt.tzinfo)

    def __add__(self, delta):
        """Override datetime '+' to return a Time object

        Add a timedelta:

        >>> Time(2010, 1, 1) + datetime.timedelta(days=1)
        <Time 2010-01-02 00:00:00+00:00>

        Add an other Time (send the original exception):

        >>> Time(2010, 1, 1) + Time(1970, 1, 1)
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand type(s) for +: 'Time' and 'Time'

        """
        d = super(Time, self).__add__(delta)
        if isinstance(d, datetime.datetime):
            return self.from_datetime(d)
        return d

    def __sub__(self, delta):
        """Override datetime '-' to return a Time object

        Sub a timedelta:

        >>> Time(2010, 1, 1) - datetime.timedelta(days=1)
        <Time 2009-12-31 00:00:00+00:00>

        Sub an other Time:

        >>> Time(2010, 1, 2) - Time(2010, 1, 1)
        datetime.timedelta(1)

        """
        d = super(Time, self).__sub__(delta)
        if isinstance(d, datetime.datetime):
            return self.from_datetime(d)
        return d

    @staticmethod
    def now():
        utility = queryUtility(IClock, default=DefaultClock)
        return utility.time.replace(tzinfo=UTC())

    @staticmethod
    def now_lt():
        return Time.now().astimezone(TzLocal())

    @classmethod
    def utcfromtimestamp(cls, ts):
        """Return a UTC datetime from a timestamp.

        >>> Time.utcfromtimestamp(0)
        <Time 1970-01-01 00:00:00+00:00>

        """

        dt = super(Time, cls).utcfromtimestamp(ts)
        return dt.replace(tzinfo=UTC())

    @classmethod
    def strptime(cls, value, format, tzinfo):
        """Parse a string to create a Time object.

        >>> from sact.epoch import UTC, TzTest
        >>> Time.strptime('2000-01-01', '%Y-%m-%d', UTC())
        <Time 2000-01-01 00:00:00+00:00>

        >>> tz_test = TzTest()
        >>> Time.strptime('2000-01-01', '%Y-%m-%d', tz_test).tzinfo == tz_test
        True

        """

        dt = super(Time, cls).strptime(value, format)
        return dt.replace(tzinfo=tzinfo)

    def astimezone(self, tz):
        """Convert Time object to another timezone and return a Time object

        This overrides the datetime's method to return a Time object instead of
        a datetime object:

        >>> type(Time.now().astimezone(TzTest()))
        <class 'sact.epoch...Time'>

        """

        dt = super(Time, self).astimezone(tz)
        return self.from_datetime(dt)

    @property
    def timestamp(self):
        """Convert this Time instance in a unix timestamp in UTC

        See sact.epoch.utils

        >>> Time(1970, 1, 1, 0, 0, 1).timestamp
        1

        """
        return datetime_to_timestamp(self)

    @property
    def iso_local(self):
        """Return the iso format in local time

        >>> Time(1970, 1, 1, 1, 1).iso_local
        '1970-01-01 01:06:00+00:05'

        """
        return self.astimezone(TzLocal()).isoformat(" ")

    @property
    def short_local(self):
        """Idem than iso_local with without time zone"""
        return self.iso_local[:-6]
<commit_msg>[US76PUSH7] change the Time object human representation<commit_after># -*- coding: utf-8 -*-
"""
.. :doctest:

"""

import datetime
import time

from zope.interface import classProvides, implements
from zope.component import queryUtility

from .interfaces import ITime, IClock
from .timezone import UTC, TzLocal
from sact.epoch.utils import datetime_to_timestamp

def round_date(date):
    """Round a timedelta to the last minute (remove seconds)

    Setup:

     >>> from sact.epoch import round_date, Time

    Nothing to do:

     >>> round_date(Time(2010, 1, 1, 1, 1, 0))
     <Time 2010-01-01 01:01:00+00:00>

    Round to 1 minute when we have 1 minute and 30 seconds:

     >>> round_date(Time(2010, 1, 1, 1, 1, 30))
     <Time 2010-01-01 01:01:00+00:00>

    """
    assert(isinstance(date, Time))

    if date.second:
        return date - datetime.timedelta(seconds=date.second)
    return date

class Clock(object):
    """Time Factory

    Will only serve the current time.

    Usage
    =====

    >>> from sact.epoch.clock import Clock
    >>> c = Clock()

    We can use property 'ts' to get the timestamp and it should change very
    accurately at each call:

    >>> c.ts < c.ts
    True

    If we need a full object we should use:

    >>> c.time
    <Time ...>

    """

    implements(IClock)

    @property
    def time(self):
        """Should return later a Time object"""

        return Time.utcfromtimestamp(self.ts)

    @property
    def ts(self):
        return time.time()

class ManageableClock(Clock):
    """Creates a manageable time object

    Can be used to control what time it is. Start/Stop method can
    start/stop time, and wait/set will alter current time.

    Usage
    =====

    >>> from sact.epoch.clock import ManageableClock
    >>> mc = ManageableClock()

    Stopping time
    -------------

    >>> mc.stop()
    >>> t1 = mc.ts
    >>> t2 = mc.ts
    >>> assert t1 == t2, 'time %r should be equal to %r and it isn\'t' \
    ...              % (t1, t2)
    >>> mc.is_running
    False

    Stoping while running should do nothing:

    >>> mc.stop()
    >>> mc.is_running
    False

    Restarting time
    ---------------

    >>> mc.start()
    >>> t1 = mc.ts
    >>> t2 = mc.ts
    >>> assert t1 != t2, 'time %r should NOT be equal to %r and it is' \
    ...              % (t1, t2)
    >>> mc.is_running
    True

    Restarting while running should do nothing:

    >>> mc.start()
    >>> mc.is_running
    True

    >>> t3 = mc.ts
    >>> assert t1 < t3, \
    ...    'time %r should be superior to %r and it isn\'t' \
    ...    % (t3, t1)

    Setting time
    ------------

    >>> mc.stop()
    >>> mc.ts = 0
    >>> mc.ts
    0
    >>> mc.start()
    >>> ts = mc.ts
    >>> assert ts > 0, \
    ...    'clock should have been running and thus timestamp should be greater than 0.' \
    ...    'It was %r.' % ts

    Altering time
    -------------

    >>> mc.stop()
    >>> mc.ts = 20
    >>> mc.ts += 10
    >>> mc.ts
    30
    >>> mc.start()
    >>> ts = mc.ts
    >>> assert ts > 30, \
    ...    'clock should have been running and thus timestamp should be greater than 30.' \
    ...    'It was %r.' % ts

    Setting time should not stop the clock if it was running:

    >>> mc.ts = 20
    >>> mc.is_running
    True

    Altering with wait
    ------------------

    >>> mc.stop()
    >>> mc.ts = 0
    >>> mc.wait(minutes=5)
    >>> mc.ts
    300
    >>> mc.start()
    >>> mc.wait(minutes=5)
    >>> ts = mc.ts
    >>> assert ts > 600, \
    ...    'clock should have been running and thus timestamp should be greater than 600.' \
    ...    'It was %r.' % ts

    """

    implements(IClock)

    def __init__(self):
        self.delta = 0
        self._ft = None ## freezed time

    def start(self):
        if self.is_running:
            return
        ## use _ft to calculate the time delta
        self.delta = self.ts - self.delta - self._ft
        self._ft = None

    def stop(self):
        ## save real current time
        self._ft = self.ts - self.delta

    @property
    def is_running(self):
        return self._ft is None

    def get_ts(self):
        if not self.is_running:
            return self._ft
        return time.time() + self.delta

    def set_ts(self, value):
        self.delta = value - time.time()
        # don't forget to update self._ft
        if self._ft is not None:
            self._ft = value

    ts = property(get_ts, set_ts)

    def wait(self, timedelta=None, **kwargs):
        """Provide a convenient shortcut to alter the current time

        timedelta can be an int/float or a timedelta objet from timedelta

        """
        if timedelta is None:
            timedelta = datetime.timedelta(**kwargs)

        if isinstance(timedelta, datetime.timedelta):
            secs = timedelta.days * 86400 + timedelta.seconds
        else:
            secs = int(timedelta)

        self.ts += secs

DefaultClock = Clock()

class Time(datetime.datetime):
    """Time Factory

    Usage
    =====

    This is quite straightforward to use:

    >>> from sact.epoch.clock import Time
    >>> Time.now()
    <Time ...+00:00>

    Notice that it has a timezone information set.

    We can give a better view thanks to a manageable clock
    as time reference:

    >>> from sact.epoch.clock import ManageableClock
    >>> clock = ManageableClock()

    We will stop the time to epoch:

    >>> clock.stop()
    >>> clock.ts = 0

    Let's set it as reference:

    >>> test.ZCA.registerUtility(clock)

    Now, let's set our TzTest as local timezone, remember it has 5 minute
    difference to UTC:

    >>> from sact.epoch import TzTest
    >>> from sact.epoch.interfaces import ITimeZone

    >>> test.ZCA.registerUtility(TzTest(), ITimeZone, name='local')

    Here is the result of each function:

    >>> Time.now()
    <Time 1970-01-01 00:00:00+00:00>

    >>> Time.now_lt()
    <Time 1970-01-01 00:05:00+00:05>

    Please note that there are 5 minutes of diff to UTC

    Instanciation
    =============

    >>> Time(1980, 01, 01)
    <Time 1980-01-01 00:00:00+00:00>

    Instanciate a Time from a datetime

    >>> from datetime import datetime
    >>> d = datetime(1970, 01, 01, tzinfo=TzTest())
    >>> t = Time(d)
    >>> t
    <Time 1970-01-01 00:00:00+00:05>

    >>> Time(t)
    <Time 1970-01-01 00:00:00+00:05>

    Representations
    ^^^^^^^^^^^^^^^

    ISO:
    >>> t.iso_local
    '1970-01-01 00:00:00+00:05'

    Short local (remove time zone):
    >>> t.short_local
    '1970-01-01 00:00:00'

    Short short local (remove seconds):
    >>> t.short_short_local
    '1970-01-01 00:00'

    """

    classProvides(ITime)

    def __new__(cls, *args, **kwargs):
        if len(args) and isinstance(args[0], datetime.datetime):
            return Time.from_datetime(args[0])

        if 'tzinfo' not in kwargs and len(args) < 8:
            # XXXjballet: to test
            kwargs['tzinfo'] = UTC()
        return super(Time, cls).__new__(cls, *args, **kwargs)

    def __repr__(self):
        return "<Time %s>" % self

    @classmethod
    def from_datetime(cls, dt):
        """Convert a datetime object with timezone to a Time object

        This method provides a handy way to convert datetime objects to Time
        objects:

        >>> import datetime
        >>> from sact.epoch import UTC
        >>> dt = datetime.datetime(2000, 1, 1, tzinfo=UTC())
        >>> Time.from_datetime(dt)
        <Time 2000-01-01 00:00:00+00:00>

        The provided datetime should contain a timezone information or the
        conversion will fail:

        >>> Time.from_datetime(datetime.datetime.now())
        Traceback (most recent call last):
        ...
        ValueError: no timezone set for ...

        """

        if dt.tzinfo is None:
            raise ValueError("no timezone set for %r" % dt)

        return cls(dt.year, dt.month, dt.day, dt.hour,
                   dt.minute, dt.second, dt.microsecond,
                   dt.tzinfo)

    def __add__(self, delta):
        """Override datetime '+' to return a Time object

        Add a timedelta:

        >>> Time(2010, 1, 1) + datetime.timedelta(days=1)
        <Time 2010-01-02 00:00:00+00:00>

        Add an other Time (send the original exception):

        >>> Time(2010, 1, 1) + Time(1970, 1, 1)
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand type(s) for +: 'Time' and 'Time'

        """
        d = super(Time, self).__add__(delta)
        if isinstance(d, datetime.datetime):
            return self.from_datetime(d)
        return d

    def __sub__(self, delta):
        """Override datetime '-' to return a Time object

        Sub a timedelta:

        >>> Time(2010, 1, 1) - datetime.timedelta(days=1)
        <Time 2009-12-31 00:00:00+00:00>

        Sub an other Time:

        >>> Time(2010, 1, 2) - Time(2010, 1, 1)
        datetime.timedelta(1)

        """
        d = super(Time, self).__sub__(delta)
        if isinstance(d, datetime.datetime):
            return self.from_datetime(d)
        return d

    @staticmethod
    def now():
        utility = queryUtility(IClock, default=DefaultClock)
        return utility.time.replace(tzinfo=UTC())

    @staticmethod
    def now_lt():
        return Time.now().astimezone(TzLocal())

    @classmethod
    def utcfromtimestamp(cls, ts):
        """Return a UTC datetime from a timestamp.

        >>> Time.utcfromtimestamp(0)
        <Time 1970-01-01 00:00:00+00:00>

        """

        dt = super(Time, cls).utcfromtimestamp(ts)
        return dt.replace(tzinfo=UTC())

    @classmethod
    def strptime(cls, value, format, tzinfo):
        """Parse a string to create a Time object.

        >>> from sact.epoch import UTC, TzTest
        >>> Time.strptime('2000-01-01', '%Y-%m-%d', UTC())
        <Time 2000-01-01 00:00:00+00:00>

        >>> tz_test = TzTest()
        >>> Time.strptime('2000-01-01', '%Y-%m-%d', tz_test).tzinfo == tz_test
        True

        """

        dt = super(Time, cls).strptime(value, format)
        return dt.replace(tzinfo=tzinfo)

    def astimezone(self, tz):
        """Convert Time object to another timezone and return a Time object

        This overrides the datetime's method to return a Time object instead of
        a datetime object:

        >>> type(Time.now().astimezone(TzTest()))
        <class 'sact.epoch...Time'>

        """

        dt = super(Time, self).astimezone(tz)
        return self.from_datetime(dt)

    @property
    def timestamp(self):
        """Convert this Time instance in a unix timestamp in UTC

        See sact.epoch.utils

        >>> Time(1970, 1, 1, 0, 0, 1).timestamp
        1

        """
        return datetime_to_timestamp(self)

    @property
    def iso_local(self):
        """Return the iso format in local time

        >>> Time(1970, 1, 1, 1, 1).iso_local
        '1970-01-01 01:06:00+00:05'

        """
        return self.astimezone(TzLocal()).isoformat(" ")

    @property
    def short_local(self):
        """Idem than iso_local with without time zone"""
        return self.astimezone(TzLocal()).strftime('%Y-%m-%d %H:%M:%S')

    @property
    def short_short_local(self):
        """Idem without seconds"""
        return self.astimezone(TzLocal()).strftime('%Y-%m-%d %H:%M')
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: UTF-8 -*-
#Author : mr-karan (Karan Sharma)
import yaml
import requests
import json
import html
from prompt_toolkit import prompt
from prompt_toolkit.contrib.completers import WordCompleter
from collections import namedtuple
import argparse
parser = argparse.ArgumentParser(prog='fa-cli')
parser.add_argument('-i', '--icon', action='store_true' ,help='Enter icon name')
parser.add_argument('-f', '--filter', action='store_true' ,help='Filter Font awesome icons')
parser.add_argument('-a', '--aliases', action='store_true' ,help='Aliases')
parser.add_argument('-c', '--category', action='store_true'  ,help='Category Icons')
args = parser.parse_args()

def give_id_unicode(p_list,*text):
    if text:
        id_unicode = {p_list[i][2]:p_list[i][3] for i in range(len(p_list)) if (p_list[i][text[1]] is not None) and (text[0] in p_list[i][text[1]])}
    else:
        id_unicode = {p_list[i][2]:p_list[i][3] for i in range(len(p_list))}
    return id_unicode

def message(give_id,text):
    return '''Icon Details
    Name : {0}
    Unicode : {1}
    HTML Hex :  {2}
    Icon :{3}

    '''.format('fa-'+text,give_id[text],'&#x'+give_id[text],html.unescape('&#x'+give_id[text]))
def clean(a):
    for i in a:
        try:

            if (i['aliases']):
                continue

        except KeyError:
            i['aliases']=''
    for i in a:
        try:

            if (i['filter']):
                continue

        except KeyError:
            i['filter']=''
    return a

try:
    with open('.fa_cache','r') as the_json:
        p_list = json.load(the_json)

except FileNotFoundError:
    yaml_file = requests.get("https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/src/icons.yml").text
    a = clean((yaml.load(yaml_file))['icons'])
    data=namedtuple('IconData','categories filter id unicode aliases')
    p_list = [data(i['categories'],i['filter'],i['id'],i['unicode'],i['aliases']) for i in a]
    with open('.fa_cache','w') as the_file:
        the_file.write(json.dumps(p_list,indent=2))

if args.icon:
    search_by_id=[p_list[i][2] for i in range(len(p_list))]
    give_id = give_id_unicode(p_list)
    i_completer = WordCompleter(search_by_id)
    texti = prompt('Enter Icon Name: ', completer=i_completer)
    print(message(give_id,texti))

if args.filter:
    search_by_filter = [p_list[i][1] for i in range(len(p_list)) if (p_list[i][1] is not None)]
    f_completer = WordCompleter(list(set([k for i in search_by_filter for k in i ])))
    textf = prompt('Search By Filter: ', completer=f_completer)
    give_id = give_id_unicode(p_list,textf,1)
    for i in list(give_id.keys()):
        print(i)
    i_completer = WordCompleter(list(give_id.keys()))
    texti = prompt('Enter Icon Name: ', completer=i_completer)
    print(message(give_id,texti))

if args.aliases:
    search_by_aliases = [p_list[i][4] for i in range(len(p_list)) if (p_list[i][4] is not None)]
    f_completer = WordCompleter(list(set([k for i in search_by_aliases for k in i ])))
    textf = prompt('Search By Aliases: ', completer=f_completer)
    give_id = give_id_unicode(p_list,textf,4)
    for i in list(give_id.keys()):
        print(i)
    i_completer = WordCompleter(list(give_id.keys()))
    texti = prompt('Enter Icon Name: ', completer=i_completer)
    print(message(give_id,texti))

if args.category:
    search_by_category = [p_list[i][0] for i in range(len(p_list)) if (p_list[i][0] is not None)]
    f_completer = WordCompleter(list(set([k for i in search_by_category for k in i ])))
    textf = prompt('Search By Category: ', completer=f_completer)
    give_id = give_id_unicode(p_list,textf,0)
    for i in list(give_id.keys()):
        print(i)
    i_completer = WordCompleter(list(give_id.keys()))
    texti = prompt('Enter Icon Name: ', completer=i_completer)
    print(message(give_id,texti))

    <commit_msg>cleaned some code<commit_after>  #!/usr/bin/python
  # -*- coding: UTF-8 -*-

import yaml
import requests
import json
import html
from prompt_toolkit import prompt
from prompt_toolkit.contrib.completers import WordCompleter
from collections import namedtuple
import argparse
parser = argparse.ArgumentParser(prog='fa-cli')
parser.add_argument('-i', '--icon', action='store_true' ,help='Enter icon name')
parser.add_argument('-f', '--filter', action='store_true' ,help='Filter Font awesome icons')
parser.add_argument('-a', '--aliases', action='store_true' ,help='Aliases')
parser.add_argument('-c', '--category', action='store_true'  ,help='Category Icons')
args = parser.parse_args()

def icon_list(p_list,*pos):
    if pos:
        return [p_list[i][pos[0]] for i in range(len(p_list)) if (p_list[i][pos[0]] is not None)]
    else:
        return [p_list[i][2] for i in range(len(p_list))]

def icon_output(l):
    print('Icon''(s) matched this criteria :')
    for i in l:
        print(i)

def give_id_unicode(p_list,*text):
    if text:
        id_unicode = {p_list[i][2]:p_list[i][3] for i in range(len(p_list)) if (p_list[i][text[1]] is not None) and (text[0] in p_list[i][text[1]])}
    else:
        id_unicode = {p_list[i][2]:p_list[i][3] for i in range(len(p_list))}
    return id_unicode

def message(give_id,text):
    return '''Icon Details
    Name : {0}
    Unicode : {1}
    HTML Hex :  {2}
    Icon :{3}

    '''.format('fa-'+text,give_id[text],'&#x'+give_id[text],html.unescape('&#x'+give_id[text]))
def clean(a):
    for i in a:
        try:
            if (i['aliases']):
                continue
        except KeyError:
            i['aliases']=''
    for i in a:
        try:
            if (i['filter']):
                continue
        except KeyError:
            i['filter']=''
    return a
if __name__ =='__main__':
    try:
        with open('.fa_cache','r') as the_json:
            p_list = json.load(the_json)

    except FileNotFoundError:
        yaml_file = requests.get("https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/src/icons.yml").text
        a = clean((yaml.load(yaml_file))['icons'])
        data=namedtuple('IconData','categories filter id unicode aliases')
        p_list = [data(i['categories'],i['filter'],i['id'],i['unicode'],i['aliases']) for i in a]
        with open('.fa_cache','w') as the_file:
            the_file.write(json.dumps(p_list,indent=2))

    if args.icon:
        search_by_id=icon_list(p_list)
        give_id = give_id_unicode(p_list)
        i_completer = WordCompleter(search_by_id)
        texti = prompt('Enter Icon Name: ', completer=i_completer)
        print(message(give_id,texti))

    if args.filter:
        search_by_filter = icon_list(p_list,1)
        f_completer = WordCompleter(list(set([k for i in search_by_filter for k in i ])))
        textf = prompt('Search By Filter: ', completer=f_completer)
        give_id = give_id_unicode(p_list,textf,1)
        icon_output(list(give_id.keys()))
        i_completer = WordCompleter(list(give_id.keys()))
        texti = prompt('Enter Icon Name: ', completer=i_completer)
        print(message(give_id,texti))

    if args.aliases:
        search_by_aliases = icon_list(p_list,4)
        f_completer = WordCompleter(list(set([k for i in search_by_aliases for k in i ])))
        textf = prompt('Search By Aliases: ', completer=f_completer)
        give_id = give_id_unicode(p_list,textf,4)
        icon_output(list(give_id.keys()))
        i_completer = WordCompleter(list(give_id.keys()))
        texti = prompt('Enter Icon Name: ', completer=i_completer)
        print(message(give_id,texti))

    if args.category:
        search_by_category = icon_list(p_list,0)
        f_completer = WordCompleter(list(set([k for i in search_by_category for k in i ])))
        textf = prompt('Search By Category: ', completer=f_completer)
        give_id = give_id_unicode(p_list,textf,0)
        icon_output(list(give_id.keys()))
        i_completer = WordCompleter(list(give_id.keys()))
        texti = prompt('Enter Icon Name: ', completer=i_completer)
        print(message(give_id,texti))

    <|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from __future__ import print_function
import boto3
import botocore
from builtins import input
import click
import prettytable
import sys

images = {
    'ap-southeast-1': {'ubuntu14': 'ami-21d30f42'},     # Singapore
    'ap-south-1': {'ubuntu14': 'ami-4a90fa25'},         # Mumbai
    'us-east-1': {'ubuntu14': 'ami-2d39803a'},          # nvirginia
    'us-west-1': {'ubuntu14': 'ami-48db9d28'},          # northcalif
    'us-west-2': {'ubuntu14': 'ami-d732f0b7'},          # oregon
    'eu-west-1': {'ubuntu14': 'ami-ed82e39e'},          # ireland
    'eu-central-1': {'ubuntu14': 'ami-26c43149'},       # frankfurt
    'ap-northeast-1': {'ubuntu14': 'ami-a21529cc'},     # tokyo
    'ap-northeast-2': {'ubuntu14': 'ami-09dc1267'},     # seoul
    'ap-southeast-2': {'ubuntu14': 'ami-ba3e14d9'},     # sydney
    'sa-east-1': {'ubuntu14': 'ami-dc48dcb0'},          # saopaolo
}

def get_connection():
    """Ensures that the AWS is configured properly.

    If not, tell how to configure it.

    Returns connection object if configured properly, else None.
    """
    try:
        ec2 = boto3.resource('ec2')
    except (botocore.exceptions.NoRegionError,
            botocore.exceptions.NoCredentialsError) as e:
        # TODO(rushiagr): instead of telling people to run credentials, ask
        # credentials here itself
        print('Credentials and region not configured? Run "aws configure" to configure it.')
        # TODO(rushiagr): let people provide singapore, and guess region name from
        # that.
        print('Provide region as "ap-southeast-1" for Singapore.')
        return None
    return ec2

def get_region_specific_ami_id(distro):
    region = boto3.session.Session().region_name
    return images.get(region).get(distro)

def abort_if_false(ctx, param, value):
    if not value:
        ctx.abort()

@click.command()
@click.option('-s', 'show_vol_info', flag_value=True,
        help='Show VM disk sizes (GBs), starting with root disk')
@click.option('-n', 'filter_name',
        help='Show only VMs which matches given string (case-insensitive)')
def lsvm(show_vol_info, filter_name):
    '''List all EC2 VMs. '''
    ec2 = get_connection()
    if not ec2:
        return

    filter_name = filter_name.lower() if filter_name else None

    if show_vol_info:
        table = prettytable.PrettyTable(
                ['ID', 'Name', 'Status', 'Flavor', 'IP', 'Vols(GB)'])
    else:
        table = prettytable.PrettyTable(
                ['ID', 'Name', 'Status', 'Flavor', 'IP', 'Vols'])

    table.left_padding_width=0
    table.right_padding_width=1
    table.border=False

    instances = ec2.instances.all()

    instances_to_print = []

    if not filter_name:
        instances_to_print = instances
    else:
        for i in instances:
            if i.tags is not None and len(i.tags) > 0:
                for tag in i.tags:
                    if(tag['Key'] == 'Name' and
                            tag['Value'].lower().find(filter_name) > -1):
                        instances_to_print.append(i)
                        break

    for i in instances_to_print:
        row = [
                i.id,
                i.tags[0]['Value'] if i.tags is not None else '',
                i.state['Name'],
                i.instance_type,
                i.public_ip_address]
        if show_vol_info:
            row.append([vol.size for vol in i.volumes.all()])
        else:
            row.append(len(i.block_device_mappings))
        table.add_row(row)

    print(table.get_string(sortby='Status'))

@click.command()
def mkvm():
    ec2 = get_connection()
    if not ec2:
        return

    flavor_names = ['t1.micro', 'm1.small', 'm1.medium', 'm1.large',
            'm1.xlarge', 'm3.medium', 'm3.large', 'm3.xlarge', 'm3.2xlarge',
            'm4.large', 'm4.xlarge', 'm4.2xlarge', 'm4.4xlarge', 'm4.10xlarge',
            't2.micro', 't2.small', 't2.medium', 't2.large', 'm2.xlarge',
            'm2.2xlarge', 'm2.4xlarge', 'cr1.8xlarge', 'i2.xlarge',
            'i2.2xlarge', 'i2.4xlarge', 'i2.8xlarge', 'hi1.4xlarge',
            'hs1.8xlarge', 'c1.medium', 'c1.xlarge', 'c3.large', 'c3.xlarge',
            'c3.2xlarge', 'c3.4xlarge', 'c3.8xlarge', 'c4.large', 'c4.xlarge',
            'c4.2xlarge', 'c4.4xlarge', 'c4.8xlarge', 'cc1.4xlarge',
            'cc2.8xlarge', 'g2.2xlarge', 'cg1.4xlarge', 'r3.large',
            'r3.xlarge', 'r3.2xlarge', 'r3.4xlarge', 'r3.8xlarge', 'd2.xlarge',
            'd2.2xlarge', 'd2.4xlarge', 'd2.8xlarge']

    print('Only Ubuntu image and Singapore region supported as of now')
    print('Available flavors:', ' '.join(flavor_names))

    selected_flavor=''
    while True:
        sys.stdout.write("Select flavor ['l' to list]: ")
        flavor=input()
        if flavor.lower() == 'l':
            print(flavor_names)
            continue
        elif flavor in flavor_names:
            selected_flavor=flavor
            break
        else:
            print('Invalid flavor name.')

    keypairs = ec2.key_pairs.all()
    keypair_names = [kp.name for kp in keypairs]
    print('Available key pairs:', ' '.join(keypair_names))
    sys.stdout.write("Select keypair: ")
    selected_keypair=input()

    secgroups = list(ec2.security_groups.all())
    secgroup_name_id_dict = {}
    for sg in secgroups:
        secgroup_name_id_dict[sg.group_name] = sg.id
    secgroup_names = [sg.group_name for sg in secgroups]

    print('Available security groups:\n    ', '\t'.join(secgroup_names))
    sys.stdout.write("Select security group [empty for no security group]: ")
    selected_security_group_name=input()

    sys.stdout.write("Enter root volume size in GBs: ")
    selected_vol_size=input()

    ami_id = get_region_specific_ami_id('ubuntu14')

    if ami_id is None:
        print('We do not have Ubuntu image for this region')
        return

    if not selected_security_group_name:
        ec2.create_instances(DryRun=False, ImageId=ami_id, MinCount=1,
                MaxCount=1, KeyName=selected_keypair, InstanceType=flavor,
                BlockDeviceMappings=[{'DeviceName': '/dev/sda1',
                    'Ebs': {"VolumeSize": int(selected_vol_size)}}])
    else:
        ec2.create_instances(DryRun=False, ImageId=ami_id, MinCount=1,
                MaxCount=1, KeyName=selected_keypair, InstanceType=flavor,
                BlockDeviceMappings=[{'DeviceName': '/dev/sda1',
                    'Ebs': {"VolumeSize": int(selected_vol_size)}}],
                SecurityGroupIds=[
                    secgroup_name_id_dict[selected_security_group_name]])

@click.command()
def lskp():
    ec2 = get_connection()
    if not ec2:
        return

    keypairs = ec2.key_pairs.all()
    keypair_names = [kp.name for kp in keypairs]
    print('Available keypairs:\n   ', '\n    '.join(keypair_names))

@click.command()
def lsimg():
    ec2 = get_connection()
    if not ec2:
        return
    client = boto3.client('ec2')
    images = client.describe_images(Owners=['self'])
    image_id_names = [i['ImageId']+' '+i['Name'] for i in images['Images']]
    print('Images:\n   ', '\n    '.join(image_id_names))

@click.command()
@click.option('-a', 'is_detail', flag_value=True,
        help='Show security group rules.')
def lssg(is_detail):
    ec2 = get_connection()
    if not ec2:
        return

    secgroups = list(ec2.security_groups.all())
    if not is_detail:
        secgroup_names = [sg.group_name for sg in secgroups]
        print('Available security groups:\n   ', '\n    '.join(secgroup_names))
        print('\nExecute "lssg -a" for viewing security group rules')
    else:
        for sg in secgroups:
            print('\nSecurity group:', sg.group_name)

            ip_permissions = sg.ip_permissions
            print('   Protocol\t  IP\t\tfrom\tto')
            for perm in ip_permissions:
                if perm['IpRanges']:
                    print('     tcp\t' + perm['IpRanges'][0]['CidrIp'] + '\t' +
                        str(perm['FromPort']) + '\t' + str(perm['ToPort']))

@click.command()
@click.argument('vm_ids', nargs=-1, required=True)
@click.option('--yes', is_flag=True, callback=abort_if_false,
              expose_value=False,
              prompt='Are you sure you want to stop and terminate the VM/VMs?'
                ' You can stop the VM by using "stpvm" command.')
def rmvm(vm_ids):

    if len(vm_ids) == 0:
        print('No VM IDs provided. Aborting')
        return

    print('Stopping and terminating VMs with IDs: ', vm_ids)

    # TODO(rushiagr): use re.match('i-[0-9a-f]+', 'i-abcd1334') to confirm
    # it's an ID

    ec2 = get_connection()
    if not ec2:
        return

    ec2.instances.filter(InstanceIds=vm_ids).stop()
    ec2.instances.filter(InstanceIds=vm_ids).terminate()

@click.command()
@click.argument('vm_ids', nargs=-1, required=True)
@click.option('--yes', is_flag=True, callback=abort_if_false,
              expose_value=False,
              prompt='Are you sure you want to stop the VM?')
def stpvm(vm_ids):
    print('Stopping (but not terminating) VMs with IDs: ', vm_ids)

    # TODO(rushiagr): use re.match('i-[0-9a-f]+', 'i-abcd1334') to confirm
    # it's an ID

    ec2 = get_connection()
    if not ec2:
        return

    ec2.instances.filter(InstanceIds=vm_ids).stop()

@click.command()
def mkkp():
    ec2 = get_connection()
    if not ec2:
        return
    sys.stdout.write("Keypair name (required): ")
    keypair_name=input()
    kp = ec2.create_key_pair(KeyName=keypair_name)
    print('Keypair', keypair_name, 'created. Private key:')
    print(kp.key_material)

@click.command()
@click.argument('keypair_name', required=False)
def rmkp(keypair_name):
    ec2 = get_connection()
    if not ec2:
        return

    if keypair_name is None:
        sys.stdout.write("Keypair name (required): ")
        keypair_name=input()

    kp = ec2.KeyPair(keypair_name)
    kp.delete()
    print('Keypair', keypair_name, 'deleted.')

@click.command()
def mksg():
    ec2 = get_connection()
    if not ec2:
        return

    sys.stdout.write("Note that only TCP rules are supported as of now.\n")

    sys.stdout.write("Security group name (required): ")
    sg_name=input()
    sys.stdout.write("Security group description (required): ")
    sg_description=input()

    ip_portrange_tuples = []

    while True:
        sys.stdout.write("Add security group rule? [y/n]: ")
        bool_inp = input()
        if bool_inp.lower().startswith('y'):
            sys.stdout.write("IP (e.g. 0.0.0.0/0): ")
            ip = input()
            sys.stdout.write("Port or port range (e.g. '8080' or '8000-8999': ")
            port_range = input()
            if port_range.find('-') > -1:
                start_port, end_port = port_range.split('-')
            else:
                start_port = end_port = port_range
            start_port, end_port = int(start_port), int(end_port)
            if start_port > end_port:
                start_port, end_port = end_port, start_port
            ip_portrange_tuples.append((ip, start_port, end_port))
        else:
            break

    mysg = ec2.create_security_group(GroupName=sg_name,
            Description=sg_description)
    for ip, start_port, end_port in ip_portrange_tuples:
        mysg.authorize_ingress(IpProtocol="tcp", CidrIp=ip,
                FromPort=start_port, ToPort=end_port)

    print('Security group', sg_name, 'created')

@click.command()
@click.argument('secgroup_name', required=False)
def rmsg(secgroup_name):
    ec2 = get_connection()
    if not ec2:
        return

    if secgroup_name is None:
        sys.stdout.write("Security group name (required): ")
        secgroup_name=input()

    sg = [sg for sg in ec2.security_groups.filter(GroupNames=[secgroup_name])][0]
    sg.delete()
    print('Security group', secgroup_name, 'deleted.')
<commit_msg>Add comment about ebs image ids, and no instance store image id is present<commit_after># -*- coding: utf-8 -*-

from __future__ import print_function
import boto3
import botocore
from builtins import input
import click
import prettytable
import sys

# All ebs image, not instance-store. All latest ubuntu 12.04, but version not
# specified. Maybe need to get from amazon
images = {
    'ap-southeast-1': {'ubuntu14': 'ami-21d30f42'},     # Singapore
    'ap-south-1': {'ubuntu14': 'ami-4a90fa25'},         # Mumbai
    'us-east-1': {'ubuntu14': 'ami-2d39803a'},          # nvirginia
    'us-west-1': {'ubuntu14': 'ami-48db9d28'},          # northcalif
    'us-west-2': {'ubuntu14': 'ami-d732f0b7'},          # oregon
    'eu-west-1': {'ubuntu14': 'ami-ed82e39e'},          # ireland
    'eu-central-1': {'ubuntu14': 'ami-26c43149'},       # frankfurt
    'ap-northeast-1': {'ubuntu14': 'ami-a21529cc'},     # tokyo
    'ap-northeast-2': {'ubuntu14': 'ami-09dc1267'},     # seoul
    'ap-southeast-2': {'ubuntu14': 'ami-ba3e14d9'},     # sydney
    'sa-east-1': {'ubuntu14': 'ami-dc48dcb0'},          # saopaolo
}

def get_connection():
    """Ensures that the AWS is configured properly.

    If not, tell how to configure it.

    Returns connection object if configured properly, else None.
    """
    try:
        ec2 = boto3.resource('ec2')
    except (botocore.exceptions.NoRegionError,
            botocore.exceptions.NoCredentialsError) as e:
        # TODO(rushiagr): instead of telling people to run credentials, ask
        # credentials here itself
        print('Credentials and region not configured? Run "aws configure" to configure it.')
        # TODO(rushiagr): let people provide singapore, and guess region name from
        # that.
        print('Provide region as "ap-southeast-1" for Singapore.')
        return None
    return ec2

def get_region_specific_ami_id(distro):
    region = boto3.session.Session().region_name
    return images.get(region).get(distro)

def abort_if_false(ctx, param, value):
    if not value:
        ctx.abort()

@click.command()
@click.option('-s', 'show_vol_info', flag_value=True,
        help='Show VM disk sizes (GBs), starting with root disk')
@click.option('-n', 'filter_name',
        help='Show only VMs which matches given string (case-insensitive)')
def lsvm(show_vol_info, filter_name):
    '''List all EC2 VMs. '''
    ec2 = get_connection()
    if not ec2:
        return

    filter_name = filter_name.lower() if filter_name else None

    if show_vol_info:
        table = prettytable.PrettyTable(
                ['ID', 'Name', 'Status', 'Flavor', 'IP', 'Vols(GB)'])
    else:
        table = prettytable.PrettyTable(
                ['ID', 'Name', 'Status', 'Flavor', 'IP', 'Vols'])

    table.left_padding_width=0
    table.right_padding_width=1
    table.border=False

    instances = ec2.instances.all()

    instances_to_print = []

    if not filter_name:
        instances_to_print = instances
    else:
        for i in instances:
            if i.tags is not None and len(i.tags) > 0:
                for tag in i.tags:
                    if(tag['Key'] == 'Name' and
                            tag['Value'].lower().find(filter_name) > -1):
                        instances_to_print.append(i)
                        break

    for i in instances_to_print:
        row = [
                i.id,
                i.tags[0]['Value'] if i.tags is not None else '',
                i.state['Name'],
                i.instance_type,
                i.public_ip_address]
        if show_vol_info:
            row.append([vol.size for vol in i.volumes.all()])
        else:
            row.append(len(i.block_device_mappings))
        table.add_row(row)

    print(table.get_string(sortby='Status'))

@click.command()
def mkvm():
    ec2 = get_connection()
    if not ec2:
        return

    flavor_names = ['t1.micro', 'm1.small', 'm1.medium', 'm1.large',
            'm1.xlarge', 'm3.medium', 'm3.large', 'm3.xlarge', 'm3.2xlarge',
            'm4.large', 'm4.xlarge', 'm4.2xlarge', 'm4.4xlarge', 'm4.10xlarge',
            't2.micro', 't2.small', 't2.medium', 't2.large', 'm2.xlarge',
            'm2.2xlarge', 'm2.4xlarge', 'cr1.8xlarge', 'i2.xlarge',
            'i2.2xlarge', 'i2.4xlarge', 'i2.8xlarge', 'hi1.4xlarge',
            'hs1.8xlarge', 'c1.medium', 'c1.xlarge', 'c3.large', 'c3.xlarge',
            'c3.2xlarge', 'c3.4xlarge', 'c3.8xlarge', 'c4.large', 'c4.xlarge',
            'c4.2xlarge', 'c4.4xlarge', 'c4.8xlarge', 'cc1.4xlarge',
            'cc2.8xlarge', 'g2.2xlarge', 'cg1.4xlarge', 'r3.large',
            'r3.xlarge', 'r3.2xlarge', 'r3.4xlarge', 'r3.8xlarge', 'd2.xlarge',
            'd2.2xlarge', 'd2.4xlarge', 'd2.8xlarge']

    print('Only Ubuntu image and Singapore region supported as of now')
    print('Available flavors:', ' '.join(flavor_names))

    selected_flavor=''
    while True:
        sys.stdout.write("Select flavor ['l' to list]: ")
        flavor=input()
        if flavor.lower() == 'l':
            print(flavor_names)
            continue
        elif flavor in flavor_names:
            selected_flavor=flavor
            break
        else:
            print('Invalid flavor name.')

    keypairs = ec2.key_pairs.all()
    keypair_names = [kp.name for kp in keypairs]
    print('Available key pairs:', ' '.join(keypair_names))
    sys.stdout.write("Select keypair: ")
    selected_keypair=input()

    secgroups = list(ec2.security_groups.all())
    secgroup_name_id_dict = {}
    for sg in secgroups:
        secgroup_name_id_dict[sg.group_name] = sg.id
    secgroup_names = [sg.group_name for sg in secgroups]

    print('Available security groups:\n    ', '\t'.join(secgroup_names))
    sys.stdout.write("Select security group [empty for no security group]: ")
    selected_security_group_name=input()

    sys.stdout.write("Enter root volume size in GBs: ")
    selected_vol_size=input()

    ami_id = get_region_specific_ami_id('ubuntu14')

    if ami_id is None:
        print('We do not have Ubuntu image for this region')
        return

    if not selected_security_group_name:
        ec2.create_instances(DryRun=False, ImageId=ami_id, MinCount=1,
                MaxCount=1, KeyName=selected_keypair, InstanceType=flavor,
                BlockDeviceMappings=[{'DeviceName': '/dev/sda1',
                    'Ebs': {"VolumeSize": int(selected_vol_size)}}])
    else:
        ec2.create_instances(DryRun=False, ImageId=ami_id, MinCount=1,
                MaxCount=1, KeyName=selected_keypair, InstanceType=flavor,
                BlockDeviceMappings=[{'DeviceName': '/dev/sda1',
                    'Ebs': {"VolumeSize": int(selected_vol_size)}}],
                SecurityGroupIds=[
                    secgroup_name_id_dict[selected_security_group_name]])

@click.command()
def lskp():
    ec2 = get_connection()
    if not ec2:
        return

    keypairs = ec2.key_pairs.all()
    keypair_names = [kp.name for kp in keypairs]
    print('Available keypairs:\n   ', '\n    '.join(keypair_names))

@click.command()
def lsimg():
    ec2 = get_connection()
    if not ec2:
        return
    client = boto3.client('ec2')
    images = client.describe_images(Owners=['self'])
    image_id_names = [i['ImageId']+' '+i['Name'] for i in images['Images']]
    print('Images:\n   ', '\n    '.join(image_id_names))

@click.command()
@click.option('-a', 'is_detail', flag_value=True,
        help='Show security group rules.')
def lssg(is_detail):
    ec2 = get_connection()
    if not ec2:
        return

    secgroups = list(ec2.security_groups.all())
    if not is_detail:
        secgroup_names = [sg.group_name for sg in secgroups]
        print('Available security groups:\n   ', '\n    '.join(secgroup_names))
        print('\nExecute "lssg -a" for viewing security group rules')
    else:
        for sg in secgroups:
            print('\nSecurity group:', sg.group_name)

            ip_permissions = sg.ip_permissions
            print('   Protocol\t  IP\t\tfrom\tto')
            for perm in ip_permissions:
                if perm['IpRanges']:
                    print('     tcp\t' + perm['IpRanges'][0]['CidrIp'] + '\t' +
                        str(perm['FromPort']) + '\t' + str(perm['ToPort']))

@click.command()
@click.argument('vm_ids', nargs=-1, required=True)
@click.option('--yes', is_flag=True, callback=abort_if_false,
              expose_value=False,
              prompt='Are you sure you want to stop and terminate the VM/VMs?'
                ' You can stop the VM by using "stpvm" command.')
def rmvm(vm_ids):

    if len(vm_ids) == 0:
        print('No VM IDs provided. Aborting')
        return

    print('Stopping and terminating VMs with IDs: ', vm_ids)

    # TODO(rushiagr): use re.match('i-[0-9a-f]+', 'i-abcd1334') to confirm
    # it's an ID

    ec2 = get_connection()
    if not ec2:
        return

    ec2.instances.filter(InstanceIds=vm_ids).stop()
    ec2.instances.filter(InstanceIds=vm_ids).terminate()

@click.command()
@click.argument('vm_ids', nargs=-1, required=True)
@click.option('--yes', is_flag=True, callback=abort_if_false,
              expose_value=False,
              prompt='Are you sure you want to stop the VM?')
def stpvm(vm_ids):
    print('Stopping (but not terminating) VMs with IDs: ', vm_ids)

    # TODO(rushiagr): use re.match('i-[0-9a-f]+', 'i-abcd1334') to confirm
    # it's an ID

    ec2 = get_connection()
    if not ec2:
        return

    ec2.instances.filter(InstanceIds=vm_ids).stop()

@click.command()
def mkkp():
    ec2 = get_connection()
    if not ec2:
        return
    sys.stdout.write("Keypair name (required): ")
    keypair_name=input()
    kp = ec2.create_key_pair(KeyName=keypair_name)
    print('Keypair', keypair_name, 'created. Private key:')
    print(kp.key_material)

@click.command()
@click.argument('keypair_name', required=False)
def rmkp(keypair_name):
    ec2 = get_connection()
    if not ec2:
        return

    if keypair_name is None:
        sys.stdout.write("Keypair name (required): ")
        keypair_name=input()

    kp = ec2.KeyPair(keypair_name)
    kp.delete()
    print('Keypair', keypair_name, 'deleted.')

@click.command()
def mksg():
    ec2 = get_connection()
    if not ec2:
        return

    sys.stdout.write("Note that only TCP rules are supported as of now.\n")

    sys.stdout.write("Security group name (required): ")
    sg_name=input()
    sys.stdout.write("Security group description (required): ")
    sg_description=input()

    ip_portrange_tuples = []

    while True:
        sys.stdout.write("Add security group rule? [y/n]: ")
        bool_inp = input()
        if bool_inp.lower().startswith('y'):
            sys.stdout.write("IP (e.g. 0.0.0.0/0): ")
            ip = input()
            sys.stdout.write("Port or port range (e.g. '8080' or '8000-8999': ")
            port_range = input()
            if port_range.find('-') > -1:
                start_port, end_port = port_range.split('-')
            else:
                start_port = end_port = port_range
            start_port, end_port = int(start_port), int(end_port)
            if start_port > end_port:
                start_port, end_port = end_port, start_port
            ip_portrange_tuples.append((ip, start_port, end_port))
        else:
            break

    mysg = ec2.create_security_group(GroupName=sg_name,
            Description=sg_description)
    for ip, start_port, end_port in ip_portrange_tuples:
        mysg.authorize_ingress(IpProtocol="tcp", CidrIp=ip,
                FromPort=start_port, ToPort=end_port)

    print('Security group', sg_name, 'created')

@click.command()
@click.argument('secgroup_name', required=False)
def rmsg(secgroup_name):
    ec2 = get_connection()
    if not ec2:
        return

    if secgroup_name is None:
        sys.stdout.write("Security group name (required): ")
        secgroup_name=input()

    sg = [sg for sg in ec2.security_groups.filter(GroupNames=[secgroup_name])][0]
    sg.delete()
    print('Security group', secgroup_name, 'deleted.')
<|endoftext|>