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.14
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,
)