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
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 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.