python-ottype


Namepython-ottype JSON
Version 24.6.0 PyPI version JSON
download
home_pageNone
SummaryA python implementation of Operational Transformation.
upload_time2024-06-17 09:09:45
maintainerNone
docs_urlNone
authorJungkook Park
requires_python<3.13,>=3.9
licenseMIT 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"
}
        
Elapsed time: 0.96018s