-
-
Notifications
You must be signed in to change notification settings - Fork 644
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
Modernizing objects
and context_aware_object_factories
(including so it shows up in docsite)
#14832
Comments
Do we still need the CAOF ( I love the |
I think so because of -- I'm realizing that I think we really want to integrate the light-weight macros from https://www.pantsbuild.org/docs/macros into CONSTANT = 1
def python2_sources(**kwargs):
python_sources(**kwargs) Also, imports are banned in those macros. So we can't define some wrapping class like I was wondering about a magic constant you declare like: _help_messages = {
CONSTANT: "blah blah",
python2_sources: "blah blah",
} We would need to special case this so that it does not cause the symbol to be registered as accessible in all BUILD files, and particularly so that when you have multiple macros we don't complain that you're defining the same symbol twice. Tricky. And maybe too boiler-platey / obscure? We could use docstring, but that won't work for constants :/ Maybe that's acceptable? Constants can't be documented and we render |
I haven't looked how these macros are loaded, but can't we populate the env when loading these macros, with say a |
We could do that I think! I'm not sure how they work with constants? Maybe they set a value like @macro("Hello world!")
CONSTANT = 1
@macro("Help")
def python2_sources():
... I like that a lot because it's consistent and we don't coerce docstring, which people might want to use for other reasons. (It was a big improvement when we stopped using docstring for targets & subsystems). |
Perhaps Perhaps @doc("help text describing the constant")
CONSTANT = "value"
# but then again, that could work for methods as well...
@doc("Help text")
def python2_sources():
... The decorator could be smart enough here to see what kind of value is being decorated and treat it accordingly. |
I like that a lot, thank you! One weird thing is that technically |
Good point. Trying out how @register(doc="help text describing the constant")
CONSTANT = "value"
# but then again, that could work for methods as well...
@register(doc="Help text")
def python2_sources():
... It has the added benefit of being a generic placeholder, where any future features may be added to it. Also, I don't think we'll want to have such a generic target name for anything else, ever.. ? (thinking that it would be scoped to what it is registering, in such cases.. For BUILD file macros, the generic |
And, come to think of it. That decorator could be a window into the macro world for plugin authors as well! Potential idea: @register(doc="Example using env var support", expand_vars=["FOO", "BAR"])
CONSTANT = "Some value with $FOO env vars and ${BAR} values but leaves $THIS alone." Where a plugin may have registered the Possibly a can of worms here, but I think it could be really cool stuff. :) |
@stuhood why is this in the 2.13 milestone? |
objects
and context_aware_object_factories
objects
and context_aware_object_factories
(including so it shows up in docsite)
@Eric-Arellano suggested I add some notes from discussion in slack: https://pantsbuild.slack.com/archives/C046T6T9U/p1655837762591389 One problem I have with macros files is that linting with (at least) flake8 is rather annoying because all of the targets and build symbols cause (For now, I'm just ignoring that flake8 error in my macros file, but a nicer solution would be great). In that discussion @thejcannon and @stuhood talked a bit about the possibility of using imports to make those symbols discoverable to tools like flake8, but that adds even more boilerplate to BUILD files. Highlighting what @stuhood said:
So, here are some ideas about how macros could look in BUILD files: class my_macro(__macro__):
def __call__(**kwargs):
... I like a function syntax a bit better than class though. def __macros__():
def my_macro(**kwargs):
... And, playing off the annotation ideas in here, let's use dunder names for the global non-target names introduced by pants (like @__macro__(help="some help")
def my_macro(**kwargs):
... |
I think it would make a lot of sense, defining macros in BUILD files, and that they are accessible for the current subtree. I like the decorator syntax, as it doesn't incur any restriction on keeping all your macros within a single 100% agree with going with dunder names, good idea! |
Also, I don't think that you can directly apply decorators to constants. This is invalid syntax: @register(doc="help text describing the constant")
CONSTANT = "value" But, since decorators are just functions, you can call the decorator after you define the constant: CONSTANT = "value"
__register__(CONSTANT, help="help text describing the constant") |
Right, my mistake :P Perhaps more concisely alternative then (and I think we would need to capture the return value of CONSTANT = __register__(doc="help text describing the constant")("value") |
Another option is to "abuse" annotations in some way: CONSTANT: "help text describing the constant" = "value" |
That would mess up MyPy users |
Yeah big -1 on abuse of annotations that way. But also very clever, I like the thought-path there |
Well, the fact you can't use annotations at all in the macros file (like this) makes the (the "in some way" was kind of leaving the door open to adjusting it into something mypy-acceptable but didn't want to invest a lot of effort and time into it right now, but also not forgetting my "thought-path", thanks Josh) |
-1 on using annotations here. But that got me thinking about the range of syntax options available in python. We could make a with __macros__:
CONSTANT = "value"
def my_macro(**kwargs):
... That does not, however address documenting constants. Though, that is not unique to pants, it is a general shortcoming of python. So, we would still need decorator and/or something else to document macro defs and constants. |
Hmm... What if we lean into the context manager even further: CONSTANT = "value"
with __macros__ as m:
m.register(CONSTANT, help="bla bla")
@m.register(help="foo bar")
def my_macro(**kwargs):
... |
So, all build symbols can be found via the Here is a brief summary of the remaining ideas I see above (did I miss any?):
|
Just to close the loop here.. less abuse would be to use CONSTANT: Annotated[str, "help text describing the constant"] = "value" |
Went ahead and made a PR trying out the |
Yeah I support |
We've leaned into
objects
by introducingparametrize
. Compare this to #12410, for example, trying to get rid of thesetup_py
object. They're here to stay.Issues
The major issue is that
objects
and CAOFs aren't integrated into./pants help
and our generated docs.(I also think it's a downside that the symbol pollutes the entire BUILD namespace, whereas a field is scoped to its owning target and allows reuse. But sometimes this is justified.)
--
Note that generally both
objects
andcontext_aware_object_factories
don't stop you from doing unsafe things likeopen()
, just like the Rules API can't stop you.Proposal:
BuildSymbol
andBuildSymbolFactory
It's very convenient that
objects
does not impose structure on you. This lets you do things like:BuildFileAliases(objects={"CONSTANT": "hello"}}
.__init__
to store data for lazy consumption by rules.It would be a regression if we imposed structure like requiring you to implement a class with
alias: ClassVar[str]
.Meanwhile, it's boiler-platey that every context_aware_object_factory has the same setup of an
__init__
that takesParseContext
, then having to define__call__
. CAOFs do not share the same flexibility asobjects
, nor can they.So I propose wrapping
objects
viaBuildSymbol
, but then defining a proper superclass for CAOFs withBuildSymbolFactory
:We'd have a new
register.py
plugin hook, replacingbuild_file_aliases
:Then our
help
code can conveniently iterate over everyBuildSymbol
&BuildSymbolFactory
, like how it does withTarget
,Field
, andSubsystem
.The text was updated successfully, but these errors were encountered: