Configuring a pydantic model that leaves field values unchanged if it would have caused a ValidationError? - validation

For example...suppose I have the code:
from pydantic import BaseModel
class User(BaseModel):
a: int
b: dict
c: str
User(**{"a": "2", "b": "gibberish", "c": "ok"}).dict() # should give {"a": 2, "b": "gibberish", "c": "ok"}
Is this achievable with Pydantic? I've tried defining custom validators (w/ all sorts of configurations...using pre=True, root validators w/ or w/out pre=True, etc) but nothing seems to work.

Use construct to create models without validation:
from pydantic import BaseModel
class User(BaseModel):
a: int
b: dict
c: str
user_dict = User.construct(**{"a": "2", "b": "gibberish", "c": "ok"}).dict()
print(user_dict) # {'a': '2', 'b': 'gibberish', 'c': 'ok'}

As per https://github.com/samuelcolvin/pydantic/discussions/4284, this is not easily configurable via Pydantic V1. The only option (according to the author themselves) is to define custom data types and call the validators directly and then catch the exception.

Related

XPath: How to sort a map?

I'm using a map for counting the number occurrences of, for example, each possible value of the attr attribute of the elem nodes:
<root>
<elem attr="a"/>
<elem attr="b"/>
<elem attr="b"/>
<elem />
<elem attr="a"/>
<elem attr="c"/>
<elem attr="b"/>
</root>
fold-left(
//elem/#attr,
map{},
function($m,$a) {map:put($m, $a, sum((1, $m($a))))}
)
Resulting map:
{
"a": 2,
"b": 3,
"c": 1
}
Now, using this map, I would like to sort the integer values in descending order and emit their associated key. The expected "output" would be:
b
a
c
How can I do it?
I can't see why you would want a map for this. It's essentially a grouping-and-sorting problem, therefore much easier in XSLT or XQuery, but if it has to be pure XPath, you can use
let $in := .
return (distinct-values(//#attr)
=> sort((), function($a){-count($in//#attr[.=$a])}))
If you store the map in a variable then it's possible to call fn:sort on the keys while using the associated values as "sort keys":
let
$map := map{ "a": 2, "b": 3, "c": 1 }
return
$map => map:keys() => sort((), function($key) { -$map($key) })
b
a
c
ASIDE
You can also define a map:sort function for maps, which would return a sequence of keys:
function(
$map as map(*),
$collation as xs:string?,
$valuate as function(xs:anyAtomicType, item()*) as xs:anyAtomicType*
) as xs:anyAtomicType*
{
sort(
map:keys($map),
$collation,
function($key) { $valuate($key, $map($key)) }
)
}
let $map:sort := function (...) {...}
return
map{ "a": 2, "b": 3, "c": 1 }
=> $map:sort((), function($k,$v) { -$v })

Why doesn't FastAPI handle types derived from int and Enum correctly?

With the following FastAPI backend:
from enum import Enum
from fastapi import FastAPI
class MyNumber(int, Enum):
ONE = 1
TWO = 2
THREE = 3
app = FastAPI()
#app.get("/add/{a}/{b}")
async def get_model(a: MyNumber, b: MyNumber):
return {"sum": a + b}
When a GET operation is done:
curl -X 'GET' \
'http://127.0.0.1:8000/add/2/3' \
-H 'accept: application/json'
Returns the following:
{
"detail": [
{
"loc": [
"path",
"a"
],
"msg": "value is not a valid enumeration member; permitted: 1, 2, 3",
"type": "type_error.enum",
"ctx": {
"enum_values": [
1,
2,
3
]
}
},
{
"loc": [
"path",
"b"
],
"msg": "value is not a valid enumeration member; permitted: 1, 2, 3",
"type": "type_error.enum",
"ctx": {
"enum_values": [
1,
2,
3
]
}
}
]
}
Why is this the case? Even the Swagger UI does recognize the possible values as integers:
I have tried the solution of using IntEnum instead (source), and I can confirm that it works, but still - why does it have to be this way?
The enum.py source code defines IntEnum as:
class IntEnum(int, Enum):
"""Enum where members are also (and must be) ints"""
Did a little digging, based on the comments below OP. To start of with why this is not working as expected: it is how Starlette handles path parameters. When a request is handled by Starlette (or to be precise, when the Router object is called from which the APIRouter inherits), it determines the path_params as a dictionary with keys are parameter names and values as its value. However, when this happens and you haven't specified a type in the path string, it automatically treats the path parameters as string. It then adds this to the Request, basically as a dict {"a":"1", "b":"2"}. Then, further up the call stack, FastAPI is then trying to find the enum value corresponding to "1", which throws throws the validation error because "1" is not 1. Note that your MyNumber class does not play any role here yet.
This behaviour is by design and can be influenced like so:
#app.get("/add/{a:int}/{b:int}")
This will make sure that Starlette will create a path_params dictionary like {"a":1, "b":2}. Note that 1 and 2 are now integers and will be handled as such by FastAPI.
As to why MyNumber(IntEnum) works out of the box while MyNumber(int, Enum) does not, that is a Pydantic implementation. I can't really seem to pinpoint how exactly, but when the ModelField is created for both parameters (which happens in utils.py -> create_response_field() upon application startup), the list of validators is including an int validator when using IntEnum.
But because ModelField is created using functools.partial() I cannot follow the call stack into Pydantic. So, I am unsure why this behaves like this.
So, in short, there are two options to fix this: either force Starlette to parse the path paramaters a and b as int (using {a:int}) or inherit from IntEnum in your own enum class.

How to get emacs indent correctly inside of initialization list in c++?

I want to initialize an array in this way:
int arr
{
3,
4
};
But my emacs misindents it to:
int arr
{
3,
4
};
Please note a missing "=". I am using an initializer list here and this syntax is correct.
Using a more recent version of emacs might help. The indentation of braced initializer list was improved in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24431.
Emacs 26.1 indents the same code as
int arr
{
3,
4
};
which is not all the way there but at least better.
As an alternative I would like to suggest clang-format with Emacs
There are plenty of options you can play with.
These options are to be defined in a .clang-format file (in your home directory or in your C++ project root directories)
And what about nesting initializer lists, with trailing commas, which are now supported (c++11)?
Say I was building JSON. (cpprestsdk)
what I see:
using JsonValue = web::json::value;
auto json = JsonValue::object({
{"a", JsonValue("v")},
{"b", JsonValue::array({
{JsonValue(1)},
{JsonValue::object({
{},
{},
})},
{JsonValue(3)},
})},
{"c", JsonValue::object()},
});
what I want to see
auto json = JsonValue::object({
{"a", JsonValue("v")},
{"b", JsonValue::array({
{JsonValue(1)},
{JsonValue::object({
{},
{},
})},
{JsonValue(3)},
})},
{"c", JsonValue::object()},
});
Assuming I prefer starting with Allman/BSD basic formatting...

RethinkDB: Updating documents

This is a longshot, but I was wondering if in rethinkDB, let's say I update a document. Is there a magic function such that, if it is a field that is a string or int, it just updates it, but if the value of the field is an array, it appends it to the array?
In that case you'd need to use .branch and branch on the type. Something like .update(function(row) { return {field: r.branch(row('field').typeOf().eq('ARRAY'), row('field').add([el]), el)}; })
There is a magic function that does something similar. .forEach has the undocumented behaviour of adding numbers, combining arrays and drops strings:
>>> r.expr([{a:1, b:[2], c:"3"}, {a:2, b:[4], c:"6"}]).forEach(r.row)
{"a": 3, "b": [2,4], "c": "3"}

Zend Validation - Specify multiple ways to validate a request - depending on whats in the request

I am using Zend Standard Validation Classes (http://framework.zend.com/manual/1.12/en/zend.validate.set.html) to validate requests to an api.
You basically specify each request parameter and a set of rules for validating each parameter.
Is it possible to specify that there can be 2 (or more) options when validating a given request, depending on what parameters are in the request?
Like for example if you post:
{
"a": "someVal",
"b": "someVal",
"c": "someVal",
"d": "someVal"
}
then a, b, c, and d are required fields, but if you post like this:
{
"a": "someVal",
"e": "someVal",
"f": "someVal",
"g": "someVal"
}
then a, e, f and g are required fields?
If you examine the data above you will notice that field "b" is a required field in one POST, but it is an optional field in the other POST.
QUESTION:
How do you specify that a request may be validated in 1 of 2 (or more) ways?
How do you specify that a request should be validated based on what parameters are in the request?
Is this possible, and how?
Have been stuck on this one for a while so any assistance would be great..
Zend Framework 2.2
You can create a strategy class that can select input filter that can fit current request based on posted parameters.
In other words your request flow should be something like this:
Request -> Route -> Controller -> Input Filter Strategy -> InputFilter -> Other Stuff

Resources