Skip to main content

Command Palette

Search for a command to run...

LOG-5 Week 2: "Milestone 1, Coming to an end"

Working at GNU Mailman GSoC'26

Updated
3 min read
LOG-5 Week 2: "Milestone 1, Coming to an end"

It has been a while since I updated on the week 2 work that about to come to an end now, I pushed my working branch and opened an MR (PR for github people), and it hasn't been merged yet because it needs more reformation.
I have slowed down a bit because of my uni exams going right now..

Working with my mentor I have come to realize that there is still so much I have to learn and do, I mean, GSoC sure does give you some real deal of people as your mentors and you are almost very minute in front of them, but they still saves time to have your back at every step even when you are lagging yourself haha!

Work
Two files were changed, three CI fix committed, one draft MR is open against the upstream master.
4e8f06c4 : Fix isort length sorting for backup utilities
05fd7694 : Fix isort ordering and add coverage tests
87930b93 : Fix Language serialization and flake8 issues

What my Mentor caught:

Problem 1 - the interface was coupled to the filesystem.
The original IBackupManager looked like this:

def export_list(mlist, output_path):
    # writes JSON to a file on disk

def restore_list(backup_path, mlist=None, overwrite=False):
    # reads JSON from a file path

Two callers will use this interface. The CLI, which works with files. And the REST endpoint, which doesn't. It sends "HTTP JSON" and receives uploaded data directly.
Coupling the interface to file paths means one of those callers can't use it cleanly. The fix is to make the engine transport-agnostic:

def export_list(self, mlist):
    # returns a dict: caller decides what to do with it

def restore_list(self, data, fqdn_listname=None, overwrite=False):
    # accepts a dict: caller decides where it came from

CLI gets the dict and writes it to disk. REST gets the dict and returns it as JSON. Same interface, two callers, neither knows about the other. That's the whole point of an interface.

Problem 2 - the interface existed but nothing implemented it

IBackupManager was declared but getUtility(IBackupManager) would have crashed because nothing was registered.

The fix, turns out isn't to delete the interface, it's to wire it properly. A concrete BackupManager class goes into models/backup_manager.py, decorated with @implementer(IBackupManager) and registered in configure.zcml. Then CLI and REST both call getUtility(IBackupManager) to get the implementation at runtime. That's what ZCA is actually for.

Problem 3 - _coerce_for_json() was missing a type (CI failed once before)

CI failed because preferred_language returns an ILanguage object, not an Enum. The original coercion function never handled it:

# before : ILanguage fell through unchanged, broke JSON serialization
def _coerce_for_json(value):
    if isinstance(value, Enum):
        return value.name
    ...
# after : ILanguage caught first
def _coerce_for_json(value):
    if ILanguage.providedBy(value):
        return value.code
    if isinstance(value, Enum):
        return value.name
    ...

the pipeline was green after that.

Next steps:

The three fixes above are not yet implemented. I have instead mailed my mentor to ask for his approval on the same. The rest of Milestone 2 will pick up speed after June 25 (exam ends, third year ends).
Milestone 2: the full export pipeline for aliases, header-match rules, bans, and archivers, then the CLI commands.

The MR stays draft until all of that lands. check it out: MR !1505

Thanks!
BYee,
Navya (Naveeeya)