From 75a473c8e48e7db0c6386bb3cc8b57a501d47ca1 Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Sun, 14 Oct 2018 17:43:02 +0300 Subject: [PATCH 1/2] Increase preference of match w/out parameters. If 'type/subtype' and 'type/subtype;param=1' are supported and 'type/subtype' is accepted, prefer 'type/subtype'. --- mimeparse.py | 16 ++++++++++------ testdata.json | 8 ++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/mimeparse.py b/mimeparse.py index 0218553..13fc44b 100644 --- a/mimeparse.py +++ b/mimeparse.py @@ -97,19 +97,23 @@ def quality_and_fitness_parsed(mime_type, parsed_ranges): # if they do, assess the "fitness" of this mime_type if type_match and subtype_match: - # 100 points if the type matches w/o a wildcard - fitness = type == target_type and 100 or 0 + # 1000 points if the type matches w/o a wildcard + fitness = type == target_type and 1000 or 0 - # 10 points if the subtype matches w/o a wildcard - fitness += subtype == target_subtype and 10 or 0 + # 100 points if the subtype matches w/o a wildcard + fitness += subtype == target_subtype and 100 or 0 - # 1 bonus point for each matching param besides "q" + # 10 bonus points for each matching param besides "q" param_matches = sum([ - 1 for (key, value) in target_params.items() + 10 for (key, value) in target_params.items() if key != 'q' and key in params and value == params[key] ]) fitness += param_matches + # 1 bonus point if neither has any parameters + if (set(target_params) | set(params)) - {'q'} == set(): + fitness += 1 + # finally, add the target's "q" param (between 0 and 1) fitness += float(target_params.get('q', 1)) diff --git a/testdata.json b/testdata.json index 2ceee39..2f21744 100644 --- a/testdata.json +++ b/testdata.json @@ -186,6 +186,14 @@ "text/html", "match should use highest order of supported when there is a tie" ], + [ + [ + ["application/json", "application/json;boundary=NL"], + "application/json" + ], + "application/json", + "exact match without parameters should be preferred over similar one with extra parameters" + ], [ [ ["application/json;q=1.0", "text/html;q=0.9", "text/plain;q=0.1"], From 1dcfb2c260015d799982432dfff9c6c8abcf2f8e Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Thu, 28 Oct 2021 12:11:40 +0300 Subject: [PATCH 2/2] Take q into account always. Take q into account also when comparing options with same type but different parameters. Change fitness into a list of metrics in decreasing order of precedence. This cleaner than building a floating point value now that a condition is evaluated after `q`. --- mimeparse.py | 32 +++++++++++++++++--------------- testdata.json | 8 ++++++++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/mimeparse.py b/mimeparse.py index 55b25e9..1f6f1cd 100644 --- a/mimeparse.py +++ b/mimeparse.py @@ -72,12 +72,12 @@ def quality_and_fitness_parsed(mime_type, parsed_ranges): Find the best match for a given mime-type against a list of media_ranges that have already been parsed by parse_media_range(). Returns a tuple of the fitness value and the value of the 'q' quality parameter of the best - match, or (-1, 0) if no match was found. Just as for quality_parsed(), + match, or (0, [-1]) if no match was found. Just as for quality_parsed(), 'parsed_ranges' must be a list of parsed media ranges. - :rtype: (float,int) + :rtype: (float,list[int]) """ - best_fitness = -1 + best_fitness = [-1] best_fit_q = 0 (target_type, target_subtype, target_params) = \ parse_media_range(mime_type) @@ -92,25 +92,27 @@ def quality_and_fitness_parsed(mime_type, parsed_ranges): # if they do, assess the "fitness" of this mime_type if type_match and subtype_match: - # 1000 points if the type matches w/o a wildcard - fitness = type == target_type and 1000 or 0 + # fitness is a list of metrics in decreasing precedence order + fitness = [] - # 100 points if the subtype matches w/o a wildcard - fitness += subtype == target_subtype and 100 or 0 + # 1) type matches w/o a wildcard + fitness.append(type == target_type) - # 10 bonus points for each matching param besides "q" + # 2) subtype matches w/o a wildcard + fitness.append(subtype == target_subtype) + + # 3) each matching param besides "q" param_matches = sum([ - 10 for (key, value) in target_params.items() + 1 for (key, value) in target_params.items() if key != 'q' and key in params and value == params[key] ]) - fitness += param_matches + fitness.append(param_matches) - # 1 bonus point if neither has any parameters - if (set(target_params) | set(params)) - {'q'} == set(): - fitness += 1 + # 4) target's "q" param (between 0 and 1) + fitness.append(float(target_params.get('q', 1))) - # finally, add the target's "q" param (between 0 and 1) - fitness += float(target_params.get('q', 1)) + # 5) no extra parameters besides q + fitness.append((set(target_params) | set(params)) - {'q'} == set()) if fitness > best_fitness: best_fitness = fitness diff --git a/testdata.json b/testdata.json index 2f21744..044d8ea 100644 --- a/testdata.json +++ b/testdata.json @@ -194,6 +194,14 @@ "application/json", "exact match without parameters should be preferred over similar one with extra parameters" ], + [ + [ + ["application/json;q=0.2", "application/json;q=0.6;foo=bar"], + "application/json" + ], + "application/json;q=0.6;foo=bar", + "q parameter should apply between options of same type" + ], [ [ ["application/json;q=1.0", "text/html;q=0.9", "text/plain;q=0.1"],