Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 90 additions & 15 deletions packages/quicktype-core/src/language/Python/JSONPythonRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,42 @@ export type ConverterFunction =
| "from-stringified-bool"
| "is-type";

// The name of a converter function in the generated code.
function converterName(cf: ConverterFunction): string {
const name = cf.replace(/-/g, "_");
if (cf.startsWith("from-") || cf.startsWith("to-") || cf.startsWith("is-"))
return name;
return `from_${name}`;
}

// What the constraint checks of `from_dict` call: the built-in `len`, the
// module `re` and `typing.cast`.
const lenFunction = "len";
const reModule = "re";
const castFunction = "cast";

// Whether `from_dict` may call a converter function: the deserializers
// and the decoding transformers' converters. The others only `to_dict`
// calls.
const calledByFromDict: Record<ConverterFunction, boolean> = {
none: true,
bool: true,
int: true,
"from-float": true,
"to-float": false,
str: true,
"to-enum": false,
list: true,
"to-class": false,
dict: true,
union: true,
"from-date": true,
"from-time": true,
"from-datetime": true,
"from-stringified-bool": true,
"is-type": false,
};

interface TopLevelConverterNames {
fromDict: Name;
toDict: Name;
Expand Down Expand Up @@ -197,7 +233,27 @@ export class JSONPythonRenderer extends PythonRenderer {

protected forbiddenForObjectProperties(c: ClassType, className: Name) {
const result = super.forbiddenForObjectProperties(c, className);
result.names = result.names.concat(["from_dict", "to_dict"]);
// `from_dict` assigns each property to a local variable of the
// property's name, so no property may be named like something
// `from_dict` uses: its parameter, `isinstance`, the converter
// functions it calls (`from_dict`, the "dict" converter, is
// already the method's name) and what its constraint checks call.
const fromDictConverters = (
Object.keys(calledByFromDict) as ConverterFunction[]
)
.filter((cf) => calledByFromDict[cf])
.map(converterName)
.filter((name) => name !== "from_dict");
result.names = result.names.concat([
"from_dict",
"to_dict",
"obj",
"isinstance",
...fromDictConverters,
lenFunction,
reModule,
castFunction,
]);
return result;
}

Expand Down Expand Up @@ -242,7 +298,7 @@ export class JSONPythonRenderer extends PythonRenderer {
return v;
}

return [this.withTyping("cast"), "(", type, ", ", v, ")"];
return [this.withTyping(castFunction), "(", type, ", ", v, ")"];
}

protected emitNoneConverter(): void {
Expand Down Expand Up @@ -677,14 +733,7 @@ export class JSONPythonRenderer extends PythonRenderer {
// Return the name of the Python converter function `cf`.
protected conv(cf: ConverterFunction): Sourcelike {
this._deserializerFunctions.add(cf);
const name = cf.replace(/-/g, "_");
if (
cf.startsWith("from-") ||
cf.startsWith("to-") ||
cf.startsWith("is-")
)
return name;
return ["from_", name];
return converterName(cf);
}

// Applies the converter function to `arg`
Expand Down Expand Up @@ -1123,9 +1172,22 @@ export class JSONPythonRenderer extends PythonRenderer {
condition,
);
if (min !== undefined)
check([min.toString(), " <= len(", name, ")"]);
check([
min.toString(),
" <= ",
lenFunction,
"(",
name,
")",
]);
if (max !== undefined)
check(["len(", name, ") <= ", max.toString()]);
check([
lenFunction,
"(",
name,
") <= ",
max.toString(),
]);
const [minValue, maxValue] =
minMaxValueForType(cp.type) ?? [];
if (minValue !== undefined)
Expand All @@ -1136,14 +1198,27 @@ export class JSONPythonRenderer extends PythonRenderer {
if (lo !== undefined || hi !== undefined) {
const raw = this.cast("str", property.value);
if (lo !== undefined)
check([lo.toString(), " <= len(", raw, ")"]);
check([
lo.toString(),
" <= ",
lenFunction,
"(",
raw,
")",
]);
if (hi !== undefined)
check(["len(", raw, ") <= ", hi.toString()]);
check([
lenFunction,
"(",
raw,
") <= ",
hi.toString(),
]);
}
const pattern = patternForType(cp.type);
if (pattern !== undefined)
check([
this.withModuleImport("re"),
this.withModuleImport(reModule),
".search(",
this.string(pattern),
", ",
Expand Down
21 changes: 12 additions & 9 deletions test/inputs/json/priority/keywords.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@
"from": { "from": 123 },
"from_dict": { "from_dict": 123 },
"from_json": { "from_json": 123 },
"func": { "func": 123 },
"from_str": { "from_str": "converted" },
"dummy": 123
},
"obj3": {
"func": { "func": 123 },
"function": { "function": 123 },
"get": { "get": 123 },
"global": { "global": 123 },
Expand Down Expand Up @@ -161,6 +162,7 @@
"IntNode": { "IntNode": 123 },
"interface": { "interface": 123 },
"internal": { "internal": 123 },
"isinstance": { "isinstance": 123 },
"IsoDateTimeOffsetConverter": { "IsoDateTimeOffsetConverter": 123 },
"iterable": { "iterable": 123 },
"is": { "is": 123 },
Expand Down Expand Up @@ -196,11 +198,11 @@
"map": { "map": 123 },
"MarshalJSON": { "MarshalJSON": 123 },
"MapEntry": { "MapEntry": 123 },
"metadata_property_handling": { "metadata_property_handling": 123 },
"module": { "module": 123 },
"dummy": 123
},
"obj4": {
"metadata_property_handling": { "metadata_property_handling": 123 },
"module": { "module": 123 },
"mutable": { "mutable": 123 },
"mutating": { "mutating": 123 },
"namespace": { "namespace": 123 },
Expand All @@ -226,6 +228,7 @@
"null": { "null": 123 },
"nullptr": { "nullptr": 123 },
"number": { "number": 123 },
"obj": { "obj": 123 },
"object": { "object": 123 },
"OffsetDateTime": { "OffsetDateTime": 123 },
"OffsetTime": { "OffsetTime": 123 },
Expand Down Expand Up @@ -262,12 +265,12 @@
"range": { "range": 123 },
"readonly": { "readonly": 123 },
"ref": { "ref": 123 },
"RegExp": { "RegExp": "550e8400-e29b-41d4-a716-446655440000" },
"register": { "register": 123 },
"reinterpret_cast": { "reinterpret_cast": 123 },
"dummy": 123
},
"obj5": {
"RegExp": { "RegExp": "550e8400-e29b-41d4-a716-446655440000" },
"register": { "register": 123 },
"reinterpret_cast": { "reinterpret_cast": 123 },
"repeat": { "repeat": 123 },
"require": { "require": 123 },
"required": { "required": 123 },
Expand Down Expand Up @@ -329,12 +332,12 @@
"top_level": { "top_level": 123 },
"toString": { "toString": 123 },
"to_dict": { "to_dict": 123 },
"transient": { "transient": 123 },
"True": { "True": 123 },
"true": { "true": 123 },
"dummy": 123
},
"obj6": {
"transient": { "transient": 123 },
"True": { "True": 123 },
"true": { "true": 123 },
"try": { "try": 123 },
"Type": { "Type": 123 },
"type": { "type": 123 },
Expand Down
3 changes: 3 additions & 0 deletions test/inputs/schema/keyword-enum.schema
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
"from",
"from_dict",
"from_json",
"from_str",
"func",
"function",
"get",
Expand Down Expand Up @@ -159,6 +160,7 @@
"IntNode",
"interface",
"internal",
"isinstance",
"IsoDateTimeOffsetConverter",
"iterable",
"is",
Expand Down Expand Up @@ -221,6 +223,7 @@
"null",
"nullptr",
"number",
"obj",
"object",
"OffsetDateTime",
"OffsetTime",
Expand Down
21 changes: 21 additions & 0 deletions test/inputs/schema/keyword-unions.schema
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,13 @@
],
"title": "union_from_json"
},
"from_str": {
"oneOf": [
{ "type": "number" },
{ "type": "object", "additionalProperties": false, "title": "from_str" }
],
"title": "union_from_str"
},
"func": {
"oneOf": [
{ "type": "number" },
Expand Down Expand Up @@ -1086,6 +1093,13 @@
],
"title": "union_internal"
},
"isinstance": {
"oneOf": [
{ "type": "number" },
{ "type": "object", "additionalProperties": false, "title": "isinstance" }
],
"title": "union_isinstance"
},
"IsoDateTimeOffsetConverter": {
"oneOf": [
{ "type": "number" },
Expand Down Expand Up @@ -1520,6 +1534,13 @@
],
"title": "union_number"
},
"obj": {
"oneOf": [
{ "type": "number" },
{ "type": "object", "additionalProperties": false, "title": "obj" }
],
"title": "union_obj"
},
"object": {
"oneOf": [
{ "type": "number" },
Expand Down
1 change: 1 addition & 0 deletions test/inputs/schema/min-max-items.1.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"len": "x",
"minOnly": ["one", "two"],
"maxOnly": [1, 2, 3],
"minAndMax": [1.25, 2.5],
Expand Down
3 changes: 3 additions & 0 deletions test/inputs/schema/min-max-items.schema
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"type": "object",
"properties": {
"len": {
"type": "string"
},
"minOnly": {
"type": "array",
"items": {
Expand Down
1 change: 1 addition & 0 deletions test/inputs/schema/minmaxlength.1.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"cast": "x",
"minlength": "aaaa",
"maxlength": "bbb",
"minmaxlength": "cccc",
Expand Down
3 changes: 3 additions & 0 deletions test/inputs/schema/minmaxlength.schema
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"type": "object",
"properties": {
"cast": {
"type": "string"
},
"minlength": {
"type": "string",
"minLength": 3
Expand Down
1 change: 1 addition & 0 deletions test/inputs/schema/pattern.1.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"re": "x",
"pattern1": "aa",
"pattern2": "ba",
"union": "ccc"
Expand Down
3 changes: 3 additions & 0 deletions test/inputs/schema/pattern.schema
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"type": "object",
"properties": {
"re": {
"type": "string"
},
"pattern1": {
"type": "string",
"pattern": "a[.]*"
Expand Down
3 changes: 3 additions & 0 deletions test/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ friend
from
from_dict
from_json
from_str "converted"
func
function
get
Expand Down Expand Up @@ -153,6 +154,7 @@ int
IntNode
interface
internal
isinstance
IsoDateTimeOffsetConverter
iterable
is
Expand Down Expand Up @@ -215,6 +217,7 @@ NULL
null
nullptr
number
obj
object
OffsetDateTime
OffsetTime
Expand Down