Accept.best_match should support media type ranges, e.g. image/*

Create issue
Issue #8 wontfix
Matt Goodall created an issue

Accept.best_match currently checks for a "" in the offers and raises an exception. However, it's quite reasonable for an application to know how to return any representation of a resource, e.g. a PNG, GIF, etc version of an image. If the application was trying to decide where to route a request it would need to pass image/ to best_match as one of the offers.

Comments (8)

  1. Sergey Schetinin

    I understand what you're saying, but a code sample to reproduce the problem would be great.

  2. Sergey Schetinin

    OK, here's my reasoning, why wildcard offers are invalid: even if you know how to output gif, png, bmp etc does not mean you can also satisfy request for image/x-foo. No matter how many output formats you support it's a finite set and to see which one you should use, you should just offer a list.

    Do you agree with that? If there's something related to routing that I'm missing, please elaborate.

  3. Matt Goodall reporter

    It's true that something that matches for image/* might not be able to handle image/x-foo but I think that's a problem for the application. If the application wants to *try* to handle all image types I don't think WebOb should disallow it.

    As a crude example, an image/* handler could attempt to convert an image using PIL without really caring what the client actually asked for by iterating the client's Accept options looking for image/something items and trying the 'something' bit as the new format. If necessary, the application could also have an explicit handler for image/x-foo. Hypothetical and untested example:

        def image_any(req):
            img = load_image()
            for mimetype in req.accept:
                if mimetype.startswith('image/'):
                       return convert_image(img, mimetype.split('/')[1])
                     except UnsupportedFormat:
            return HTTPNotAcceptable(...)
        def image_xfoo(req):
            return convert_image_xfoo(img)

    That probably demonstrates what I meant by "routing" too :).

    Incidentally, the main reason I submitted this ticket is because I tried to remove a dependency on mimeparse. mimeparse does support an image/* offer in its implementation of best_match abd I was surprised to find WebOb did not.

  4. Sergey Schetinin

    I see, but I imagine that PIL does provide a list of formats it can handle, I think it's a much better idea to query for that and generate the list of offered formats accordingly, adding any additional formats as necessary.

    Also, I believe that while this seems like it would be a simpler to just let wildcard among the offers it in fact introduces a fragility that most users would not immediately recognize, and from the example you provided, I think you might have missed it as well. Consider this case: the request has accept: image/*. The offer is image/* as well. If you run req.accept.best_match(['image/*']) you get image/* as a match. And that result is useless. A best match definitely should be a specific mime type and therefore all of the offers have to be specific as well, this is a very useful invariant.

    See, in your sample code you could be calling convert_image(img, '*'). You might handle that, but wouldn't it be much cleaner to do mimetype = req.accept.best_match([native_mimetype]+pil_mimetypes+['image/x-foo']), possibly adding quality ratings as well?

  5. Matt Goodall reporter

    PIL may provide a list of supported formats, I don't know, but what if it doesn't? And the image/* issue is trivial to handle by having a fallback that you know the image conversion library supports. It was only a "hypothetical and untested" example after all ;-).

    It's not a big issue for me - I can stick with mimeparse - but it does seem a shame that WebOb stops an application doing whatever makes most sense.

  6. Sergey Schetinin

    BTW, the example did not show the code that would actually produce the error.

    It's not as simple though. Think about a different case -- the request accepts image/png, you offer image/*, the match would have to be image/png in that case. And that's not one of the things that were explicitly offered. The quality ratings handling is gone as well.

    Basically this issue is the case where an 'immediately obvious' behavior adds complexity to the library, makes the library user do more work and makes things overall more fragile and less well defined. Trust me, getting a definite list of mimetypes you can produce is the best way to do it.

  7. Matt Goodall reporter

    Actually, best_match, "returns the best match in the sequence of offered types", so the match in your example would be image/*, i.e. exactly what the application said it could offer.

  8. Sergey Schetinin

    Right, that's because it does not allow the wildcards to be offered. If it did, it would be pointless to return that image/* is a match if the user-agent only accepts image/png. So the behavior and the docstring would have to change.

  9. Log in to comment