def queryInnerProxy(obj, proxytype, default=None):
    """Look for the inner-most proxy of the given type around the object

        If no such proxy can be found, return the default.

        If there is such a proxy, return the inner-most one.
        """


def render_pep440_old(pieces):
    """TAG[.postDISTANCE[.dev0]] .

    The ".dev0" means dirty.

    Eexceptions:
    1: no tags. 0.postDISTANCE[.dev0]
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance'] or pieces['dirty']:
            rendered += '.post%d' % pieces['distance']
            if pieces['dirty']:
                rendered += '.dev0'
    else:
        rendered = '0.post%d' % pieces['distance']
        if pieces['dirty']:
            rendered += '.dev0'
    return rendered


def func_lineno(func):
    """Get the line number of a function. First looks for
    compat_co_firstlineno, then func_code.co_first_lineno.
    """
    try:
        return func.compat_co_firstlineno
    except AttributeError:
        try:
            return func.__code__.co_firstlineno
        except AttributeError:
            return -1


def is_valid_default_handler(handler):
    """Returns `True` if the `handler` is a valid default handler, as
    described in the `IRequestHandler` interface documentation.
    """
    return handler and getattr(handler, 'is_valid_default_handler', True)


def render_pep440_pre(pieces):
    """TAG[.post.devDISTANCE] -- No -dirty.

    Exceptions:
    1: no tags. 0.post.devDISTANCE
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance']:
            rendered += '.post.dev%d' % pieces['distance']
    else:
        rendered = '0.post.dev%d' % pieces['distance']
    return rendered


@staticmethod
def _label_encoder(X, Y, sample_weight):
    """
        Convert input to proper format to be used by sklearn logistic regression.
        Mainly transforms Y to a 1d vector containing the class label for each sample.
        This function is designed to be override by subclasses.
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) for DiscreteMNL and
            (n_samples, n_classes) for CrossEntropyMNL
        sample_weight: sample weight vector of shape (n_samples, )
        Returns
        -------
        X_transformed : design matrix of shape (n, n_features), 2d
        Y_transformed : response matrix of shape (n, )
        sample_weight_transformed: sample weight vector of shape (n, )
        where n:
        is n_samples in the discrete case and
        is n_samples * n_classes in the cross entropy case
        """
    raise NotImplementedError


def eof():
    """
    Return an indication that the end of the input was reached.
    """
    return [('message', 'end of input')]


def phpquote(s):
    """ Quote a string for use in PHP code.
  """
    return "'" + s.replace('\\', '\\\\').replace("'", "\\'") + "'"


def find_first_in_list(txt: str, str_list: [str]) ->int:
    """
    Returns the index of the earliest occurrence of an item from a list in a string

    Ex: find_first_in_list('foobar', ['bar', 'fin']) -> 3
    """
    start = len(txt) + 1
    for item in str_list:
        if start > txt.find(item) > -1:
            start = txt.find(item)
    return start if len(txt) + 1 > start > -1 else -1


def object_add_grid_scale(context):
    """Return scale which should be applied on object data to align it to grid scale

    """
    pass


@staticmethod
def _addElement(element, key, value):
    """
        Only add attributes to an tag if the key is not None

        :param element: the tag element of the xml tree
        :param key: the key of the attribute
        :param value: the value of the attribute
        :return:
        """
    if value is not None:
        element.set(key, value)


def _yield_finished_futures(fs, waiter, ref_collect):
    """
    Iterate on the list *fs*, yielding finished futures one by one in
    reverse order.
    Before yielding a future, *waiter* is removed from its waiters
    and the future is removed from each set in the collection of sets
    *ref_collect*.

    The aim of this function is to avoid keeping stale references after
    the future is yielded and before the iterator resumes.
    """
    while fs:
        f = fs[-1]
        for futures_set in ref_collect:
            futures_set.remove(f)
        with f._condition:
            f._waiters.remove(waiter)
        del f
        yield fs.pop()


def get_bokeh_tools():
    """Returns a list tools used along with a bokeh graph."""
    return 'crosshair,pan,zoom_in,wheel_zoom,zoom_out,box_zoom,reset,save'


def getInfo(generation):
    """Return an information string about the evolution that is used to
        upgrade to the specified generation.

        If no information is available, `None` should be returned.
        """


def loop_multi_select(ring: bool=False):
    """Select a loop of connected edges by connection type

    :param ring: Ring
    :type ring: bool
    """
    pass


def clean_unneeded_html_tags(html):
    """Clean unneeded optional html tags.

    >>> clean_unneeded_html_tags('a	b

c')
    'abc'
    """
    for tag_to_remove in """</area> </base>
 </col>
        </colgroup>

        </input>
 </link> </meta> </option> </param>

 </basefont> </isindex> </param>
            """.split(
        ):
        html = html.replace(tag_to_remove, '')
    return html


def __new__(cls, *args, **kwargs):
    """A singleton implementation of Manager. There can be only one.
        """
    if not cls._instance:
        cls._instance = super().__new__(cls)
    return cls._instance


def assoc(key, val, obj=None):
    """Add ("associate") "val" as "key" to "obj". "obj" should be a
    dictionary-like object; if it is None, then a new empty dict will be
    created. The return value will always be a dict, casting the original
    argument as needed.
    """
    new_obj = {} if obj is None else dict(obj)
    new_obj[key] = val
    return new_obj


def psi_MgOH_HCO3_OH_HMW84(T, P):
    """c-a-a': magnesium-hydroxide bicarbonate hydroxide [HMW84]."""
    psi = 0
    valid = T == 298.15
    return psi, valid


@staticmethod
def is_msg_delayed(msg):
    """
        Return True if msg has a delay stanza with a timestamp.
        """
    delay = msg.get('delay', None)
    return delay and delay.get_stamp()


def render_args(arglst, argdct):
    """Render arguments for command-line invocation.

    arglst: A list of Argument objects (specifies order)
    argdct: A mapping of argument names to values (specifies rendered values)
    """
    out = ''
    for arg in arglst:
        if arg.name in argdct:
            rendered = arg.render(argdct[arg.name])
            if rendered:
                out += ' '
                out += rendered
    return out


def add_header(environ, header_name, header_value):
    """
    Used to add a header to the environment of the request
    :param environ: the request environment
    :param header_name: name of the header to add
    :param header_value: value of the header
    """
    environ.get('spore.headers')[header_name] = header_value


def plus_or_dot(pieces):
    """Return a + if we don't already have one, else return a ."""
    if '+' in pieces.get('closest-tag', ''):
        return '.'
    return '+'


