rules icon indicating copy to clipboard operation
rules copied to clipboard

[python] checking field existence results infinite loop

Open GergelyMolnar opened this issue 6 years ago • 4 comments

Hi again,

I've been trying to test against non-existent fields, but it seems there's a bug that causes an infinite loop of executing the consequent function.

Easy to reproduce:

jj = {'v': None}

with ruleset('does_exist'):
    @when_all(-m.w)
    def is_exist(c):
        print(f'{c.m.w} is non-existent')

post('does_exist', jj)

Will print endlessly: "None is non-existent".

Just for the sake of it, the f-string or the reference to a non-existent value are NOT responsible for this issue. Removed both and the script will end up in an infinite loop again, printing w is non-existent endlessly.

jj = {'v': None}

with ruleset('does_exist'):
    @when_all(-m.w)
    def is_exist(c):
        print('w is non-existent')

post('does_exist', jj)

I hope this is helpful.

GergelyMolnar avatar Feb 01 '20 19:02 GergelyMolnar

Hi, thanks for asking the question. The problem is the context is asserted every time an action is executed. Because the context doesn't have a 'w' property it keeps satisfying the rule. This sample works as expected:

from durable.lang import *

jj = {'v': None}

with ruleset('does_exist'):
    @when_all(-m.w)
    def is_exist(c):
        print(f'{c.m.w} is non-existent')
        
update_state('does_exist', { 'w': 1 })
post('does_exist', jj)

jruizgit avatar Feb 03 '20 06:02 jruizgit

Hi, Thanks, the update state() is new to me so as durable rules.

My use-case is fetching a number of SalesForce objects (btw SF returns non-sparse JSON objects) and parse them (cross-relating object fields) into a very complex and different JSON object. My hope was to able to use durable_rules as the heart of my application to build complex and well-organised rule-base. All rulesets stored in a separate Python module, and I call post() in a very concise, looped manner in the body of my application. Each rule returns a fix-structured dictionary, eg. for a missing value.

with ruleset('is_color'):
    @when_all(m.source_object.Obj_color != None)
    def is_color(c):
        value = str(c.m.source_object.Obj_color)
        c.s.wrt = {'path': 'target_obj.sub_obj.target_field',
                   'value': value}

    @when_all(m.source_object.Obj_color == None)
    def is_color(c):
        value = str(c.m.source_object.Obj_color)
        c.s.wrt = {'path': 'target_obj.sub_obj.target_field',
                   'value': None}
        c.s.msg = {
            'type': 'warning',
            'name': 'Obj_color',
            'msg': 'No color data found.',
            'field': 'source_object.Obj_color',
            'value': value
        }

I'm trying to figure out a way to implement your example. But I feel like updating state, aka injecting a value - when the aim is to find out its existence - effectively hiding the fact of missing key/value and potentially lead to false evaluation by overwriting an existing value. Please correct me if my assumption was wrong.

Though, checking for missing key/value doesn't directly affect my actual project I feel this is certainly a misbehaviour of durable_rules library - rendering the - operator to be a "feature" that will 100% break the code.

Thanks again, G

GergelyMolnar avatar Feb 03 '20 09:02 GergelyMolnar

You are right, context changes should not affect the fact/event rules unless explicitly stated (using s.field in the expressions). Let me think about this, the fix might be a little involved.

jruizgit avatar Feb 05 '20 06:02 jruizgit

Hi Jesus, Hope you are doing well. Do you have any news/update on this issue?

GergelyMolnar avatar Mar 19 '20 07:03 GergelyMolnar