Metadata-Version: 2.4
Name: fake_winreg
Version: 1.9.1
Summary: A fake Windows registry for testing registry-related code on non-Windows platforms
Project-URL: Homepage, https://github.com/bitranox/fake_winreg
Project-URL: Repository, https://github.com/bitranox/fake_winreg.git
Project-URL: Issues, https://github.com/bitranox/fake_winreg/issues
Author-email: bitranox <bitranox@gmail.com>
License: MIT
License-File: LICENSE
Keywords: fake,linux,mock,registry,testing,windows,winreg
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: lib-cli-exit-tools>=2.3.2
Requires-Dist: lib-layered-config>=5.5.2
Requires-Dist: lib-log-rich>=6.3.5
Requires-Dist: orjson>=3.11.9
Requires-Dist: pydantic>=2.13.4
Requires-Dist: rich-click>=1.9.8
Provides-Extra: dev
Requires-Dist: bandit>=1.9.4; extra == 'dev'
Requires-Dist: build>=1.5.0; extra == 'dev'
Requires-Dist: codecov-cli>=11.2.8; extra == 'dev'
Requires-Dist: httpx2>=2.4.0; extra == 'dev'
Requires-Dist: hypothesis>=6.155.2; extra == 'dev'
Requires-Dist: import-linter>=2.11; extra == 'dev'
Requires-Dist: jaraco-context>=6.1.2; extra == 'dev'
Requires-Dist: pip-audit>=2.10.1; extra == 'dev'
Requires-Dist: pynacl>=1.6.2; extra == 'dev'
Requires-Dist: pyright[nodejs]>=1.1.410; extra == 'dev'
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
Requires-Dist: pytest>=9.1.0; extra == 'dev'
Requires-Dist: python-multipart>=0.0.32; extra == 'dev'
Requires-Dist: rtoml>=0.13.0; extra == 'dev'
Requires-Dist: ruff>=0.15.17; extra == 'dev'
Requires-Dist: textual>=8.2.7; extra == 'dev'
Requires-Dist: twine>=6.2.0; extra == 'dev'
Requires-Dist: urllib3>=2.7.0; extra == 'dev'
Requires-Dist: virtualenv>=21.5.0; extra == 'dev'
Requires-Dist: wheel>=0.47.0; extra == 'dev'
Description-Content-Type: text/markdown

# fake_winreg

<!-- Badges -->
[![CI](https://github.com/bitranox/fake_winreg/actions/workflows/default_cicd_public.yml/badge.svg)](https://github.com/bitranox/fake_winreg/actions/workflows/default_cicd_public.yml)
[![CodeQL](https://github.com/bitranox/fake_winreg/actions/workflows/codeql.yml/badge.svg)](https://github.com/bitranox/fake_winreg/actions/workflows/codeql.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Open in Codespaces](https://img.shields.io/badge/Codespaces-Open-blue?logo=github&logoColor=white&style=flat-square)](https://codespaces.new/bitranox/fake_winreg?quickstart=1)
[![PyPI](https://img.shields.io/pypi/v/fake_winreg.svg)](https://pypi.org/project/fake_winreg/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/fake_winreg.svg)](https://pypi.org/project/fake_winreg/)
[![Code Style: Ruff](https://img.shields.io/badge/Code%20Style-Ruff-46A3FF?logo=ruff&labelColor=000)](https://docs.astral.sh/ruff/)
[![codecov](https://codecov.io/gh/bitranox/fake_winreg/graph/badge.svg?token=UFBaUDIgRk)](https://codecov.io/gh/bitranox/fake_winreg)
[![Maintainability](https://qlty.sh/badges/041ba2c1-37d6-40bb-85a0-ec5a8a0aca0c/maintainability.svg)](https://qlty.sh/gh/bitranox/projects/fake_winreg)
[![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)

## Table of Contents

- [Why fake_winreg?](#why-fake_winreg)
- [Overview](#overview)
- [Installation](#installation)
- [Configuration](#configuration)
- [Quick Start](#quick-start)
  - [Python API](#python-api)
- [API Reference](#api-reference)
  - [Registry Functions](#registry-functions)
  - [Backend Management](#backend-management)
  - [Import / Export](#import--export)
  - [Constants](#constants)
  - [Data Types](#data-types)
  - [Test Registries](#test-registries)
- [CLI Usage](#cli-usage)
- [Development](#development)
- [License](#license)

## Why fake_winreg?

Python's [`winreg`](https://docs.python.org/3/library/winreg.html) module is
only available on Windows. If your code reads or writes the Windows registry,
you can't test it on Linux or macOS - and you can't run it in CI on
Ubuntu runners.  
`fake_winreg` solves this by providing a complete fake
registry that works everywhere Python runs. Just replace `import winreg`
with `import fake_winreg as winreg` and your tests work on any platform.
Test your code before it hits the real registry!

You can also export real registry data from Windows via `regedit.exe` as a
`.reg` file, import it into a SQLite database with `fake-winreg convert
if=exported.reg of=registry.db`, and run your tests against real-world data.

## Overview

`fake_winreg` provides a drop-in replacement for Python's built-in
`winreg` module, enabling testing of Windows registry-dependent code on
Linux and macOS without a Windows environment, or to test software without hitting a real registry on Windows.

**Key capabilities:**

- All 22 `winreg` API functions (`OpenKey`, `SetValueEx`, `EnumKey`, `SaveKey`, `LoadKey`, etc.) with matching signatures and error behavior
- Three storage backends: **in-memory** (default), **SQLite** (for large registries), **JSON** (for fixtures)
- Import and export of Windows `.reg` files (Version 5.00 and REGEDIT4 formats)
- CLI commands for querying and modifying persistent SQLite registries (`fake-winreg reg`)
- Streaming format conversion between `.db`, `.json`, and `.reg` via CLI or Python API
- Pre-built test registries mimicking Windows 10, Windows 11 23H2, and Wine environments
- Positional-only parameter enforcement matching real `winreg` behavior
- Clean Architecture with `import-linter` enforcement, pyright strict mode, high test coverage

## Installation

### Recommended: uv (fast, isolated)

```bash
# install uv if needed
curl -LsSf https://astral.sh/uv/install.sh | sh

# one-shot run without installing
uvx fake_winreg@latest info

# persistent install as CLI tool
uv tool install fake_winreg

# install as project dependency
uv pip install fake_winreg
```

### Via pip

```bash
pip install fake_winreg
```

### From source

```bash
pip install "git+https://github.com/bitranox/fake_winreg"
```

See [INSTALL.md](INSTALL.md) for all options (pipx, Poetry, PDM, system packages).

## Configuration

See [CONFIG.md](CONFIG.md) for detailed documentation on the layered configuration
system, including precedence rules, profile support, and customization best practices.

## Quick Start

```bash
# Install
uv tool install fake_winreg

# Verify
fake-winreg --version

# Deploy config files
fake-winreg config-deploy --target user

# Export demo registries (Windows 10, 11, Wine) as .json, .reg, .db
fake-winreg export-demo-registries

# Query a persistent registry
fake-winreg reg --db windows11.db list-keys HKEY_LOCAL_MACHINE\SOFTWARE
fake-winreg reg --db windows11.db get HKEY_LOCAL_MACHINE\SOFTWARE\...\CurrentVersion CurrentBuild

# Convert between formats
fake-winreg convert if=windows11.db of=export.reg

# Show package info
fake-winreg info
```

### Python API

```python
import fake_winreg as winreg

# Load a pre-built Windows 10-like test registry
fake_registry = winreg.fake_reg_tools.get_minimal_windows_testregistry()
winreg.load_fake_registry(fake_registry)

# Use exactly like the real winreg module
reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
key = winreg.OpenKey(reg, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion")
value, value_type = winreg.QueryValueEx(key, "CurrentBuild")
```

## API Reference

All functions mirror the signatures and behavior of Python's built-in `winreg` module.

### Registry Functions

#### Connection

```python
ConnectRegistry(computer_name: str | None, key: Handle, /) -> PyHKEY
```

Establish a connection to a predefined registry handle. Pass `None` for `computer_name` to connect to the local (fake) registry.

#### Key Operations

```python
CreateKey(key: Handle, sub_key: str | None, /) -> PyHKEY
```

Create or open a registry key, returning a handle.

```python
CreateKeyEx(key: Handle, sub_key: str, reserved: int = 0, access: int = KEY_WRITE, /) -> PyHKEY
```

Create or open a registry key with explicit access control.

```python
OpenKey(key: Handle, sub_key: str | None, reserved: int = 0, access: int = KEY_READ) -> PyHKEY
```

Open an existing registry key. Does not create the key if it does not exist.

```python
OpenKeyEx(key: Handle, sub_key: str | None, reserved: int = 0, access: int = KEY_READ) -> PyHKEY
```

Open an existing registry key with explicit access. Equivalent to `OpenKey`.

```python
DeleteKey(key: Handle, sub_key: str, /) -> None
```

Delete a registry key. The key must have no subkeys.

```python
DeleteKeyEx(key: Handle, sub_key: str, access: int = KEY_WOW64_64KEY, reserved: int = 0, /) -> None
```

Delete a registry key (64-bit variant).

```python
CloseKey(hkey: int | HKEYType, /) -> None
```

Close a previously opened registry key.

```python
FlushKey(key: Handle, /) -> None
```

Write all attributes of a key to the registry. No-op in the fake implementation since data is already in memory.

#### Save / Load

```python
SaveKey(key: Handle, file_name: str, /) -> None
```

Save a registry key and all its subkeys/values to a JSON file. On real Windows this writes a binary hive file; the fake implementation serialises the subtree as JSON so that `LoadKey` can restore it.

```python
LoadKey(key: Handle, sub_key: str, file_name: str, /) -> None
```

Load registry data from a JSON file (produced by `SaveKey`) into a subkey. On real Windows this loads a binary hive file; the fake implementation reads the JSON and grafts the subtree under `sub_key` of `key`.

#### Value Operations

```python
SetValue(key: Handle, sub_key: str | None, type: int, value: str, /) -> None
```

Set the default (unnamed) value of a key. Only `REG_SZ` is accepted for `type`.

```python
SetValueEx(key: Handle, value_name: str | None, reserved: int, type: int, value: RegData, /) -> None
```

Store data in a named value of an open registry key. Supports all registry value types.

```python
QueryValue(key: Handle, sub_key: str | None, /) -> str
```

Retrieve the default (unnamed) value of a key as a string.

```python
QueryValueEx(key: Handle, value_name: str | None, /) -> tuple[RegData, int]
```

Retrieve value data and its type code for a named value. Returns `(data, type)`.

```python
DeleteValue(key: Handle, value: str | None, /) -> None
```

Remove a named value from a registry key.

#### Enumeration

```python
EnumKey(key: Handle, index: int, /) -> str
```

Enumerate subkeys of an open key by zero-based index. Raises `OSError` when `index` exceeds the number of subkeys.

```python
EnumValue(key: Handle, index: int, /) -> tuple[str, RegData, int]
```

Enumerate values of an open key by zero-based index. Returns `(name, data, type)`. Raises `OSError` when `index` exceeds the number of values.

```python
QueryInfoKey(key: Handle, /) -> tuple[int, int, int]
```

Return information about a key: `(num_subkeys, num_values, last_modified_timestamp)`.

#### Utility

```python
ExpandEnvironmentStrings(string: str, /) -> str
```

Expand `%VAR%`-style environment variable references in a string. Works on all platforms.

```python
DisableReflectionKey(key: Handle, /) -> None
EnableReflectionKey(key: Handle, /) -> None
QueryReflectionKey(key: Handle, /) -> bool
```

Registry reflection stubs. No-op in the fake implementation; `QueryReflectionKey` always returns `True`.

### Backend Management

`fake_winreg` supports multiple storage backends. The default is an in-memory backend.

```python
import fake_winreg as winreg

# Default: in-memory (created automatically if no backend is set)
winreg.use_backend(winreg.InMemoryBackend())

# SQLite: for large registries or persistent storage
winreg.use_backend(winreg.SqliteBackend("/path/to/registry.db"))

# JSON: load from a JSON file, work in memory
winreg.use_backend(winreg.JsonBackend("/path/to/registry.json"))

# Backward-compatible: load a FakeRegistry object directly
fake_registry = winreg.fake_reg_tools.get_minimal_windows_testregistry()
winreg.load_fake_registry(fake_registry)
```

### Import/Export

Exchange registry data between formats. All exports produce deterministic,
alphabetically sorted output — keys and values are ordered by name regardless
of which backend is active.

```python
import fake_winreg as winreg

# JSON format (always UTF-8, per RFC 8259)
winreg.export_json("/path/to/snapshot.json")
winreg.import_json("/path/to/fixture.json")

# Windows .reg format (Version 5.00, UTF-16 LE with BOM by default)
winreg.export_reg("/path/to/export.reg")
winreg.import_reg("/path/to/import.reg")

# REGEDIT4 format (legacy, ANSI encoding)
winreg.export_reg("/path/to/export.reg", version=4)

# Override encoding for human-readable .reg files
winreg.export_reg("/path/to/export.reg", encoding="utf-8")

# Convert between formats (streaming, memory-efficient for large registries)
winreg.convert_registry("source.db", "target.reg")
winreg.convert_registry("source.reg", "target.json")
```

#### Character Encoding

| Format | Default Encoding | Notes |
|--------|-----------------|-------|
| `.json` | UTF-8 | Per RFC 8259. Binary values are base64-encoded. |
| `.reg` export (v5) | UTF-16 LE with BOM | Default. Matches Windows `regedit.exe`. Override with `encoding="utf-8"`. |
| `.reg` export (v4) | ASCII | `export_reg(path, version=4)`. Legacy REGEDIT4 format, no Unicode. |
| `.reg` import | Auto-detected | UTF-16 LE if BOM (`FF FE`) present, otherwise UTF-8. Both v4 and v5 supported. |
| `.db` (SQLite) | N/A | Binary format. String values stored as SQLite TEXT (UTF-8 internally). |

The Windows registry stores all strings as UTF-16 LE internally. Values encoded as
`hex(2):` (REG_EXPAND_SZ) and `hex(7):` (REG_MULTI_SZ) in `.reg` files use UTF-16 LE
byte sequences regardless of the file encoding.

### Constants

All constants from the `winreg` module are available.

**Hive keys:**

| Constant               | Description                                      |
|------------------------|--------------------------------------------------|
| `HKEY_CLASSES_ROOT`    | Registry entries for file associations and COM    |
| `HKEY_CURRENT_USER`    | Settings for the current user                     |
| `HKEY_LOCAL_MACHINE`   | System-wide settings                              |
| `HKEY_USERS`           | Settings for all user profiles                    |
| `HKEY_CURRENT_CONFIG`  | Current hardware profile                          |
| `HKEY_PERFORMANCE_DATA` | Performance counters                             |
| `HKEY_DYN_DATA`        | Dynamic data (Windows 95/98)                      |

**Value types:**

| Constant                          | Description                                          |
|-----------------------------------|------------------------------------------------------|
| `REG_NONE`                        | No defined value type                                |
| `REG_SZ`                          | Null-terminated string                               |
| `REG_EXPAND_SZ`                   | String with unexpanded environment variable refs     |
| `REG_BINARY`                      | Binary data                                          |
| `REG_DWORD`                       | 32-bit integer (little-endian)                       |
| `REG_DWORD_BIG_ENDIAN`            | 32-bit integer (big-endian)                          |
| `REG_LINK`                        | Symbolic link (Unicode)                              |
| `REG_MULTI_SZ`                    | Array of null-terminated strings                     |
| `REG_QWORD`                       | 64-bit integer (little-endian)                       |
| `REG_RESOURCE_LIST`               | Device driver resource list                          |
| `REG_FULL_RESOURCE_DESCRIPTOR`    | Hardware resource descriptor                         |
| `REG_RESOURCE_REQUIREMENTS_LIST`  | Hardware resource requirements                       |

**Access rights:**

| Constant                | Description                            |
|-------------------------|----------------------------------------|
| `KEY_READ`              | Read access                            |
| `KEY_WRITE`             | Write access                           |
| `KEY_ALL_ACCESS`        | Full access                            |
| `KEY_EXECUTE`           | Execute access (same as `KEY_READ`)    |
| `KEY_QUERY_VALUE`       | Query subkey values                    |
| `KEY_SET_VALUE`         | Set subkey values                      |
| `KEY_CREATE_SUB_KEY`    | Create subkeys                         |
| `KEY_ENUMERATE_SUB_KEYS` | Enumerate subkeys                     |
| `KEY_NOTIFY`            | Change notification                    |
| `KEY_CREATE_LINK`       | Create symbolic link                   |
| `KEY_WOW64_64KEY` | Access 64-bit registry view |
| `KEY_WOW64_32KEY` | Access 32-bit registry view |

### Data Types

```python
# All possible types of data that registry values can hold
RegData = None | bytes | int | str | list[str]

# Handle types accepted by all API functions
Handle = int | HKEYType | PyHKEY

# Registry data structures
FakeRegistry       # Top-level registry container (holds hive roots)
FakeRegistryKey    # A single registry key with subkeys and values
FakeRegistryValue  # A named value with data and type code
```

`PyHKEY` supports the context manager protocol:

```python
with winreg.OpenKey(reg, r"SOFTWARE\Microsoft") as key:
    value, vtype = winreg.QueryValueEx(key, "SomeName")
```

### Test Registries

Pre-built registry fixtures for common test scenarios. See
[docs/registry_values.md](docs/registry_values.md) for a detailed reference
of all registry values, version detection logic, and branch codenames.

```python
import fake_winreg as winreg

# Windows 10 (build 18363)
reg = winreg.fake_reg_tools.get_minimal_windows_testregistry()

# Windows 11 23H2 Pro (build 22631)
reg = winreg.fake_reg_tools.get_minimal_windows11_testregistry()

# Wine environment (build 7601)
reg = winreg.fake_reg_tools.get_minimal_wine_testregistry()
```

## CLI Usage

```bash
# Query a persistent SQLite registry
fake-winreg reg --db registry.db list-keys HKEY_LOCAL_MACHINE\SOFTWARE
fake-winreg reg --db registry.db list-values HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
fake-winreg reg --db registry.db get HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\...\CurrentVersion CurrentBuild
fake-winreg reg --db registry.db info HKEY_LOCAL_MACHINE\SOFTWARE

# Modify a persistent registry
fake-winreg reg --db registry.db create-key HKEY_LOCAL_MACHINE\SOFTWARE\MyApp
fake-winreg reg --db registry.db set HKEY_LOCAL_MACHINE\SOFTWARE\MyApp Name "hello"
fake-winreg reg --db registry.db set HKEY_LOCAL_MACHINE\SOFTWARE\MyApp Count 42 --type REG_DWORD
fake-winreg reg --db registry.db delete-value HKEY_LOCAL_MACHINE\SOFTWARE\MyApp Name
fake-winreg reg --db registry.db delete-key HKEY_LOCAL_MACHINE\SOFTWARE\MyApp

# Convert between registry formats
fake-winreg convert if=registry.db of=export.reg
fake-winreg convert if=export.reg of=registry.json

# Export demo registries (Windows 10 + 11 + Wine) as .json, .reg, .db
fake-winreg export-demo-registries

# Show package information
fake-winreg info
```

The `--db` option can be replaced by configuring `registry.db_path` via TOML config,
`.env` file (`REGISTRY__DB_PATH=/path/to/db`), or environment variable
(`FAKE_WINREG___REGISTRY__DB_PATH=/path/to/db`).

## Development

Run the full test suite (lint, type-check, tests with coverage):

```bash
make test
```

See [DEVELOPMENT.md](DEVELOPMENT.md) for the complete development setup guide.

## License

[MIT](LICENSE)
