1

I have an issue with the following code please.

Forgive me if not, I am a python newbie.

The following code is in my dodo.py:

def closed_over(par):
    print("\n??? " + par)
    if par == "bar":
        return False
    else:
        return True

def task_bug():
    for par in ("foo", "bar"):
        print("par: " + par)
        # closure!
        exist_fn = lambda: closed_over(par)
        print(exist_fn)

        yield {
            "name": par,
            "actions": [["echo", "action:", par]],
            "verbosity": 2,
            "uptodate": [exist_fn]
        }

When I run doit bug:foo I expect to NOT execute it as (closed_over returns True), but:

par: foo
<function task_bug.<locals>.<lambda> at 0x7f8926f9a560>
par: bar
<function task_bug.<locals>.<lambda> at 0x7f8926f9a5f0>

??? bar      <- par should be foo
.  bug:foo
action: foo  <- echo was called

As you can see above the two closures outside the yield are different function objects but for some reason uptodate is always calling the same.

Andrea Richiardi
  • 673
  • 6
  • 20

2 Answers2

2

Your problem is regarding python closure scoping (it scope variables not values)... Nothing to do with doit. Probably have many questions about this here on Stack Overflow :)

Python lambda closure scoping

As you can see above the two closures outside the yield are different function objects but for some reason uptodate is always calling the same.

No, it is not calling same closure. The problem is the same variable as parameter.

schettino72
  • 2,761
  • 26
  • 27
0

As it turns out, I assumed that python had dynamic scope, while it actually does lexical scoping.

According to a couple of answers in the wild, this is what actually works:

from functools import partial

def closed_over(par):
    print("\n??? " + par)
    if par == "bar":
        return False
    else:
        return True

def task_bug():
    """Test a doit bug."""

    for par in ("foo", "bar"):
        print("par: " + par)
        exist_fn = partial(closed_over, par)
        print(exist_fn)

        yield {
            "name": par,
            "actions": [["echo", "action:", par]],
            "verbosity": 2,
            "uptodate": [exist_fn]
        }
$ doit bug
par: foo
functools.partial(<function closed_over at 0x7f6ab3eb6cb0>, 'foo')
par: bar
functools.partial(<function closed_over at 0x7f6ab3eb6cb0>, 'bar')

??? foo
-- bug:foo

??? bar
.  bug:bar
action: bar
Andrea Richiardi
  • 673
  • 6
  • 20