Skip to main content

A neuron morphology IO library

Project description

MorphIO Build Status

Table of content

Installation

Dependencies

To build MorphIO from sources, the following dependencies are required:

  • cmake >= 3.2
  • libhdf5-dev
  • A C++11 compiler

Debian:

sudo apt install cmake libhdf5-dev

Red Hat:

sudo yum install cmake3.x86_64 hdf5-devel.x86_64

Max OS:

brew install hdf5 cmake

BB5

source /opt/rh/rh-python36/enable
module load gcc/5.4.0 nix/cmake/3.9.6

Installation instructions

Install as a C++ library

For manual installation:

git clone git@github.com:bluebrain/morphio.git --recursive
cd morphio
mkdir build && cd build
cmake ..
make install

To use the installed library:

find_package(MorphIO REQUIRED)

target_link_libraries(mylib MorphIO::morphio)

Install as a Python package

The python binding can directly be installed using pip:

pip install morphio

Introduction

MorphIO is a library for reading and writing neuron morphology files. It supports the following formats:

  • SWC
  • ASC (aka. neurolucida)
  • H5 v1
  • H5 v2

It provides 3 C++ classes that are the starting point of every morphology analysis:

  • Soma: contains the information related to the soma.

  • Section: a section is the succession of points between two bifurcations. To the bare minimum the Section object will contain the section type, the position and diameter of each point.

  • Morphology: the morphology object contains general information about the loaded cell but also provides accessors to the different sections.

One important concept is that MorphIO is split into a read-only part and a read/write one.

Quick summary

C++ vs Python:

  • C++ accessors become Python properties.
  • style: C++ functions are camel case while Python ones are snake case.

Include/imports

  • C++ mutable
#include <morphio/morphology.h>
#include <morphio/section.h>
#include <morphio/soma.h>
  • Python mutable
from morphio import Morphology, Section, Soma
  • C++ immutable
#include <morphio/mut/morphology.h>
#include <morphio/mut/section.h>
#include <morphio/mut/soma.h>
  • Python immutable
from morphio.mut import Morphology, Section, Soma

Read-only API

The read-only API aims at providing better performances as its internal data representation is contiguous in memory. All accessors return immutable objects.

Internally, in this API the morphology object is in fact where all data are stored. The Soma and Section classes are lightweight classes that provide views on the Morphology data.

For more convenience, all section data are accessed through properties, such as:

points = section.points
diameters = section.diameters

C++

In C++ the API is available under the morphio/mut namespace:

#include <morphio/mut/morphology.h>
#include <morphio/mut/section.h>
#include <morphio/mut/soma.h>

Python

In Python the API is available under the morphio.mut module:

from morphio.mut import Morphology, Section, Soma

Mutable Read/Write API

C++

#include <morphio/morphology.h>
#include <morphio/section.h>

int main()
{
    auto m = morphio::Morphology("sample.asc");

    auto roots = m.rootSections();

    auto first_root = roots[0];

    // iterate on sections starting at first_root
    for (auto it = first_root.depth_begin(); it != first_root.depth_end(); ++it) {
        const morphio::Section &section = *it;

        std::cout << "Section type: " << section.type()
                  << "\nSection id: " << section.id()
                  << "\nParent section id: " << section.parent().id()
                  << "\nNumber of child sections: " << section.children().size()
                  << "\nX - Y - Z - Diameter";
        for (auto i = 0u; i < section.points().size(); ++i) {
            const auto& point = section.points()[i];
            std::copy(point.begin(), point.end(), std::ostream_iterator<float>(std::cout, " "));
            std::cout << '\n' << section.diameters()[i] << '\n';
        }
        std::cout << '\n';
    }
}

Python

from morphio import Morphology

m = Morphology("sample.asc")
roots = m.rootSections
first_root = roots[0]

# iterate on sections starting at first_root
for section in first_root.iter():
    print("Section type: {}".format(section.type))
    print("Section id: {}".format(section.id))
    print("Parent section id: {}".format(section.parent.id))
    print("Number of child sections: {}".format(len(section.children)))
    print("X - Y - Z - Diameter")

    for point, diameter in zip(section.points, section.diameters):
        print('{} - {}'.format(point, diameter))

Creating morphologies with the mutable API

Here is a simple example to create a morphology from scratch and write it to disk

#include <morphio/mut/morphology.h>

