contentful.py
contentful.py copied to clipboard
Fetching an entity recursive builds forever - hanging forever
When trying to fetch an entity via
workout = self.client.entry(workout_id)
The program hung and continued to grow in memory usage. The id put in the URL of contentful entity and it appears just fine.
Looking into the code I've found that the call to build
which is called from the _get
function is the recursive issue in resource_builder.py
. It will unpack everything in the entity and try to build its descendants.
In this instance, the entity has a link to similar workouts entities which may recursively link back to the original entity which I believe is the likely cause of this problem.
Though this obviously isn't an issue on the website, and is totally permitted behavior.
There looks like there are things in place to detect the depth of entities being built to try and address this issue but obviously there is a bug in this code and it fails to do so, or exit with exception.
In the order of execution build is called first and _build_array
is run, and then subsequently, _build_single()
is called over and over again.
Courting the number of times the original id appears in the ids list, I can see that it appear 7 times in the first 100 runs of the build single.
def build(self):
"""Creates the objects from the JSON response"""
if self.json['sys']['type'] == 'Array':
if any(k in self.json for k in ['nextSyncUrl', 'nextPageUrl']):
print('sync page')
return SyncPage(
self.json,
default_locale=self.default_locale,
localized=True
)
print('build array')
return self._build_array()
print('build single')
return self._build_single()
def _build_single(self):
includes = []
errors = []
if self.includes_for_single is not None:
includes = self.includes_for_single
if self.errors_for_single is not None:
errors = self.errors_for_single
return self._build_item(
self.json,
includes=includes,
errors=errors
)
def _build_array(self):
includes = self._includes()
print('build includes')
errors = self._errors()
print('build errors')
items = [self._build_item(
item,
includes=includes,
errors=errors
) for item in self.json['items']
if not unresolvable(item, self._errors())]
print('build array items')
return Array(self.json, items)
ids = list()
def _build_item(self, item, includes=None, errors=None):
print(f'building item {item}')
if includes is None:
includes = []
if errors is None:
errors = []
buildables = {
'Entry': Entry,
'Asset': Asset,
'ContentType': ContentType,
'Space': Space,
'DeletedEntry': DeletedEntry,
'DeletedAsset': DeletedAsset,
'Locale': Locale
}
print(self.reuse_entries)
resource = self._resource_from_cache(item) if self.reuse_entries else None
if resource is not None:
return resource
print('cache')
if item['sys']['type'] in buildables:
print('check item', item)
self.ids.append(item['sys']['id'])
if len(self.ids) > 100:
print(self.ids)
s = set(self.ids)
print(len(s), s)
exit()
return buildables[item['sys']['type']](
item,
default_locale=self.default_locale,
localized=self.localized,
includes=includes,
errors=errors,
resources=self.resources,
depth=self.depth,
max_depth=self.max_depth
)