diff --git a/docs-requirements.txt b/docs-requirements.txt
index 17d71b91aff..10ccf1b21c3 100644
--- a/docs-requirements.txt
+++ b/docs-requirements.txt
@@ -26,3 +26,4 @@ boto~=2.0
google-cloud-trace >=0.23.0
google-cloud-monitoring>=0.36.0
botocore~=1.0
+starlette~=0.13
diff --git a/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/version.py b/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/version.py
index 603bf0b7e5f..6d4fefa599e 100644
--- a/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/version.py
+++ b/docs/examples/opentelemetry-example-app/src/opentelemetry_example_app/version.py
@@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-__version__ = "0.9.dev0"
+__version__ = "0.10.dev0"
diff --git a/docs/ext/starlette/starlette.rst b/docs/ext/starlette/starlette.rst
new file mode 100644
index 00000000000..8e2d1d7bc83
--- /dev/null
+++ b/docs/ext/starlette/starlette.rst
@@ -0,0 +1,9 @@
+.. include:: ../../../ext/opentelemetry-instrumentation-starlette/README.rst
+
+API
+---
+
+.. automodule:: opentelemetry.instrumentation.starlette
+ :members:
+ :undoc-members:
+ :show-inheritance:
\ No newline at end of file
diff --git a/ext/opentelemetry-ext-asgi/setup.cfg b/ext/opentelemetry-ext-asgi/setup.cfg
index 8ccd7bd5e47..ab0e3e7f47c 100644
--- a/ext/opentelemetry-ext-asgi/setup.cfg
+++ b/ext/opentelemetry-ext-asgi/setup.cfg
@@ -40,6 +40,7 @@ package_dir=
packages=find_namespace:
install_requires =
opentelemetry-api == 0.10.dev0
+ opentelemetry-instrumentation == 0.10.dev0
asgiref ~= 3.0
[options.extras_require]
diff --git a/ext/opentelemetry-ext-asgi/src/opentelemetry/ext/asgi/__init__.py b/ext/opentelemetry-ext-asgi/src/opentelemetry/ext/asgi/__init__.py
index 69c30848da3..43b8804c245 100644
--- a/ext/opentelemetry-ext-asgi/src/opentelemetry/ext/asgi/__init__.py
+++ b/ext/opentelemetry-ext-asgi/src/opentelemetry/ext/asgi/__init__.py
@@ -22,11 +22,13 @@
import typing
import urllib
from functools import wraps
+from typing import Tuple
from asgiref.compatibility import guarantee_single_callable
from opentelemetry import context, propagators, trace
from opentelemetry.ext.asgi.version import __version__ # noqa
+from opentelemetry.instrumentation.utils import http_status_to_canonical_code
from opentelemetry.trace.status import Status, StatusCanonicalCode
@@ -44,37 +46,6 @@ def get_header_from_scope(scope: dict, header_name: str) -> typing.List[str]:
]
-def http_status_to_canonical_code(code: int, allow_redirect: bool = True):
- # pylint:disable=too-many-branches,too-many-return-statements
- if code < 100:
- return StatusCanonicalCode.UNKNOWN
- if code <= 299:
- return StatusCanonicalCode.OK
- if code <= 399:
- if allow_redirect:
- return StatusCanonicalCode.OK
- return StatusCanonicalCode.DEADLINE_EXCEEDED
- if code <= 499:
- if code == 401: # HTTPStatus.UNAUTHORIZED:
- return StatusCanonicalCode.UNAUTHENTICATED
- if code == 403: # HTTPStatus.FORBIDDEN:
- return StatusCanonicalCode.PERMISSION_DENIED
- if code == 404: # HTTPStatus.NOT_FOUND:
- return StatusCanonicalCode.NOT_FOUND
- if code == 429: # HTTPStatus.TOO_MANY_REQUESTS:
- return StatusCanonicalCode.RESOURCE_EXHAUSTED
- return StatusCanonicalCode.INVALID_ARGUMENT
- if code <= 599:
- if code == 501: # HTTPStatus.NOT_IMPLEMENTED:
- return StatusCanonicalCode.UNIMPLEMENTED
- if code == 503: # HTTPStatus.SERVICE_UNAVAILABLE:
- return StatusCanonicalCode.UNAVAILABLE
- if code == 504: # HTTPStatus.GATEWAY_TIMEOUT:
- return StatusCanonicalCode.DEADLINE_EXCEEDED
- return StatusCanonicalCode.INTERNAL
- return StatusCanonicalCode.UNKNOWN
-
-
def collect_request_attributes(scope):
"""Collects HTTP request attributes from the ASGI scope and returns a
dictionary to be used as span creation attributes."""
@@ -134,11 +105,19 @@ def set_status_code(span, status_code):
span.set_status(Status(http_status_to_canonical_code(status_code)))
-def get_default_span_name(scope):
- """Default implementation for name_callback"""
+def get_default_span_details(scope: dict) -> Tuple[str, dict]:
+ """Default implementation for span_details_callback
+
+ Args:
+ scope: the asgi scope dictionary
+
+ Returns:
+ a tuple of the span, and any attributes to attach to the
+ span.
+ """
method_or_path = scope.get("method") or scope.get("path")
- return method_or_path
+ return method_or_path, {}
class OpenTelemetryMiddleware:
@@ -149,15 +128,18 @@ class OpenTelemetryMiddleware:
Args:
app: The ASGI application callable to forward requests to.
- name_callback: Callback which calculates a generic span name for an
- incoming HTTP request based on the ASGI scope.
- Optional: Defaults to get_default_span_name.
+ span_details_callback: Callback which should return a string
+ and a tuple, representing the desired span name and a
+ dictionary with any additional span attributes to set.
+ Optional: Defaults to get_default_span_details.
"""
- def __init__(self, app, name_callback=None):
+ def __init__(self, app, span_details_callback=None):
self.app = guarantee_single_callable(app)
self.tracer = trace.get_tracer(__name__, __version__)
- self.name_callback = name_callback or get_default_span_name
+ self.span_details_callback = (
+ span_details_callback or get_default_span_details
+ )
async def __call__(self, scope, receive, send):
"""The ASGI application
@@ -173,13 +155,15 @@ async def __call__(self, scope, receive, send):
token = context.attach(
propagators.extract(get_header_from_scope, scope)
)
- span_name = self.name_callback(scope)
+ span_name, additional_attributes = self.span_details_callback(scope)
+ attributes = collect_request_attributes(scope)
+ attributes.update(additional_attributes)
try:
with self.tracer.start_as_current_span(
span_name + " asgi",
kind=trace.SpanKind.SERVER,
- attributes=collect_request_attributes(scope),
+ attributes=attributes,
):
@wraps(receive)
diff --git a/ext/opentelemetry-ext-asgi/tests/test_asgi_middleware.py b/ext/opentelemetry-ext-asgi/tests/test_asgi_middleware.py
index edc90444a7b..05aa84b3c41 100644
--- a/ext/opentelemetry-ext-asgi/tests/test_asgi_middleware.py
+++ b/ext/opentelemetry-ext-asgi/tests/test_asgi_middleware.py
@@ -176,9 +176,8 @@ def test_override_span_name(self):
"""Test that span_names can be overwritten by our callback function."""
span_name = "Dymaxion"
- # pylint:disable=unused-argument
- def get_predefined_span_name(scope):
- return span_name
+ def get_predefined_span_details(_):
+ return span_name, {}
def update_expected_span_name(expected):
for entry in expected:
@@ -188,7 +187,7 @@ def update_expected_span_name(expected):
return expected
app = otel_asgi.OpenTelemetryMiddleware(
- simple_asgi, name_callback=get_predefined_span_name
+ simple_asgi, span_details_callback=get_predefined_span_details
)
self.seed_app(app)
self.send_default_request()
diff --git a/ext/opentelemetry-ext-boto/tests/test_boto_instrumentation.py b/ext/opentelemetry-ext-boto/tests/test_boto_instrumentation.py
index 492fac5a883..a629b108705 100644
--- a/ext/opentelemetry-ext-boto/tests/test_boto_instrumentation.py
+++ b/ext/opentelemetry-ext-boto/tests/test_boto_instrumentation.py
@@ -19,13 +19,13 @@
import boto.elasticache
import boto.s3
import boto.sts
-
from moto import ( # pylint: disable=import-error
mock_ec2_deprecated,
mock_lambda_deprecated,
mock_s3_deprecated,
mock_sts_deprecated,
)
+
from opentelemetry.ext.boto import BotoInstrumentor
from opentelemetry.test.test_base import TestBase
diff --git a/ext/opentelemetry-ext-botocore/tests/test_botocore_instrumentation.py b/ext/opentelemetry-ext-botocore/tests/test_botocore_instrumentation.py
index 56b136ea290..64d0c3d7b7c 100644
--- a/ext/opentelemetry-ext-botocore/tests/test_botocore_instrumentation.py
+++ b/ext/opentelemetry-ext-botocore/tests/test_botocore_instrumentation.py
@@ -1,6 +1,5 @@
import botocore.session
from botocore.exceptions import ParamValidationError
-
from moto import ( # pylint: disable=import-error
mock_ec2,
mock_kinesis,
@@ -9,6 +8,7 @@
mock_s3,
mock_sqs,
)
+
from opentelemetry.ext.botocore import BotocoreInstrumentor
from opentelemetry.test.test_base import TestBase
diff --git a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
index 14e27dea2a4..e267910a9d7 100644
--- a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
+++ b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
@@ -33,6 +33,7 @@
DEFAULT_AGENT_URL = "http://localhost:8126"
_INSTRUMENTATION_SPAN_TYPES = {
"opentelemetry.ext.aiohttp-client": DatadogSpanTypes.HTTP,
+ "opentelemetry.ext.asgi": DatadogSpanTypes.WEB,
"opentelemetry.ext.dbapi": DatadogSpanTypes.SQL,
"opentelemetry.ext.django": DatadogSpanTypes.WEB,
"opentelemetry.ext.flask": DatadogSpanTypes.WEB,
diff --git a/ext/opentelemetry-instrumentation-starlette/CHANGELOG.md b/ext/opentelemetry-instrumentation-starlette/CHANGELOG.md
new file mode 100644
index 00000000000..f7132ca8306
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## Unreleased
+
+- Initial release ([#777](https://github.com/open-telemetry/opentelemetry-python/pull/777))
\ No newline at end of file
diff --git a/ext/opentelemetry-instrumentation-starlette/README.rst b/ext/opentelemetry-instrumentation-starlette/README.rst
new file mode 100644
index 00000000000..1d05c0b717f
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/README.rst
@@ -0,0 +1,45 @@
+OpenTelemetry Starlette Instrumentation
+=======================================
+
+|pypi|
+
+.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-starlette.svg
+ :target: https://pypi.org/project/opentelemetry-instrumentation-starlette/
+
+
+This library provides automatic and manual instrumentation of Starlette web frameworks,
+instrumenting http requests served by applications utilizing the framework.
+
+auto-instrumentation using the opentelemetry-instrumentation package is also supported.
+
+Installation
+------------
+
+::
+
+ pip install opentelemetry-instrumentation-starlette
+
+
+Usage
+-----
+
+.. code-block:: python
+
+ from opentelemetry.instrumentation.starlette import StarletteInstrumentor
+ from starlette import applications
+ from starlette.responses import PlainTextResponse
+ from starlette.routing import Route
+
+ def home(request):
+ return PlainTextResponse("hi")
+
+ app = applications.Starlette(
+ routes=[Route("/foobar", home)]
+ )
+ StarletteInstrumentor.instrument_app(app)
+
+
+References
+----------
+
+* `OpenTelemetry Project `_
diff --git a/ext/opentelemetry-instrumentation-starlette/setup.cfg b/ext/opentelemetry-instrumentation-starlette/setup.cfg
new file mode 100644
index 00000000000..7659c9fadb6
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/setup.cfg
@@ -0,0 +1,55 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[metadata]
+name = opentelemetry-instrumentation-starlette
+description = OpenTelemetry Starlette Instrumentation
+long_description = file: README.rst
+long_description_content_type = text/x-rst
+author = OpenTelemetry Authors
+author_email = cncf-opentelemetry-contributors@lists.cncf.io
+url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-instrumentation-starlette
+platforms = any
+license = Apache-2.0
+classifiers =
+ Development Status :: 4 - Beta
+ Intended Audience :: Developers
+ License :: OSI Approved :: Apache Software License
+ Programming Language :: Python
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3.6
+ Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
+
+[options]
+python_requires = >=3.6
+package_dir=
+ =src
+packages=find_namespace:
+install_requires =
+ opentelemetry-api == 0.10.dev0
+ opentelemetry-ext-asgi == 0.10.dev0
+
+[options.entry_points]
+opentelemetry_instrumentor =
+ starlette = opentelemetry.instrumentation.starlette:StarletteInstrumentor
+
+[options.extras_require]
+test =
+ opentelemetry-test == 0.10.dev0
+ starlette ~= 0.13.0
+ requests ~= 2.23.0 # needed for testclient
+
+[options.packages.find]
+where = src
diff --git a/ext/opentelemetry-instrumentation-starlette/setup.py b/ext/opentelemetry-instrumentation-starlette/setup.py
new file mode 100644
index 00000000000..0232a6f448c
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/setup.py
@@ -0,0 +1,31 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import os
+
+import setuptools
+
+BASE_DIR = os.path.dirname(__file__)
+VERSION_FILENAME = os.path.join(
+ BASE_DIR,
+ "src",
+ "opentelemetry",
+ "instrumentation",
+ "starlette",
+ "version.py",
+)
+PACKAGE_INFO = {}
+with open(VERSION_FILENAME) as f:
+ exec(f.read(), PACKAGE_INFO)
+
+setuptools.setup(version=PACKAGE_INFO["__version__"])
diff --git a/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py b/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py
new file mode 100644
index 00000000000..197a38d7591
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/__init__.py
@@ -0,0 +1,82 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from typing import Optional
+
+from starlette import applications
+from starlette.routing import Match
+
+from opentelemetry.ext.asgi import OpenTelemetryMiddleware
+from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
+from opentelemetry.instrumentation.starlette.version import __version__ # noqa
+
+
+class StarletteInstrumentor(BaseInstrumentor):
+ """An instrumentor for starlette
+
+ See `BaseInstrumentor`
+ """
+
+ _original_starlette = None
+
+ @staticmethod
+ def instrument_app(app: applications.Starlette):
+ """Instrument a previously instrumented Starlette application.
+ """
+ if not getattr(app, "is_instrumented_by_opentelemetry", False):
+ app.add_middleware(
+ OpenTelemetryMiddleware,
+ span_details_callback=_get_route_details,
+ )
+ app.is_instrumented_by_opentelemetry = True
+
+ def _instrument(self, **kwargs):
+ self._original_starlette = applications.Starlette
+ applications.Starlette = _InstrumentedStarlette
+
+ def _uninstrument(self, **kwargs):
+ applications.Starlette = self._original_starlette
+
+
+class _InstrumentedStarlette(applications.Starlette):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.add_middleware(
+ OpenTelemetryMiddleware, span_details_callback=_get_route_details
+ )
+
+
+def _get_route_details(scope):
+ """Callback to retrieve the starlette route being served.
+
+ TODO: there is currently no way to retrieve http.route from
+ a starlette application from scope.
+
+ See: https://github.com/encode/starlette/pull/804
+ """
+ app = scope["app"]
+ route = None
+ for starlette_route in app.routes:
+ match, _ = starlette_route.matches(scope)
+ if match == Match.FULL:
+ route = starlette_route.path
+ break
+ if match == Match.PARTIAL:
+ route = starlette_route.path
+ # method only exists for http, if websocket
+ # leave it blank.
+ span_name = route or scope.get("method", "")
+ attributes = {}
+ if route:
+ attributes["http.route"] = route
+ return span_name, attributes
diff --git a/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py b/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py
new file mode 100644
index 00000000000..6d4fefa599e
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/version.py
@@ -0,0 +1,15 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = "0.10.dev0"
diff --git a/ext/opentelemetry-instrumentation-starlette/tests/__init__.py b/ext/opentelemetry-instrumentation-starlette/tests/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/ext/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py b/ext/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py
new file mode 100644
index 00000000000..a49db07c9a7
--- /dev/null
+++ b/ext/opentelemetry-instrumentation-starlette/tests/test_starlette_instrumentation.py
@@ -0,0 +1,102 @@
+# Copyright The OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+from starlette import applications
+from starlette.responses import PlainTextResponse
+from starlette.routing import Route
+from starlette.testclient import TestClient
+
+import opentelemetry.instrumentation.starlette as otel_starlette
+from opentelemetry.test.test_base import TestBase
+
+
+class TestStarletteManualInstrumentation(TestBase):
+ def _create_app(self):
+ app = self._create_starlette_app()
+ self._instrumentor.instrument_app(app)
+ return app
+
+ def setUp(self):
+ super().setUp()
+ self._instrumentor = otel_starlette.StarletteInstrumentor()
+ self._app = self._create_app()
+ self._client = TestClient(self._app)
+
+ def test_basic_starlette_call(self):
+ self._client.get("/foobar")
+ spans = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(spans), 3)
+ for span in spans:
+ self.assertIn("/foobar", span.name)
+
+ def test_starlette_route_attribute_added(self):
+ """Ensure that starlette routes are used as the span name."""
+ self._client.get("/user/123")
+ spans = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(spans), 3)
+ for span in spans:
+ self.assertIn("/user/{username}", span.name)
+ self.assertEqual(
+ spans[-1].attributes["http.route"], "/user/{username}"
+ )
+ # ensure that at least one attribute that is populated by
+ # the asgi instrumentation is successfully feeding though.
+ self.assertEqual(spans[-1].attributes["http.flavor"], "1.1")
+
+ @staticmethod
+ def _create_starlette_app():
+ def home(_):
+ return PlainTextResponse("hi")
+
+ app = applications.Starlette(
+ routes=[Route("/foobar", home), Route("/user/{username}", home)]
+ )
+ return app
+
+
+class TestAutoInstrumentation(TestStarletteManualInstrumentation):
+ """Test the auto-instrumented variant
+
+ Extending the manual instrumentation as most test cases apply
+ to both.
+ """
+
+ def _create_app(self):
+ # instrumentation is handled by the instrument call
+ self._instrumentor.instrument()
+ return self._create_starlette_app()
+
+ def tearDown(self):
+ self._instrumentor.uninstrument()
+ super().tearDown()
+
+
+class TestAutoInstrumentationLogic(unittest.TestCase):
+ def test_instrumentation(self):
+ """Verify that instrumentation methods are instrumenting and
+ removing as expected.
+ """
+ instrumentor = otel_starlette.StarletteInstrumentor()
+ original = applications.Starlette
+ instrumentor.instrument()
+ try:
+ instrumented = applications.Starlette
+ self.assertIsNot(original, instrumented)
+ finally:
+ instrumentor.uninstrument()
+
+ should_be_original = applications.Starlette
+ self.assertIs(original, should_be_original)
diff --git a/opentelemetry-instrumentation/README.rst b/opentelemetry-instrumentation/README.rst
index d52ed037b75..6be744251b2 100644
--- a/opentelemetry-instrumentation/README.rst
+++ b/opentelemetry-instrumentation/README.rst
@@ -13,6 +13,34 @@ Installation
pip install opentelemetry-instrumentation
+
+This package provides a couple of commands that help automatically instruments a program:
+
+opentelemetry-instrument
+------------------------
+
+::
+
+ opentelemetry-instrument python program.py
+
+The code in ``program.py`` needs to use one of the packages for which there is
+an OpenTelemetry integration. For a list of the available integrations please
+check `here `_
+
+
+opentelemetry-bootstrap
+-----------------------
+
+::
+
+ opentelemetry-bootstrap --action=install|requirements
+
+This commands inspects the active Python site-packages and figures out which
+instrumentation packages the user might want to install. By default it prints out
+a list of the suggested instrumentation packages which can be added to a requirements.txt
+file. It also supports installing the suggested packages when run with :code:`--action=install`
+flag.
+
References
----------
diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/__init__.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/__init__.py
deleted file mode 100644
index 149a9b46c93..00000000000
--- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/__init__.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright The OpenTelemetry Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-
-This package provides a couple of commands that help automatically instruments a program:
-
-opentelemetry-instrument
------------------------------------
-
-::
-
- opentelemetry-instrument python program.py
-
-The code in ``program.py`` needs to use one of the packages for which there is
-an OpenTelemetry integration. For a list of the available integrations please
-check :doc:`here <../../index>`.
-
-
-opentelemetry-bootstrap
-------------------------
-
-::
-
- opentelemetry-bootstrap --action=install|requirements
-
-This commands inspects the active Python site-packages and figures out which
-instrumentation packages the user might want to install. By default it prints out
-a list of the suggested instrumentation packages which can be added to a requirements.txt
-file. It also supports installing the suggested packages when run with :code:`--action=install`
-flag.
-"""
diff --git a/scripts/check_for_valid_readme.py b/scripts/check_for_valid_readme.py
index edf94d9c3e1..d555b4fa2f9 100644
--- a/scripts/check_for_valid_readme.py
+++ b/scripts/check_for_valid_readme.py
@@ -10,7 +10,7 @@ def is_valid_rst(path):
"""Checks if RST can be rendered on PyPI."""
with open(path) as readme_file:
markup = readme_file.read()
- return readme_renderer.rst.render(markup) is not None
+ return readme_renderer.rst.render(markup, stream=sys.stderr) is not None
def parse_args():
diff --git a/tox.ini b/tox.ini
index cab2031aa7c..875f63fc3f2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -17,8 +17,8 @@ envlist =
pypy3-test-sdk
; opentelemetry-instrumentation
- py3{5,6,7,8}-test-instrumentation
- pypy3-test-instrumentation
+ py3{5,6,7,8}-test-instrumentation-base
+ pypy3-test-instrumentation-base
; opentelemetry-example-app
py3{4,5,6,7,8}-test-example-app
@@ -56,6 +56,11 @@ envlist =
py3{4,5,6,7,8}-test-ext-requests
pypy3-test-ext-requests
+ ; opentelemetry-instrumentation-starlette.
+ ; starlette only supports 3.6 and above.
+ py3{6,7,8}-test-instrumentation-starlette
+ pypy3-test-instrumentation-starlette
+
; opentelemetry-ext-jinja2
py3{4,5,6,7,8}-test-ext-jinja2
pypy3-test-ext-jinja2
@@ -102,7 +107,7 @@ envlist =
; opentelemetry-ext-pyramid
py3{4,5,6,7,8}-test-ext-pyramid
pypy3-test-ext-pyramid
-
+
; opentelemetry-ext-asgi
py3{5,6,7,8}-test-ext-asgi
pypy3-test-ext-asgi
@@ -172,8 +177,9 @@ setenv =
changedir =
test-api: opentelemetry-api/tests
test-sdk: opentelemetry-sdk/tests
+ instrumentation-base: opentelemetry-instrumentation/tests
+ test-instrumentation-starlette: ext/opentelemetry-instrumentation-starlette/tests
test-proto: opentelemetry-proto/tests
- test-instrumentation: opentelemetry-instrumentation/tests
test-ext-grpc: ext/opentelemetry-ext-grpc/tests
test-ext-aiohttp-client: ext/opentelemetry-ext-aiohttp-client/tests
test-ext-requests: ext/opentelemetry-ext-requests/tests
@@ -223,10 +229,9 @@ commands_pre =
grpc: pip install {toxinidir}/ext/opentelemetry-ext-grpc[test]
- wsgi,flask,django,asgi,pyramid: pip install {toxinidir}/tests/util
+ wsgi,flask,django,asgi,pyramid,starlette: pip install {toxinidir}/tests/util
wsgi,flask,django,pyramid: pip install {toxinidir}/ext/opentelemetry-ext-wsgi
-
- asgi: pip install {toxinidir}/ext/opentelemetry-ext-asgi
+ asgi,starlette: pip install {toxinidir}/ext/opentelemetry-ext-asgi
boto: pip install {toxinidir}/ext/opentelemetry-ext-boto[test]
@@ -264,6 +269,8 @@ commands_pre =
requests: pip install {toxinidir}/ext/opentelemetry-ext-requests[test]
+ starlette: pip install {toxinidir}/ext/opentelemetry-instrumentation-starlette[test]
+
jinja2: pip install {toxinidir}/ext/opentelemetry-ext-jinja2[test]
aiohttp-client: pip install {toxinidir}/opentelemetry-sdk {toxinidir}/ext/opentelemetry-ext-aiohttp-client
@@ -274,7 +281,7 @@ commands_pre =
opentracing-shim: pip install {toxinidir}/ext/opentelemetry-ext-opentracing-shim
datadog: pip install {toxinidir}/opentelemetry-sdk {toxinidir}/ext/opentelemetry-ext-datadog
-
+
zipkin: pip install {toxinidir}/ext/opentelemetry-ext-zipkin
sqlalchemy: pip install {toxinidir}/ext/opentelemetry-ext-sqlalchemy
@@ -322,7 +329,7 @@ deps =
httpretty
commands_pre =
- python scripts/eachdist.py install --editable
+ python scripts/eachdist.py install --editable --with-test-deps
commands =
python scripts/eachdist.py lint --check-only