# Path: doctor/resource.py
# class ResourceAnnotation(object):
# 
#     """Metadata about the types used for a given request method.
# 
#     :param logic: The logic function for the resource.
#     :param http_method: The http method for this resource.  One of `DELETE`,
#         `GET`, `POST` or `PUT`.
#     :param title: The title to be used by the api documentation for this
#         resource.
#     """
# 
#     def __init__(self, logic: Callable, http_method: str, title: str=None):
#         self.annotated_parameters = {
#             k: p for k, p in logic._doctor_signature.parameters.items()
#             if issubclass(p.annotation, SuperType)
#         }
#         self.http_method = http_method.upper()
#         self.logic = logic
#         self.params = logic._doctor_params
#         self.return_annotation = logic._doctor_signature.return_annotation
#         self.title = title
#         if title is None:
#             self.title = HTTP_METHOD_TITLES.get(http_method.upper())
# 
# class ResourceSchemaAnnotation(object):
# 
#     """Metadata about the schema used for a given request method.
# 
#     An instance of this class is attached to each handler method in a
#     _schema_annotation attribute. It can be used for introspection about the
#     schemas, to generate things like API documentation and hyper schemas from
#     the code.
# 
#     :param func logic: Logic function which will handle the request.
#     :param str http_method: The HTTP request method for this request (e.g. GET).
#     :param doctor.resource.ResourceSchema schema: The resource schema
#         object for this handler.
#     :param dict request_schema: The schema used to validate the request.
#     :param dict response_schema: The schema used to validate the response.
#     :param str title: A short title for the route.  e.g. 'Create Foo' might
#         be used for a POST method on a FooListHandler.
#     """
# 
#     def __init__(self, logic, http_method, schema, request_schema,
#                  response_schema, title=None):
#         self.logic = logic
#         self.http_method = http_method
#         self.schema = schema
#         self.request_schema = request_schema
#         self.response_schema = response_schema
#         self.title = title
#         if title is None:
#             self.title = HTTP_METHOD_TITLES.get(http_method.upper())
# 
#     @classmethod
#     def get_annotation(cls, fn):
#         """Find the _schema_annotation attribute for the given function.
# 
#         This will descend through decorators until it finds something that has
#         the attribute. If it doesn't find it anywhere, it will return None.
# 
#         :param func fn: Find the attribute on this function.
#         :returns: an instance of
#             :class:`~doctor.resource.ResourceSchemaAnnotation` or
#             None.
#         """
#         while fn is not None:
#             if hasattr(fn, '_schema_annotation'):
#                 return fn._schema_annotation
#             fn = getattr(fn, 'im_func', fn)
#             closure = getattr(fn, '__closure__', None)
#             fn = closure[0].cell_contents if closure is not None else None
#         return None
# 
# Path: test/base.py
# class TestCase(unittest.TestCase):
# 
#     maxDiff = None
# 
#     def shortDescription(self):
#         # Do not show first line of the docstring as a test description
#         return None
# 
# Path: test/types.py
# def parse_comma_separated_str(value):
# class ExampleObject(Object):
# class FooInstance(Object):
# class AgeOrColor(UnionType):
# class ColorsOrObject(UnionType):
# 
# Path: test/utils.py
# def add_doctor_attrs(func, req_obj_type: Callable = None):
#     """Adds required _doctor* attrs to a function so it can be used in tests.
# 
#     :param req_obj_type: A doctor :class:`~doctor.types.Object` type that the
#         request body should be converted to.
#     """
#     sig = inspect.signature(func)
#     func._doctor_req_obj_type = req_obj_type
#     params = get_params_from_func(func)
#     func._doctor_allowed_exceptions = None
#     func._doctor_signature = sig
#     func._doctor_params = params
#     return func


from functools import wraps
from inspect import Parameter
from doctor.resource import ResourceAnnotation, ResourceSchemaAnnotation
from .base import TestCase
from .types import ItemId
from .utils import add_doctor_attrs
import mock
def plain_logic_with_kwargs(a, b=1, **kwargs):
    return (a, b, kwargs)


@does_nothing
def decorated_logic(a, b=1):
    return (a, b)


@does_nothing
def decorated_logic_with_kwargs(a, b=1, **kwargs):
    return (a, b, kwargs)