def detach_policy(DirectoryArn=None, PolicyReference=None, ObjectReference=None
    ):
    """
    Detaches a policy from an object.
    See also: AWS API Documentation

    :example: response = client.detach_policy(
        DirectoryArn='string',
        PolicyReference={
            'Selector': 'string'
        },
        ObjectReference={
            'Selector': 'string'
        }
    )

    :type DirectoryArn: string
    :param DirectoryArn: [REQUIRED]
            The Amazon Resource Name (ARN) that is associated with the Directory where both objects reside. For more information, see arns .

    :type PolicyReference: dict
    :param PolicyReference: [REQUIRED]
            Reference that identifies the policy object.
            Selector (string) --A path selector supports easy selection of an object by the parent/child links leading to it from the directory root. Use the link names from each parent/child link to construct the path. Path selectors start with a slash (/) and link names are separated by slashes. For more information about paths, see Accessing Objects . You can identify an object in one of the following ways:
            $ObjectIdentifier - An object identifier is an opaque string provided by Amazon Cloud Directory. When creating objects, the system will provide you with the identifier of the created object. An object s identifier is immutable and no two objects will ever share the same object identifier
            /some/path - Identifies the object based on path
            #SomeBatchReference - Identifies the object in a batch call

    :type ObjectReference: dict
    :param ObjectReference: [REQUIRED]
            Reference that identifies the object whose policy object will be detached.
            Selector (string) --A path selector supports easy selection of an object by the parent/child links leading to it from the directory root. Use the link names from each parent/child link to construct the path. Path selectors start with a slash (/) and link names are separated by slashes. For more information about paths, see Accessing Objects . You can identify an object in one of the following ways:
            $ObjectIdentifier - An object identifier is an opaque string provided by Amazon Cloud Directory. When creating objects, the system will provide you with the identifier of the created object. An object s identifier is immutable and no two objects will ever share the same object identifier
            /some/path - Identifies the object based on path
            #SomeBatchReference - Identifies the object in a batch call

    :rtype: dict
    :return: {}

    :returns:
    (dict) --

    """
    pass


@staticmethod
def _get_recursion_desired_value_from_flags(flags: int) ->bool:
    """
        Retrieve the recursion_desired value from flags.

        :param flags: an integer representing the flags of a dns message
        :return: value of the recursion_desired field as bool
        """
    return (flags & int.from_bytes(b'\x00\x00\x01\x00', 'big')) >> 8 == 1


def storecollapsemsg(repo, collapsemsg):
    """Store the collapse message to allow recovery"""
    collapsemsg = collapsemsg or b''
    f = repo.vfs(b'last-message.txt', b'w')
    f.write(b'%s\n' % collapsemsg)
    f.close()


def get_hostname_suggestion(LayerId=None):
    """
    Gets a generated host name for the specified layer, based on the current host name theme.
    See also: AWS API Documentation

    :example: response = client.get_hostname_suggestion(
        LayerId='string'
    )

    :type LayerId: string
    :param LayerId: [REQUIRED]
            The layer ID.

    :rtype: dict
    :return: {
        'LayerId': 'string',
        'Hostname': 'string'
    }

    """
    pass


def mandelbrot_iterate(c, max_iterations, julia_seed=None):
    """
    Returns the number of iterations before escaping the Mandelbrot fractal.

    :param c: Coordinates as a complex number
    :type c: complex
    :param max_iterations: Limit of how many tries are attempted.
    :return: Tuple containing the last complex number in the sequence and the number of iterations.
    """
    z = c
    if julia_seed is not None:
        c = julia_seed
    for iterations in range(max_iterations):
        z = z * z + c
        if abs(z) > 1000:
            return z, iterations
    return z, max_iterations


def money_format(money):
    """Return the integer value of a monetary amount string.

    Remove euro currency sign from `money` and letters expressing the
    order of magnitude from the passed in string, e.g. "K" for thousands
    and "M" for milions. Cast the string into an integer. Returned values
    are expressed in thousands of euros.

    Parameters
    ----------
    money : str
        String containing monetary amount with a currency sign and
        order of magnitude abbreviation.

    Returns
    -------
    money : int
        Integer value of the monetary amount.

    Examples
    --------
    >>> v = money_format("€500K")
    >>> print(v)
    500

    >>> v = money_format("€70.5M")
    >>> print(v)
    70500
    """
    money = money.replace('€', '')
    if 'M' in money:
        money = money.replace('M', '')
        return int(float(money) * 1000)
    money = money.replace('K', '')
    return int(money)


def char_range(starting_char, ending_char):
    """
    Create a range generator for chars
    """
    assert isinstance(starting_char, str), 'char_range: Wrong argument/s type'
    assert isinstance(ending_char, str), 'char_range: Wrong argument/s type'
    for char in range(ord(starting_char), ord(ending_char) + 1):
        yield chr(char)


def skip_blockheader(f):
    """
    Read a block header but do not unpack.

    This is a replacement for get_blockheader.  It skips f ahead 28 bytes.
    """
    f.read(28)
    return


def object_as_camera():
    """Set the active object as the active camera for this view or scene

    """
    pass


@staticmethod
def extract_message_data(msg):
    """ Extracts user name from 'sender', message caption and message content from msg.
            :param msg: Raw message object.

            :return
                (...) tuple of message attributes
        """
    sender = msg.sender
    is_sent_by_bot = None
    if sender:
        name = getattr(sender, 'username', None)
        if not name:
            name = getattr(sender, 'title', None)
            if not name:
                name = (sender.first_name or '') + ' ' + (sender.last_name or
                    '')
                name = name.strip()
            if not name:
                name = '???'
        is_sent_by_bot = getattr(sender, 'bot', None)
    else:
        name = '???'
    caption = None
    if hasattr(msg, 'message'):
        content = msg.message
    elif hasattr(msg, 'action'):
        content = str(msg.action)
    else:
        content = type(msg).__name__
    re_id_str = ''
    if hasattr(msg, 'reply_to_msg_id') and msg.reply_to_msg_id is not None:
        re_id_str = str(msg.reply_to_msg_id)
    is_contains_media = False
    media_content = None
    if getattr(msg, 'media', None):
        is_contains_media = True
        caption = getattr(msg.media, 'caption', '')
        media_content = '<{}> {}'.format(type(msg.media).__name__, caption)
    return (name, caption, content, re_id_str, is_sent_by_bot,
        is_contains_media, media_content)


