Changeset 5c879a8 in rattail


Ignore:
Timestamp:
11/20/2022 07:35:41 PM (2 months ago)
Author:
Lance Edgar <lance@…>
Branches:
master
Children:
23318f2
Parents:
8cda194
Message:

Add luigi module/class awareness to overnight task launcher

so we can invoke luigi directly instead of relying on script

this also improves logic for how run-n-mail and/or at are used

Location:
rattail
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • rattail/commands/luigi.py

    r8cda194 r5c879a8  
    5757                            "determining recipients etc.")
    5858
     59        parser.add_argument('--wait', action='store_true', default=True,
     60                            help="Run the task in-process, and wait for it to "
     61                            "finish.  This is the default behavior.")
     62        parser.add_argument('--no-wait', action='store_false', dest='wait',
     63                            help="Do not wait for the task to finish.  This will "
     64                            "cause the command to run via the `at` utility, i.e. "
     65                            "scheduled to start within the next minute.")
     66
    5967        parser.add_argument('--dry-run', action='store_true',
    6068                            help="Log the final command for the task, but do not "
     
    7078
    7179        date = args.date or self.app.yesterday()
    72         luigi_handler.launch_overnight_task(task, date, with_at=False,
     80        luigi_handler.launch_overnight_task(task, date,
    7381                                            email_if_empty=args.email_if_empty,
    7482                                            email_key=args.email_key,
     83                                            wait=args.wait,
    7584                                            dry_run=args.dry_run)
  • rattail/luigi/handler.py

    r8cda194 r5c879a8  
    114114                'key': key,
    115115                'description': self.get_overnight_task_setting(key, 'description'),
     116                'module': self.get_overnight_task_setting(key, 'module'),
     117                'class_name': self.get_overnight_task_setting(key, 'class_name'),
    116118                'script': self.get_overnight_task_setting(key, 'script'),
    117119                'notes': self.get_overnight_task_setting(key, 'notes'),
     
    151153                              email_if_empty=True,
    152154                              email_key=None,
    153                               with_at=True,
     155                              wait=True,
    154156                              dry_run=False,
    155157                              **kwargs):
     
    170172           used in determining recipients etc.
    171173
    172         :param with_at: If true (currently the default), the task
    173            should be scheduled via the ``at`` command, to begin within
    174            the next minute.  (This lets process control return
    175            immediately to the caller.)  If false, the task will run
     174        :param wait: If true (the default), the task will run
    176175           in-process, and so will begin immediately, but caller must
    177            wait for it to complete.  You are encouraged to specify the
    178            value you want here, as the default may change in the
    179            future.
     176           wait for it to complete.  If false, the task will be
     177           scheduled via the ``at`` command, to begin within the next
     178           minute.  (This lets process control return immediately to
     179           the caller.)
    180180
    181181        :param dry_run: If true, log the final command for the task
     
    183183        """
    184184        appdir = self.config.appdir()
    185 
    186185        env = {
     186            'PYTHONPATH': appdir,
    187187            'RATTAIL_CONFIG_FILES': os.path.join(appdir, 'silent.conf'),
    188188        }
    189189
    190         cmd = '{} {}'.format(task['script'], date)
    191 
    192         cmd = [os.path.join(sys.prefix, 'bin', 'rattail'),
    193                '--config', os.path.join(appdir, 'silent.conf'),
    194                '--no-versioning',
    195                'run-n-mail',
    196                '-S', "Overnight for {}: {}".format(date, task['key']),
    197                cmd]
    198         if email_key:
    199             cmd.extend(['--key', email_key])
    200         if not email_if_empty:
    201             cmd.append('--skip-if-empty')
    202 
    203         if with_at:
     190        # build our command, which will vary per caller request.  by
     191        # default we do not use the shell to run command, but in some
     192        # cases must (e.g. when invoking `at`)
     193        shell = False
     194
     195        # not waiting means schedule via `at`
     196        if not wait:
     197
     198            # rattail overnight <task>
     199            cmd = [
     200                '{}/bin/rattail'.format(sys.prefix),
     201                '--config', '{}/silent.conf'.format(appdir),
     202                '--no-versioning',
     203                'overnight', task['key'],
     204                '--date={}'.format(date),
     205                '--wait',
     206            ]
     207            if email_key:
     208                cmd.extend(['--email-key', email_key])
     209            if not email_if_empty:
     210                cmd.append('--no-email-if-empty')
     211
     212            # echo 'rattail overnight <task>' | at now
    204213            cmd = ['echo', shlex_join(cmd)]
    205214            cmd = shlex_join(cmd)
    206215            cmd = "{} | at 'now + 1 minute'".format(cmd)
     216            shell = True        # must run command via shell
     217
     218        elif task['script']:
     219            # use whatever task config dictates
     220            cmd = self.config.parse_list(task['script'])
     221
     222        else:
     223            # invoke luigi directly, via run-n-mail
     224
     225            # luigi -m MOD TASK
     226            cmd = [
     227                '{}/bin/luigi'.format(sys.prefix),
     228                '--logging-conf-file', 'logging.conf',
     229                '--module', task['module'],
     230                task['class_name'],
     231                '--date={}'.format(date),
     232            ]
     233
     234            # rattail run-n-mail 'luigi -m MOD TASK'
     235            cmd = [
     236                os.path.join(sys.prefix, 'bin', 'rattail'),
     237                '--config', os.path.join(appdir, 'silent.conf'),
     238                '--no-versioning',
     239                'run-n-mail',
     240                '--subject', "Overnight for {}: {}".format(date, task['key']),
     241                shlex_join(cmd),
     242            ]
     243            if email_key:
     244                cmd.extend(['--key', email_key])
     245            if not email_if_empty:
     246                cmd.append('--skip-if-empty')
    207247
    208248        # log final command
     
    213253
    214254        # run command in subprocess
     255        curdir = os.getcwd()
    215256        try:
    216             subprocess.check_output(cmd, shell=with_at, env=env,
     257            # nb. always chdir to luigi folder, even if not needed
     258            os.chdir(os.path.join(appdir, 'luigi'))
     259            subprocess.check_output(cmd, shell=shell, env=env,
    217260                                    stderr=subprocess.PIPE)
    218261        except subprocess.CalledProcessError as error:
     
    221264            log.warning(error.stderr.decode('utf_8'))
    222265            raise
     266        finally:
     267            # nb. always change back to first dir, in case we're being
     268            # called by some long-running process, e.g. web app
     269            os.chdir(curdir)
    223270
    224271    def record_overnight_last_date(self, task, date, session=None, **kwargs):
Note: See TracChangeset for help on using the changeset viewer.