Skip to main content

Redux implementation for Python (Binary Extension)

Project description

🎛️ Python Redux

codecov PyPI - Python Version PyPI PyPI - License GitHub Workflow Status

🌟 Overview

Python Redux is a Redux implementation for Python, bringing Redux's state management architecture to Python applications.

🔎 Sample Usage

Minimal todo application store implemented using python-redux:

import uuid
from dataclasses import replace
from typing import Sequence

from immutable import Immutable

from redux import (
    BaseAction,
    BaseEvent,
    CompleteReducerResult,
    FinishAction,
    ReducerResult,
)
from redux.main import Store


# state:
class ToDoItem(Immutable):
    id: str
    content: str
    is_done: bool = False


class ToDoState(Immutable):
    items: Sequence[ToDoItem]


# actions:
class AddTodoItemAction(BaseAction):
    content: str


class MarkTodoItemDone(BaseAction):
    id: str


class RemoveTodoItemAction(BaseAction):
    id: str


# events:
class CallApi(BaseEvent):
    parameters: object


# reducer:
def reducer(
    state: ToDoState | None,
    action: BaseAction,
) -> ReducerResult[ToDoState, BaseAction, BaseEvent]:
    if state is None:
        return ToDoState(
            items=[
                ToDoItem(
                    id=uuid.uuid4().hex,
                    content='Initial Item',
                ),
            ],
        )
    if isinstance(action, AddTodoItemAction):
        return replace(
            state,
            items=[
                *state.items,
                ToDoItem(
                    id=uuid.uuid4().hex,
                    content=action.content,
                ),
            ],
        )
    if isinstance(action, RemoveTodoItemAction):
        return replace(
            state,
            actions=[item for item in state.items if item.id != action.id],
        )
    if isinstance(action, MarkTodoItemDone):
        return CompleteReducerResult(
            state=replace(
                state,
                items=[
                    replace(item, is_done=True) if item.id == action.id else item
                    for item in state.items
                ],
            ),
            events=[CallApi(parameters={})],
        )
    return state


store = Store(reducer)


# subscription:
dummy_render = print
store.subscribe(dummy_render)


# autorun:
@store.autorun(
    lambda state: state.items[0].content if len(state.items) > 0 else None,
)
def reaction(content: str | None) -> None:
    print(content)


@store.view(lambda state: state.items[0])
def first_item(first_item: ToDoItem) -> ToDoItem:
    return first_item


@store.view(lambda state: [item for item in state.items if item.is_done])
def done_items(done_items: list[ToDoItem]) -> list[ToDoItem]:
    return done_items


# event listener, note that this will run async in a separate thread, so it can include
# io operations like network calls, etc:
dummy_api_call = print
store.subscribe_event(
    CallApi,
    lambda event: dummy_api_call(event.parameters, done_items()),
)

# dispatch:
store.dispatch(AddTodoItemAction(content='New Item'))

store.dispatch(MarkTodoItemDone(id=first_item().id))

store.dispatch(FinishAction())

⚙️ Features

  • Redux API for Python developers.

  • Reduce boilerplate by dropping type property, payload classes and action creators:

    • Each action is a subclass of BaseAction.
    • Its type is checked by utilizing isinstance (no need for type property).
    • Its payload are its direct properties (no need for a separate payload object).
    • Its creator is its auto-generated constructor.
  • Use type annotations for all its API.

  • Immutable state management for predictable state updates using python-immutable.

  • Offers a streamlined, native API for handling side-effects asynchronously, eliminating the necessity for more intricate utilities such as redux-thunk or redux-saga.

  • Incorporates the autorun decorator and the view decorator, inspired by the mobx framework, to better integrate with elements of the software following procedural patterns.

  • Supports middlewares.

📦 Installation

The package handle in PyPI is python-redux

Pip

pip install python-redux

Poetry

poetry add python-redux

🛠 Usage

Handling Side Effects with Events

Python-redux introduces a powerful concept for managing side effects: Events. This approach allows reducers to remain pure while still signaling the need for side effects.

Why Events?

  • Separation of Concerns: By returning events, reducers stay pure and focused solely on state changes, delegating side effects to other parts of the software.
  • Flexibility: Events allow asynchronous operations like API calls to be handled separately, enhancing scalability and maintainability.

How to Use Events

  • Reducers: Reducers primarily return a new state. They can optionally return actions and events, maintaining their purity as these do not enact side effects themselves.
  • Dispatch Function: Besides actions, dispatch function can now accept events, enabling a more integrated flow of state and side effects.
  • Event Listeners: Implement listeners for specific events. These listeners handle the side effects (e.g., API calls) asynchronously.

Best Practices

  • Define Clear Events: Create well-defined events that represent specific side effects.
  • Use Asynchronously: Design event listeners to operate asynchronously, keeping your application responsive. Note that python-redux, by default, runs all event handler functions in new threads.

This concept fills the gap in handling side effects within Redux's ecosystem, offering a more nuanced and integrated approach to state and side effect management.

See todo sample below or check the todo demo or features demo to see it in action.

Autorun Decorator

Inspired by MobX's autorun and reaction, python-redux introduces the autorun decorator. This decorator requires a selector function as an argument. The selector is a function that accepts the store instance and returns a derived object from the store's state. The primary function of autorun is to establish a subscription to the store. Whenever the store is changed, autorun executes the selector with the updated store. Importantly, the decorated function is triggered only if there is a change in the selector's return value. This mechanism ensures that the decorated function runs in response to relevant state changes, enhancing efficiency and responsiveness in the application.

See todo sample below or check the todo demo or features demo to see it in action.

View Decorator

Inspired by MobX's computed, python-redux introduces the view decorator. It takes a selector and each time the decorated function is called, it only runs the function body if the returned value of the selector is changed, otherwise it simply returns the previous value. So unlike computed of MobX, it doesn't extract the requirements of the function itself, you need to provide them in the return value of the selector function.

Combining reducers - combine_reducers

You can compose high level reducers by combining smaller reducers using combine_reducers utility function. This works mostly the same as the JS redux library version except that it provides a mechanism to dynamically add/remove reducers to/from it. This is done by generating an id and returning it along the generated reducer. This id is used to refer to this reducer in the future. Let's assume you composed a reducer like this:

reducer, reducer_id = combine_reducers(
    state_type=StateType,
    first=straight_reducer,
    second=second_reducer,
)

You can then add a new reducer to it using the reducer_id like this:

store.dispatch(
    CombineReducerRegisterAction(
        combine_reducers_id=reducer_id,
        key='third',
        third=third_reducer,
    ),
)

You can also remove a reducer from it like this:

store.dispatch(
    CombineReducerRegisterAction(
        combine_reducers_id=reducer_id,
        key='second',
    ),
)

Without this id, all the combined reducers in the store tree would register third reducer and unregister second reducer, but thanks to this reducer_id, these actions will only target the desired combined reducer.

🎉 Demo

For a detailed example, see features demo.

🤝 Contributing

Contributions following Python best practices are welcome.

📜 License

This project is released under the Apache-2.0 License. See the LICENSE file for more details.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_x86_64.whl (2.9 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ARM64

python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_10_13_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12macOS 10.13+ x86-64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_arm64.whl (977.5 kB view details)

Uploaded CPython 3.11Windows ARM64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_amd64.whl (1.0 MB view details)

Uploaded CPython 3.11Windows x86-64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-win32.whl (989.4 kB view details)

Uploaded CPython 3.11Windows x86

python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ARM64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (2.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_10_9_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11macOS 10.9+ x86-64

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 b6399872ea5d128b113eddee8b84422e943d85e0087f9d5f7599aead4517b30f
MD5 dca504c0dde1c0f1962b84c4d0727509
BLAKE2b-256 fc45d50b08b3b325ae3f8cd30fad13a4417ddd2da90c13ed614f6fb782a12f54

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 ec99dd45952074a221b167feea3e519ba13f72739dd54fc20bea7ec012749527
MD5 9a7adf31ddc28dc859fd1f0341713e38
BLAKE2b-256 0bf3e1ff7670343febe0215323151a4335c4829d28948b9e7c60f9848c21934f

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-musllinux_1_2_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 be7572a277cb656f07a7a69b337656ed4efabc5607c980744a31a465f41e6f35
MD5 9e02d52bdff9a3dceb5da448cd6f8a65
BLAKE2b-256 1466e598be138c479ea84d98ebb0652cae3f8225aca2e258cefe66cf489785f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 eac6cc0aef64e3c1cfa4c369df4a5e4a9f75b1600d34144db61ccf6b94ea1e4c
MD5 8d418ddb5e7136ee9e427cc6ae90cec7
BLAKE2b-256 0962c687143221ff4ec4f409313e006d2ca841e16391b68c349170c9758858b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 300c76eb83d008a7185fbf28ebf1785a4c72933ffdf9e607e8b3dfdc4de09eb6
MD5 2a417bdabd18c1669fca9cb0181ef642
BLAKE2b-256 1bc6c316318ee6017ae348ddfdf860d05a91e5ae9a650001094d9e215d43dbf4

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 4568645ed76ff01857f75d95c10cac8218d755bcddb3d260319925333376538a
MD5 94c1e6e0326f38f825c8d2980cc8377a
BLAKE2b-256 3263df38602ba1e05f794ff35c5f9457e91e65629645d957defde340c19433d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp312-cp312-macosx_10_13_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_arm64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_arm64.whl
Algorithm Hash digest
SHA256 33f7acd5036a7b12cb1426b900ed77396038c3377285cda8079a76c3ff86fb79
MD5 6a82ca25a0b8a679b0d4d598f8cf862b
BLAKE2b-256 8d864b435d0dc005753a0887d9dcb6e43bc99ea7c7fc591d11570d01d0e3d769

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_arm64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 8e729a9b182e4a8a3e37673855943b3991107ff95eea5bdcd7f6e8be21730852
MD5 b7f3a58e583f52e8640235768d0e426d
BLAKE2b-256 551811a7e6857821a703d9bd400cfb674d629489f5063a598d46d11d0352430e

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win_amd64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-win32.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win32.whl
Algorithm Hash digest
SHA256 b5d8d1febdf2853434d2073ee39f6bc9cf750bc29e513d880ca0bb29f1bf5499
MD5 f91d8bd7588187a46a00f323688a6e21
BLAKE2b-256 d7deffdb31b9114c166e4fe47d19d662aa5113ebb8bcd60dc7c13ea43577bf3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-win32.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 2e68dcd6391e104e0ae895b50276e92cb7a94b08d40378773246d7adbe6f62cb
MD5 ea314365a9430afb355d863edce69967
BLAKE2b-256 8c7f1471519eef696a3b17629623c844b22bff5f68667e20a806c42d9aec44bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 743817ae13379d3afa9a7f61d55ca78c629f057e84452bd537bdc0b14f435fc6
MD5 99b3b29cfef2eb3008daee40490805d0
BLAKE2b-256 8f968ea91f7b7fc6f178e604f7e63374d5c660ad46c960986e06771a4d0312fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-musllinux_1_2_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7c3b3932ff00144af1c7c7693a6e2932ee7d9bc7abe6b7fb8b7be42350ecbff4
MD5 6e240893ac3018a9d10af75b7ddc8443
BLAKE2b-256 04245d68e56cbeabf500904223da9b2bc0ab68671130ee84734037b16ef5de68

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 7bac32684ec59badf630506b724bc21d2ab5c6aca4437272c5ed7acf886aaaba
MD5 af3a65678517337968cfd2b41609abbd
BLAKE2b-256 9bbf618adb1715428c42af06687f39aeb1a47a1cbbef795f35bbb2befc32e898

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 bb6844b93fa5445cb3b42c3eb239060308c8c2411163dcbb2d3d923688c5c6e5
MD5 31c9ed407c779aa3cfbe25a656c28175
BLAKE2b-256 eb42eb3ba0d62039d204c807e72dee35b246794745c6dfae42590d3f92c6a381

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 3d163a77a14475f32d37f57453054a623926586546430e5544417d2ed34a3122
MD5 c4a166107d8494e5d826f89eaaa80fb8
BLAKE2b-256 d09f8bf52303ac3c25e65c9557221cb4c4135bfeb0002269a8559ef6624b047a

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_redux-0.25.2.dev226020910357484956-cp311-cp311-macosx_10_9_x86_64.whl:

Publisher: integration_delivery.yml on sassanh/python-redux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page