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