{"id":13770420,"url":"https://github.com/holinnn/lupin","last_synced_at":"2025-03-21T03:33:40.541Z","repository":{"id":26032666,"uuid":"105884622","full_name":"holinnn/lupin","owner":"holinnn","description":"Python document object mapper (load python object from JSON and vice-versa)","archived":false,"fork":false,"pushed_at":"2022-02-12T18:09:27.000Z","size":130,"stargazers_count":26,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-03-17T20:43:29.185Z","etag":null,"topics":["deserialization","json","mapper","python","schema","serialization","validation"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/holinnn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.rst","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-05T11:59:47.000Z","updated_at":"2024-10-04T09:36:18.000Z","dependencies_parsed_at":"2022-08-07T11:16:19.380Z","dependency_job_id":null,"html_url":"https://github.com/holinnn/lupin","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Flupin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Flupin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Flupin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/holinnn%2Flupin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/holinnn","download_url":"https://codeload.github.com/holinnn/lupin/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244734070,"owners_count":20501014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deserialization","json","mapper","python","schema","serialization","validation"],"created_at":"2024-08-03T17:00:37.224Z","updated_at":"2025-03-21T03:33:40.236Z","avatar_url":"https://github.com/holinnn.png","language":"Python","funding_links":[],"categories":["Model, Schema","Python"],"sub_categories":[],"readme":"# lupin is a Python JSON object mapper\n\n\nlupin is meant to help in serializing python objects to JSON and unserializing JSON data to python objects.\n\n\n## Installation\n\n```\npip install lupin\n```\n\n## Usage\n\nlupin uses schemas to create a representation of a python object.\n\nA schema is composed of fields which represents the way to load and dump an attribute of an object.\n\n### Define schemas\n\n```python\nfrom datetime import datetime\nfrom lupin import Mapper, Schema, fields as f\n\n\n# 1) Define your models\nclass Thief(object):\n    def __init__(self, name, stolen_items):\n        self.name = name\n        self.stolen_items = stolen_items\n\n\nclass Painting(object):\n    def __init__(self, name, author):\n        self.name = name\n        self.author = author\n\n\nclass Artist(object):\n    def __init__(self, name, birth_date):\n        self.name = name\n        self.birth_date = birth_date\n\n\n# 2) Create schemas\nartist_schema = Schema({\n    \"name\": f.String(),\n    \"birthDate\": f.DateTime(binding=\"birth_date\", format=\"%Y-%m-%d\")\n}, name=\"artist\")\n\npainting_schema = Schema({\n    \"name\": f.String(),\n    \"author\": f.Object(artist_schema)\n}, name=\"painting\")\n\nthief_schema = Schema({\n    \"name\": f.String(),\n    \"stolenItems\": f.List(painting_schema, binding=\"stolen_items\")\n}, name=\"thief\")\n\n# 3) Create a mapper and register a schema for each of your models you want to map to JSON objects\nmapper = Mapper()\n\nmapper.register(Artist, artist_schema)\nmapper.register(Painting, painting_schema)\nmapper.register(Thief, thief_schema)\n\n\n# 4) Create some sample data\nleonardo = Artist(name=\"Leonardo da Vinci\", birth_date=datetime(1452, 4, 15))\nmona_lisa = Painting(name=\"Mona Lisa\", author=leonardo)\narsene = Thief(name=\"Arsène Lupin\", stolen_items=[mona_lisa])\n```\n\n### Dump objects\n\n```python\n# use mapper to dump python objects\nassert mapper.dump(leonardo) == {\n    \"name\": \"Leonardo da Vinci\",\n    \"birthDate\": \"1452-04-15\"\n}\n\nassert mapper.dump(mona_lisa) == {\n    \"name\": \"Mona Lisa\",\n    \"author\": {\n        \"name\": \"Leonardo da Vinci\",\n        \"birthDate\": \"1452-04-15\"\n    }\n}\n\nassert mapper.dump(arsene) == {\n    \"name\": \"Arsène Lupin\",\n    \"stolenItems\": [\n        {\n            \"name\": \"Mona Lisa\",\n            \"author\": {\n                \"name\": \"Leonardo da Vinci\",\n                \"birthDate\": \"1452-04-15\"\n            }\n        }\n    ]\n}\n```\n\n### Load objects\n\n```python\n# use mapper to load JSON data\ndata = {\n    \"name\": \"Mona Lisa\",\n    \"author\": {\n        \"name\": \"Leonardo da Vinci\",\n        \"birthDate\": \"1452-04-15\"\n    }\n}\npainting = mapper.load(data, \"painting\")  # \"painting\" is the name of the schame you want to use\nartist = painting.author\n\nassert isinstance(painting, Painting)\nassert painting.name == \"Mona Lisa\"\n\nassert isinstance(artist, Artist)\nassert artist.name == \"Leonardo da Vinci\"\nassert artist.birth_date == datetime(1452, 4, 15)\n```\n\n### Polymorphic lists\n\nSometimes a list can contain multiple type of objects. In such cases you will have to use a `PolymorphicList`, you will also need to add\na key in the items schema to store the type of the object (you can use a `Constant` field).\n\nSay that our thief has level up and has stolen a diamond.\n\n```python\nclass Diamond(object):\n    def __init__(self, carat):\n        self.carat = carat\n\n\nmapper = Mapper()\n\n# Register a schema for diamonds\ndiamond_schema = Schema({\n    \"carat\": f.Field(),\n    \"type\": f.Constant(\"diamond\")  # this will be used to know which schema to used while loading JSON\n}, name=\"diamond\")\nmapper.register(Diamond, diamond_schema)\n\n# Change our painting schema in order to include a `type` field\npainting_schema = Schema({\n    \"name\": f.String(),\n    \"type\": f.Constant(\"painting\"),\n    \"author\": f.Object(artist_schema)\n}, name=\"painting\")\nmapper.register(Painting, painting_schema)\n\n# Use `PolymorphicList` for `stolen_items`\nthief_schema = Schema({\n    \"name\": f.String(),\n    \"stolenItems\": f.PolymorphicList(on=\"type\",  # JSON key to lookup for the polymorphic type\n                                     binding=\"stolen_items\",\n                                     schemas={\n                                         \"painting\": painting_schema,  # if `type == \"painting\"` then use painting_schema\n                                         \"diamond\": diamond_schema  # if `type == \"diamond\"` then use diamond_schema\n                                     })\n}, name=\"thief\")\nmapper.register(Thief, thief_schema)\n\n\ndiamond = Diamond(carat=20)\narsene.stolen_items.append(diamond)\n\n# Dump object\ndata = mapper.dump(arsene)\nassert data == {\n    \"name\": \"Arsène Lupin\",\n    \"stolenItems\": [\n        {\n            \"name\": \"Mona Lisa\",\n            \"type\": \"painting\",\n            \"author\": {\n                \"name\": \"Leonardo da Vinci\",\n                \"birthDate\": \"1452-04-15\"\n            }\n        },\n        {\n            \"carat\": 20,\n            \"type\": \"diamond\"\n        }\n    ]\n}\n\n# Load data\nthief = mapper.load(data, \"thief\")\nassert isinstance(thief.stolen_items[0], Painting)\nassert isinstance(thief.stolen_items[1], Diamond)\n```\n\n### Validation\n\nLupin provides a set of builtin validators, you can find them in the [lupin/validators](https://github.com/holinnn/lupin/tree/develop/lupin/validators) folder.\n\nWhile creating your schemas you can assign validators to the fields. Before loading a document lupin will validate\nits format. If one field is invalid, an `InvalidDocument` is raised with all the error detected in the data.\n\nExample :\n\n```python\nfrom lupin import Mapper, Schema, fields as f, validators as v\nfrom lupin.errors import InvalidDocument, InvalidLength\nfrom models import Artist\n\nmapper = Mapper()\n\nartist_schema = Schema({\n    \"name\": f.String(validators=v.Length(max=10)),\n}, name=\"artist\")\nmapper.register(Artist, artist_schema)\n\ndata = {\n    \"name\": \"Leonardo da Vinci\"\n}\n\ntry:\n    mapper.load(data, artist_schema, allow_partial=True)\nexcept InvalidDocument as errors:\n    error = errors[0]\n    assert isinstance(error, InvalidLength)\n    assert error.path == [\"name\"]\n```\n\nCurrent validators are :\n- `DateTimeFormat` (validate that value is a valid datetime format)\n- `Equal` (validate that value is equal to a predefined one)\n- `In` (validate that a value is contained in a set of value)\n- `Length` (validate the length of a value)\n- `Match` (validate the format of a value with a regex)\n- `Type` (validate the type of a value, this validator is already included in all fields to match the field type)\n- `URL` (validate an URL string format)\n- `IsNone` (validate that value is None)\n- `Between` (validate that value belongs to a range)\n\n\n#### Combination\n\nYou can build validators combinations using the `\u0026` and `|` operator.\n\nExample :\n\n```python\nfrom lupin import validators as v\nfrom lupin.errors import ValidationError\n\nvalidators = v.Equal(\"Lupin\") | v.Equal(\"Andrésy\")\n# validators passes only if value is \"Lupin\" or \"Andrésy\"\n\nvalidators(\"Lupin\", [])\n\ntry:\n    validators(\"Holmes\", [])\nexcept ValidationError:\n    print(\"Validation error\")\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fholinnn%2Flupin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fholinnn%2Flupin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fholinnn%2Flupin/lists"}