# Cain
<img align="right" src="./assets/cain.png" height="220px">
***A small yet powerful data format ✨***
<br>
<br>
[![PyPI version](https://badge.fury.io/py/cain.svg)](https://pypi.org/project/cain/)
[![Downloads](https://static.pepy.tech/personalized-badge/cain?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Total%20Downloads)](https://pepy.tech/project/cain)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/cain)](https://pypistats.org/packages/cain)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cain)](https://pypi.org/project/cain/)
[![PyPI - Status](https://img.shields.io/pypi/status/cain)](https://pypi.org/project/cain/)
[![GitHub - License](https://img.shields.io/github/license/Animenosekai/cain)](https://github.com/Animenosekai/cain/blob/master/LICENSE)
[![GitHub top language](https://img.shields.io/github/languages/top/Animenosekai/cain)](https://github.com/Animenosekai/cain)
[![CodeQL Checks Badge](https://github.com/Animenosekai/cain/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Animenosekai/cain/actions/workflows/codeql-analysis.yml)
![Code Size](https://img.shields.io/github/languages/code-size/Animenosekai/cain)
![Repo Size](https://img.shields.io/github/repo-size/Animenosekai/cain)
![Issues](https://img.shields.io/github/issues/Animenosekai/cain)
## Index
- [Index](#index)
- [Purpose](#purpose)
- [Comparison](#comparison)
- [JSON](#json)
- [Cain](#cain-1)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Installing](#installing)
- [Option 1: From PyPI](#option-1-from-pypi)
- [Option 2: From Git](#option-2-from-git)
- [Usage](#usage)
- [Python](#python)
- [Encoding](#encoding)
- [Decoding](#decoding)
- [Handling Schemas](#handling-schemas)
- [Encoding](#encoding-1)
- [Decoding](#decoding-1)
- [Custom Encoder](#custom-encoder)
- [CLI](#cli)
- [Examples](#examples)
- [Deployment](#deployment)
- [Contributing](#contributing)
- [Authors](#authors)
- [Licensing](#licensing)
## Purpose
Cain is a new data interchange format which aims at providing the smallest possible size to encode data.
It is based on pre-defined schemas which leverages the need to specify it within the final encoded data.
> **Note**
> Look at the [*SPECIFICATIONS*](https://github.com/Animenosekai/cain/blob/main/SPECIFICATIONS.md) file for more information on the purpose and idea behind this project.
### Comparison
For example, we consider the following object:
```python
{
"b": 3,
"c": 5.5,
"d": True,
"e": {
"f": False,
# "g": b"Hello world"
"h": "HELLO WORLD",
"i": "Hi!",
"j": [1, 2, 3, 1, 1],
"k": (1, "hello", True),
"l": None,
"m": "Yay",
"n": "Hi",
"o": 2,
"p": None
}
}
```
#### JSON
This is the expected result from a minified JSON encoding:
```json
{"b":3,"c":5.5,"d":true,"e":{"f":false,"h":"HELLO WORLD","i":"Hi!","j":[1,2,3,1,1],"k":[1,"hello",true],"l":null,"m":"Yay","n":"Hi","o":2,"p":null}}
```
#### Cain
This is the expected result from the Cain data format:
```cain
\x00\x00\x03\x00\x00\xb0@\x01\x00\x00HELLO WORLD\x00Hi!\x00\x00\x05\x00\x00\x00\x01\x00\x02\x00\x03\x00\x01\x00\x01\x00\x00\x01hello\x00\x01\x00\x01\x00Yay\x00\x00Hi\x00\x01\x00\x02
```
> **Note**
> This is 56.76% smaller than the JSON version ✨
***Moreover, objects which can't be encoded using JSON (bytes, set, range, etc.) or wrongly encoded using JSON (ex: tuple) are working out of the box with Cain!***
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
### Prerequisites
You will need Python 3 to use this module
```bash
Minimum required versions: 3.9
Incompatible versions: 2
```
Always check if your Python version works with `cain` before using it in production.
## Installing
### Option 1: From PyPI
```bash
pip install --upgrade cain
```
> This will install the latest version from PyPI
### Option 2: From Git
```bash
pip install --upgrade git+https://github.com/Animenosekai/cain.git
```
> This will install the latest development version from the git repository
You can check if you successfully installed it by printing out its version:
```bash
$ cain --version
1.1
```
## Usage
### Python
The main entry point ([cain.py](./cain/cain.py)) provides an API familiar to users of the standard library `json` module. The different datatype also present a very pythonic way of handling data to keep a nice and clean codebase.
#### Encoding
Encoding basic Python object hierarchies:
```python
>>> import cain
>>> from cain.types import Object, Optional
>>> cain.dumps({"a": 2}, Object[{"a": int}])
b'\x00\x00\x02'
>>> class TestObject(Object):
... bar: tuple[str, Optional[str], float, int]
...
>>> cain.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}], list[str, TestObject])
b'\x00foo\x00\x00\x00baz\x00\x00\x00\x00\x80?\x00\x02'
>>> print(cain.dumps("\"foo\bar", str))
b'"foo\x08ar\x00'
>>> print(cain.dumps('\u1234', str))
b'\xe1\x88\xb4\x00'
>>> print(cain.dumps('\\', str))
b'\\\x00'
>>> schema = list[str, Object[{"bar": tuple[str, Optional[str], float, int]}]]
>>> with open('test.cain', 'w+b') as fp:
... cain.dump(['foo', {'bar': ('baz', None, 1.0, 2)}], fp, schema)
...
>>> from cain.types import Int
>>> from cain.types.numbers import unsigned
>>> Int[unsigned].encode(4)
b'\x00\x04'
```
You can also add a header using the `include_header` parameter to add a header containing the schema for the encoding data. This gives a more portable output but increases its size.
#### Decoding
Decoding Cain:
```python
>>> import cain
>>> from cain.types import Optional, Object
>>> schema = list[str, Object[{"bar": tuple[str, Optional[str], float, int]}]]
>>> cain.loads(b'\x00foo\x00\x00\x00baz\x00\x00\x00\x00\x80?\x00\x02', schema)
['foo', {'bar': ('baz', None, 1.0, 2)}]
>>> with open('test.cain', 'r+b') as fp:
... cain.load(fp, schema)
...
['foo', {'bar': ('baz', None, 1.0, 2)}]
>>> from cain.types import Int
>>> from cain.types.numbers import unsigned
>>> Int[unsigned].decode(b'\x00\x04')
4
```
#### Handling Schemas
If you want to dynamically encode/decode data with the Cain format, it is also possible to encode/decode the schema.
This is especially useful when developing a public API for example.
##### Encoding
```python
>>> import cain
>>> from cain.types import Object, Optional
>>> cain.encode_schema(Object[{"a": int}])
b'\x00\x00\x01\x00\x00a\x00\x00\x01\x00\x00\x01\x03\x00\x01\x02\x00\x00\x00\x00\x06\x00\x00\x00\x00\x16'
>>> class TestObject(Object):
... bar: tuple[str, Optional[str], float, int]
...
>>> cain.encode_schema(list[str, TestObject])
b'\x01\x02\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00...\x00\x16\x01\x00TestObject\x00\x00\x00'
```
##### Decoding
```python
>>> import cain
>>> cain.decode_schema(b'\x00\x00\x01\x00\x00a\x00\x00\x01\x00\x00\x01\x03\x00\x01\x02\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x16\x00')
Object<{'a': Int}>
>>> cain.decode_schema(b'\x01\x02\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00...\x00\x16\x01\x00TestObject\x00\x00\x00')
Array[String, TestObject]
```
#### Custom Encoder
You can also create your own encoders:
```python
>>> import typing
>>> from cain.model import Datatype
>>> class MyObject(Datatype):
... @classmethod # *args contains the args passed here : MyObject[args]
... def _encode(cls, value: typing.Any,*args) -> bytes:
... ... # your custom encoding
... return b'encoded data'
... #
... @classmethod
... def _decode(cls, value: bytes, *args) -> typing.Tuple[typing.Any, bytes]:
... ... # `value` contains more than just the value you should decode
... ... # try to only decode the first few bytes
... ... # your custom decoding
... return 'decoded data', value # the rest of the value that you didn't decode
... # you can now use `MyObject` in your schemas and encode/decode from it
```
> **Warning**
> Keep in mind that custom datatypes outside of subclasses of `Object` won't be able to be encoded by the Type encoder (used in schema headers for example)
### CLI
Cain has a pretty complete command-line interface, which lets you manipulate and interact with the Cain data format easily.
For more information, head over to your console and enter:
```bash
cain --help
```
Or
```bash
cain <action> --help
```
#### Examples
> Example usage of the CLI
Preparing the schema:
```python
# test.py
from cain import Object
class Test(Object):
username: str
favorite_number: int
```
Trying to encode with a Python schema:
```bash
cain encode '{"username": "Anise", "favorite_number": 2}' --schema="test.py" --schema-name="Test" --include-header --output="test.cain"
```
Trying to decode the previous file:
```bash
$ cain decode test.cain
{
"favorite_number": 2,
"username": "Anise"
}
```
Looking up at its schema:
```bash
$ cain schema lookup test.cain --schema-header
{
"index": 22,
"name": "Test",
"annotations_keys": [
"username",
"favorite_number"
],
"annotations_values": [
{
"index": 26,
"name": null,
"annotations_keys": [],
"annotations_values": [],
"arguments": [],
"datatype": "String"
},
{
"index": 6,
"name": null,
"annotations_keys": [],
"annotations_values": [],
"arguments": [],
"datatype": "Int"
}
],
"arguments": [],
"datatype": "Object"
}
```
Exporting its schema:
```bash
cain schema export test.cain --schema-header --output test.cainschema
```
Trying to encode another object with the exported schema:
```bash
$ cain encode '{"username": "yay", "favorite_number": 3}' --schema=test.cainschema
\x00\x00\x03yay\x00
```
Encoding "Hello world":
```bash
$ cain encode '"Hello world"' --schema="str" --schema-eval
Hello world\x00
$ cain encode '["Hello", "world"]' --schema="list[str]" --schema-eval
\x00\x02\x00\x00Hello\x00world\x00
```
## Deployment
This module is currently in development and might contain bugs.
This comes with a few disadvantages (for example, it takes a longer time to encode objects with Cain than with the standard `json` module) but this is expected to improve over time.
Please verify and test the module thoroughly before releasing anything at a production stage.
Feel free to report any issue you might encounter on Cain's GitHub page.
## Contributing
Pull requests are welcome. For major changes, please open a discussion first to discuss what you would like to change.
Please make sure to update the tests accordingly.
## Authors
- **Animenosekai** - *Initial work* - [Animenosekai](https://github.com/Animenosekai)
## Licensing
This software is licensed under the MIT License. See the [*LICENSE*](./LICENSE) file for more information.
Raw data
{
"_id": null,
"home_page": "https://github.com/Animenosekai/cain",
"name": "cain",
"maintainer": "Animenosekai",
"docs_url": null,
"requires_python": ">=3.9,<4.0",
"maintainer_email": "animenosekai.mail@gmail.com",
"keywords": "animenosekai,cain,data,format",
"author": "Animenosekai",
"author_email": "animenosekai.mail@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/47/c2/a72773ae6f837b9f3c46cce4ffbf31d9dab99a42f9c63b07cddee25a9b12/cain-1.1.1.tar.gz",
"platform": null,
"description": "# Cain\n\n<img align=\"right\" src=\"./assets/cain.png\" height=\"220px\">\n\n***A small yet powerful data format \u2728***\n\n<br>\n<br>\n\n[![PyPI version](https://badge.fury.io/py/cain.svg)](https://pypi.org/project/cain/)\n[![Downloads](https://static.pepy.tech/personalized-badge/cain?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Total%20Downloads)](https://pepy.tech/project/cain)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/cain)](https://pypistats.org/packages/cain)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cain)](https://pypi.org/project/cain/)\n[![PyPI - Status](https://img.shields.io/pypi/status/cain)](https://pypi.org/project/cain/)\n[![GitHub - License](https://img.shields.io/github/license/Animenosekai/cain)](https://github.com/Animenosekai/cain/blob/master/LICENSE)\n[![GitHub top language](https://img.shields.io/github/languages/top/Animenosekai/cain)](https://github.com/Animenosekai/cain)\n[![CodeQL Checks Badge](https://github.com/Animenosekai/cain/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Animenosekai/cain/actions/workflows/codeql-analysis.yml)\n![Code Size](https://img.shields.io/github/languages/code-size/Animenosekai/cain)\n![Repo Size](https://img.shields.io/github/repo-size/Animenosekai/cain)\n![Issues](https://img.shields.io/github/issues/Animenosekai/cain)\n\n## Index\n\n- [Index](#index)\n- [Purpose](#purpose)\n - [Comparison](#comparison)\n - [JSON](#json)\n - [Cain](#cain-1)\n- [Getting Started](#getting-started)\n - [Prerequisites](#prerequisites)\n- [Installing](#installing)\n - [Option 1: From PyPI](#option-1-from-pypi)\n - [Option 2: From Git](#option-2-from-git)\n- [Usage](#usage)\n - [Python](#python)\n - [Encoding](#encoding)\n - [Decoding](#decoding)\n - [Handling Schemas](#handling-schemas)\n - [Encoding](#encoding-1)\n - [Decoding](#decoding-1)\n - [Custom Encoder](#custom-encoder)\n - [CLI](#cli)\n - [Examples](#examples)\n- [Deployment](#deployment)\n- [Contributing](#contributing)\n- [Authors](#authors)\n- [Licensing](#licensing)\n\n## Purpose\n\nCain is a new data interchange format which aims at providing the smallest possible size to encode data.\n\nIt is based on pre-defined schemas which leverages the need to specify it within the final encoded data.\n\n> **Note** \n> Look at the [*SPECIFICATIONS*](https://github.com/Animenosekai/cain/blob/main/SPECIFICATIONS.md) file for more information on the purpose and idea behind this project.\n\n### Comparison\n\nFor example, we consider the following object:\n\n```python\n{\n \"b\": 3,\n \"c\": 5.5,\n \"d\": True,\n \"e\": {\n \"f\": False,\n # \"g\": b\"Hello world\"\n \"h\": \"HELLO WORLD\",\n \"i\": \"Hi!\",\n \"j\": [1, 2, 3, 1, 1],\n \"k\": (1, \"hello\", True),\n \"l\": None,\n \"m\": \"Yay\",\n \"n\": \"Hi\",\n \"o\": 2,\n \"p\": None\n }\n}\n```\n\n#### JSON\n\nThis is the expected result from a minified JSON encoding:\n\n```json\n{\"b\":3,\"c\":5.5,\"d\":true,\"e\":{\"f\":false,\"h\":\"HELLO WORLD\",\"i\":\"Hi!\",\"j\":[1,2,3,1,1],\"k\":[1,\"hello\",true],\"l\":null,\"m\":\"Yay\",\"n\":\"Hi\",\"o\":2,\"p\":null}}\n```\n\n#### Cain\n\nThis is the expected result from the Cain data format:\n\n```cain\n\\x00\\x00\\x03\\x00\\x00\\xb0@\\x01\\x00\\x00HELLO WORLD\\x00Hi!\\x00\\x00\\x05\\x00\\x00\\x00\\x01\\x00\\x02\\x00\\x03\\x00\\x01\\x00\\x01\\x00\\x00\\x01hello\\x00\\x01\\x00\\x01\\x00Yay\\x00\\x00Hi\\x00\\x01\\x00\\x02\n```\n\n> **Note** \n> This is 56.76% smaller than the JSON version \u2728\n\n***Moreover, objects which can't be encoded using JSON (bytes, set, range, etc.) or wrongly encoded using JSON (ex: tuple) are working out of the box with Cain!***\n\n## Getting Started\n\nThese instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.\n\n### Prerequisites\n\nYou will need Python 3 to use this module\n\n```bash\nMinimum required versions: 3.9\nIncompatible versions: 2\n```\n\nAlways check if your Python version works with `cain` before using it in production.\n\n## Installing\n\n### Option 1: From PyPI\n\n```bash\npip install --upgrade cain\n```\n\n> This will install the latest version from PyPI\n\n### Option 2: From Git\n\n```bash\npip install --upgrade git+https://github.com/Animenosekai/cain.git\n```\n\n> This will install the latest development version from the git repository\n\nYou can check if you successfully installed it by printing out its version:\n\n```bash\n$ cain --version\n1.1\n```\n\n## Usage\n\n### Python\n\nThe main entry point ([cain.py](./cain/cain.py)) provides an API familiar to users of the standard library `json` module. The different datatype also present a very pythonic way of handling data to keep a nice and clean codebase.\n\n#### Encoding\n\nEncoding basic Python object hierarchies:\n\n```python\n>>> import cain\n>>> from cain.types import Object, Optional\n>>> cain.dumps({\"a\": 2}, Object[{\"a\": int}])\nb'\\x00\\x00\\x02'\n>>> class TestObject(Object):\n... bar: tuple[str, Optional[str], float, int]\n...\n>>> cain.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}], list[str, TestObject])\nb'\\x00foo\\x00\\x00\\x00baz\\x00\\x00\\x00\\x00\\x80?\\x00\\x02'\n>>> print(cain.dumps(\"\\\"foo\\bar\", str))\nb'\"foo\\x08ar\\x00'\n>>> print(cain.dumps('\\u1234', str))\nb'\\xe1\\x88\\xb4\\x00'\n>>> print(cain.dumps('\\\\', str))\nb'\\\\\\x00'\n>>> schema = list[str, Object[{\"bar\": tuple[str, Optional[str], float, int]}]]\n>>> with open('test.cain', 'w+b') as fp:\n... cain.dump(['foo', {'bar': ('baz', None, 1.0, 2)}], fp, schema)\n...\n>>> from cain.types import Int\n>>> from cain.types.numbers import unsigned\n>>> Int[unsigned].encode(4)\nb'\\x00\\x04'\n```\n\nYou can also add a header using the `include_header` parameter to add a header containing the schema for the encoding data. This gives a more portable output but increases its size.\n\n#### Decoding\n\nDecoding Cain:\n\n```python\n>>> import cain\n>>> from cain.types import Optional, Object\n>>> schema = list[str, Object[{\"bar\": tuple[str, Optional[str], float, int]}]]\n>>> cain.loads(b'\\x00foo\\x00\\x00\\x00baz\\x00\\x00\\x00\\x00\\x80?\\x00\\x02', schema)\n['foo', {'bar': ('baz', None, 1.0, 2)}]\n>>> with open('test.cain', 'r+b') as fp:\n... cain.load(fp, schema)\n...\n['foo', {'bar': ('baz', None, 1.0, 2)}]\n>>> from cain.types import Int\n>>> from cain.types.numbers import unsigned\n>>> Int[unsigned].decode(b'\\x00\\x04')\n4\n```\n\n#### Handling Schemas\n\nIf you want to dynamically encode/decode data with the Cain format, it is also possible to encode/decode the schema.\n\nThis is especially useful when developing a public API for example.\n\n##### Encoding\n\n```python\n>>> import cain\n>>> from cain.types import Object, Optional\n>>> cain.encode_schema(Object[{\"a\": int}])\nb'\\x00\\x00\\x01\\x00\\x00a\\x00\\x00\\x01\\x00\\x00\\x01\\x03\\x00\\x01\\x02\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x16'\n>>> class TestObject(Object):\n... bar: tuple[str, Optional[str], float, int]\n...\n>>> cain.encode_schema(list[str, TestObject])\nb'\\x01\\x02\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00...\\x00\\x16\\x01\\x00TestObject\\x00\\x00\\x00'\n```\n\n##### Decoding\n\n```python\n>>> import cain\n>>> cain.decode_schema(b'\\x00\\x00\\x01\\x00\\x00a\\x00\\x00\\x01\\x00\\x00\\x01\\x03\\x00\\x01\\x02\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x00\\x00\\x16\\x00')\nObject<{'a': Int}>\n>>> cain.decode_schema(b'\\x01\\x02\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00...\\x00\\x16\\x01\\x00TestObject\\x00\\x00\\x00')\nArray[String, TestObject]\n```\n\n#### Custom Encoder\n\nYou can also create your own encoders:\n\n```python\n>>> import typing\n>>> from cain.model import Datatype\n>>> class MyObject(Datatype):\n... @classmethod # *args contains the args passed here : MyObject[args]\n... def _encode(cls, value: typing.Any,*args) -> bytes:\n... ... # your custom encoding\n... return b'encoded data'\n... #\n... @classmethod\n... def _decode(cls, value: bytes, *args) -> typing.Tuple[typing.Any, bytes]:\n... ... # `value` contains more than just the value you should decode\n... ... # try to only decode the first few bytes\n... ... # your custom decoding\n... return 'decoded data', value # the rest of the value that you didn't decode\n... # you can now use `MyObject` in your schemas and encode/decode from it\n```\n\n> **Warning** \n> Keep in mind that custom datatypes outside of subclasses of `Object` won't be able to be encoded by the Type encoder (used in schema headers for example)\n\n### CLI\n\nCain has a pretty complete command-line interface, which lets you manipulate and interact with the Cain data format easily.\n\nFor more information, head over to your console and enter:\n\n```bash\ncain --help\n```\n\nOr\n\n```bash\ncain <action> --help\n```\n\n#### Examples\n\n> Example usage of the CLI\n\nPreparing the schema:\n\n```python\n# test.py\nfrom cain import Object\nclass Test(Object):\n username: str\n favorite_number: int\n```\n\nTrying to encode with a Python schema:\n\n```bash\ncain encode '{\"username\": \"Anise\", \"favorite_number\": 2}' --schema=\"test.py\" --schema-name=\"Test\" --include-header --output=\"test.cain\"\n```\n\nTrying to decode the previous file:\n\n```bash\n$ cain decode test.cain\n{\n \"favorite_number\": 2,\n \"username\": \"Anise\"\n}\n```\n\nLooking up at its schema:\n\n```bash\n$ cain schema lookup test.cain --schema-header\n{\n \"index\": 22,\n \"name\": \"Test\",\n \"annotations_keys\": [\n \"username\",\n \"favorite_number\"\n ],\n \"annotations_values\": [\n {\n \"index\": 26,\n \"name\": null,\n \"annotations_keys\": [],\n \"annotations_values\": [],\n \"arguments\": [],\n \"datatype\": \"String\"\n },\n {\n \"index\": 6,\n \"name\": null,\n \"annotations_keys\": [],\n \"annotations_values\": [],\n \"arguments\": [],\n \"datatype\": \"Int\"\n }\n ],\n \"arguments\": [],\n \"datatype\": \"Object\"\n}\n```\n\nExporting its schema:\n\n```bash\ncain schema export test.cain --schema-header --output test.cainschema\n```\n\nTrying to encode another object with the exported schema:\n\n```bash\n$ cain encode '{\"username\": \"yay\", \"favorite_number\": 3}' --schema=test.cainschema\n\\x00\\x00\\x03yay\\x00\n```\n\nEncoding \"Hello world\":\n\n```bash\n$ cain encode '\"Hello world\"' --schema=\"str\" --schema-eval\nHello world\\x00\n$ cain encode '[\"Hello\", \"world\"]' --schema=\"list[str]\" --schema-eval\n\\x00\\x02\\x00\\x00Hello\\x00world\\x00\n```\n\n## Deployment\n\nThis module is currently in development and might contain bugs.\n\nThis comes with a few disadvantages (for example, it takes a longer time to encode objects with Cain than with the standard `json` module) but this is expected to improve over time.\n\nPlease verify and test the module thoroughly before releasing anything at a production stage.\n\nFeel free to report any issue you might encounter on Cain's GitHub page.\n\n## Contributing\n\nPull requests are welcome. For major changes, please open a discussion first to discuss what you would like to change.\n\nPlease make sure to update the tests accordingly.\n\n## Authors\n\n- **Animenosekai** - *Initial work* - [Animenosekai](https://github.com/Animenosekai)\n\n## Licensing\n\nThis software is licensed under the MIT License. See the [*LICENSE*](./LICENSE) file for more information.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A small yet powerful data format \u2728",
"version": "1.1.1",
"project_urls": {
"Documentation": "https://github.com/Animenosekai/cain/blob/main/README.md",
"Homepage": "https://github.com/Animenosekai/cain",
"Issue Tracker": "https://github.com/Animenosekai/cain/issues",
"Repository": "https://github.com/Animenosekai/cain"
},
"split_keywords": [
"animenosekai",
"cain",
"data",
"format"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "8f1f51b560d4006603e525a57468086ee33ac9142f70fb3d2254ea4f8ea29d04",
"md5": "8a8e9aaa7b99ad4cbe12a31ed90c713f",
"sha256": "67528e6f70ae725befe831824fc35a741c160fe4204afdc764b2dc35348d80d3"
},
"downloads": -1,
"filename": "cain-1.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "8a8e9aaa7b99ad4cbe12a31ed90c713f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9,<4.0",
"size": 35552,
"upload_time": "2023-08-25T10:11:42",
"upload_time_iso_8601": "2023-08-25T10:11:42.581699Z",
"url": "https://files.pythonhosted.org/packages/8f/1f/51b560d4006603e525a57468086ee33ac9142f70fb3d2254ea4f8ea29d04/cain-1.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "47c2a72773ae6f837b9f3c46cce4ffbf31d9dab99a42f9c63b07cddee25a9b12",
"md5": "342942163900501b028368052383b405",
"sha256": "e2faeee41ad97de527e2d3bc459b3153da5d55f4da4a55d10136bdb91c77e9cb"
},
"downloads": -1,
"filename": "cain-1.1.1.tar.gz",
"has_sig": false,
"md5_digest": "342942163900501b028368052383b405",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9,<4.0",
"size": 28983,
"upload_time": "2023-08-25T10:11:44",
"upload_time_iso_8601": "2023-08-25T10:11:44.561482Z",
"url": "https://files.pythonhosted.org/packages/47/c2/a72773ae6f837b9f3c46cce4ffbf31d9dab99a42f9c63b07cddee25a9b12/cain-1.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-08-25 10:11:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Animenosekai",
"github_project": "cain",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "cain"
}