# Buggy code
def _from_table_row(cls, tr: bs4.Tag) -> "AtCoderProblem":
    tds = tr.find_all("td")
    assert len(tds) == 5
    path = tds[1].find("a")["href"]
    self = cls.from_url("https://atcoder.jp" + path)
    assert self is not None
    self._alphabet = tds[0].text
    self._task_name = tds[1].text
    self._time_limit_msec = int(float(utils.remove_suffix(tds[2].text, " sec")) * 1000)
    self._memory_limit_byte = (
        int(utils.remove_suffix(tds[3].text, " MB")) * 1000 * 1000
    )  # TODO: confirm this is MB truly, not MiB
    assert tds[4].text.strip() in ("", "Submit")
    return self


# Traceback (AssertionError)
$ python3 setup.py test -s tests.service_atcoder
running test
running egg_info
writing online_judge_tools.egg-info/PKG-INFO
writing dependency_links to online_judge_tools.egg-info/dependency_links.txt
writing requirements to online_judge_tools.egg-info/requires.txt
writing top-level names to online_judge_tools.egg-info/top_level.txt
reading manifest file 'online_judge_tools.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'online_judge_tools.egg-info/SOURCES.txt'
running build_ext
test_from_url (tests.service_atcoder.AtCoderContestTest) ... ok
test_iterate_submissions (tests.service_atcoder.AtCoderContestTest) ... ok
test_list_problems (tests.service_atcoder.AtCoderContestTest) ... FAIL
test_load_details (tests.service_atcoder.AtCoderContestTest) ... ok
test_normal (tests.service_atcoder.AtCoderProblemGetInputFormatTest) ... ok
test_old_problem (tests.service_atcoder.AtCoderProblemGetInputFormatTest) ... ok
test_from_url (tests.service_atcoder.AtCoderProblemTest) ... ok
test_get_alphabet (tests.service_atcoder.AtCoderProblemTest) ... ok
test_get_score (tests.service_atcoder.AtCoderProblemTest) ... ok
test_iterate_submissions (tests.service_atcoder.AtCoderProblemTest) ... ok
test_load_details (tests.service_atcoder.AtCoderProblemTest) ... ok
test_from_url (tests.service_atcoder.AtCoderSerivceTest) ... ok
test_iterate_contests (tests.service_atcoder.AtCoderSerivceTest) ... ok
test_from_url (tests.service_atcoder.AtCoderSubmissionTest) ... ok
test_get_source_code (tests.service_atcoder.AtCoderSubmissionTest) ... ok
test_get_test_cases (tests.service_atcoder.AtCoderSubmissionTest) ... ok
test_get_test_sets (tests.service_atcoder.AtCoderSubmissionTest) ... ok
test_submission_info (tests.service_atcoder.AtCoderSubmissionTest) ... ok

======================================================================
FAIL: test_list_problems (tests.service_atcoder.AtCoderContestTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/user/GitHub/online-judge-tools/tests/service_atcoder.py", line 61, in test_list_problems
problems = contest.list_problems()
File "/home/user/GitHub/online-judge-tools/onlinejudge/service/atcoder.py", line 286, in list_problems
return [AtCoderProblem._from_table_row(tr) for tr in tbody.find_all('tr')]
File "/home/user/GitHub/online-judge-tools/onlinejudge/service/atcoder.py", line 286, in <listcomp>
return [AtCoderProblem._from_table_row(tr) for tr in tbody.find_all('tr')]
File "/home/user/GitHub/online-judge-tools/onlinejudge/service/atcoder.py", line 366, in _from_table_row
assert len(tds) == 5
AssertionError

----------------------------------------------------------------------
Ran 18 tests in 4.449s

FAILED (failures=1)
Test failed: <unittest.runner.TextTestResult run=18 errors=0 failures=1>
error: Test failed: <unittest.runner.TextTestResult run=18 errors=0 failures=1>

# Fixed code
def _from_table_row(cls, tr: bs4.Tag) -> "AtCoderProblem":
    tds = tr.find_all("td")
    assert 4 <= len(tds) <= 5
    path = tds[1].find("a")["href"]
    self = cls.from_url("https://atcoder.jp" + path)
    assert self is not None
    self._alphabet = tds[0].text
    self._task_name = tds[1].text
    self._time_limit_msec = int(float(utils.remove_suffix(tds[2].text, " sec")) * 1000)
    self._memory_limit_byte = (
        int(utils.remove_suffix(tds[3].text, " MB")) * 1000 * 1000
    )  # TODO: confirm this is MB truly, not MiB
    if len(tds) == 5:
        assert tds[4].text.strip() in ("", "Submit", "提出")
    return self