durabletask-dapr


Namedurabletask-dapr JSON
Version 0.2.0a3 PyPI version JSON
download
home_pageNone
SummaryA Durable Task Client SDK for Python
upload_time2024-11-27 16:47:54
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT License Copyright (c) Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
keywords durable task workflow
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Durable Task Client SDK for Python (Dapr fork)

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Build Validation](https://github.com/microsoft/durabletask-python/actions/workflows/pr-validation.yml/badge.svg)](https://github.com/microsoft/durabletask-python/actions/workflows/pr-validation.yml)
[![PyPI version](https://badge.fury.io/py/durabletask.svg)](https://badge.fury.io/py/durabletask)

This repo contains a Python client SDK for use with the [Durable Task Framework for Go](https://github.com/microsoft/durabletask-go) and [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/). With this SDK, you can define, schedule, and manage durable orchestrations using ordinary Python code.

⚠️ **This SDK is currently under active development and is not yet ready for production use.** ⚠️

> Note that this project is **not** currently affiliated with the [Durable Functions](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview) project for Azure Functions. If you are looking for a Python SDK for Durable Functions, please see [this repo](https://github.com/Azure/azure-functions-durable-python).


## Supported patterns

The following orchestration patterns are currently supported.

### Function chaining

An orchestration can chain a sequence of function calls using the following syntax:

```python
# simple activity function that returns a greeting
def hello(ctx: task.ActivityContext, name: str) -> str:
    return f'Hello {name}!'

# orchestrator function that sequences the activity calls
def sequence(ctx: task.OrchestrationContext, _):
    result1 = yield ctx.call_activity(hello, input='Tokyo')
    result2 = yield ctx.call_activity(hello, input='Seattle')
    result3 = yield ctx.call_activity(hello, input='London')

    return [result1, result2, result3]
```

You can find the full sample [here](./examples/activity_sequence.py).

### Fan-out/fan-in

An orchestration can fan-out a dynamic number of function calls in parallel and then fan-in the results using the following syntax:

```python
# activity function for getting the list of work items
def get_work_items(ctx: task.ActivityContext, _) -> List[str]:
    # ...

# activity function for processing a single work item
def process_work_item(ctx: task.ActivityContext, item: str) -> int:
    # ...

# orchestrator function that fans-out the work items and then fans-in the results
def orchestrator(ctx: task.OrchestrationContext, _):
    # the number of work-items is unknown in advance
    work_items = yield ctx.call_activity(get_work_items)

    # fan-out: schedule the work items in parallel and wait for all of them to complete
    tasks = [ctx.call_activity(process_work_item, input=item) for item in work_items]
    results = yield task.when_all(tasks)

    # fan-in: summarize and return the results
    return {'work_items': work_items, 'results': results, 'total': sum(results)}
```

You can find the full sample [here](./examples/fanout_fanin.py).

### Human interaction and durable timers

An orchestration can wait for a user-defined event, such as a human approval event, before proceding to the next step. In addition, the orchestration can create a timer with an arbitrary duration that triggers some alternate action if the external event hasn't been received:

```python
def purchase_order_workflow(ctx: task.OrchestrationContext, order: Order):
    """Orchestrator function that represents a purchase order workflow"""
    # Orders under $1000 are auto-approved
    if order.Cost < 1000:
        return "Auto-approved"

    # Orders of $1000 or more require manager approval
    yield ctx.call_activity(send_approval_request, input=order)

    # Approvals must be received within 24 hours or they will be canceled.
    approval_event = ctx.wait_for_external_event("approval_received")
    timeout_event = ctx.create_timer(timedelta(hours=24))
    winner = yield task.when_any([approval_event, timeout_event])
    if winner == timeout_event:
        return "Canceled"

    # The order was approved
    yield ctx.call_activity(place_order, input=order)
    approval_details = approval_event.get_result()
    return f"Approved by '{approval_details.approver}'"
```

As an aside, you'll also notice that the example orchestration above works with custom business objects. Support for custom business objects includes support for custom classes, custom data classes, and named tuples. Serialization and deserialization of these objects is handled automatically by the SDK.

You can find the full sample [here](./examples/human_interaction.py).

## Feature overview

The following features are currently supported:

### Orchestrations

Orchestrations are implemented using ordinary Python functions that take an `OrchestrationContext` as their first parameter. The `OrchestrationContext` provides APIs for starting child orchestrations, scheduling activities, and waiting for external events, among other things. Orchestrations are fault-tolerant and durable, meaning that they can automatically recover from failures and rebuild their local execution state. Orchestrator functions must be deterministic, meaning that they must always produce the same output given the same input.

### Activities

Activities are implemented using ordinary Python functions that take an `ActivityContext` as their first parameter. Activity functions are scheduled by orchestrations and have at-least-once execution guarantees, meaning that they will be executed at least once but may be executed multiple times in the event of a transient failure. Activity functions are where the real "work" of any orchestration is done.

### Durable timers

Orchestrations can schedule durable timers using the `create_timer` API. These timers are durable, meaning that they will survive orchestrator restarts and will fire even if the orchestrator is not actively in memory. Durable timers can be of any duration, from milliseconds to months.

### Sub-orchestrations

Orchestrations can start child orchestrations using the `call_sub_orchestrator` API. Child orchestrations are useful for encapsulating complex logic and for breaking up large orchestrations into smaller, more manageable pieces.

### External events

Orchestrations can wait for external events using the `wait_for_external_event` API. External events are useful for implementing human interaction patterns, such as waiting for a user to approve an order before continuing.

### Continue-as-new (TODO)

Orchestrations can be continued as new using the `continue_as_new` API. This API allows an orchestration to restart itself from scratch, optionally with a new input.

### Suspend, resume, and terminate

Orchestrations can be suspended using the `suspend_orchestration` client API and will remain suspended until resumed using the `resume_orchestration` client API. A suspended orchestration will stop processing new events, but will continue to buffer any that happen to arrive until resumed, ensuring that no data is lost. An orchestration can also be terminated using the `terminate_orchestration` client API. Terminated orchestrations will stop processing new events and will discard any buffered events.

### Retry policies (TODO)

Orchestrations can specify retry policies for activities and sub-orchestrations. These policies control how many times and how frequently an activity or sub-orchestration will be retried in the event of a transient error.

## Getting Started

### Prerequisites

- Python 3.8
- A Durable Task-compatible sidecar, like [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/)

### Installing the Durable Task Python client SDK

Installation is currently only supported from source. Ensure pip, setuptools, and wheel are up-to-date.

```sh
python3 -m pip install --upgrade pip setuptools wheel
```

To install this package from source, clone this repository and run the following command from the project root:

```sh
python3 -m pip install .
```

### Run the samples

See the [examples](./examples) directory for a list of sample orchestrations and instructions on how to run them.

## Development

The following is more information about how to develop this project. Note that development commands require that `make` is installed on your local machine. If you're using Windows, you can install `make` using [Chocolatey](https://chocolatey.org/) or use WSL.

### Generating protobufs

Protobuf definitions are stored in the [./submodules/durabletask-proto](./submodules/durabletask-proto) directory, which is a submodule. To update the submodule, run the following command from the project root:

```sh
git submodule update --init
```

Once the submodule is available, the corresponding source code can be regenerated using the following command from the project root:

```sh
make gen-proto
```

### Running unit tests

Unit tests can be run using the following command from the project root. Unit tests _don't_ require a sidecar process to be running.

```sh
make test-unit
```

### Running E2E tests

The E2E (end-to-end) tests require a sidecar process to be running. You can use the Dapr sidecar for this or run a Durable Task test sidecar using the following `docker` command:

```sh
docker run --name durabletask-sidecar -p 4001:4001 --env 'DURABLETASK_SIDECAR_LOGLEVEL=Debug' --rm cgillum/durabletask-sidecar:latest start --backend Emulator
```

To run the E2E tests, run the following command from the project root:

```sh
make test-e2e
```

## Contributing

This project welcomes contributions and suggestions.  Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

## Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 
trademarks or logos is subject to and must follow 
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "durabletask-dapr",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "durable, task, workflow",
    "author": null,
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/93/f5/cdda6e53edabc6e8f026fdecfcc937147899e0317a1d8dce0bb1db760f32/durabletask_dapr-0.2.0a3.tar.gz",
    "platform": null,
    "description": "# Durable Task Client SDK for Python (Dapr fork)\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Build Validation](https://github.com/microsoft/durabletask-python/actions/workflows/pr-validation.yml/badge.svg)](https://github.com/microsoft/durabletask-python/actions/workflows/pr-validation.yml)\n[![PyPI version](https://badge.fury.io/py/durabletask.svg)](https://badge.fury.io/py/durabletask)\n\nThis repo contains a Python client SDK for use with the [Durable Task Framework for Go](https://github.com/microsoft/durabletask-go) and [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/). With this SDK, you can define, schedule, and manage durable orchestrations using ordinary Python code.\n\n\u26a0\ufe0f **This SDK is currently under active development and is not yet ready for production use.** \u26a0\ufe0f\n\n> Note that this project is **not** currently affiliated with the [Durable Functions](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview) project for Azure Functions. If you are looking for a Python SDK for Durable Functions, please see [this repo](https://github.com/Azure/azure-functions-durable-python).\n\n\n## Supported patterns\n\nThe following orchestration patterns are currently supported.\n\n### Function chaining\n\nAn orchestration can chain a sequence of function calls using the following syntax:\n\n```python\n# simple activity function that returns a greeting\ndef hello(ctx: task.ActivityContext, name: str) -> str:\n    return f'Hello {name}!'\n\n# orchestrator function that sequences the activity calls\ndef sequence(ctx: task.OrchestrationContext, _):\n    result1 = yield ctx.call_activity(hello, input='Tokyo')\n    result2 = yield ctx.call_activity(hello, input='Seattle')\n    result3 = yield ctx.call_activity(hello, input='London')\n\n    return [result1, result2, result3]\n```\n\nYou can find the full sample [here](./examples/activity_sequence.py).\n\n### Fan-out/fan-in\n\nAn orchestration can fan-out a dynamic number of function calls in parallel and then fan-in the results using the following syntax:\n\n```python\n# activity function for getting the list of work items\ndef get_work_items(ctx: task.ActivityContext, _) -> List[str]:\n    # ...\n\n# activity function for processing a single work item\ndef process_work_item(ctx: task.ActivityContext, item: str) -> int:\n    # ...\n\n# orchestrator function that fans-out the work items and then fans-in the results\ndef orchestrator(ctx: task.OrchestrationContext, _):\n    # the number of work-items is unknown in advance\n    work_items = yield ctx.call_activity(get_work_items)\n\n    # fan-out: schedule the work items in parallel and wait for all of them to complete\n    tasks = [ctx.call_activity(process_work_item, input=item) for item in work_items]\n    results = yield task.when_all(tasks)\n\n    # fan-in: summarize and return the results\n    return {'work_items': work_items, 'results': results, 'total': sum(results)}\n```\n\nYou can find the full sample [here](./examples/fanout_fanin.py).\n\n### Human interaction and durable timers\n\nAn orchestration can wait for a user-defined event, such as a human approval event, before proceding to the next step. In addition, the orchestration can create a timer with an arbitrary duration that triggers some alternate action if the external event hasn't been received:\n\n```python\ndef purchase_order_workflow(ctx: task.OrchestrationContext, order: Order):\n    \"\"\"Orchestrator function that represents a purchase order workflow\"\"\"\n    # Orders under $1000 are auto-approved\n    if order.Cost < 1000:\n        return \"Auto-approved\"\n\n    # Orders of $1000 or more require manager approval\n    yield ctx.call_activity(send_approval_request, input=order)\n\n    # Approvals must be received within 24 hours or they will be canceled.\n    approval_event = ctx.wait_for_external_event(\"approval_received\")\n    timeout_event = ctx.create_timer(timedelta(hours=24))\n    winner = yield task.when_any([approval_event, timeout_event])\n    if winner == timeout_event:\n        return \"Canceled\"\n\n    # The order was approved\n    yield ctx.call_activity(place_order, input=order)\n    approval_details = approval_event.get_result()\n    return f\"Approved by '{approval_details.approver}'\"\n```\n\nAs an aside, you'll also notice that the example orchestration above works with custom business objects. Support for custom business objects includes support for custom classes, custom data classes, and named tuples. Serialization and deserialization of these objects is handled automatically by the SDK.\n\nYou can find the full sample [here](./examples/human_interaction.py).\n\n## Feature overview\n\nThe following features are currently supported:\n\n### Orchestrations\n\nOrchestrations are implemented using ordinary Python functions that take an `OrchestrationContext` as their first parameter. The `OrchestrationContext` provides APIs for starting child orchestrations, scheduling activities, and waiting for external events, among other things. Orchestrations are fault-tolerant and durable, meaning that they can automatically recover from failures and rebuild their local execution state. Orchestrator functions must be deterministic, meaning that they must always produce the same output given the same input.\n\n### Activities\n\nActivities are implemented using ordinary Python functions that take an `ActivityContext` as their first parameter. Activity functions are scheduled by orchestrations and have at-least-once execution guarantees, meaning that they will be executed at least once but may be executed multiple times in the event of a transient failure. Activity functions are where the real \"work\" of any orchestration is done.\n\n### Durable timers\n\nOrchestrations can schedule durable timers using the `create_timer` API. These timers are durable, meaning that they will survive orchestrator restarts and will fire even if the orchestrator is not actively in memory. Durable timers can be of any duration, from milliseconds to months.\n\n### Sub-orchestrations\n\nOrchestrations can start child orchestrations using the `call_sub_orchestrator` API. Child orchestrations are useful for encapsulating complex logic and for breaking up large orchestrations into smaller, more manageable pieces.\n\n### External events\n\nOrchestrations can wait for external events using the `wait_for_external_event` API. External events are useful for implementing human interaction patterns, such as waiting for a user to approve an order before continuing.\n\n### Continue-as-new (TODO)\n\nOrchestrations can be continued as new using the `continue_as_new` API. This API allows an orchestration to restart itself from scratch, optionally with a new input.\n\n### Suspend, resume, and terminate\n\nOrchestrations can be suspended using the `suspend_orchestration` client API and will remain suspended until resumed using the `resume_orchestration` client API. A suspended orchestration will stop processing new events, but will continue to buffer any that happen to arrive until resumed, ensuring that no data is lost. An orchestration can also be terminated using the `terminate_orchestration` client API. Terminated orchestrations will stop processing new events and will discard any buffered events.\n\n### Retry policies (TODO)\n\nOrchestrations can specify retry policies for activities and sub-orchestrations. These policies control how many times and how frequently an activity or sub-orchestration will be retried in the event of a transient error.\n\n## Getting Started\n\n### Prerequisites\n\n- Python 3.8\n- A Durable Task-compatible sidecar, like [Dapr Workflow](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/)\n\n### Installing the Durable Task Python client SDK\n\nInstallation is currently only supported from source. Ensure pip, setuptools, and wheel are up-to-date.\n\n```sh\npython3 -m pip install --upgrade pip setuptools wheel\n```\n\nTo install this package from source, clone this repository and run the following command from the project root:\n\n```sh\npython3 -m pip install .\n```\n\n### Run the samples\n\nSee the [examples](./examples) directory for a list of sample orchestrations and instructions on how to run them.\n\n## Development\n\nThe following is more information about how to develop this project. Note that development commands require that `make` is installed on your local machine. If you're using Windows, you can install `make` using [Chocolatey](https://chocolatey.org/) or use WSL.\n\n### Generating protobufs\n\nProtobuf definitions are stored in the [./submodules/durabletask-proto](./submodules/durabletask-proto) directory, which is a submodule. To update the submodule, run the following command from the project root:\n\n```sh\ngit submodule update --init\n```\n\nOnce the submodule is available, the corresponding source code can be regenerated using the following command from the project root:\n\n```sh\nmake gen-proto\n```\n\n### Running unit tests\n\nUnit tests can be run using the following command from the project root. Unit tests _don't_ require a sidecar process to be running.\n\n```sh\nmake test-unit\n```\n\n### Running E2E tests\n\nThe E2E (end-to-end) tests require a sidecar process to be running. You can use the Dapr sidecar for this or run a Durable Task test sidecar using the following `docker` command:\n\n```sh\ndocker run --name durabletask-sidecar -p 4001:4001 --env 'DURABLETASK_SIDECAR_LOGLEVEL=Debug' --rm cgillum/durabletask-sidecar:latest start --backend Emulator\n```\n\nTo run the E2E tests, run the following command from the project root:\n\n```sh\nmake test-e2e\n```\n\n## Contributing\n\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n## Trademarks\n\nThis project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft \ntrademarks or logos is subject to and must follow \n[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\nAny use of third-party trademarks or logos are subject to those third-party's policies.\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) Microsoft Corporation.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ",
    "summary": "A Durable Task Client SDK for Python",
    "version": "0.2.0a3",
    "project_urls": {
        "changelog": "https://github.com/dapr/durabletask-python/blob/main/CHANGELOG.md",
        "repository": "https://github.com/dapr/durabletask-python"
    },
    "split_keywords": [
        "durable",
        " task",
        " workflow"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9600d1cc071ce0ad52c8279e7c2bec58322cecd916443885a48a754a0da45e65",
                "md5": "6fcc69395a429bac12bbce49cb30b9bd",
                "sha256": "36c705492c30eb107a0eadc7ee62335b2fb12fef08083ac061634454be4aa0d7"
            },
            "downloads": -1,
            "filename": "durabletask_dapr-0.2.0a3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6fcc69395a429bac12bbce49cb30b9bd",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 42099,
            "upload_time": "2024-11-27T16:47:53",
            "upload_time_iso_8601": "2024-11-27T16:47:53.012172Z",
            "url": "https://files.pythonhosted.org/packages/96/00/d1cc071ce0ad52c8279e7c2bec58322cecd916443885a48a754a0da45e65/durabletask_dapr-0.2.0a3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "93f5cdda6e53edabc6e8f026fdecfcc937147899e0317a1d8dce0bb1db760f32",
                "md5": "a611d74f6671786c859f7f5a1735fe8d",
                "sha256": "794a9a41574f8e12b9cd496de8b618befa36e63ddda477c69b1064eeea8255e6"
            },
            "downloads": -1,
            "filename": "durabletask_dapr-0.2.0a3.tar.gz",
            "has_sig": false,
            "md5_digest": "a611d74f6671786c859f7f5a1735fe8d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 59724,
            "upload_time": "2024-11-27T16:47:54",
            "upload_time_iso_8601": "2024-11-27T16:47:54.636396Z",
            "url": "https://files.pythonhosted.org/packages/93/f5/cdda6e53edabc6e8f026fdecfcc937147899e0317a1d8dce0bb1db760f32/durabletask_dapr-0.2.0a3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-27 16:47:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dapr",
    "github_project": "durabletask-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "durabletask-dapr"
}
        
Elapsed time: 0.46101s