Пример #1
0
    def save(self, *args, **kwargs):
        if not self.name:
            self.name = self.job_name.split('.')[-1]

        # [TODO] not too efficient... consider do the invalidation in db?
        try:
            old = WorkflowJob.objects.get(pk=self.pk)
        except WorkflowJob.DoesNotExist:
            old = WorkflowJob() # empty
        cond1 = self.workflow_id != old.workflow_id
        cond2 = self.job_id != old.job_id
        cond3 = not deep_eq(self.job_settings, old.job_settings)
        super(WorkflowJob, self).save(*args, **kwargs)
        if cond1 or cond2 or cond3:
            wf_id = self.workflow_id
            Workflow.objects.filter(pk__in=list(set([wf_id, old.workflow_id]))).update(valid=False)
Пример #2
0
    def save(self, *args, **kwargs):
        if not self.name:
            self.name = self.job_name.split('.')[-1]

        # [TODO] not too efficient... consider do the invalidation in db?
        try:
            old = WorkflowJob.objects.get(pk=self.pk)
        except WorkflowJob.DoesNotExist:
            old = WorkflowJob()  # empty
        cond1 = self.workflow_id != old.workflow_id
        cond2 = self.job_id != old.job_id
        cond3 = not deep_eq(self.job_settings, old.job_settings)
        super(WorkflowJob, self).save(*args, **kwargs)
        if cond1 or cond2 or cond3:
            wf_id = self.workflow_id
            Workflow.objects.filter(
                pk__in=list(set([wf_id, old.workflow_id]))).update(valid=False)
