fix(spark): reject bool type as CPU in _validate_cpu_value - #752
fix(spark): reject bool type as CPU in _validate_cpu_value#752Neha-Nimsatkar wants to merge 4 commits into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
🎉 Welcome to the Kubeflow SDK! 🎉 Thanks for opening your first PR! We're happy to have you as part of our community 🚀 Here's what happens next:
Join the community:
Feel free to ask questions in the comments if you need any help or clarification! |
|
/ok-to-test |
|
Hi @Neha-Nimsatkar! Thanks for raising the PR. You'd need to fix the errors in pre-commit to pass the CI. |
HarshPopat23
left a comment
There was a problem hiding this comment.
I reviewed the latest head (d596f10). Rejecting bool is a valid correctness fix because bool subclasses int in Python, so the current implementation incorrectly accepts True as one CPU core.
I still see three points that should be addressed before merging:
Blocking 1: Public API types do not support the new float input
The private helper now accepts float, but the user-facing APIs still declare resource dictionaries as dict[str, str]. This includes SparkClient.connect(), SparkClient.submit_job(), Driver.resources, Executor.resources_per_executor, and the relevant resolver/builder methods.
As a result, resources_per_executor={"cpu": 1.5} works dynamically but is rejected by static type checkers and IDEs. Could we either update the public resource-value type annotations and documentation to make numeric CPU values an official part of the API, or keep the public contract string-based and scope this PR to the boolean-validation fix?
Blocking 2: Please clarify the float semantics
1.5 is normalized with math.ceil() and becomes 2. The generated CR therefore receives two Spark cores; it does not preserve a fractional 1.5-CPU Kubernetes request.
The Spark Operator distinguishes the integer cores field, which maps to spark.executor.cores, from the string coreRequest field used for physical Kubernetes CPU requests. The current title and description could therefore be interpreted as fractional CPU-request support even though this change only accepts a float representation and rounds it to an integer core count.
Could we document that fractional values are rounded upward? If the intention is to support fractional Kubernetes CPU allocation, this would need to target coreRequest instead.
Blocking 3: Propagate the new exception contract
Changing unsupported types from ValueError to TypeError is semantically reasonable, but that exception propagates through public methods. The resolver, spec-builder, and SparkClient.connect() docstrings still document only ValueError for invalid resource configuration.
Could we update the relevant Raises sections so users can reliably handle this new exception type?
Please also add at least one test through a public construction path, such as get_spark_connect_executor_spec(resources_per_executor={"cpu": 1.5}), and assert that the resulting CR field is cores == 2. A similar driver-resource test would cover the advanced API path.
The explicit boolean rejection itself looks correct.
|
@HarshPopat23: changing LGTM is restricted to collaborators DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
|
Thanks @HarshPopat23 for the thorough review! I have updated the PR to address all three points:
All unit tests, Ruff linter checks, and pre-commit hooks are passing cleanly. Please let me know if any further tweaks are needed! |
|
Non-blocking nit: |
|
Thanks @Goku2099! Extracting ResourceDict = dict[str, str | int | float] into constants.py makes total sense to clean up duplicate signatures across the file. I'll be happy to open a quick fast-follow PR for that right after this one is merged! |
…_value Signed-off-by: Neha-Nimsatkar <1492neha@gmail.com>
e732b96 to
962840c
Compare
|
Thanks for the PR! |
|
New changes are detected. LGTM label has been removed. |
|
/retest |
|
@Neha-Nimsatkar @Goku2099 @HarshPopat23 Looking into the PR changes a bit more I think we should keep this PR focused only on rejecting The reason is because the CR construction step currently converts resources into an integer number of cores and not directly CPU request and limit. sdk/kubeflow/spark/backends/kubernetes/utils.py Lines 466 to 470 in 3febcd7 There is an issue raised in the spark-operator project to support request and limit directly on the @Neha-Nimsatkar Please remove the support for float CPU values for now from the PR so we can move forward with the changes. |
Signed-off-by: Neha-Nimsatkar <1492neha@gmail.com>
d3b9bf5 to
1ce0858
Compare
Thanks for the feedback @tariq-hasan! That makes complete sense regarding the backend integer cores mapping and the Spark Operator CRD tracking. I have updated the PR to remove float CPU support and scoped the changes strictly to rejecting bool types (along with updating the unit tests). All linting, pre-commit checks, and test suites are passing locally. Please take a look when you have a chance! |
|
/test E2E Test (1.34.0, master) |
|
@Neha-Nimsatkar: No presubmit jobs available for kubeflow/sdk@main DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
|
cc @tariq-hasan Hi Tariq, the |
|
/ok-to-test |
|
|
||
| image: str | None = None | ||
| resources: dict[str, str] | None = None | ||
| resources: dict[str, str | int] | None = None |
There was a problem hiding this comment.
I think best to follow the approach from other clients.
| resources: dict[str, str | int] | None = None | |
| resources: dict | None = None |
sdk/kubeflow/trainer/types/types.py
Line 61 in 3febcd7
These should change as well.
sdk/kubeflow/spark/api/spark_client.py
Line 67 in 3febcd7
sdk/kubeflow/spark/api/spark_client.py
Line 178 in 3febcd7
|
|
||
| num_instances: int | None = None | ||
| resources_per_executor: dict[str, str] | None = None | ||
| resources_per_executor: dict[str, str | int] | None = None |
There was a problem hiding this comment.
Same here.
| resources_per_executor: dict[str, str | int] | None = None | |
| resources_per_executor: dict | None = None |
| executor: Executor | None = None, | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
| executor: Executor | None = None, | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
| spark_version: str | None = None, | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
| def get_spark_job_executor_spec( | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
| arguments: list[str] | None = None, | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
| func_args: dict[str, Any] | None = None, | ||
| num_executors: int | None = None, | ||
| resources_per_executor: dict[str, str] | None = None, | ||
| resources_per_executor: dict[str, str | int] | None = None, |
There was a problem hiding this comment.
| resources_per_executor: dict[str, str | int] | None = None, | |
| resources_per_executor: dict | None = None, |
Signed-off-by: Neha-Nimsatkar <1492neha@gmail.com>
|
Thanks for the review and guidance, @tariq-hasan! I have addressed all the feedback and pushed the updates:
Ready for another look whenever you have time! |
What this PR does / why we need it:
_validate_cpu_valuestrictly to rejectingbooltypes (True/False), which previously passed as 1 core due to Python'sisinstance(True, int)subclassing behavior.floatCPU support and reverts resource dictionary type hints to align with backendSparkV1alpha1ServerSpeccore allocation semantics.TypeErrorwhen an unsupported argument type (e.g.bool,list,dict) is passed.utils_test.pyandtypes_test.py.Which issue(s) this PR fixes:
N/A
Checklist: