Skip to content

fix: watch files that do not exist yet - #408

Merged
JarbasAl merged 1 commit into
devfrom
fix/filewatcher-not-yet-existing-file
Jul 24, 2026
Merged

fix: watch files that do not exist yet#408
JarbasAl merged 1 commit into
devfrom
fix/filewatcher-not-yet-existing-file

Conversation

@JarbasAl

Copy link
Copy Markdown
Member

Summary

FileWatcher.__init__ chose file-mode vs directory-mode with os.path.isfile(file_path). A path that does not exist yet is not a file, so it fell through to directory mode: watch_dir became the nonexistent path (which watchdog cannot schedule an observer on) and the handler ran unfiltered instead of being scoped to that single path.

Practical effect: FileWatcher(["/some/dir/config.json"], cb) could not detect the file being created later, even with the default ignore_creation=False whose whole point is to report created events. Callers were forced to either pre-create the file or watch the entire containing directory.

This is exactly what ovos-config PR #194 hit: it switched to watching specific config files, and a test that registers the watcher before writing mycroft.conf stopped firing, because the not-yet-existing file was silently dropped into directory mode.

Fix

Dispatch on os.path.isdir() instead of os.path.isfile():

  • an existing directory → directory mode, unchanged behavior
  • anything else (an existing file, or a path that doesn't exist yet) → file mode: watch the parent directory, filter to that single path. This is safe because of the per-file filtering added in fix: FileEventHandler must only fire for the file it watches #406 — the handler watches the parent dir but only fires for its own path.

If the parent directory also doesn't exist, watchdog can't schedule an observer on it either. Rather than let an opaque watchdog traceback propagate, that entry is skipped with LOG.warning.

Consumers re-checked

  • ovos-workshop skill_launcher.py: FileWatcher([self.skill_directory], ...) — existing directory, still lands in directory mode. Unaffected.
  • ovos-microphone-plugin-files: FileWatcher([self.files_folder], ...) — same, unaffected.

This unblocks downstream consumers (e.g. ovos-config) that need to watch specific config files which may not exist yet at startup.

Test plan

  • Path that doesn't exist yet is watched in file mode; a created event for it fires the callback, while a different file created in the same directory does not
  • Existing directory still watched in directory mode, fires for any file inside (pre-existing test, still passing)
  • Existing file still fires only for itself (pre-existing test, still passing)
  • Missing parent directory: entry is skipped with LOG.warning, no exception raised
  • Full suite: 935 passed (up from 933 baseline), same 2 pre-existing unrelated failures in test_log_parser.py::TestOvosLogsCLINoStrayFiles

FileWatcher dispatched file-vs-directory mode with os.path.isfile(),
so a path that doesn't exist yet fell into directory mode: watchdog
was asked to schedule an observer on the nonexistent path (which
fails) and the handler ran unfiltered. Callers watching a config file
that is created later (eg. ovos-config watching mycroft.conf before it
exists) never got the 'created' event.

Dispatch on os.path.isdir() instead: an existing directory keeps
directory mode unchanged; anything else (an existing file, or a path
that doesn't exist yet) is file mode, watching the parent directory
and filtering to that single path - safe because of the per-file
filtering added in #406.

If the parent directory also doesn't exist, watchdog can't schedule an
observer on it either; skip that entry with a LOG.warning instead of
letting an opaque watchdog exception propagate.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@JarbasAl, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7540e6b6-34bb-47bf-9e13-8571b7593e16

📥 Commits

Reviewing files that changed from the base of the PR and between 756a011 and f813ddc.

📒 Files selected for processing (2)
  • ovos_utils/file_utils.py
  • test/unittests/test_file_utils.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/filewatcher-not-yet-existing-file

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the fix label Jul 24, 2026
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Processing sequence 0x4F564F53 complete! 🦾

I've aggregated the results of the automated checks for this PR below.

📋 Repo Health

A detailed health report for the project. 📝

✅ All required files present.

Latest Version: 0.13.8a1

ovos_utils/version.py — Version file
README.md — README
LICENSE — License file
pyproject.toml — pyproject.toml
⚠️ setup.py — setup.py
CHANGELOG.md — Changelog
ovos_utils/version.py has valid version block markers

🏷️ Release Preview

I've checked the release assets for completeness. 💾

Current: 0.13.8a1Next: 0.13.9a1

Signal Value
Label (none)
PR title fix: watch files that do not exist yet
Bump build

✅ PR title follows conventional commit format.


🚀 Release Channel Compatibility

Predicted next version: 0.13.9a1

Channel Status Note Current Constraint
Stable Too new (must be <0.9.0) ovos-utils>=0.8.1,<0.9.0
Testing Too new (must be <0.8.5) ovos-utils>=0.8.4,<0.8.5
Alpha Compatible ovos-utils>=0.13.7a1

⚖️ License Check

Verifying that everything is above board legally. ⚓

✅ No license violations found.

Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed.

🔍 Lint

I've finished the digital walk-through of your PR. 🚶‍♂️

ruff: issues found — see job log

🔒 Security (pip-audit)

I've audited the packages. Safety first! 🦺

✅ No known vulnerabilities found (47 packages scanned).

📊 Coverage

Mapping out the 'known' vs 'unknown' in your code. 🗺️

85.2% total coverage

Files below 80% coverage (5 files)
File Coverage Missing lines
ovos_utils/log_parser.py 48.4% 225
ovos_utils/__init__.py 63.6% 16
ovos_utils/file_utils.py 74.4% 56
ovos_utils/thread_utils.py 76.9% 12
ovos_utils/geolocation.py 78.4% 22

Full report: download the coverage-report artifact.

🔨 Build Tests

The build pipeline has finished its work. 🏁

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

The silent guardian of the dev branch. 🦇

@JarbasAl
JarbasAl marked this pull request as ready for review July 24, 2026 14:36
@JarbasAl
JarbasAl merged commit 5455945 into dev Jul 24, 2026
14 checks passed
@JarbasAl
JarbasAl deleted the fix/filewatcher-not-yet-existing-file branch July 24, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant