diff --git a/changelog/1016.feature.md b/changelog/1016.feature.md new file mode 100644 index 000000000..1e8deacc8 --- /dev/null +++ b/changelog/1016.feature.md @@ -0,0 +1,2 @@ +Added tracing functionality to the Rasa SDK, bringing enhanced monitoring, execution profiling and debugging capabilities to the Rasa Actions Server. +See [Rasa Documentation on Tracing](https://rasa.com/docs/rasa/monitoring/tracing/#configuring-a-tracing-backend-or-collector) to know more about configuring a tracing backend or collector. \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 5dea37683..ef6570865 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "aiofiles" @@ -33,6 +33,18 @@ doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] trio = ["trio (>=0.16,<0.22)"] +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + [[package]] name = "black" version = "22.12.0" @@ -73,7 +85,7 @@ uvloop = ["uvloop (>=0.15.2)"] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -85,7 +97,7 @@ files = [ name = "charset-normalizer" version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" +category = "main" optional = false python-versions = "*" files = [ @@ -324,6 +336,24 @@ requests = ">=1.0.0" [package.extras] yaml = ["PyYAML (>=3.10)"] +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "docopt" version = "0.6.2" @@ -350,6 +380,82 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "googleapis-common-protos" +version = "1.56.2" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "googleapis-common-protos-1.56.2.tar.gz", hash = "sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3"}, + {file = "googleapis_common_protos-1.56.2-py2.py3-none-any.whl", hash = "sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c"}, +] + +[package.dependencies] +protobuf = ">=3.15.0,<4.0.0dev" + +[package.extras] +grpc = ["grpcio (>=1.0.0,<2.0.0dev)"] + +[[package]] +name = "grpcio" +version = "1.56.2" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.56.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9"}, + {file = "grpcio-1.56.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316"}, + {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950"}, + {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d"}, + {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4"}, + {file = "grpcio-1.56.2-cp310-cp310-win32.whl", hash = "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57"}, + {file = "grpcio-1.56.2-cp310-cp310-win_amd64.whl", hash = "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d"}, + {file = "grpcio-1.56.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd"}, + {file = "grpcio-1.56.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95"}, + {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c"}, + {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c"}, + {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c"}, + {file = "grpcio-1.56.2-cp311-cp311-win32.whl", hash = "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774"}, + {file = "grpcio-1.56.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f"}, + {file = "grpcio-1.56.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756"}, + {file = "grpcio-1.56.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1"}, + {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c"}, + {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9"}, + {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475"}, + {file = "grpcio-1.56.2-cp37-cp37m-win_amd64.whl", hash = "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5"}, + {file = "grpcio-1.56.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed"}, + {file = "grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87"}, + {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328"}, + {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b"}, + {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d"}, + {file = "grpcio-1.56.2-cp38-cp38-win32.whl", hash = "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8"}, + {file = "grpcio-1.56.2-cp38-cp38-win_amd64.whl", hash = "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a"}, + {file = "grpcio-1.56.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35"}, + {file = "grpcio-1.56.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116"}, + {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6"}, + {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1"}, + {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f"}, + {file = "grpcio-1.56.2-cp39-cp39-win32.whl", hash = "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9"}, + {file = "grpcio-1.56.2-cp39-cp39-win_amd64.whl", hash = "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a"}, + {file = "grpcio-1.56.2.tar.gz", hash = "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.56.2)"] + [[package]] name = "h11" version = "0.14.0" @@ -481,7 +587,7 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -755,6 +861,180 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "opentelemetry-api" +version = "1.15.0" +description = "OpenTelemetry Python API" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_api-1.15.0-py3-none-any.whl", hash = "sha256:e6c2d2e42140fd396e96edf75a7ceb11073f4efb4db87565a431cc9d0f93f2e0"}, + {file = "opentelemetry_api-1.15.0.tar.gz", hash = "sha256:79ab791b4aaad27acc3dc3ba01596db5b5aac2ef75c70622c6038051d6c2cded"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +setuptools = ">=16.0" + +[[package]] +name = "opentelemetry-exporter-jaeger" +version = "1.15.0" +description = "Jaeger Exporters for OpenTelemetry" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_jaeger-1.15.0-py3-none-any.whl", hash = "sha256:e8d1b8b95095736507fbef46eea4ee9472e9e7f415ee4461f9414d9d1590ac37"}, + {file = "opentelemetry_exporter_jaeger-1.15.0.tar.gz", hash = "sha256:5d0e5a1b37589a4d7eb67be90aa1fec45431565f8e84ae4960437e77b779002e"}, +] + +[package.dependencies] +opentelemetry-exporter-jaeger-proto-grpc = "1.15.0" +opentelemetry-exporter-jaeger-thrift = "1.15.0" + +[[package]] +name = "opentelemetry-exporter-jaeger-proto-grpc" +version = "1.15.0" +description = "Jaeger Protobuf Exporter for OpenTelemetry" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_jaeger_proto_grpc-1.15.0-py3-none-any.whl", hash = "sha256:78c46b8b8c9ceabd1107cc85a85b463bd50a049e980c370483d0c3c577632991"}, + {file = "opentelemetry_exporter_jaeger_proto_grpc-1.15.0.tar.gz", hash = "sha256:ff650cc786932cf0fce9809d18f680df7fb49955511009067322470a25b27c5c"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.52,<1.56.3" +grpcio = ">=1.0.0,<2.0.0" +opentelemetry-api = ">=1.3,<2.0" +opentelemetry-sdk = ">=1.11,<2.0" + +[[package]] +name = "opentelemetry-exporter-jaeger-thrift" +version = "1.15.0" +description = "Jaeger Thrift Exporter for OpenTelemetry" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_jaeger_thrift-1.15.0-py3-none-any.whl", hash = "sha256:a9d6dcdb203d10d6b0f72bfaeebf1e4822e2636d7d35ff67ed5a9fc672d76fc5"}, + {file = "opentelemetry_exporter_jaeger_thrift-1.15.0.tar.gz", hash = "sha256:2d85ad991c49f63f2397bcbae3881b9d58e51797d2f9c6fe4e02d6372e92b3ec"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.3,<2.0" +opentelemetry-sdk = ">=1.11,<2.0" +thrift = ">=0.10.0" + +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.15.0" +description = "OpenTelemetry Collector Exporters" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_otlp-1.15.0-py3-none-any.whl", hash = "sha256:79f22748b6a54808a0448093dfa189c8490e729f67c134d4c992533d9393b33e"}, + {file = "opentelemetry_exporter_otlp-1.15.0.tar.gz", hash = "sha256:4f7c49751d9720e2e726e13b0bb958ccade4e29122c305d92c033da432c8d2c5"}, +] + +[package.dependencies] +opentelemetry-exporter-otlp-proto-grpc = "1.15.0" +opentelemetry-exporter-otlp-proto-http = "1.15.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.15.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.15.0-py3-none-any.whl", hash = "sha256:c2a5492ba7d140109968135d641d06ce3c5bd73c50665f787526065d57d7fd1d"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.15.0.tar.gz", hash = "sha256:844f2a4bb9bcda34e4eb6fe36765e5031aacb36dc60ed88c90fc246942ea26e7"}, +] + +[package.dependencies] +backoff = {version = ">=1.10.0,<3.0.0", markers = "python_version >= \"3.7\""} +googleapis-common-protos = ">=1.52,<2.0" +grpcio = ">=1.0.0,<2.0.0" +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-proto = "1.15.0" +opentelemetry-sdk = ">=1.12,<2.0" + +[package.extras] +test = ["pytest-grpc"] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.15.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.15.0-py3-none-any.whl", hash = "sha256:3ec2a02196c8a54bf5cbf7fe623a5238625638e83b6047a983bdf96e2bbb74c0"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.15.0.tar.gz", hash = "sha256:11b2c814249a49b22f6cca7a06b05701f561d577b747f3660dfd67b6eb9daf9c"}, +] + +[package.dependencies] +backoff = {version = ">=1.10.0,<3.0.0", markers = "python_version >= \"3.7\""} +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-proto = "1.15.0" +opentelemetry-sdk = ">=1.12,<2.0" +requests = ">=2.7,<3.0" + +[package.extras] +test = ["responses (==0.22.0)"] + +[[package]] +name = "opentelemetry-proto" +version = "1.15.0" +description = "OpenTelemetry Python Proto" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_proto-1.15.0-py3-none-any.whl", hash = "sha256:044b6d044b4d10530f250856f933442b8753a17f94ae37c207607f733fb9a844"}, + {file = "opentelemetry_proto-1.15.0.tar.gz", hash = "sha256:9c4008e40ac8cab359daac283fbe7002c5c29c77ea2674ad5626a249e64e0101"}, +] + +[package.dependencies] +protobuf = ">=3.19,<5.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.15.0" +description = "OpenTelemetry Python SDK" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_sdk-1.15.0-py3-none-any.whl", hash = "sha256:555c533e9837766119bbccc7a80458c9971d853a6f1da683a2246cd5e53b4645"}, + {file = "opentelemetry_sdk-1.15.0.tar.gz", hash = "sha256:98dbffcfeebcbff12c0c974292d6ea603180a145904cf838b1fe4d5c99078425"}, +] + +[package.dependencies] +opentelemetry-api = "1.15.0" +opentelemetry-semantic-conventions = "0.36b0" +setuptools = ">=16.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.36b0" +description = "OpenTelemetry Semantic Conventions" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_semantic_conventions-0.36b0-py3-none-any.whl", hash = "sha256:adc05635e87b9d3e007c9f530eed487fc3ef2177d02f82f674f28ebf9aff8243"}, + {file = "opentelemetry_semantic_conventions-0.36b0.tar.gz", hash = "sha256:829dc221795467d98b773c04096e29be038d77526dc8d6ac76f546fb6279bf01"}, +] + [[package]] name = "packaging" version = "20.9" @@ -844,6 +1124,38 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, + {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, + {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, + {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, + {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, + {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, + {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, + {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, + {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, + {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, + {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, + {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, + {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, + {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, + {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, + {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, + {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, + {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, + {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, +] + [[package]] name = "pyparsing" version = "3.0.9" @@ -954,7 +1266,7 @@ docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphin name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1024,8 +1336,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, @@ -1169,6 +1480,35 @@ files = [ dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] doc = ["Sphinx", "sphinx-rtd-theme"] +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sniffio" version = "1.3.0" @@ -1181,6 +1521,25 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "thrift" +version = "0.16.0" +description = "Python bindings for the Apache Thrift RPC system" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"}, +] + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + [[package]] name = "toml" version = "0.10.2" @@ -1319,7 +1678,7 @@ files = [ name = "urllib3" version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1468,6 +1827,91 @@ files = [ {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, ] +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + [[package]] name = "zipp" version = "3.16.2" @@ -1487,4 +1931,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "6e5b1d3c248dbe56e8bd2b78f7bead2874643e6a3ea4636341ae9267059a9a57" +content-hash = "3da81d4c130579cba382a51a7bd09f11a61e84e90d9892b6ff405f3d6a123d6a" diff --git a/pyproject.toml b/pyproject.toml index ad74494b7..bcb7ea114 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,10 @@ prompt-toolkit = "^3.0,<3.0.29" "ruamel.yaml" = ">=0.16.5,<0.18.0" websockets = ">=10.0,<12.0" pluggy = "^1.0.0" +opentelemetry-api = "~1.15.0" +opentelemetry-sdk = "~1.15.0" +opentelemetry-exporter-jaeger = "~1.15.0" +opentelemetry-exporter-otlp = "~1.15.0" [tool.poetry.dev-dependencies] pytest-cov = "^4.1.0" diff --git a/rasa_sdk/__main__.py b/rasa_sdk/__main__.py index f4520dc40..05b1805cb 100644 --- a/rasa_sdk/__main__.py +++ b/rasa_sdk/__main__.py @@ -3,6 +3,7 @@ from rasa_sdk import utils from rasa_sdk.endpoint import create_argument_parser, run from rasa_sdk.constants import APPLICATION_ROOT_LOGGER_NAME +from rasa_sdk.tracing.utils import get_tracer_provider def main_from_args(args): @@ -17,6 +18,7 @@ def main_from_args(args): args.logging_config_file, ) utils.update_sanic_log_level() + tracer_provider = get_tracer_provider(args) run( args.actions, @@ -26,6 +28,7 @@ def main_from_args(args): args.ssl_keyfile, args.ssl_password, args.auto_reload, + tracer_provider, ) diff --git a/rasa_sdk/endpoint.py b/rasa_sdk/endpoint.py index ca13654a7..4c2a038f0 100644 --- a/rasa_sdk/endpoint.py +++ b/rasa_sdk/endpoint.py @@ -4,6 +4,7 @@ import types import zlib import json +from opentelemetry.sdk.trace import TracerProvider from typing import List, Text, Union, Optional from ssl import SSLContext @@ -18,6 +19,7 @@ from rasa_sdk.executor import ActionExecutor from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException from rasa_sdk.plugin import plugin_manager +from rasa_sdk.tracing.utils import get_tracer_and_context, set_span_attributes logger = logging.getLogger(__name__) @@ -65,6 +67,7 @@ def create_app( action_package_name: Union[Text, types.ModuleType], cors_origins: Union[Text, List[Text], None] = "*", auto_reload: bool = False, + tracer_provider: Optional[TracerProvider] = None, ) -> Sanic: """Create a Sanic application and return it. @@ -73,6 +76,7 @@ def create_app( from. cors_origins: CORS origins to allow. auto_reload: When `True`, auto-reloading of actions is enabled. + tracer_provider: Tracer provider to use for tracing. Returns: A new Sanic application ready to be run. @@ -93,34 +97,38 @@ async def health(_) -> HTTPResponse: @app.post("/webhook") async def webhook(request: Request) -> HTTPResponse: """Webhook to retrieve action calls.""" - if request.headers.get("Content-Encoding") == "deflate": - # Decompress the request data using zlib - decompressed_data = zlib.decompress(request.body) - # Load the JSON data from the decompressed request data - action_call = json.loads(decompressed_data) - else: - action_call = request.json - if action_call is None: - body = {"error": "Invalid body request"} - return response.json(body, status=400) - - utils.check_version_compatibility(action_call.get("version")) - - if auto_reload: - executor.reload() - - try: - result = await executor.run(action_call) - except ActionExecutionRejection as e: - logger.debug(e) - body = {"error": e.message, "action_name": e.action_name} - return response.json(body, status=400) - except ActionNotFoundException as e: - logger.error(e) - body = {"error": e.message, "action_name": e.action_name} - return response.json(body, status=404) - - return response.json(result, status=200) + tracer, context, span_name = get_tracer_and_context(tracer_provider, request) + + with tracer.start_as_current_span(span_name, context=context) as span: + if request.headers.get("Content-Encoding") == "deflate": + # Decompress the request data using zlib + decompressed_data = zlib.decompress(request.body) + # Load the JSON data from the decompressed request data + action_call = json.loads(decompressed_data) + else: + action_call = request.json + if action_call is None: + body = {"error": "Invalid body request"} + return response.json(body, status=400) + + utils.check_version_compatibility(action_call.get("version")) + + if auto_reload: + executor.reload() + try: + result = await executor.run(action_call) + except ActionExecutionRejection as e: + logger.debug(e) + body = {"error": e.message, "action_name": e.action_name} + return response.json(body, status=400) + except ActionNotFoundException as e: + logger.error(e) + body = {"error": e.message, "action_name": e.action_name} + return response.json(body, status=404) + + set_span_attributes(span, action_call) + + return response.json(result, status=200) @app.get("/actions") async def actions(_) -> HTTPResponse: @@ -151,11 +159,15 @@ def run( ssl_keyfile: Optional[Text] = None, ssl_password: Optional[Text] = None, auto_reload: bool = False, + tracer_provider: Optional[TracerProvider] = None, ) -> None: """Starts the action endpoint server with given config values.""" logger.info("Starting action endpoint server...") app = create_app( - action_package_name, cors_origins=cors_origins, auto_reload=auto_reload + action_package_name, + cors_origins=cors_origins, + auto_reload=auto_reload, + tracer_provider=tracer_provider, ) ## Attach additional sanic extensions: listeners, middleware and routing logger.info("Starting plugins...") diff --git a/rasa_sdk/tracing/config.py b/rasa_sdk/tracing/config.py new file mode 100644 index 000000000..2f7e304b5 --- /dev/null +++ b/rasa_sdk/tracing/config.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import abc +import logging +import os +from typing import Any, Dict, Optional, Text + +import grpc +from opentelemetry.exporter.jaeger.thrift import JaegerExporter +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from rasa_sdk.tracing.endpoints import EndpointConfig, read_endpoint_config + + +TRACING_SERVICE_NAME = os.environ.get("TRACING_SERVICE_NAME", "rasa_sdk") + +ENDPOINTS_TRACING_KEY = "tracing" + +logger = logging.getLogger(__name__) + + +def get_tracer_provider(endpoints_file: Text) -> Optional[TracerProvider]: + """Configure tracing backend. + + When a known tracing backend is defined in the endpoints file, this + function will configure the tracing infrastructure. When no or an unknown + tracing backend is defined, this function does nothing. + + :param endpoints_file: The configuration file containing information about the + tracing backend. + :return: The `TracingProvider` to be used for all subsequent tracing. + """ + cfg = read_endpoint_config(endpoints_file, ENDPOINTS_TRACING_KEY) + + if not cfg: + logger.info( + f"No endpoint for tracing type available in {endpoints_file}," + f"tracing will not be configured." + ) + return None + if cfg.type == "jaeger": + tracer_provider = JaegerTracerConfigurer.configure_from_endpoint_config(cfg) + elif cfg.type == "otlp": + tracer_provider = OTLPCollectorConfigurer.configure_from_endpoint_config(cfg) + else: + logger.warning( + f"Unknown tracing type {cfg.type} read from {endpoints_file}, ignoring." + ) + return None + + return tracer_provider + + +class TracerConfigurer(abc.ABC): + """Abstract superclass for tracing configuration. + + `TracerConfigurer` is the abstract superclass from which all configurers + for different supported backends should inherit. + """ + + @classmethod + @abc.abstractmethod + def configure_from_endpoint_config(cls, cfg: EndpointConfig) -> TracerProvider: + """Configure tracing. + + This abstract method should be implemented by all concrete `TracerConfigurer`s. + It shall read the configuration from the supplied argument, configure all + necessary infrastructure for tracing, and return the `TracerProvider` to be + used for tracing purposes. + + :param cfg: The configuration to be read for configuring tracing. + :return: The configured `TracerProvider`. + """ + + +class JaegerTracerConfigurer(TracerConfigurer): + """The `TracerConfigurer` for a Jaeger backend.""" + + @classmethod + def configure_from_endpoint_config(cls, cfg: EndpointConfig) -> TracerProvider: + """Configure tracing for Jaeger. + + This will read the Jaeger-specific configuration from the `EndpointConfig` and + create a corresponding `TracerProvider` that exports to the given Jaeger + backend. + + :param cfg: The configuration to be read for configuring tracing. + :return: The configured `TracerProvider`. + """ + provider = TracerProvider( + resource=Resource.create( + {SERVICE_NAME: cfg.kwargs.get("service_name", TRACING_SERVICE_NAME)} + ) + ) + + jaeger_exporter = JaegerExporter( + **cls._extract_config(cfg), udp_split_oversized_batches=True + ) + logger.info( + f"Registered {cfg.type} endpoint for tracing. Traces will be exported to" + f" {jaeger_exporter.agent_host_name}:{jaeger_exporter.agent_port}" + ) + provider.add_span_processor(BatchSpanProcessor(jaeger_exporter)) + + return provider + + @classmethod + def _extract_config(cls, cfg: EndpointConfig) -> Dict[str, Any]: + return { + "agent_host_name": (cfg.kwargs.get("host", "localhost")), + "agent_port": (cfg.kwargs.get("port", 6831)), + "username": cfg.kwargs.get("username"), + "password": cfg.kwargs.get("password"), + } + + +class OTLPCollectorConfigurer(TracerConfigurer): + """The `TracerConfigurer` for an OTLP collector backend.""" + + @classmethod + def configure_from_endpoint_config(cls, cfg: EndpointConfig) -> TracerProvider: + """Configure tracing for Jaeger. + + This will read the OTLP collector-specific configuration from the + `EndpointConfig` and create a corresponding `TracerProvider` that exports to + the given OTLP collector. + Currently, this only supports insecure connections via gRPC. + + :param cfg: The configuration to be read for configuring tracing. + :return: The configured `TracerProvider`. + """ + provider = TracerProvider( + resource=Resource.create( + {SERVICE_NAME: cfg.kwargs.get("service_name", TRACING_SERVICE_NAME)} + ) + ) + + insecure = cfg.kwargs.get("insecure") + + credentials = cls._get_credentials(cfg, insecure) # type: ignore + + otlp_exporter = OTLPSpanExporter( + endpoint=cfg.kwargs["endpoint"], + insecure=insecure, + credentials=credentials, + ) + logger.info( + f"Registered {cfg.type} endpoint for tracing." + f"Traces will be exported to {cfg.kwargs['endpoint']}" + ) + provider.add_span_processor(BatchSpanProcessor(otlp_exporter)) + + return provider + + @classmethod + def _get_credentials( + cls, cfg: EndpointConfig, insecure: bool + ) -> Optional[grpc.ChannelCredentials]: + credentials = None + if not insecure and "root_certificates" in cfg.kwargs: + with open(cfg.kwargs.get("root_certificates"), "rb") as f: # type: ignore + root_cert = f.read() + credentials = grpc.ssl_channel_credentials(root_certificates=root_cert) + return credentials diff --git a/rasa_sdk/tracing/endpoints.py b/rasa_sdk/tracing/endpoints.py new file mode 100644 index 000000000..a5520a70d --- /dev/null +++ b/rasa_sdk/tracing/endpoints.py @@ -0,0 +1,64 @@ +import logging + +import os +from typing import Any, Dict, Optional, Text +import rasa_sdk.utils + + +logger = logging.getLogger(__name__) +DEFAULT_ENCODING = "utf-8" + + +def read_endpoint_config( + filename: Text, endpoint_type: Text +) -> Optional["EndpointConfig"]: + """Read an endpoint configuration file from disk and extract one + + config.""" + if not filename: + return None + + try: + content = rasa_sdk.utils.read_file(filename) + content = rasa_sdk.utils.read_yaml(content) + + if content.get(endpoint_type) is None: + return None + + return EndpointConfig.from_dict(content[endpoint_type]) + except FileNotFoundError: + logger.error( + "Failed to read endpoint configuration " + "from {}. No such file.".format(os.path.abspath(filename)) + ) + return None + + +class EndpointConfig: + """Configuration for an external HTTP endpoint.""" + + def __init__( + self, + url: Optional[Text] = None, + params: Optional[Dict[Text, Any]] = None, + headers: Optional[Dict[Text, Any]] = None, + basic_auth: Optional[Dict[Text, Text]] = None, + token: Optional[Text] = None, + token_name: Text = "token", + cafile: Optional[Text] = None, + **kwargs: Any, + ) -> None: + """Creates an `EndpointConfig` instance.""" + self.url = url + self.params = params or {} + self.headers = headers or {} + self.basic_auth = basic_auth or {} + self.token = token + self.token_name = token_name + self.type = kwargs.pop("store_type", kwargs.pop("type", None)) + self.cafile = cafile + self.kwargs = kwargs + + @classmethod + def from_dict(cls, data: Dict[Text, Any]) -> "EndpointConfig": + return EndpointConfig(**data) diff --git a/rasa_sdk/tracing/utils.py b/rasa_sdk/tracing/utils.py new file mode 100644 index 000000000..cb5d22450 --- /dev/null +++ b/rasa_sdk/tracing/utils.py @@ -0,0 +1,56 @@ +import argparse +from rasa_sdk.tracing import config +from opentelemetry import trace +from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator + +from opentelemetry.sdk.trace import TracerProvider +from sanic.request import Request + +from typing import Optional, Tuple, Any, Text + + +def get_tracer_provider( + cmdline_arguments: argparse.Namespace, +) -> Optional[TracerProvider]: + """Gets the tracer provider from the command line arguments.""" + tracer_provider = None + endpoints_file = "" + if "endpoints" in cmdline_arguments: + endpoints_file = cmdline_arguments.endpoints + + if endpoints_file is not None: + tracer_provider = config.get_tracer_provider(endpoints_file) + return tracer_provider + + +def get_tracer_and_context( + tracer_provider: Optional[TracerProvider], request: Request +) -> Tuple[Any, Any, Text]: + """Gets tracer and context""" + span_name = "rasa_sdk.create_app.webhook" + if tracer_provider is None: + tracer = trace.get_tracer(span_name) + context = None + else: + tracer = tracer_provider.get_tracer(span_name) + context = TraceContextTextMapPropagator().extract(request.headers) + return (tracer, context, span_name) + + +def set_span_attributes(span: Any, action_call: dict) -> None: + """Sets span attributes""" + tracker = action_call.get("tracker", {}) + set_span_attributes = { + "http.method": "POST", + "http.route": "/webhook", + "next_action": action_call.get("next_action"), + "version": action_call.get("version"), + "sender_id": tracker.get("sender_id"), + "message_id": tracker.get("latest_message", {}).get("message_id"), + } + + if span.is_recording(): + for key, value in set_span_attributes.items(): + span.set_attribute(key, value) + + return None diff --git a/rasa_sdk/utils.py b/rasa_sdk/utils.py index 82c59f8bf..37a66ae27 100644 --- a/rasa_sdk/utils.py +++ b/rasa_sdk/utils.py @@ -12,6 +12,7 @@ from typing import AbstractSet, Any, Dict, List, Text, Optional, Coroutine, Union import rasa_sdk + from rasa_sdk.constants import ( DEFAULT_ENCODING, DEFAULT_SANIC_WORKERS, diff --git a/tests/tracing/conftest.py b/tests/tracing/conftest.py new file mode 100644 index 000000000..ff6547da7 --- /dev/null +++ b/tests/tracing/conftest.py @@ -0,0 +1,74 @@ +import pathlib +import socket +from concurrent import futures +from typing import Generator, Optional + +import grpc +import opentelemetry.proto.collector.trace.v1.trace_service_pb2_grpc as trace_service +import pytest +from google.protobuf.internal.containers import RepeatedCompositeFieldContainer +from opentelemetry.exporter.jaeger.thrift.gen.agent.Agent import emitBatch_args +from opentelemetry.exporter.jaeger.thrift.gen.jaeger.ttypes import Batch +from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ( + ExportTraceServiceRequest, + ExportTraceServiceResponse, +) +from opentelemetry.proto.trace.v1.trace_pb2 import ResourceSpans +from thrift.protocol.TCompactProtocol import TCompactProtocol +from thrift.transport.TTransport import TMemoryBuffer + + +TRACING_TESTS_FIXTURES_DIRECTORY = pathlib.Path(__file__).parent / "fixtures" + + +@pytest.fixture +def udp_server() -> Generator[socket.socket, None, None]: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind(("localhost", 6832)) + yield sock + sock.close() + + +class CapturingTestSpanExporter(trace_service.TraceServiceServicer): + def __init__(self) -> None: + self.spans: Optional[RepeatedCompositeFieldContainer[ResourceSpans]] = None + + def Export( + self, request: ExportTraceServiceRequest, context: grpc.ServicerContext + ) -> ExportTraceServiceResponse: + self.spans = request.resource_spans + + return ExportTraceServiceResponse() + + +@pytest.fixture +def span_exporter() -> CapturingTestSpanExporter: + return CapturingTestSpanExporter() + + +@pytest.fixture +def grpc_server( + span_exporter: CapturingTestSpanExporter, +) -> Generator[grpc.Server, None, None]: + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + trace_service.add_TraceServiceServicer_to_server( # type: ignore + span_exporter, server + ) + + server.add_insecure_port("[::]:4317") + + server.start() + yield server + server.stop(None) + + +def deserialize_jaeger_batch(data: bytearray) -> Batch: + trans = TMemoryBuffer(data) + prot = TCompactProtocol(trans) + prot.readMessageBegin() + emitBatch = emitBatch_args() # type: ignore + emitBatch.read(prot) # type: ignore + prot.readMessageEnd() + + return emitBatch.batch diff --git a/tests/tracing/fixtures/jaeger_endpoints.yml b/tests/tracing/fixtures/jaeger_endpoints.yml new file mode 100644 index 000000000..93811949e --- /dev/null +++ b/tests/tracing/fixtures/jaeger_endpoints.yml @@ -0,0 +1,4 @@ +tracing: + type: jaeger + host: localhost + port: 6832 diff --git a/tests/tracing/fixtures/no_tracing.yml b/tests/tracing/fixtures/no_tracing.yml new file mode 100644 index 000000000..76cbfe7aa --- /dev/null +++ b/tests/tracing/fixtures/no_tracing.yml @@ -0,0 +1,2 @@ +random config: + a: 1 diff --git a/tests/tracing/fixtures/otlp_endpoints.yml b/tests/tracing/fixtures/otlp_endpoints.yml new file mode 100644 index 000000000..91df61402 --- /dev/null +++ b/tests/tracing/fixtures/otlp_endpoints.yml @@ -0,0 +1,4 @@ +tracing: + type: otlp + endpoint: localhost:4317 + insecure: true diff --git a/tests/tracing/instrumentation/conftest.py b/tests/tracing/instrumentation/conftest.py new file mode 100644 index 000000000..df3a7a166 --- /dev/null +++ b/tests/tracing/instrumentation/conftest.py @@ -0,0 +1,23 @@ +import pytest + +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter + + +@pytest.fixture(scope="session") +def tracer_provider() -> TracerProvider: + return TracerProvider() + + +@pytest.fixture(scope="session") +def span_exporter(tracer_provider: TracerProvider) -> InMemorySpanExporter: + exporter = InMemorySpanExporter() # type: ignore + tracer_provider.add_span_processor(SimpleSpanProcessor(exporter)) + return exporter + + +@pytest.fixture(scope="function") +def previous_num_captured_spans(span_exporter: InMemorySpanExporter) -> int: + captured_spans = span_exporter.get_finished_spans() # type: ignore + return len(captured_spans) diff --git a/tests/tracing/instrumentation/test_tracing.py b/tests/tracing/instrumentation/test_tracing.py new file mode 100644 index 000000000..8d47c8379 --- /dev/null +++ b/tests/tracing/instrumentation/test_tracing.py @@ -0,0 +1,75 @@ +import json + +import rasa_sdk.endpoint as ep + +from typing import Sequence +from opentelemetry.sdk.trace import TracerProvider + +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter + + +def test_server_webhook_custom_action_is_instrumented( + tracer_provider: TracerProvider, + span_exporter: InMemorySpanExporter, + previous_num_captured_spans: int, +) -> None: + """Tests that the custom action is instrumented.""" + data = { + "next_action": "custom_action", + "version": "1.0.0", + "tracker": { + "sender_id": "1", + "conversation_id": "default", + "latest_message": {"message_id": "1"}, + }, + } + app = ep.create_app(None, tracer_provider=tracer_provider) + _, response = app.test_client.post("/webhook", data=json.dumps(data)) + + assert response.status == 200 + + captured_spans: Sequence[ + ReadableSpan + ] = span_exporter.get_finished_spans() # type: ignore + + num_captured_spans = len(captured_spans) - previous_num_captured_spans + assert num_captured_spans == 1 + + captured_span = captured_spans[-1] + + assert captured_span.attributes == { + "http.method": "POST", + "http.route": "/webhook", + "next_action": data["next_action"], + "version": data["version"], + "sender_id": data["tracker"]["sender_id"], + "message_id": data["tracker"]["latest_message"]["message_id"], + } + + +def test_server_webhook_custom_action_is_not_instrumented( + span_exporter: InMemorySpanExporter, + previous_num_captured_spans: int, +) -> None: + """Tests that the server is not instrumented if no tracer provider is provided.""" + data = { + "next_action": "custom_action", + "version": "1.0.0", + "tracker": { + "sender_id": "1", + "conversation_id": "default", + "latest_message": {"message_id": "1"}, + }, + } + app = ep.create_app(None) + _, response = app.test_client.post("/webhook", data=json.dumps(data)) + + assert response.status == 200 + + captured_spans: Sequence[ + ReadableSpan + ] = span_exporter.get_finished_spans() # type: ignore + + num_captured_spans = len(captured_spans) - previous_num_captured_spans + assert num_captured_spans == 0 diff --git a/tests/tracing/test_config.py b/tests/tracing/test_config.py new file mode 100644 index 000000000..5f5a6d207 --- /dev/null +++ b/tests/tracing/test_config.py @@ -0,0 +1,63 @@ +import socket + +from rasa_sdk.tracing.endpoints import EndpointConfig + +from rasa_sdk.tracing import config +from rasa_sdk.tracing.config import JaegerTracerConfigurer +from tests.tracing import conftest +from tests.tracing.conftest import ( + TRACING_TESTS_FIXTURES_DIRECTORY, +) + +UDP_BUFFER_SIZE = 2048 + + +def test_jaeger_config_correctly_extracted() -> None: + """Tests that the Jaeger config is correctly extracted from the endpoint config.""" + cfg = EndpointConfig( + host="hostname", + port=1234, + username="user", + password="password", + ) + + extracted = JaegerTracerConfigurer._extract_config(cfg) + + assert extracted["agent_host_name"] == cfg.kwargs["host"] + assert extracted["agent_port"] == cfg.kwargs["port"] + assert extracted["username"] == cfg.kwargs["username"] + assert extracted["password"] == cfg.kwargs["password"] + + +def test_jaeger_config_sets_defaults() -> None: + """Tests that the Jaeger config sets default config.""" + extracted = JaegerTracerConfigurer._extract_config(EndpointConfig()) + + assert extracted["agent_host_name"] == "localhost" + assert extracted["agent_port"] == 6831 + assert extracted["username"] is None + assert extracted["password"] is None + + +def test_get_tracer_provider_jaeger(udp_server: socket.socket) -> None: + """Tests that the tracer provider is correctly configured for Jaeger.""" + endpoints_file = str(TRACING_TESTS_FIXTURES_DIRECTORY / "jaeger_endpoints.yml") + + tracer_provider = config.get_tracer_provider(endpoints_file) + assert tracer_provider is not None + + tracer = tracer_provider.get_tracer(__name__) + + with tracer.start_as_current_span("jaeger_test_span"): + pass + + tracer_provider.force_flush() + + message, _ = udp_server.recvfrom(UDP_BUFFER_SIZE) + + batch = conftest.deserialize_jaeger_batch(bytearray(message)) + + assert batch.process.serviceName == "rasa_sdk" + + assert len(batch.spans) == 1 + assert batch.spans[0].operationName == "jaeger_test_span" diff --git a/tests/tracing/test_utils.py b/tests/tracing/test_utils.py new file mode 100644 index 000000000..a49ab7dd3 --- /dev/null +++ b/tests/tracing/test_utils.py @@ -0,0 +1,77 @@ +import json +import argparse + +import rasa_sdk.endpoint as ep + +from opentelemetry.trace import ProxyTracer +from opentelemetry.sdk.trace import TracerProvider + +from rasa_sdk.tracing.utils import ( + get_tracer_provider, + get_tracer_and_context, +) + +from tests.tracing.conftest import ( + TRACING_TESTS_FIXTURES_DIRECTORY, +) + + +def test_get_tracer_provider_returns_none_if_no_endpoints_file() -> None: + """Tests that get_tracer_provider returns None if no endpoints file is provided.""" + parser = argparse.ArgumentParser() + parser.add_argument("--random_args", type=str, default=None) + + args = parser.parse_args(["--random_args", "random text"]) + + tracer_provider = get_tracer_provider(args) + + assert tracer_provider is None + + +def test_get_tracer_provider_returns_none_if_tracing_is_not_configured() -> None: + """Tests that get_tracer_provider returns None if tracing is not configured.""" + parser = argparse.ArgumentParser() + parser.add_argument("--endpoints", type=str, default=None) + + endpoints_file = str(TRACING_TESTS_FIXTURES_DIRECTORY / "no_tracing.yml") + args = parser.parse_args(["--endpoints", endpoints_file]) + + tracer_provider = get_tracer_provider(args) + + assert tracer_provider is None + + +def test_get_tracer_provider_returns_provider() -> None: + """Tests that get_tracer_provider returns a TracerProvider + if tracing is configured.""" + parser = argparse.ArgumentParser() + parser.add_argument("--endpoints", type=str, default=None) + + endpoints_file = str(TRACING_TESTS_FIXTURES_DIRECTORY / "jaeger_endpoints.yml") + args = parser.parse_args(["--endpoints", endpoints_file]) + + tracer_provider = get_tracer_provider(args) + + assert tracer_provider is not None + assert isinstance(tracer_provider, TracerProvider) + + +def test_get_tracer_and_context() -> None: + """Tests that get_tracer_and_context returns a ProxyTracer and span name""" + data = { + "next_action": "custom_action", + "version": "1.0.0", + "tracker": { + "sender_id": "1", + "conversation_id": "default", + "latest_message": {"message_id": "1"}, + }, + } + app = ep.create_app(None) + request, _ = app.test_client.post("/webhook", data=json.dumps(data)) + tracer, context, span_name = get_tracer_and_context(None, request) + print(type(tracer)) + + assert isinstance(tracer, ProxyTracer) + assert span_name == "rasa_sdk.create_app.webhook" + assert context is None