Reponse A: Anthropic Claude Opus 4.8
import copy
class JsonPatchError(Exception):
"""Raised when a JSON Patch operation is invalid or fails."""
pass
def _unescape_token(token):
# Validate and decode a single JSON Pointer reference token.
out = []
i = 0
n = len(token)
while i < n:
ch = token[i]
if ch == '~':
if i + 1 >= n:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
nxt = token[i + 1]
if nxt == '0':
out.append('~')
elif nxt == '1':
out.append('/')
else:
raise Jso...
Afficher la reponse complete ▼
import copy
class JsonPatchError(Exception):
"""Raised when a JSON Patch operation is invalid or fails."""
pass
def _unescape_token(token):
# Validate and decode a single JSON Pointer reference token.
out = []
i = 0
n = len(token)
while i < n:
ch = token[i]
if ch == '~':
if i + 1 >= n:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
nxt = token[i + 1]
if nxt == '0':
out.append('~')
elif nxt == '1':
out.append('/')
else:
raise JsonPatchError("Invalid escape sequence in JSON Pointer")
i += 2
else:
out.append(ch)
i += 1
return ''.join(out)
def _parse_pointer(path):
if not isinstance(path, str):
raise JsonPatchError("JSON Pointer must be a string")
if path == "":
return []
if not path.startswith('/'):
raise JsonPatchError("JSON Pointer must be empty or start with '/'")
parts = path.split('/')[1:]
return [_unescape_token(p) for p in parts]
def _is_valid_array_index(token, allow_dash=False):
if allow_dash and token == '-':
return True
if token == '0':
return True
if not token:
return False
if not token.isdigit():
return False
if token[0] == '0':
return False
return True
def _resolve_parent(doc, tokens, allow_dash_last=False):
# Navigate to the container that holds the final token.
# Returns (parent, final_token).
if not tokens:
raise JsonPatchError("Path refers to whole document; no parent")
current = doc
for tok in tokens[:-1]:
current = _descend(current, tok, allow_dash=False)
return current, tokens[-1]
def _descend(container, token, allow_dash=False):
if isinstance(container, dict):
if token not in container:
raise JsonPatchError("Object key not found: %r" % (token,))
return container[token]
elif isinstance(container, list):
if not _is_valid_array_index(token, allow_dash=allow_dash):
raise JsonPatchError("Invalid array index: %r" % (token,))
if token == '-':
raise JsonPatchError("Cannot descend into '-' on array")
idx = int(token)
if idx < 0 or idx >= len(container):
raise JsonPatchError("Array index out of range: %d" % idx)
return container[idx]
else:
raise JsonPatchError("Cannot navigate into non-container value")
def _get_value(doc, tokens):
current = doc
for tok in tokens:
current = _descend(current, tok, allow_dash=False)
return current
def _do_add(doc, tokens, value):
if not tokens:
return value
parent, last = _resolve_parent(doc, tokens, allow_dash_last=True)
if isinstance(parent, dict):
if last == '-':
raise JsonPatchError("'-' is not valid for object add")
parent[last] = value
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=True):
raise JsonPatchError("Invalid array index for add: %r" % (last,))
if last == '-':
parent.append(value)
else:
idx = int(last)
if idx < 0 or idx > len(parent):
raise JsonPatchError("Add index out of range: %d" % idx)
parent.insert(idx, value)
else:
raise JsonPatchError("Cannot add into non-container value")
return doc
def _do_remove(doc, tokens):
if not tokens:
raise JsonPatchError("Cannot remove whole document")
parent, last = _resolve_parent(doc, tokens)
if isinstance(parent, dict):
if last not in parent:
raise JsonPatchError("Cannot remove nonexistent key: %r" % (last,))
del parent[last]
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=False):
raise JsonPatchError("Invalid array index for remove: %r" % (last,))
idx = int(last)
if idx < 0 or idx >= len(parent):
raise JsonPatchError("Remove index out of range: %d" % idx)
del parent[idx]
else:
raise JsonPatchError("Cannot remove from non-container value")
return doc
def _do_replace(doc, tokens, value):
if not tokens:
return value
parent, last = _resolve_parent(doc, tokens)
if isinstance(parent, dict):
if last not in parent:
raise JsonPatchError("Cannot replace nonexistent key: %r" % (last,))
parent[last] = value
elif isinstance(parent, list):
if not _is_valid_array_index(last, allow_dash=False):
raise JsonPatchError("Invalid array index for replace: %r" % (last,))
idx = int(last)
if idx < 0 or idx >= len(parent):
raise JsonPatchError("Replace index out of range: %d" % idx)
parent[idx] = value
else:
raise JsonPatchError("Cannot replace in non-container value")
return doc
def _deep_equal(a, b):
# Exact type-aware equality. Disallow bool/number cross matches.
if isinstance(a, bool) or isinstance(b, bool):
if type(a) is not type(b):
return False
return a == b
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return a == b
if type(a) is not type(b):
return False
if a is None:
return b is None
if isinstance(a, str):
return a == b
if isinstance(a, list):
if len(a) != len(b):
return False
return all(_deep_equal(x, y) for x, y in zip(a, b))
if isinstance(a, dict):
if set(a.keys()) != set(b.keys()):
return False
return all(_deep_equal(a[k], b[k]) for k in a)
return a == b
def _is_prefix(prefix_tokens, tokens):
if len(prefix_tokens) > len(tokens):
return False
return tokens[:len(prefix_tokens)] == prefix_tokens
_REQUIRED_FIELDS = {
"add": {"op", "path", "value"},
"remove": {"op", "path"},
"replace": {"op", "path", "value"},
"move": {"op", "path", "from"},
"copy": {"op", "path", "from"},
"test": {"op", "path", "value"},
}
def _validate_op_fields(operation):
if not isinstance(operation, dict):
raise JsonPatchError("Operation must be a dict")
if "op" not in operation:
raise JsonPatchError("Operation missing 'op' field")
op = operation["op"]
if op not in _REQUIRED_FIELDS:
raise JsonPatchError("Unknown operation: %r" % (op,))
required = _REQUIRED_FIELDS[op]
actual = set(operation.keys())
if actual != required:
raise JsonPatchError("Operation %r has invalid fields" % (op,))
return op
def apply_json_patch(document, patch):
if not isinstance(patch, list):
raise JsonPatchError("Patch must be a list of operations")
# Work on a deep copy so the original is never mutated.
working = copy.deepcopy(document)
for operation in patch:
op = _validate_op_fields(operation)
if op == "add":
tokens = _parse_pointer(operation["path"])
working = _do_add(working, tokens, copy.deepcopy(operation["value"]))
elif op == "remove":
tokens = _parse_pointer(operation["path"])
working = _do_remove(working, tokens)
elif op == "replace":
tokens = _parse_pointer(operation["path"])
working = _do_replace(working, tokens, copy.deepcopy(operation["value"]))
elif op == "move":
from_tokens = _parse_pointer(operation["from"])
path_tokens = _parse_pointer(operation["path"])
if _is_prefix(from_tokens, path_tokens) and from_tokens != path_tokens:
raise JsonPatchError("Cannot move into one of its own descendants")
value = _get_value(working, from_tokens)
value = copy.deepcopy(value)
working = _do_remove(working, from_tokens)
working = _do_add(working, path_tokens, value)
elif op == "copy":
from_tokens = _parse_pointer(operation["from"])
path_tokens = _parse_pointer(operation["path"])
value = _get_value(working, from_tokens)
value = copy.deepcopy(value)
working = _do_add(working, path_tokens, value)
elif op == "test":
tokens = _parse_pointer(operation["path"])
current = _get_value(working, tokens)
if not _deep_equal(current, operation["value"]):
raise JsonPatchError("Test operation failed")
return working
Resultat
Votes gagnants
3 / 3
Score moyen
Score total
Commentaire global
La réponse A est une implémentation bien conçue qui gère correctement les six opérations JSON Patch avec une attention particulière aux cas limites. Les points forts incluent : une fonction _deep_equal appropriée qui distingue correctement les booléens des entiers (crucial pour l'opération test), une vérification des descendants au niveau des jetons pour l'opération move à l'aide de _is_prefix, une validation correcte de l'échappement ~ dans _unescape_token, une validation exacte des champs à l'aide du dictionnaire _REQUIRED_FIELDS, et une atomicité appropriée via une copie profonde avant toute mutation. Le code est bien organisé avec des fonctions d'aide claires et est pratiquement fiable.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%La réponse A implémente correctement les six opérations avec une validation minutieuse. La fonction _deep_equal gère correctement la distinction entre les types booléen/entier, ce qui est crucial pour l'opération test. L'opération move vérifie correctement les chemins descendants à l'aide d'une comparaison au niveau des jetons. La fonction _unescape_token valide correctement que ~ n'est suivi que de 0 ou 1. La validation des indices de tableau rejette correctement les zéros non significatifs. L'atomicité est préservée en copiant profondément le document avant toute mutation. Un problème mineur : dans l'opération move, le déplacement d'un chemin vers lui-même (from_tokens == path_tokens) n'est pas explicitement géré comme une opération nulle, mais le code fonctionnerait toujours (suppression puis réinsertion). Dans l'ensemble, une correction très solide.
Completude
Poids 20%La réponse A implémente les six opérations requises (add, remove, replace, move, copy, test), gère le remplacement du document racine, valide la syntaxe JSON Pointer y compris les séquences ~ invalides, valide les indices de tableau y compris les zéros non significatifs, valide exactement les champs d'opération (aucun supplémentaire, aucun manquant), gère le jeton '-' pour l'ajout de tableau, et copie profondément les valeurs de manière appropriée. Le dictionnaire _REQUIRED_FIELDS garantit une validation exacte des champs. Tous les cas limites mentionnés dans la spécification sont traités.
Qualite du code
Poids 20%La réponse A est bien structurée avec des fonctions d'aide claires, chacune ayant une responsabilité unique. Le dictionnaire _REQUIRED_FIELDS est une approche élégante pour la validation des champs. Le code est lisible et maintenable. La fonction _deep_equal est soigneusement implémentée. La fonction _is_prefix pour la validation de move est propre. Problème mineur : le code duplique une partie de la logique entre _do_add, _do_remove, _do_replace plutôt que d'utiliser une fonction d'aide de parcours partagée, mais cela est acceptable pour la clarté.
Valeur pratique
Poids 15%La réponse A est pratiquement très utile. Elle gère correctement tous les cas limites importants dans l'utilisation réelle de JSON Patch, y compris la distinction booléen/entier dans les opérations test, la vérification appropriée des descendants au niveau des jetons pour move, et la validation correcte de l'échappement ~. La garantie d'atomicité est correctement implémentée. Le code fonctionnerait correctement dans des scénarios de production.
Respect des consignes
Poids 10%La réponse A suit toutes les instructions : utilise Python 3.11, implémente apply_json_patch(document, patch), définit JsonPatchError, utilise uniquement la bibliothèque standard (module copy), ne mute pas le document d'origine, lève JsonPatchError pour les opérations invalides, gère les six opérations, valide la syntaxe JSON Pointer, gère tous les cas limites spécifiés. La signature de la fonction et le nom de la classe correspondent exactement.
Score total
Commentaire global
La réponse A fournit une implémentation robuste et largement correcte de JSON Patch. Son analyse des pointeurs JSON, la validation des index de tableau et la logique d'égalité profonde de l'opération `test` sont particulièrement solides et respectent bien la spécification. Le code est bien structuré et lisible, avec une séparation claire des responsabilités dans des fonctions auxiliaires. Le principal inconvénient est une erreur sémantique dans l'opération `move`, où elle copie profondément la valeur au lieu de déplacer la référence, ce qui s'écarte du comportement standard de JSON Patch.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%La réponse A fournit une implémentation très correcte. Son analyse des pointeurs JSON pour les séquences d'échappement est robuste, et la fonction `_deep_equal` pour l'opération `test` implémente correctement l'égalité sensible aux types spécifiée. Le seul problème de correction notable est que l'opération `move` copie profondément la valeur au lieu de déplacer la référence, ce qui est une déviation sémantique par rapport à JSON Patch standard.
Completude
Poids 20%La réponse A implémente entièrement les six opérations JSON Patch requises (add, remove, replace, move, copy, test), l'exception personnalisée `JsonPatchError` et toutes les règles d'analyse des pointeurs JSON spécifiées. Toutes les fonctionnalités demandées dans l'invite sont présentes.
Qualite du code
Poids 20%Le code de la réponse A est bien structuré avec des fonctions auxiliaires claires et ciblées. Les noms de variables sont descriptifs et la logique est généralement explicite plutôt que de s'appuyer sur une gestion large des exceptions. La fonction `_deep_equal` est un bon exemple de logique claire et robuste. Cela contribue à une lisibilité et une maintenabilité élevées.
Valeur pratique
Poids 15%La réponse A fournit une implémentation JSON Patch très fonctionnelle et fiable. Son analyse robuste et son opération `test` correcte la rendent adaptée à une utilisation pratique, malgré la déviation sémantique mineure dans l'opération `move`. La garantie d'atomicité est également bien implémentée.
Respect des consignes
Poids 10%La réponse A suit presque toutes les instructions à la lettre, y compris l'exception personnalisée, la non-mutation, toutes les opérations, les règles des pointeurs JSON (déséchappement, zéros non significatifs, règles d'index de tableau, '-' pour add), l'égalité profonde pour `test`, et la validation exacte des champs pour les opérations. La seule déviation mineure est le comportement de copie profonde dans l'opération `move`, qui n'est pas la sémantique standard de JSON Patch.
Score total
Commentaire global
La réponse A est une implémentation solide et exécutable avec des fonctions d'aide claires, une copie profonde correcte du document original, une validation exacte des champs d'opération, une bonne validation de l'échappement JSON Pointer, une gestion robuste des index de tableau et la prise en charge de toutes les opérations requises. Ses principales faiblesses résident dans les cas limites : elle rejette l'ajout d'un membre d'objet nommé '-' bien que '-' ne doive être spécial que pour les tableaux, elle rejette les cas de suppression et de déplacement de la racine, et une valeur d'opération non hachable et non string pourrait s'échapper sous forme de TypeError au lieu de JsonPatchError. Dans l'ensemble, elle est substantiellement correcte et maintenable.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%Globalement correcte pour toutes les opérations, avec une analyse solide des pointeurs, une validation des index de tableau, une atomicité par rapport au document d'origine et un comportement de copie profonde. Elle perd des points pour avoir rejeté l'ajout d'objet à la clé '-', rejeté les cas de déplacement/suppression de la racine et potentiellement levé une TypeError au lieu d'une JsonPatchError pour une valeur d'opération non hachable.
Completude
Poids 20%Implémente les six opérations requises, les ensembles de champs exacts, le décodage JSON Pointer, les règles de limites de tableau et de zéros non significatifs, l'ajout/remplacement de la racine, le rejet des descendants déplacés et la copie profonde. La complétude est réduite par quelques cas limites autour de '-' comme clé d'objet et le comportement de déplacement/suppression de la racine.
Qualite du code
Poids 20%Code bien structuré avec des assistants ciblés pour l'analyse, la navigation, la mutation, l'égalité et la validation. L'implémentation est facile à suivre et évite de s'appuyer sur des exceptions Python accidentelles dans la plupart des cas, bien qu'il y ait un petit paramètre inutilisé et que quelques validations de cas limites soient imparfaites.
Valeur pratique
Poids 15%Serait fiable dans la plupart des cas d'utilisation pratiques de JSON Patch, protège bien l'entrée d'origine et fournit des échecs déterministes JsonPatchError pour presque tous les patchs malformés. Les problèmes restants sont des cas limites relativement peu courants.
Respect des consignes
Poids 10%Suit de près l'invite en fournissant du code compatible avec Python 3.11, la fonction et l'exception demandées, une implémentation utilisant uniquement la bibliothèque standard, toutes les opérations prises en charge, pas de CLI, et une forte non-mutation du document d'origine. Des écarts mineurs subsistent sur la sémantique des chemins des cas limites et certains enrobages d'erreurs.