Попаднах на подобен проблем и след няколко часа кръв, пот и сълзи открих, че отговорът просто изисква добавяне на един параметър.
Вместо
cursor = conn.cursor()
пишете
cursor = conn.cursor(name="my_cursor_name")
или още по-просто
cursor = conn.cursor("my_cursor_name")
Подробностите се намират на http://initd.org/psycopg/docs/usage.html#server-side-cursors
Намерих инструкциите за малко объркващи, тъй като реших, че ще трябва да пренапиша своя SQL, за да включа "DECLARE my_cursor_name ..." и след това "FETCH count 2000 FROM my_cursor_name", но се оказа, че psycopg прави всичко това вместо вас под hood, ако просто презапишете параметъра по подразбиране "name=None", когато създавате курсор.
Предложението по-горе за използване на fetchone или fetchmany не решава проблема, тъй като, ако оставите параметъра за име ненастроен, psycopg по подразбиране ще се опита да зареди цялата заявка в ram. Единственото друго нещо, което може да се наложи да направите (освен да декларирате параметър за име), е да промените атрибута cursor.itersize от 2000 по подразбиране на 1000, ако все още имате твърде малко памет.