Пример #3
0
    def __init__(cls, clsname, bases, attrs):
        super(RodanTaskType, cls).__init__(clsname, bases, attrs)

        # check the number of arguments of implemented function
        if 'run_my_task' in attrs:
            argspec = inspect.getargspec(attrs['run_my_task'])
            assert len(argspec.args) == 4, 'run_my_task'
        if 'get_my_interface' in attrs:
            argspec = inspect.getargspec(attrs['get_my_interface'])
            assert len(argspec.args) == 3, 'get_my_interface'
        if 'validate_my_user_input' in attrs:
            argspec = inspect.getargspec(attrs['validate_my_user_input'])
            assert len(argspec.args) == 4, 'validate_my_user_input'
        if 'test_my_task' in attrs:
            argspec = inspect.getargspec(attrs['test_my_task'])
            assert len(argspec.args) == 2, 'test_my_task'

        if attrs.get('_abstract') == True:  # not the abstract class
            return
        else:
            if not Job.objects.filter(name=attrs['name']).exists():
                if not getattr(settings, '_update_rodan_jobs', None) and not settings.TEST:
                    raise ImproperlyConfigured('The catalogue of local jobs does not match the ones in database: local job `{0}` has not been registered. Please run `manage.py migrate` on Rodan server to update the database.')

                try:
                    # verify the schema
                    jsonschema.Draft4Validator.check_schema(attrs['settings'])
                except jsonschema.exceptions.SchemaError as e:
                    raise e
                schema = attrs['settings'] or {'type': 'object'}

                j = Job(name=attrs['name'],
                        author=attrs['author'],
                        description=attrs['description'],
                        settings=schema,
                        enabled=attrs['enabled'],
                        category=attrs['category'],
                        interactive=attrs['interactive'])
                j.save()

                try:
                    for ipt in attrs['input_port_types']:
                        i = InputPortType(job=j,
                                          name=ipt['name'],
                                          minimum=ipt['minimum'],
                                          maximum=ipt['maximum'],
                                          is_list=ipt.get('is_list', False))
                        i.save()
                        resource_types = RodanTaskType._resolve_resource_types(ipt['resource_types'])
                        if len(resource_types) == 0:
                            raise ValueError('No available resource types found for this InputPortType: {0}'.format(ipt['resource_types']))
                        i.resource_types.add(*resource_types)

                    for opt in attrs['output_port_types']:
                        o = OutputPortType(job=j,
                                           name=opt['name'],
                                           minimum=opt['minimum'],
                                           maximum=opt['maximum'],
                                           is_list=opt.get('is_list', False))
                        o.save()
                        resource_types = RodanTaskType._resolve_resource_types(opt['resource_types'])
                        if len(resource_types) == 0:
                            raise ValueError('No available resource types found for this OutputPortType: {0}'.format(opt['resource_types']))
                        o.resource_types.add(*resource_types)
                except Exception as e:
                    j.delete()  # clean the job
                    raise e

                if not settings.TEST:
                    print "Added: {0}".format(j.name)
            else:
                UPDATE_JOBS = getattr(rodan_settings, "_update_rodan_jobs", False)
                # perform an integrity check, and update jobs if demanded.
                j = Job.objects.get(name=attrs['name'])

                def check_field(field_name, original_value, new_value, compare_fn=lambda x, y: x == y):
                    if not compare_fn(original_value, new_value):
                        if not UPDATE_JOBS:
                            raise ImproperlyConfigured("The field `{0}` of Job `{1}` seems to be updated: {2} --> {3}. Try to run `manage.py migrate` to confirm this update.".format(field_name, j.name, convert_to_unicode(original_value), convert_to_unicode(new_value)))
                        else:
                            confirm_update = raw_input("The field `{0}` of Job `{1}` seems to be updated: \n{2}\n  -->\n{3}\n\nConfirm (y/N)? ".format(field_name, j.name, convert_to_unicode(original_value), convert_to_unicode(new_value)))
                            if confirm_update.lower() == 'y':
                                setattr(j, field_name, new_value)
                                j.save()
                                print "  ..updated.\n\n"
                            else:
                                print "  ..not updated.\n\n"

                check_field("author", j.author, attrs['author'])
                check_field("description", j.description, attrs['description'])
                check_field("settings", j.settings, attrs['settings'] or {'type': 'object'}, compare_fn=lambda x, y: deep_eq(x, y))
                check_field("enabled", j.enabled, attrs['enabled'])
                check_field("category", j.category, attrs['category'])
                check_field("interactive", j.interactive, attrs['interactive'])


                # Input Port Types
                def check_port_types(which):
                    "which == 'in' or 'out'"
                    if which == 'in':
                        attrs_pts = list(copy.deepcopy(attrs['input_port_types']))
                        db_pts = list(j.input_port_types.all())
                        msg = 'Input'
                    elif which == 'out':
                        attrs_pts = list(copy.deepcopy(attrs['output_port_types']))
                        db_pts = list(j.output_port_types.all())
                        msg = 'Output'

                    for pt in db_pts:
                        pt_name = pt.name

                        idx = next((i for (i, this_pt) in enumerate(attrs_pts) if this_pt['name'] == pt_name), None)
                        if idx is not None:  # pt exists in database and in code. Check values
                            attrs_pt = attrs_pts[idx]

                            # Compare values
                            if attrs_pt['minimum'] != pt.minimum:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update.".format('minimum', pt_name, j.name, pt.minimum, attrs_pt['minimum'], msg))
                                else:
                                    confirm_update = raw_input("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? ".format('minimum', pt_name, j.name, pt.minimum, attrs_pt['minimum'], msg))
                                    if confirm_update.lower() == 'y':
                                        pt.minimum = attrs_pt['minimum']
                                        pt.save()
                                        print "  ..updated.\n\n"
                                    else:
                                        print "  ..not updated.\n\n"

                            if attrs_pt['maximum'] != pt.maximum:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update.".format('maximum', pt_name, j.name, pt.maximum, attrs_pt['maximum'], msg))
                                else:
                                    confirm_update = raw_input("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? ".format('maximum', pt_name, j.name, pt.maximum, attrs_pt['maximum'], msg))
                                    if confirm_update.lower() == 'y':
                                        pt.maximum = attrs_pt['maximum']
                                        pt.save()
                                        print "  ..updated.\n\n"
                                    else:
                                        print "  ..not updated.\n\n"

                            attrs_is_list = bool(attrs_pt.get('is_list', False))
                            if attrs_is_list != pt.is_list:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update.".format('is_list', pt_name, j.name, pt.is_list, attrs_is_list, msg))
                                else:
                                    confirm_update = raw_input("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? ".format('is_list', pt_name, j.name, pt.is_list, attrs_is_list, msg))
                                    if confirm_update.lower() == 'y':
                                        pt.is_list = attrs_is_list
                                        pt.save()
                                        print "  ..updated.\n\n"
                                    else:
                                        print "  ..not updated.\n\n"


                            resource_types = RodanTaskType._resolve_resource_types(attrs_pt['resource_types'])
                            rt_code = set(map(lambda rt: rt.mimetype, resource_types))
                            rt_db = set(map(lambda rt: rt.mimetype, pt.resource_types.all()))
                            if rt_code != rt_db:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update.".format('resource_types', pt_name, j.name, rt_db, rt_code, msg))
                                else:
                                    confirm_update = raw_input("The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? ".format('resource_types', pt_name, j.name, rt_db, rt_code, msg))
                                    if confirm_update.lower() == 'y':
                                        pt.resource_types.clear()
                                        pt.resource_types.add(*resource_types)
                                        print "  ..updated.\n\n"
                                    else:
                                        print "  ..not updated.\n\n"

                            del attrs_pts[idx]

                        else:  # pt exists in database but not in code. Should be deleted.
                            if not UPDATE_JOBS:
                                raise ImproperlyConfigured("The {2} Port Type `{0}` of Job `{1}` seems to be deleted. Try to run `manage.py migrate` to confirm this deletion.".format(pt_name, j.name, msg))
                            else:
                                confirm_delete = raw_input("The {2} Port Type `{0}` of Job `{1}` seems to be deleted. Confirm (y/N)? ".format(pt_name, j.name, msg))
                                if confirm_delete.lower() == 'y':
                                    try:
                                        pt.delete()
                                        print "  ..deleted.\n\n"
                                    except Exception as e:
                                        print "  ..not deleted because of an exception: {0}. Please fix it manually.\n\n".format(str(e))
                                else:
                                    print "  ..not deleted.\n\n"

                    if attrs_pts:  # ipt exists in code but not in database. Should be added to the database.
                        for pt in attrs_pts:
                            if not UPDATE_JOBS:
                                raise ImproperlyConfigured("The {2} Port Type `{0}` of Job `{1}` seems to be newly added. Try to run `manage.py migrate` to confirm this update.".format(pt['name'], j.name, msg))
                            else:
                                confirm_update = raw_input("The {2} Port Type `{0}` of Job `{1}` seems to be newly added. Confirm (y/N)? ".format(pt['name'], j.name, msg))
                                if confirm_update.lower() == 'y':
                                    if which == 'in':
                                        Model = InputPortType
                                    elif which == 'out':
                                        Model = OutputPortType
                                    i = Model(job=j,
                                              name=pt['name'],
                                              minimum=pt['minimum'],
                                              maximum=pt['maximum'],
                                              is_list=bool(pt.get('is_list', False)))
                                    i.save()
                                    resource_types = RodanTaskType._resolve_resource_types(pt['resource_types'])
                                    if len(resource_types) == 0:
                                        raise ValueError('No available resource types found for this {1}PortType: {0}'.format(pt['resource_types'], msg))
                                    i.resource_types.add(*resource_types)
                                    print "  ..updated.\n\n"
                                else:
                                    print "  ..not updated.\n\n"

                check_port_types('in')
                check_port_types('out')

            # Process done
            from rodan.jobs.load import job_list
            if attrs['name'] in job_list:
                job_list.remove(attrs['name'])
