Invoer die code wordt · oefening
Begrens en normaliseer elk veld
validate_book laat de juiste sleutels door. Over de
{"title": "(200 spaces)", "author": "A"}
{"title": "T", "author": "A", "year": true}
{"title": "T", "author": "A", "tags": ["tag", "tag", (500 of them)]}
Stuur de tweede of de derde naar je server en hij antwoordt 201: een boek met als jaar true, en een boek met 500 tags. Alleen de eerste wordt tegengehouden, door de oude book_error, die van lege titels weet en van weinig anders.
Deze les geeft elk veld een regel, zet alle regels in validate_book, en schaft book_error af.
Normaliseer, controleer, en gebruik wat je controleerde
Begin met de titel. Er komen twee regels op: haal de witruimte eromheen weg met strip(), en sta hoogstens 200 tekens toe. De volgorde van die twee bepaalt wat er wordt opgeslagen.
| De verstuurde titel | Eerst meten, dan strippen | Eerst strippen, dan meten |
|---|---|---|
| 200 spaties | Komt erdoor met 200 tekens, en er wordt een lege titel opgeslagen | Geweigerd: er blijft niets over |
| 200 letters en vijf spaties | Geweigerd om zijn 205 tekens | Geaccepteerd: 200 letters |
De middelste kolom heeft beide gevallen fout, omdat hij de ene tekst mat en een andere opsloeg. De regel heeft dus drie stappen, in deze volgorde:
Normaliseer. Breng de waarde in de ene vorm die je wilt opslaan.
Controleer de genormaliseerde waarde.
Gebruik wat je controleerde. De genormaliseerde waarde gaat het boek in. Naar de waarde die de client stuurde, wordt niet meer gekeken.
Een validator controleert len(title) <= 200 en slaat daarna title.strip() op. Een client stuurt 200 spaties. Wat wordt er opgeslagen?
Twee strings die er hetzelfde uitzien
Strippen is één soort normaliseren. Tekst heeft nog een tweede soort nodig. Het voorbeeldboek Café Society heeft een é in de titel, en Unicode heeft twee manieren om die letter te schrijven: als één teken, of als een gewone e gevolgd door een combinerend accent, een teken dat zich vastmaakt aan de letter ervoor. Op het scherm zien de twee er hetzelfde uit. In dit blok is ́ het combinerende accent:
import unicodedata
typed = "Café Society"
stored = "Café Society"
print(typed == stored, len(typed), len(stored))
print(unicodedata.normalize("NFC", typed) == stored)
Het drukt False 13 12 af en daarna True. De twee titels zijn verschillende ==, in en len houden ze allemaal uit elkaar, en een client mag elk van beide sturen.
unicodedata.normalize("NFC", text) geeft de tekst terug in één afgesproken vorm, NFC genoemd, waarin een letter en zijn accent tot één teken zijn samengevoegd. unicodedata hoort bij de standaardbibliotheek en wordt bovenaan reading_server.py al geïmporteerd. Dit is de enige nieuwe aanroep van de les. Voor elke tekstwaarde wordt de eerste van de drie stappen unicodedata.normalize("NFC", value).strip().
In deze code schrijft a het accent als een apart combinerend teken na de e, en schrijft b de é als één teken. Wat drukt hij af?
import unicodedata
a = "Café"
b = "Café"
print(a == b, len(a), len(b), unicodedata.normalize("NFC", a) == b)
Een limiet voor elk veld
| Veld | Regel |
|---|---|
title, author | Verplicht. Tekst van 1 tot en met 200 tekens, geteld na normaliseren en strippen |
year | Optioneel. Een int van 1 tot en met 2100, en geen bool |
status | Optioneel. Een van to-read, reading, done, precies zoals verstuurd |
tags | Optioneel. Een |
De getallen zijn constanten bovenaan het bestand: TEXT_LIMIT, FIRST_YEAR, LAST_YEAR, MOST_TAGS en TAG_LIMIT, naast de set STATUSES. Twee van de regels hebben in Python een valkuil.
De eerste is het jaar. De true van True, en isinstance(True, int) is True: een bool telt als een int, en True + 1998 is 1999. Een test op alleen int accepteert hem. Vraag dus eerst naar bool: if isinstance(year, bool) or not isinstance(year, int). Dezelfde regel weigert de tekst "1999" en de float 1999.0.
De tweede is de status. status not in STATUSES lijkt compleet. Maar een client kan een lijst sturen, en ["done"] in STATUSES werpt TypeError glipt zo langs je except ValueError, en de fout van de client wordt een 500. Test eerst isinstance(status, str). De status wordt niet gestript: het is een code uit een vaste lijst, geen tekst die iemand vrij intypt.
Waarom weigert de jaarcontrole True voordat hij isinstance(year, int) vraagt?
Elke tag gaat door dezelfde drie stappen als een titel, en de test op herhaling vergelijkt de genormaliseerde tags. Dus "x" en " x " zijn één tag die twee keer voorkomt, en dat geldt ook voor één woord dat in beide Unicode-vormen is verstuurd.
Welke waarde gaat het boek in nadat een tag is genormaliseerd en gecontroleerd?
Je taak
In reading_server.py:
Schrijf eerst een helper,
clean_text(value, name, limit). Die werptValueErrorop alsvaluegeenstris. Hij normaliseert naar NFC en stript. Hij werptValueErrorop als het resultaat korter is dan 1 of langer danlimittekens. Daarna geeft hij het resultaat terug.nameis voor de meldingen, zoals inf"{name} must be text".Laat
validate_bookgroeien. Houd de sleutelcontroles uit les 1 bovenaan. Bouw daarna de nieuweveld voor veld op, volgens de tabel: titleenauthormoeten er zijn en gaan doorclean_text;year,statusentagsworden gecontroleerd als ze er zijn. Elke weigering is eenValueErrorwaarvan de melding het veld noemt.Haal in
book_request_errorde aanroep vanbook_errorweg.validate_bookmeldt nu een ontbrekende titel. Je kunt debook_errorook verwijderen, want niets anders roept hem aan.
Probeer het daarna in de
python -c "import reading_server; print(reading_server.validate_book({'title': ' Dune ', 'author': 'Frank Herbert', 'tags': [' sand ']}))"
Het drukt {'title': 'Dune', 'author': 'Frank Herbert', 'tags': ['sand']} af: het boek dat wordt opgeslagen, is het boek dat werd gecontroleerd.
Dit alles heeft twee grenzen. NFC maakt tekst alleen gelijk waar het verschil een letter was die met zijn accent samengevoegd of los geschreven was. Letters uit verschillende alfabetten die op elkaar lijken, blijven verschillend. Een sterkere vorm, NFKC, vouwt meer samen en verandert ook wat sommige tekst betekent, dus een echte dienst kiest met zorg. En een titel mag nog steeds elk teken bevatten, ook een regeleinde in het midden. Zo’n titel komt door elke regel uit deze les.
Druk daarna op Check my work. De volgende les gaat over waarom een titel die door de controle kwam, toch schade kan doen.
Opdracht
Geef elk veld van een boek een regel, in validate_book in reading_server.py.
Houd wat les 1 schreef: een body die geen owner en id, en elke sleutel buiten ALLOWED_FIELDS werpen
| Veld | Regel | Teruggegeven als |
|---|---|---|
title, author | Verplicht. Een str van 1 tot en met TEXT_LIMIT (200) tekens, geteld na unicodedata.normalize("NFC", value).strip() | De genormaliseerde, gestripte tekst |
year | Optioneel. Een int van FIRST_YEAR (1) tot en met LAST_YEAR (2100). True en False worden geweigerd | Het getal |
status | Optioneel. Een str die in STATUSES staat, vergeleken zoals hij werd verstuurd | De tekst |
tags | Optioneel. Een list van hoogstens MOST_TAGS (10) tags. Elke tag is een str van 1 tot en met TAG_LIMIT (30) tekens, geteld zoals een titel. Dezelfde tag twee keer, vergeleken na normaliseren en strippen, wordt geweigerd | Een nieuwe |
Elke weigering is een ValueError waarvan de melding het veld noemt: title, author, year, status, of het woord tag. De formulering bepaal je zelf. Een helper clean_text(value, name, limit) die de drie stappen voor één
Haal in book_request_error de aanroep van book_error weg. create_book en replace_book blijven lopen over wat validate_book teruggeeft.
Druk daarna op Check my work. Het roept validate_book() aan met bodies die je niet hebt gezien en vergelijkt elke geaccepteerde waarde exact: tekst met witruimte eromheen, 200 letters gevolgd door vijf spaties, tekst getypt met een combinerend accent, de jaren 1 en 2100, elke status, een lege lijst met tags, tien tags, en een tag van 30 tekens. Deze moeten met ValueError worden geweigerd: 200 spaties, 201 tekens, een lege of ontbrekende titel of auteur, een getal als titel; True, False, 0, 2101, -5, "1999" en 1999.0 als jaar; een status in hoofdletters, een lege, een getal en een lijst; tags die tekst of een dict zijn, elf tags, een tag van 31 tekens, een lege of blanco tag, een lijst of een getal als tag, en dezelfde tag twee keer in twee vormen. Daarna roept het handle() aan met eigen gebruikers en tokens: het opgeslagen boek moet de genormaliseerde waarden bevatten, en een waarde van het verkeerde type moet 422 krijgen, nooit 500. Niets uit je