Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test-driver: Run ptpython REPL at global scope #111486

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

andersk
Copy link
Contributor

@andersk andersk commented Jan 31, 2021

Motivation for this change

This is a workaround for prompt-toolkit/ptpython#279, which was making it impossible to run the installer test interactively:

>>> start_all()
…
>>> test_script()
…
machine # [  382.137683] reboot: Power down
(7.06 seconds)
(8.23 seconds)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/nix/store/lbn2xnibdcvpgkpbrkdh2vlf1ap6930v-nixos-test-driver/bin/.nixos-test-driver-wrapped", line 888, in test_script
    exec(os.environ["testScript"])
  File "<string>", line 69, in <module>
  File "<string>", line 18, in create_machine_named
NameError: name 'default_flags' is not defined

name 'default_flags' is not defined

There’s no reason to expose the local variables of the run_tests function, anyway.

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

This is a workaround for
prompt-toolkit/ptpython#279, which was making
it impossible to run the installer test interactively:

>>> start_all()
…
>>> test_script()
…
machine # [  382.137683] reboot: Power down
(7.06 seconds)
(8.23 seconds)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/nix/store/lbn2xnibdcvpgkpbrkdh2vlf1ap6930v-nixos-test-driver/bin/.nixos-test-driver-wrapped", line 888, in test_script
    exec(os.environ["testScript"])
  File "<string>", line 69, in <module>
  File "<string>", line 18, in create_machine_named
NameError: name 'default_flags' is not defined

name 'default_flags' is not defined

There’s no reason to expose the local variables of the run_tests
function, anyway.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
@SuperSandro2000
Copy link
Member

ping @tfc @flokli

@@ -900,7 +900,7 @@ def run_tests() -> None:
traceback.print_exc()
sys.exit(1)
else:
ptpython.repl.embed(locals(), globals())
ptpython.repl.embed(globals())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ptpython.repl.embed(globals())
ptpython.repl.embed(globals=globals())

if I read this correctly, we effectively previously ran this with globals=locals(), locals=globals()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do. But we have no reason to pass locals() anyway, and we don’t pass locals() in the normal non-interactive mode, nor do we specify globals= as a keyword argument to exec().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just meant explicitly setting globals by kwarg makes this a bit less error-prone.

Copy link
Contributor Author

@andersk andersk Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, but when passing one argument, there’s no error to be prone to; the same argument is used as both globals and locals.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, that's not really obvious. Then maybe a comment explaining what we do and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you expand a bit more of the diff context, you’ll see we already do exec(tests, globals()) 6 lines above this; exec has clearer documentation but works exactly the same way. I don’t see any other way a reader might think it would work? What comment do you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just think we already mistakenly confused locals and globals (by relying on the number of arguments), and being a bit more descriptive in what we actually pass here as what wouldn't hurt. How things are handled if only one argument is passed isn't described anywhere in the function docstring either. Just using a subtle globals= makes it clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember, the main bug wasn’t that the arguments were provided in the wrong order—it’s that two arguments were provided at all. The bug would still have been there, just with different variables, if the two arguments had been provided in the right order:

>>> import ptpython
>>> a = 12
>>> def f(b):
...     ptpython.repl.embed(globals(), locals())
... 
>>> f(34)
>>> a
12
>>> (lambda: a)()
12
>>> b
34
>>> (lambda: b)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
NameError: name 'b' is not defined

name 'b' is not defined

It might have been less visible since we don’t actually need any of the locals from inside the function that called embed. But that’s just another way of saying that the correct solution is to avoid providing the caller’s locals() at all.

Now, if, in some hypothetical alternate reality, the locals and globals parameters of embed were swapped in ptpython, then in order for it to continue supporting one-argument calls, the swap would necessarily need to preserve the property that the second parameter defaults to the first parameter, not the property that the locals parameter defaults to the globals parameter.

-def embed(globals=None, locals=None, …):
+def embed(locals=None, globals=None, …):
     ...
-    locals = locals or globals
+    globals = globals or locals

So embed(globals()) would still do the right thing, while embed(globals=globals()) would not. Therefore, even if we’re trying to be robust against counterfactual multiverse translocation, we really do want to provide the first argument here, not the globals argument.

This may explain why exec(code, globals()) is idiomatic (see 6 lines above, and 2to3’s translation of exec code in globals()) and exec(code, globals=globals()) is not.

I understand the desire to find a subtle way to prevent this bug from happening in the future, but the best way to do that is to provide a correct call that can be referenced rather than an incorrect one. I’ll add a comment linking back here if it will help get this PR merged.

Requested in review.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
@andersk andersk requested a review from flokli June 2, 2021 02:55
@stale
Copy link

stale bot commented Jan 9, 2022

I marked this as stale due to inactivity. → More info

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jan 9, 2022
@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants