Repository navigation
Handle dbi item groups that EduPage serialises as an empty array - #126
Merged
Merged
Conversation
EduPage serialises a `dbi` item group that has no entries as an empty JSON array
instead of an empty object. `DbiHelper.__get_item_with_id()` calls `.get()` on
the group, so an array-shaped group raises:
AttributeError: 'list' object has no attribute 'get'
Read an *empty* array as an empty mapping:
if isinstance(item_group, list) and not item_group:
return {}
Groups that are already objects are returned unchanged, and a missing group or a
missing `dbi` still returns `None`.
Only empty arrays are handled. A populated array has not been observed in a `dbi`
group, and mapping it onto positional keys would fabricate ids that can never
match a real reference - so a populated array is left alone and keeps failing
loudly rather than silently losing data.
Reported via rine77/homeassistantedupage (the Home Assistant integration), where
it presents as get_classes() and get_timetable() failing every poll:
get_classes failed: 'list' object has no attribute 'get'
Failed to fetch timetable data for <date>: 'list' object has no attribute 'get'
The failing lookup reaches this through get_teacher() -> get_teachers() ->
EduAccount.parse -> fetch_classroom_number(), so the group left empty is
classrooms, teachers or subjects. An empty `classes` group fails differently -
with 'list' object has no attribute 'items' - because get_classes() iterates it
rather than calling .get() on it.
Koky05
force-pushed
the
fix-dbi-array-item-groups
branch
from
October 8, 2026 18:48
58b0585 to
d2f3e63
Compare
Koky05
added a commit
to Koky05/homeassistantedupage
that referenced
this pull request
Oct 8, 2026
Two independent fixes for 2026.10.0, plus regression tests.
1. Empty array-shaped `dbi` item groups
EduPage serialises a `dbi` item group that has no entries as an empty JSON array
instead of an empty object. `DbiHelper.__get_item_with_id()` calls `.get()` on
the group, so an array-shaped group raises:
AttributeError: 'list' object has no attribute 'get'
breaking get_classes() and get_timetable() on every poll for accounts at schools
that leave a group empty. The timetable loop breaks on the first failure, so the
whole 14-day window is lost and the calendar stays empty.
`_normalise_dbi_groups()` reads an *empty* array as an empty mapping, applied at
the two points where a payload is handed to the library:
- `Edupage._load_session()` - the runtime path used on setup and every poll
- `config_flow._finalize_setup()` - every initial-setup path converges here:
plain login, the official two-factor flow, and the custom fallback driver
A populated array is deliberately left alone: mapping it onto positional keys
would fabricate ids that can never match a real reference, so it keeps raising
instead of silently losing data. Nested arrays inside a group are untouched too.
The reconfigure path (`_apply_reconfigure`) needs no change - it reads only the
PHPSESSID cookie and never touches `dbi`, and the reload it triggers
re-normalises via `_load_session()`.
Tests: `tests/test_dbi_normalise.py` covers empty groups, unchanged dictionaries
(identity-checked), nested arrays left alone, populated arrays untouched,
missing/malformed payloads, idempotence, the runtime call site, and an
end-to-end check that the real library no longer raises.
2. `await entry.async_start_reauth(...)`
`async_start_reauth()` is a `@callback` annotated `-> None` that schedules its own
task, so awaiting it always raises `TypeError: 'NoneType' object can't be
awaited`. Both call sites did that, on the session-expired path, so the reauth
flow was never started and the entry could not be recovered from the UI.
This is kept as its own commit so it can be reviewed or reverted independently.
Upstream fix for the library-side bug: EdupageAPI/edupage-api#126. This
workaround can be dropped once a released edupage-api includes it.
Koky05
added a commit
to Koky05/homeassistantedupage
that referenced
this pull request
Oct 8, 2026
Two independent fixes for 2026.10.0, plus regression tests.
1. Empty array-shaped `dbi` item groups
EduPage serialises a `dbi` item group that has no entries as an empty JSON array
instead of an empty object. `DbiHelper.__get_item_with_id()` calls `.get()` on
the group, so an array-shaped group raises:
AttributeError: 'list' object has no attribute 'get'
breaking get_classes() and get_timetable() on every poll for accounts at schools
that leave a group empty. The timetable loop breaks on the first failure, so the
whole 14-day window is lost and the calendar stays empty.
`_normalise_dbi_groups()` reads an *empty* array as an empty mapping, applied at
the two points where a payload is handed to the library:
- `Edupage._load_session()` - the runtime path used on setup and every poll
- `config_flow._finalize_setup()` - every initial-setup path converges here:
plain login, the official two-factor flow, and the custom fallback driver
A populated array is deliberately left alone: mapping it onto positional keys
would fabricate ids that can never match a real reference, so it keeps raising
instead of silently losing data. Nested arrays inside a group are untouched too.
The reconfigure path (`_apply_reconfigure`) needs no change - it reads only the
PHPSESSID cookie and never touches `dbi`, and the reload it triggers
re-normalises via `_load_session()`.
Tests: `tests/test_dbi_normalise.py` covers empty groups, unchanged dictionaries
(identity-checked), nested arrays left alone, populated arrays untouched,
missing/malformed payloads, idempotence, the runtime call site, and an
end-to-end check that the real library no longer raises.
2. `await entry.async_start_reauth(...)`
`async_start_reauth()` is a `@callback` annotated `-> None` that schedules its own
task, so awaiting it always raises `TypeError: 'NoneType' object can't be
awaited`. Both call sites did that, on the session-expired path, so the reauth
flow was never started and the entry could not be recovered from the UI.
This is kept as its own commit so it can be reviewed or reverted independently.
Upstream fix for the library-side bug: EdupageAPI/edupage-api#126. This
workaround can be dropped once a released edupage-api includes it.
`tests/test_reauth.py` is updated accordingly: it used `AsyncMock` for
`entry.async_start_reauth` and asserted `assert_awaited_once()`, which encoded
the bug rather than the intended behaviour. It now uses `Mock` and asserts on the
call. No other test asserted the awaited form - the remaining `assert_awaited*`
uses in the suite are `async_call_service` and `config_entries.async_reload`,
which are genuine coroutines.
rine77
pushed a commit
to rine77/homeassistantedupage
that referenced
this pull request
Oct 9, 2026
Two independent fixes for 2026.10.0, plus regression tests.
1. Empty array-shaped `dbi` item groups
EduPage serialises a `dbi` item group that has no entries as an empty JSON array
instead of an empty object. `DbiHelper.__get_item_with_id()` calls `.get()` on
the group, so an array-shaped group raises:
AttributeError: 'list' object has no attribute 'get'
breaking get_classes() and get_timetable() on every poll for accounts at schools
that leave a group empty. The timetable loop breaks on the first failure, so the
whole 14-day window is lost and the calendar stays empty.
`_normalise_dbi_groups()` reads an *empty* array as an empty mapping, applied at
the two points where a payload is handed to the library:
- `Edupage._load_session()` - the runtime path used on setup and every poll
- `config_flow._finalize_setup()` - every initial-setup path converges here:
plain login, the official two-factor flow, and the custom fallback driver
A populated array is deliberately left alone: mapping it onto positional keys
would fabricate ids that can never match a real reference, so it keeps raising
instead of silently losing data. Nested arrays inside a group are untouched too.
The reconfigure path (`_apply_reconfigure`) needs no change - it reads only the
PHPSESSID cookie and never touches `dbi`, and the reload it triggers
re-normalises via `_load_session()`.
Tests: `tests/test_dbi_normalise.py` covers empty groups, unchanged dictionaries
(identity-checked), nested arrays left alone, populated arrays untouched,
missing/malformed payloads, idempotence, the runtime call site, and an
end-to-end check that the real library no longer raises.
2. `await entry.async_start_reauth(...)`
`async_start_reauth()` is a `@callback` annotated `-> None` that schedules its own
task, so awaiting it always raises `TypeError: 'NoneType' object can't be
awaited`. Both call sites did that, on the session-expired path, so the reauth
flow was never started and the entry could not be recovered from the UI.
This is kept as its own commit so it can be reviewed or reverted independently.
Upstream fix for the library-side bug: EdupageAPI/edupage-api#126. This
workaround can be dropped once a released edupage-api includes it.
`tests/test_reauth.py` is updated accordingly: it used `AsyncMock` for
`entry.async_start_reauth` and asserted `assert_awaited_once()`, which encoded
the bug rather than the intended behaviour. It now uses `Mock` and asserts on the
call. No other test asserted the awaited form - the remaining `assert_awaited*`
uses in the suite are `async_call_service` and `config_entries.async_reload`,
which are genuine coroutines.
Member
|
I thought about what to do when edupage serializes something as a non-empty array. I've decided that can't happen and if it will, other things will probably break, so leaving it to fail with Thank you for your contribution! |
Koky05
added a commit
to Koky05/edupage-api
that referenced
this pull request
Oct 12, 2026
The body of `if item_group == []:` uses 3 spaces instead of 12.
if item_group == []:
return {}
Shipped in 0.13.2 via EdupageAPI#126. No behaviour change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Handle dbi item groups that EduPage serialises as an empty array
Problem
Since 2026-10-08,
get_classes()andget_timetable()fail for some accounts:dbiitself is still an object. The change is one level deeper: EduPage nowserialises a
dbiitem group that has no entries as an empty JSON array(
[]) instead of an empty object ({}).DbiHelper.__get_item_with_id()doesitems_in_group.get(str(item_id)), sowhen the group arrives as an array that call raises
AttributeError. Thegroups reached through
.get()areteachers,students,subjects,classroomsandclasses, so which call fails depends on which group theschool happens to leave empty.
Observed payload (types only, values omitted):
Accounts at a school that populates every group are unaffected, which is why
this looks intermittent.
Fix
Normalise array-shaped groups in
__get_item_group()— the single place everygroup lookup passes through:
An empty array becomes an empty mapping, which is exactly the pre-change
behaviour. Groups that are already objects are returned untouched, and a
missing group or missing
dbistill returnsNone.This is deliberately handled generically rather than per group name: the shape
change is not tied to one group, and EduPage may serialise other groups this
way later.
Tests
New
tests/test_dbi.py, following the existingunittestconventions:Noneteachers,students,subjects,classrooms,classes) is normalised, not just onedbistill returnNoneVerified with the full suite: 38 passed (33 pre-existing + 5 new). Without
the patch, 2 of the new tests fail.
Notes for review
a non-empty array in a
dbigroup, so that path is defensive rather thanevidence-driven. If EduPage does send populated arrays, the key format should
probably follow the item's own id instead of its position — happy to adjust
if anyone has a sample payload.
Which group is actually empty
Worth recording, because it is not obvious and it explains why this looks
intermittent. The failing lookup reaches
__get_item_with_id()viaso the group left empty is
classrooms,teachersorsubjects. An emptyclassesgroup fails differently - with'list' object has no attribute 'items'- becauseget_classes()iterates it rather than calling.get()on it.Reproduced against 0.13.0:
Different schools leave different groups empty, which is why only some accounts
are affected.
Related
where it presents as the same
AttributeErroron edupage-api 0.13.0/0.13.1.at a different school whose relevant group is empty.