Metadata-Version: 2.4
Name: rdfmapper
Version: 0.2.0
Summary: A declarative Object-RDF Mapper for Python — map Python classes to RDF graphs using decorators.
Author-email: Felipe dos Santos Goiabeira <felipe.goiabeira@discente.ufma.br>, Sergio Costa <sergio.costa@ufma.br>
License: MIT
Project-URL: Homepage, https://github.com/lambdageo/rdfmapper
Project-URL: Repository, https://github.com/lambdageo/rdfmapper
Project-URL: Issues, https://github.com/lambdageo/rdfmapper/issues
Keywords: rdf,semantic web,linked data,orm,sparql,shacl
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: rdflib>=6.0.0
Requires-Dist: pyshacl>=0.20.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mkdocs<2.0,>=1.5; extra == "dev"
Requires-Dist: mkdocstrings[python]>=0.24; extra == "dev"
Requires-Dist: mkdocs-material>=9.0; extra == "dev"

# rdfmapper

**rdfmapper** is a declarative Object-RDF Mapper for Python. It lets you map Python classes to RDF graphs using decorators, inspired by ORM frameworks such as JPA and SQLAlchemy, without requiring you to write SPARQL or manipulate triples manually.

[![Tests](https://github.com/lambdageo/rdfmapper/actions/workflows/ci.yml/badge.svg)](https://github.com/lambdageo/rdfmapper/actions)
[![PyPI](https://img.shields.io/pypi/v/rdfmapper-py)](https://pypi.org/project/rdfmapper-py/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python](https://img.shields.io/pypi/pyversions/rdfmapper-py)](https://pypi.org/project/rdfmapper-py/)

---

## Features

- Declarative mapping of Python classes to RDF types and predicates via decorators
- Support for `one-to-one` and `one-to-many` relationships
- Automatic serialization and deserialization between Python objects and RDF graphs
- Dynamic query repository (`find_by_*`, `count_by_*`, `group_by_count`) powered by SPARQL
- Automatic SHACL shape generation and validation from class metadata
- Circular reference detection during serialization and deserialization
- Type-aware literal conversion (int, float, bool, date, datetime)

---

## Installation

```bash
pip install rdfmapper-py
```

Or install from source:

```bash
git clone https://github.com/lambdageo/rdfmapper.git
cd rdfmapper
pip install -e ".[dev]"
```

---

## Quick start

```python
from rdflib import Namespace
from rdfmapper import RDFMapper, RDFRepository

EX = Namespace("http://example.org/")
FOAF = Namespace("http://xmlns.com/foaf/0.1/")

mapper = RDFMapper()


@mapper.rdf_entity(EX.Person)
class Person:
    def __init__(self, uri, name: str, age: int = None):
        self.uri = uri
        self._name = name
        self._age = age

    @mapper.rdf_property(FOAF.name, minCount=1)
    def name(self): pass

    @mapper.rdf_property(FOAF.age)
    def age(self): pass


# Serialize to RDF
person = Person(uri=EX["person/1"], name="Felipe", age=25)
graph = mapper.to_rdf(person)
print(graph.serialize(format="turtle"))

# Deserialize back to Python
restored = mapper.from_rdf(graph, Person, str(EX["person/1"]))
print(restored.name)  # Felipe

# Query with repository
repo = RDFRepository(mapper, graph, Person)
results = repo.find_by_name(name="Felipe")
count = repo.count_by_name(name="Felipe")
```

---

## Relationships

```python
@mapper.rdf_entity(EX.Address)
class Address:
    def __init__(self, uri, city: str):
        self.uri = uri
        self._city = city

    @mapper.rdf_property(EX.city)
    def city(self): pass


@mapper.rdf_entity(EX.Person)
class Person:
    def __init__(self, uri, name: str, address=None, phones=None):
        self.uri = uri
        self._name = name
        self._address = address
        self._phones = phones or []

    @mapper.rdf_property(FOAF.name)
    def name(self): pass

    @mapper.rdf_one_to_one(EX.address, target_class=lambda: Address)
    def address(self): pass

    @mapper.rdf_one_to_many(EX.phone, target_class=lambda: Phone)
    def phones(self): pass
```

---

## SHACL validation

```python
# Auto-generate SHACL shape from class metadata
shacl_graph = mapper.to_shacl(Person)

# Validate an RDF graph
conforms, _, report = mapper.validate(graph, entity_class=Person)
print("Conforms:", conforms)
print(report)
```

---

## Dynamic repository queries

```python
repo = RDFRepository(mapper, graph, Person)

# Exact match
repo.find_by_name(name="Felipe")

# Regex match
repo.find_by_name_like(name="Fel")

# Compound filter
repo.find_by_name_and_age(name="Felipe", age=25)

# Pagination
repo.find_by_name(name="Felipe", limit=10, offset=0)

# Count
repo.count_by_name(name="Felipe")

# Aggregation
repo.group_by_count(Person, "name", order="DESC")
```

---

## Examples

See the [`examples/`](examples/) directory for complete runnable scripts:

- [`examples/basic/person_shacl.py`](examples/basic/person_shacl.py) — basic mapping and SHACL validation
- [`examples/relationships/person_address.py`](examples/relationships/person_address.py) — one-to-one and one-to-many relationships

---

## Development

```bash
# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Type checking
mypy src/
```

---

## Citation

If you use rdfmapper in your research, please cite:

```bibtex
@software{goiabeira2025pyrdm,
  author    = {Goiabeira, Felipe dos Santos and Costa, Sergio Souza},
  title     = {rdfmapper: A Declarative Object-RDF Mapper for Python},
  year      = {2025},
  publisher = {GitHub},
  url       = {https://github.com/lambdageo/rdfmapper}
}
```

---

## License

MIT — see [LICENSE](LICENSE).

---

## Acknowledgements

rdfmapper was originally developed as part of a Bachelor's thesis at the Federal University of Maranhão (UFMA) by Felipe dos Santos Goiabeira, advised by Prof. Dr. Sergio Souza Costa, LambdaGeo Research Group.