class TestResourceAnnotation(TestCase):

    def test_init(self):
        def logic(user_id: int, item_id: ItemId):
            pass

        logic = add_doctor_attrs(logic)
        annotation = ResourceAnnotation(logic, 'POST')
        assert annotation.logic == logic
        assert annotation.http_method == 'POST'
        assert annotation.title == 'Create'

        # Verify that the annotated_parameters attribute only has parameters
        # from the signature that are doctor types.  It shouldn't include
        # `user_id` since int doesn't extend doctor.types.SuperType
        expected = Parameter('item_id', Parameter.POSITIONAL_OR_KEYWORD,

# Path: doctor/resource.py
# class ResourceAnnotation(object):
# 
#     """Metadata about the types used for a given request method.
# 
#     :param logic: The logic function for the resource.
#     :param http_method: The http method for this resource.  One of `DELETE`,
#         `GET`, `POST` or `PUT`.
#     :param title: The title to be used by the api documentation for this
#         resource.
#     """
# 
#     def __init__(self, logic: Callable, http_method: str, title: str=None):
#         self.annotated_parameters = {
#             k: p for k, p in logic._doctor_signature.parameters.items()
#             if issubclass(p.annotation, SuperType)
#         }
#         self.http_method = http_method.upper()
#         self.logic = logic
#         self.params = logic._doctor_params
#         self.return_annotation = logic._doctor_signature.return_annotation
#         self.title = title
#         if title is None:
#             self.title = HTTP_METHOD_TITLES.get(http_method.upper())
# 
# class ResourceSchemaAnnotation(object):
# 
#     """Metadata about the schema used for a given request method.
# 
#     An instance of this class is attached to each handler method in a
#     _schema_annotation attribute. It can be used for introspection about the
#     schemas, to generate things like API documentation and hyper schemas from
#     the code.
# 
#     :param func logic: Logic function which will handle the request.
#     :param str http_method: The HTTP request method for this request (e.g. GET).
#     :param doctor.resource.ResourceSchema schema: The resource schema
#         object for this handler.
#     :param dict request_schema: The schema used to validate the request.
#     :param dict response_schema: The schema used to validate the response.
#     :param str title: A short title for the route.  e.g. 'Create Foo' might
#         be used for a POST method on a FooListHandler.
#     """
# 
#     def __init__(self, logic, http_method, schema, request_schema,
#                  response_schema, title=None):
#         self.logic = logic
#         self.http_method = http_method
#         self.schema = schema
#         self.request_schema = request_schema
#         self.response_schema = response_schema
#         self.title = title
#         if title is None:
#             self.title = HTTP_METHOD_TITLES.get(http_method.upper())
# 
#     @classmethod
#     def get_annotation(cls, fn):
#         """Find the _schema_annotation attribute for the given function.
# 
#         This will descend through decorators until it finds something that has
#         the attribute. If it doesn't find it anywhere, it will return None.
# 
#         :param func fn: Find the attribute on this function.
#         :returns: an instance of
#             :class:`~doctor.resource.ResourceSchemaAnnotation` or
#             None.
#         """
#         while fn is not None:
#             if hasattr(fn, '_schema_annotation'):
#                 return fn._schema_annotation
#             fn = getattr(fn, 'im_func', fn)
#             closure = getattr(fn, '__closure__', None)
#             fn = closure[0].cell_contents if closure is not None else None
#         return None
# 
# Path: test/base.py
# class TestCase(unittest.TestCase):
# 
#     maxDiff = None
# 
#     def shortDescription(self):
#         # Do not show first line of the docstring as a test description
#         return None
# 
# Path: test/types.py
# def parse_comma_separated_str(value):
# class ExampleObject(Object):
# class FooInstance(Object):
# class AgeOrColor(UnionType):
# class ColorsOrObject(UnionType):
# 
# Path: test/utils.py
# def add_doctor_attrs(func, req_obj_type: Callable = None):
#     """Adds required _doctor* attrs to a function so it can be used in tests.
# 
#     :param req_obj_type: A doctor :class:`~doctor.types.Object` type that the
#         request body should be converted to.
#     """
#     sig = inspect.signature(func)
#     func._doctor_req_obj_type = req_obj_type
#     params = get_params_from_func(func)
#     func._doctor_allowed_exceptions = None
#     func._doctor_signature = sig
#     func._doctor_params = params
#     return func


# Path: test/test_resource.py
from functools import wraps
from inspect import Parameter
from doctor.resource import ResourceAnnotation, ResourceSchemaAnnotation
from .base import TestCase
from .types import ItemId
from .utils import add_doctor_attrs
import mock

def plain_logic_with_kwargs(a, b=1, **kwargs):
    return (a, b, kwargs)


@does_nothing
def decorated_logic(a, b=1):
    return (a, b)


@does_nothing
def decorated_logic_with_kwargs(a, b=1, **kwargs):
    return (a, b, kwargs)


class TestResourceAnnotation(TestCase):

    def test_init(self):
        def logic(user_id: int, item_id: ItemId):
            pass

        logic = add_doctor_attrs(logic)
        annotation = ResourceAnnotation(logic, 'POST')
        assert annotation.logic == logic
        assert annotation.http_method == 'POST'
        assert annotation.title == 'Create'

        # Verify that the annotated_parameters attribute only has parameters
        # from the signature that are doctor types.  It shouldn't include
        # `user_id` since int doesn't extend doctor.types.SuperType
        expected = Parameter('item_id', Parameter.POSITIONAL_OR_KEYWORD,

                             default=Parameter.empty, annotation=ItemId)