Пример #4
0
    def __init__(cls, clsname, bases, attrs):
        super(RodanTaskType, cls).__init__(clsname, bases, attrs)

        # check the number of arguments of implemented function
        if 'run_my_task' in attrs:
            argspec = inspect.getargspec(attrs['run_my_task'])
            assert len(argspec.args) == 4, 'run_my_task'
        if 'get_my_interface' in attrs:
            argspec = inspect.getargspec(attrs['get_my_interface'])
            assert len(argspec.args) == 3, 'get_my_interface'
        if 'validate_my_user_input' in attrs:
            argspec = inspect.getargspec(attrs['validate_my_user_input'])
            assert len(argspec.args) == 4, 'validate_my_user_input'
        if 'test_my_task' in attrs:
            argspec = inspect.getargspec(attrs['test_my_task'])
            assert len(argspec.args) == 2, 'test_my_task'

        if attrs.get('_abstract') == True:  # not the abstract class
            return
        else:

            # Set base settings schema if they do not already exist in the job.
            schema = attrs.get('settings', {
                'job_queue': 'celery',
                'type': 'object'
            })

            if not Job.objects.filter(name=attrs['name']).exists():
                if not getattr(settings, '_update_rodan_jobs',
                               None) and not settings.TEST:
                    raise ImproperlyConfigured(
                        'The catalogue of local jobs does not match the ones in database: local job `{0}` has not been registered. Please run `manage.py migrate` on Rodan server to update the database.'
                    )

                try:
                    # verify the schema
                    jsonschema.Draft4Validator.check_schema(attrs['settings'])
                except jsonschema.exceptions.SchemaError as e:
                    raise e

                j = Job(
                    name=attrs['name'],
                    author=attrs['author'],
                    description=attrs['description'],
                    settings=schema,
                    enabled=attrs['enabled'],
                    category=attrs['category'],
                    interactive=attrs['interactive'],
                    # Check for the presence of job_queue in the rodan job's settings, if not use the default 'celery'
                    job_queue=schema.get('job_queue', 'celery'))
                j.save()

                try:
                    for ipt in attrs['input_port_types']:
                        i = InputPortType(job=j,
                                          name=ipt['name'],
                                          minimum=ipt['minimum'],
                                          maximum=ipt['maximum'],
                                          is_list=ipt.get('is_list', False))
                        i.save()
                        resource_types = RodanTaskType._resolve_resource_types(
                            ipt['resource_types'])
                        if len(resource_types) == 0:
                            raise ValueError(
                                'No available resource types found for this InputPortType: {0}'
                                .format(ipt['resource_types']))
                        i.resource_types.add(*resource_types)

                    for opt in attrs['output_port_types']:
                        o = OutputPortType(job=j,
                                           name=opt['name'],
                                           minimum=opt['minimum'],
                                           maximum=opt['maximum'],
                                           is_list=opt.get('is_list', False))
                        o.save()
                        resource_types = RodanTaskType._resolve_resource_types(
                            opt['resource_types'])
                        if len(resource_types) == 0:
                            raise ValueError(
                                'No available resource types found for this OutputPortType: {0}'
                                .format(opt['resource_types']))
                        o.resource_types.add(*resource_types)
                except Exception as e:
                    j.delete()  # clean the job
                    raise e

                if not settings.TEST:
                    print("Added: {0}".format(j.name))
            else:
                UPDATE_JOBS = getattr(rodan_settings, "_update_rodan_jobs",
                                      False)
                # perform an integrity check, and update jobs if demanded.
                j = Job.objects.get(name=attrs['name'])

                def check_field(field_name,
                                original_value,
                                new_value,
                                compare_fn=lambda x, y: x == y):
                    if not compare_fn(original_value, new_value):
                        if not UPDATE_JOBS:
                            raise ImproperlyConfigured(
                                "The field `{0}` of Job `{1}` seems to be updated: {2} --> {3}. Try to run `manage.py migrate` to confirm this update."
                                .format(field_name, j.name,
                                        convert_to_unicode(original_value),
                                        convert_to_unicode(new_value)))
                        else:
                            confirm_update = confirm(
                                "The field `{0}` of Job `{1}` seems to be updated: \n{2}\n  -->\n{3}\n\nConfirm (y/N)? "
                                .format(field_name, j.name,
                                        convert_to_unicode(original_value),
                                        convert_to_unicode(new_value)))
                            if confirm_update:
                                setattr(j, field_name, new_value)
                                j.save()
                                print("  ..updated.\n\n")
                            else:
                                print("  ..not updated.\n\n")

                check_field("author", j.author, attrs['author'])
                check_field("description", j.description, attrs['description'])
                check_field("settings",
                            j.settings,
                            schema,
                            compare_fn=lambda x, y: deep_eq(x, y))
                check_field("enabled", j.enabled, attrs['enabled'])
                check_field("category", j.category, attrs['category'])
                check_field("interactive", j.interactive, attrs['interactive'])
                check_field("job_queue", j.job_queue,
                            schema.get('job_queue', 'celery'))

                # Input Port Types
                def check_port_types(which):
                    "which == 'in' or 'out'"
                    if which == 'in':
                        attrs_pts = list(
                            copy.deepcopy(attrs['input_port_types']))
                        db_pts = list(j.input_port_types.all())
                        msg = 'Input'
                    elif which == 'out':
                        attrs_pts = list(
                            copy.deepcopy(attrs['output_port_types']))
                        db_pts = list(j.output_port_types.all())
                        msg = 'Output'

                    for pt in db_pts:
                        pt_name = pt.name

                        idx = next((i for (i, this_pt) in enumerate(attrs_pts)
                                    if this_pt['name'] == pt_name), None)
                        if idx is not None:  # pt exists in database and in code. Check values
                            attrs_pt = attrs_pts[idx]

                            # Compare values
                            if attrs_pt['minimum'] != pt.minimum:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update."
                                        .format('minimum', pt_name, j.name,
                                                pt.minimum,
                                                attrs_pt['minimum'], msg))
                                else:
                                    confirm_update = confirm(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? "
                                        .format('minimum', pt_name, j.name,
                                                pt.minimum,
                                                attrs_pt['minimum'], msg))
                                    if confirm_update:
                                        pt.minimum = attrs_pt['minimum']
                                        pt.save()
                                        print("  ..updated.\n\n")
                                    else:
                                        print("  ..not updated.\n\n")

                            if attrs_pt['maximum'] != pt.maximum:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update."
                                        .format('maximum', pt_name, j.name,
                                                pt.maximum,
                                                attrs_pt['maximum'], msg))
                                else:
                                    confirm_update = confirm(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? "
                                        .format('maximum', pt_name, j.name,
                                                pt.maximum,
                                                attrs_pt['maximum'], msg))
                                    if confirm_update:
                                        pt.maximum = attrs_pt['maximum']
                                        pt.save()
                                        print("  ..updated.\n\n")
                                    else:
                                        print("  ..not updated.\n\n")

                            attrs_is_list = bool(attrs_pt.get(
                                'is_list', False))
                            if attrs_is_list != pt.is_list:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update."
                                        .format('is_list', pt_name, j.name,
                                                pt.is_list, attrs_is_list,
                                                msg))
                                else:
                                    confirm_update = confirm(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? "
                                        .format('is_list', pt_name, j.name,
                                                pt.is_list, attrs_is_list,
                                                msg))
                                    if confirm_update:
                                        pt.is_list = attrs_is_list
                                        pt.save()
                                        print("  ..updated.\n\n")
                                    else:
                                        print("  ..not updated.\n\n")

                            resource_types = RodanTaskType._resolve_resource_types(
                                attrs_pt['resource_types'])
                            rt_code = set(
                                map(lambda rt: rt.mimetype, resource_types))
                            rt_db = set(
                                map(lambda rt: rt.mimetype,
                                    pt.resource_types.all()))
                            if rt_code != rt_db:
                                if not UPDATE_JOBS:
                                    raise ImproperlyConfigured(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: {3} --> {4}. Try to run `manage.py migrate` to confirm this update."
                                        .format('resource_types', pt_name,
                                                j.name, rt_db, rt_code, msg))
                                else:
                                    confirm_update = confirm(
                                        "The field `{0}` of {5} Port Type `{1}` of Job `{2}` seems to be updated: \n{3}\n  -->\n{4}\n\nConfirm (y/N)? "
                                        .format('resource_types', pt_name,
                                                j.name, rt_db, rt_code, msg))
                                    if confirm_update:
                                        pt.resource_types.clear()
                                        pt.resource_types.add(*resource_types)
                                        print("  ..updated.\n\n")
                                    else:
                                        print("  ..not updated.\n\n")

                            del attrs_pts[idx]

                        else:  # pt exists in database but not in code. Should be deleted.
                            if not UPDATE_JOBS:
                                raise ImproperlyConfigured(
                                    "The {2} Port Type `{0}` of Job `{1}` seems to be deleted. Try to run `manage.py migrate` to confirm this deletion."
                                    .format(pt_name, j.name, msg))
                            else:
                                confirm_delete = confirm(
                                    "The {2} Port Type `{0}` of Job `{1}` seems to be deleted. Confirm (y/N)? "
                                    .format(pt_name, j.name, msg))
                                if confirm_delete:
                                    try:
                                        pt.delete()
                                        print("  ..deleted.\n\n")
                                    except Exception as e:
                                        print(
                                            "  ..not deleted because of an exception: {0}. Please fix it manually.\n\n"
                                            .format(str(e)))
                                else:
                                    print("  ..not deleted.\n\n")

                    if attrs_pts:  # ipt exists in code but not in database. Should be added to the database.
                        for pt in attrs_pts:
                            if not UPDATE_JOBS:
                                raise ImproperlyConfigured(
                                    "The {2} Port Type `{0}` of Job `{1}` seems to be newly added. Try to run `manage.py migrate` to confirm this update."
                                    .format(pt['name'], j.name, msg))
                            else:
                                confirm_update = confirm(
                                    "The {2} Port Type `{0}` of Job `{1}` seems to be newly added. Confirm (y/N)? "
                                    .format(pt['name'], j.name, msg))
                                if confirm_update:
                                    if which == 'in':
                                        Model = InputPortType
                                    elif which == 'out':
                                        Model = OutputPortType
                                    i = Model(job=j,
                                              name=pt['name'],
                                              minimum=pt['minimum'],
                                              maximum=pt['maximum'],
                                              is_list=bool(
                                                  pt.get('is_list', False)))
                                    i.save()
                                    resource_types = RodanTaskType._resolve_resource_types(
                                        pt['resource_types'])
                                    if len(resource_types) == 0:
                                        raise ValueError(
                                            'No available resource types found for this {1}PortType: {0}'
                                            .format(pt['resource_types'], msg))
                                    i.resource_types.add(*resource_types)
                                    print("  ..updated.\n\n")
                                else:
                                    print("  ..not updated.\n\n")

                check_port_types('in')
                check_port_types('out')

            # Process done
            from rodan.jobs.load import job_list
            if attrs['name'] in job_list:
                job_list.remove(attrs['name'])