def parse_cluster_pubsub_numsub(res, **options):
    """
    Result callback, handles different return types
    switchable by the `aggregate` flag.
    """
    aggregate = options.get('aggregate', True)
    if not aggregate:
        return res
    numsub_d = dict()
    for _, numsub_tups in res.items():
        for channel, numsubbed in numsub_tups:
            try:
                numsub_d[channel] += numsubbed
            except KeyError:
                numsub_d[channel] = numsubbed
    ret_numsub = []
    for channel, numsub in numsub_d.items():
        ret_numsub.append((channel, numsub))
    return ret_numsub


@classmethod
def web_sessions_change_idle_length_policy(cls, val):
    """
        Create an instance of this class set to the
        ``web_sessions_change_idle_length_policy`` tag with value ``val``.

        :param WebSessionsChangeIdleLengthPolicyType val:
        :rtype: EventType
        """
    return cls('web_sessions_change_idle_length_policy', val)


def set_event_loop(loop):
    """Set the current event loop object."""
    global _current_loop
    _current_loop = loop
    return loop


def split_filter(string):
    """Simple function to split on semicolon and return tuple."""
    return string.split(':')


def product(iterable):
    """Return product of sequence of numbers.

    Equivalent of functools.reduce(operator.mul, iterable, 1).

    >>> product([2**8, 2**30])
    274877906944
    >>> product([])
    1

    """
    prod = 1
    for i in iterable:
        prod *= i
    return prod


def should_build_with_cython(package, release=None):
    """Returns the previously used Cython version (or 'unknown' if not
    previously built) if Cython should be used to build extension modules from
    pyx files.  If the ``release`` parameter is not specified an attempt is
    made to determine the release flag from `astropy.version`.
    """
    try:
        version_module = __import__(package + '.cython_version', fromlist=[
            'release', 'cython_version'])
    except ImportError:
        version_module = None
    if release is None and version_module is not None:
        try:
            release = version_module.release
        except AttributeError:
            pass
    try:
        cython_version = version_module.cython_version
    except AttributeError:
        cython_version = 'unknown'
    have_cython = False
    try:
        import Cython
        have_cython = True
    except ImportError:
        pass
    if have_cython and (not release or cython_version == 'unknown'):
        return cython_version
    else:
        return False


def do_assert(condition, message='Assertion failed.'):
    """Assert that a ``condition`` holds or raise an ``Exception`` otherwise.

    This was added because `assert` statements are removed in optimized code.
    It replaced `assert` statements throughout the library, but that was
    reverted again for readability and performance reasons.

    Parameters
    ----------
    condition : bool
        If ``False``, an exception is raised.

    message : str, optional
        Error message.

    """
    if not condition:
        raise AssertionError(str(message))


@staticmethod
def get_table_name(aliased_class):
    """Returns the table name given an Aliased class based on Aldjemy"""
    return aliased_class._aliased_insp._target.table.name


def _get_classes(data):
    """
    Function retrieve classes for each image or return None if no classes.
    """
    return list(map(data.domain.class_var.repr_val, data.Y)
        ) if data.domain.class_var else None


def has_env_attr(env, attr, max_depth=100):
    """
    Check if a potentially wrapped environment has a given attribute.

    Parameters
    ----------
    env : gym environment

        A potentially wrapped environment.

    attr : str

        The attribute name.

    max_depth : positive int, optional

        The maximum depth of wrappers to traverse.

    """
    e = env
    for i in range(max_depth):
        if hasattr(e, attr):
            return True
        if not hasattr(e, 'env'):
            break
        e = e.env
    return False


def duplicate():
    """Duplicate the selected Grease Pencil strokes

    """
    pass


@staticmethod
def to_kelvin(romer):
    """ Convert romer to kelvin """
    return (romer - 7.5) * 40 / 21 + 273.15


def blumberg(time, alpha, w0, slope, time_0=0):
    """
    Computes the Blumberg growth model

    Parameters
    ----------
    time : time
    alpha : upper asymptote
    w0 : a reference value at time = time_0
    slope : slope of growth
    time_0 : time shift (default 0)

    See Also
    --------
    blumberg_inverse

    References
    ----------
    .. [1] A. Tsoularis and J. Wallace, "Analysis of logistic growth models.,"
           Math Biosci, vol. 179, no. 1, pp. 21-55, Jul. 2002.
    """
    result = alpha * (time + time_0) ** slope / (w0 + (time + time_0) ** slope)
    return result


def sumOutputSerializeSizes(outputs):
    """
    sumOutputSerializeSizes sums up the serialized size of the supplied outputs.

    Args:
        outputs list(TxOut): Transaction outputs.

    Returns:
        int: Estimated size of the byte-encoded transaction outputs.
    """
    serializeSize = 0
    for txOut in outputs:
        serializeSize += txOut.serializeSize()
    return serializeSize


def lmst(gmst, long):
    """local mean sideral time
Given:
    gmst
    long in degrees, positive for place east of Greenwich"""
    return gmst + 3600.0 * long / 15.0


@classmethod
def _get_origin(cls):
    """Return list of Model names for origin Reference"""
    return ['account.fiscalyear', 'account.move']


def validate_list(option, value):
    """Validates that 'value' is a list."""
    if not isinstance(value, list):
        raise TypeError('%s must be a list' % (option,))
    return value


def challenge(request):
    """Possibly issues a challenge.

        This is typically done in a protocol-specific way.

        If a challenge was issued, return True, otherwise return False.
        """


def determine_attributes(desc, targ, miss=None):
    """
    Determine the entire list of attributes.
    """
    if miss is None:
        miss = []
    atts = list(set(desc).union(targ).union(miss))
    atts.sort()
    return atts


def distance_squared(a, b):
    """The square of the distance between two (x, y) points."""
    xA, yA = a
    xB, yB = b
    return (xA - xB) ** 2 + (yA - yB) ** 2


def get_paginator(operation_name=None):
    """
    Create a paginator for an operation.

    :type operation_name: string
    :param operation_name: The operation name. This is the same name
            as the method name on the client. For example, if the
            method name is create_foo, and you'd normally invoke the
            operation as client.create_foo(**kwargs), if the
            create_foo operation can be paginated, you can use the
            call client.get_paginator('create_foo').

    :rtype: L{botocore.paginate.Paginator}
    """
    pass


def _check_pyopengl_3D():
    """Helper to ensure users have OpenGL for 3D texture support (for now)"""
    global USE_TEX_3D
    USE_TEX_3D = True
    try:
        import OpenGL.GL as _gl
    except ImportError:
        raise ImportError('PyOpenGL is required for 3D texture support')
    return _gl


def get_keywords():
    """Get the keywords needed to look up the version information."""
    git_refnames = '$Format:%d$'
    git_full = '$Format:%H$'
    git_date = '$Format:%ci$'
    keywords = {'refnames': git_refnames, 'full': git_full, 'date': git_date}
    return keywords


def set_flux_bounds(reaction, lb, ub):
    """ Set flux bounds on given reaction. """
    rplugin = reaction.getPlugin('fbc')
    rplugin.setLowerFluxBound(lb)
    rplugin.setUpperFluxBound(ub)


def outOfStockCheckoutAllowed():
    """ Returns whether checking out if this product is out of stock is allowed or not
        """


def _improve_answer_span(doc_tokens, input_start, input_end, tokenizer,
    orig_answer_text):
    """Returns tokenized answer spans that better match the annotated answer."""
    tok_answer_text = ' '.join(tokenizer.tokenize(orig_answer_text))
    for new_start in range(input_start, input_end + 1):
        for new_end in range(input_end, new_start - 1, -1):
            text_span = ' '.join(doc_tokens[new_start:new_end + 1])
            if text_span == tok_answer_text:
                return new_start, new_end
    return input_start, input_end


def format_query(query):
    """Helper to strip special characters from filepaths"""
    illegal_chars = [' ', '*', ':', '"', '<', '>', '/', '\\', '|', '?']
    for i in illegal_chars:
        if i in query:
            query = query.replace(i, '_')
    return query


def posint(x):
    """Positive integer"""
    return max(0, int(round(x)))


def remove_general(text):
    """
    Remove the unnecessary text in disclosure.
    If `True`, the line should be removed.
    """
    keywords = ['证券简称', '公告编号', '虚假记载', '重大遗漏', '【活动报名】', '图片来源：', '郑重声明',
        '请扫描右方二维码', '免责声明', '仅代表', '如有侵权', '采编', '证券之星', '查看更多',
        '未经许可，请勿转载', '扫一扫', '打开微信', '如需了解更多信息', '新浪声明', '注：以上信息仅供参考', '请稍后...']
    for word in keywords:
        if word in text:
            return True
    return False


@classmethod
def bind_complete(cls, ldap):
    """Called to mark a successful bind to the LDAP server.

        :param ldap.LDAPObject ldap: The LDAP object.

        :return: None - any returned value is ignored.

        This callback is used to indicate a successful bind, and to give you
        one opportunity to interact with the LDAP server, before the connection
        is taken over by the syncrepl search.

        For example, if you want to record the bind DN (which you might not
        know, if you are doing things like a SASL bind), or you want to
        retrieve the schema stored on the server, this is the time to do it!

        This is the very first callback to be called.  Once the callback
        completes, the syncrepl search will begin, and other callbacks will
        start coming in.

        .. note::

            Once the callback has completed, there is no guarantee that `ldap`
            will still be a valid reference.

        .. warning::

            If you start any asynchronous operations (which includes searches),
            those operations **must** be completed before this callback
            returns.

        .. warning::

            Please do not unbind `ldap` from the LDAP server!

        .. warning::

            Once the callback completes, this LDAP connection will be used for
            the syncrepl search.  **No other operations will be allowed!**
            If you want to communicate with the LDAP server after this callback
            completes, you will need to set up a separate LDAP connection.
        """
    pass


@staticmethod
def is_unique(field):
    """ Return True is field's value should be a unique.

        :return bool:

        """
    return field.scheme.unique


@classmethod
def shared_link_remove_expiry_details(cls, val):
    """
        Create an instance of this class set to the
        ``shared_link_remove_expiry_details`` tag with value ``val``.

        :param SharedLinkRemoveExpiryDetails val:
        :rtype: EventDetails
        """
    return cls('shared_link_remove_expiry_details', val)


def plus_or_dot(pieces):
    """Return a + if we don't already have one, else return a ."""
    if '+' in pieces.get('closest-tag', ''):
        return '.'
    return '+'


def and_(*conditions) ->dict:
    """Helper that produces mongo query dict for AND of multiple conditions"""
    if len(conditions) == 1:
        return conditions[0]
    return {'$and': list(conditions)}


def read_td_query(query, engine, index_col=None, parse_dates=None,
    distributed_join=False, params=None):
    """Read Treasure Data query into a DataFrame.

    Returns a DataFrame corresponding to the result set of the query string.
    Optionally provide an index_col parameter to use one of the columns as
    the index, otherwise default integer index will be used.

    Parameters
    ----------
    query : string
        Query string to be executed.
    engine : QueryEngine
        Handler returned by create_engine.
    index_col : string, optional
        Column name to use as index for the returned DataFrame object.
    parse_dates : list or dict, optional
        - List of column names to parse as dates
        - Dict of {column_name: format string} where format string is strftime
          compatible in case of parsing string times or is one of (D, s, ns, ms, us)
          in case of parsing integer timestamps
    distributed_join : boolean, default False
        (Presto only) If True, distributed join is enabled. If False, broadcast join is used.
        See https://prestodb.io/docs/current/release/release-0.77.html
    params : dict, optional
        Parameters to pass to execute method.
        Available parameters:
        - result_url (str): result output URL
        - priority (int or str): priority (e.g. "NORMAL", "HIGH", etc.)
        - retry_limit (int): retry limit

    Returns
    -------
    DataFrame
    """
    if params is None:
        params = {}
    header = engine.create_header('read_td_query')
    if engine.type == 'presto' and distributed_join is not None:
        header += "-- set session distributed_join = '{0}'\n".format('true' if
            distributed_join else 'false')
    r = engine.execute(header + query, **params)
    return r.to_dataframe(index_col=index_col, parse_dates=parse_dates)


def _get_window_size(offset, step_size, image_size):
    """
    Calculate window width or height.
    Usually same as block size, except when at the end of image and only a
    fracture of block size remains
    :param offset: start columns/ row
    :param step_size: block width/ height
    :param image_size: image width/ height
    :return: window width/ height
    """
    if offset + step_size > image_size:
        return image_size - offset
    else:
        return step_size


@staticmethod
def __clamp_kalvin__(val):
    """
        clamp given kalvin values for a range from 3000..6500
        """
    if val > 6500:
        val = 6500
    elif val < 3000:
        val = 3000
    return val


def stripnull(string, null=b'\x00'):
    """Return string truncated at first null character.

    Clean NULL terminated C strings. For unicode strings use null='\\0'.

    """
    i = string.find(null)
    return string if i < 0 else string[:i]


@staticmethod
def _call_function_no_args_nor_kwargs(supplied_function, arg_list,
    stop_arg_index):
    """Call supplied function without either *args nor **kwargs."""
    return supplied_function(*arg_list[:stop_arg_index])


def grace(data, xlab='', ylab='', title=''):
    """
	Input data is of the form, [ [x1,y1], [x2,y2],....] where x and y are vectors
	"""
    try:
        import pygrace
        pg = pygrace.grace()
        for xy in data:
            pg.plot(xy[0], xy[1])
            pg.hold(1)
        pg.xlabel(xlab)
        pg.ylabel(ylab)
        pg.title(title)
        return True
    except:
        return False


def convert_storage(container):
    """Convert the file storage for files contained inside the
        container to the correct storage, if needed.
        """


def flip_names():
    """Flips (and corrects) the axis suffixes of the names of selected bones

    """
    pass


def configure_action_logging():
    """
    Any additional configuration (register callback) for airflow.utils.action_loggers
    module
    :rtype: None
    """


@staticmethod
def _ack(message_id, subscription_id, **kwargs):
    """Acknowledge receipt of a message. This only makes sense when the
        'acknowledgement' flag was set for the relevant subscription.
        :param message_id: ID of the message to be acknowledged.
        :param subscription_id: ID of the associated subscription.
        :param **kwargs: Further parameters for the transport layer. For example
               transaction: Transaction ID if acknowledgement should be part of
                            a transaction
        """
    raise NotImplementedError('Transport interface not implemented')


def as_scalar(data):
    """Returns a scalar whose value is copied from this array.

    Parameters
    ----------
    data : Tensor
        The input data

    Returns
    -------
    scalar
        The scalar value in the tensor.
    """
    pass


@staticmethod
def merge(reports):
    """Works for dictionary reports. Merges the reports list of
        dictionaries in a single dictionary. The list of the first
        dictionary are extended by those of all subsequent lists."""
    report = reports[0]
    keys_report = list(report.keys())
    for other_report in reports[1:]:
        for key in keys_report:
            report[key] += other_report[key]
    return report


def box_normalized_tile_offset(x_norm, y_norm, x_tile, y_tile, size=6):
    """ Given a x normal and y normal and x and y tile with in the box return the offset
    usefull for calculating texture uniform offsets in an a texture atlas

    Args:
        x_norm: either 0 or 1 for left or right of the box
        y_norm: either 0 or 1 for the top or bottom of the box
        x_tile: x tile offset to fetch
        y_tile: y tile offset to fetch
        size: number of tiles in the grid

    Returns:
      x_norm: adjusted x normal
      y_norm: adjusted y normal

    Links:
    """
    if x_tile and y_tile >= size:
        return None
    ratio = 1.0 / size
    return ratio * x_tile + x_norm * ratio, ratio * y_tile + y_norm * ratio


@classmethod
def insufficient_plan(cls, val):
    """
        Create an instance of this class set to the ``insufficient_plan`` tag
        with value ``val``.

        :param InsufficientPlan val:
        :rtype: PermissionDeniedReason
        """
    return cls('insufficient_plan', val)


def as_label(metanode):
    """
    Retrieve neo4j-style label for a metanode.
    """
    import warnings
    warnings.warn(
        'hetio.neo4j.as_label is deprecated. Use metanode.neo4j_label instead.'
        , DeprecationWarning)
    return metanode.neo4j_label


def cas2sas_time(cdt):
    """
    Convert a CAS time to a SAS time

    Parameters
    ----------
    cdt : int
        CAS time.

    Examples
    --------
    >>> cas2sas_time(43200000000)
    43200.0

    Returns
    -------
    float
        SAS time

    """
    return cdt / float(10 ** 6)


def axify(expr, axis, keepdims=False):
    """ inject axis argument into expression

    Helper function for compute_up(Summary, np.ndarray)

    >>> from blaze import symbol
    >>> s = symbol('s', '10 * 10 * int')
    >>> expr = s.sum()
    >>> axify(expr, axis=0)
    sum(s, axis=(0,))
    """
    return type(expr)(expr._child, axis=axis, keepdims=keepdims)


def analog_r3_released(controller):
    """
    Callback function that is triggered, when the right stick button was released

    :param controller:              the controller that triggered the action
    :type controller:               steuer.Controller
    """
    print('ACTION:' + str(controller.number) + ':' + controller.name +
        ':Analog R3 released')


def convert2boolean(arg):
    """convert2boolean is used for environmental variables
       that must be returned as boolean

       Parameters
       ==========
       arg: the argument to convert to boolean. Must be a string given
       grabbed from environment.
    """
    if not isinstance(arg, bool):
        return arg.lower() in ('yes', 'true', 't', '1', 'y')
    return arg


def plus_or_dot(pieces):
    """Return a + if we don't already have one, else return a ."""
    if '+' in pieces.get('closest-tag', ''):
        return '.'
    return '+'


def getOrdering():
    """ return the currently active ordering adapter for this folder """


def determine_when_sne_are_ripe_for_discovery(log, redshiftArray,
    limitingMags, observedFrameLightCurveInfo, discoverableList, plot=True):
    """
    *Given the observe frame lightcurve, determine if the SNe are discoverable by the survey or not.*

    **Key Arguments:**
        - ``log`` -- logger
        - ``redshiftArray`` -- the array of random redshifts
        - ``observedFrameLightCurveInfo`` -- the observed franme lightcurve info (dictionary)
        - ``discoverableList`` -- a list of dictionaries describing if the object is discoverable in each filter and finally if it is discoverable in any filter.
        - ``plot`` -- generate plots?

    **Return:**
        - ``ripeDayList`` -- a list of dictionaries describing the time relative to peak that the SN reaches the limiting mag of the survey
    """
    import math
    filters = ['g', 'r', 'i', 'z']
    models = ['he80', 'he100', 'he130']
    ripeDayList = []
    for item in range(len(redshiftArray)):
        ripeDayDict = {}
        if not discoverableList[item]['any']:
            ripeDayDict['any'] = False
            for ffilter in filters:
                ripeDayDict[ffilter] = False
        else:
            for ffilter in filters:
                if not discoverableList[item][ffilter]:
                    ripeDayDict[ffilter] = False
                elif observedFrameLightCurveInfo[item]['lightCurves'][ffilter
                    ] is None:
                    ripeDayDict[ffilter] = False
                else:
                    explosionDay = observedFrameLightCurveInfo[item][
                        'explosionDay']
                    lowerLimit = explosionDay
                    upperLimit = 0.0
                    ripeDay = lowerLimit
                    magGuess = observedFrameLightCurveInfo[item]['lightCurves'
                        ][ffilter](ripeDay)
                    magDiff = limitingMags[ffilter] - magGuess
                    if magGuess > limitingMags[ffilter]:
                        while math.fabs(magDiff) > 0.01:
                            if magGuess > limitingMags[ffilter]:
                                lowerLimit = ripeDay
                                ripeDay = lowerLimit + (upperLimit - lowerLimit
                                    ) / 2.0
                                magGuess = observedFrameLightCurveInfo[item][
                                    'lightCurves'][ffilter](ripeDay)
                            elif magGuess < limitingMags[ffilter]:
                                upperLimit = ripeDay
                                ripeDay = lowerLimit + (upperLimit - lowerLimit
                                    ) / 2.0
                                magGuess = observedFrameLightCurveInfo[item][
                                    'lightCurves'][ffilter](ripeDay)
                            magDiff = limitingMags[ffilter] - magGuess
                    ripeDay = float('%5.3f' % (ripeDay,))
                    ripeDayDict[ffilter] = ripeDay
            ripeDayDict['any'] = False
            for ffilter in filters:
                if ripeDayDict[ffilter]:
                    ripeDayDict['any'] = True
        ripeDayList.append(ripeDayDict)
    return ripeDayList


@classmethod
def get_base(cls, name):
    """ Checks a string for a possible base name of an object (no prefix, no suffix).
            We need to do a full parse to make sure we've ruled out all the possible other parse queries

        :param name: str, string that represents a possible name of an object
        :return: str, the detected basename
        """
    return cls.parse_name(name).get('basename', None)


def view_zoom_out(location: float=(0.0, 0.0)):
    """Zoom out the image (centered around 2D cursor)

    :param location: Location, Cursor location in screen coordinates
    :type location: float
    """
    pass


def isBuildConfChanged(buildconf, buildroot):
    """
    Try to detect if current buildconf file is changed.
    Returns True if it's changed or file just doesn't exist.
    """
    raise NotImplementedError


@classmethod
def from_api_repr(cls, resource, zone):
    """Factory:  construct a record set given its API representation

        :type resource: dict
        :param resource: record sets representation returned from the API

        :type zone: :class:`google.cloud.dns.zone.ManagedZone`
        :param zone: A zone which holds one or more record sets.

        :rtype: :class:`google.cloud.dns.zone.ResourceRecordSet`
        :returns: RRS parsed from ``resource``.
        """
    name = resource['name']
    record_type = resource['type']
    ttl = int(resource['ttl'])
    rrdatas = resource['rrdatas']
    return cls(name, record_type, ttl, rrdatas, zone=zone)


def convert_site_dm3_table_directions(sites_df):
    """
    Convert MagIC site headers to short/readable
    headers for a figure (used by ipmag.sites_extract)
    Directional table only.

    Parameters
    ----------
    sites_df : pandas DataFrame
        sites information

    Returns
    ---------
    dir_df : pandas DataFrame
        directional site data with easily readable headers
    """
    dir_df = sites_df.copy().dropna(subset=['dir_dec', 'dir_inc'])
    DirCols = ['Site', 'TC (%)', 'Dec.', 'Inc.', 'N', 'k    ', 'R', 'a95',
        'VGP Lat', 'VGP Long']
    columns = ['site', 'dir_tilt_correction', 'dir_dec', 'dir_inc',
        'dir_n_samples', 'dir_k', 'dir_r', 'dir_alpha95', 'vgp_lat', 'vgp_lon']
    dm3_to_readable = dict(zip(columns, DirCols))
    if len(dir_df) > 0:
        for col in ['dir_n_samples', 'dir_tilt_correction']:
            if col in dir_df.columns:
                dir_df[col] = dir_df[col].values.astype('int')
        columns = dir_df.columns.intersection(columns)
        has_vgps = False
        if 'vgp_lat' in dir_df.columns:
            test_vgp = dir_df.dropna(subset=['vgp_lat', 'vgp_lon'])
            if len(test_vgp) > 0:
                has_vgps = True
        if has_vgps:
            dir_df['vgp_lat_abs'] = dir_df.vgp_lat.abs()
            dir_df.sort_values(by=['site', 'vgp_lat_abs'], ascending=False,
                inplace=True)
            dir_df = dir_df[columns]
            dir_df.drop_duplicates(subset=['dir_dec', 'dir_inc', 'site'],
                inplace=True)
        else:
            dir_df.drop_duplicates(subset=['dir_dec', 'dir_inc', 'site'],
                inplace=True)
            dir_df = dir_df[['site', 'dir_tilt_correction', 'dir_dec',
                'dir_inc', 'dir_n_samples', 'dir_k', 'dir_r', 'dir_alpha95']]
        dir_df.rename(dm3_to_readable, axis='columns', inplace=True)
        dir_df.sort_values(by=['Site'], inplace=True, ascending=True)
        new_cols = list(dir_df.columns.drop('Site'))
        dir_df = dir_df[['Site'] + new_cols]
    return dir_df


def get_following_comment_same_line(node):
    """
    returns (as string) any comment that appears on the same line,
    after the node, including the #
    """
    try:
        if node.type == 'for_stmt':
            whitespace = node.children[5].get_first_leaf().prefix
        elif node.type == 'with_stmt':
            whitespace = node.children[3].get_first_leaf().prefix
        elif node.type == 'funcdef':
            whitespace = node.children[4].get_first_leaf().get_next_leaf(
                ).prefix
        else:
            whitespace = node.get_last_leaf().get_next_leaf().prefix
    except AttributeError:
        return None
    except ValueError:
        return None
    if '#' not in whitespace:
        return None
    comment = whitespace[whitespace.index('#'):]
    if '\r' in comment:
        comment = comment[:comment.index('\r')]
    if '\n' in comment:
        comment = comment[:comment.index('\n')]
    return comment


def file_loader(config_file):
    """
    Load config from YAML file

    Args:
        config_file: String path to config file

    Returns:
        Result of yaml.load

    Raises:
        KeyError: If 'jobs' key not defined in config
    """
    from yaml import load
    with open(config_file, 'r') as fh:
        config = load(fh.read())
    if 'jobs' not in config:
        raise KeyError('No jobs defined. "jobs" key missing.')
    return config


def starts_with(string, substring, *, start: int):
    """
  Functionally equivalent to
    string[start:].startswith(substring)
  but faster
  """
    return string[start:start + len(substring)] == substring


@staticmethod
def def_comparator(d, d0, p1, p2):
    """Consider 2 points to be in the same cluster if their distance is closer
           than the original threshold D0. Do not take into account anything else."""
    return True


def splitstatus(a, statusfn):
    """split sequence into subsequences based on binary condition statusfn. a is a list, returns list of lists"""
    groups = []
    mode = None
    for elt, status in zip(a, list(map(statusfn, a))):
        assert isinstance(status, bool)
        if status != mode:
            mode = status
            group = [mode]
            groups.append(group)
        group.append(elt)
    return groups


def _loop_qt(app):
    """Inner-loop for running the Qt eventloop

    Pulled from guisupport.start_event_loop in IPython < 5.2,
    since IPython 5.2 only checks `get_ipython().active_eventloop` is defined,
    rather than if the eventloop is actually running.
    """
    app._in_event_loop = True
    app.exec_()
    app._in_event_loop = False


def _format_servers_list_networks(networks):
    """Return a formatted string of a server's networks

    :param networks: a Server.networks field
    :rtype: a string of formatted network addresses
    """
    output = []
    for network, addresses in networks.items():
        if not addresses:
            continue
        addresses_csv = ', '.join(addresses)
        group = '%s=%s' % (network, addresses_csv)
        output.append(group)
    return '; '.join(output)


def dms_to_degrees(v):
    """Convert degree/minute/second to decimal degrees."""
    d = float(v[0][0]) / float(v[0][1])
    m = float(v[1][0]) / float(v[1][1])
    s = float(v[2][0]) / float(v[2][1])
    return d + m / 60.0 + s / 3600.0


def _int2r(val):
    """Convert the integer value to the light sensor resistance.

    This method should only be used internally.

    Note
    ----
    A smaller returned value indicates a higher brightness. Resistance
    value ranges from 5.0 (brightest) to 35.0 (darkest).

    Parameters
    ----------
    val : int
        The raw data read from grove ADC.

    Returns
    -------
    float
        The light sensor resistance indicating the light intensity.

    """
    if 0 < val <= 4095:
        r_sensor = (4095.0 - val) * 10 / val
    else:
        raise RuntimeError('Value out of range or device not connected.')
    return float('{0:.2f}'.format(r_sensor))


def infer_dtype(value):
    """get correct data type for the first element."""
    import numpy as np
    array_dtype = 'none'
    if isinstance(value, np.ndarray):
        dtype = np.ndarray
        array_dtype = value.dtype.__str__()
    elif isinstance(value, str):
        dtype = str
    elif isinstance(value, int):
        dtype = int
    elif isinstance(value, list):
        dtype = list
    elif isinstance(value, dict):
        dtype = dict
    elif isinstance(value, float):
        dtype = float
    return dtype, array_dtype


@staticmethod
def level_matches(level, consumer_level):
    """
        >>> l = Logger([])
        >>> l.level_matches(3, 4)
        False
        >>> l.level_matches(3, 2)
        True
        >>> l.level_matches(slice(None, 3), 3)
        False
        >>> l.level_matches(slice(None, 3), 2)
        True
        >>> l.level_matches(slice(1, 3), 1)
        True
        >>> l.level_matches(slice(2, 3), 1)
        False
        """
    if isinstance(level, slice):
        start, stop = level.start, level.stop
        if start is not None and start > consumer_level:
            return False
        if stop is not None and stop <= consumer_level:
            return False
        return True
    else:
        return level >= consumer_level


def splitline(text):
    """
    Splits the given text at newline.

        >>> splitline('foo\\nbar')
        ('foo\\n', 'bar')
        >>> splitline('foo')
        ('foo', '')
        >>> splitline('')
        ('', '')
    """
    index = text.find('\n') + 1
    if index:
        return text[:index], text[index:]
    else:
        return text, ''


def wipe_deploy_internal_info(node):
    """Remove temporary deployment fields from driver_internal_info."""
    info = node.driver_internal_info
    info.pop('agent_secret_token', None)
    info.pop('agent_secret_token_pregenerated', None)
    info['deploy_steps'] = None
    info.pop('agent_cached_deploy_steps', None)
    info.pop('deploy_step_index', None)
    info.pop('deployment_reboot', None)
    info.pop('deployment_polling', None)
    info.pop('skip_current_deploy_step', None)
    info.pop('steps_validated', None)
    info.pop('agent_url', None)
    node.driver_internal_info = info


