-
-
Notifications
You must be signed in to change notification settings - Fork 636
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
"Visibility" support (akin to Bazel's) #13393
Comments
+1 for this. |
This would be a great value for our codebase well! It's something that's easy to introduce by mistake and wastes a lot of time when not handled correctly |
One thing that occurs to me would be to use something like a I think it would probably work fairly well to use a similar model as This would not be as fine grained as something in the build files, but could work pretty well without needing to define much new syntax |
This also relates to some of the proposals in #13621. |
I think this is a really important feature not only for governance but for stability. An example from today - we had an issue where a developer introduced a python dependency on another microservice in a code path that wasn't covered in CI. |
So the idea would be that ~every target in every service would declare that it was only visible to that service? Finding a way to do this without a tremendous amount of boilerplate will be a challenge. Maybe related to #13767. |
Out of curiosity, why didn't dep inference pull in the dependency at the code level? |
We can't use pants to package and even if we did the service doesn't have the permissions that the other service has. |
So how would this feature have helped? If Pants isn't doing the packaging then it can't enforce deps at packaging time. I guess you'd want Pants to check dependency hygiene when you run "lint" or something like that? |
Probably when tests were run, right? The test process would die on an import error, conceivably. |
Yeah that makes sense |
On second thought, it might be good to not have to get that far before seeing the error. There might be cases where the code isn't tested, but conceivably some pants operation would run on it where the dep resolution would error. So I presume the error should be raised in dep resolution, then it's on the user to ensure Pants tries to resolve the deps one way or another. |
Agreed. I think this would happen when calculating I doubt we want to "always no matter what" run the check. |
Another data point, dependency inference is where the "unowned dependency error" is raised.
|
Here is a tool that implements something like this for Python projects https://import-linter.readthedocs.io/en/stable/index.html. Could be useful to consider the types of import contracts it supports. |
The contracts concept is useful! |
This makes me think. How would people feel about this being a (Thinking about it more, "unowned dependency error" could've been a |
Hey folks: #15373 added a |
Meaning |
Also introduces a new `defaults` target which allows to dynamically provide "scoped" default field values. Closes pantsbuild#13393. [ci skip-rust] [ci skip-build-wheels]
What would you call a |
I think you're right that these are different things. From what I understand, most have been asking for the |
I have a list of 9 python modules (top-level modules I should say, there are actually quite a few other python modules in the monorepo) that are closely related. One of them is a client module (an api client library) that gets distributed via pypi for 3rd party use, and it can also be used by anything in the code base. But, it must be very lightweight in terms of dependencies, and it must not import any of the other modules (the server-side code). So, I need a way to make sure that that module is a self-contained package (except for a few 3rd party packages). With |
Actually, I think the Example: # src/client/lightweight/BUILD
# Explicitly exclude any dependencies from these sources..
__defaults__(all=dict(dependencies=["!src/lib::", "!src/common/stuff::", ...]))
# The normal target stuff... If it is just one target, then that dependencies ejection could simply be added directly to that target. I think this ought to work. But then again, we may need to improve any error handling here, when the inference may add a dependency that have been explicitly excluded, we should say so rather than just let things fail later. (If that's the case, I've not tried this out) |
I think different repos are going to want very different ways of expression allowed/disallowed dependencies. Some will be on the dependency side, some on the dependee side, some will allowlist, some will blocklist, etc. So I think it's best to implement this at two levels:
That way anyone with custom needs that don't align with the choices and restrictions imposed by 2) can drop down to 1), write their own in-repo rule and plug it in. |
For the base API on the pants/src/python/pants/engine/internals/graph.py Lines 1196 to 1205 in 04cc7b4
which is the enabler for the visibility implementation taking shape in #15803, which then would be bullet 2 above. For the # visibility
__defaults__(all=dict(visibility=["src/lib/a::", "src/lib/b::"]))
# vs dep excludes
__defaults__(all=dict(dependencies=["!!src/common::"])) N.b. I may have mixed up the Also, the visibility check is implemented slightly backwards, as it goes from a target, and then checks if it is allowed according to each dependency's visibility rules. |
Got it. so ValidateDependenciesRequest is pluggable? Seems very sensible to me. |
Yah, correct: pants/src/python/pants/engine/target.py Lines 2612 to 2628 in 04cc7b4
|
Oh, I got to make a shout out to
|
Absolutely epic work from @kaos here! We've finally closed one of our most requested issues!! Now just need docs and a blog post. |
Is your feature request related to a problem? Please describe.
In a monorepo you will like have N services (likely containing one-or-more underlying packages) as well as M "common" packages. Logically speaking, any piece of code should be able to dip it's hand into at most 2 top-level packages: it's own and the common code. Having code from service X depend on code from service Y would be a mistake and shouldn't be mechanically allowed.
Describe the solution you'd like
Some way of specifying visibility in a
BUILD
file (with the default as "public" to maintain backwards-compatibility).Initial implementation can be scoped to only supporting 2 values: "public" and "package".
For simplicity, if visibility was inherited from the nearest-declared-ancestor-dir that would limit the amount of visibility declarations to N
BUILD
files (one for each service, the M common packages can remain with the public default).Additionally, this could be hidden behind a plugin to make it truly opt-in.
Describe alternatives you've considered
A plugin which enforces this at
test
/lint
/check
-time instead of visibility declarations inBUILD
files.Additional context
N/A
The text was updated successfully, but these errors were encountered: