# Source code for cherrypy.test.test_objectmapping

    import sys
    import cherrypy
    from cherrypy._cpcompat import ntou
    from cherrypy._cptree import Application
    from cherrypy.test import helper

    script_names = ['', '/foo', '/users/fred/blog', '/corp/blog']

    class ObjectMappingTest(helper.CPWebCase):

        @staticmethod
        def setup_server():
            class Root:

                @cherrypy.expose
                def index(self, name='world'):
                    return name

                @cherrypy.expose
                def foobar(self):
                    return 'bar'

                @cherrypy.expose
                def default(self, *params, **kwargs):
                    return 'default:' + repr(params)

                @cherrypy.expose
                def other(self):
                    return 'other'

                @cherrypy.expose
                def extra(self, *p):
                    return repr(p)

                @cherrypy.expose
                def redirect(self):
                    raise cherrypy.HTTPRedirect('dir1/', 302)

                def notExposed(self):
                    return 'not exposed'

                @cherrypy.expose
                def confvalue(self):
                    return cherrypy.request.config.get('user')

                @cherrypy.expose
                def redirect_via_url(self, path):
                    raise cherrypy.HTTPRedirect(cherrypy.url(path))

                @cherrypy.expose
                def translate_html(self):
                    return 'OK'

            @cherrypy.expose
            def mapped_func(self, ID=None):
                return 'ID is %s' % ID
            setattr(Root, 'Von B\xfclow', mapped_func)

            class Exposing:

                @cherrypy.expose
                def base(self):
                    return 'expose works!'
                cherrypy.expose(base, '1')
                cherrypy.expose(base, '2')

            class ExposingNewStyle(object):

                @cherrypy.expose
                def base(self):
                    return 'expose works!'
                cherrypy.expose(base, '1')
                cherrypy.expose(base, '2')

            class Dir1:

                @cherrypy.expose
                def index(self):
                    return 'index for dir1'

                @cherrypy.expose
                @cherrypy.config(**{'tools.trailing_slash.extra': True})
                def myMethod(self):
                    return 'myMethod from dir1, path_info is:' + repr(
                        cherrypy.request.path_info)

                @cherrypy.expose
                def default(self, *params):
                    return 'default for dir1, param is:' + repr(params)

            class Dir2:

                @cherrypy.expose
                def index(self):
                    return 'index for dir2, path is:' + cherrypy.request.path_info

                @cherrypy.expose
                def script_name(self):
                    return cherrypy.tree.script_name()

                @cherrypy.expose
                def cherrypy_url(self):
                    return cherrypy.url('/extra')

                @cherrypy.expose
                def posparam(self, *vpath):
                    return '/'.join(vpath)

            class Dir3:

                def default(self):
                    return 'default for dir3, not exposed'

            class Dir4:

                def index(self):
                    return 'index for dir4, not exposed'

            class DefNoIndex:

                @cherrypy.expose
                def default(self, *args):
                    raise cherrypy.HTTPRedirect('contact')

            # MethodDispatcher code
            @cherrypy.expose
            class ByMethod:

                def __init__(self, *things):
                    self.things = list(things)

                def GET(self):
                    return repr(self.things)

                def POST(self, thing):
                    self.things.append(thing)

            class Collection:
                default = ByMethod('a', 'bit')

            Root.exposing = Exposing()
            Root.exposingnew = ExposingNewStyle()
            Root.dir1 = Dir1()
            Root.dir1.dir2 = Dir2()
            Root.dir1.dir2.dir3 = Dir3()
            Root.dir1.dir2.dir3.dir4 = Dir4()
            Root.defnoindex = DefNoIndex()
            Root.bymethod = ByMethod('another')
            Root.collection = Collection()

            d = cherrypy.dispatch.MethodDispatcher()
            for url in script_names:
                conf = {'/': {'user': (url or '/').split('/')[-2]},
                        '/bymethod': {'request.dispatch': d},
                        '/collection': {'request.dispatch': d},
                        }
                cherrypy.tree.mount(Root(), url, conf)

            class Isolated:

                @cherrypy.expose
                def index(self):
                    return 'made it!'

            cherrypy.tree.mount(Isolated(), '/isolated')

            @cherrypy.expose
            class AnotherApp:

                def GET(self):
                    return 'milk'

            cherrypy.tree.mount(AnotherApp(), '/app',
                                {'/': {'request.dispatch': d}})

        def testObjectMapping(self):
            for url in script_names:
                self.script_name = url

                self.getPage('/')
                self.assertBody('world')

                self.getPage('/dir1/myMethod')
                self.assertBody(
                    "myMethod from dir1, path_info is:'/dir1/myMethod'")

                self.getPage('/this/method/does/not/exist')
                self.assertBody(
                    "default:('this', 'method', 'does', 'not', 'exist')")

                self.getPage('/extra/too/much')
                self.assertBody("('too', 'much')")

                self.getPage('/other')
                self.assertBody('other')

                self.getPage('/notExposed')
                self.assertBody("default:('notExposed',)")

                self.getPage('/dir1/dir2/')
                self.assertBody('index for dir2, path is:/dir1/dir2/')

                # Test omitted trailing slash (should be redirected by default).
                self.getPage('/dir1/dir2')
                self.assertStatus(301)
                self.assertHeader('Location', '%s/dir1/dir2/' % self.base())

                # Test extra trailing slash (should be redirected if configured).
                self.getPage('/dir1/myMethod/')
                self.assertStatus(301)
                self.assertHeader('Location', '%s/dir1/myMethod' % self.base())

                # Test that default method must be exposed in order to match.
                self.getPage('/dir1/dir2/dir3/dir4/index')
                self.assertBody(
                    "default for dir1, param is:('dir2', 'dir3', 'dir4', 'index')")

                # Test *vpath when default() is defined but not index()
                # This also tests HTTPRedirect with default.
                self.getPage('/defnoindex')
                self.assertStatus((302, 303))
                self.assertHeader('Location', '%s/contact' % self.base())
                self.getPage('/defnoindex/')
                self.assertStatus((302, 303))
                self.assertHeader('Location', '%s/defnoindex/contact' %
                                  self.base())
                self.getPage('/defnoindex/page')
                self.assertStatus((302, 303))
                self.assertHeader('Location', '%s/defnoindex/contact' %
                                  self.base())

                self.getPage('/redirect')
                self.assertStatus('302 Found')
                self.assertHeader('Location', '%s/dir1/' % self.base())

                if not getattr(cherrypy.server, 'using_apache', False):
                    # Test that we can use URL's which aren't all valid Python
                    # identifiers
                    # This should also test the %XX-unquoting of URL's.
                    self.getPage('/Von%20B%fclow?ID=14')
                    self.assertBody('ID is 14')

                    # Test that %2F in the path doesn't get unquoted too early;
                    # that is, it should not be used to separate path components.
                    # See ticket #393.
                    self.getPage('/page%2Fname')
                    self.assertBody("default:('page/name',)")

                self.getPage('/dir1/dir2/script_name')
                self.assertBody(url)
                self.getPage('/dir1/dir2/cherrypy_url')
                self.assertBody('%s/extra' % self.base())

                # Test that configs don't overwrite each other from different apps
                self.getPage('/confvalue')
                self.assertBody((url or '/').split('/')[-2])

            self.script_name = ''

            # Test absoluteURI's in the Request-Line
            self.getPage('http://%s:%s/' % (self.interface(), self.PORT))
            self.assertBody('world')

            self.getPage('http://%s:%s/abs/?service=http://192.168.0.1/x/y/z' %
                         (self.interface(), self.PORT))
            self.assertBody("default:('abs',)")

            self.getPage('/rel/?service=http://192.168.120.121:8000/x/y/z')
            self.assertBody("default:('rel',)")

            # Test that the "isolated" app doesn't leak url's into the root app.
            # If it did leak, Root.default() would answer with
            #   "default:('isolated', 'doesnt', 'exist')".
            self.getPage('/isolated/')
            self.assertStatus('200 OK')
            self.assertBody('made it!')
            self.getPage('/isolated/doesnt/exist')
            self.assertStatus('404 Not Found')

            # Make sure /foobar maps to Root.foobar and not to the app
            # mounted at /foo. See
            # https://github.com/cherrypy/cherrypy/issues/573
            self.getPage('/foobar')
            self.assertBody('bar')

        def test_translate(self):
            self.getPage('/translate_html')
            self.assertStatus('200 OK')
            self.assertBody('OK')

            self.getPage('/translate.html')
            self.assertStatus('200 OK')
            self.assertBody('OK')

            self.getPage('/translate-html')
            self.assertStatus('200 OK')
            self.assertBody('OK')

        def test_redir_using_url(self):
            for url in script_names:
                self.script_name = url

                # Test the absolute path to the parent (leading slash)
                self.getPage('/redirect_via_url?path=./')
                self.assertStatus(('302 Found', '303 See Other'))
                self.assertHeader('Location', '%s/' % self.base())

                # Test the relative path to the parent (no leading slash)
                self.getPage('/redirect_via_url?path=./')
                self.assertStatus(('302 Found', '303 See Other'))
                self.assertHeader('Location', '%s/' % self.base())

                # Test the absolute path to the parent (leading slash)
                self.getPage('/redirect_via_url/?path=./')
                self.assertStatus(('302 Found', '303 See Other'))
                self.assertHeader('Location', '%s/' % self.base())

                # Test the relative path to the parent (no leading slash)
                self.getPage('/redirect_via_url/?path=./')
                self.assertStatus(('302 Found', '303 See Other'))
                self.assertHeader('Location', '%s/' % self.base())

        def testPositionalParams(self):
            self.getPage('/dir1/dir2/posparam/18/24/hut/hike')
            self.assertBody('18/24/hut/hike')

            # intermediate index methods should not receive posparams;
            # only the "final" index method should do so.
            self.getPage('/dir1/dir2/5/3/sir')
            self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")

            # test that extra positional args raises an 404 Not Found
            # See https://github.com/cherrypy/cherrypy/issues/733.
            self.getPage('/dir1/dir2/script_name/extra/stuff')
            self.assertStatus(404)

        def testExpose(self):
            # Test the cherrypy.expose function/decorator
            self.getPage('/exposing/base')
            self.assertBody('expose works!')

            self.getPage('/exposing/1')
            self.assertBody('expose works!')

            self.getPage('/exposing/2')
            self.assertBody('expose works!')

            self.getPage('/exposingnew/base')
            self.assertBody('expose works!')

            self.getPage('/exposingnew/1')
            self.assertBody('expose works!')

            self.getPage('/exposingnew/2')
            self.assertBody('expose works!')

        def testMethodDispatch(self):
            self.getPage('/bymethod')
            self.assertBody("['another']")
            self.assertHeader('Allow', 'GET, HEAD, POST')

            self.getPage('/bymethod', method='HEAD')
            self.assertBody('')
            self.assertHeader('Allow', 'GET, HEAD, POST')

            self.getPage('/bymethod', method='POST', body='thing=one')
            self.assertBody('')
            self.assertHeader('Allow', 'GET, HEAD, POST')

            self.getPage('/bymethod')
            self.assertBody(repr(['another', ntou('one')]))
            self.assertHeader('Allow', 'GET, HEAD, POST')

            self.getPage('/bymethod', method='PUT')
            self.assertErrorPage(405)
            self.assertHeader('Allow', 'GET, HEAD, POST')

            # Test default with posparams
            self.getPage('/collection/silly', method='POST')
            self.getPage('/collection', method='GET')
            self.assertBody("['a', 'bit', 'silly']")

            # Test custom dispatcher set on app root (see #737).
            self.getPage('/app')
            self.assertBody('milk')

        def testTreeMounting(self):
            class Root(object):

                @cherrypy.expose
                def hello(self):
                    return 'Hello world!'

            # When mounting an application instance,
            # we can't specify a different script name in the call to mount.
            a = Application(Root(), '/somewhere')
            self.assertRaises(ValueError, cherrypy.tree.mount, a, '/somewhereelse')

            # When mounting an application instance...
            a = Application(Root(), '/somewhere')
            # ...we MUST allow in identical script name in the call to mount...
            cherrypy.tree.mount(a, '/somewhere')
            self.getPage('/somewhere/hello')
            self.assertStatus(200)
            # ...and MUST allow a missing script_name.
            del cherrypy.tree.apps['/somewhere']
            cherrypy.tree.mount(a)
            self.getPage('/somewhere/hello')
            self.assertStatus(200)

            # In addition, we MUST be able to create an Application using
            # script_name == None for access to the wsgi_environ.
            a = Application(Root(), script_name=None)
            # However, this does not apply to tree.mount
            self.assertRaises(TypeError, cherrypy.tree.mount, a, None)

        def testKeywords(self):
            if sys.version_info < (3,):
                return self.skip('skipped (Python 3 only)')
            exec("""class Root(object):
        @cherrypy.expose
        def hello(self, *, name='world'):
            return 'Hello %s!' % name
    cherrypy.tree.mount(Application(Root(), '/keywords'))""")

            self.getPage('/keywords/hello')
            self.assertStatus(200)
            self.getPage('/keywords/hello/extra')
            self.assertStatus(404)