pylpg 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pylpg-0.1.0/.commitlintrc.json +5 -0
- pylpg-0.1.0/.github/workflows/docs.yml +56 -0
- pylpg-0.1.0/.github/workflows/release.yml +202 -0
- pylpg-0.1.0/.github/workflows/tests.yml +68 -0
- pylpg-0.1.0/.gitignore +15 -0
- pylpg-0.1.0/.pre-commit-config.yaml +7 -0
- pylpg-0.1.0/.python-version +1 -0
- pylpg-0.1.0/CLAUDE.md +66 -0
- pylpg-0.1.0/PKG-INFO +87 -0
- pylpg-0.1.0/README.md +59 -0
- pylpg-0.1.0/cliff.toml +52 -0
- pylpg-0.1.0/docs/about.md +3 -0
- pylpg-0.1.0/docs/api_reference/backend/base.md +3 -0
- pylpg-0.1.0/docs/api_reference/backend/falkordb.md +3 -0
- pylpg-0.1.0/docs/api_reference/backend/falkordblite.md +3 -0
- pylpg-0.1.0/docs/api_reference/backend/neo4j.md +3 -0
- pylpg-0.1.0/docs/api_reference/node.md +3 -0
- pylpg-0.1.0/docs/api_reference/relationship.md +3 -0
- pylpg-0.1.0/docs/api_reference/session.md +3 -0
- pylpg-0.1.0/docs/api_reference/types.md +3 -0
- pylpg-0.1.0/docs/getting_started.md +220 -0
- pylpg-0.1.0/docs/index.md +90 -0
- pylpg-0.1.0/main.py +6 -0
- pylpg-0.1.0/mkdocs.yml +59 -0
- pylpg-0.1.0/pyproject.toml +69 -0
- pylpg-0.1.0/src/pylpg/__init__.py +0 -0
- pylpg-0.1.0/src/pylpg/backend/__init__.py +0 -0
- pylpg-0.1.0/src/pylpg/backend/base.py +71 -0
- pylpg-0.1.0/src/pylpg/backend/falkordb.py +151 -0
- pylpg-0.1.0/src/pylpg/backend/falkordblite.py +22 -0
- pylpg-0.1.0/src/pylpg/backend/neo4j.py +255 -0
- pylpg-0.1.0/src/pylpg/cypher.py +204 -0
- pylpg-0.1.0/src/pylpg/node.py +74 -0
- pylpg-0.1.0/src/pylpg/relationship.py +215 -0
- pylpg-0.1.0/src/pylpg/session.py +210 -0
- pylpg-0.1.0/src/pylpg/types.py +69 -0
- pylpg-0.1.0/tests/__init__.py +0 -0
- pylpg-0.1.0/tests/conftest.py +106 -0
- pylpg-0.1.0/tests/models.py +33 -0
- pylpg-0.1.0/tests/test_batch.py +69 -0
- pylpg-0.1.0/tests/test_execute_query.py +52 -0
- pylpg-0.1.0/tests/test_node.py +68 -0
- pylpg-0.1.0/tests/test_registry.py +25 -0
- pylpg-0.1.0/tests/test_relationship.py +69 -0
- pylpg-0.1.0/tests/test_traversal.py +74 -0
- pylpg-0.1.0/tox.ini +16 -0
- pylpg-0.1.0/uv.lock +1363 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Deploy documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
workflow_run:
|
|
6
|
+
workflows: ["Release"]
|
|
7
|
+
types: [completed]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
deploy-docs:
|
|
11
|
+
name: Deploy documentation
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v5
|
|
19
|
+
with:
|
|
20
|
+
ref: main
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- name: Get latest tag and checkout
|
|
24
|
+
id: get_tag
|
|
25
|
+
run: |
|
|
26
|
+
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*" origin/main)
|
|
27
|
+
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
|
28
|
+
git checkout $LATEST_TAG
|
|
29
|
+
|
|
30
|
+
- name: Configure Git Credentials
|
|
31
|
+
run: |
|
|
32
|
+
git config user.name github-actions[bot]
|
|
33
|
+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
|
34
|
+
- uses: actions/setup-python@v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: 3.x
|
|
37
|
+
- name: Cache dependencies
|
|
38
|
+
uses: actions/cache@v5
|
|
39
|
+
with:
|
|
40
|
+
key: mkdocs-material-${{ github.run_id }}
|
|
41
|
+
path: ~/.cache
|
|
42
|
+
restore-keys: |
|
|
43
|
+
mkdocs-material-
|
|
44
|
+
- name: Install MkDocs and dependencies
|
|
45
|
+
run: pip install mkdocs-material griffe-fieldz mkdocstrings-python griffe-inherited-docstrings
|
|
46
|
+
- name: Generate changelog
|
|
47
|
+
uses: orhun/git-cliff-action@v4
|
|
48
|
+
with:
|
|
49
|
+
config: cliff.toml
|
|
50
|
+
args: --verbose
|
|
51
|
+
env:
|
|
52
|
+
OUTPUT: docs/CHANGELOG.md
|
|
53
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
54
|
+
|
|
55
|
+
- name: Deploy documentation
|
|
56
|
+
run: mkdocs gh-deploy --force
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Run tests
|
|
12
|
+
uses: ./.github/workflows/tests.yml
|
|
13
|
+
|
|
14
|
+
build:
|
|
15
|
+
name: Build package
|
|
16
|
+
needs:
|
|
17
|
+
- test
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v5
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
ref: main
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
|
|
27
|
+
- name: Get latest tag
|
|
28
|
+
id: get_tag
|
|
29
|
+
run: |
|
|
30
|
+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
31
|
+
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*" origin/main)
|
|
32
|
+
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
|
33
|
+
git checkout $LATEST_TAG
|
|
34
|
+
else
|
|
35
|
+
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
36
|
+
fi
|
|
37
|
+
- name: Set up Python
|
|
38
|
+
uses: actions/setup-python@v6
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.13"
|
|
41
|
+
- name: Install pypa/build
|
|
42
|
+
run: python3 -m pip install build --user
|
|
43
|
+
- name: Build a binary wheel and a source tarball
|
|
44
|
+
run: python3 -m build
|
|
45
|
+
- name: Store the packages
|
|
46
|
+
uses: actions/upload-artifact@v5
|
|
47
|
+
with:
|
|
48
|
+
name: python-package-distributions
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
testpypi:
|
|
52
|
+
name: Publish package to TestPyPI
|
|
53
|
+
needs:
|
|
54
|
+
- build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
|
|
57
|
+
environment:
|
|
58
|
+
name: testpypi
|
|
59
|
+
url: https://test.pypi.org/p/pylpg
|
|
60
|
+
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
|
|
64
|
+
steps:
|
|
65
|
+
- name: Download all the dists
|
|
66
|
+
uses: actions/download-artifact@v5
|
|
67
|
+
with:
|
|
68
|
+
name: python-package-distributions
|
|
69
|
+
path: dist/
|
|
70
|
+
- name: Publish package to TestPyPI
|
|
71
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
72
|
+
with:
|
|
73
|
+
repository-url: https://test.pypi.org/legacy/
|
|
74
|
+
|
|
75
|
+
pypi:
|
|
76
|
+
name: Publish package to PyPI
|
|
77
|
+
needs:
|
|
78
|
+
- testpypi
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
environment:
|
|
81
|
+
name: pypi
|
|
82
|
+
url: https://pypi.org/p/pylpg
|
|
83
|
+
permissions:
|
|
84
|
+
id-token: write
|
|
85
|
+
|
|
86
|
+
steps:
|
|
87
|
+
- name: Download all the dists
|
|
88
|
+
uses: actions/download-artifact@v5
|
|
89
|
+
with:
|
|
90
|
+
name: python-package-distributions
|
|
91
|
+
path: dist/
|
|
92
|
+
- name: Publish package to PyPI
|
|
93
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
94
|
+
|
|
95
|
+
changelog:
|
|
96
|
+
name: Generate changelog
|
|
97
|
+
needs:
|
|
98
|
+
- build
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
permissions:
|
|
101
|
+
contents: write
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v5
|
|
104
|
+
with:
|
|
105
|
+
ref: main
|
|
106
|
+
fetch-depth: 0
|
|
107
|
+
|
|
108
|
+
- name: Get tag
|
|
109
|
+
id: get_tag
|
|
110
|
+
run: |
|
|
111
|
+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
112
|
+
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*" origin/main)
|
|
113
|
+
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
|
114
|
+
else
|
|
115
|
+
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
- name: Generate changelog for this release
|
|
119
|
+
uses: orhun/git-cliff-action@v4
|
|
120
|
+
id: cliff
|
|
121
|
+
with:
|
|
122
|
+
config: cliff.toml
|
|
123
|
+
args: --latest --strip header
|
|
124
|
+
env:
|
|
125
|
+
OUTPUT: CHANGELOG_RELEASE.md
|
|
126
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
127
|
+
|
|
128
|
+
- name: Upload changelog snippet
|
|
129
|
+
uses: actions/upload-artifact@v5
|
|
130
|
+
with:
|
|
131
|
+
name: changelog-snippet
|
|
132
|
+
path: CHANGELOG_RELEASE.md
|
|
133
|
+
|
|
134
|
+
github-release:
|
|
135
|
+
name: Create GitHub Release
|
|
136
|
+
needs:
|
|
137
|
+
- pypi
|
|
138
|
+
- changelog
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
|
|
141
|
+
permissions:
|
|
142
|
+
contents: write
|
|
143
|
+
|
|
144
|
+
steps:
|
|
145
|
+
- name: Checkout to get tag info
|
|
146
|
+
uses: actions/checkout@v5
|
|
147
|
+
with:
|
|
148
|
+
ref: main
|
|
149
|
+
fetch-depth: 0
|
|
150
|
+
|
|
151
|
+
- name: Get latest tag
|
|
152
|
+
id: get_tag
|
|
153
|
+
run: |
|
|
154
|
+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
155
|
+
LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*" origin/main)
|
|
156
|
+
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
|
157
|
+
else
|
|
158
|
+
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
- name: Download dist artifacts
|
|
162
|
+
uses: actions/download-artifact@v5
|
|
163
|
+
with:
|
|
164
|
+
name: python-package-distributions
|
|
165
|
+
path: dist/
|
|
166
|
+
|
|
167
|
+
- name: Download changelog snippet
|
|
168
|
+
uses: actions/download-artifact@v5
|
|
169
|
+
with:
|
|
170
|
+
name: changelog-snippet
|
|
171
|
+
|
|
172
|
+
- name: Extract version from tag
|
|
173
|
+
run: |
|
|
174
|
+
TAG="${{ steps.get_tag.outputs.tag }}"
|
|
175
|
+
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
|
176
|
+
|
|
177
|
+
- name: Build release body
|
|
178
|
+
run: |
|
|
179
|
+
cat > RELEASE_BODY.md << 'HEADER'
|
|
180
|
+
## What's Changed
|
|
181
|
+
HEADER
|
|
182
|
+
cat CHANGELOG_RELEASE.md >> RELEASE_BODY.md
|
|
183
|
+
cat >> RELEASE_BODY.md << EOF
|
|
184
|
+
|
|
185
|
+
## Installation
|
|
186
|
+
|
|
187
|
+
\`\`\`bash
|
|
188
|
+
pip install pylpg==${{ env.VERSION }}
|
|
189
|
+
\`\`\`
|
|
190
|
+
|
|
191
|
+
## Links
|
|
192
|
+
|
|
193
|
+
- [Documentation](https://rougny.github.io/pylpg/)
|
|
194
|
+
- [PyPI](https://pypi.org/project/pylpg/${{ env.VERSION }}/)
|
|
195
|
+
EOF
|
|
196
|
+
|
|
197
|
+
- name: Create GitHub Release
|
|
198
|
+
uses: softprops/action-gh-release@v2
|
|
199
|
+
with:
|
|
200
|
+
tag_name: ${{ steps.get_tag.outputs.tag }}
|
|
201
|
+
files: dist/*
|
|
202
|
+
body_path: RELEASE_BODY.md
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- testing
|
|
8
|
+
- main
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: Test on ${{ matrix.os }}
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest]
|
|
18
|
+
services:
|
|
19
|
+
neo4j:
|
|
20
|
+
image: neo4j:5
|
|
21
|
+
env:
|
|
22
|
+
NEO4J_AUTH: neo4j/testpassword
|
|
23
|
+
ports:
|
|
24
|
+
- 7687:7687
|
|
25
|
+
options: >-
|
|
26
|
+
--health-cmd "cypher-shell -u neo4j -p testpassword 'RETURN 1'"
|
|
27
|
+
--health-interval 10s
|
|
28
|
+
--health-timeout 5s
|
|
29
|
+
--health-retries 10
|
|
30
|
+
falkordb:
|
|
31
|
+
image: falkordb/falkordb:latest
|
|
32
|
+
ports:
|
|
33
|
+
- 6379:6379
|
|
34
|
+
options: >-
|
|
35
|
+
--health-cmd "redis-cli ping"
|
|
36
|
+
--health-interval 10s
|
|
37
|
+
--health-timeout 5s
|
|
38
|
+
--health-retries 5
|
|
39
|
+
steps:
|
|
40
|
+
- name: Checkout code
|
|
41
|
+
uses: actions/checkout@v5
|
|
42
|
+
- name: Set up Python 3.10
|
|
43
|
+
uses: actions/setup-python@v6
|
|
44
|
+
with:
|
|
45
|
+
python-version: '3.10'
|
|
46
|
+
- name: Set up Python 3.11
|
|
47
|
+
uses: actions/setup-python@v6
|
|
48
|
+
with:
|
|
49
|
+
python-version: '3.11'
|
|
50
|
+
- name: Set up Python 3.12
|
|
51
|
+
uses: actions/setup-python@v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: '3.12'
|
|
54
|
+
- name: Set up Python 3.13
|
|
55
|
+
uses: actions/setup-python@v6
|
|
56
|
+
with:
|
|
57
|
+
python-version: '3.13'
|
|
58
|
+
- name: Set up Python 3.14
|
|
59
|
+
uses: actions/setup-python@v6
|
|
60
|
+
with:
|
|
61
|
+
python-version: '3.14'
|
|
62
|
+
allow-prereleases: true
|
|
63
|
+
- name: Install uv and tox
|
|
64
|
+
run: pip install uv tox tox-uv
|
|
65
|
+
- name: Run tests with tox
|
|
66
|
+
env:
|
|
67
|
+
CI: true
|
|
68
|
+
run: tox -e py310-min,py311-min,py312-min,py313-min,py314-min
|
pylpg-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
pylpg-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project overview
|
|
6
|
+
|
|
7
|
+
`pylpg` is a Python Object Graph Mapper (OGM) for labeled property graph databases (Neo4j, FalkorDB, FalkorDBLite). It maps Python classes to graph nodes/relationships and generates Cypher queries for CRUD operations.
|
|
8
|
+
|
|
9
|
+
## Development commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install (uses uv + hatchling)
|
|
13
|
+
uv sync --all-extras
|
|
14
|
+
|
|
15
|
+
# Lint
|
|
16
|
+
uv run ruff check src/ tests/
|
|
17
|
+
|
|
18
|
+
# Format
|
|
19
|
+
uv run ruff format src/ tests/
|
|
20
|
+
|
|
21
|
+
# Test (requires Docker for Neo4j and FalkorDB containers)
|
|
22
|
+
uv run pytest tests/ -v
|
|
23
|
+
|
|
24
|
+
# Test with coverage
|
|
25
|
+
uv run pytest tests/ --cov=pylpg --cov-report=term
|
|
26
|
+
|
|
27
|
+
# Test across Python versions
|
|
28
|
+
uv run tox -e py310-min,py311-min,py312-min,py313-min
|
|
29
|
+
|
|
30
|
+
# Build docs
|
|
31
|
+
uv run mkdocs build
|
|
32
|
+
|
|
33
|
+
# Serve docs locally
|
|
34
|
+
uv run mkdocs serve
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Code style
|
|
38
|
+
|
|
39
|
+
- Always use absolute imports: `import x` then `x.y`, never `from x import y`.
|
|
40
|
+
- Use full words for variable names, no abbreviations (e.g. `type_` not `t`, `annotation` not `ann`, `relationship` not `rel`). Exception: `cls` is allowed.
|
|
41
|
+
- No runtime value validation on properties — validation only happens at class definition time (type checking in `validate_properties`). The database backend is the ultimate authority on what values it accepts.
|
|
42
|
+
- Always run ruff check and ruff format after code changes.
|
|
43
|
+
- When calling functions across modules, use named parameters.
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
All source code is under `src/pylpg/`.
|
|
48
|
+
|
|
49
|
+
- **`node.py`** — `Node` base class with global label/name registries for hydration. Subclasses define graph nodes via type-annotated attributes. Labels default to class name.
|
|
50
|
+
- **`relationship.py`** — `Relationship` base class (requires `__type__`, `source`, `target`). Descriptor classes (`RelationshipTo`, `RelationshipFrom`, `RelationshipUndirected`) attach traversal and `connect()` capabilities to Node subclasses.
|
|
51
|
+
- **`types.py`** — Property type utilities. Extracts primitive properties and relationship descriptors, validates types at class definition time.
|
|
52
|
+
- **`cypher.py`** — Shared Cypher query builder used by cypher-based backends. Not imported by session.
|
|
53
|
+
- **`session.py`** — Orchestrates persistence via `save()`, `delete()`, `execute_query()`. Handles node hydration from query results.
|
|
54
|
+
- **`backend/`** — `Backend` ABC with full CRUD, batch, and traversal operations:
|
|
55
|
+
- `Neo4jBackend` — uses UNWIND for batch operations
|
|
56
|
+
- `FalkorDBBackend` — uses individual queries for batch (optimal for FalkorDB)
|
|
57
|
+
- `FalkorDBLiteBackend` — embedded FalkorDB, inherits from FalkorDBBackend
|
|
58
|
+
|
|
59
|
+
## Design decisions
|
|
60
|
+
|
|
61
|
+
- **No custom exceptions** — plain `ValueError`/`TypeError` are sufficient.
|
|
62
|
+
- **No runtime value validation** — only class-definition-time type checking.
|
|
63
|
+
- **`__init_subclass__` over metaclass** — simpler, no metaclass conflicts.
|
|
64
|
+
- **Backend-specific batch strategies** — Neo4j uses UNWIND queries, FalkorDB uses individual queries. Each backend implements `save_batch()` optimally.
|
|
65
|
+
- **Global node registry** — `Node._label_registry` and `Node._name_registry` enable hydration without passing target classes through the call chain.
|
|
66
|
+
- **Session is orchestration only** — all database operations live on the backend. The session dispatches and handles Python-side concerns (ID mapping, hydration).
|
pylpg-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pylpg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python Object Graph Mapper for labeled property graph databases
|
|
5
|
+
Project-URL: Homepage, https://github.com/rougny/pylpg
|
|
6
|
+
Project-URL: Issues, https://github.com/rougny/pylpg/issues
|
|
7
|
+
Author-email: Adrien Rougny <adrienrougny@gmail.com>
|
|
8
|
+
License: GPL-3.0-or-later
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: falkordb>=1.0; extra == 'all'
|
|
20
|
+
Requires-Dist: neo4j>=5.0; extra == 'all'
|
|
21
|
+
Provides-Extra: falkordb
|
|
22
|
+
Requires-Dist: falkordb>=1.0; extra == 'falkordb'
|
|
23
|
+
Provides-Extra: falkordblite
|
|
24
|
+
Requires-Dist: falkordblite>=0.1; (python_version >= '3.12') and extra == 'falkordblite'
|
|
25
|
+
Provides-Extra: neo4j
|
|
26
|
+
Requires-Dist: neo4j>=5.0; extra == 'neo4j'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# pylpg
|
|
30
|
+
|
|
31
|
+
A Python Object Graph Mapper for labeled property graph databases.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- Simple model definition using Python type annotations
|
|
36
|
+
- Multi-backend: Neo4j, FalkorDB, FalkorDBLite (embedded)
|
|
37
|
+
- Session-based persistence with node binding
|
|
38
|
+
- Transactional batch operations (Neo4j)
|
|
39
|
+
- Node hydration from raw query results
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install pylpg[neo4j] # Neo4j
|
|
45
|
+
pip install pylpg[falkordb] # FalkorDB
|
|
46
|
+
pip install pylpg[falkordblite] # FalkorDBLite (embedded, no server)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick example
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import pylpg.node
|
|
53
|
+
import pylpg.relationship
|
|
54
|
+
import pylpg.session
|
|
55
|
+
import pylpg.backend.neo4j
|
|
56
|
+
|
|
57
|
+
class Person(pylpg.node.Node):
|
|
58
|
+
name: str
|
|
59
|
+
age: int | None = None
|
|
60
|
+
|
|
61
|
+
class Knows(pylpg.relationship.Relationship):
|
|
62
|
+
__type__ = "KNOWS"
|
|
63
|
+
since: str | None = None
|
|
64
|
+
|
|
65
|
+
backend = pylpg.backend.neo4j.Neo4jBackend(
|
|
66
|
+
hostname="localhost",
|
|
67
|
+
username="neo4j",
|
|
68
|
+
password="password",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
with pylpg.session.Session(backend) as session:
|
|
72
|
+
alice = Person(name="Alice", age=30)
|
|
73
|
+
bob = Person(name="Bob")
|
|
74
|
+
session.save(alice)
|
|
75
|
+
session.save(bob)
|
|
76
|
+
|
|
77
|
+
rel = Knows(source=alice, target=bob, since="2024")
|
|
78
|
+
session.save(rel)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
Full documentation is available at [https://adrienrougny.github.io/pylpg/](https://adrienrougny.github.io/pylpg/).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
GPLv3. See [LICENSE](LICENSE) for details.
|
pylpg-0.1.0/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# pylpg
|
|
2
|
+
|
|
3
|
+
A Python Object Graph Mapper for labeled property graph databases.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Simple model definition using Python type annotations
|
|
8
|
+
- Multi-backend: Neo4j, FalkorDB, FalkorDBLite (embedded)
|
|
9
|
+
- Session-based persistence with node binding
|
|
10
|
+
- Transactional batch operations (Neo4j)
|
|
11
|
+
- Node hydration from raw query results
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install pylpg[neo4j] # Neo4j
|
|
17
|
+
pip install pylpg[falkordb] # FalkorDB
|
|
18
|
+
pip install pylpg[falkordblite] # FalkorDBLite (embedded, no server)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick example
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import pylpg.node
|
|
25
|
+
import pylpg.relationship
|
|
26
|
+
import pylpg.session
|
|
27
|
+
import pylpg.backend.neo4j
|
|
28
|
+
|
|
29
|
+
class Person(pylpg.node.Node):
|
|
30
|
+
name: str
|
|
31
|
+
age: int | None = None
|
|
32
|
+
|
|
33
|
+
class Knows(pylpg.relationship.Relationship):
|
|
34
|
+
__type__ = "KNOWS"
|
|
35
|
+
since: str | None = None
|
|
36
|
+
|
|
37
|
+
backend = pylpg.backend.neo4j.Neo4jBackend(
|
|
38
|
+
hostname="localhost",
|
|
39
|
+
username="neo4j",
|
|
40
|
+
password="password",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
with pylpg.session.Session(backend) as session:
|
|
44
|
+
alice = Person(name="Alice", age=30)
|
|
45
|
+
bob = Person(name="Bob")
|
|
46
|
+
session.save(alice)
|
|
47
|
+
session.save(bob)
|
|
48
|
+
|
|
49
|
+
rel = Knows(source=alice, target=bob, since="2024")
|
|
50
|
+
session.save(rel)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Documentation
|
|
54
|
+
|
|
55
|
+
Full documentation is available at [https://adrienrougny.github.io/pylpg/](https://adrienrougny.github.io/pylpg/).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
GPLv3. See [LICENSE](LICENSE) for details.
|
pylpg-0.1.0/cliff.toml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[changelog]
|
|
2
|
+
header = """# Changelog
|
|
3
|
+
|
|
4
|
+
All notable changes to pylpg will be documented in this file.
|
|
5
|
+
|
|
6
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
7
|
+
and this project adheres to [Conventional Commits](https://www.conventionalcommits.org/).
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
body = """
|
|
11
|
+
{%- macro remote_url() -%}
|
|
12
|
+
https://github.com/rougny/pylpg
|
|
13
|
+
{%- endmacro -%}
|
|
14
|
+
|
|
15
|
+
{% if version -%}
|
|
16
|
+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
|
17
|
+
{% else -%}
|
|
18
|
+
## [Unreleased]
|
|
19
|
+
{% endif -%}
|
|
20
|
+
|
|
21
|
+
{% for group, commits in commits | group_by(attribute="group") %}
|
|
22
|
+
### {{ group | upper_first }}
|
|
23
|
+
{% for commit in commits %}
|
|
24
|
+
- {{ commit.message | upper_first | trim }}\
|
|
25
|
+
{% if commit.scope %} *({{ commit.scope }})*{% endif %}\
|
|
26
|
+
{% if commit.breaking %} **BREAKING**{% endif -%}
|
|
27
|
+
{% endfor %}
|
|
28
|
+
{% endfor %}
|
|
29
|
+
"""
|
|
30
|
+
footer = ""
|
|
31
|
+
trim = true
|
|
32
|
+
|
|
33
|
+
[git]
|
|
34
|
+
conventional_commits = true
|
|
35
|
+
filter_unconventional = false
|
|
36
|
+
split_commits = false
|
|
37
|
+
commit_parsers = [
|
|
38
|
+
{ message = "^feat", group = "Added" },
|
|
39
|
+
{ message = "^fix", group = "Fixed" },
|
|
40
|
+
{ message = "^perf", group = "Performance" },
|
|
41
|
+
{ message = "^refactor", group = "Changed" },
|
|
42
|
+
{ message = "^doc", group = "Documentation" },
|
|
43
|
+
{ message = "^style", group = "Styling" },
|
|
44
|
+
{ message = "^test", group = "Testing" },
|
|
45
|
+
{ message = "^ci", group = "CI/CD" },
|
|
46
|
+
{ message = "^chore", group = "Miscellaneous" },
|
|
47
|
+
{ message = ".*", group = "Other" },
|
|
48
|
+
]
|
|
49
|
+
protect_breaking_commits = false
|
|
50
|
+
filter_commits = false
|
|
51
|
+
tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+"
|
|
52
|
+
sort_commits = "newest"
|