def watchfn(reloader: SourceFileReloader):
    """Watch python files in a given module.

    get_changes is taken from uvicorn's default file watcher.
    """

    NO_RELOAD.set(False)

    # The thread running watchfn will be the thread reloading
    # the app. So we need to modify this thread_data attr here
    # so that subsequent calls to reload don't launch the app
    from gradio.cli.commands.reload import reload_thread

    reload_thread.running_reload = True

    def get_changes() -> Path | None:
        for file in iter_py_files():
            try:
                mtime = file.stat().st_mtime
            except OSError:  # pragma: nocover
                continue

            old_time = mtimes.get(file)
            if old_time is None:
                mtimes[file] = mtime
                continue
            elif mtime > old_time:
                return file
        return None

    def iter_py_files() -> Iterator[Path]:
        for reload_dir in reload_dirs:
            for path in list(reload_dir.rglob("*.py")):
                yield path.resolve()
            for path in list(reload_dir.rglob("*.css")):
                yield path.resolve()

    reload_dirs = [Path(dir_) for dir_ in reloader.watch_dirs]
    import sys

    for dir_ in reload_dirs:
        sys.path.insert(0, str(dir_))

    mtimes = {}
    # Need to import the module in this thread so that the
    # module is available in the namespace of this thread
    module = reloader.watch_module
    no_reload_source_code = _remove_if_name_main_codeblock(str(reloader.demo_file))
    # Reset the context to id 0 so that the loaded module is the same as the original
    # See https://github.com/gradio-app/gradio/issues/10253
    from gradio.context import Context

    Context.id = 0
    exec(no_reload_source_code, module.__dict__)
    sys.modules[reloader.watch_module_name] = module

    while reloader.should_watch():
        changed = get_changes()
        if changed:
            print(f"Changes detected in: {changed}")
            try:
                # How source file reloading works
                # 1. Remove the gr.no_reload code blocks from the temp file
                # 2. Execute the changed source code in the original module's namespac
                # 3. Delete the package the module is in from sys.modules.
                # This is so that the updated module is available in the entire package
                # 4. Do 1-2 for the main demo file even if it did not change.
                # This is because the main demo file may import the changed file and we need the
                # changes to be reflected in the main demo file.

                if changed.suffix == ".py":
                    changed_in_copy = _remove_if_name_main_codeblock(str(changed))
                    if changed != reloader.demo_file:
                        changed_module = _find_module(changed)
                        if changed_module:
                            exec(changed_in_copy, changed_module.__dict__)
                            top_level_parent = sys.modules[
                                changed_module.__name__.split(".")[0]
                            ]
                            if top_level_parent != changed_module:
                                importlib.reload(top_level_parent)

                changed_demo_file = _remove_if_name_main_codeblock(
                    str(reloader.demo_file)
                )
                exec(changed_demo_file, module.__dict__)
            except Exception as error:
                print(
                    f"Reloading {reloader.watch_module_name} failed with the following exception: "
                )
                if not isinstance(error, Error) or error.print_exception:
                    traceback.print_exc()
                mtimes = {}
                reloader.alert_change("error")
                reloader.app.reload_error_message = traceback.format_exc()
                continue
            demo = getattr(module, reloader.demo_name)
            reloader.swap_blocks(demo)
            mtimes = {}
        time.sleep(0.05)
Watch python files in a given module.

    get_changes is taken from uvicorn's default file watcher.
    
gradio/utils.py