Skip to content

Conversation

@kmcgrady
Copy link
Collaborator

@kmcgrady kmcgrady commented Oct 18, 2025

Describe your changes

Refactored the AppNode into separate files. Besides the fact that AppNode has expanded to an unwieldy file. This change is the beginning of an adjustment to the AppNode tree hierarchy.

Testing Plan

  • Unit Tests (JS and/or Python) - Migrated to separate files
  • Rest of the tests should pass as expected.

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Contributor

snyk-io bot commented Oct 18, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Licenses 0 0 0 0 0 issues
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 18, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-12809/streamlit-1.50.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-12809.streamlit.app (☁️ Deploy here if not accessible)

@kmcgrady kmcgrady added security-assessment-completed Security assessment has been completed for PR impact:internal PR changes only affect internal code change:refactor PR contains code refactoring without behavior change labels Oct 18, 2025
@kmcgrady kmcgrady requested a review from Copilot October 18, 2025 19:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the monolithic AppNode.ts file by breaking it into separate, focused modules within a new render-tree directory structure. The refactoring improves code organization without changing functionality, making the codebase more maintainable as the AppNode tree hierarchy continues to evolve.

Key Changes:

  • Extracted the large AppNode.ts file into separate specialized files
  • Created a new render-tree directory to contain the tree-related modules
  • Moved existing tests to accompany their respective modules

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
frontend/lib/src/AppNode.ts Converted to a simple re-export module that maintains backward compatibility
frontend/lib/src/render-tree/AppNode.interface.ts Extracted the AppNode interface definition with comprehensive documentation
frontend/lib/src/render-tree/AppRoot.ts Extracted the AppRoot class and related interfaces for managing the data tree root
frontend/lib/src/render-tree/BlockNode.ts Extracted the BlockNode class for container nodes that hold children
frontend/lib/src/render-tree/ElementNode.ts Extracted the ElementNode class for leaf nodes containing single elements
frontend/lib/src/render-tree/test-utils.ts Extracted shared test utilities and helper functions
frontend/lib/src/render-tree/AppRoot.test.ts Moved AppRoot-specific tests to accompany the extracted class
frontend/lib/src/render-tree/BlockNode.test.ts Moved BlockNode-specific tests to accompany the extracted class
frontend/lib/src/render-tree/ElementNode.test.ts Moved ElementNode-specific tests to accompany the extracted class

@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from 671f483 to dcda273 Compare October 18, 2025 19:46
@kmcgrady kmcgrady requested a review from Copilot October 18, 2025 19:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

frontend/lib/src/render-tree/test-utils.ts:1

  • Corrected spelling of 'NamedDataset' to 'NamedDataSet' for consistency.
/**

@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from dcda273 to fd9ced5 Compare October 18, 2025 19:49
@github-actions
Copy link
Contributor

github-actions bot commented Oct 18, 2025

📈 Frontend coverage change detected

The frontend unit test (vitest) coverage has increased by 0.0100%

  • Current PR: 86.0100% (49525 lines, 6927 missed)
  • Latest develop: 86.0000% (49408 lines, 6916 missed)

✅ Coverage change is within normal range.

📊 View detailed coverage comparison

@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from fd9ced5 to 0004f15 Compare October 19, 2025 04:21
@kmcgrady kmcgrady changed the base branch from develop to graphite-base/12809 October 19, 2025 16:20
@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from 0004f15 to da3475c Compare October 19, 2025 16:20
@kmcgrady kmcgrady changed the base branch from graphite-base/12809 to 10-19-create_e2e_tests_for_common_appnode_scenarios_not_particularly_tested October 19, 2025 16:20
@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from da3475c to 7627ccb Compare October 19, 2025 16:47
@kmcgrady kmcgrady force-pushed the 10-19-create_e2e_tests_for_common_appnode_scenarios_not_particularly_tested branch from 48d1cd6 to 50e7ec0 Compare October 19, 2025 16:47
@kmcgrady kmcgrady marked this pull request as ready for review October 24, 2025 14:26
@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from 7627ccb to f5f6301 Compare October 24, 2025 15:05
@kmcgrady kmcgrady requested a review from Copilot October 24, 2025 15:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

frontend/lib/src/render-tree/AppRoot.test.ts:1

  • The refactored AppRoot tests are missing several test cases from the original file. Specifically missing: tests for query parameter variations ('?embed_options=hide_loading_screen', '?embed_options=show_loading_screen_v1', '?embed_options=show_loading_screen_v2'), the test for v1 loading screen on non-initial render, and the logo passing test. These should be included to maintain coverage parity.
/**

@kmcgrady kmcgrady force-pushed the 10-19-create_e2e_tests_for_common_appnode_scenarios_not_particularly_tested branch from 09805fb to 89ad1a4 Compare October 24, 2025 16:42
@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from f5f6301 to 17e8ba1 Compare October 24, 2025 16:42
@kmcgrady kmcgrady force-pushed the 10-19-create_e2e_tests_for_common_appnode_scenarios_not_particularly_tested branch from 89ad1a4 to 6b854af Compare October 24, 2025 16:56
@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from 17e8ba1 to 19b19a3 Compare October 24, 2025 16:56
@github-actions
Copy link
Contributor

github-actions bot commented Oct 24, 2025

📉 Python coverage change detected

The Python unit test coverage has decreased by 0.0050%

  • Current PR: 92.8205% (20057 statements, 1440 missed)
  • Latest develop: 92.8254% (20057 statements, 1439 missed)

✅ Coverage change is within normal range.

Coverage by files
Name Stmts Miss Cover
streamlit/__init__.py 141 0 100%
streamlit/__main__.py 3 3 0%
streamlit/auth_util.py 100 25 75%
streamlit/cli_util.py 39 6 85%
streamlit/column_config.py 3 0 100%
streamlit/commands/__init__.py 0 0 100%
streamlit/commands/echo.py 54 11 80%
streamlit/commands/execution_control.py 59 10 83%
streamlit/commands/experimental_query_params.py 40 2 95%
streamlit/commands/logo.py 41 6 85%
streamlit/commands/navigation.py 106 2 98%
streamlit/commands/page_config.py 101 4 96%
streamlit/components/__init__.py 0 0 100%
streamlit/components/lib/__init__.py 0 0 100%
streamlit/components/lib/local_component_registry.py 35 2 94%
streamlit/components/types/__init__.py 0 0 100%
streamlit/components/types/base_component_registry.py 14 0 100%
streamlit/components/types/base_custom_component.py 49 6 88%
streamlit/components/v1/__init__.py 5 0 100%
streamlit/components/v1/component_arrow.py 33 8 76%
streamlit/components/v1/component_registry.py 41 3 93%
streamlit/components/v1/components.py 4 4 0%
streamlit/components/v1/custom_component.py 85 7 92%
streamlit/components/v2/__init__.py 24 0 100%
streamlit/components/v2/bidi_component/__init__.py 4 0 100%
streamlit/components/v2/bidi_component/constants.py 5 0 100%
streamlit/components/v2/bidi_component/main.py 109 12 89%
streamlit/components/v2/bidi_component/serialization.py 81 5 94%
streamlit/components/v2/bidi_component/state.py 13 0 100%
streamlit/components/v2/component_definition_resolver.py 30 0 100%
streamlit/components/v2/component_file_watcher.py 117 9 92%
streamlit/components/v2/component_manager.py 96 16 83%
streamlit/components/v2/component_manifest_handler.py 24 0 100%
streamlit/components/v2/component_path_utils.py 68 5 93%
streamlit/components/v2/component_registry.py 118 8 93%
streamlit/components/v2/get_bidi_component_manager.py 8 1 88%
streamlit/components/v2/manifest_scanner.py 224 25 89%
streamlit/components/v2/presentation.py 84 19 77%
streamlit/components/v2/types.py 8 8 0%
streamlit/config.py 412 12 97%
streamlit/config_option.py 79 3 96%
streamlit/config_util.py 288 7 98%
streamlit/connections/__init__.py 6 0 100%
streamlit/connections/base_connection.py 45 0 100%
streamlit/connections/snowflake_connection.py 60 13 78%
streamlit/connections/snowpark_connection.py 44 3 93%
streamlit/connections/sql_connection.py 56 6 89%
streamlit/connections/util.py 33 0 100%
streamlit/cursor.py 82 2 98%
streamlit/dataframe_util.py 501 45 91%
streamlit/delta_generator.py 205 6 97%
streamlit/delta_generator_singletons.py 76 8 89%
streamlit/deprecation_util.py 59 4 93%
streamlit/development.py 1 0 100%
streamlit/elements/__init__.py 0 0 100%
streamlit/elements/alert.py 60 0 100%
streamlit/elements/arrow.py 198 15 92%
streamlit/elements/balloons.py 10 0 100%
streamlit/elements/bokeh_chart.py 27 0 100%
streamlit/elements/code.py 20 1 95%
streamlit/elements/deck_gl_json_chart.py 104 10 90%
streamlit/elements/dialog_decorator.py 38 0 100%
streamlit/elements/doc_string.py 227 9 96%
streamlit/elements/empty.py 16 4 75%
streamlit/elements/exception.py 101 10 90%
streamlit/elements/form.py 54 2 96%
streamlit/elements/graphviz_chart.py 35 1 97%
streamlit/elements/heading.py 56 0 100%
streamlit/elements/html.py 48 0 100%
streamlit/elements/iframe.py 29 0 100%
streamlit/elements/image.py 32 0 100%
streamlit/elements/json.py 39 2 95%
streamlit/elements/layouts.py 140 3 98%
streamlit/elements/lib/__init__.py 0 0 100%
streamlit/elements/lib/built_in_chart_utils.py 387 26 93%
streamlit/elements/lib/color_util.py 100 4 96%
streamlit/elements/lib/column_config_utils.py 168 1 99%
streamlit/elements/lib/column_types.py 189 4 98%
streamlit/elements/lib/dialog.py 67 1 99%
streamlit/elements/lib/dicttools.py 39 2 95%
streamlit/elements/lib/file_uploader_utils.py 30 0 100%
streamlit/elements/lib/form_utils.py 26 0 100%
streamlit/elements/lib/image_utils.py 176 21 88%
streamlit/elements/lib/js_number.py 28 3 89%
streamlit/elements/lib/layout_utils.py 109 1 99%
streamlit/elements/lib/mutable_status_container.py 73 4 95%
streamlit/elements/lib/options_selector_utils.py 90 0 100%
streamlit/elements/lib/pandas_styler_utils.py 80 2 98%
streamlit/elements/lib/policies.py 56 1 98%
streamlit/elements/lib/streamlit_plotly_theme.py 49 0 100%
streamlit/elements/lib/subtitle_utils.py 76 13 83%
streamlit/elements/lib/utils.py 76 5 93%
streamlit/elements/map.py 110 1 99%
streamlit/elements/markdown.py 63 2 97%
streamlit/elements/media.py 181 8 96%
streamlit/elements/metric.py 91 0 100%
streamlit/elements/pdf.py 49 2 96%
streamlit/elements/plotly_chart.py 114 4 96%
streamlit/elements/progress.py 36 0 100%
streamlit/elements/pyplot.py 39 2 95%
streamlit/elements/snow.py 10 0 100%
streamlit/elements/space.py 12 0 100%
streamlit/elements/spinner.py 34 0 100%
streamlit/elements/text.py 16 0 100%
streamlit/elements/toast.py 26 0 100%
streamlit/elements/vega_charts.py 226 3 99%
streamlit/elements/widgets/__init__.py 0 0 100%
streamlit/elements/widgets/audio_input.py 68 10 85%
streamlit/elements/widgets/button.py 214 3 99%
streamlit/elements/widgets/button_group.py 161 1 99%
streamlit/elements/widgets/camera_input.py 62 10 84%
streamlit/elements/widgets/chat.py 169 40 76%
streamlit/elements/widgets/checkbox.py 52 0 100%
streamlit/elements/widgets/color_picker.py 56 2 96%
streamlit/elements/widgets/data_editor.py 239 14 94%
streamlit/elements/widgets/file_uploader.py 103 18 83%
streamlit/elements/widgets/multiselect.py 105 4 96%
streamlit/elements/widgets/number_input.py 143 5 97%
streamlit/elements/widgets/radio.py 83 5 94%
streamlit/elements/widgets/select_slider.py 97 0 100%
streamlit/elements/widgets/selectbox.py 91 2 98%
streamlit/elements/widgets/slider.py 241 8 97%
streamlit/elements/widgets/text_widgets.py 130 6 95%
streamlit/elements/widgets/time_widgets.py 248 15 94%
streamlit/elements/write.py 166 30 82%
streamlit/emojis.py 4 0 100%
streamlit/env_util.py 21 3 86%
streamlit/error_util.py 33 2 94%
streamlit/errors.py 188 25 87%
streamlit/external/__init__.py 0 0 100%
streamlit/external/langchain/__init__.py 2 0 100%
streamlit/external/langchain/streamlit_callback_handler.py 141 82 42%
streamlit/file_util.py 84 8 90%
streamlit/git_util.py 100 5 95%
streamlit/logger.py 54 0 100%
streamlit/material_icon_names.py 1 0 100%
streamlit/navigation/__init__.py 0 0 100%
streamlit/navigation/page.py 78 2 97%
streamlit/net_util.py 55 3 95%
streamlit/platform.py 10 1 90%
streamlit/runtime/__init__.py 8 0 100%
streamlit/runtime/app_session.py 447 94 79%
streamlit/runtime/caching/__init__.py 19 0 100%
streamlit/runtime/caching/cache_data_api.py 164 3 98%
streamlit/runtime/caching/cache_errors.py 45 4 91%
streamlit/runtime/caching/cache_resource_api.py 122 0 100%
streamlit/runtime/caching/cache_type.py 11 1 91%
streamlit/runtime/caching/cache_utils.py 165 9 95%
streamlit/runtime/caching/cached_message_replay.py 108 1 99%
streamlit/runtime/caching/hashing.py 311 25 92%
streamlit/runtime/caching/legacy_cache_api.py 14 0 100%
streamlit/runtime/caching/storage/__init__.py 2 0 100%
streamlit/runtime/caching/storage/cache_storage_protocol.py 31 2 94%
streamlit/runtime/caching/storage/dummy_cache_storage.py 21 0 100%
streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py 60 0 100%
streamlit/runtime/caching/storage/local_disk_cache_storage.py 86 4 95%
streamlit/runtime/connection_factory.py 85 9 89%
streamlit/runtime/context.py 140 0 100%
streamlit/runtime/context_util.py 18 0 100%
streamlit/runtime/credentials.py 139 4 97%
streamlit/runtime/forward_msg_cache.py 23 2 91%
streamlit/runtime/forward_msg_queue.py 63 4 94%
streamlit/runtime/fragment.py 112 2 98%
streamlit/runtime/media_file_manager.py 69 7 90%
streamlit/runtime/media_file_storage.py 15 0 100%
streamlit/runtime/memory_media_file_storage.py 68 0 100%
streamlit/runtime/memory_session_storage.py 15 0 100%
streamlit/runtime/memory_uploaded_file_manager.py 41 1 98%
streamlit/runtime/metrics_util.py 193 12 94%
streamlit/runtime/pages_manager.py 59 2 97%
streamlit/runtime/runtime.py 248 18 93%
streamlit/runtime/runtime_util.py 30 1 97%
streamlit/runtime/script_data.py 16 0 100%
streamlit/runtime/scriptrunner/__init__.py 5 0 100%
streamlit/runtime/scriptrunner/exec_code.py 49 5 90%
streamlit/runtime/scriptrunner/magic.py 83 1 99%
streamlit/runtime/scriptrunner/magic_funcs.py 10 1 90%
streamlit/runtime/scriptrunner/script_cache.py 27 0 100%
streamlit/runtime/scriptrunner/script_runner.py 230 27 88%
streamlit/runtime/scriptrunner_utils/__init__.py 0 0 100%
streamlit/runtime/scriptrunner_utils/exceptions.py 11 1 91%
streamlit/runtime/scriptrunner_utils/script_requests.py 106 5 95%
streamlit/runtime/scriptrunner_utils/script_run_context.py 135 2 99%
streamlit/runtime/secrets.py 242 26 89%
streamlit/runtime/session_manager.py 60 1 98%
streamlit/runtime/state/__init__.py 7 0 100%
streamlit/runtime/state/common.py 52 2 96%
streamlit/runtime/state/presentation.py 19 4 79%
streamlit/runtime/state/query_params.py 110 3 97%
streamlit/runtime/state/query_params_proxy.py 71 0 100%
streamlit/runtime/state/safe_session_state.py 77 11 86%
streamlit/runtime/state/session_state.py 433 29 93%
streamlit/runtime/state/session_state_proxy.py 62 8 87%
streamlit/runtime/state/widgets.py 15 1 93%
streamlit/runtime/stats.py 42 0 100%
streamlit/runtime/theme_util.py 46 1 98%
streamlit/runtime/uploaded_file_manager.py 39 3 92%
streamlit/runtime/websocket_session_manager.py 66 0 100%
streamlit/source_util.py 36 1 97%
streamlit/string_util.py 88 7 92%
streamlit/temporary_directory.py 18 1 94%
streamlit/testing/__init__.py 0 0 100%
streamlit/testing/v1/__init__.py 2 0 100%
streamlit/testing/v1/app_test.py 239 6 97%
streamlit/testing/v1/element_tree.py 1327 87 93%
streamlit/testing/v1/local_script_runner.py 71 2 97%
streamlit/testing/v1/util.py 17 0 100%
streamlit/time_util.py 28 1 96%
streamlit/type_util.py 138 12 91%
streamlit/url_util.py 39 5 87%
streamlit/user_info.py 87 8 91%
streamlit/util.py 38 1 97%
streamlit/version.py 3 0 100%
streamlit/watcher/__init__.py 3 0 100%
streamlit/watcher/event_based_path_watcher.py 181 24 87%
streamlit/watcher/folder_black_list.py 14 1 93%
streamlit/watcher/local_sources_watcher.py 127 9 93%
streamlit/watcher/path_watcher.py 43 3 93%
streamlit/watcher/polling_path_watcher.py 55 2 96%
streamlit/watcher/util.py 49 1 98%
streamlit/web/__init__.py 0 0 100%
streamlit/web/bootstrap.py 138 18 87%
streamlit/web/cache_storage_manager_config.py 5 0 100%
streamlit/web/cli.py 186 17 91%
streamlit/web/server/__init__.py 5 0 100%
streamlit/web/server/app_static_file_handler.py 29 3 90%
streamlit/web/server/authlib_tornado_integration.py 18 1 94%
streamlit/web/server/bidi_component_request_handler.py 65 8 88%
streamlit/web/server/browser_websocket_handler.py 115 31 73%
streamlit/web/server/component_file_utils.py 24 0 100%
streamlit/web/server/component_request_handler.py 55 4 93%
streamlit/web/server/media_file_handler.py 65 9 86%
streamlit/web/server/oauth_authlib_routes.py 118 18 85%
streamlit/web/server/oidc_mixin.py 44 0 100%
streamlit/web/server/routes.py 90 7 92%
streamlit/web/server/server.py 188 11 94%
streamlit/web/server/server_util.py 67 5 93%
streamlit/web/server/stats_request_handler.py 53 4 92%
streamlit/web/server/upload_file_request_handler.py 53 9 83%
streamlit/web/server/websocket_headers.py 19 1 95%
TOTAL 20057 1440 93%

📊 View detailed coverage comparison

Base automatically changed from 10-19-create_e2e_tests_for_common_appnode_scenarios_not_particularly_tested to develop October 27, 2025 22:54
*/
export const NO_SCRIPT_RUN_ID = "NO_SCRIPT_RUN_ID"

/**
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's great that this also adds more documentation and explanation of how this works 🙌

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess this isn't new! But it is more noticeable now 😆

public static empty(
mainScriptHash = "",
isInitialRender = true,
sidebarElements?: BlockNode,
Copy link
Collaborator

Choose a reason for hiding this comment

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

[suggestion] It could add some helpful documentation to indicate under which circumstances we would initialize the AppRoot with a sidebar sub-tree, and when this might be empty.

Copy link
Collaborator

@sfc-gh-lwilby sfc-gh-lwilby left a comment

Choose a reason for hiding this comment

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

LGTM -- it took me a bit to realize that this is purely just moving the code, right? I was not familiar enough with the original code.

Copy link
Collaborator

@mayagbarnes mayagbarnes left a comment

Choose a reason for hiding this comment

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

Checked for expected code/comments transferred into respective files, LGTM 👍🏼
Two little things:

  • Looks like the e2e tests/snaps from merged PR also still in the diff here
  • Should render-tree be RenderTree in file names? (ex: frontend/lib/src/render-tree/AppNode.interface.ts)

Copy link
Contributor

@sfc-gh-nbellante sfc-gh-nbellante left a comment

Choose a reason for hiding this comment

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

One q, but otherwise lgtm.

@kmcgrady kmcgrady force-pushed the 10-18-break_up_appnode_into_separate_files branch from 19b19a3 to d469706 Compare October 28, 2025 23:26
@kmcgrady
Copy link
Collaborator Author

Should render-tree be RenderTree in file names? (ex: frontend/lib/src/render-tree/AppNode.interface.ts)

@mayagbarnes to answer this question, I am perhaps thinking back to the frontend split work, and I can imagine a library called @streamlit/render-tree which houses all of this code properly, which is why I got to this naming scheme. Of course, that has been less necessity, but the goal will be to keep this folder relatively independent to the other code (and avoid intertwining)

@kmcgrady kmcgrady merged commit 0468e85 into develop Oct 29, 2025
38 checks passed
@kmcgrady kmcgrady deleted the 10-18-break_up_appnode_into_separate_files branch October 29, 2025 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:refactor PR contains code refactoring without behavior change impact:internal PR changes only affect internal code security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants