Name | python-ottype JSON |
Version |
24.6.0
JSON |
| download |
home_page | None |
Summary | A python implementation of Operational Transformation. |
upload_time | 2024-06-17 09:09:45 |
maintainer | None |
docs_url | None |
author | Jungkook Park |
requires_python | <3.13,>=3.9 |
license | MIT License |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Python-OTType
A python library for Operational Transformation (OT). Basic idea follows the spec at https://github.com/ottypes/docs.
Supported Python versions : CPython 3.9 - 3.12, PyPy 3.9 - 3.10
## Installation
```sh
pip install python-ottype
```
Basic usage:
```python
import ottype
assert ottype.apply('asdf', [3, '123', {'d':'f'}]) == 'asd123'
```
## OT Operations
### Skip
Object type : `int`
Skip `n` characters from the current position is represented as `n`
```python
assert apply('asdf', [3]) == 'asdf'
```
### Insert
Object type : `str`
Insert a string `s` at the current position is represented as `s`
```python
assert apply('asdf', ['qwer']) == 'qwerasdf'
assert apply('asdf', [2, 'qwer']) == 'asqwerdf'
```
### Delete
Object type : `dict`
Delete a string `s` at the current position is represented as `{'d': s}`
```python
assert apply('asdf', [{'d': 'as'}]) == 'df'
assert apply('asdf', [1, {'d': 'sd'}]) == 'af'
```
## Supported Functions
```python
OT = int | str | dict[str, str]
```
### `check(ots: Sequence[OT], *, check_unoptimized: bool = True) -> bool`
Check the sequence if it only contains valid OTs. If `check_unoptimized` is `True`, only normalized sequence of OTs is accepted.
```python
assert check(['a', 4, 'b'])
assert not check(['a', 'b']) # is not normalized
assert not check([3]) # is not normalized
```
### `apply(doc: str, ots: Sequence[OT], *, check_unoptimized: bool = True) -> str`
Apply a sequence of OTs to a string.
```python
assert apply('abcde', [2, 'qq', {'d': 'c'}, 1, 'w']) == 'abqqdwe'
```
### `inverse_apply(doc: str, ots: Sequence[OT], *, check_unoptimized: bool = True) -> str`
Inversely apply a sequence of OTs to a string.
```python
assert inverse_apply(apply(doc, ots), ots) == doc
```
### `normalize(ots: Sequence[OT]) -> Sequence[OT]`
Normalize a sequence of OTs : merge consecutive OTs and trim the last skip operation.
```python
assert normalize([1, 2, 'as', 'df', {'d': 'qw'}, {'d': 'er'}, 3]) \
== [3, 'asdf', {'d': 'qwer'}]
```
### `transform(ots1: Sequence[OT], ots2: Sequence[OT]) -> Sequence[OT]`
Transform a sequence of OTs with the property:
```python
assert apply(apply(doc, ots1), transform(ots2, ots1, 'left')) \
== apply(apply(doc, ots2), transform(ots1, ots2, 'right'))
```
### `compose(ots1: Sequence[OT], ots2: Sequence[OT]) -> Sequence[OT]`
Compose two sequences of OTs with the property:
```python
assert apply(apply(doc, ots1), ots2) == apply(doc, compose(ots1, ots2))
```
## Benchmark (at CPython 3.12.1)
### Benchmark : `apply` operation
| len(doc) | len(ots) | python (Kops/s) | cython (Kops/s) |
|---:|---:|---:|---:|
| 100 | 5 | 289.58 ( 1.00x) | 663.47 ( 2.29x) |
| 100 | 10 | 206.50 ( 1.00x) | 513.00 ( 2.48x) |
| 100 | 20 | 114.52 ( 1.00x) | 298.82 ( 2.61x) |
| 100 | 50 | 48.37 ( 1.00x) | 127.90 ( 2.64x) |
| 100 | 100 | 26.96 ( 1.00x) | 71.84 ( 2.66x) |
| 1000 | 5 | 412.41 ( 1.00x) | 974.43 ( 2.36x) |
| 1000 | 10 | 220.19 ( 1.00x) | 493.53 ( 2.24x) |
| 1000 | 20 | 128.66 ( 1.00x) | 317.18 ( 2.47x) |
| 1000 | 50 | 37.56 ( 1.00x) | 98.56 ( 2.62x) |
| 1000 | 100 | 23.43 ( 1.00x) | 64.25 ( 2.74x) |
| 10000 | 5 | 203.14 ( 1.00x) | 322.31 ( 1.59x) |
| 10000 | 10 | 142.25 ( 1.00x) | 279.79 ( 1.97x) |
| 10000 | 20 | 95.63 ( 1.00x) | 202.97 ( 2.12x) |
| 10000 | 50 | 48.58 ( 1.00x) | 122.09 ( 2.51x) |
| 10000 | 100 | 21.17 ( 1.00x) | 57.59 ( 2.72x) |
### Benchmark : `inverse_apply` operation
| len(doc) | len(ots) | python (Kops/s) | cython (Kops/s) |
|---:|---:|---:|---:|
| 100 | 5 | 194.12 ( 1.00x) | 408.56 ( 2.10x) |
| 100 | 10 | 113.27 ( 1.00x) | 269.10 ( 2.38x) |
| 100 | 20 | 60.99 ( 1.00x) | 150.49 ( 2.47x) |
| 100 | 50 | 26.35 ( 1.00x) | 64.27 ( 2.44x) |
| 100 | 100 | 16.62 ( 1.00x) | 42.87 ( 2.58x) |
| 1000 | 5 | 309.57 ( 1.00x) | 565.47 ( 1.83x) |
| 1000 | 10 | 153.62 ( 1.00x) | 351.55 ( 2.29x) |
| 1000 | 20 | 69.48 ( 1.00x) | 162.52 ( 2.34x) |
| 1000 | 50 | 37.12 ( 1.00x) | 91.88 ( 2.48x) |
| 1000 | 100 | 17.06 ( 1.00x) | 42.51 ( 2.49x) |
| 10000 | 5 | 145.11 ( 1.00x) | 257.81 ( 1.78x) |
| 10000 | 10 | 96.66 ( 1.00x) | 198.97 ( 2.06x) |
| 10000 | 20 | 65.18 ( 1.00x) | 140.90 ( 2.16x) |
| 10000 | 50 | 27.40 ( 1.00x) | 67.74 ( 2.47x) |
| 10000 | 100 | 13.53 ( 1.00x) | 32.23 ( 2.38x) |
Raw data
{
"_id": null,
"home_page": null,
"name": "python-ottype",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.13,>=3.9",
"maintainer_email": null,
"keywords": null,
"author": "Jungkook Park",
"author_email": "pjknkda@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/ca/e0/8281f066d1af41c98c844100d11d84c43c89aede5bea2cfea51be96fc502/python_ottype-24.6.0.tar.gz",
"platform": null,
"description": "# Python-OTType\n\nA python library for Operational Transformation (OT). Basic idea follows the spec at https://github.com/ottypes/docs.\n\nSupported Python versions : CPython 3.9 - 3.12, PyPy 3.9 - 3.10\n\n\n## Installation\n\n```sh\npip install python-ottype\n```\n\nBasic usage:\n\n```python\nimport ottype\n\nassert ottype.apply('asdf', [3, '123', {'d':'f'}]) == 'asd123'\n```\n\n\n## OT Operations\n\n### Skip\n\nObject type : `int`\n\nSkip `n` characters from the current position is represented as `n`\n\n```python\nassert apply('asdf', [3]) == 'asdf'\n```\n\n### Insert\n\nObject type : `str`\n\nInsert a string `s` at the current position is represented as `s`\n\n```python\nassert apply('asdf', ['qwer']) == 'qwerasdf'\nassert apply('asdf', [2, 'qwer']) == 'asqwerdf'\n```\n\n### Delete\n\nObject type : `dict`\n\nDelete a string `s` at the current position is represented as `{'d': s}`\n\n```python\nassert apply('asdf', [{'d': 'as'}]) == 'df'\nassert apply('asdf', [1, {'d': 'sd'}]) == 'af'\n```\n\n## Supported Functions\n\n```python\nOT = int | str | dict[str, str]\n```\n\n### `check(ots: Sequence[OT], *, check_unoptimized: bool = True) -> bool`\n\nCheck the sequence if it only contains valid OTs. If `check_unoptimized` is `True`, only normalized sequence of OTs is accepted.\n\n```python\nassert check(['a', 4, 'b'])\nassert not check(['a', 'b']) # is not normalized\nassert not check([3]) # is not normalized\n```\n\n### `apply(doc: str, ots: Sequence[OT], *, check_unoptimized: bool = True) -> str`\n\nApply a sequence of OTs to a string.\n\n```python\nassert apply('abcde', [2, 'qq', {'d': 'c'}, 1, 'w']) == 'abqqdwe'\n```\n\n### `inverse_apply(doc: str, ots: Sequence[OT], *, check_unoptimized: bool = True) -> str`\n\nInversely apply a sequence of OTs to a string.\n\n```python\nassert inverse_apply(apply(doc, ots), ots) == doc\n```\n\n### `normalize(ots: Sequence[OT]) -> Sequence[OT]`\n\nNormalize a sequence of OTs : merge consecutive OTs and trim the last skip operation.\n\n```python\nassert normalize([1, 2, 'as', 'df', {'d': 'qw'}, {'d': 'er'}, 3]) \\\n == [3, 'asdf', {'d': 'qwer'}]\n```\n\n\n### `transform(ots1: Sequence[OT], ots2: Sequence[OT]) -> Sequence[OT]`\n\nTransform a sequence of OTs with the property:\n\n```python\nassert apply(apply(doc, ots1), transform(ots2, ots1, 'left')) \\\n == apply(apply(doc, ots2), transform(ots1, ots2, 'right'))\n```\n\n### `compose(ots1: Sequence[OT], ots2: Sequence[OT]) -> Sequence[OT]`\n\nCompose two sequences of OTs with the property:\n\n```python\nassert apply(apply(doc, ots1), ots2) == apply(doc, compose(ots1, ots2))\n```\n\n\n\n## Benchmark (at CPython 3.12.1)\n\n### Benchmark : `apply` operation\n\n| len(doc) | len(ots) | python (Kops/s) | cython (Kops/s) |\n|---:|---:|---:|---:|\n| 100 | 5 | 289.58 ( 1.00x) | 663.47 ( 2.29x) |\n| 100 | 10 | 206.50 ( 1.00x) | 513.00 ( 2.48x) |\n| 100 | 20 | 114.52 ( 1.00x) | 298.82 ( 2.61x) |\n| 100 | 50 | 48.37 ( 1.00x) | 127.90 ( 2.64x) |\n| 100 | 100 | 26.96 ( 1.00x) | 71.84 ( 2.66x) |\n| 1000 | 5 | 412.41 ( 1.00x) | 974.43 ( 2.36x) |\n| 1000 | 10 | 220.19 ( 1.00x) | 493.53 ( 2.24x) |\n| 1000 | 20 | 128.66 ( 1.00x) | 317.18 ( 2.47x) |\n| 1000 | 50 | 37.56 ( 1.00x) | 98.56 ( 2.62x) |\n| 1000 | 100 | 23.43 ( 1.00x) | 64.25 ( 2.74x) |\n| 10000 | 5 | 203.14 ( 1.00x) | 322.31 ( 1.59x) |\n| 10000 | 10 | 142.25 ( 1.00x) | 279.79 ( 1.97x) |\n| 10000 | 20 | 95.63 ( 1.00x) | 202.97 ( 2.12x) |\n| 10000 | 50 | 48.58 ( 1.00x) | 122.09 ( 2.51x) |\n| 10000 | 100 | 21.17 ( 1.00x) | 57.59 ( 2.72x) |\n\n### Benchmark : `inverse_apply` operation\n\n| len(doc) | len(ots) | python (Kops/s) | cython (Kops/s) |\n|---:|---:|---:|---:|\n| 100 | 5 | 194.12 ( 1.00x) | 408.56 ( 2.10x) |\n| 100 | 10 | 113.27 ( 1.00x) | 269.10 ( 2.38x) |\n| 100 | 20 | 60.99 ( 1.00x) | 150.49 ( 2.47x) |\n| 100 | 50 | 26.35 ( 1.00x) | 64.27 ( 2.44x) |\n| 100 | 100 | 16.62 ( 1.00x) | 42.87 ( 2.58x) |\n| 1000 | 5 | 309.57 ( 1.00x) | 565.47 ( 1.83x) |\n| 1000 | 10 | 153.62 ( 1.00x) | 351.55 ( 2.29x) |\n| 1000 | 20 | 69.48 ( 1.00x) | 162.52 ( 2.34x) |\n| 1000 | 50 | 37.12 ( 1.00x) | 91.88 ( 2.48x) |\n| 1000 | 100 | 17.06 ( 1.00x) | 42.51 ( 2.49x) |\n| 10000 | 5 | 145.11 ( 1.00x) | 257.81 ( 1.78x) |\n| 10000 | 10 | 96.66 ( 1.00x) | 198.97 ( 2.06x) |\n| 10000 | 20 | 65.18 ( 1.00x) | 140.90 ( 2.16x) |\n| 10000 | 50 | 27.40 ( 1.00x) | 67.74 ( 2.47x) |\n| 10000 | 100 | 13.53 ( 1.00x) | 32.23 ( 2.38x) |\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "A python implementation of Operational Transformation.",
"version": "24.6.0",
"project_urls": {
"Homepage": "https://github.com/pjknkda/python-ottype"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0822e4cdcb2f99abd2551ad281d78fc6b83ea4581a00591ed95b01474da13e67",
"md5": "b5a575f2b5d4c279d5698b24716cf636",
"sha256": "f351bab11ce2cef7e566a0d5c3791840925eddeb2444377a7dfcd2ccad2d4b40"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "b5a575f2b5d4c279d5698b24716cf636",
"packagetype": "bdist_wheel",
"python_version": "cp310",
"requires_python": "<3.13,>=3.9",
"size": 503823,
"upload_time": "2024-06-17T09:09:32",
"upload_time_iso_8601": "2024-06-17T09:09:32.776149Z",
"url": "https://files.pythonhosted.org/packages/08/22/e4cdcb2f99abd2551ad281d78fc6b83ea4581a00591ed95b01474da13e67/python_ottype-24.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "83d29d591b6236a3527f764cd0e775734712c4970767a8e40329f928e6ea899a",
"md5": "41200e4ee35692ceabfd5e0fb944a3d5",
"sha256": "ad1f4b5f06d5f6b887aaf5734b89eaffbad50bef81ff9e8a5acc35346df27b5f"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "41200e4ee35692ceabfd5e0fb944a3d5",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": "<3.13,>=3.9",
"size": 541933,
"upload_time": "2024-06-17T09:09:36",
"upload_time_iso_8601": "2024-06-17T09:09:36.051494Z",
"url": "https://files.pythonhosted.org/packages/83/d2/9d591b6236a3527f764cd0e775734712c4970767a8e40329f928e6ea899a/python_ottype-24.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "1d298028fc9d4590bf544f9a6a1e824df2f94b4d774b80aaa719dee861e752f1",
"md5": "6d5e7477ef2120ad4041503ced577d1d",
"sha256": "54f394b2e27c7add1d23a7a1a719d210af46b6731724fa1b0a2db2f00009fc82"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "6d5e7477ef2120ad4041503ced577d1d",
"packagetype": "bdist_wheel",
"python_version": "cp312",
"requires_python": "<3.13,>=3.9",
"size": 532899,
"upload_time": "2024-06-17T09:09:37",
"upload_time_iso_8601": "2024-06-17T09:09:37.902703Z",
"url": "https://files.pythonhosted.org/packages/1d/29/8028fc9d4590bf544f9a6a1e824df2f94b4d774b80aaa719dee861e752f1/python_ottype-24.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "627e88c758477db7ad91932bd0b104ce8dbba96daf4382a02f5f898b26f8fca2",
"md5": "7f56f500556509c5520eb6e7fee752ec",
"sha256": "610b54586153c07c7d56d4c670198939ae94de82e99f93397688f74a3eb8b782"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "7f56f500556509c5520eb6e7fee752ec",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": "<3.13,>=3.9",
"size": 507891,
"upload_time": "2024-06-17T09:09:40",
"upload_time_iso_8601": "2024-06-17T09:09:40.814173Z",
"url": "https://files.pythonhosted.org/packages/62/7e/88c758477db7ad91932bd0b104ce8dbba96daf4382a02f5f898b26f8fca2/python_ottype-24.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "884456e1fa6652e6195aee8778c955012a4c3c0e2266e8eb40118367c2d27ae6",
"md5": "37195f524e056338041f2675a04e363b",
"sha256": "54112bac3ef09518591c1eb6abb7df23d51977809917ce8635561c0b6ca99fca"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "37195f524e056338041f2675a04e363b",
"packagetype": "bdist_wheel",
"python_version": "pp310",
"requires_python": "<3.13,>=3.9",
"size": 182242,
"upload_time": "2024-06-17T09:09:42",
"upload_time_iso_8601": "2024-06-17T09:09:42.285678Z",
"url": "https://files.pythonhosted.org/packages/88/44/56e1fa6652e6195aee8778c955012a4c3c0e2266e8eb40118367c2d27ae6/python_ottype-24.6.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "93d0e690fa9290063776c37006bdd431def35f1cd10a9e4c083c752bd0a534f3",
"md5": "6e8900581eaf6d01562b3d86fa8659f4",
"sha256": "5c16b863cdc6ddbe246f96bcf405579e39454920a8a69f59b0db54e9e0a73b61"
},
"downloads": -1,
"filename": "python_ottype-24.6.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "6e8900581eaf6d01562b3d86fa8659f4",
"packagetype": "bdist_wheel",
"python_version": "pp39",
"requires_python": "<3.13,>=3.9",
"size": 182091,
"upload_time": "2024-06-17T09:09:43",
"upload_time_iso_8601": "2024-06-17T09:09:43.897828Z",
"url": "https://files.pythonhosted.org/packages/93/d0/e690fa9290063776c37006bdd431def35f1cd10a9e4c083c752bd0a534f3/python_ottype-24.6.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "cae08281f066d1af41c98c844100d11d84c43c89aede5bea2cfea51be96fc502",
"md5": "dac4f9a3031d281362b0a81d4791729d",
"sha256": "1b785fdef26d7d3ac163a039ba612f0be0e3b8d142da172e0ca20e4a02051355"
},
"downloads": -1,
"filename": "python_ottype-24.6.0.tar.gz",
"has_sig": false,
"md5_digest": "dac4f9a3031d281362b0a81d4791729d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.13,>=3.9",
"size": 11345,
"upload_time": "2024-06-17T09:09:45",
"upload_time_iso_8601": "2024-06-17T09:09:45.128895Z",
"url": "https://files.pythonhosted.org/packages/ca/e0/8281f066d1af41c98c844100d11d84c43c89aede5bea2cfea51be96fc502/python_ottype-24.6.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-17 09:09:45",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "pjknkda",
"github_project": "python-ottype",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "python-ottype"
}