Search code examples
pythonpython-3.xalgorithmoptimizationrestful-url

Extraction pattern from a defined pattern list using a given string without regex in python


I am currently creating an HTTP server that supports RESTful. In order to support url like /user/:user/:id, I have created a mechanism to find the endpoint from the url.

My current code:


self.index_array= [
    "POST-/foo/__", 
    "GET-/user/__/__"
]

self.index = { 
    "/foo/__": ["POST"],
    "/user/__/__": ["GET"] 
}

self.endpoints = [EndPointClass(...), EndPointClass(...)]

def get(self, method, path):
    # Static
    if path in self.index and method in self.index[path]:
        return self.endpoints[self.index_array.index(method + "-" + path)]

    # Dynamic
    parts = path.split("/")

    for a in parts:
        test = path.replace(a, "__", 1)
        if method + "-" + test in self.index_array:
            return self.endpoints[self.index_array.index(method + "-" + test)]

This above code can route paths like /foo/bar, but cannot route paths with more than one path argument like /user/username/1234. I am thinking of splitting this for method and processing it recursively, but this idea will slow down the execution speed as the path nesting gets deeper.

Also, the reason why I don't use regular expressions is because I believe that using regular expressions will cause terrible backtracking and slow down the process.

Can anyone tell me how I can route an endpoint that contains more than one path argument?

I am using a translator, so if you have any questions, please comment and I will do my best to answer them. If you have any questions or comments, please comment to me.

Thank you.


Solution

  • There is a million ways to do it just keep it simple for yourself. You can turn your index_array into index_tree using dictionaries, for example:

    index_tree = {
    "POST":{"foo":{"endpoint":1}}
    "GET": {
            "user":{
                    "username": {"endpoint":2},
                    "email":{"endpoint":5}
                   },
            "bar":{
                   "DirtyGoose": {"endpoint":3},
                   "3HornUnicorn": {"endpoint":4}
                  }
            }
    }
    

    And then navigate them simply after split:

    nodes = path.split()
    cur = index_tree
    for i, node in enumerate(nodes):
        if "endpoint" in cur:
            if i == len(nodes) - 1:
                return cur["endpoint"], None
            return cur["endpoint"], nodes[i+1:] # arguments for your path
        if node not in cur:
            return None, None
        cur = cur[node] # this will go as deep as you want until "endpoint" or incorrect node
    

    But you could also do it without splitting: save then as sorted list of all possible paths and do bisect to find one that looks like yours. All that in not in a path - throw away or use as parameters.