int main()
{
    morphio::mut::Morphology morpho;
    morpho.soma()->points() = {{0, 0, 0}, {1, 1, 1}};
    morpho.soma()->diameters() = {1, 1};

    auto section = morpho.appendRootSection(
        morphio::Property::PointLevel(
            {{2, 2, 2}, {3, 3, 3}}, // x,y,z coordinates of each point
            {4, 4}, // diameter of each point
            {5, 5}),
        morphio::SectionType::SECTION_AXON); // (optional) perimeter of each point

    auto childSection = section->appendSection(
        morphio::Property::PointLevel(
            {{3, 3, 3}, {4, 4, 4}},
            {4, 4},
            {5, 5}),
        morphio::SectionType::SECTION_AXON);

    // Writing the file in the 3 formats
    morpho.write("outfile.asc");
    morpho.write("outfile.swc");
    morpho.write("outfile.h5");
}

Mutable Python

Reading morphologies

from morphio.mut import Morphology

m = Morphology("sample.asc")
roots = m.root_sections
first_root = roots[0]

# iterate on sections starting at first_root
for section in m.iter(first_root):
    print("Section type: {}".format(section.type))
    print("Section id: {}".format(section.id))
    if not m.is_root(section):
        print("Parent section id: {}".format(m.parent(section)))
    print("Number of child sections: {}".format(len(m.children(section))))
    print("X - Y - Z - Diameter")

    for point, diameter in zip(section.points, section.diameters):
        print('{} - {}'.format(point, diameter))

Creating morphologies

Here is a simple example to create a morphology from scratch and writing it to disk

from morphio.mut import Morphology
from morphio import SectionType, PointLevel

morpho = Morphology()
morpho.soma.points = [[0, 0, 0], [1, 1, 1]]
morpho.soma.diameters = [1, 1]

section = morpho.append_root_section(
    PointLevel(
        [[2, 2, 2], [3, 3, 3]],  # x, y, z coordinates of each point
        [4, 4],  # diameter of each point
        [5, 5]),
    SectionType.axon)  # (optional) perimeter of each point

child_section = section.append_section(
    PointLevel(
        [[3, 3, 3], [4, 4, 4]],
        [4, 4],
        [5, 5])) # section type is omitted -> parent section type will be used

morpho.write("outfile.asc")
morpho.write("outfile.swc")
morpho.write("outfile.h5")

Opening flags

When opening the file, modifier flags can be passed to alter the morphology representation. The following flags are supported:

  • morphio::NO\_MODIFIER: This is the default flag, it will do nothing.
  • morphio::TWO\_POINTS\_SECTIONS: Each section gets reduce to a line made of the first and last point.
  • morphio::SOMA\_SPHERE: The soma is reduced to a sphere which is the center of gravity of the real soma.
  • morphio::NO\_DUPLICATES: The duplicate point are not present. It means the first point of each section is no longer the last point of the parent section.
  • morphio::NRN\_ORDER: Neurite are reordered according to the NEURON simulator ordering

Multiple flags can be passed by using the standard bit flag manipulation (works the same way in C++ and Python):

C++:

#include <morphio/Morphology.h>
Morphology("myfile.asc", options=morphio::NO_DUPLICATES|morphio::NRN_ORDER)

Python:

from morphio import Morphology, Option
Morphology("myfile.asc", options=Option.no_duplicates|Option.nrn_order)

Mitochondria

It is also possible to read and write mitochondria from/to the h5 files (SWC and ASC are not supported). As mitochondria can be represented as trees, one can define the concept of mitochondrial section similar to neuronal section and end up with a similar API. The morphology object has a mitochondria handle method that exposes the basic methods:

  • root_sections: returns the section ID of the starting mitochondrial section of each mitochondrion.
  • section(id): returns a given mitochondrial section
  • append_section: creates a new mitochondrial section _ depth_begin: a depth first iterator _ breadth_begin: a breadth first iterator _ upstream_begin: an upstream iterator
from morphio.mut import Morphology
from morphio import MitochondriaPointLevel, PointLevel, SectionType

morpho = Morphology()

# A neuronal section that will store mitochondria
section = morpho.append_root_section(
    PointLevel([[2, 2, 2], [3, 3, 3]], [4, 4], [5, 5]),
    SectionType.axon)

# Creating a new mitochondrion
mito_id = morpho.mitochondria.append_section(
    -1,
    MitochondriaPointLevel([section.id, section.id], # section id hosting the mitochondria point
                           [0.5, 0.6], # relative distance between the start of the section and the point
                           [10, 20] # mitochondria diameters
                           ))

# Appending a new mitochondrial section to the previous one
morpho.mitochondria.append_section(
    mito_id, MitochondriaPointLevel([0, 0, 0, 0],
                                    [0.6, 0.7, 0.8, 0.9],
                                    [20, 30, 40, 50]))

# Iteration works the same as iteration on neuronal sections
first_root = morpho.mitochondria.root_sections[0]
for section_id in morpho.mitochondria.depth_begin(first_root):
    section = morpho.mitochondria.section(section_id)
    print('relative_path_length - diameter')
    for relative_path_length, diameter in zip(section.diameters,
                                              section.relative_path_lengths):
        print("{} - {}".format(relative_path_length, diameter))

Reading mithochondria from H5 files:

from morphio import Morphology

morpho = Morphology("file_with_mithochondria.h5")

for mitochondrial_section in morpho.mitochondria.root_sections:
    print('{neurite_id}, {relative_path_lengths}, {diameters}'.format(
          neurite_id=mitochondrial_section.neurite_section_ids,
          relative_path_lengths=mitochondrial_section.relative_path_lengths,
          diameters=mitochondrial_section.diameters))

    print("Number of children: {}".format(len(mitochondrial_section.children)))

Tips

Maximum number of warnings

On can control the maximum number of warnings using the command:

# Will stop displaying warnings after 100 warnings
morphio.set_maximum_warnings(100)

# Will never stop displaying warnings
morphio.set_maximum_warnings(-1)

# Warnings won't be displayed
morphio.set_maximum_warnings(0)

Specification

See https://github.com/BlueBrain/MorphIO/blob/master/doc/specification.md

Contributing

If you want to improve the project or you see any issue, every contribution is welcome. Please check the contribution guidelines for more information.

Project details


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

MorphIO-2.3.5-cp38-cp38-manylinux1_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.8

MorphIO-2.3.5-cp38-cp38-manylinux1_i686.whl (2.0 MB view details)

Uploaded CPython 3.8

MorphIO-2.3.5-cp38-cp38-macosx_10_13_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.8 macOS 10.13+ x86-64

MorphIO-2.3.5-cp37-cp37m-manylinux1_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.7m

MorphIO-2.3.5-cp37-cp37m-manylinux1_i686.whl (2.0 MB view details)

Uploaded CPython 3.7m

MorphIO-2.3.5-cp37-cp37m-macosx_10_13_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.7m macOS 10.13+ x86-64

MorphIO-2.3.5-cp36-cp36m-manylinux1_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.6m

MorphIO-2.3.5-cp36-cp36m-manylinux1_i686.whl (2.0 MB view details)

Uploaded CPython 3.6m

MorphIO-2.3.5-cp36-cp36m-macosx_10_13_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.6m macOS 10.13+ x86-64

MorphIO-2.3.5-cp27-cp27mu-manylinux1_x86_64.whl (2.1 MB view details)

Uploaded CPython 2.7mu

MorphIO-2.3.5-cp27-cp27mu-manylinux1_i686.whl (2.0 MB view details)

Uploaded CPython 2.7mu

MorphIO-2.3.5-cp27-cp27m-macosx_10_13_x86_64.whl (1.7 MB view details)

Uploaded CPython 2.7m macOS 10.13+ x86-64

File details

Details for the file MorphIO-2.3.5-cp38-cp38-manylinux1_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp38-cp38-manylinux1_x86_64.whl
  • Upload date:
  • Size: 2.1 MB
  • Tags: CPython 3.8
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp38-cp38-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 361be74bbf13e52cf18c34796f0f1890d478fe125934d6c986b3687b1a1b9f83
MD5 2936eba34e9a2aed2f4a90d12d164047
BLAKE2b-256 b51f53c5bdfde8598d54725264c88373c6ae8e3940c28d40523540e3a9654a04

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp38-cp38-manylinux1_i686.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp38-cp38-manylinux1_i686.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.8
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp38-cp38-manylinux1_i686.whl
Algorithm Hash digest
SHA256 0417f7af728ebd73381362316d9c574a3a4e9712f6b6442bbc9e19444555358a
MD5 c9a875094bf182c3360de30baa32906f
BLAKE2b-256 108ce4dc8ee8c5acf249e008db1d66217068252319ee2826138c5ffa08bc0ccc

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp38-cp38-macosx_10_13_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp38-cp38-macosx_10_13_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: CPython 3.8, macOS 10.13+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.8.1

File hashes

Hashes for MorphIO-2.3.5-cp38-cp38-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 c709ada13b87f8f8a12f9417c54e183c2de21022601b1f706c49f3fe6af9d2da
MD5 1f3dd1c19f9ecd7b3dc0c425e82c83c1
BLAKE2b-256 be30ed74756bdde184c5afd61c2571681734da601a456e6de35fadc7fda97387

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp37-cp37m-manylinux1_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp37-cp37m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 2.1 MB
  • Tags: CPython 3.7m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp37-cp37m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 a0447f97cc2e413eb2f78dd5bee028e6487298f4b7f07ce426a3ce6416a8fe0d
