-
Notifications
You must be signed in to change notification settings - Fork 19
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
Fix a memory leak caused by schematics json schemas #50
Changes from all 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 |
---|---|---|
@@ -1,4 +1,5 @@ | ||
# uranium | ||
.virtualenv | ||
.env | ||
bin | ||
build | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
from schematics.types.compound import DictType, ModelType | ||
from schematics.exceptions import ValidationError | ||
from transmute_core.exceptions import SerializationException, NoSerializerFound | ||
from transmute_core.object_serializers.schematics_serializer import SchematicsSerializer | ||
|
||
|
||
class Card(Model): | ||
|
@@ -85,8 +86,7 @@ def test_schematics_to_json_schema(object_serializer_set): | |
(IntType, {"type": "integer"}), | ||
(datetime, {"type": "string", "format": "date-time"}), | ||
(UTCDateTimeType, {"type": "string", "format": "date-time"}), | ||
(Serializable(fget=lambda: None, | ||
type=StringType()), {"type": "string"}), | ||
(Serializable(fget=lambda: None, type=StringType()), {"type": "string"}), | ||
], | ||
) | ||
def test_to_json_schema(object_serializer_set, inp, expected): | ||
|
@@ -165,3 +165,27 @@ def test_can_handle(object_serializer_set, cls, should_handle): | |
the object serializer set should not raise an exception if it can handle these types. | ||
""" | ||
object_serializer_set[cls] | ||
|
||
|
||
def test_schematics_uses_cached_entries(): | ||
""" | ||
Regression test. Validating that SchematicsSerializer uses the | ||
same json schema it used previously, as before erroneous | ||
use of the instantiated model as a key was causing memory leaks. | ||
""" | ||
serializer = SchematicsSerializer() | ||
# A nested schema type is required as primitives have | ||
# hard-coded dictionaries representing the json. | ||
class SchematicsBody(Model): | ||
name = StringType(max_length=5) | ||
|
||
# ensure that instances have the same key as well. | ||
instance = ModelType(SchematicsBody) | ||
original_payload = serializer.to_json_schema(instance) | ||
second_payload = serializer.to_json_schema(instance) | ||
assert original_payload is second_payload | ||
|
||
# classes are also valid output to recieve that the json schema | ||
original_payload = serializer.to_json_schema(SchematicsBody) | ||
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. These 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. SchematicsBody classes should not get instantiated. you could argue _enforce_instance is a misnomer here, as schematics has an incongruent apis between "types" and "models":
that incongruency leads to a lot of the code that exists in this file, dealing with specific edge cases around the various classes and their similar, but slightly mismatched, APIs. to clarify, I could rename this method to "_enforce_instance_type_or_model". How does that sound? 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. Ahh, got it, thanks for clarifying. That change sounds great! |
||
second_payload = serializer.to_json_schema(SchematicsBody) | ||
assert original_payload is second_payload |
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.
Should we expect two different instances of the same thing to be the same payload? e.g.
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.
as far as transmute is concerned, it would be a weird pattern to wrap your type in a ModelType in a non-nested scenario. But could definitely see people doing that.
This won't cause a memory leak as ModelTypes are only created dynamically once when the raw type is passed through, and cached from that point on. Assuming the user isn't dynamically creating modeltypes, that cache won't grow unbounded.
I think it's a good optimization, but if I was to clean this up properly I'd probably add more strict validation to reject weird edge cases like this. If you'd like to follow up with that optimization in a PR I encourage it, but I'm personally not sure the extra complexity is worth a few fewer cache entries.