A more pythonic way to access the Windows registry as winreg.
Wraps Python's winreg module with a high-level Registry class providing
connection caching, automatic type detection, recursive key operations, and
a context manager. On non-Windows platforms, fake_winreg provides a
simulated registry so tests and code run everywhere.
- Pythonic
Registryclass with context manager support - Automatic registry value type detection (str, int, bytes, list)
- Connection and key handle caching for performance
- Recursive key creation (
parents=True) and deletion (delete_subkeys=True) - Remote computer registry access
- SID enumeration and username resolution
- Save/load registry subtrees to/from files
- WOW64 32/64-bit registry view support
- Registry reflection control
- Cross-platform via
fake_winregon Linux/macOS - CLI entry point with rich-click styling
pip install lib_registryOr with uv (recommended):
uv pip install lib_registryFor all installation methods (pipx, uvx, poetry, etc.), see INSTALL.md.
- Targets Python 3.10 and newer
- Runtime dependencies:
rich-clickfor CLI output,rtomlfor TOML parsing,fake_winregon non-Windows platforms - CI covers CPython 3.10 through 3.15
This repository commits a uv.lock. It pins every package the project needs, transitive
dependencies included, to an exact version and to the hashes of that version's files. The
exclude-newer setting under [tool.uv] in pyproject.toml adds a cooldown: uv leaves any
release younger than that out of the lock. CI installs from the lock, so a hijacked release that
gets pulled again within the cooldown never reaches our builds, and a file whose hash does not
match is refused.
That protection stays in this repository. The installable package declares only lower bounds
(>=) for its dependencies, so a plain pip install takes the newest release of every transitive
dependency at the moment you run it. If one of them was hijacked an hour earlier, you get the
hijacked version. pipx install and uv tool install without options do the same.
To install the command line tool with a cooldown of your own (uv then skips any release younger than 7 days, transitive ones included), or to run the exact tree our CI tests:
uv tool install --exclude-newer "7 days" lib_registry
git clone https://github.com/bitranox/lib_registry.git && cd lib_registry && uv sync --locked && uv run lib_registry --helpIf you depend on lib_registry in your own project, give that project the same cooldown (7 days here, as
an example) and commit the uv.lock that uv add lib_registry writes:
[tool.uv]
exclude-newer = "7 days"import lib_registry
# Create a registry accessor
registry = lib_registry.Registry()
# Check if a key exists
registry.key_exist("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")
# Read a value
build = registry.get_value(
"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"CurrentBuild",
)
# Create a key and write values
registry.create_key("HKCU\\Software\\MyApp", parents=True)
registry.set_value("HKCU\\Software\\MyApp", "Setting", "value")
registry.set_value("HKCU\\Software\\MyApp", "Count", 42)
# Iterate subkeys and values
for subkey in registry.subkeys("HKEY_USERS"):
print(subkey)
for name, data, reg_type in registry.values("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"):
print(f"{name} = {data}")
# Save and load registry subtrees
registry.save_key("HKCU\\Software\\MyApp", "/tmp/myapp_backup.json")
registry.load_key(lib_registry.winreg.HKEY_CURRENT_USER, "Software\\MyAppCopy", "/tmp/myapp_backup.json")
# Clean up
registry.delete_key("HKCU\\Software\\MyApp", delete_subkeys=True)
# SID / user operations (no try/except needed — .DEFAULT handled gracefully)
for sid in registry.sids():
print(f"{sid} -> {registry.username_from_sid(sid)}")
# Reverse lookup: find SID by username
sid = registry.sid_from_username("alice")
# Use as context manager (handles closed automatically)
with lib_registry.Registry("HKLM") as reg:
info = reg.key_info("HKLM\\SOFTWARE")Both backslashes and forward slashes are accepted in registry paths:
# These are equivalent:
registry.key_exist("HKLM\\SOFTWARE\\Microsoft")
registry.key_exist("HKLM/SOFTWARE/Microsoft")All commands accept registry paths with either backslashes (\) or forward
slashes (/). Hive names are case-insensitive (HKLM, hklm,
HKEY_LOCAL_MACHINE all work). Run any command with -h for built-in help.
lib_registry [OPTIONS] COMMAND [ARGS]...
| Option | Default | Description |
|---|---|---|
--version |
Print version and exit | |
--traceback/--no-traceback |
on | Show full Python traceback on errors |
-q, --quiet |
off | Suppress non-error output |
--json |
off | Emit JSON output (for scripting) |
--computer HOST |
Connect to remote computer registry | |
-h, --help |
Show help and exit |
Also available as python -m lib_registry.
lib_registry infoPrint resolved package metadata (name, version, homepage, author).
lib_registry get [OPTIONS] KEY VALUE_NAME| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path (e.g. HKLM/SOFTWARE/...) |
VALUE_NAME |
yes | Name of the value to read (omit for default) |
--type |
no | Also display the REG_* type alongside the data |
--default |
no | Read the unnamed default value |
Examples:
lib_registry get "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion" CurrentBuild
# Output: 19045
lib_registry get --type "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion" CurrentBuild
# Output: REG_SZ: 19045lib_registry set [OPTIONS] KEY VALUE_NAME DATA| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
VALUE_NAME |
yes | Name of the value to write |
DATA |
yes | Value data as string (converted based on --type) |
--type TYPE |
no | Registry type. One of: REG_SZ, REG_DWORD, REG_QWORD, REG_BINARY, REG_MULTI_SZ, REG_EXPAND_SZ, REG_NONE, etc. Without --type, data is always stored as REG_SZ (string). Use --type REG_DWORD explicitly for integers. |
--default |
no | Write the unnamed default value (VALUE_NAME ignored) |
Type conversion rules:
--type |
DATA is parsed as | Example |
|---|---|---|
| (omitted) | always string (REG_SZ) | 42 -> REG_SZ, hello -> REG_SZ |
REG_SZ |
string | "hello world" |
REG_DWORD |
integer (32-bit) | 42 |
REG_QWORD |
integer (64-bit) | 9999999999 |
REG_BINARY |
UTF-8 encoded bytes | "raw data" |
REG_MULTI_SZ |
\0-delimited string list |
"line1\0line2\0line3" |
REG_NONE |
None (empty) | (DATA ignored) |
Examples:
lib_registry set HKCU/Software/MyApp Setting "hello world"
lib_registry set HKCU/Software/MyApp Count 42 --type REG_DWORD
lib_registry set HKCU/Software/MyApp Paths "C:\bin\0D:\tools" --type REG_MULTI_SZSilent on success (exit code 0).
lib_registry delete-value KEY VALUE_NAME| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
VALUE_NAME |
yes | Name of the value to delete |
--force |
no | Suppress error if value does not exist |
Silent on success.
lib_registry list [OPTIONS] KEY| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
--keys |
no | Show subkeys only |
--values |
no | Show values only |
--recursive, -r |
no | Recurse into subkeys |
Without flags, shows both subkeys and values. Output format:
[KEY] SubkeyName
[REG_SZ] ValueName = data
[REG_DWORD] Count = 42
Examples:
lib_registry list HKEY_USERS
lib_registry list --keys HKEY_USERS
lib_registry list --values "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion"lib_registry exists KEY| Argument | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
Produces no output. Exit code 0 if the key exists, 1 if it does not. Designed for shell scripting:
if lib_registry exists HKCU/Software/MyApp; then
echo "Key exists"
filib_registry create-key [OPTIONS] KEY| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path to create |
--parents |
no | Create intermediate parent keys if missing |
Silent on success. Without --parents, fails if the parent key does not exist.
Examples:
lib_registry create-key HKCU/Software/MyApp
lib_registry create-key HKCU/Software/MyApp/Deep/Nested/Key --parentslib_registry delete-key [OPTIONS] KEY| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path to delete |
--recursive |
no | Also delete all subkeys and their values recursively |
--force |
no | Suppress error if key does not exist |
Silent on success. Without --recursive, fails if the key has subkeys.
Examples:
lib_registry delete-key HKCU/Software/MyApp
lib_registry delete-key HKCU/Software/MyApp --recursivelib_registry export KEY FILE| Argument | Required | Description |
|---|---|---|
KEY |
yes | Registry key path to export |
FILE |
yes | Output file path (JSON format) |
Saves the key and all its subkeys/values to a JSON file. Use import
to restore.
lib_registry export HKCU/Software/MyApp backup.jsonlib_registry import KEY SUB_KEY FILE| Argument | Required | Description |
|---|---|---|
KEY |
yes | Hive or parent key (e.g. HKCU) |
SUB_KEY |
yes | Subkey path under KEY where data will be loaded |
FILE |
yes | Input file path (JSON, as produced by export) |
lib_registry import HKCU Software/MyAppCopy backup.jsonlib_registry search [OPTIONS] KEY| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path to search under (recursive) |
--name PATTERN |
no | Glob pattern to match value names (e.g. "Install*") |
--data PATTERN |
no | Glob pattern to match value data (e.g. "*Microsoft*") |
Recursively walks all subkeys under KEY and prints matching values.
At least one of --name or --data should be specified.
Output format:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion [REG_SZ] CurrentBuild = 19045
Examples:
lib_registry search "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion" --name "Current*"
lib_registry search HKLM/SOFTWARE --data "*Python*"lib_registry usersNo arguments. Enumerates SIDs from the ProfileList and resolves usernames. Output is a formatted table:
Registry Users
+-----------------------+----------+
| SID | Username |
+-----------------------+----------+
| S-1-5-21-...-1000 | alice |
| S-1-5-18 | SYSTEM |
+-----------------------+----------+
lib_registry sid SID| Argument | Required | Description |
|---|---|---|
SID |
yes | Security Identifier to resolve |
Prints the username for the given SID. Handles .DEFAULT gracefully.
lib_registry sid S-1-5-21-1234567890-1234567890-1234567890-1001
# Output: alice
lib_registry sid .DEFAULT
# Output: Default
lib_registry --json sid .DEFAULT
# Output: {"sid": ".DEFAULT", "username": "Default"}lib_registry whoami USERNAME| Argument | Required | Description |
|---|---|---|
USERNAME |
yes | Windows username to look up |
Reverse lookup: finds the SID for the given username (case-insensitive).
lib_registry whoami alice
# Output: S-1-5-21-1234567890-1234567890-1234567890-1001
lib_registry --json whoami alice
# Output: {"username": "alice", "sid": "S-1-5-21-..."}lib_registry tree [OPTIONS] KEY| Argument/Option | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
--depth N |
no | Maximum depth to display (default: 3) |
Displays a Rich tree view of the key hierarchy. With --json, outputs a
nested dict.
lib_registry tree HKEY_USERS --depth 2
lib_registry --json tree HKLM/SOFTWARE --depth 1lib_registry copy [OPTIONS] SRC_KEY DST_KEY| Argument/Option | Required | Description |
|---|---|---|
SRC_KEY |
yes | Source key path |
DST_KEY |
yes | Destination key path (created if needed) |
--recursive |
no | Also copy all subkeys recursively |
Copies all values from SRC_KEY to DST_KEY. With --recursive, copies the
entire subtree.
lib_registry copy HKCU/Software/MyApp HKCU/Software/MyAppBackup --recursivelib_registry rename KEY OLD_NAME NEW_NAME| Argument | Required | Description |
|---|---|---|
KEY |
yes | Registry key path |
OLD_NAME |
yes | Current value name |
NEW_NAME |
yes | New value name |
Renames a registry value by copying its data and type to the new name, then deleting the old name. Atomic within a single key.
lib_registry rename HKCU/Software/MyApp OldSetting NewSettinglib_registry diff KEY_A KEY_B| Argument | Required | Description |
|---|---|---|
KEY_A |
yes | First key path to compare |
KEY_B |
yes | Second key path to compare |
Recursively compares values and subkeys between two registry key subtrees. Shows values only in A, only in B, and values that differ.
lib_registry diff HKCU/Software/MyApp HKCU/Software/MyAppBackup
lib_registry --json diff HKCU/Software/V1 HKCU/Software/V2Output format:
- HKCU\Software\MyApp (only in KEY_A)
+ HKCU\Software\MyAppBackup (only in KEY_B)
~ HKCU\Software\MyApp / Setting
A: old_value
B: new_value
All winreg KEY_* access constants are re-exported for convenience:
from lib_registry import KEY_READ, KEY_WRITE, KEY_ALL_ACCESS, KEY_WOW64_64KEYRegistry(key=None, computer_name=None)Create a registry accessor. Optionally connect to a hive immediately.
Supports context manager (with Registry('HKLM') as reg:).
# Local registry
reg = Registry()
# Connect to hive at creation
reg = Registry("HKLM")
# Remote computer
reg = Registry("HKLM", computer_name="server01")
# Context manager — handles closed automatically
with Registry("HKCU") as reg:
build = reg.get_value("HKCU\\Software\\Microsoft", "Version")Create a registry key and return a handle.
reg.create_key("HKCU/Software/MyApp") # existing key OK
reg.create_key("HKCU/Software/MyApp", exist_ok=False) # raises if exists
reg.create_key("HKCU/Software/A/B/C", parents=True) # create intermediatesCreate with explicit access mask (for WOW64 scenarios).
from lib_registry import KEY_WOW64_64KEY
reg.create_key_ex("HKCU/Software/MyApp", access=KEY_WOW64_64KEY)Delete a key. Fails if key has children unless delete_subkeys=True.
reg.delete_key("HKCU/Software/MyApp") # leaf key only
reg.delete_key("HKCU/Software/MyApp", delete_subkeys=True) # recursive
reg.delete_key("HKCU/Software/Missing", missing_ok=True) # no error if absentDelete with WOW64 view control.
if reg.key_exist("HKLM/SOFTWARE/Python"):
print("Python is installed")Returns (number_of_subkeys, number_of_values, last_modified_win_timestamp).
subkeys, values, ts = reg.key_info("HKLM/SOFTWARE")Unix epoch timestamp (seconds since 1970-01-01).
Windows timestamp (100ns intervals since 1601-01-01).
for name in reg.subkeys("HKEY_USERS"):
print(name)Yields (value_name, value_data, value_type_int) tuples.
for name, data, reg_type in reg.values("HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion"):
print(f"{name} ({reg_type}) = {data}")Read a value's data. Pass None or '' as value_name for the unnamed default value.
build = reg.get_value("HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion", "CurrentBuild")Read data and registry type.
data, reg_type = reg.get_value_ex("HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion", "CurrentBuild")
# reg_type is e.g. winreg.REG_SZ (1)Write a value. Type is auto-detected from the Python type if value_type is None.
| Python type | Auto-detected REG_TYPE |
|---|---|
str |
REG_SZ |
int |
REG_DWORD |
bytes |
REG_BINARY |
list[str] |
REG_MULTI_SZ |
None |
REG_NONE |
reg.set_value("HKCU/Software/MyApp", "Name", "Alice") # REG_SZ
reg.set_value("HKCU/Software/MyApp", "Count", 42) # REG_DWORD
reg.set_value("HKCU/Software/MyApp", "Data", b"\x00\x01") # REG_BINARY
reg.set_value("HKCU/Software/MyApp", "Paths", ["C:\\", "D:\\"]) # REG_MULTI_SZ
reg.set_value("HKCU/Software/MyApp", "Env", "%PATH%", winreg.REG_EXPAND_SZ) # explicit typereg.delete_value("HKCU/Software/MyApp", "Name")Save a key subtree to a file (JSON with fake_winreg, binary hive on Windows).
reg.save_key("HKCU/Software/MyApp", "/tmp/backup.json")Load a subtree from file into a registry location.
reg.load_key(winreg.HKEY_CURRENT_USER, "Software/MyAppCopy", "/tmp/backup.json")Force pending writes to disk (rarely needed).
Explicitly close a cached key handle.
Close all cached key handles and hive connections. Called automatically by __exit__.
Iterate SIDs from the ProfileList.
for sid in reg.sids():
print(sid) # e.g. 'S-1-5-21-...-1001'Resolve a SID to a username. Returns "Default" for .DEFAULT.
reg.username_from_sid("S-1-5-21-...-1001") # 'alice'
reg.username_from_sid(".DEFAULT") # 'Default'Reverse lookup (case-insensitive).
reg.sid_from_username("alice") # 'S-1-5-21-...-1001'
reg.sid_from_username("Default") # '.DEFAULT'Returns True if reflection is disabled.
Expand %VAR% references.
Registry.expand_environment_strings("%USERPROFILE%\\Documents")
# '/home/alice/Documents' (or 'C:\Users\alice\Documents' on Windows)from lib_registry import resolve_key, get_key_as_string, normalize_separators
resolve_key("HKLM/SOFTWARE/Microsoft") # (HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft')
get_key_as_string(winreg.HKEY_LOCAL_MACHINE) # 'HKEY_LOCAL_MACHINE'
normalize_separators("HKLM/SOFTWARE/test") # 'HKLM\\SOFTWARE\\test'RegData = None | bytes | int | str | list[str]All registry values are one of these types.
RegistryError
RegistryConnectionError
RegistryKeyError
RegistryKeyNotFoundError
RegistryKeyExistsError
RegistryKeyCreateError
RegistryKeyDeleteError
RegistryValueError
RegistryValueNotFoundError
RegistryValueDeleteError
RegistryValueWriteError
RegistryHKeyError
RegistryNetworkConnectionError
RegistryHandleInvalidError
from lib_registry import Registry, RegistryKeyNotFoundError
reg = Registry()
try:
reg.get_value("HKCU/Software/NoSuchApp", "key")
except RegistryKeyNotFoundError:
print("Key not found")Re-exported from winreg for convenience:
from lib_registry import (
KEY_READ,
KEY_WRITE,
KEY_ALL_ACCESS,
KEY_WOW64_32KEY,
KEY_WOW64_64KEY,
KEY_QUERY_VALUE,
KEY_SET_VALUE,
KEY_CREATE_SUB_KEY,
KEY_ENUMERATE_SUB_KEYS,
KEY_NOTIFY,
KEY_CREATE_LINK,
KEY_EXECUTE,
)