{"id":23511212,"url":"https://github.com/percolate/bfh","last_synced_at":"2025-07-13T17:08:43.313Z","repository":{"id":35578251,"uuid":"39850654","full_name":"percolate/bfh","owner":"percolate","description":"A Python DSL for schema transformations","archived":false,"fork":false,"pushed_at":"2023-03-16T17:07:18.000Z","size":268,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":60,"default_branch":"master","last_synced_at":"2025-06-24T14:11:31.813Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/percolate.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-28T18:22:15.000Z","updated_at":"2024-10-19T16:30:28.000Z","dependencies_parsed_at":"2024-12-25T12:13:52.410Z","dependency_job_id":"164256a5-86cf-4a04-bab3-df0620ec702a","html_url":"https://github.com/percolate/bfh","commit_stats":{"total_commits":105,"total_committers":8,"mean_commits":13.125,"dds":0.5523809523809524,"last_synced_commit":"8f4c603737c846e1af5d95d839dbda7f43aa605e"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/percolate/bfh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fbfh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fbfh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fbfh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fbfh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/percolate","download_url":"https://codeload.github.com/percolate/bfh/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/percolate%2Fbfh/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265175567,"owners_count":23722661,"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":[],"created_at":"2024-12-25T12:13:45.472Z","updated_at":"2025-07-13T17:08:43.280Z","avatar_url":"https://github.com/percolate.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BFH\n\nA Python DSL for schema transformations\n\nDocs: \u003chttp://percolate.github.io/bfh/\u003e\n\n![bfh](http://timberframe-postandbeamhomes.com/media/uploads/galleries/trusses/naked_trusses/iain_with_beatle_hammer.jpg)\n\nLook, you can put square pegs in round holes:\n\n```python\nclass SquarePeg(Schema):\n    id = IntegerField()\n    name = UnicodeField()\n    width = NumberField()\n\n\nclass RoundHole(Schema):\n    id = UnicodeField()\n    name = UnicodeField()\n    diameter = NumberField()\n\n\ndef largest_square(width):\n    return math.sqrt(2 * width**2)\n\n\nclass SquarePegToRoundHole(Mapping):\n    source_schema = SquarePeg\n    target_schema = RoundHole\n\n    id = Concat('from_square', ':', Str(Get('id')))\n    name = Get('name')\n    diameter = Do(largest_square, Get('width'))\n\nmy_peg = SquarePeg(id=1, name=\"peggy\", width=50)\n\ntransformed = SquarePegToRoundHole().apply(my_peg).serialize()\ntransformed['id']\n# u'from_square:1'\ntransformed['name']\n# u'peggy'\ntransformed['diameter']\n# 70.71067811865476\n```\n\nBFH is a DSL for mapping blobs to other blobs. It can map dict-ish or object-ish\nthings... as long as you got names and the names got values, you can whack it\ninto shape. The use of explicit schema objects is *totally optional*... a\nmapping can be used without input and output schemas... the names in the mapping\nare all you *really* need. Viz.\n\n```python\n\nclass ImpliesSchemas(Mapping):\n    id = Concat('author', ':', Get('nom_de_plume'))\n    name = Get('nom_de_plume')\n    book = Get('best_known_for')\n\nsource = {\n    \"nom_de_plume\": \"Mark Twain\",\n    \"best_known_for\": \"Huckleberry Finn\"\n}\noutput = ImpliesSchemas().apply(source)\n\ntype(output)\n# \u003cclass 'bfh.GenericSchema'\u003e\noutput.serialize().keys()\n# ['book', 'id', 'name']\n\n```\n\nExplicit schemas, however, can help preserve your sanity when things get\ncomplex.\n\n## Validation\n\nWhile BFH can validate a schema, it's not primarily a validation library, OK?\nThere are lots of those out there, so we don't get too fancy here. Just some\nsanity checking.\n\n```python\nmy_peg = SquarePeg(id=1, name=\"peggy\", width=50)\nmy_peg.validate()\n# True\n\nbroken_peg = SquarePeg(id=2, name=\"broken\", width=\"a hundred\")\nbroken_peg.validate()\n# ... (raises Invalid)\n\n```\n\n## Reserved Words\n\nObviously your schema needs a field called 'if' or 'finally'. Use a\ndouble-underscore name and all will be well:\n\n    class Fancy(Schema):\n        # if = IntegerField()  # Ouch! SyntaxError!\n        __if = IntegerField()\n\nYou can init in any of these ways:\n\n    Fancy(__if=1)\n    Fancy(**{\"__if\": 1})\n    Fancy(**{\"if\": 1})\n\nIn a mapping, use the de-dundered name\n\n    Get(\"if\")\n\n## Implicit null values and serialization\n\nGiven a sample dataset:\n\n```json\n    {\n        \"name\": \"peg\",\n        \"age\": null\n    }\n```\n\nIf you call serialize with the default kwargs, you would get the following behavior:\n\n```python\ntransformed = SomeMapping().apply(parsed_data).serialize()\n\u003e\u003e\u003e {'name': 'peg', 'age': None}\n```\n\nBut if `implicit_nulls` is set to True, you would get:\n\n```python\ntransformed = SomeMapping().apply(parsed_data).serialize(implicit_nulls=True)\n\u003e\u003e\u003e {'name': 'peg'}\n```\n\nThis can be useful to filter out null values from a dataset on serialization.\n\n## Build Status\n\nTested on Python 2.7.10 and 3.5.0:\n\n[![Circle CI](https://circleci.com/gh/percolate/bfh.svg?style=svg)](https://circleci.com/gh/percolate/bfh)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpercolate%2Fbfh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpercolate%2Fbfh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpercolate%2Fbfh/lists"}