Skip to content

fix(spark): reject bool type as CPU in _validate_cpu_value - #752

Open
Neha-Nimsatkar wants to merge 4 commits into
kubeflow:mainfrom
Neha-Nimsatkar:fix/spark-utils-cpu-validation
Open

fix(spark): reject bool type as CPU in _validate_cpu_value#752
Neha-Nimsatkar wants to merge 4 commits into
kubeflow:mainfrom
Neha-Nimsatkar:fix/spark-utils-cpu-validation

Conversation

@Neha-Nimsatkar

@Neha-Nimsatkar Neha-Nimsatkar commented Aug 10, 2026

Copy link
Copy Markdown

What this PR does / why we need it:

  • Scopes CPU validation in _validate_cpu_value strictly to rejecting bool types (True/False), which previously passed as 1 core due to Python's isinstance(True, int) subclassing behavior.
  • Removes float CPU support and reverts resource dictionary type hints to align with backend SparkV1alpha1ServerSpec core allocation semantics.
  • Raises TypeError when an unsupported argument type (e.g. bool, list, dict) is passed.
  • Updates unit tests in utils_test.py and types_test.py.

Which issue(s) this PR fixes:

N/A

Checklist:

  • Pre-commit checks passed
  • Unit tests passed
  • DCO Signed-off

@google-oss-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign kramaranya for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@github-actions

Copy link
Copy Markdown
Contributor

🎉 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:

  • If you haven't already, please check out our Contributing Guide for repo-specific guidelines and the Kubeflow Contributor Guide for general community standards
  • Our team will review your PR soon! cc @kubeflow/kubeflow-sdk-team

Join the community:

Feel free to ask questions in the comments if you need any help or clarification!
Thanks again for contributing to Kubeflow! 🙏

@tariq-hasan

Copy link
Copy Markdown
Member

/ok-to-test

@tariq-hasan

Copy link
Copy Markdown
Member

Hi @Neha-Nimsatkar! Thanks for raising the PR.

You'd need to fix the errors in pre-commit to pass the CI.

Comment thread kubeflow/spark/backends/kubernetes/utils.py Outdated
@google-oss-prow google-oss-prow Bot added size/S and removed size/M labels Aug 11, 2026

@HarshPopat23 HarshPopat23 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@google-oss-prow

Copy link
Copy Markdown
Contributor

@HarshPopat23: changing LGTM is restricted to collaborators

Details

In response to this:

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.

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.

@google-oss-prow google-oss-prow Bot added size/M and removed size/S labels Aug 11, 2026
@Neha-Nimsatkar

Copy link
Copy Markdown
Author

Thanks @HarshPopat23 for the thorough review!

I have updated the PR to address all three points:

  1. Public API Types: Updated resources_per_executor type annotations to dict[str, str | int | float] across the relevant helper and builder functions.
  2. Float Semantics: Documented in _validate_cpu_value that fractional CPU values are rounded upward (math.ceil) for Spark executor cores.
  3. Exception Contract & Tests: Documented TypeError in the relevant Raises sections and added public construction path unit tests in utils_test.py (test_get_spark_connect_executor_spec_float_cpu and test_get_spark_connect_executor_spec_bool_cpu_raises_type_error).

All unit tests, Ruff linter checks, and pre-commit hooks are passing cleanly. Please let me know if any further tweaks are needed!

@Goku2099

Copy link
Copy Markdown
Member

Non-blocking nit: dict[str, str | int | float] | None is now repeated across multiple signatures in this file. Might be worth pulling it into a shared alias in constants.py (e.g. ResourceDict = dict[str, str | int | float]) so future changes to accepted types only need one edit. Could be a fast-follow rather than blocking this PR.. @tariq-hasan thoughts?

@Neha-Nimsatkar

Copy link
Copy Markdown
Author

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>
@Neha-Nimsatkar
Neha-Nimsatkar force-pushed the fix/spark-utils-cpu-validation branch from e732b96 to 962840c Compare August 27, 2026 09:16
@Goku2099

Copy link
Copy Markdown
Member

Thanks for the PR!
/lgtm
cc @tariq-hasan

@google-oss-prow google-oss-prow Bot removed the lgtm label Aug 29, 2026
@google-oss-prow

Copy link
Copy Markdown
Contributor

New changes are detected. LGTM label has been removed.

@Neha-Nimsatkar

Copy link
Copy Markdown
Author

/retest

@tariq-hasan

Copy link
Copy Markdown
Member

@Neha-Nimsatkar @Goku2099 @HarshPopat23 Looking into the PR changes a bit more I think we should keep this PR focused only on rejecting bool type as CPU.

The reason is because the CR construction step currently converts resources into an integer number of cores and not directly CPU request and limit.

return models.SparkV1alpha1ServerSpec(
cores=cores,
memory=memory,
template=template,
)

There is an issue raised in the spark-operator project to support request and limit directly on the SparkConnect CRD - similar to SparkApplication- kubeflow/spark-operator#2957. Once this is completed we can revisit how to support non-integer resources directly through CPU request and limit on the SDK - to ensure the semantics of resources on the SDK matches the expectation that this be treated as K8s-native resource allocation instead of Spark-native interpretation of task parallelism.

@Neha-Nimsatkar Please remove the support for float CPU values for now from the PR so we can move forward with the changes.

@google-oss-prow google-oss-prow Bot added size/M and removed size/L labels Sep 5, 2026
@Neha-Nimsatkar Neha-Nimsatkar changed the title fix(spark): support float cpu values and reject bool in _validate_cpu_value fix(spark): reject bool type as CPU in _validate_cpu_value Sep 5, 2026
Signed-off-by: Neha-Nimsatkar <1492neha@gmail.com>
@Neha-Nimsatkar
Neha-Nimsatkar force-pushed the fix/spark-utils-cpu-validation branch from d3b9bf5 to 1ce0858 Compare September 5, 2026 20:02
@Neha-Nimsatkar

Copy link
Copy Markdown
Author

@Neha-Nimsatkar @Goku2099 @HarshPopat23 Looking into the PR changes a bit more I think we should keep this PR focused only on rejecting bool type as CPU.

The reason is because the CR construction step currently converts resources into an integer number of cores and not directly CPU request and limit.

return models.SparkV1alpha1ServerSpec(
cores=cores,
memory=memory,
template=template,
)

There is an issue raised in the spark-operator project to support request and limit directly on the SparkConnect CRD - similar to SparkApplication- kubeflow/spark-operator#2957. Once this is completed we can revisit how to support non-integer resources directly through CPU request and limit on the SDK - to ensure the semantics of resources on the SDK matches the expectation that this be treated as K8s-native resource allocation instead of Spark-native interpretation of task parallelism.

@Neha-Nimsatkar Please remove the support for float CPU values for now from the PR so we can move forward with the changes.

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!

@Neha-Nimsatkar

Copy link
Copy Markdown
Author

/test E2E Test (1.34.0, master)

@google-oss-prow

Copy link
Copy Markdown
Contributor

@Neha-Nimsatkar: No presubmit jobs available for kubeflow/sdk@main

Details

In response to this:

/test E2E Test (1.34.0, master)

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.

@Neha-Nimsatkar

Copy link
Copy Markdown
Author

cc @tariq-hasan Hi Tariq, the E2E Test (1.34.0, master) job hit an infrastructure timeout ("self-hosted runner lost communication") while all other versions passed cleanly. Could you please click "Re-run failed jobs" when you get a chance? Thanks!

@Goku2099

Goku2099 commented Sep 6, 2026

Copy link
Copy Markdown
Member

/ok-to-test
/retest

Comment thread kubeflow/spark/types/types.py Outdated

image: str | None = None
resources: dict[str, str] | None = None
resources: dict[str, str | int] | None = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think best to follow the approach from other clients.

Suggested change
resources: dict[str, str | int] | None = None
resources: dict | None = None

resources_per_node: dict | None = None

These should change as well.

resources_per_executor: dict[str, str] | None = None,

resources_per_executor: dict[str, str] | None = None,

Comment thread kubeflow/spark/types/types.py Outdated

num_instances: int | None = None
resources_per_executor: dict[str, str] | None = None
resources_per_executor: dict[str, str | int] | None = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resources_per_executor: dict[str, str | int] | None = None,
resources_per_executor: dict | None = None,

Signed-off-by: Neha-Nimsatkar <1492neha@gmail.com>
@Neha-Nimsatkar

Copy link
Copy Markdown
Author

Thanks for the review and guidance, @tariq-hasan!

I have addressed all the feedback and pushed the updates:

  • Simplified resources and resources_per_executor annotations to dict | None = None across types.py and utils.py to match the convention in kubeflow.trainer.
  • Updated the two occurrences in spark_client.py (lines 67 and 178) as well.
  • Ran pre-commit hooks and confirmed all Spark unit tests are passing locally.

Ready for another look whenever you have time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants