Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@akramsystems
Copy link
Contributor

@akramsystems akramsystems commented Oct 12, 2024

Describe your changes

This PR adds a new configuration option server.folderWatchList to allow users to specify a custom path to watch for file changes. Currently, Streamlit only supports watching the directory of the main script or excluding files via a blacklist, but this change provides the flexibility to monitor external directories or specific folders. Additionally, logic has been added to ensure the new path is recursively watched for file changes, and tests have been updated to cover this feature.

Note: This is my first time doing a PR to open source, so any feedback would be appreciated.

GitHub Issue Link (if applicable)

Issue Number: #9655

Testing Plan

  • Unit tests were added to local_sources_watcher_test.py to ensure the custom watch path feature behaves as expected.

MANUAL TEST

streamlit run app/hello.py --server.runOnSave=true --server.folderWatchList=<BASE_PATH>/test_folder --logger.level=debug

...
...
├── app/
│   ├── hello.py
└── test_folder
    └── test_file.txt

app/hello.py

import streamlit as st

st.write("Hello, World!")

with open("test_folder_1/test_file.txt", "r") as f:
    st.write("test_folder_1/test_file.txt")
    st.write(f.read())

with open("test_folder_2/test_file.txt", "r") as f:
    st.write("test_folder_2/test_file.txt")
    st.write(f.read())

.streamlit/config.toml

[server]
folderWatchList = [
    "test_folder_1",
    "test_folder_2"
]
runOnSave = true

Note: the list of folders should be absolute paths currently, thought this is a good first step and then we can add regex search or relative paths in another PR if possible, since smaller PR's are more manageable/

Contribution License Agreement

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

@jrieke
Copy link
Collaborator

jrieke commented Nov 4, 2024

Hey, this is awesome, thanks for the PR! We wanted something like this for a long time. I think it would be great to align this a bit more to the existing folderWatchBlacklist by a) accepting a list of folders instead of just a single path and b) renaming the parameter to folderWatchList (we could also do folderWatchWhitelist but I think that's unnecessarily complicated). What do you think?

@akramsystems
Copy link
Contributor Author

akramsystems commented Nov 11, 2024

@jrieke for sure I'll implement that today, sorry I was busy during the week! I can definitely do that, and yes i agree using the name folderWatchWhitelist is too long and unnecessary.

@akramsystems
Copy link
Contributor Author

akramsystems commented Nov 11, 2024

@jrieke updated the PR take a look note i removed the need to add the custom_watch_path to the EventsBasedWatcher since that code wasn't needed or used .

@akramsystems akramsystems changed the title added server.customWatchPath to cli + LocalSourcesWatcher added server.FolderWatchList to cli + LocalSourcesWatcher Nov 11, 2024
@jrieke jrieke added security-assessment-completed Security assessment has been completed for PR impact:users PR changes affect end users change:feature PR contains new feature or enhancement implementation labels Nov 14, 2024
@jrieke
Copy link
Collaborator

jrieke commented Nov 14, 2024

Sweet! I just triggered the test runs, let's see if there are any issues. And I also pinged our engineers to give this a proper review.

@akramsystems
Copy link
Contributor Author

And I also pinged our engineers to give this a proper review.

Awesome I'll await their feedback, also I update the branch when ever it is behind which requires the test to be run again but i seen that 22 passed and there were no errors. Feel free to trigger it again if you like, and thanks again for being proactive with this PR

@kmcgrady kmcgrady self-assigned this Nov 18, 2024
Copy link
Collaborator

@kmcgrady kmcgrady left a comment

Choose a reason for hiding this comment

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

Hey @akramsystems! Thank you for creating a PR. I realized my review didn't send out, so apologies for the delay. Overall things are looking good, I provided a couple comments on where we can tighten things up and potentially reduce memory/process overhead. Would you mind taking a look and let me know if it helps us?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We probably should be validating that the folder is a directory otherwise the is_directory will be not valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for sure will add this check

Copy link
Collaborator

Choose a reason for hiding this comment

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

We do have logic that walks a full directory. https://github.com/streamlit/streamlit/blob/develop/lib/streamlit/watcher/path_watcher.py#L143

It seems to just create a watch on the path, and that can allow for . https://github.com/streamlit/streamlit/blob/develop/lib/streamlit/watcher/path_watcher.py#L119

Perhaps we can just do a watch on the path itself. Perhaps we can modify _register_watcher. We can test if the glob pattern needs to be set. This would reduce watchers generated and perhaps simplify the logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for sure i'll look at incorporating the watch_dir function and try to use the glob pattern to make watching paths easier

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a seperate function for this as I didn't want to interfere with existing logic which uses the on_file_changed callback, and make it more explicit that this callback can be called for changes to directories

Copy link
Collaborator

Choose a reason for hiding this comment

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

I didn't need to create this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed this function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as requested using the watch_dir with the usage of glob_pattern to search all subdirectories and types of files to walk the directory files

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this will simplify if you update the entire try block to just be this.

kwargs = {}
if is_directory:
    kwargs["glob_pattern"] = "**/*"

wm = WatchedModule(
    watcher=PathWatcher(filepath, self.on_file_changed, **kwargs),
    module_name=module_name,
)
self._watched_modules[filepath] = wm

Apologies if I was confusing, but my comment was on watch_dir was to borrow the logic from it. Looking at the function, it's simply creating a watcher with the glob pattern, and we already have the watcher, so we just need to specify the glob pattern if it's a directory. self.on_file_changed will have the correct file path because the watcher will supply the directory path, and not the actual file that changed. That's fine with us because we just want to rerun the page if we detect any change.

As an additional cleanup, I might consider modifying the name of on_file_changed to on_path_changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

applied this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needed to be done since the event_based_path_watcher only checks for abs_changed_paths, this approach checks if the path that has changed is a common ancestor of the parent path. making it simple to check for for changes to a directory no matter the file under the directory

Copy link
Collaborator

Choose a reason for hiding this comment

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

So I was able to get everything working with removing this code. Seems to work fine in my tests.

Copy link
Contributor Author

@akramsystems akramsystems Mar 17, 2025

Choose a reason for hiding this comment

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

@kmcgrady I can remove the check for if changed_path_info is None, but if I remove the for loop going over the watched_paths.items()and checking if they share common path then it fails to update the app there is a change in the directories being watched.

Copy link
Contributor Author

@akramsystems akramsystems left a comment

Choose a reason for hiding this comment

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

@kmcgrady I made some small changes (and left some comments) let me know your thoughts, or if this was what you had in mind. Your feedback is greatly appreciated! 👍🏽

Copy link
Collaborator

@kmcgrady kmcgrady left a comment

Choose a reason for hiding this comment

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

Apologies for the delay. I'm working hard to wrap up some stuff before the holidays occur.

I provided some comments with suggested changes. I think we can simplify this a lot more. Can you check to see if the changes work for your use cases. I did some testing of editing files in a directory, and having the script rerun successfully for both Event and Polling Strategy.

I am around this week if you want to talk more. After that, I go on vacation, and will resume reviewing in the new year.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You shouldn't need to specify the type_. I think it's a little weird, but I think it should be str because the values are strings, but we will accept multiple strings for the paths. The fact that the default val is a list hints at taking multiple values, and str is inferred by default.

Copy link
Collaborator

Choose a reason for hiding this comment

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

So I was able to get everything working with removing this code. Seems to work fine in my tests.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I didn't need to create this function.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this will simplify if you update the entire try block to just be this.

kwargs = {}
if is_directory:
    kwargs["glob_pattern"] = "**/*"

wm = WatchedModule(
    watcher=PathWatcher(filepath, self.on_file_changed, **kwargs),
    module_name=module_name,
)
self._watched_modules[filepath] = wm

Apologies if I was confusing, but my comment was on watch_dir was to borrow the logic from it. Looking at the function, it's simply creating a watcher with the glob pattern, and we already have the watcher, so we just need to specify the glob pattern if it's a directory. self.on_file_changed will have the correct file path because the watcher will supply the directory path, and not the actual file that changed. That's fine with us because we just want to rerun the page if we detect any change.

As an additional cleanup, I might consider modifying the name of on_file_changed to on_path_changed.

@kmcgrady
Copy link
Collaborator

kmcgrady commented Jan 6, 2025

Hey @akramsystems Just following up to ask if you plan to continue working on this PR and if the feedback given makes sense. Let me know! :-)

@akramsystems
Copy link
Contributor Author

akramsystems commented Jan 6, 2025

@kmcgrady yes I plan on continuing to work on this PR, sorry i had a surgery in the end of the year so that caused me to delay some of my work. Will continue the grind! Yes your comments make sense i will update the PR

@kmcgrady
Copy link
Collaborator

kmcgrady commented Jan 6, 2025

I hope your surgery went well and a speedy recovery. Let me know how I can help!

@kmcgrady kmcgrady changed the title added server.FolderWatchList to cli + LocalSourcesWatcher [WIP] added server.FolderWatchList to cli + LocalSourcesWatcher Jan 27, 2025
@kmcgrady
Copy link
Collaborator

Hey @akramsystems I updated the title with [WIP] for Work in Progress. It provides a way for us to filter PRs awaiting reviews. Once you have made changes and feel ready for review, feel free to just remove the [WIP] and it will pop back on my list to review.

@N-Demir
Copy link

N-Demir commented Jan 30, 2025

+1 this is an awesome addition!

@sfc-gh-lmasuch sfc-gh-lmasuch marked this pull request as draft February 2, 2025 16:30
@eliasecchig
Copy link

+1 on this!

@snyk-io
Copy link
Contributor

snyk-io bot commented Mar 15, 2025

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

security/snyk check is complete. No issues have been found. (View Details)

license/snyk check is complete. No issues have been found. (View Details)

@akramsystems akramsystems marked this pull request as ready for review March 17, 2025 17:33
@akramsystems
Copy link
Contributor Author

@kmcgrady ready for re-review I applied all but one of your recommendations

@akramsystems akramsystems requested a review from kmcgrady March 31, 2025 16:51
@kmcgrady
Copy link
Collaborator

@akramsystems Seems like mypy passed just fine, but there seems to be a python test failure.

@akramsystems akramsystems force-pushed the gh-9655/custom-watch-path branch from 8a5e615 to 8367a4c Compare April 27, 2025 04:48
@akramsystems akramsystems requested a review from a team as a code owner April 27, 2025 04:48
akramsystems and others added 10 commits April 27, 2025 00:57
…s under the folder to PathWatcher for the LocalSourcesWatcher
…cepting a list of folder paths instead of a single path to watch
…witched to using watch_dir with glob_pattern to easily walk directory + using a new callback to trigger changes within directories specifically + updated events based path watcher to take account for triggering and event to fire the callback function when a directory is changed + updated test to reflect new logic
Trying to set a record for the smallest code contribution to Streamlit. 
In an ironic twist, I completely ignored the very instructions I'm
updating and skipped creating an issue first.

## Describe your changes
Fixing a typo in the README.md file: 
Ff -> If.
Tiny change. Big impact. 

## GitHub Issue Link (if applicable)
Taking the scenic route and skipping the issue for this monumental PR.

## Testing Plan

- Visual inspection by reviewer.
- Reflecting on the irony of fixing instructions I did not follow...

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
@akramsystems akramsystems force-pushed the gh-9655/custom-watch-path branch from 8367a4c to 0bb7daf Compare April 27, 2025 04:58
@akramsystems
Copy link
Contributor Author

@kmcgrady seems like the tests are passing ?

@kmcgrady kmcgrady changed the title [WIP] added server.FolderWatchList to cli + LocalSourcesWatcher added server.FolderWatchList to cli + LocalSourcesWatcher Apr 28, 2025
@kmcgrady kmcgrady merged commit d9c9c99 into streamlit:develop Apr 28, 2025
33 checks passed
@kmcgrady
Copy link
Collaborator

Great work @akramsystems Approved and merged

@akramsystems
Copy link
Contributor Author

HALLALUYAH THANK YOU SO MUCH @kmcgrady

@jrieke
Copy link
Collaborator

jrieke commented May 5, 2025

Wohoo, congrats for getting this merged @akramsystems!

@sfc-gh-jesmith
Copy link

Awesome @akramsystems!
Would love to send ya a lil Streamlit swag to say thanks..feel free to fill out this form if interested. 😊

@adamalton
Copy link

@sfc-gh-jesmith it looks like this was merged to develop on 2025/04/28 (merge commit), and then the next day the 1.45.0 release happened, but didn't contain the change, and then 2 weeks later on 2025/05/12 release 1.45.1 happened, but that also didn't contain this change.

I don't think it's just an issue with the release notes, as doing git tag --contains d9c9c998d1bc14f4c81991ed0c0b7fc363f4e8a4 (the merge commit) gives:

1.44.2.dev20250428
1.45.1.dev20250429
1.45.1.dev20250430
1.45.1.dev20250501
1.45.1.dev20250502
1.45.1.dev20250503
1.45.1.dev20250504
1.45.1.dev20250505
1.45.1.dev20250506
1.45.1.dev20250507
1.45.1.dev20250508
1.45.1.dev20250509
1.45.1.dev20250510
1.45.1.dev20250511
1.45.2.dev20250512
1.45.2.dev20250513
1.45.2.dev20250514
1.45.2.dev20250515
1.45.2.dev20250516
1.45.2.dev20250517
1.45.2.dev20250518
1.45.2.dev20250519
1.45.2.dev20250520
1.45.2.dev20250521
1.45.2.dev20250522
1.45.2.dev20250523
1.45.2.dev20250525
1.45.2.dev20250526

Only dev tags 🤷 .

The change definitely exists in the develop branch, but if I download the source code for the 1.45.1 release, the change is definitely not in there.

Do you know what's happened to this change, and/or when it's going to make it into a release? Thanks! 🙏

@lukasmasuch
Copy link
Collaborator

@adamalton the change will be released in 1.46

@adamalton
Copy link

@lukasmasuch Thank you 🙏

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 introduces the new configuration option server.folderWatchList to allow users to specify additional folders to watch for file changes. Key changes include updates to the file watch API (renaming on_file_changed to on_path_changed), new tests for folder-based watching, and corresponding configuration updates.

Reviewed Changes

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

Show a summary per file
File Description
lib/tests/streamlit/watcher/local_sources_watcher_test.py Updated tests to use the new on_path_changed method and added test_folder_watch_list to verify watcher registration for custom folders.
lib/tests/streamlit/config_test.py Included the new server.folderWatchList option in configuration option keys tests.
lib/streamlit/watcher/local_sources_watcher.py Implemented support for custom watch folders via server.folderWatchList, updated callback naming, and modified _register_watcher accordingly.
lib/streamlit/watcher/event_based_path_watcher.py Adjusted path change event handling to detect file changes within watched directories.
lib/streamlit/config.py Added the new configuration option with an appropriate description.
Comments suppressed due to low confidence (2)

lib/streamlit/watcher/local_sources_watcher.py:180

  • Duplicate assignment to self._watched_modules[filepath] exists after the try block. Removing the redundant assignment would improve code clarity.
        self._watched_modules[filepath] = wm

lib/tests/streamlit/watcher/local_sources_watcher_test.py:526

  • [nitpick] Consider asserting that the watcher for the main script is also registered as expected, not just the folder watchers, to ensure comprehensive test coverage.
        self.assertIn(expected_calls[1], actual_calls)

@akramsystems
Copy link
Contributor Author

Awesome @akramsystems! Would love to send ya a lil Streamlit swag to say thanks..feel free to fill out this form if interested. 😊

Hey sorry for taking a while getting back to you, but yes I just replied to the form :D !

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

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants