Can macros access global variables from the root o...
# general
c
Can macros access global variables from the root of the calling BUILD file? With a macro something like
Copy code
def my_special_wrapper(**kwargs):
    real_target(something=MY_SPECIAL_GLOBAL_VARIABLE, **kwargs)
and a BUILD file with something like
Copy code
MY_SPECIAL_GLOBAL_VARIABLE = "something"

my_special_wrapper()
I've tried this and it raises a
NameError
in the macro call. The error message it logs (see stack trace in 🧵) implies that
MY_SPECIAL_GLOBAL_VARIABLE
maybe should be available. My use case is that I want to wrap a number of target types to set python interpreter constraints across them consistently, and it would be nice if each of the wrapper macros could suck the value out of a single global variable in each BUILD file, rather than having to remember to pass it in to each macro call.
✅ 1
Specifically, it says
Copy code
All registered symbols: ['MY_SPECIAL_GLOBAL_VARIABLE', ...
which implies that the value is there in the dict that's passed into
exec
e
Any build file can contain a function. Just write a local function that calls the macro passing the ICs as a single value and convert the macro to taking ICs as a parameter instead of the behind the back global variable.
a
The problem is that there's a lot of boiler plate there. python_sources need to have all the supported python versions, python_tests need to have a parametrize version of that, and pex_binary needs to have just one version
So we'd need 3 functions, per BUILD file (or, well, less where we don't have binaries/tests)
e
Macro:
Copy code
def my_macro(ic: str, ...):
   pex_binary(interpreter_constraints=[ic], ...)
BUILD file call site:
Copy code
def curry_ic(...):
  my_macro(ic=FIXED, ...)

curry_ic(name="bob", ...)
curry_ic(name="Jane", ...)
@ancient-france-42909 same difference as global right?
a
True, but, but it's more boilerplate, and less straight forward than just defining a variable. Definitely cleaner in terms of design, but yeah, I was hoping this would work, without doing ugly things like going up the stack 🙂
e
Do you know about
__defaults__(...)
?
Again more characters typed than a global, but I don't know what to say about that line of complaints.
a
Oh, that's actually probably okay, and maybe an even better solution that the variables.
e
You could also have the macro return a tuple of curried functions:
Copy code
# We don't use the y target in this BUILD
x, _, z = my_macro("==3.11.*")
x(...)
x(name="bob", ...)
z(name="Jane", ...)
It's all just Python sans imports
a
The
__defaults__
one seems the simplest so far. We won't even need to change the targets (int his case either, I guess, since you can override the builtin ones, I assume?)
e
Correct.
a
Alex is working on it right now 🙂 Actually, there's an even bigger advantage to
__defaults__
, we have a weird structure in places, with the sources and tests in different folders, we only need to do it for one BUILD file.
c
happy if defaults turns out to work better, but just wanted to ask any way: Any reason you can’t have the global data in the macro file rather than in the root build file? then it would be available everywhere.
a
It's not in the root BUILD file, it's gonna be per library/app. Not all of our apps have the same constraints (and that won't change in the near future)
c
Ah, ok. interesting use case.
This looks like a bug to me..
there are some edge cases, but it ought to work in some form
c
We're migrating from 3.7 up to 3.10 across ~300
python_sources
targets and ~200
pex_binary
targets across ~300 BUILD files, adding support for 3.10 from the least-dependent targets up to the most-dependent. This is basically done now, but we'd also like to start dropping 3.7 support, and maybe think about testing out 3.11, and have realised that we have a LOT of copy/pasted boilerplate - which sometimes people get wrong (like forgetting to use
parametrize
on
python_tests
targets).
c
yea, for that it sounds like
__defaults__
could be a good option as long as the field values are not too dynamic in nature.
c
This seems to do what I need it to, and is much more succinct than having one wrapper macro per target.
thanks
c
cool
maybe controversial so may not land as-is or at all (besides the error message improvemnts): https://github.com/pantsbuild/pants/pull/19284
a
I literally was just talking to Alex, I like this defaults solution more than the original one, but I'm sure there could be cases where that's useful too. It sounds a bit counter intuitive, since obviously, the function should have access to the globals from where it's defined, but might be useful 🙂 thanks
c
yes, as noted in the PR description, I’m not sure this is a good idea (for this particular use case), but as I found it wrong that it didn’t work… 🤷
at least there’s some improvements in the error reporting worth keeping none the less..