A general-purpose property graph database library built on SQLite
Project description
PropWeaver
A general-purpose property graph database library built on SQLite.
Features
- 🗂️ Nodes and edges with arbitrary properties - Store any Python data type as properties
- ⚡ CRUD operations with bulk mutations - Individual and mass operations with transaction safety
- 🔒 Transaction support with automatic rollback - ACID compliance for data integrity
- 🔗 Lazy query evaluation with method chaining - XPath-inspired fluent API
- 🛡️ Type-safe operations - Modern Python type hints and comprehensive testing
- 📦 Zero external dependencies - Pure Python standard library implementation
Installation
From PyPI
pip install propweaver
# or with Pydantic API models:
pip install "propweaver[api]"
Development Setup (with uv)
git clone https://github.com/mdubinko/propweaver.git
cd propweaver
uv venv
uv pip install -e .
Development Setup (with pip)
git clone https://github.com/mdubinko/propweaver.git
cd propweaver
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e .
Optional: Pydantic API Models
propweaver.api provides Pydantic v2 models for validating PropWeaver return values.
Install the extra to use them:
uv pip install -e ".[api]"
# or
pip install "propweaver[api]"
Quick Start
from propweaver import PropertyGraph
# Create an in-memory graph
with PropertyGraph() as graph:
# Create nodes with properties
alice = graph.add_node("User", name="Alice", age=30, active=True)
bob = graph.add_node("User", name="Bob", age=25, active=False)
project = graph.add_node("Project", name="Web App", status="active")
# Create relationships
works_on = graph.add_edge(alice, "WORKS_ON", project, role="Lead", since="2023-01-01")
# Query nodes
active_users = list(graph.nodes("User", active=True))
print(f"Found {len(active_users)} active users")
# Bulk operations
deleted = graph.nodes("User", active=False).delete().execute()
print(f"Deleted {deleted} inactive users")
Security Note
PropWeaver is designed for trusted environments. Before production use with sensitive data or untrusted input, review SECURITY_AUDIT.md for security considerations including path validation, encryption options, and resource monitoring. Use graph.resource_stats() to monitor database size and entity counts.
Logging Configuration
PropWeaver uses Python's standard logging module and respects your application's logging configuration. As a library, PropWeaver does not configure handlers or formatters - it only emits log messages that your application controls.
Application Setup
Configure PropWeaver logging through your application's logging setup:
import logging.config
# Example dictConfig for applications using PropWeaver
LOGGING_CONFIG = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
}
},
'loggers': {
'propweaver': {
'level': 'INFO',
'handlers': ['console'],
'propagate': False,
},
'propweaver.storage': { # SQL operations
'level': 'WARNING', # Reduce verbosity
},
'propweaver.query': { # Query operations
'level': 'INFO',
},
}
}
logging.config.dictConfig(LOGGING_CONFIG)
Log Levels
PropWeaver provides structured logging with these levels:
SUMMARY(25) - Token-efficient emoji messages for key operationsINFO- Standard operational messagesDEBUG- Detailed SQL queries and performance dataWARNING/ERROR- Issues and exceptions
from propweaver import PropertyGraph, SUMMARY
# Quick setup for testing/development
import logging
logging.getLogger('propweaver').setLevel(SUMMARY)
# Your PropertyGraph operations will now emit concise emoji logs
with PropertyGraph() as graph:
user = graph.add_node("User", name="Alice") # 💾 INSERT (2.1ms)
Component Loggers
PropWeaver uses hierarchical loggers for different components:
propweaver.storage- Database operations and SQL queriespropweaver.query- Query execution and resultspropweaver.performance- Slow operation warningspropweaver.stats- Graph statistics and metrics
Enable SQL debugging by setting the storage logger to DEBUG level - PropWeaver will automatically log all SQL queries with parameters and timing.
Core Features
Basic CRUD Operations
# Create
user = graph.add_node("User", name="Alice", email="alice@example.com")
friendship = graph.add_edge(user1, "FRIENDS", user2, since="2023-01-01")
# Read
name = user.props["name"] # Dict-like access
active_users = list(graph.nodes("User", active=True))
# Update
user.props["last_login"] = "2023-12-01" # Dict-like assignment
user.props.update({"verified": True, "score": 95}) # Bulk update
# Delete properties
del user.props["temp_token"] # Remove specific property
user.props.clear() # Remove all properties
Dict-like Property Interface
# Rich dict-like operations on properties
user = graph.add_node("User", name="Alice", age=30)
# Check properties
if "age" in user.props:
print(f"User has {len(user.props)} properties")
# Iterate over properties
for key in user.props:
print(f"{key}: {user.props[key]}")
# Get with defaults
role = user.props.get("role", "user") # Returns "user" if role not set
# Export properties
all_props = user.props.copy() # Get dict copy
items = list(user.props.items()) # Get key-value pairs
# Property validation
try:
user.props["invalid"] = None # Raises ValueError - None not allowed
except ValueError as e:
print(f"Error: {e}") # Use 'del props[key]' instead
Bulk Mutations
# Delete all temporary data
temp_deleted = graph.nodes("TempUser").delete().execute()
expired_edges = graph.edges("SESSION", expired=True).delete().execute()
# Chain filters for complex operations
inactive_old_users = (graph.nodes("User")
.filter(active=False)
.filter(last_login__before="2022-01-01")
.delete()
.execute())
Transaction Safety
# Automatic transactions for bulk operations
try:
count = graph.nodes("BadData").delete().execute()
print(f"Cleaned up {count} items")
except Exception as e:
print("Cleanup failed, no data was modified")
# Automatic rollback on error
# Manual transaction control
with graph._storage.transaction():
user = graph.add_node("User", name="Alice")
project = graph.add_node("Project", name="New Project")
graph.add_edge(user, "OWNS", project)
# All committed together, or all rolled back on error
Query Chaining
# XPath-style lazy evaluation
senior_engineers = (graph.nodes("User")
.filter(department="Engineering")
.filter(level="Senior")
.filter(active=True))
# Execute only when iterating
for engineer in senior_engineers:
print(f"Senior Engineer: {engineer.props['name']}")
# Get a count of the results
count = len(list(senior_engineers))
More Examples
See the examples/ directory for real-world usage patterns:
# Run all examples
./bin/examples.sh
# Run specific examples
./bin/examples.sh social # Social network modeling
./bin/examples.sh knowledge # Knowledge graph patterns
./bin/examples.sh deps # Dependency analysis
API Contract
contract.json in the repo root is a machine-readable description of the complete public API — every class, method signature, parameter type, and return type. It is generated from the source and committed so that breaking changes are visible as diffs in pull requests.
Consuming projects (including AI coding assistants) can read this file to verify they are calling APIs correctly without having to inspect source code.
To regenerate after making API changes:
uv run --extra api python bin/build_contract.py
API Reference
PropertyGraph Class
The main interface to the graph database.
Constructor
PropertyGraph(db_path: Optional[str] = None)- Create graph (in-memory if no path)
Node Operations
add_node(node_type: str, **properties) -> NodeProxy- Create nodenodes(node_type: Optional[str] = None, **properties) -> NodeIterator- Query nodes
Edge Operations
add_edge(source, edge_type: str, target, **properties) -> EdgeProxy- Create edgeedges(edge_type: Optional[str] = None, **properties) -> EdgeIterator- Query edges
Graph Operations
node_count() -> int- Total number of nodesedge_count() -> int- Total number of edgesnode_types() -> list[str]- List all distinct node typesedge_types() -> list[str]- List all distinct edge typesresource_stats() -> dict- Get database size and entity counts (for monitoring)props- Dict-like property interface (see PropertyDict below)
NodeProxy Class
Lightweight proxy for individual nodes.
props- Dict-like property interface (see PropertyDict below)to_json() -> dict- Export as dictionarytimestamp() -> float- Creation timestamp
EdgeProxy Class
Lightweight proxy for individual edges.
props- Dict-like property interface (see PropertyDict below)to_json() -> dict- Export as dictionarysrc_id/dst_id- Source and destination node IDs
PropertyDict Class
Dict-like interface for properties (accessed via .props on graph/node/edge):
props[key]- Get property value (raises KeyError if missing)props[key] = value- Set property valuedel props[key]- Delete property (raises KeyError if missing)key in props- Check if property existslen(props)- Number of propertiesprops.keys()- Property namesprops.values()- Property valuesprops.items()- Property (key, value) pairsprops.get(key, default=None)- Get with optional defaultprops.update(dict)- Bulk update from dictionaryprops.clear()- Remove all propertiesprops.copy()- Export as regular dict
Testing
# Run all tests (pytest)
./bin/test.sh
# Run specific test suites
./bin/test.sh fast # Fast tests only
./bin/test.sh integration # Integration tests
./bin/test.sh coverage # With coverage report
# Run comprehensive test suite
./bin/test.sh comprehensive
Development
Quick Start
# Set up development environment (uv recommended)
./bin/dev.sh setup
# Test basic functionality
./bin/dev.sh basic
# Run all tests
./bin/test.sh
# Format code and run checks
./bin/dev.sh lint
Requirements
- Python 3.10+
- SQLite 3.7.11+ (for foreign key support)
- Zero external runtime dependencies
- uv recommended for development
Development Scripts
./bin/test.sh- Run tests with various options./bin/examples.sh- Run examples./bin/dev.sh- Development utilities (setup, lint, check, etc.)uv run --extra api python bin/build_contract.py- Regeneratecontract.json
Running Tests with Optional Extras
# Core tests (no extras needed)
uv run --extra dev pytest
# Including api model tests
uv run --extra dev --extra api pytest
Architecture
src/propweaver/storage.py: SQLite operations and type mappingsrc/propweaver/query.py: Query building and lazy evaluationsrc/propweaver/core.py: Main API and proxy classes
Contributing
- Fork the repository
- Create a feature branch
- Run
./bin/dev.sh setupfor development environment - Add tests for new functionality
- Run
./bin/dev.sh checkto verify all checks pass - Submit a pull request
Release Process
See docs/RELEASE.md for the TestPyPI dry run and tagged PyPI release flow.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file propweaver-0.2.1.tar.gz.
File metadata
- Download URL: propweaver-0.2.1.tar.gz
- Upload date:
- Size: 60.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c1488438299f01a5ee18c7403b38bbb071e6cc8f11e6597fd69e110f001bcc8
|
|
| MD5 |
93706093f4cc95d921a7274bbecaa59e
|
|
| BLAKE2b-256 |
a6250e91644c9697b506cb9b47e2eab4f4e36ebc8a594bc60925ba8b46f8f84f
|
Provenance
The following attestation bundles were made for propweaver-0.2.1.tar.gz:
Publisher:
publish-to-pypi.yml on mdubinko/propweaver
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
propweaver-0.2.1.tar.gz -
Subject digest:
8c1488438299f01a5ee18c7403b38bbb071e6cc8f11e6597fd69e110f001bcc8 - Sigstore transparency entry: 1265220063
- Sigstore integration time:
-
Permalink:
mdubinko/propweaver@a4441b8d576cee8576a921329caa6c0ed683dcd2 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/mdubinko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@a4441b8d576cee8576a921329caa6c0ed683dcd2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file propweaver-0.2.1-py3-none-any.whl.
File metadata
- Download URL: propweaver-0.2.1-py3-none-any.whl
- Upload date:
- Size: 43.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b09d8350f7a169299e56ba59a08cb73eed0fef96924f6b8087c47d35530207e
|
|
| MD5 |
73de0eed602dabf33cfa8243bcf7e0df
|
|
| BLAKE2b-256 |
9de8dbf4bb6cb4dfe0d7cdeb9e7a210cf738cab5aeb7942cc168693056ff2efb
|
Provenance
The following attestation bundles were made for propweaver-0.2.1-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on mdubinko/propweaver
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
propweaver-0.2.1-py3-none-any.whl -
Subject digest:
5b09d8350f7a169299e56ba59a08cb73eed0fef96924f6b8087c47d35530207e - Sigstore transparency entry: 1265220229
- Sigstore integration time:
-
Permalink:
mdubinko/propweaver@a4441b8d576cee8576a921329caa6c0ed683dcd2 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/mdubinko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@a4441b8d576cee8576a921329caa6c0ed683dcd2 -
Trigger Event:
push
-
Statement type: