summaryrefslogtreecommitdiff
path: root/jobs/__init__.py
blob: efce333dad177ecf27eab8d40de1dd1c13ed5e5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env python3

from builtins import str
from builtins import object
import os
import sys
import inspect
import logging
from imp import load_module, find_module
import importlib

import jobs
import jobs.JobBase
import jobs.JobSpawner

class JobFinder(object):
    def __init__(self, config):
        """
        Opens the jobs folder and looks at every .py module in that directory.
        Finds available jobs by looking at any class defined in those modules
        that implements the JobBase abstract class.
        Returns a list of job classes.
        """
        self._jobs = set([])
        self.config = config
        
        job_modules = self.get_job_modules_dynamic()
        
        for module in job_modules:
            # Check every declaration in that module
            for name in dir(module):
                obj = getattr(module, name)
                if name not in module.__name__:
                    # Jobs have to have the same class name as their module name
                    # This prevents Job B from being detected twice when there is a Job A that imports Job B
                    continue

                if inspect.isclass(obj):
                    # A class declaration was found in that module
                    # Checking if it's a subclass of JobBase
                    # Discarding JobBase as a subclass of JobBase
                    if obj != jobs.JobBase.JobBase and obj != jobs.JobSpawner.JobSpawner:
                        logging.info("Found " + str(obj))
                        for base in obj.__bases__:
                            # H4ck because issubclass() doesn't seem to work as expected on Linux
                            # It has to do with JobBase being imported multiple times (within jobs) or something
                            if base.__name__ == 'JobBase':
                                # A job was found, keep it
                                self._jobs.add(obj(self.config))
                            elif base.__name__ == 'JobSpawner':
                                spawner = obj()
                                for j in spawner.get_sub_jobs(self.config):
                                    self._jobs.add(j)

    
    def get_job_modules_dynamic(self):
        job_modules = []

        job_dir = jobs.__path__[0]
        full_job_dir = os.path.join(sys.path[0], job_dir)
        if os.path.exists(full_job_dir):
            for (root, dirs, files) in os.walk(full_job_dir):
                del dirs[:] # Do not walk into subfolders of the job directory
                # Checking every .py module in the job directory
                jobs_loaded = []
                for source in (s for s in files if s.endswith((".py"))):
                    module_name = os.path.splitext(os.path.basename(source))[0]
                    if module_name in jobs_loaded:
                        continue
                    jobs_loaded.append(module_name)
                    full_name = os.path.splitext(source)[0].replace(os.path.sep,'.')

                    try: # Try to import the job package
                    # The job package HAS to be imported as a submodule
                    # of module 'jobs' or it will break windows compatibility
                        (file, pathname, description) = \
                            find_module(full_name, jobs.__path__)
                        module = load_module('jobs.' + full_name, file,
                                                pathname, description)
                    except Exception as e:
                        logging.critical('Import Error on ' + module_name + ': ' + repr(e))
                        logging.critical(logging.traceback.format_exc())
                        jobs.JobBase.sendEmail(self.config, 'Import Error on ' + module_name, str(e))
                        continue
                    job_modules.append(module)
        return job_modules
    
    def get_jobs(self):
        return self._jobs