Reputation: 1607
I have the following code snippet that got me thinking
# Utlizing api_sess from the /token response
# api_sess is a requests.Session() with Authorization header set with session token
products_resp = api_sess.get(
f'{API_BASE}/products',
params = {'type': 'Item'}
)
if products_resp.ok:
while True:
current_page = products_resp.headers.get('X-Current-Page', 1)
total_pages = products_resp.headers.get('X-Total-Pages', 0)
products_json = products_resp.json()
for product in products_json:
print(product['groupCode'], product['sku'], product['productStatus'])
# Handle pagination
if current_page < total_pages:
products_resp = api_sess.get(
f'{API_BASE}/products',
params = {'type': 'Item', 'page': current_page + 1}
)
if not products_resp.ok:
# Break out of while loop before raising error
break
else:
break
# Raise exception outside of while loop to prevent memory leak
if not products_resp.ok:
raise ValueException(f'Request Error {products_resp.status_code}')
else:
raise ValueException(f'Request Error {products_resp.status_code}')
Originally I was going to raise
the exception inside of the while
where I check the response of the next page request like so:
if current_page < total_pages:
products_resp = api_sess.get(
f'{API_BASE}/products',
params = {'type': 'Item', 'page': current_page + 1}
)
if not products_resp.ok:
raise ValueException(f'Request Error {products_resp.status_code}')
else:
break
At first I didn't think anything of it, but I started to wonder if resource cleanpup would take place since the error is raised inside an infinite while
loop due to the True
condition. Thus, why I inevitably reworked my logic to break out of the while
loop first, then recheck response status.
In my opinion, this cautious method isn't as elegant as I'd like seeing as I'm duplicating the checking of the response status. Which leads me to my question:
Is it safe to keep the Exception raising inside the while
loop or will it lead to memory leaks?
I know the question and scenario might seem trivial, but I'd like to stick with best practices in all aspects whenever possible. And upon my googling (while I didn't spend hours atleast) of the matter did not turn out any applicable explanations of how this scenario is handled.
Upvotes: 1
Views: 1122
Reputation: 531768
Whether you leave the current scope via return
or an uncaught exception, the reference count on the object will be decreased. A simple example:
import sys
a = "hello"
def foo(b):
x = a
print(f'x: {sys.getrefcount(x)}')
if b:
return
else:
raise Exception()
print(f'a: {sys.getrefcount(a)}')
foo(True)
print(f'a: {sys.getrefcount(a)}')
try:
print(f'a: {sys.getrefcount(a)}')
foo(False)
except Exception:
pass
print(f'a: {sys.getrefcount(a)}')
The exact output may vary, but you should observe the reference count for x
being one greater than the reference count for a
, which remains constant before and after the calls to foo
.
In both cases, the reference count for the global a
increases when foo
is called, and is decreased after foo
exits (or after the try
statement which catches the exception completes). If the exception is never caught, the interpreter
exits anyway, making the question of a memory leak moot.
Upvotes: 3