manifest: model west-commands as per-entry records#966
Conversation
661bd0b to
02fb69f
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #966 +/- ##
==========================================
+ Coverage 86.40% 86.51% +0.10%
==========================================
Files 11 11
Lines 3532 3559 +27
==========================================
+ Hits 3052 3079 +27
Misses 480 480
|
ea2092e to
ec491ab
Compare
ec491ab to
c35e889
Compare
|
I think the tests in #962 should got first because they record/"document" the existing behavior, as it was just changed. Then, this PR can adjust the tests if needed which will again document any behavior change (or none if none) |
Bump the schema version and relax the west-commands type from str to any. The parsing of the west-commands is offloaded to the manifest parser to allow for maps with a file/base_dit keyset. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
c35e889 to
da49e22
Compare
Replace the WestCommandsType alias and the parallel
_west_commands_manifest_dirs dict with a frozen WestCommandsEntry
dataclass carrying (path, base_dir). Project.west_commands_entries is
the new canonical attribute; the old west_commands becomes a deprecated
read-only property. Schema accepts str / list[str] / list[{file,
base-dir}] so resolved manifests roundtrip through as_yaml() without
losing the base_dir needed to resolve `file:` paths inside specs
imported from a project subdirectory.
Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
da49e22 to
8660875
Compare
There was a problem hiding this comment.
I'm afraid "entry" sounds pretty vague.
We have a massive and self-inflicted name collision between:
- The
west-commands:field in the west.yml manifest - The "west-commands.yml" file(s) it points at
- The OTHER
west-commands:header line in those files! - The "west commands" descriptors in those files. "Descriptor" is an exception: theonly specific name around here? I wish we had more of these.
- The "west commands" .py files that actually implement the commands
- The mypy
WestCommandsType - The WestCommand python class
- A new "entry" class added by this PR
- project.west_commands
- others?
EVERYTHING is a "West command"! Unless it's an "extension", which does not help at all.
This mass collision starts in the user interface and documentation at https://docs.zephyrproject.org/latest/develop/west/extensions.html and unsurprisingly gets worse in the code. That relevant code has already given me many headaches and this one PR will obviously increase the count. I would like those collisions to ideally stop or at least start be mitigated.
Naming is hard, but this PR is definitely the right time to formalize some better terminology. I'm not implying a mass search/replace, at least not yet. But at least finalizing some "correct" names to accelerate communication, bug tracking and reviews.
To be fair, some of the above are unique enough not to need a specific name. For instance, there is only one Python file (per command), so a "west command python file" is specific enough. Not concise but good enough. On the other hand, the stuff in YAML files definitely needs more specific names. Even more so considering it's the user interface, even more so considering we have multiple YAML files.
Let me think about some terminology, just a heads-up.
marc-hb
left a comment
There was a problem hiding this comment.
I'm confused for now, sorry. I don't understand why this requires a schema change. Maybe I need more time to better understand the problem statement.
I think I understand the value of having some base_dir in the internal implementation but I don't understand yet why this has to be exposed in the (changed) user interface. Why can't base_dir simply be added as a prefix in the output of west manifest --resolve?
Adding that base_dir does not seem to add any value to the user interface, it looks like a pure hack to make the output of west manifest --resolve work again.
Also, do we really need to add yet another level of lists? We already have two. That bit really looks like a new feature, not a bug fix.
Let me think about some terminology, just a heads-up.
-
west-commands.yml is a DESCRIPTOR LIST (e.g:
cmd_descs_listor longer). LIST sounds vague but I think we have no other, top-level list in this context and we can always extend it to "(command) descriptor list" in any context where it could be ambiguous. Hopefully, making a difference between the list, its file and the pointer to it from the manifest is obvious enough from the context when needed and they don't need different names. -
Each DESCRIPTOR has a single pointer to one commands PYTHON FILE.
-
Each PYTHON FILE has a list of command CLASSES. No collision there.
|
|
||
| ret: list[WestCommandsEntry] = [] | ||
| for item in raw: | ||
| if isinstance(item, WestCommandsEntry): |
There was a problem hiding this comment.
In general, prefer the newer case and pattern matching. But don't go and change this before we close the high-level discussions.
|
|
||
| @dataclass(frozen=True) | ||
| class WestCommandsEntry: | ||
| '''A single west-commands spec declared in a project. |
There was a problem hiding this comment.
The term "project" is also problematic :-(
For west, a project (not my choice...) is a git repo:
https://docs.zephyrproject.org/latest/develop/west/manifest.html#projects
But in most IDEs and even Zephyr sometimes, a "project" can be the workspace:
- https://code.visualstudio.com/docs/editing/userinterface#_basic-layout
- https://docs.zephyrproject.org/latest/develop/getting_started/index.html#get-zephyr-and-install-python-dependencies
In the following instructions the name "zephyrproject" is used for the workspace,
I think in the case the fix is easy: use either "workspace topdir" or "git top dir".
There was a problem hiding this comment.
I think for West, it's clear what a project is? Maybe should just name it like that.
| '''A single west-commands spec declared in a project. | |
| '''A single west-commands spec declared in a West project. |
| wc1: list[WestCommandsEntry], wc2: list[WestCommandsEntry] | ||
| ) -> list[WestCommandsEntry]: | ||
| # Merge two west_commands lists, dropping any entry from wc2 | ||
| # whose path is already in wc1 (first wins). |
There was a problem hiding this comment.
Such an override should print at least a DEBUG.
There was a problem hiding this comment.
Sure, the existing code also did not print though.
| ``base_dir`` needed to resolve Python files inside the spec when | ||
| the entry was promoted from a subdirectory submanifest. | ||
| ''' | ||
| warnings.warn( |
There was a problem hiding this comment.
Why not just fail? This wasn't part of any documented API AFAICT
There was a problem hiding this comment.
I was under the impression that all fields that do not start with an underscore are considered public? Anyway we don't need to break users of this.
pdgendt
left a comment
There was a problem hiding this comment.
I'm confused for now, sorry. I don't understand why this requires a schema change. Maybe I need more time to better understand the problem statement.
I think I understand the value of having some
base_dirin the internal implementation but I don't understand yet why this has to be exposed in the (changed) user interface. Why can'tbase_dirsimply be added as a prefix in the output ofwest manifest --resolve?
The problem is that west manifest --resolve outputs a single yaml file, aggregated from the imported ones. The west-commands.yml descriptor files stay in their projects untouched.
We can't simply add the prefix, that conflates two genuinely different directories:
path= location of thewest-commands.ymlspec file, relative to project root (e.g.mf_subdir/scripts/winsubA/west-commands.yml)base_dir= the root that thefile:entries inside that spec resolve against (e.g.mf_subdir)
The Python file resolves as base_dir / desc['file'], i.e. relative to mf_subdir, not relative to the spec file's own directory mf_subdir/scripts/winsubA/. So you can't fold base_dir into the path prefix; they point at different places.
Adding that
base_dirdoes not seem to add any value to the user interface, it looks like a pure hack to make the output ofwest manifest --resolvework again.
True, base-dir is meaningless for a hand-authored manifest, but it is needed for a resolved manifest to work. I wouldn't call it a hack, but it is an internal detail surfacing in the user-facing schema.
Also, do we really need to add yet another level of lists? We already have two.
There's no level added? Currently we have the following "levels":
- the manifest
west-commands:field -> pointer(s) to spec file(s). Wasstr | list[str]. - inside each
west-commands.yml, the top-levelwest-commands:key -> list of descriptors. - inside each descriptor,
commands:-> list of command classes.
This PR changes level 1 only, and it widens the element type of that existing list from str to str | {file, base-dir}.
That bit really looks like a new feature, not a bug fix.
We're not adding new features? Users can't express anything new. It is genuinely a bug fix IMO.
Let me think about some terminology, just a heads-up.
- west-commands.yml is a DESCRIPTOR LIST (e.g:
cmd_descs_listor longer). LIST sounds vague but I think we have no other, top-level list in this context and we can always extend it to "(command) descriptor list" in any context where it could be ambiguous. Hopefully, making a difference between the list, its file and the pointer to it from the manifest is obvious enough from the context when needed and they don't need different names.- Each DESCRIPTOR has a single pointer to one commands PYTHON FILE.
- Each PYTHON FILE has a list of command CLASSES. No collision there.
I like the idea of using descriptor.
|
|
||
| @dataclass(frozen=True) | ||
| class WestCommandsEntry: | ||
| '''A single west-commands spec declared in a project. |
There was a problem hiding this comment.
I think for West, it's clear what a project is? Maybe should just name it like that.
| '''A single west-commands spec declared in a project. | |
| '''A single west-commands spec declared in a West project. |
|
|
||
| ret: list[WestCommandsEntry] = [] | ||
| for item in raw: | ||
| if isinstance(item, WestCommandsEntry): |
| wc1: list[WestCommandsEntry], wc2: list[WestCommandsEntry] | ||
| ) -> list[WestCommandsEntry]: | ||
| # Merge two west_commands lists, dropping any entry from wc2 | ||
| # whose path is already in wc1 (first wins). |
There was a problem hiding this comment.
Sure, the existing code also did not print though.
| ``base_dir`` needed to resolve Python files inside the spec when | ||
| the entry was promoted from a subdirectory submanifest. | ||
| ''' | ||
| warnings.warn( |
There was a problem hiding this comment.
I was under the impression that all fields that do not start with an underscore are considered public? Anyway we don't need to break users of this.
Replace the
WestCommandsTypealias and the parallel_west_commands_manifest_dirsdict with a frozenWestCommandsEntrydataclass carrying (path,base_dir).Project.west_commands_entriesis the new canonical attribute; the oldwest_commandsbecomes a deprecated read-only property. Schema acceptsstr/list[str]/list[{file, base-dir}]so resolved manifests roundtrip throughas_yaml()without losing thebase_dirneeded to resolvefile:paths inside specs imported from a project subdirectory.Fixes #959
Supersedes #965