How to get all 'nonlocals' variables?

Background: I came across PEP 736 (which I haven’t finished reading it yet) and I was messing around trying to “reproduce” the effect.

I have defined a function lazyArg() as follows:

def lazyArg(func):
	# Assuming Ellipsis will never be an argument of `func`
	def new_func(*args, **kwargs):
		for k, v in kwargs.items():
			if isinstance(v, type(Ellipsis)):
				kwargs[k] = globals()[k]
		func(*args, **kwargs)
	return new_func

such that I can do things like this:

@lazyArg
def say(name, msg, target=None):
	if target is None:
		print(f"{name} says: {msg}")
	else:
		print(f"{name} says to {target}: {msg}")


name = 'Foo'
msg = 'Hello World!'
target = 'Bar'

say(
	name=...,
	msg=...,
	target=...,
) # Foo says to Bar: Hello World!

Of course, as I used globals() in lazyArg(), this won’t work:

def main():
	name = 'Foo'
	msg = 'Hello World!'
	target = 'Bar'
	
	say(
		name=...,
		msg=...,
		target=...,
	)


main()  # KeyError

My question is, how to obtain all the ‘outter scope’ variable, just like doing the ‘nonlocals’ version of globals()?

You mean you want get the local variables of the location from where the call happened?

Use sys._getframe(1).f_locals, or something like it.

1 Like

I am not sure what you are asking, but function objects have an attribute (use dir(f)) for nonlocals.

I was trying to do something like this:

def main():
    x = 1
    f(x=Ellipsis)

To let f() replace the Ellipsis argument with 1, f() would need to access all variables under main(). globals() can’t do that, neither can locals(), so I am looking for some way to do that, something like a nonlocals()

Oh, wow, that’s right, this does exactly what OP wants.

def lazyArg(func):
	import sys
	# Assuming Ellipsis will never be an argument of `func`
	def new_func(*args, **kwargs):
		for k, v in kwargs.items():
			if isinstance(v, type(Ellipsis)):
				kwargs[k] = sys._getframe(1).f_locals[k]  # <<--
		func(*args, **kwargs)
	return new_func
1 Like

Just be aware, sys._getframe has an underscore for a reason. It is internal details, and can change at any time; it’s only in CPython, not part of other Python implementations (or if it is, it can be completely different); and anything you do that depends on it could break at any time.

But, with all those caveats said: Go have fun with it!

2 Likes