Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

v3 adds mypy #187

Merged
merged 110 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
66d005e
Adds lib for types
spacether Jul 10, 2023
2ecde39
Fixes type errors in format.py
spacether Jul 10, 2023
2ea988c
Adds query_params_suffix to fix type error
spacether Jul 10, 2023
4e4d556
Fixes unset type
spacether Jul 10, 2023
c4ffdf3
Fixes type errors in schema_configuration
spacether Jul 10, 2023
a0a86cd
Adds needed type info in validation.py
spacether Jul 10, 2023
2258931
Fixes more type errors in validation.py
spacether Jul 10, 2023
95c6833
Adds U+T binding to fix type errors
spacether Jul 10, 2023
3343f83
Improves return type of _get_new_instance_without_conversion
spacether Jul 11, 2023
256f843
Changes some typing.Type to type
spacether Jul 11, 2023
59b272c
Adds validate_base_ in Schema
spacether Jul 11, 2023
a9cb541
Uses Schema.validate_base in api_client
spacether Jul 11, 2023
57fe1a9
Removes clashing validate overloads from schemas classes
spacether Jul 11, 2023
961c187
Adds more missing types in schema.py
spacether Jul 11, 2023
221a779
Adds needed type hint
spacether Jul 11, 2023
c4b9632
Fixes some api_client types
spacether Jul 11, 2023
bcb207c
Fixes type error in update_params_for_auth
spacether Jul 11, 2023
64cc7bc
Fixes type errors with vars write_file and new_file
spacether Jul 11, 2023
64eb667
FIxes type error with serialize methods for header classes
spacether Jul 11, 2023
99b1e87
Fixes type error with None detection
spacether Jul 11, 2023
4cbdd88
Type error another none fix
spacether Jul 11, 2023
d5b62dd
Fixes type error by using literal for key_prefix in get_security_requ…
spacether Jul 11, 2023
225438e
Fixes 2 type errors by using literals in get_server_url
spacether Jul 11, 2023
f670918
Adds another validate_base signature to fix type errors in dictschema…
spacether Jul 11, 2023
b2bf1c5
Improves type hints for DictSchema
spacether Jul 11, 2023
2816de8
Fixes type error with tuple detection
spacether Jul 11, 2023
832fb43
Fixes broken python tests by having composed dict schemas use the Sch…
spacether Jul 11, 2023
9685e6d
Eliminates incompatible sequence overloads
spacether Jul 11, 2023
f21c753
Fixes 1 type error
spacether Jul 11, 2023
8d8a7f1
Fixes another type error
spacether Jul 11, 2023
ab1133b
Fixes tuple type error
spacether Jul 11, 2023
c541bd2
Fixes type error for path_to_schemas
spacether Jul 11, 2023
7c69815
Updates path_to_schemas to store one schema
spacether Jul 11, 2023
c4c429f
Adds _instances type
spacether Jul 11, 2023
caa240f
Fixes more type errors
spacether Jul 11, 2023
d12050d
Fixes another type error
spacether Jul 11, 2023
600e68e
Removes unneeded overload
spacether Jul 11, 2023
8208588
Detects and handles addProps=False and only one property
spacether Jul 11, 2023
2f93054
Removes one sequence usage in template
spacether Jul 12, 2023
7b4842a
Removes another sequence usage
spacether Jul 12, 2023
831c03e
Relaces last sequence in templates
spacether Jul 12, 2023
401486c
Fixes type errors by fixing get_security_requirement_object literal i…
spacether Jul 12, 2023
7b43f3e
Adds exception raising for statuses in operation handling code, reduc…
spacether Jul 12, 2023
0751646
Fixes assertion error, adds storage of error status codes for operation
spacether Jul 12, 2023
95b086f
Adds error status codes to python
spacether Jul 12, 2023
ef66ed6
Fixes operation return type
spacether Jul 12, 2023
a537906
Fixes get_server_url invocation, adds literals
spacether Jul 12, 2023
b854a71
Renames all ApiResponse classes to ApiResponse, uses them in operatio…
spacether Jul 12, 2023
e848878
Fixes type of headers_schema
spacether Jul 12, 2023
7d17fa6
Removes dataclass from OpenApiResponse
spacether Jul 12, 2023
dfafed5
Removes unneeded dataclasses from OpenApiResponse and classes it uses
spacether Jul 12, 2023
23fe5c1
Fixes skip_deserialization overload
spacether Jul 12, 2023
552bbd8
Prevents parameter collisions
spacether Jul 12, 2023
9da1fa0
Adds array output class tuple generic type
spacether Jul 12, 2023
62e0f8e
Removes typeddict from responses
spacether Jul 12, 2023
56480ab
Adds missing elipses for tuple output type
spacether Jul 12, 2023
8b93b88
Adds validate_base sequence back in
spacether Jul 13, 2023
24f2815
Removes getitem from list schemas
spacether Jul 13, 2023
d5370c0
Removes typeddict from header classes
spacether Jul 13, 2023
fa79679
Changes order to int float
spacether Jul 13, 2023
463459e
Do not generate validate with number schema + enum
spacether Jul 13, 2023
18a876c
Controls when composed schema validate methods are written, fixes typ…
spacether Jul 13, 2023
5b36372
Adds type(None) to types definition, fixes type errors
spacether Jul 13, 2023
2809c22
Turns off getitem, adds optional props
spacether Jul 13, 2023
88dc19d
Writes property methods
spacether Jul 13, 2023
496d065
Changes additional_properties to get_additional_property
spacether Jul 13, 2023
d32a154
Adds get_property method implementation
spacether Jul 14, 2023
83bec28
Changes template mode to get_property
spacether Jul 14, 2023
68088b3
Changes property to overloads
spacether Jul 14, 2023
d65b356
Has overloads use elipses
spacether Jul 14, 2023
1e1fdb7
Handles the case where there is one required or optional property
spacether Jul 14, 2023
75ae614
Adds get_additional_property casting
spacether Jul 14, 2023
c14628f
Only cast individual property outputs
spacether Jul 14, 2023
609be33
Ignores pyright reportWildcardImportFromLibrary
spacether Jul 14, 2023
4137bf0
Fixes array self ref type hint
spacether Jul 14, 2023
a9e6a30
Makes T param bound to sequences, fixes type errors
spacether Jul 14, 2023
b961d0a
Adds required and optional keys
spacether Jul 14, 2023
c68e33a
Adds unset return for optional property values when there is only one…
spacether Jul 14, 2023
2f75c45
Changes name variable to key
spacether Jul 14, 2023
355468e
Removes unused templates
spacether Jul 15, 2023
18415e3
Adds required property returns in get_property
spacether Jul 15, 2023
18b88f9
Fixes get_property implementation
spacether Jul 15, 2023
f867b9d
Improves get_property implementation
spacether Jul 15, 2023
0fd5d59
Changes method name to get_additional_property_
spacether Jul 17, 2023
2555a9e
Adds @property back
spacether Jul 17, 2023
7d449c8
Convers ref properties back into getters
spacether Jul 18, 2023
74b9236
Converts get_property back into get_x methods
spacether Jul 18, 2023
7936b82
Fixes python tests
spacether Jul 18, 2023
75fae57
Adds unset returning in get_additional_property_
spacether Jul 18, 2023
489f4fb
Adds unset output for many cases
spacether Jul 18, 2023
e4e067b
Adds unset types on all except for array and object models
spacether Jul 18, 2023
bedc6e5
Adds array unset return value
spacether Jul 18, 2023
cc81d59
Adds unset types for optional map
spacether Jul 18, 2023
4554ac2
Writes properties with unmodifed names, only writes them if they are …
spacether Jul 18, 2023
765ffc2
Fixes python tests
spacether Jul 18, 2023
34bb0b0
Adds mypy to petstore test
spacether Jul 18, 2023
ddd2a59
Fixes type error
spacether Jul 18, 2023
6cc333c
FIxes last mypy errors
spacether Jul 18, 2023
35c4679
Adds py.typed, improves server imports
spacether Jul 18, 2023
d868897
Adds __all__ definitions
spacether Jul 18, 2023
f6d57d6
Fixes unset type setting on optional returns
spacether Jul 18, 2023
153c7e3
Fixes additiona prperties return type
spacether Jul 18, 2023
0ea20e9
Adds unset case for binary output
spacether Jul 18, 2023
4c79a3f
Fixes validate return types
spacether Jul 18, 2023
60fe6fc
Adds mapValueSchema
spacether Jul 18, 2023
3c36f91
Refines types of SchemaDict values, supresses mypy response body type…
spacether Jul 18, 2023
c8990a3
Adds get_response method in OpenApiResponse classes
spacether Jul 18, 2023
eed05f7
Fixes java test
spacether Jul 19, 2023
3b8150a
Samples regenerated
spacether Jul 19, 2023
adcdce2
Fixes unit test sample, samples regenerated
spacether Jul 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 6 additions & 6 deletions docs/generators/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|tuple|
|boolean|schemas.BoolClass|
|integer|decimal.Decimal|
|null|schemas.NoneClass|
|number|decimal.Decimal|
|object|frozendict.frozendict|
|boolean|bool|
|integer|int|
|null|None|
|number|typing.Union[float, int]|
|object|immutabledict.immutabledict|
|string|str|


Expand Down Expand Up @@ -85,9 +85,9 @@ These options may be applied as additional-properties (cli) or configOptions (pl
<li>float</li>
<li>for</li>
<li>from</li>
<li>frozendict</li>
<li>global</li>
<li>if</li>
<li>immutabledict</li>
<li>import</li>
<li>in</li>
<li>int</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2338,6 +2338,20 @@ public CodegenSchema fromSchema(Schema p, String sourceJsonPath, String currentJ
}
}
// end of properties that need to be ordered to set correct camelCase jsonPathPieces
CodegenSchema additionalProperties = property.additionalProperties;
LinkedHashMapWithContext<CodegenKey, CodegenSchema> properties = property.properties;
if (additionalProperties != null || properties != null) {
CodegenSchema mapValueSchema = new CodegenSchema();
if (additionalProperties != null) {
mapValueSchema = mapValueSchema.add(additionalProperties);
}
if (properties != null) {
for (CodegenSchema prop: properties.values()) {
mapValueSchema = prop.add(mapValueSchema);
}
}
property.mapValueSchema = mapValueSchema;
}

if (currentJsonPath != null) {
String[] pathPieces = currentJsonPath.split("/");
Expand Down Expand Up @@ -2514,7 +2528,10 @@ public CodegenOperation fromOperation(Operation operation, String jsonPath) {
TreeMap<String, CodegenResponse> nonDefaultResponses = null;
TreeMap<Integer, CodegenResponse> wildcardCodeResponses = null;
TreeMap<Integer, CodegenResponse> statusCodeResponses = null;
boolean hasErrorResponseObject = false;
LinkedHashSet<String> errorStatusCodes = null;
LinkedHashSet<Integer> errorWildcardStatusCodes = null;
LinkedHashSet<String> nonErrorStatusCodes = null;
LinkedHashSet<Integer> nonErrorWildcardStatusCodes = null;
if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
responses = new TreeMap<>();
for (Map.Entry<String, ApiResponse> operationGetResponsesEntry : operation.getResponses().entrySet()) {
Expand Down Expand Up @@ -2545,13 +2562,17 @@ public CodegenOperation fromOperation(Operation operation, String jsonPath) {
}
int firstNumber = Integer.parseInt(key.substring(0, 1));
wildcardCodeResponses.put(firstNumber, r);
if (firstNumber > 3 && r.content != null) {
for (CodegenMediaType cm: r.content.values()) {
if (cm.schema != null) {
hasErrorResponseObject = true;
break;
}
if (firstNumber > 3) {
// store error response code whether on not it has a body
if (errorWildcardStatusCodes == null) {
errorWildcardStatusCodes = new LinkedHashSet<>();
}
errorWildcardStatusCodes.add(firstNumber);
} else {
if (nonErrorWildcardStatusCodes == null) {
nonErrorWildcardStatusCodes = new LinkedHashSet<>();
}
nonErrorWildcardStatusCodes.add(firstNumber);
}
continue;
}
Expand All @@ -2560,13 +2581,17 @@ public CodegenOperation fromOperation(Operation operation, String jsonPath) {
}
int statusCode = Integer.parseInt(key);
statusCodeResponses.put(statusCode, r);
if (statusCode > 299 && r.content != null) {
for (CodegenMediaType cm: r.content.values()) {
if (cm.schema != null) {
hasErrorResponseObject = true;
break;
}
if (statusCode > 399) {
// store error response code whether on not it has a body
if (errorStatusCodes == null) {
errorStatusCodes = new LinkedHashSet<>();
}
errorStatusCodes.add(key);
} else {
if (nonErrorStatusCodes == null) {
nonErrorStatusCodes = new LinkedHashSet<>();
}
nonErrorStatusCodes.add(key);
}
}

Expand Down Expand Up @@ -2729,7 +2754,10 @@ else if (one.required)

return new CodegenOperation(
deprecated,
hasErrorResponseObject,
nonErrorStatusCodes,
nonErrorWildcardStatusCodes,
errorStatusCodes,
errorWildcardStatusCodes,
summary,
unescapedDescription,
description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ private void generatePathItem(List<File> files, CodegenKey pathKey, CodegenPathI
endpointMap.put("pathItem", pathItem);
endpointMap.put("httpMethod", httpMethod);
endpointMap.put("security", security);
endpointMap.put("path", pathKey);
generateXs(files, operationJsonPath, CodegenConstants.JSON_PATH_LOCATION_TYPE.OPERATION, CodegenConstants.APIS, endpointMap, true);

// operation docs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("migration_2_0_0.hbs", "", "migration_2_0_0.md"));
supportingFiles.add(new SupportingFile("migration_other_python_generators.hbs", "", "migration_other_python_generators.md"));
supportingFiles.add(new SupportingFile("__init__package.hbs", packagePath(), "__init__.py"));
supportingFiles.add(new SupportingFile("__init__.hbs", packagePath(), "py.typed"));

if (!generateSourceCodeOnly) {
supportingFiles.add(new SupportingFile("tox.hbs", "", "tox.ini"));
Expand Down Expand Up @@ -727,6 +728,7 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("schemas/schema.hbs", packagePath() + File.separator + "schemas", "schema.py"));
supportingFiles.add(new SupportingFile("schemas/schemas.hbs", packagePath() + File.separator + "schemas", "schemas.py"));
supportingFiles.add(new SupportingFile("schemas/format.hbs", packagePath() + File.separator + "schemas", "format.py"));
supportingFiles.add(new SupportingFile("schemas/original_immutabledict.hbs", packagePath() + File.separator + "schemas", "original_immutabledict.py"));
supportingFiles.add(new SupportingFile("security_schemes.hbs", packagePath(), "security_schemes.py"));
supportingFiles.add(new SupportingFile("server.hbs", packagePath(), "server.py"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@

public class CodegenOperation {
public final Boolean deprecated;
public final boolean hasErrorResponseObject; // if 4xx, 5xx responses have at least one error object defined
public final LinkedHashSet<String> nonErrorStatusCodes; // values like 201
public final LinkedHashSet<Integer> nonErrorWildcardStatusCodes; // values like 2 for @2XX
public final LinkedHashSet<String> errorStatusCodes; // values like 401
public final LinkedHashSet<Integer> errorWildcardStatusCodes; // values like 4 for 4XX
public final String summary, unescapedDescription, description;
public final LinkedHashSet<String> produces;
public final List<CodegenServer> servers;
Expand Down Expand Up @@ -55,9 +58,12 @@ public class CodegenOperation {
public final CodegenKey operationId;
public final CodegenKey jsonPathPiece;

public CodegenOperation(Boolean deprecated, boolean hasErrorResponseObject, String summary, String unescapedDescription, String description, LinkedHashSet<String> produces, List<CodegenServer> servers, CodegenRequestBody requestBody, List<CodegenSchema> requestBodySchemas, List<CodegenParameter> allParams, List<CodegenParameter> pathParams, CodegenSchema pathParameters, List<CodegenParameter> queryParams, CodegenSchema queryParameters, List<CodegenParameter> headerParams, CodegenSchema headerParameters, List<CodegenParameter> cookieParams, CodegenSchema cookieParameters, boolean hasRequiredParamOrBody, boolean hasOptionalParamOrBody, List<HashMap<String, CodegenSecurityRequirementValue>> security, Map<String, CodegenTag> tags, TreeMap<String, CodegenResponse> responses, TreeMap<Integer, CodegenResponse> statusCodeResponses, TreeMap<Integer, CodegenResponse> wildcardCodeResponses, TreeMap<String, CodegenResponse> nonDefaultResponses, CodegenResponse defaultResponse, List<CodegenCallback> callbacks, ExternalDocumentation externalDocs, Map<String, Object> vendorExtensions, CodegenKey operationId, CodegenKey jsonPathPiece) {
public CodegenOperation(Boolean deprecated, LinkedHashSet<String> nonErrorStatusCodes, LinkedHashSet<Integer> nonErrorWildcardStatusCodes, LinkedHashSet<String> errorStatusCodes, LinkedHashSet<Integer> errorWildcardStatusCodes, String summary, String unescapedDescription, String description, LinkedHashSet<String> produces, List<CodegenServer> servers, CodegenRequestBody requestBody, List<CodegenSchema> requestBodySchemas, List<CodegenParameter> allParams, List<CodegenParameter> pathParams, CodegenSchema pathParameters, List<CodegenParameter> queryParams, CodegenSchema queryParameters, List<CodegenParameter> headerParams, CodegenSchema headerParameters, List<CodegenParameter> cookieParams, CodegenSchema cookieParameters, boolean hasRequiredParamOrBody, boolean hasOptionalParamOrBody, List<HashMap<String, CodegenSecurityRequirementValue>> security, Map<String, CodegenTag> tags, TreeMap<String, CodegenResponse> responses, TreeMap<Integer, CodegenResponse> statusCodeResponses, TreeMap<Integer, CodegenResponse> wildcardCodeResponses, TreeMap<String, CodegenResponse> nonDefaultResponses, CodegenResponse defaultResponse, List<CodegenCallback> callbacks, ExternalDocumentation externalDocs, Map<String, Object> vendorExtensions, CodegenKey operationId, CodegenKey jsonPathPiece) {
this.deprecated = deprecated;
this.hasErrorResponseObject = hasErrorResponseObject;
this.nonErrorStatusCodes = nonErrorStatusCodes;
this.nonErrorWildcardStatusCodes = nonErrorWildcardStatusCodes;
this.errorStatusCodes = errorStatusCodes;
this.errorWildcardStatusCodes = errorWildcardStatusCodes;
this.summary = summary;
this.unescapedDescription = unescapedDescription;
this.description = description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ public class CodegenResponse {
public final TreeSet<String> imports;
public final boolean componentModule;

public CodegenResponse getSelfOrDeepestRef() {
if (refInfo == null) {
return this;
}
CodegenResponse refObject = refInfo.ref;
while (refObject.refInfo != null) {
refObject = refObject.refInfo.ref;
}
return refObject;
}

public CodegenResponse(CodegenKey jsonPathPiece, Map<String, CodegenHeader> headers, CodegenSchema headersObjectSchema, String description, Map<String, Object> vendorExtensions, LinkedHashMap<CodegenKey, CodegenMediaType> content, CodegenRefInfo<CodegenResponse> refInfo, TreeSet<String> imports, boolean componentModule) {
this.jsonPathPiece = jsonPathPiece;
this.headers = headers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public class CodegenSchema {
public boolean isBooleanSchemaFalse; // supports boolean schemas

// Extra needed fields
public CodegenSchema mapValueSchema;
public boolean componentModule;
public TreeSet<String> imports;
public CodegenKey jsonPathPiece;
Expand All @@ -97,6 +98,7 @@ public class CodegenSchema {
* used in getAllSchemas to write type definitions for allOfType/anyOfType/oneOfType/propertiesType
*/
public String instanceType;
// used to store the expanded schemas that define a codegenschema in code file
private ArrayList<CodegenSchema> allSchemas = null;

public boolean hasValidation() {
Expand Down Expand Up @@ -134,6 +136,32 @@ public CodegenSchema getSelfOrDeepestRef() {
return refObject;
}

public CodegenSchema add(CodegenSchema other) {
CodegenSchema newSchema = new CodegenSchema();
if (other == null) {
newSchema.types = types;
return newSchema;
}
if (refInfo != null || oneOf != null || anyOf != null || allOf != null || not != null) {
return null;
}
if (other.refInfo != null || other.oneOf != null || other.anyOf != null || other.allOf != null || other.not != null) {
return null;
}
if (types == null) {
newSchema.types = other.types;
return newSchema;
}
if (other.types == null) {
newSchema.types = types;
return newSchema;
}
LinkedHashSet<String> interSectionTypes = new LinkedHashSet<>(types);
interSectionTypes.retainAll(other.types);
newSchema.types = interSectionTypes;
return newSchema;
}

public boolean hasAnyRefs() {
// todo cache this, also pass in sourceJsonPath because one is looking for external refs
if (refInfo != null) {
Expand Down Expand Up @@ -329,6 +357,8 @@ private void getAllSchemas(ArrayList<CodegenSchema> schemasBeforeImports, ArrayL
mapOut.requiredProperties = requiredProperties;
mapOut.additionalProperties = additionalProperties;
mapOut.mapOutputJsonPathPiece = mapOutputJsonPathPiece;
mapOut.properties = properties;
mapOut.mapValueSchema = mapValueSchema;
// inputs needed for Schema validate invocation in new method
mapOut.mapInputJsonPathPiece = mapInputJsonPathPiece;
mapOut.jsonPathPiece = jsonPathPiece;
Expand Down Expand Up @@ -358,6 +388,8 @@ private void getAllSchemas(ArrayList<CodegenSchema> schemasBeforeImports, ArrayL
mapOut.optionalProperties = optionalProperties;
mapOut.additionalProperties = additionalProperties;
mapOut.mapOutputJsonPathPiece = mapOutputJsonPathPiece;
mapOut.properties = properties;
mapOut.mapValueSchema = mapValueSchema;
// inputs needed for Schema validate invocation in new method
mapOut.mapInputJsonPathPiece = mapInputJsonPathPiece;
mapOut.jsonPathPiece = jsonPathPiece;
Expand Down Expand Up @@ -405,8 +437,10 @@ private void getAllSchemas(ArrayList<CodegenSchema> schemasBeforeImports, ArrayL
mapOut.instanceType = "propertiesOutputType";
mapOut.optionalProperties = optionalProperties;
mapOut.requiredProperties = requiredProperties;
mapOut.properties = properties;
mapOut.additionalProperties = additionalProperties;
mapOut.mapOutputJsonPathPiece = mapOutputJsonPathPiece;
mapOut.mapValueSchema = mapValueSchema;
// inputs needed for Schema validate invocation in new method
mapOut.mapInputJsonPathPiece = mapInputJsonPathPiece;
mapOut.jsonPathPiece = jsonPathPiece;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
{{#if tornado}}
{{#if quoted}}"{{/if}}tornado >= 4.2{{#if quoted}}",{{/if}}
{{/if}}
{{#if quoted}}"{{/if}}types-python-dateutil{{#if quoted}}",{{/if}}
{{#if quoted}}"{{/if}}types-urllib3{{#if quoted}}",{{/if}}
{{#if quoted}}"{{/if}}typing_extensions ~= 4.5.0{{#if quoted}}",{{/if}}
{{#if quoted}}"{{/if}}urllib3 ~= 2.0.a3{{#if quoted}}",{{/if}}
Loading