def KK_RC76_fit(params, w, t_values):
    """
    Kramers-Kronig Function: -RC-

    Kristian B. Knudsen (kknu@berkeley.edu / kristianbknudsen@gmail.com)
    """
    Rs = params['Rs']
    R1 = params['R1']
    R2 = params['R2']
    R3 = params['R3']
    R4 = params['R4']
    R5 = params['R5']
    R6 = params['R6']
    R7 = params['R7']
    R8 = params['R8']
    R9 = params['R9']
    R10 = params['R10']
    R11 = params['R11']
    R12 = params['R12']
    R13 = params['R13']
    R14 = params['R14']
    R15 = params['R15']
    R16 = params['R16']
    R17 = params['R17']
    R18 = params['R18']
    R19 = params['R19']
    R20 = params['R20']
    R21 = params['R21']
    R22 = params['R22']
    R23 = params['R23']
    R24 = params['R24']
    R25 = params['R25']
    R26 = params['R26']
    R27 = params['R27']
    R28 = params['R28']
    R29 = params['R29']
    R30 = params['R30']
    R31 = params['R31']
    R32 = params['R32']
    R33 = params['R33']
    R34 = params['R34']
    R35 = params['R35']
    R36 = params['R36']
    R37 = params['R37']
    R38 = params['R38']
    R39 = params['R39']
    R40 = params['R40']
    R41 = params['R41']
    R42 = params['R42']
    R43 = params['R43']
    R44 = params['R44']
    R45 = params['R45']
    R46 = params['R46']
    R47 = params['R47']
    R48 = params['R48']
    R49 = params['R49']
    R50 = params['R50']
    R51 = params['R51']
    R52 = params['R52']
    R53 = params['R53']
    R54 = params['R54']
    R55 = params['R55']
    R56 = params['R56']
    R57 = params['R57']
    R58 = params['R58']
    R59 = params['R59']
    R60 = params['R60']
    R61 = params['R61']
    R62 = params['R62']
    R63 = params['R63']
    R64 = params['R64']
    R65 = params['R65']
    R66 = params['R66']
    R67 = params['R67']
    R68 = params['R68']
    R69 = params['R69']
    R70 = params['R70']
    R71 = params['R71']
    R72 = params['R72']
    R73 = params['R73']
    R74 = params['R74']
    R75 = params['R75']
    R76 = params['R76']
    return Rs + R1 / (1 + w * 1.0j * t_values[0]) + R2 / (1 + w * 1.0j *
        t_values[1]) + R3 / (1 + w * 1.0j * t_values[2]) + R4 / (1 + w *
        1.0j * t_values[3]) + R5 / (1 + w * 1.0j * t_values[4]) + R6 / (1 +
        w * 1.0j * t_values[5]) + R7 / (1 + w * 1.0j * t_values[6]) + R8 / (
        1 + w * 1.0j * t_values[7]) + R9 / (1 + w * 1.0j * t_values[8]
        ) + R10 / (1 + w * 1.0j * t_values[9]) + R11 / (1 + w * 1.0j *
        t_values[10]) + R12 / (1 + w * 1.0j * t_values[11]) + R13 / (1 + w *
        1.0j * t_values[12]) + R14 / (1 + w * 1.0j * t_values[13]) + R15 / (
        1 + w * 1.0j * t_values[14]) + R16 / (1 + w * 1.0j * t_values[15]
        ) + R17 / (1 + w * 1.0j * t_values[16]) + R18 / (1 + w * 1.0j *
        t_values[17]) + R19 / (1 + w * 1.0j * t_values[18]) + R20 / (1 + w *
        1.0j * t_values[19]) + R21 / (1 + w * 1.0j * t_values[20]) + R22 / (
        1 + w * 1.0j * t_values[21]) + R23 / (1 + w * 1.0j * t_values[22]
        ) + R24 / (1 + w * 1.0j * t_values[23]) + R25 / (1 + w * 1.0j *
        t_values[24]) + R26 / (1 + w * 1.0j * t_values[25]) + R27 / (1 + w *
        1.0j * t_values[26]) + R28 / (1 + w * 1.0j * t_values[27]) + R29 / (
        1 + w * 1.0j * t_values[28]) + R30 / (1 + w * 1.0j * t_values[29]
        ) + R31 / (1 + w * 1.0j * t_values[30]) + R32 / (1 + w * 1.0j *
        t_values[31]) + R33 / (1 + w * 1.0j * t_values[32]) + R34 / (1 + w *
        1.0j * t_values[33]) + R35 / (1 + w * 1.0j * t_values[34]) + R36 / (
        1 + w * 1.0j * t_values[35]) + R37 / (1 + w * 1.0j * t_values[36]
        ) + R38 / (1 + w * 1.0j * t_values[37]) + R39 / (1 + w * 1.0j *
        t_values[38]) + R40 / (1 + w * 1.0j * t_values[39]) + R41 / (1 + w *
        1.0j * t_values[40]) + R42 / (1 + w * 1.0j * t_values[41]) + R43 / (
        1 + w * 1.0j * t_values[42]) + R44 / (1 + w * 1.0j * t_values[43]
        ) + R45 / (1 + w * 1.0j * t_values[44]) + R46 / (1 + w * 1.0j *
        t_values[45]) + R47 / (1 + w * 1.0j * t_values[46]) + R48 / (1 + w *
        1.0j * t_values[47]) + R49 / (1 + w * 1.0j * t_values[48]) + R50 / (
        1 + w * 1.0j * t_values[49]) + R51 / (1 + w * 1.0j * t_values[50]
        ) + R52 / (1 + w * 1.0j * t_values[51]) + R53 / (1 + w * 1.0j *
        t_values[52]) + R54 / (1 + w * 1.0j * t_values[53]) + R55 / (1 + w *
        1.0j * t_values[54]) + R56 / (1 + w * 1.0j * t_values[55]) + R57 / (
        1 + w * 1.0j * t_values[56]) + R58 / (1 + w * 1.0j * t_values[57]
        ) + R59 / (1 + w * 1.0j * t_values[58]) + R60 / (1 + w * 1.0j *
        t_values[59]) + R61 / (1 + w * 1.0j * t_values[60]) + R62 / (1 + w *
        1.0j * t_values[61]) + R63 / (1 + w * 1.0j * t_values[62]) + R64 / (
        1 + w * 1.0j * t_values[63]) + R65 / (1 + w * 1.0j * t_values[64]
        ) + R66 / (1 + w * 1.0j * t_values[65]) + R67 / (1 + w * 1.0j *
        t_values[66]) + R68 / (1 + w * 1.0j * t_values[67]) + R69 / (1 + w *
        1.0j * t_values[68]) + R70 / (1 + w * 1.0j * t_values[69]) + R71 / (
        1 + w * 1.0j * t_values[70]) + R72 / (1 + w * 1.0j * t_values[71]
        ) + R73 / (1 + w * 1.0j * t_values[72]) + R74 / (1 + w * 1.0j *
        t_values[73]) + R75 / (1 + w * 1.0j * t_values[74]) + R76 / (1 + w *
        1.0j * t_values[75])


def put_notification_configuration(AutoScalingGroupName=None, TopicARN=None,
    NotificationTypes=None):
    """
    Configures an Auto Scaling group to send notifications when specified events take place. Subscribers to the specified topic can have messages delivered to an endpoint such as a web server or an email address.
    This configuration overwrites any existing configuration.
    For more information see Getting SNS Notifications When Your Auto Scaling Group Scales in the Auto Scaling User Guide .
    See also: AWS API Documentation

    Examples
    This example adds the specified notification to the specified Auto Scaling group.
    Expected Output:

    :example: response = client.put_notification_configuration(
        AutoScalingGroupName='string',
        TopicARN='string',
        NotificationTypes=[
            'string',
        ]
    )

    :type AutoScalingGroupName: string
    :param AutoScalingGroupName: [REQUIRED]
            The name of the Auto Scaling group.

    :type TopicARN: string
    :param TopicARN: [REQUIRED]
            The Amazon Resource Name (ARN) of the Amazon Simple Notification Service (SNS) topic.

    :type NotificationTypes: list
    :param NotificationTypes: [REQUIRED]
            The type of event that will cause the notification to be sent. For details about notification types supported by Auto Scaling, see DescribeAutoScalingNotificationTypes .
            (string) --

    :return: response = client.put_notification_configuration(
        AutoScalingGroupName='my-auto-scaling-group',
        NotificationTypes=[
            'autoscaling:TEST_NOTIFICATION',
        ],
        TopicARN='arn:aws:sns:us-west-2:123456789012:my-sns-topic',
    )

    print(response)

    """
    pass