Repository: PrefectHQ/prefect

Issue:
Cannot set retries to 0 using with_options for flow
### First check

- [X] I added a descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Prefect documentation for this issue.
- [X] I checked that this issue is related to Prefect and not one of its dependencies.

### Bug summary

When using the with_options function to run a flow, it is not possible to set retries to 0. Any other value that gets evaluated as boolean True will work.

I believe this line needs changing. As it always reverts to the predefined retries value, that is specified, when the flow is defined.
https://github.com/PrefectHQ/prefect/blob/e578d4b83a2a2c713f5c342dc55dd7ab851aa1fd/src/prefect/flows.py#L438C21-L438C28

### Reproduction

```python3
from prefect import flow, task, context
from prefect.server.schemas.states import Failed
import logging


@task
def i_am_succeeding():
    logging.debug("I am a success")


@task(retries=2, retry_delay_seconds=3)
def i_am_failing():
    message = f"I am a task failure, but noone cares."
    logging.error(message)
    return Failed()


@flow(name="project_retries", retries=2)
def flow_retries():
    """
    This flow is supposed to illustrate how we overwrite task parameters.
    """
    logging.info(
        f"This is 'flow_retries' run number {context.get_run_context().flow_run.run_count}"
    )
    i_am_succeeding()
    i_am_failing(return_state=True)


if __name__ == "__main__":
    logging.info(
        f"Should run flow 3 times. Only the first run executes 'i_am_succeeding'. Every run should execute 'i_am_failing' 3 times."
    )
    flow_retries(return_state=True)
    logging.info(
        f"Should run flow 1 time. It executes 'i_am_succeeding' once and 'i_am_failing' 3 times."
    )
    logging.error(f"Currently also running 3 times, suspecting prefect bug")
    flow_retries.with_options(retries=0)(return_state=True)
```


### Error

_No response_

### Versions

```Text
Version:             2.13.8
API version:         0.8.4
Python version:      3.8.18
Git commit:          1331bb3b
Built:               Thu, Oct 19, 2023 2:29 PM
OS/Arch:             darwin/arm64
Profile:             default
Server type:         ephemeral
Server:
  Database:          sqlite
  SQLite version:    3.43.2
```


### Additional context

_No response_


Discussion:
Thanks @danieljirausch - we'll accept this onto the backlog. We'd also welcome a contribution if you'd care to make one! Thanks.

Resolving patch:
diff --git a/src/prefect/flows.py b/src/prefect/flows.py
--- a/src/prefect/flows.py
+++ b/src/prefect/flows.py
@@ -361,8 +361,8 @@ def with_options(
         *,
         name: str = None,
         version: str = None,
-        retries: int = 0,
-        retry_delay_seconds: Union[int, float] = 0,
+        retries: Optional[int] = None,
+        retry_delay_seconds: Optional[Union[int, float]] = None,
         description: str = None,
         flow_run_name: Optional[Union[Callable[[], str], str]] = None,
         task_runner: Union[Type[BaseTaskRunner], BaseTaskRunner] = None,
@@ -440,11 +440,13 @@ def with_options(
             fn=self.fn,
             name=name or self.name,
             description=description or self.description,
-            flow_run_name=flow_run_name,
+            flow_run_name=flow_run_name or self.flow_run_name,
             version=version or self.version,
             task_runner=task_runner or self.task_runner,
-            retries=retries or self.retries,
-            retry_delay_seconds=retry_delay_seconds or self.retry_delay_seconds,
+            retries=retries if retries is not None else self.retries,
+            retry_delay_seconds=retry_delay_seconds
+            if retry_delay_seconds is not None
+            else self.retry_delay_seconds,
             timeout_seconds=(
                 timeout_seconds if timeout_seconds is not None else self.timeout_seconds
             ),


Test patch:
diff --git a/tests/test_flows.py b/tests/test_flows.py
--- a/tests/test_flows.py
+++ b/tests/test_flows.py
@@ -379,6 +379,33 @@ def initial_flow():
         flow_with_options = initial_flow.with_options(timeout_seconds=0)
         assert flow_with_options.timeout_seconds is None
 
+    def test_with_options_can_unset_retries_with_zero(self):
+        @flow(retries=3)
+        def initial_flow():
+            pass
+
+        flow_with_options = initial_flow.with_options(retries=0)
+        assert flow_with_options.retries == 0
+
+    def test_with_options_can_unset_retry_delay_seconds_with_zero(self):
+        @flow(retry_delay_seconds=3)
+        def initial_flow():
+            pass
+
+        flow_with_options = initial_flow.with_options(retry_delay_seconds=0)
+        assert flow_with_options.retry_delay_seconds == 0
+
+    def test_with_options_uses_parent_flow_run_name_if_not_provided(self):
+        def generate_flow_run_name():
+            return "new-name"
+
+        @flow(retry_delay_seconds=3, flow_run_name=generate_flow_run_name)
+        def initial_flow():
+            pass
+
+        flow_with_options = initial_flow.with_options()
+        assert flow_with_options.flow_run_name is generate_flow_run_name
+
     def test_with_options_can_unset_result_options_with_none(self):
         @flow(
             persist_result=True,