Skip to content

[ENH] V1 -> V2 Migration - Flows (module)#1609

Open
Omswastik-11 wants to merge 110 commits intoopenml:mainfrom
Omswastik-11:flow-migration-stacked
Open

[ENH] V1 -> V2 Migration - Flows (module)#1609
Omswastik-11 wants to merge 110 commits intoopenml:mainfrom
Omswastik-11:flow-migration-stacked

Conversation

@Omswastik-11
Copy link
Contributor

@Omswastik-11 Omswastik-11 commented Jan 8, 2026

Fixes #1601

added a Create method in FlowAPI for publishing flow but not refactored with old publish . (Needs discussion on this)
Added tests using fake_methods so that we can test without local V2 server . I have tested the FlowsV2 methods (get and exists ) and delete and list were not implemented in V2 server so I skipped them .

@Omswastik-11 Omswastik-11 changed the title [ENH] V1 -> V2 Migration [ENH] V1 -> V2 Migration - Flows (module) Jan 8, 2026
@Omswastik-11 Omswastik-11 marked this pull request as ready for review January 8, 2026 15:09
@geetu040 geetu040 mentioned this pull request Jan 9, 2026
25 tasks
@codecov-commenter
Copy link

codecov-commenter commented Jan 12, 2026

Codecov Report

❌ Patch coverage is 65.42751% with 279 lines in your changes missing coverage. Please review.
✅ Project coverage is 47.57%. Comparing base (d421b9e) to head (8802b8a).

Files with missing lines Patch % Lines
openml/_api/resources/flow.py 21.50% 73 Missing ⚠️
openml/_api/clients/http.py 68.20% 62 Missing ⚠️
openml/_api/resources/base/versions.py 43.82% 50 Missing ⚠️
openml/_api/resources/base/fallback.py 26.31% 28 Missing ⚠️
openml/_api/setup/backend.py 71.91% 25 Missing ⚠️
openml/_api/setup/_utils.py 56.00% 11 Missing ⚠️
openml/flows/flow.py 21.42% 11 Missing ⚠️
openml/_api/setup/builder.py 81.08% 7 Missing ⚠️
openml/flows/functions.py 40.00% 6 Missing ⚠️
openml/_api/resources/base/base.py 78.57% 3 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1609      +/-   ##
==========================================
- Coverage   52.04%   47.57%   -4.47%     
==========================================
  Files          36       63      +27     
  Lines        4333     5070     +737     
==========================================
+ Hits         2255     2412     +157     
- Misses       2078     2658     +580     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@geetu040 geetu040 left a comment

Choose a reason for hiding this comment

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

Nicely done overall.

Signed-off-by: Omswastik-11 <omswastikpanda11@gmail.com>
@Omswastik-11 Omswastik-11 force-pushed the flow-migration-stacked branch from 614411f to 36184e5 Compare January 14, 2026 14:56
@Omswastik-11 Omswastik-11 requested a review from geetu040 January 14, 2026 15:10
@Omswastik-11
Copy link
Contributor Author

Hi @geetu040 !! Can you review the pre-commit failure It was due to merge conflicts more specifically for tasks . Should I change it on my branch ?

@geetu040
Copy link
Collaborator

Hi @geetu040 !! Can you review the pre-commit failure It was due to merge conflicts more specifically for tasks . Should I change it on my branch ?

can you try again, sync your branch with mine? It should be fixed now.

@Omswastik-11
Copy link
Contributor Author

I think that due to conflicts it did not synced properly . I have to revert it manually

Copy link
Collaborator

@geetu040 geetu040 left a comment

Choose a reason for hiding this comment

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

Nice overall, few changes needed. I'll look at the tests later when the implementation is final.

Omswastik-11 and others added 24 commits February 2, 2026 17:06
Signed-off-by: Omswastik-11 <omswastikpanda11@gmail.com>
Signed-off-by: Omswastik-11 <omswastikpanda11@gmail.com>
Signed-off-by: Omswastik-11 <omswastikpanda11@gmail.com>
This reverts commit fd43c48.
…into flow-migration-stacked

# Conflicts:
#	openml/_api/setup/config.py
- removing this since it was not part of the sdk previously
- some tests fail because of the timeout in stacked PRs
- this option can easily be added if needed in future
@mock.patch("openml.flows.functions.get_flow")
@mock.patch("openml.flows.functions.flow_exists")
@mock.patch("openml._api_calls._perform_api_call")
def test_publish_error(self, api_call_mock, flow_exists_mock, get_flow_mock):
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's happening here, doesn't look right, can you please explain?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought we are not using openml._api_calls._perform_api_call anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

https://github.com/openml/openml-python/pull/1609/changes#r2687563796

this comment was mentioned "resolved" but was never really answered, the question is still there: why skipping these tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Omswastik-11
Copy link
Contributor Author

@pytest.mark.sklearn()
    @mock.patch("openml.flows.functions.get_flow")
    @mock.patch("openml._api_calls._perform_api_call")
    @mock.patch("openml.flows.functions.flow_exists")
    def test_publish_error(self, api_call_mock, flow_exists_mock, get_flow_mock):
        model = sklearn.ensemble.RandomForestClassifier()
        flow = self.extension.model_to_flow(model)
        api_call_mock.return_value = (
            "<oml:upload_flow>\n" "    <oml:id>1</oml:id>\n" "</oml:upload_flow>"
        )
        flow_exists_mock.return_value = False
        get_flow_mock.return_value = flow

        flow.publish()
        # Not collecting flow_id for deletion since this is a test for failed upload

        assert api_call_mock.call_count == 1
        assert get_flow_mock.call_count == 1
        assert flow_exists_mock.call_count == 1

        flow_copy = copy.deepcopy(flow)
        flow_copy.name = flow_copy.name[:-1]
        get_flow_mock.return_value = flow_copy
        flow_exists_mock.return_value = 1

        if Version(sklearn.__version__) < Version("0.22"):
            fixture = (
                "The flow on the server is inconsistent with the local flow. "
                "The server flow ID is 1. Please check manually and remove "
                "the flow if necessary! Error is:\n"
                "'Flow sklearn.ensemble.forest.RandomForestClassifier: "
                "values for attribute 'name' differ: "
                "'sklearn.ensemble.forest.RandomForestClassifier'"
                "\nvs\n'sklearn.ensemble.forest.RandomForestClassifie'.'"
            )
        else:
            # sklearn.ensemble.forest -> sklearn.ensemble._forest
            fixture = (
                "The flow on the server is inconsistent with the local flow. "
                "The server flow ID is 1. Please check manually and remove "
                "the flow if necessary! Error is:\n"
                "'Flow sklearn.ensemble._forest.RandomForestClassifier: "
                "values for attribute 'name' differ: "
                "'sklearn.ensemble._forest.RandomForestClassifier'"
                "\nvs\n'sklearn.ensemble._forest.RandomForestClassifie'.'"
            )
        with pytest.raises(ValueError, match=fixture):
            flow.publish()

        TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
        TestBase.logger.info(
            f"collected from {__file__.split('/')[-1]}: {flow.flow_id}",
        )

        assert get_flow_mock.call_count == 2


@mock.patch.object(requests.Session, "delete")
def test_delete_flow_not_owned(mock_delete, test_files_directory, test_api_key):
    openml.config.start_using_configuration_for_example()
    content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml"
    mock_delete.return_value = create_request_response(
        status_code=412,
        content_filepath=content_file,
    )

    with pytest.raises(
        OpenMLNotAuthorizedError,
        match="The flow can not be deleted because it was not uploaded by you.",
    ):
        openml.flows.delete_flow(40_000)

    flow_url = "https://test.openml.org/api/v1/xml/flow/40000"
    assert flow_url == mock_delete.call_args.args[0]
    assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")


@mock.patch.object(requests.Session, "delete")
def test_delete_flow_with_run(mock_delete, test_files_directory, test_api_key):
    openml.config.start_using_configuration_for_example()
    content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml"
    mock_delete.return_value = create_request_response(
        status_code=412,
        content_filepath=content_file,
    )

    with pytest.raises(
        OpenMLNotAuthorizedError,
        match="The flow can not be deleted because it still has associated entities:",
    ):
        openml.flows.delete_flow(40_000)

    flow_url = "https://test.openml.org/api/v1/xml/flow/40000"
    assert flow_url == mock_delete.call_args.args[0]
    assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")


@mock.patch.object(requests.Session, "delete")
def test_delete_subflow(mock_delete, test_files_directory, test_api_key):
    openml.config.start_using_configuration_for_example()
    content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml"
    mock_delete.return_value = create_request_response(
        status_code=412,
        content_filepath=content_file,
    )

    with pytest.raises(
        OpenMLNotAuthorizedError,
        match="The flow can not be deleted because it still has associated entities:",
    ):
        openml.flows.delete_flow(40_000)

    flow_url = "https://test.openml.org/api/v1/xml/flow/40000"
    assert flow_url == mock_delete.call_args.args[0]
    assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")


@mock.patch.object(requests.Session, "delete")
def test_delete_flow_success(mock_delete, test_files_directory, test_api_key):
    openml.config.start_using_configuration_for_example()
    content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml"
    mock_delete.return_value = create_request_response(
        status_code=200,
        content_filepath=content_file,
    )

    success = openml.flows.delete_flow(33364)
    assert success

    flow_url = "https://test.openml.org/api/v1/xml/flow/33364"
    assert flow_url == mock_delete.call_args.args[0]
    assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")


@mock.patch.object(requests.Session, "delete")
@pytest.mark.xfail(reason="failures_issue_1544", strict=False)
def test_delete_unknown_flow(mock_delete, test_files_directory, test_api_key):
    openml.config.start_using_configuration_for_example()
    content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml"
    mock_delete.return_value = create_request_response(
        status_code=412,
        content_filepath=content_file,
    )

    with pytest.raises(
        OpenMLServerException,
        match="flow does not exist",
    ):
        openml.flows.delete_flow(9_999_999)

    flow_url = "https://test.openml.org/api/v1/xml/flow/9999999"
    assert flow_url == mock_delete.call_args.args[0]
    assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")

@geetu040 These are the tests failing I am not sure what to do with these

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH] V1 → V2 API Migration - flows

5 participants