python
60 lines · 7 steps
A hot-reloading config file watcher
A Settings class loads JSON config and watches its file to reload automatically on change, guarded by a lock for thread safety.
Explained by
highlit
1import json
2import logging
3import threading
4from pathlib import Path
5
6from watchdog.events import FileSystemEventHandler
7from watchdog.observers import Observer
8
9logger = logging.getLogger(__name__)
10
11
12class Settings:
13 def __init__(self, path):
14 self._path = Path(path).resolve()
15 self._lock = threading.RLock()
16 self._values = {}
17 self._reload()
18
19 self._observer = Observer()
20 self._observer.schedule(
21 _ConfigReloadHandler(self),
22 str(self._path.parent),
23 recursive=False,
24 )
25 self._observer.start()
26
27 def get(self, key, default=None):
28 with self._lock:
29 return self._values.get(key, default)
30
31 def as_dict(self):
32 with self._lock:
33 return dict(self._values)
34
35 def _reload(self):
36 try:
37 raw = self._path.read_text(encoding="utf-8")
38 parsed = json.loads(raw)
39 except (OSError, json.JSONDecodeError) as exc:
40 logger.warning("Ignoring bad config %s: %s", self._path, exc)
41 return
42
43 with self._lock:
44 self._values = parsed
45 logger.info("Reloaded config from %s", self._path)
46
47 def stop(self):
48 self._observer.stop()
49 self._observer.join()
50
51
52class _ConfigReloadHandler(FileSystemEventHandler):
53 def __init__(self, settings):
54 self._settings = settings
55
56 def on_modified(self, event):
57 if not event.is_directory and Path(event.src_path).resolve() == self._settings._path:
58 self._settings._reload()
59
60 on_created = on_modified
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A reentrant lock keeps reads and writes consistent while a background thread swaps config in.
- 2Filesystem watchers let you reload config without restarting the process.
- 3Failing safe on parse errors keeps a running app alive despite a broken config file.
Related explainers
javascript
const legacyRedirects = [ { from: '/blog/:slug', to: '/articles/:slug' }, { from: '/shop/product/:id', to: '/store/items/:id' }, { from: '/about-us', to: '/about' },
How redirects work in Next.js config
redirects
routing
configuration
Intermediate
7 steps
python
import heapq class MovingMedian:
Running median with two heaps
heaps
streaming
invariants
Advanced
8 steps
python
class Parser: def __init__(self, text): self.tokens = self._tokenize(text) self.pos = 0
A recursive descent arithmetic parser
recursive-descent
tokenizer
operator-precedence
Intermediate
9 steps
typescript
import { InjectionToken, inject, Provider, isDevMode } from '@angular/core'; import { WINDOW } from './window.token'; export interface AnalyticsConfig {
Layered config with an Angular InjectionToken
dependency-injection
configuration
factory-provider
Intermediate
8 steps
java
public final class CircuitBreaker { private enum State { CLOSED, OPEN, HALF_OPEN }
How a circuit breaker guards failing calls
state-machine
resilience
concurrency
Advanced
7 steps
python
import re from dataclasses import dataclass, field
Building a table of contents from Markdown
regular-expressions
parsing
slugification
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-hot-reloading-config-file-watcher-explained-python-eaa1/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.