From 7af5af0dd60d4ea2b29f84e255213909f41f5c5a Mon Sep 17 00:00:00 2001 From: Simon Kohlmeyer Date: Thu, 20 Feb 2025 16:26:57 +0100 Subject: [PATCH] Fix interaction with procrastinate This monkey-patching broke procrastinate in an interesting way: Procrastinate uses the add_arguments hook to check the previously defined arguments like this: ```python class Command(BaseCommand): def add_arguments(self, parser): self._django_options = {a.dest for a in parser._actions} ... ``` in order to later filter them out like this: ```python def handle(self, *args, **kwargs): ... kwargs = {k: v for k, v in kwargs.items() if k not in self._django_options} ... ``` This is a problem because the CONFIGURATION_ARGUMENT arg is added *after* procrastinate's `add_arguments` is called. Here's the call graph: ``` create_parser (django-configurations) -> create_parser (django) (a.k.a. orig_create_parser) -> add_arguments (procrastinate) -> (CONFIGURATION_ARGUMENT added here) ``` This commit esesentially swaps the inner two actions so it looks like this: ``` create_parser (django-configurations) -> create_parser (django) (a.k.a. orig_create_parser) -> (CONFIGURATION_ARGUMENT added here) -> add_arguments (procrastinate) ``` It does this by temporarily overriding `add_arguments` with a no-op and then later restoring and calling it. This is a hack on top of two hacks, but I can't really see any other ways to make it work. --- configurations/importer.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configurations/importer.py b/configurations/importer.py index 9403f8e..d680aa1 100644 --- a/configurations/importer.py +++ b/configurations/importer.py @@ -31,7 +31,13 @@ def install(check_options=False): orig_create_parser = base.BaseCommand.create_parser def create_parser(self, prog_name, subcommand): + # Since some subclasses of BaseCommand, procrastinate's in particular, assume that all + # but their own arguments are already defined when add_arguments is called, we're + # temporarily swapping it out for a no-op and call it later. + add_arguments = self.add_arguments + self.add_arguments = lambda *a, **k: None parser = orig_create_parser(self, prog_name, subcommand) + if isinstance(parser, OptionParser): # in case the option_list is set the create_parser # will actually return a OptionParser for backward @@ -43,6 +49,9 @@ def create_parser(self, prog_name, subcommand): # probably argparse, let's not import argparse though parser.add_argument(CONFIGURATION_ARGUMENT, help=CONFIGURATION_ARGUMENT_HELP) + + self.add_arguments = add_arguments + self.add_arguments(parser) return parser base.BaseCommand.create_parser = create_parser