# kadet
Easily define and reuse complex Python objects that serialize into JSON or YAML.
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/kapicorp/kadet/Python%20lint%20and%20tests)
## Example
```python
from kadet import BaseObj
from pprint import pprint
ships = BaseObj()
ships.root.type.container = ["panamax", "suezmax", "post-panamax"]
ships.root.type.carrier = ["conventional", "geared", "gearless"]
ships.root.type.tanker = BaseObj.from_yaml("tankers.yml")
pprint(ships.root)
# output
{'type': {'carrier': ['conventional',
'geared',
'gearless'],
'container': ['panamax',
'suezmax',
'post-panamax'],
'tanker': ['oil', 'liquified-gas', 'chemical']}}
```
## Installation
Install using `pip install kadet`.
## Overview
### BaseObj
BaseObj implements the basic object that serializes into JSON or YAML.
Setting keys in `self.root` means they will be serialized. Keys can be set as an hierarchy of attributes.
The `self.body()` method is reserved for setting self.root on instantiation.
The example below:
```python
class MyApp(BaseObj):
def body(self):
self.root.name = "myapp"
self.root.inner.foo = "bar"
self.root.list = [1, 2, 3]
yaml.dump(MyApp().dump())
```
serializes into:
```yaml
---
name: myapp
inner:
foo: bar
list:
- 1
- 2
- 3
```
The `self.new()` method can be used to define a basic constructor.
`self.need()` checks if a key is set and errors if it isn't (with an optional custom error message).
`self.optional()` sets a key as optional. Use `default` keyword to set default value when not set.
Both `self.new()` and `self.body()` method accept the `istype` keyword to validate value type on runtime.
Supports `typing` types.
`kwargs` that are passed onto a new instance of BaseObj are always accessible via `self.kwargs`
`self.new_with()` is an utility method to call `super().new()` while passing kwargs to the super class.
In this example, MyApp needs `name` and `foo` to be passed as kwargs.
```python
class MyApp(BaseObj):
def new(self):
self.need("name")
self.need("foo", msg="please provide a value for foo")
self.optional("baz")
def body(self):
self.root.name = self.kwargs.name
self.root.inner.foo = self.kwargs.foo
self.root.list = [1, 2, 3]
obj = MyApp(name="myapp", foo="bar")
```
### Setting a skeleton
Defining a large body with Python can be quite hard and repetitive to read and write.
The `self.root_file()` method allows importing a YAML/JSON file to set `self.root`.
MyApp's skeleton can be set instead like this:
```yaml
#skel.yml
---
name: myapp
inner:
foo: bar
list:
- 1
- 2
- 3
```
```python
class MyApp(BaseObj):
def new(self):
self.need("name")
self.need("foo", msg="please provide a value for foo")
self.root_file("path/to/skel.yml")
```
Extending a MyApp's skeleton is possible just by implementing `self.body()`:
```python
class MyApp(BaseObj):
def new(self):
self.need("name")
self.need("foo", msg="please provide a value for foo")
self.root_file("path/to/skel.yml")
def body(self):
self.set_replicas()
self.root.metadata.labels = {"app": "mylabel"}
def set_replicas(self):
self.root.spec.replicas = 5
```
### Inheritance
Python inheritance will work as expected:
```python
class MyOtherApp(MyApp):
def new(self):
super().new() # MyApp's new()
self.need("size")
def body(self):
super().body() # we want to extend MyApp's body
self.root.size = self.kwargs.size
del self.root.list # get rid of "list"
obj = MyOtherApp(name="otherapp1", foo="bar2", size=3)
yaml.dump(obj.dump())
```
serializes to:
```yaml
---
name: otherapp1
inner:
foo: bar2
replicas: 5
size: 3
```
### BaseModel
BaseModel integrates Kadet semantics with [Pydantic](https://github.com/pydantic/pydantic)'s BaseModel together with powerful data validation and type hinting features.
Just like in BaseObj, keys in `self.root` will be serialized, but kwargs is no longer necessary as BaseModel's parameters are set as attributes in `self`.
The `self.body()` method is reserved for setting self.root on instantiation.
The example below:
```python
class Boat(BaseModel):
name: str # Required
length: int # Required
description: str = "I am a boat" # Default description
def body(self):
self.root.name = self.name
self.root.details.length = self.length
self.root.details.description = self.description
print(yaml.dump(Boat(name="Boaty", length=600).dump()))
---
details:
description: I am a boat
length: 600
name: Boaty
```
Raw data
{
"_id": null,
"home_page": "https://github.com/kapicorp/kadet",
"name": "kadet",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "config, kapitan, kadet, yaml, json",
"author": "Ricardo Amaro",
"author_email": "ramaro@kapicorp.com",
"download_url": "https://files.pythonhosted.org/packages/57/6c/0a257cd62ce85a3360b9b4b191958f4afb0b3fd919b3ac4570b176e78528/kadet-0.3.2.tar.gz",
"platform": null,
"description": "# kadet\n\nEasily define and reuse complex Python objects that serialize into JSON or YAML.\n\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/kapicorp/kadet/Python%20lint%20and%20tests)\n\n## Example\n\n```python\nfrom kadet import BaseObj\nfrom pprint import pprint\n\nships = BaseObj()\nships.root.type.container = [\"panamax\", \"suezmax\", \"post-panamax\"]\nships.root.type.carrier = [\"conventional\", \"geared\", \"gearless\"]\nships.root.type.tanker = BaseObj.from_yaml(\"tankers.yml\")\n\npprint(ships.root)\n\n# output\n{'type': {'carrier': ['conventional',\n 'geared',\n 'gearless'],\n 'container': ['panamax',\n 'suezmax',\n 'post-panamax'],\n 'tanker': ['oil', 'liquified-gas', 'chemical']}}\n```\n\n## Installation\n\nInstall using `pip install kadet`.\n\n## Overview\n\n### BaseObj\n\nBaseObj implements the basic object that serializes into JSON or YAML.\nSetting keys in `self.root` means they will be serialized. Keys can be set as an hierarchy of attributes.\n\nThe `self.body()` method is reserved for setting self.root on instantiation.\n\nThe example below:\n\n```python\nclass MyApp(BaseObj):\n def body(self):\n self.root.name = \"myapp\"\n self.root.inner.foo = \"bar\"\n self.root.list = [1, 2, 3]\n\nyaml.dump(MyApp().dump())\n```\n\nserializes into:\n\n```yaml\n---\nname: myapp\ninner:\n foo: bar\nlist:\n - 1\n - 2\n - 3\n```\n\nThe `self.new()` method can be used to define a basic constructor.\n\n`self.need()` checks if a key is set and errors if it isn't (with an optional custom error message).\n`self.optional()` sets a key as optional. Use `default` keyword to set default value when not set.\n\nBoth `self.new()` and `self.body()` method accept the `istype` keyword to validate value type on runtime.\nSupports `typing` types.\n\n`kwargs` that are passed onto a new instance of BaseObj are always accessible via `self.kwargs`\n\n`self.new_with()` is an utility method to call `super().new()` while passing kwargs to the super class.\n\nIn this example, MyApp needs `name` and `foo` to be passed as kwargs.\n\n```python\nclass MyApp(BaseObj):\n def new(self):\n self.need(\"name\")\n self.need(\"foo\", msg=\"please provide a value for foo\")\n self.optional(\"baz\")\n\n def body(self):\n self.root.name = self.kwargs.name\n self.root.inner.foo = self.kwargs.foo\n self.root.list = [1, 2, 3]\n\nobj = MyApp(name=\"myapp\", foo=\"bar\")\n```\n\n### Setting a skeleton\n\nDefining a large body with Python can be quite hard and repetitive to read and write.\n\nThe `self.root_file()` method allows importing a YAML/JSON file to set `self.root`.\n\nMyApp's skeleton can be set instead like this:\n\n```yaml\n#skel.yml\n---\nname: myapp\ninner:\n foo: bar\nlist:\n - 1\n - 2\n - 3\n```\n\n```python\nclass MyApp(BaseObj):\n def new(self):\n self.need(\"name\")\n self.need(\"foo\", msg=\"please provide a value for foo\")\n self.root_file(\"path/to/skel.yml\")\n```\n\nExtending a MyApp's skeleton is possible just by implementing `self.body()`:\n\n```python\nclass MyApp(BaseObj):\n def new(self):\n self.need(\"name\")\n self.need(\"foo\", msg=\"please provide a value for foo\")\n self.root_file(\"path/to/skel.yml\")\n\n def body(self):\n self.set_replicas()\n self.root.metadata.labels = {\"app\": \"mylabel\"}\n\n def set_replicas(self):\n self.root.spec.replicas = 5\n```\n\n### Inheritance\n\nPython inheritance will work as expected:\n\n```python\n\nclass MyOtherApp(MyApp):\n def new(self):\n super().new() # MyApp's new()\n self.need(\"size\")\n\n def body(self):\n super().body() # we want to extend MyApp's body\n self.root.size = self.kwargs.size\n del self.root.list # get rid of \"list\"\n\nobj = MyOtherApp(name=\"otherapp1\", foo=\"bar2\", size=3)\nyaml.dump(obj.dump())\n```\nserializes to:\n\n```yaml\n---\nname: otherapp1\ninner:\n foo: bar2\nreplicas: 5\nsize: 3\n```\n\n### BaseModel\n\nBaseModel integrates Kadet semantics with [Pydantic](https://github.com/pydantic/pydantic)'s BaseModel together with powerful data validation and type hinting features.\nJust like in BaseObj, keys in `self.root` will be serialized, but kwargs is no longer necessary as BaseModel's parameters are set as attributes in `self`.\n\nThe `self.body()` method is reserved for setting self.root on instantiation.\n\nThe example below:\n\n```python\nclass Boat(BaseModel):\n name: str # Required\n length: int # Required\n description: str = \"I am a boat\" # Default description\n\n def body(self):\n self.root.name = self.name\n self.root.details.length = self.length\n self.root.details.description = self.description\n\nprint(yaml.dump(Boat(name=\"Boaty\", length=600).dump()))\n\n---\ndetails:\n description: I am a boat\n length: 600\nname: Boaty\n```\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "Easily define and reuse complex Python objects that serialize into JSON or YAML.",
"version": "0.3.2",
"project_urls": {
"Homepage": "https://github.com/kapicorp/kadet",
"Repository": "https://github.com/kapicorp/kadet"
},
"split_keywords": [
"config",
" kapitan",
" kadet",
" yaml",
" json"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "be14134502054b3fd355e1b8c4d36ccddf08b79dcd26fd3d7f6caa46edf88e6c",
"md5": "af31ddff5f74cd4449cd4c1a2201df93",
"sha256": "e801fe01d1f9baf169fa176c8ed046e654f437162fe8e88e88281eea9bf94d12"
},
"downloads": -1,
"filename": "kadet-0.3.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "af31ddff5f74cd4449cd4c1a2201df93",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 8858,
"upload_time": "2024-05-13T13:51:48",
"upload_time_iso_8601": "2024-05-13T13:51:48.695804Z",
"url": "https://files.pythonhosted.org/packages/be/14/134502054b3fd355e1b8c4d36ccddf08b79dcd26fd3d7f6caa46edf88e6c/kadet-0.3.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "576c0a257cd62ce85a3360b9b4b191958f4afb0b3fd919b3ac4570b176e78528",
"md5": "2e20f34667ee66e9d73ffd5693c77ebe",
"sha256": "c746e341d7cf8b0c623344cb033e9fd09bbbda57e4cc664e45e6c2897c772860"
},
"downloads": -1,
"filename": "kadet-0.3.2.tar.gz",
"has_sig": false,
"md5_digest": "2e20f34667ee66e9d73ffd5693c77ebe",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 8403,
"upload_time": "2024-05-13T13:51:50",
"upload_time_iso_8601": "2024-05-13T13:51:50.251111Z",
"url": "https://files.pythonhosted.org/packages/57/6c/0a257cd62ce85a3360b9b4b191958f4afb0b3fd919b3ac4570b176e78528/kadet-0.3.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-13 13:51:50",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "kapicorp",
"github_project": "kadet",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "kadet"
}