remapper


Nameremapper JSON
Version 1.0.2 PyPI version JSON
download
home_pageNone
SummaryTransform objects to and from similar structural mappings.
upload_time2025-07-25 17:44:21
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords mapping dataclass presentation models util
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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/49/6c/985b35d69b5165dc79403f4d8ddbe9a58ffab1e5eb7f1b37de1c49cdcc55/remapper-1.0.2.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.2",
    "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": "326a20c49a32f1b27780605dd760bcc96af01e15a90e494ecfe453c9110392fc",
                "md5": "172511c705f8994143bfa4059dd06f67",
                "sha256": "cba2e5e54a22da25e6951448ce43bd1d3138cc791d8da92d74dc084de6684ff5"
            },
            "downloads": -1,
            "filename": "remapper-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "172511c705f8994143bfa4059dd06f67",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 5486,
            "upload_time": "2025-07-25T17:44:20",
            "upload_time_iso_8601": "2025-07-25T17:44:20.953688Z",
            "url": "https://files.pythonhosted.org/packages/32/6a/20c49a32f1b27780605dd760bcc96af01e15a90e494ecfe453c9110392fc/remapper-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "496c985b35d69b5165dc79403f4d8ddbe9a58ffab1e5eb7f1b37de1c49cdcc55",
                "md5": "cfac6cd8368078ed890b572ed025f224",
                "sha256": "31abb2794657df4e03a56c7a95b455d436c0158b97c2bc462f13e9725d343498"
            },
            "downloads": -1,
            "filename": "remapper-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "cfac6cd8368078ed890b572ed025f224",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 5592,
            "upload_time": "2025-07-25T17:44:21",
            "upload_time_iso_8601": "2025-07-25T17:44:21.842366Z",
            "url": "https://files.pythonhosted.org/packages/49/6c/985b35d69b5165dc79403f4d8ddbe9a58ffab1e5eb7f1b37de1c49cdcc55/remapper-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-25 17:44:21",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "thearchitector",
    "github_project": "remapper",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "remapper"
}
        
Elapsed time: 1.55787s