I have this CherryPy object to process some simple REST API, but I fail to understand how _cp_dispatch is supposed to work. class Products(object): def __init__(self, product_database): self.product_database = product_database def _cp_dispatch(self, vpath): # batch/ if len(vpath) == 1 and vpath[0] == "batch": vpath[0] = "realbatch" return self # batch/{id}/product/ if len(vpath) == 3 and vpath[0] == "batch": vpath.pop(0) # pop "batch" cherrypy.request.params['batch_id'] = vpath.pop(0) return self # batch/{id}/product/{sku}/ if len(vpath) == 4 and vpath[0] == "batch" and vpath[2] == "product": vpath.pop(0) # pop "batch" cherrypy.request.params['batch_id'] = vpath.pop(0) vpath.pop(0) # pop "product" cherrypy.request.params['sku'] = vpath[0] vpath[0] = "product" return self # defaults return self # POST rest-products/batch --> create a batch (name in JSON body), return batch id # GET rest-products/batch --> return list of batches @cherrypy.expose @cherrypy.tools.json_out() def realbatch(self): # this method is not called when calling "batch/" pass # # POST rest-products/batch/{id}/product --> add product to batch (product data in JSON body) @cherrypy.expose @cherrypy.tools.json_out() def product(self, batch_id, sku = None): # this method is called when calling "batch/{id}/product/" or "batch/{id}/product/{sku} pass As how I understand it, CherryPy tries to find a match on the path (which fails, because it cannot find a batch method). Then it tries to find a catch-all index() which also fails, so it goes to _cp_dispatch. In my case, for batch/ to rewrites it into realbatch/, and tries again. However, that doesn't happen. I can fix it by simply renaming realbatch to index, but then calls to batc/ work as well, which I don't want. My goal is simple: route batch/ to the realbatch() method and batch/{id}/product/{sku} to product(). All other paths should return 404. Continue reading...