MD5 8dfb5064541a273a0093df3952275d95
BLAKE2b-256 596132830f21c9c956b19195dd8b0e05299307c6d04ac4bfa31b46320d87891a

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp37-cp37m-manylinux1_i686.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp37-cp37m-manylinux1_i686.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.7m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp37-cp37m-manylinux1_i686.whl
Algorithm Hash digest
SHA256 9ce9946e4ce6f129597c85f4c9e5fbc886a77312b1a79ae90720a1dcf353c469
MD5 f2fc216719ee4d50dca12df42f81ca5c
BLAKE2b-256 59383beaa67675f9cb6602e2ce90a26acad8f4d7e83ca6d190b87ae0cc852cc9

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp37-cp37m-macosx_10_13_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp37-cp37m-macosx_10_13_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: CPython 3.7m, macOS 10.13+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.7.6

File hashes

Hashes for MorphIO-2.3.5-cp37-cp37m-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 9dd7c284df54427e8988626b2ad6ccd3a9abae0118c59f63967b91c43e566ad8
MD5 028b35ea030a957b00003cda16310f2d
BLAKE2b-256 b044ce60b33fa5f0ce223a7770514efe7abc51db5d47bb7616bf6c398f4e6477

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp36-cp36m-manylinux1_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp36-cp36m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 2.1 MB
  • Tags: CPython 3.6m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp36-cp36m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 9fb57bf726afccaf3c6fcad12894f9edc339feabb7297025e056e823eba5e8bf
MD5 64a18ec3ce28559a276e5e1adaf3ca63
BLAKE2b-256 38fc6e2f1305f561f7fd1d412be23b556370c790b05bd67715be22f566630833

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp36-cp36m-manylinux1_i686.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp36-cp36m-manylinux1_i686.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.6m
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp36-cp36m-manylinux1_i686.whl
Algorithm Hash digest
SHA256 9780c78e125395180cca3937ff3be0e804c13fb6f7a10246ea943f809ce952d1
MD5 7d07fa0f27ab9ed3c8688cb55be03941
BLAKE2b-256 99a09d34ce942fd27e0e6149ca924640c617017cc27b7986a72fc2d66f0c2724

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp36-cp36m-macosx_10_13_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp36-cp36m-macosx_10_13_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: CPython 3.6m, macOS 10.13+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.6.8

File hashes

Hashes for MorphIO-2.3.5-cp36-cp36m-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 c8a1f98bb085ce82d457939ac4c80fd60c3db4d554bf81153d5c65b33f7fd61e
MD5 09ad9aa9e825c53b848f243ad13aa48f
BLAKE2b-256 7f12a426ba0fb0c5e4cb2cea98d613885230a633559f7942175cbfcd3863c64f

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp27-cp27mu-manylinux1_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp27-cp27mu-manylinux1_x86_64.whl
  • Upload date:
  • Size: 2.1 MB
  • Tags: CPython 2.7mu
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp27-cp27mu-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 d3705ad8e57c07844f03befa0ac3b7e505ac3cd2c12c6d91b36a169bbd123a4c
MD5 77ccd7bdff02577cb8dcbe427fcf92ff
BLAKE2b-256 1c1e02312baa126f3f621d8c24b42465c893d51ed644b531b07565a9e9ab706b

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp27-cp27mu-manylinux1_i686.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp27-cp27mu-manylinux1_i686.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 2.7mu
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.12

File hashes

Hashes for MorphIO-2.3.5-cp27-cp27mu-manylinux1_i686.whl
Algorithm Hash digest
SHA256 1a3abc5e91fb56d8a3347a1d3573a31ac05d6cc01fed553fcde79624508f58b2
MD5 4a189c7d7f3a44fa430957f1d2fffc1c
BLAKE2b-256 d70cb484be63e8a4c49baccbb232a3b0e04893adf2edba77fe6a47111c7cc63d

See more details on using hashes here.

File details

Details for the file MorphIO-2.3.5-cp27-cp27m-macosx_10_13_x86_64.whl.

File metadata

  • Download URL: MorphIO-2.3.5-cp27-cp27m-macosx_10_13_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: CPython 2.7m, macOS 10.13+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.15.0 pkginfo/1.5.0.1 requests/2.23.0 setuptools/44.0.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/2.7.17

File hashes

Hashes for MorphIO-2.3.5-cp27-cp27m-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 f37f61d9a4b0d8a315ac2119764138d05012c1e11aa3669dfb6bae68febce966
MD5 f97210061c5d20d543fe707ef0e1abbe
BLAKE2b-256 f647cabc265aacf6c17aa856d0a7cd74f99a62522b3b6cd4a3d2797fbab97ff8

See more details on using hashes here.

Supported by

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