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.
This commit is contained in:
Simon Kohlmeyer 2025-02-20 16:26:57 +01:00
parent 3d0d4216ca
commit 7af5af0dd6

View file

@ -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 install(check_options=False):
# 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