Python global variable flag interface

Summary

This proposal considers modifying the way Guild applies flag values to Python script globals. Guild’s current method is to modify the Python module AST at runtime to assign flag values to the applicable variables/state in the Python VM at runtime. This proposal considers re-writing the applicable Python module source code when it initializes the run directory. The re-written Python source code files would contain the applicable flag values.

This proposal is under development

Problem

Guild’s method of setting flag values for Python global begs two questions that are important to users:

  • How are the values applied to the source code at runtime?
  • In retrospect, what code ran for a run?

This information can be implied from the record but is not explicitly reflected anywhere.

This arrangement undermines Guild’s mandate to create a clear, interprettable record of what was run.

Proposed Approach

Rather than inject flag values at runtime, Guild will modify applicable Python source code files to reflect the current set of flag values.

Consider this simple example.

# test.py
x = 1
print(x)

For a command:

guild run test.py x=2

Guild would re-write the file test.py as follows:

# test.py
x = 2
print(x)

Or, perhaps with annotations:

# test.py
x = 2  # flag x=2
print(x)

Considerations

How does a user know if a change to source code is from a project origin or from a Guild change?

  1. Guild could use comments to denote that it made the change.
  2. Guild could write the modified source files to an empty directory that is then included in the Python path, ahead of the run directory.

Both of these approaches would solve the problem and make it clear what changes Guild is making for a run. Option 2 provides a clean separation of Guild mods to project source code. However, it makes it more difficult to diff run source code to project source code.

Option 1 adds complexity to the re-write algorithm but otherwise solves the problem.

Re-starting a run with different flags modifies source code.

This is not really a problem as the user has the option of using current project source code on a restart anyway. Guild does not strictly lock runs after they finish (regardless of exit status).

1 Like

A library for round-trip parsing of Python source: parso - A Python Parser — parso 0.8.3 documentation

I see one of a common practice for some AI coder is they would implement a config class e.g.:

class CFG():
  flag_a = 1
  flag_b = 2
  ...

And then in the main script uses these values:

from cfg import CFG 

def main():
  cfg = CFG()
  cfg.flag_a # do something with it 
  ...

if __name__ == '__main__':
  main()

This might be an answer to your consideration, where guild only modifies the cfg class file template from the original source code folder and writes the modified version to the guild directory.

Comments from @t-kalinowski offline:

It was attractive at first, but I think modifying the AST is going to be more robust longterm. The tradeoff is, at first glance, about discoverability for users. But that can be solved through other ways. On the R side, it’s solved 2 ways:

  1. We emit a message to stderr when modifying the AST, so the magic is by default visible to users inspecting a run output.
  2. We echo deparsed R expressions to stdout as we evaluate them, so the modified flags show up in stdout also by default (though I do intend to add a --no-echo option to turn this off.