"""Tests for the object inspection functionality.
"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from __future__ import print_function

import os
import re
import sys

import nose.tools as nt

from .. import oinspect
from IPython.core.magic import (Magics, magics_class, line_magic,
                                cell_magic, line_cell_magic,
                                register_line_magic, register_cell_magic,
                                register_line_cell_magic)
from decorator import decorator
from IPython.testing.decorators import skipif
from IPython.testing.tools import AssertPrints
from IPython.utils.path import compress_user
from IPython.utils import py3compat
from IPython.utils.signatures import Signature, Parameter


#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------

inspector = oinspect.Inspector()
ip = get_ipython()

#-----------------------------------------------------------------------------
# Local utilities
#-----------------------------------------------------------------------------

# WARNING: since this test checks the line number where a function is
# defined, if any code is inserted above, the following line will need to be
# updated.  Do NOT insert any whitespace between the next line and the function
# definition below.
THIS_LINE_NUMBER = 43  # Put here the actual number of this line
def test_find_source_lines():
    nt.assert_equal(oinspect.find_source_lines(test_find_source_lines), 
                    THIS_LINE_NUMBER+1)


# A couple of utilities to ensure these tests work the same from a source or a
# binary install
def pyfile(fname):
    return os.path.normcase(re.sub('.py[co]$', '.py', fname))


def match_pyfiles(f1, f2):
    nt.assert_equal(pyfile(f1), pyfile(f2))


def test_find_file():
    match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))


def test_find_file_decorated1():

    @decorator
    def noop1(f):
        def wrapper():
            return f(*a, **kw)
        return wrapper

    @noop1
    def f(x):
        "My docstring"
    
    match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
    nt.assert_equal(f.__doc__, "My docstring")


def test_find_file_decorated2():

    @decorator
    def noop2(f, *a, **kw):
        return f(*a, **kw)

    @noop2
    @noop2
    @noop2
    def f(x):
        "My docstring 2"
    
    match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
    nt.assert_equal(f.__doc__, "My docstring 2")
    

def test_find_file_magic():
    run = ip.find_line_magic('run')
    nt.assert_not_equal(oinspect.find_file(run), None)


# A few generic objects we can then inspect in the tests below

class Call(object):
    """This is the class docstring."""

    def __init__(self, x, y=1):
        """This is the constructor docstring."""

    def __call__(self, *a, **kw):
        """This is the call docstring."""

    def method(self, x, z=2):
        """Some method's docstring"""

class HasSignature(object):
    """This is the class docstring."""
    __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])

    def __init__(self, *args):
        """This is the init docstring"""


class SimpleClass(object):
    def method(self, x, z=2):
        """Some method's docstring"""


class OldStyle:
    """An old-style class for testing."""
    pass


def f(x, y=2, *a, **kw):
    """A simple function."""


def g(y, z=3, *a, **kw):
    pass  # no docstring


@register_line_magic
def lmagic(line):
    "A line magic"


@register_cell_magic
def cmagic(line, cell):
    "A cell magic"


@register_line_cell_magic
def lcmagic(line, cell=None):
    "A line/cell magic"


@magics_class
class SimpleMagics(Magics):
    @line_magic
    def Clmagic(self, cline):
        "A class-based line magic"
        
    @cell_magic
    def Ccmagic(self, cline, ccell):
        "A class-based cell magic"