-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Pydantic to version 2.4.2 #10
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,21 +2,19 @@ | |
|
||
import logging | ||
from collections import defaultdict | ||
from typing import Any, TypeAlias | ||
from typing import Any | ||
|
||
from django.db import models | ||
from django.db.models import F | ||
from django.db.utils import IntegrityError | ||
from django.utils.timezone import now as tz_now | ||
from pydantic import BaseModel, Field, root_validator, validator | ||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator | ||
|
||
from .settings import PolicyType | ||
from .utils import strip_query | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
PydanticValues: TypeAlias = dict[str, str | None] | ||
|
||
|
||
class ReportData(BaseModel): | ||
# browser support for CSP reports turns out to be patchy at best - | ||
|
@@ -28,17 +26,18 @@ class ReportData(BaseModel): | |
# min_length ensures we don't have an empty string | ||
blocked_uri: str = Field(alias="blocked-uri", min_length=1) | ||
# we must have one of these - validate_directives enforces this | ||
effective_directive: str | None = Field(alias="effective-directive") | ||
violated_directive: str | None = Field(alias="violated-directive") | ||
effective_directive: str | None = Field(None, alias="effective-directive") | ||
violated_directive: str | None = Field(None, alias="violated-directive") | ||
# optional | ||
disposition: str | None = Field("", alias="disposition") | ||
document_uri: str | None = Field("", alias="document-uri") | ||
original_policy: str | None = Field(alias="original-policy") | ||
referrer: str | None = Field(alias="referrer") | ||
script_sample: str | None = Field(alias="script-sample") | ||
original_policy: str | None = Field(None, alias="original-policy") | ||
referrer: str | None = Field(None, alias="referrer") | ||
script_sample: str | None = Field(None, alias="script-sample") | ||
status_code: str | None = Field(0, alias="status-code") | ||
|
||
@validator("document_uri", "blocked_uri") | ||
@field_validator("document_uri", "blocked_uri") | ||
@classmethod | ||
def strip_uri(cls, uri: str) -> str: | ||
""" | ||
Strip querystring and truncate to fit model length. | ||
|
@@ -49,25 +48,24 @@ def strip_uri(cls, uri: str) -> str: | |
""" | ||
return strip_query(uri)[:200] if uri else "" | ||
|
||
@root_validator | ||
def validate_directives(cls, values: PydanticValues) -> PydanticValues: | ||
@model_validator(mode="after") | ||
def validate_directives(self) -> ReportData: | ||
"""Ensure that we have either effective_directive or violated_directive.""" | ||
if values.get("effective_directive", ""): | ||
return values | ||
if self.effective_directive: | ||
return self | ||
# if effective_directive is empty, but violated_directive is not, | ||
# then udpate the former with the latter. | ||
if violated_directive := values.get("violated_directive", ""): | ||
# then update the former with the latter. | ||
if self.violated_directive: | ||
logger.debug( | ||
"'effective_directive' missing - using 'violated_directive' attr." | ||
) | ||
values["effective_directive"] = violated_directive | ||
return values | ||
self.effective_directive = self.violated_directive | ||
return self | ||
raise ValueError( | ||
"Either 'effective_directive' or 'violated_directive' must be present." | ||
) | ||
|
||
class Config: | ||
allow_population_by_field_name = True | ||
model_config = ConfigDict(populate_by_name=True, coerce_numbers_to_str=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we want to keep this behaviour or not though. |
||
|
||
|
||
class DispositionChoices(models.TextChoices): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "django-csp-plus" | ||
version = "0.10.0" | ||
version = "3.0.0" | ||
description = "CSP tracking and violation report endpoint." | ||
license = "MIT" | ||
authors = ["YunoJuno <[email protected]>"] | ||
|
@@ -19,8 +19,6 @@ classifiers = [ | |
"License :: OSI Approved :: MIT License", | ||
"Operating System :: OS Independent", | ||
"Programming Language :: Python :: 3 :: Only", | ||
"Programming Language :: Python :: 3.8", | ||
"Programming Language :: Python :: 3.9", | ||
"Programming Language :: Python :: 3.10", | ||
"Programming Language :: Python :: 3.11", | ||
] | ||
|
@@ -29,7 +27,7 @@ packages = [{ include = "csp" }] | |
[tool.poetry.dependencies] | ||
python = "^3.8" | ||
django = "^3.2 || ^4.0" | ||
pydantic = "^1" | ||
pydantic = "*" | ||
|
||
[tool.poetry.dev-dependencies] | ||
black = "*" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the
model_validator
with modebefore
would allow us to get the values as a dictionary, similar to what was happening with the deprecatedroot_validator
. However, it seems like usingbefore
with themodel_validator
ignores the field'salias
parameter, which means that if users are passingeffective-directive
, that's what we get on the values dictionary.I'm not sure if this Pydantic change was intentional or not, since
before
validators are supposed to validate before any parsing is done on the fields, but it seems to me like we should be able to access the original field by the field's name, not its alias.