# remapper
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/thearchitector/remapper/test.yaml?label=tests&style=flat-square)
![PyPI - Downloads](https://img.shields.io/pypi/dm/remapper?style=flat-square)
![GitHub](https://img.shields.io/github/license/thearchitector/remapper?style=flat-square)
Transform objects to and from similar structural mappings. Useful for translating between sources of truth and presentational models of data.
Supports Python 3.8+. No dependencies.
## Installation
```sh
$ pdm add remapper
# or
$ python -m pip install --user remapper
```
## Usage
> The examples below use dataclasses because they're easy, but `remap` works with any destination type that exposes attributes via its `__init__`.
A trivial example for `remap` is converting one dataclass into another without having to manually pass attributes:
```python
from dataclasses import dataclass
from remapper import remap
@dataclass
class Source:
a: int
b: int
c: int
@dataclass
class Destination:
a: int
b: int
c: int
dest = remap(Source(a=1, b=2, c=3), Destination)
# >> Destination(a=1, b=2, c=3)
```
A more useful example would be in mapping an internal database model to an externally-facing dataclass:
```python
from dataclasses import dataclass
from remapper import remap
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column
class Base(DeclarativeBase, MappedAsDataclass):
pass
class Source(Base):
id: Mapped[int] = mapped_column(init=False, primary_key=True)
a: Mapped[int]
b: Mapped[int]
c: Mapped[int]
@dataclass
class Destination:
a: int
b: int
c: int
@property
def d(self) -> int:
return self.a + self.b + self.c
source = Source(a=1, b=2, c=3)
await session.commit(source)
dest = remap(source, Destination)
dest.d
# >> 6
```
### Overrides
There's a non-zero chance that a destination may require more data than available on a source. In those cases, you can manually provide values via the `overrides` keyword argument. Values in `overrides` will take precedence over values on the source:
```python
from dataclasses import dataclass
from remapper import remap
@dataclass
class Source:
a: int
b: int
@dataclass
class Destination:
a: int
b: int
c: int
dest = remap(Source(a=1, b=2), Destination, overrides={"b": 0, "c": 3})
# >> Destination(a=1, b=0, c=3)
```
### Defaults
If a destination defines more attributes than available on a source, but the destination has default values for those additional attributes, you don't need to supply overrides:
```python
from dataclasses import dataclass
from remapper import remap
@dataclass
class Source:
a: int
b: int
@dataclass
class Destination:
a: int
b: int
c: int = 3
dest = remap(Source(a=1, b=2), Destination)
# >> Destination(a=1, b=2, c=3)
```
### Nested Types
Some complex sources may have attributes that themselves need to be converted. Rather than remapping in several steps, you can use the `nested_types` keyword argument to specify inner types of the destination attributes:
```python
from dataclasses import dataclass
from remapper import remap
@dataclass
class Source:
b: int
@dataclass
class Destination:
b: int
@dataclass
class ParentSource:
a: int
child: Source
@dataclass
class ParentDestination:
a: int
child: Destination
## this works but is kind of clunky
source = ParentSource(a=1, child=Source(b=1))
dest_child = remap(source.child, Destination)
dest = remap(source, ParentDestination, overrides={"child": dest_child})
# >> ParentDestination(a=1,child=Destination(b=1))
## so use this instead
dest = remap(
ParentSource(a=1, child=Source(b=1)),
ParentDestination,
nested_types={"child": Destination},
)
# >> ParentDestination(a=1,child=Destination(b=1))
```
## License
This software is licensed under the [BSD 3-Clause Clear License](LICENSE).
Raw data
{
"_id": null,
"home_page": null,
"name": "remapper",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "mapping, dataclass, presentation, models, util",
"author": null,
"author_email": "Elias Gabriel <oss@eliasfgabriel.com>",
"download_url": "https://files.pythonhosted.org/packages/05/73/27f05b3127ba4fe16226a04074d8ce715596619d9a5742ed1045575343bd/remapper-1.0.0.post0.tar.gz",
"platform": null,
"description": "# remapper\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/thearchitector/remapper/test.yaml?label=tests&style=flat-square)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/remapper?style=flat-square)\n![GitHub](https://img.shields.io/github/license/thearchitector/remapper?style=flat-square)\n\nTransform objects to and from similar structural mappings. Useful for translating between sources of truth and presentational models of data.\n\nSupports Python 3.8+. No dependencies.\n\n## Installation\n\n```sh\n$ pdm add remapper\n# or\n$ python -m pip install --user remapper\n```\n\n## Usage\n\n> The examples below use dataclasses because they're easy, but `remap` works with any destination type that exposes attributes via its `__init__`.\n\nA trivial example for `remap` is converting one dataclass into another without having to manually pass attributes:\n\n```python\nfrom dataclasses import dataclass\n\nfrom remapper import remap\n\n\n@dataclass\nclass Source:\n a: int\n b: int\n c: int\n\n\n@dataclass\nclass Destination:\n a: int\n b: int\n c: int\n\n\ndest = remap(Source(a=1, b=2, c=3), Destination)\n# >> Destination(a=1, b=2, c=3)\n```\n\nA more useful example would be in mapping an internal database model to an externally-facing dataclass:\n\n```python\nfrom dataclasses import dataclass\n\nfrom remapper import remap\nfrom sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column\n\n\nclass Base(DeclarativeBase, MappedAsDataclass):\n pass\n\n\nclass Source(Base):\n id: Mapped[int] = mapped_column(init=False, primary_key=True)\n\n a: Mapped[int]\n b: Mapped[int]\n c: Mapped[int]\n\n\n@dataclass\nclass Destination:\n a: int\n b: int\n c: int\n\n @property\n def d(self) -> int:\n return self.a + self.b + self.c\n\n\nsource = Source(a=1, b=2, c=3)\nawait session.commit(source)\n\ndest = remap(source, Destination)\ndest.d\n# >> 6\n```\n\n### Overrides\n\nThere's a non-zero chance that a destination may require more data than available on a source. In those cases, you can manually provide values via the `overrides` keyword argument. Values in `overrides` will take precedence over values on the source:\n\n```python\nfrom dataclasses import dataclass\n\nfrom remapper import remap\n\n\n@dataclass\nclass Source:\n a: int\n b: int\n\n\n@dataclass\nclass Destination:\n a: int\n b: int\n c: int\n\n\ndest = remap(Source(a=1, b=2), Destination, overrides={\"b\": 0, \"c\": 3})\n# >> Destination(a=1, b=0, c=3)\n```\n\n### Defaults\n\nIf a destination defines more attributes than available on a source, but the destination has default values for those additional attributes, you don't need to supply overrides:\n\n```python\nfrom dataclasses import dataclass\n\nfrom remapper import remap\n\n\n@dataclass\nclass Source:\n a: int\n b: int\n\n\n@dataclass\nclass Destination:\n a: int\n b: int\n c: int = 3\n\n\ndest = remap(Source(a=1, b=2), Destination)\n# >> Destination(a=1, b=2, c=3)\n```\n\n### Nested Types\n\nSome complex sources may have attributes that themselves need to be converted. Rather than remapping in several steps, you can use the `nested_types` keyword argument to specify inner types of the destination attributes:\n\n```python\nfrom dataclasses import dataclass\n\nfrom remapper import remap\n\n\n@dataclass\nclass Source:\n b: int\n\n\n@dataclass\nclass Destination:\n b: int\n\n\n@dataclass\nclass ParentSource:\n a: int\n child: Source\n\n\n@dataclass\nclass ParentDestination:\n a: int\n child: Destination\n\n\n## this works but is kind of clunky\nsource = ParentSource(a=1, child=Source(b=1))\ndest_child = remap(source.child, Destination)\ndest = remap(source, ParentDestination, overrides={\"child\": dest_child})\n# >> ParentDestination(a=1,child=Destination(b=1))\n\n## so use this instead\ndest = remap(\n ParentSource(a=1, child=Source(b=1)),\n ParentDestination,\n nested_types={\"child\": Destination},\n)\n# >> ParentDestination(a=1,child=Destination(b=1))\n```\n\n## License\n\nThis software is licensed under the [BSD 3-Clause Clear License](LICENSE).\n",
"bugtrack_url": null,
"license": null,
"summary": "Transform objects to and from similar structural mappings.",
"version": "1.0.0.post0",
"project_urls": {
"changelog": "https://github.com/thearchitector/remapper/releases",
"homepage": "https://github.com/thearchitector/remapper",
"issues": "https://github.com/thearchitector/remapper/issues",
"source": "https://github.com/thearchitector/remapper"
},
"split_keywords": [
"mapping",
" dataclass",
" presentation",
" models",
" util"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a7d1920d8c553423e3b1590c171bbefbb18419c691eb9c4278ac0ed3a87e3585",
"md5": "598ba21607b2106e942dca4456661543",
"sha256": "61ba9d6321b62d6487f354864599a6402e1f58deafe703c8e50c509429f16058"
},
"downloads": -1,
"filename": "remapper-1.0.0.post0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "598ba21607b2106e942dca4456661543",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 5415,
"upload_time": "2025-01-26T07:33:12",
"upload_time_iso_8601": "2025-01-26T07:33:12.326863Z",
"url": "https://files.pythonhosted.org/packages/a7/d1/920d8c553423e3b1590c171bbefbb18419c691eb9c4278ac0ed3a87e3585/remapper-1.0.0.post0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "057327f05b3127ba4fe16226a04074d8ce715596619d9a5742ed1045575343bd",
"md5": "cfa95cb165d076f52bdaf1f5f278466d",
"sha256": "1801103b38801de5a00f1c54b8af62e6d43250dde90b5469a75380a8159b889d"
},
"downloads": -1,
"filename": "remapper-1.0.0.post0.tar.gz",
"has_sig": false,
"md5_digest": "cfa95cb165d076f52bdaf1f5f278466d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 5559,
"upload_time": "2025-01-26T07:33:14",
"upload_time_iso_8601": "2025-01-26T07:33:14.057599Z",
"url": "https://files.pythonhosted.org/packages/05/73/27f05b3127ba4fe16226a04074d8ce715596619d9a5742ed1045575343bd/remapper-1.0.0.post0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-26 07:33:14",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "thearchitector",
"github_project": "remapper",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "remapper"
}