Drop the duplicate DB save that crashed --submit --db (#3122)

The save at the end of Submitter.dialog read self.args.db, an attribute
argparse never creates: maigret.py declares --db with dest="db_file", and
the line right above it in the same block already reads self.args.db_file.

So `maigret --submit <url> --db custom.json` ran the whole interactive
dialog and then raised AttributeError on the final step. dialog() never
returned True, so main() never reached save_db_safely() either, and the
newly submitted site was lost after every question had been answered. The
default path was unaffected, because the block is guarded by
`db_file != settings.sites_db_path`.

Removing the block rather than renaming the attribute: main() already
persists the database after a successful submit, through
save_db_safely(db, db_file, logger). That call takes its path from
resolve_db_path(), which returns an absolute or module-relative path for a
custom --db, so the removed line could have written to a different file
than the rest of the run uses. save_db_safely also tolerates a read-only
installation, which db.save_to_file() does not.

The existing dialog tests pass a MagicMock, which answers to any attribute
name, so the typo was invisible to them. The new test builds its namespace
with the real parser.

Co-authored-by: datrixlab <325650023+datrixlab@users.noreply.github.com>
This commit is contained in:
Nguyen Thanh Dat
2026-10-04 19:49:32 +05:00
committed by GitHub
co-authored by datrixlab
parent 3c511cab41
commit 5de069eb88
2 changed files with 35 additions and 7 deletions
-7
View File
@@ -660,11 +660,4 @@ class Submitter:
final_site = old_site if old_site else stripped_site
self.db.update_site(final_site)
# save the db in file
if self.args.db_file != self.settings.sites_db_path:
print(
f"{Fore.GREEN}[+] Maigret DB is saved to {self.args.db}.{Style.RESET_ALL}"
)
self.db.save_to_file(self.args.db)
return True
+35
View File
@@ -208,6 +208,41 @@ async def test_dialog_selects_status_code_check(settings):
assert db.sites[0].check_type == "status_code"
@pytest.mark.asyncio
async def test_dialog_completes_with_parser_built_args(settings, tmp_path):
# The dialog tests above hand Submitter a MagicMock, which answers to any
# attribute name -- a reference to an attribute argparse never builds stays
# invisible there. At runtime Submitter gets the namespace below, where --db
# lands on `db_file`.
from maigret.maigret import setup_arguments_parser
args = setup_arguments_parser(settings).parse_args(
[
"--db",
str(tmp_path / "custom_db.json"),
"--submit",
"https://example.com/claimed",
]
)
assert args.db_file == str(tmp_path / "custom_db.json")
assert not hasattr(args, "db")
db = MaigretDatabase()
submitter = Submitter(db, settings, logging.getLogger("test_logger"), args)
submitter.detect_known_engine = AsyncMock(return_value=([], ""))
submitter.extract_username_dialog = MagicMock(return_value="claimed")
submitter.check_features_manually = AsyncMock(
return_value=(None, None, "Found", "unclaimed", 200, 404)
)
submitter.site_self_check = AsyncMock(return_value={"disabled": False})
with patch('builtins.input', side_effect=['y', '', '']):
result = await submitter.dialog("https://example.com/claimed", None)
assert result is True
assert len(db.sites) == 1
@pytest.mark.asyncio
async def test_dialog_keeps_message_check_for_redirect(settings):
db = MaigretDatabase()