#!/usr/bin/env python
#
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Common utility library."""
from __future__ import with_statement

import datetime
import functools
import inspect
import os
import re
import sys

import six

__all__ = [
    'Error',
    'decode_datetime',
    'get_package_for_module',
    'positional',
    'TimeZoneOffset',
    'total_seconds',
]


class Error(Exception):
    """Base class for protorpc exceptions."""


_TIME_ZONE_RE_STRING = r"""
  # Examples:
  #   +01:00
  #   -05:30
  #   Z12:00
  ((?P<z>Z) | (?P<sign>[-+])
   (?P<hours>\d\d) :
   (?P<minutes>\d\d))$
"""
_TIME_ZONE_RE = re.compile(_TIME_ZONE_RE_STRING, re.IGNORECASE | re.VERBOSE)


def positional(max_positional_args):
    """A decorator to declare that only the first N arguments may be positional.

    This decorator makes it easy to support Python 3 style keyword-only
    parameters. For example, in Python 3 it is possible to write:

      def fn(pos1, *, kwonly1=None, kwonly1=None):
        ...

    All named parameters after * must be a keyword:

      fn(10, 'kw1', 'kw2')  # Raises exception.
      fn(10, kwonly1='kw1')  # Ok.

    Example:
      To define a function like above, do:

        @positional(1)
        def fn(pos1, kwonly1=None, kwonly2=None):
          ...

      If no default value is provided to a keyword argument, it
      becomes a required keyword argument:

        @positional(0)
        def fn(required_kw):
          ...

      This must be called with the keyword parameter:

        fn()  # Raises exception.
        fn(10)  # Raises exception.
        fn(required_kw=10)  # Ok.

      When defining instance or class methods always remember to account for
      'self' and 'cls':

        class MyClass(object):

          @positional(2)
          def my_method(self, pos1, kwonly1=None):
            ...

          @classmethod
          @positional(2)
          def my_method(cls, pos1, kwonly1=None):
            ...

      One can omit the argument to 'positional' altogether, and then no
      arguments with default values may be passed positionally. This
      would be equivalent to placing a '*' before the first argument
      with a default value in Python 3. If there are no arguments with
      default values, and no argument is given to 'positional', an error
      is raised.

        @positional
        def fn(arg1, arg2, required_kw1=None, required_kw2=0):
          ...

        fn(1, 3, 5)  # Raises exception.
        fn(1, 3)  # Ok.
        fn(1, 3, required_kw1=5)  # Ok.

    Args:
      max_positional_arguments: Maximum number of positional arguments.  All
        parameters after the this index must be keyword only.

    Returns:
      A decorator that prevents using arguments after max_positional_args from
      being used as positional parameters.

    Raises:
      TypeError if a keyword-only argument is provided as a positional
        parameter.
      ValueError if no maximum number of arguments is provided and the function
        has no arguments with default values.
    """
    def positional_decorator(wrapped):
        """Creates a function wraper to enforce number of arguments."""