diff --git a/.github/workflows/instrumentations_0.yml b/.github/workflows/instrumentations_0.yml
index d54cb50119..69c07bfacc 100644
--- a/.github/workflows/instrumentations_0.yml
+++ b/.github/workflows/instrumentations_0.yml
@@ -66,7 +66,6 @@ jobs:
- "redis"
- "remoulade"
- "requests"
- - "sklearn"
- "sqlalchemy"
- "sqlite3"
- "starlette"
@@ -75,14 +74,6 @@ jobs:
- "tortoiseorm"
os: [ubuntu-20.04]
exclude:
- - python-version: py39
- package: "sklearn"
- - python-version: py310
- package: "sklearn"
- - python-version: py311
- package: "sklearn"
- - python-version: py312
- package: "sklearn"
- python-version: py312
package: "boto"
- python-version: py312
@@ -103,8 +94,6 @@ jobs:
package: "remoulade"
- python-version: pypy3
package: "requests"
- - python-version: pypy3
- package: "sklearn"
- python-version: pypy3
package: "confluent-kafka"
- python-version: pypy3
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index c156f7d942..29cd5cac16 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -93,31 +93,3 @@ jobs:
key: v7-build-tox-cache-${{ matrix.package }}-${{ hashFiles('tox.ini', 'gen-requirements.txt', 'dev-requirements.txt') }}
- name: run tox
run: tox -e lint-${{ matrix.package }}
-
- lint-3_8:
- strategy:
- fail-fast: false # ensures the entire test matrix is run, even if one permutation fails
- matrix:
- package:
- - "instrumentation-sklearn"
- os: [ubuntu-20.04]
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout Contrib Repo @ SHA - ${{ github.sha }}
- uses: actions/checkout@v4
- - name: Set up Python 3.8
- uses: actions/setup-python@v5
- with:
- python-version: 3.8
- - name: Install tox
- run: pip install tox
- - name: Cache tox environment
- # Preserves .tox directory between runs for faster installs
- uses: actions/cache@v4
- with:
- path: |
- .tox
- ~/.cache/pip
- key: v7-build-tox-cache-${{ matrix.package }}-${{ hashFiles('tox.ini', 'gen-requirements.txt', 'dev-requirements.txt') }}
- - name: run tox
- run: tox -e lint-${{ matrix.package }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 57447ccf97..2de76b2736 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- `opentelemetry-instrumentation-sklearn` Deprecated the sklearn instrumentation
+ ([#2708](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2708))
- `opentelemetry-instrumentation-pyramid` Record exceptions raised when serving a request
([#2622](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2622))
- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator
diff --git a/eachdist.ini b/eachdist.ini
index 7f170e4947..b698eaf299 100644
--- a/eachdist.ini
+++ b/eachdist.ini
@@ -54,7 +54,6 @@ packages=
[lintroots]
extraroots=examples/*,scripts/
subglob=*.py,tests/,test/,src/*,examples/*
-ignore=sklearn
[testroots]
extraroots=examples/*,tests/
diff --git a/instrumentation/README.md b/instrumentation/README.md
index bbc5d8d75c..989ba529cb 100644
--- a/instrumentation/README.md
+++ b/instrumentation/README.md
@@ -38,7 +38,6 @@
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | experimental
| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No | experimental
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration
-| [opentelemetry-instrumentation-sklearn](./opentelemetry-instrumentation-sklearn) | scikit-learn ~= 0.24.0 | No | experimental
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy | Yes | experimental
| [opentelemetry-instrumentation-sqlite3](./opentelemetry-instrumentation-sqlite3) | sqlite3 | No | experimental
| [opentelemetry-instrumentation-starlette](./opentelemetry-instrumentation-starlette) | starlette ~= 0.13.0 | Yes | experimental
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE b/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE
deleted file mode 100644
index 261eeb9e9f..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/README.rst b/instrumentation/opentelemetry-instrumentation-sklearn/README.rst
deleted file mode 100644
index 9a3300c4f2..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/README.rst
+++ /dev/null
@@ -1,24 +0,0 @@
-OpenTelemetry Scikit-Learn Instrumentation
-==========================================
-
-|pypi|
-
-.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-sklearn.svg
- :target: https://pypi.org/project/opentelemetry-instrumentation-sklearn/
-
-This library allows tracing HTTP requests made by the
-`scikit-learn `_ library.
-
-Installation
-------------
-
-::
-
- pip install opentelemetry-instrumentation-sklearn
-
-References
-----------
-
-* `OpenTelemetry sklearn Instrumentation `_
-* `OpenTelemetry Project `_
-* `OpenTelemetry Python Examples `_
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/pyproject.toml b/instrumentation/opentelemetry-instrumentation-sklearn/pyproject.toml
deleted file mode 100644
index 6e57529ed1..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/pyproject.toml
+++ /dev/null
@@ -1,49 +0,0 @@
-[build-system]
-requires = ["hatchling"]
-build-backend = "hatchling.build"
-
-[project]
-name = "opentelemetry-instrumentation-sklearn"
-dynamic = ["version"]
-description = "OpenTelemetry sklearn instrumentation"
-readme = "README.rst"
-license = "Apache-2.0"
-requires-python = ">=3.8"
-authors = [
- { name = "OpenTelemetry Authors", email = "cncf-opentelemetry-contributors@lists.cncf.io" },
-]
-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.8",
-]
-dependencies = [
- "opentelemetry-api ~= 1.12",
- "opentelemetry-instrumentation == 0.47b0.dev",
-]
-
-[project.optional-dependencies]
-instruments = [
- "scikit-learn ~= 0.24.0",
-]
-
-[project.entry-points.opentelemetry_instrumentor]
-sklearn = "opentelemetry.instrumentation.sklearn:SklearnInstrumentor"
-
-[project.urls]
-Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-sklearn"
-
-[tool.hatch.version]
-path = "src/opentelemetry/instrumentation/sklearn/version.py"
-
-[tool.hatch.build.targets.sdist]
-include = [
- "/src",
- "/tests",
-]
-
-[tool.hatch.build.targets.wheel]
-packages = ["src/opentelemetry"]
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/__init__.py b/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/__init__.py
deleted file mode 100644
index a67bfa6ef4..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/__init__.py
+++ /dev/null
@@ -1,792 +0,0 @@
-# Copyright 2020, 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.
-"""
-The integration with sklearn supports the scikit-learn compatible libraries,
-it can be enabled by using ``SklearnInstrumentor``.
-
-.. sklearn: https://github.com/scikit-learn/scikit-learn
-
-Usage
------
-
-Package instrumentation example:
-
-.. code-block:: python
-
- from opentelemetry.instrumentation.sklearn import SklearnInstrumentor
-
- # instrument the sklearn library
- SklearnInstrumentor().instrument()
-
- # instrument sklearn and other libraries
- SklearnInstrumentor(
- packages=["sklearn", "lightgbm", "xgboost"]
- ).instrument()
-
-
-Model instrumentation example:
-
-.. code-block:: python
-
- from opentelemetry.instrumentation.sklearn import SklearnInstrumentor
- from sklearn.datasets import load_iris
- from sklearn.ensemble import RandomForestClassifier
- from sklearn.model_selection import train_test_split
- from sklearn.pipeline import Pipeline
-
- X, y = load_iris(return_X_y=True)
- X_train, X_test, y_train, y_test = train_test_split(X, y)
-
- model = Pipeline(
- [
- ("class", RandomForestClassifier(n_estimators=10)),
- ]
- )
-
- model.fit(X_train, y_train)
-
- SklearnInstrumentor().instrument_estimator(model)
-
-"""
-import logging
-import os
-from functools import wraps
-from importlib import import_module
-from inspect import isclass
-from pkgutil import iter_modules
-from typing import (
- Callable,
- Collection,
- Dict,
- List,
- MutableMapping,
- Sequence,
- Type,
- Union,
-)
-
-from sklearn.base import BaseEstimator
-from sklearn.pipeline import FeatureUnion, Pipeline
-from sklearn.tree import BaseDecisionTree
-from sklearn.utils.metaestimators import _IffHasAttrDescriptor
-
-from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
-
-# pylint: disable=no-name-in-module
-from opentelemetry.instrumentation.sklearn.package import _instruments
-from opentelemetry.instrumentation.sklearn.version import __version__
-from opentelemetry.trace import get_tracer
-from opentelemetry.util.types import Attributes
-
-logger = logging.getLogger(__name__)
-
-
-def implement_span_estimator(
- func: Callable,
- estimator: Union[BaseEstimator, Type[BaseEstimator]],
- attributes: Attributes = None,
-):
- """Wrap the method call with a span.
-
- Args:
- func: A callable to be wrapped in a span
- estimator: An instance or class of an estimator
- attributes: Attributes to apply to the span
-
- Returns:
- The passed function wrapped in a span.
- """
- if isclass(estimator):
- name = estimator.__name__
- else:
- name = estimator.__class__.__name__
- logger.debug("Instrumenting: %s.%s", name, func.__name__)
- attributes = attributes or {}
- name = f"{name}.{func.__name__}"
- return implement_span_function(func, name, attributes)
-
-
-def implement_span_function(func: Callable, name: str, attributes: Attributes):
- """Wrap the function with a span.
-
- Args:
- func: A callable to be wrapped in a span
- name: The name of the span
- attributes: Attributes to apply to the span
-
- Returns:
- The passed function wrapped in a span.
- """
-
- @wraps(func)
- def wrapper(*args, **kwargs):
- with get_tracer(
- __name__,
- __version__,
- schema_url="https://opentelemetry.io/schemas/1.11.0",
- ).start_as_current_span(name=name) as span:
- if span.is_recording():
- for key, val in attributes.items():
- span.set_attribute(key, val)
- return func(*args, **kwargs)
-
- return wrapper
-
-
-def implement_span_delegator(
- obj: _IffHasAttrDescriptor, attributes: Attributes = None
-):
- """Wrap the descriptor's fn with a span.
-
- Args:
- obj: An instance of _IffHasAttrDescriptor
- attributes: Attributes to apply to the span
- """
- # Don't instrument inherited delegators
- if hasattr(obj, "_otel_original_fn"):
- logger.debug("Already instrumented: %s", obj.fn.__qualname__)
- return
- logger.debug("Instrumenting: %s", obj.fn.__qualname__)
- attributes = attributes or {}
- setattr(obj, "_otel_original_fn", getattr(obj, "fn"))
- setattr(
- obj,
- "fn",
- implement_span_function(obj.fn, obj.fn.__qualname__, attributes),
- )
-
-
-def get_delegator(
- estimator: Type[BaseEstimator], method_name: str
-) -> Union[_IffHasAttrDescriptor, None]:
- """Get the delegator from a class method or None.
-
- Args:
- estimator: A class derived from ``sklearn``'s ``BaseEstimator``.
- method_name (str): The method name of the estimator on which to
- check for delegation.
-
- Returns:
- The delegator, if one exists, otherwise None.
- """
- class_attr = getattr(estimator, method_name)
- if getattr(class_attr, "__closure__", None) is not None:
- for cell in class_attr.__closure__:
- if isinstance(cell.cell_contents, _IffHasAttrDescriptor):
- return cell.cell_contents
- return None
-
-
-def get_base_estimators(packages: List[str]) -> Dict[str, Type[BaseEstimator]]:
- """Walk package hierarchies to get BaseEstimator-derived classes.
-
- Args:
- packages (list(str)): A list of package names to instrument.
-
- Returns:
- A dictionary of qualnames and classes inheriting from
- ``BaseEstimator``.
- """
- klasses = {}
- for package_name in packages:
- lib = import_module(package_name)
- package_dir = os.path.dirname(lib.__file__)
- for _, module_name, _ in iter_modules([package_dir]):
- # import the module and iterate through its attributes
- try:
- module = import_module(package_name + "." + module_name)
- except ImportError:
- logger.warning(
- "Unable to import %s.%s", package_name, module_name
- )
- continue
- for attribute_name in dir(module):
- attrib = getattr(module, attribute_name)
- if isclass(attrib) and issubclass(attrib, BaseEstimator):
- klasses[
- ".".join([package_name, module_name, attribute_name])
- ] = attrib
- return klasses
-
-
-# Methods on which spans should be applied.
-DEFAULT_METHODS = [
- "fit",
- "transform",
- "predict",
- "predict_proba",
- "_fit",
- "_transform",
- "_predict",
- "_predict_proba",
-]
-
-# Classes and their attributes which contain a list of tupled estimators
-# through which we should walk recursively for estimators.
-DEFAULT_NAMEDTUPLE_ATTRIBS = {
- Pipeline: ["steps"],
- FeatureUnion: ["transformer_list"],
-}
-
-# Classes and their attributes which contain an estimator or sequence of
-# estimators through which we should walk recursively for estimators.
-DEFAULT_ATTRIBS = {}
-
-# Classes (including children) explicitly excluded from autoinstrumentation
-DEFAULT_EXCLUDE_CLASSES = [BaseDecisionTree]
-
-# Default packages for autoinstrumentation
-DEFAULT_PACKAGES = ["sklearn"]
-
-
-class SklearnInstrumentor(BaseInstrumentor):
- """Instrument a fitted sklearn model with opentelemetry spans.
-
- Instrument methods of ``BaseEstimator``-derived components in a sklearn
- model. The assumption is that a machine learning model ``Pipeline`` (or
- class descendent) is being instrumented with opentelemetry. Within a
- ``Pipeline`` is some hierarchy of estimators and transformers.
-
- The ``instrument_estimator`` method walks this hierarchy of estimators,
- implementing each of the defined methods with its own span.
-
- Certain estimators in the sklearn ecosystem contain other estimators as
- instance attributes. Support for walking this embedded sub-hierarchy is
- supported with ``recurse_attribs``. This argument is a dictionary
- with classes as keys, and a list of attributes representing embedded
- estimators as values. By default, ``recurse_attribs`` is empty.
-
- Similar to Pipelines, there are also estimators which have class attributes
- as a list of 2-tuples; for instance, the ``FeatureUnion`` and its attribute
- ``transformer_list``. Instrumenting estimators like this is also
- supported through the ``recurse_namedtuple_attribs`` argument. This
- argument is a dictionary with classes as keys, and a list of attribute
- names representing the namedtuple list(s). By default, the
- ``recurse_namedtuple_attribs`` dictionary supports
- ``Pipeline`` with ``steps``, and ``FeatureUnion`` with
- ``transformer_list``.
-
- Note that spans will not be generated for any child transformer whose
- parent transformer has ``n_jobs`` parameter set to anything besides
- ``None`` or ``1``.
-
- Package instrumentation example:
-
- .. code-block:: python
-
- from opentelemetry.instrumentation.sklearn import SklearnInstrumentor
-
- # instrument the sklearn library
- SklearnInstrumentor().instrument()
-
- # instrument several sklearn-compatible libraries
- packages = ["sklearn", "lightgbm", "xgboost"]
- SklearnInstrumentor(packages=packages).instrument()
-
-
- Model instrumentation example:
-
- .. code-block:: python
-
- from opentelemetry.instrumentation.sklearn import SklearnInstrumentor
- from sklearn.datasets import load_iris
- from sklearn.ensemble import RandomForestClassifier
- from sklearn.model_selection import train_test_split
- from sklearn.pipeline import Pipeline
-
- X, y = load_iris(return_X_y=True)
- X_train, X_test, y_train, y_test = train_test_split(X, y)
-
- model = Pipeline(
- [
- ("class", RandomForestClassifier(n_estimators=10)),
- ]
- )
-
- model.fit(X_train, y_train)
-
- SklearnInstrumentor().instrument_estimator(model)
-
- Args:
- methods (list): A list of method names on which to instrument a span.
- This list of methods will be checked on all estimators in the model
- hierarchy. Used in package and model instrumentation
- recurse_attribs (dict): A dictionary of ``BaseEstimator``-derived
- sklearn classes as keys, with values being a list of attributes. Each
- attribute represents either an estimator or list of estimators on
- which to also implement spans. An example is
- ``RandomForestClassifier`` and its attribute ``estimators_``. Used
- in model instrumentation only.
- recurse_namedtuple_attribs (dict): A dictionary of ``BaseEstimator``-
- derived sklearn types as keys, with values being a list of
- attribute names. Each attribute represents a list of 2-tuples in
- which the first element is the estimator name, and the second
- element is the estimator. Defaults include sklearn's ``Pipeline``
- and its attribute ``steps``, and the ``FeatureUnion`` and its
- attribute ``transformer_list``. Used in model instrumentation only.
- packages: A list of sklearn-compatible packages to
- instrument. Used with package instrumentation only.
- exclude_classes: A list of classes to exclude from instrumentation.
- Child classes are also excluded. Default is sklearn's
- ``[BaseDecisionTree]``.
- """
-
- def __new__(cls, *args, **kwargs):
- """Override new.
-
- The base class' new method passes args and kwargs. We override because
- we init the class with configuration and Python raises TypeError when
- additional arguments are passed to the object.__new__() method.
- """
- if cls._instance is None:
- cls._instance = object.__new__(cls)
-
- return cls._instance
-
- def __init__(
- self,
- methods: List[str] = None,
- recurse_attribs: Dict[Type[BaseEstimator], List[str]] = None,
- recurse_namedtuple_attribs: Dict[
- Type[BaseEstimator], List[str]
- ] = None,
- packages: List[str] = None,
- exclude_classes: List[Type] = None,
- ):
- self.methods = methods or DEFAULT_METHODS
- self.recurse_attribs = recurse_attribs or DEFAULT_ATTRIBS
- self.recurse_namedtuple_attribs = (
- recurse_namedtuple_attribs or DEFAULT_NAMEDTUPLE_ATTRIBS
- )
- self.packages = packages or DEFAULT_PACKAGES
- if exclude_classes is None:
- self.exclude_classes = tuple(DEFAULT_EXCLUDE_CLASSES)
- else:
- self.exclude_classes = tuple(exclude_classes)
-
- def instrumentation_dependencies(self) -> Collection[str]:
- return _instruments
-
- def _instrument(self, **kwargs):
- """Instrument the library, and any additional specified on init."""
- klasses = get_base_estimators(packages=self.packages)
- attributes = kwargs.get("attributes")
- for _, klass in klasses.items():
- if issubclass(klass, self.exclude_classes):
- logger.debug("Not instrumenting (excluded): %s", str(klass))
- else:
- logger.debug("Instrumenting: %s", str(klass))
- for method_name in self.methods:
- if hasattr(klass, method_name):
- self._instrument_class_method(
- estimator=klass,
- method_name=method_name,
- attributes=attributes,
- )
-
- def _uninstrument(self, **kwargs):
- """Uninstrument the library"""
- klasses = get_base_estimators(packages=self.packages)
- for _, klass in klasses.items():
- logger.debug("Uninstrumenting: %s", str(klass))
- for method_name in self.methods:
- if hasattr(klass, method_name):
- self._uninstrument_class_method(
- estimator=klass, method_name=method_name
- )
-
- def instrument_estimator(
- self, estimator: BaseEstimator, attributes: Attributes = None
- ):
- """Instrument a fitted estimator and its hierarchy where configured.
-
- Args:
- estimator (sklearn.base.BaseEstimator): A fitted ``sklearn``
- estimator, typically a ``Pipeline`` instance.
- attributes (dict): Attributes to attach to the spans.
- """
- if isinstance(estimator, self.exclude_classes):
- logger.debug(
- "Not instrumenting (excluded): %s",
- estimator.__class__.__name__,
- )
- return
-
- if isinstance(
- estimator, tuple(self.recurse_namedtuple_attribs.keys())
- ):
- self._instrument_estimator_namedtuple(
- estimator=estimator, attributes=attributes
- )
-
- if isinstance(estimator, tuple(self.recurse_attribs.keys())):
- self._instrument_estimator_attribute(
- estimator=estimator, attributes=attributes
- )
-
- for method_name in self.methods:
- if hasattr(estimator, method_name):
- self._instrument_instance_method(
- estimator=estimator,
- method_name=method_name,
- attributes=attributes,
- )
-
- def uninstrument_estimator(self, estimator: BaseEstimator):
- """Uninstrument a fitted estimator and its hierarchy where configured.
-
- Args:
- estimator (sklearn.base.BaseEstimator): A fitted ``sklearn``
- estimator, typically a ``Pipeline`` instance.
- """
- if isinstance(estimator, self.exclude_classes):
- logger.debug(
- "Not uninstrumenting (excluded): %s",
- estimator.__class__.__name__,
- )
- return
-
- if isinstance(
- estimator, tuple(self.recurse_namedtuple_attribs.keys())
- ):
- self._uninstrument_estimator_namedtuple(estimator=estimator)
-
- if isinstance(estimator, tuple(self.recurse_attribs.keys())):
- self._uninstrument_estimator_attribute(estimator=estimator)
-
- for method_name in self.methods:
- if hasattr(estimator, method_name):
- self._uninstrument_instance_method(
- estimator=estimator, method_name=method_name
- )
-
- def _check_instrumented(
- self,
- estimator: Union[BaseEstimator, Type[BaseEstimator]],
- method_name: str,
- ) -> bool:
- """Check an estimator-method is instrumented.
-
- Args:
- estimator (BaseEstimator): A class or instance of an ``sklearn``
- estimator.
- method_name (str): The method name of the estimator on which to
- check for instrumentation.
- """
- orig_method_name = "_otel_original_" + method_name
- has_original = hasattr(estimator, orig_method_name)
- orig_class, orig_method = getattr(
- estimator, orig_method_name, (None, None)
- )
- same_class = orig_class == estimator
- if has_original and same_class:
- class_method = self._unwrap_function(
- getattr(estimator, method_name)
- )
- # if they match then the subclass doesn't override
- # if they don't then the overridden method needs instrumentation
- if class_method.__name__ == orig_method.__name__:
- return True
- return False
-
- def _uninstrument_class_method(
- self, estimator: Type[BaseEstimator], method_name: str
- ):
- """Uninstrument a class method.
-
- Replaces the patched method with the original, and deletes the
- attribute which stored the original method.
-
- Args:
- estimator (BaseEstimator): A class or instance of an ``sklearn``
- estimator.
- method_name (str): The method name of the estimator on which to
- apply a span.
- """
- orig_method_name = "_otel_original_" + method_name
- if isclass(estimator):
- qualname = estimator.__qualname__
- else:
- qualname = estimator.__class__.__qualname__
- delegator = get_delegator(estimator, method_name)
- if self._check_instrumented(estimator, method_name):
- logger.debug(
- "Uninstrumenting: %s.%s",
- qualname,
- method_name,
- )
- _, orig_method = getattr(estimator, orig_method_name)
- setattr(
- estimator,
- method_name,
- orig_method,
- )
- delattr(estimator, orig_method_name)
- elif delegator is not None:
- if not hasattr(delegator, "_otel_original_fn"):
- logger.debug(
- "Already uninstrumented: %s.%s",
- qualname,
- method_name,
- )
- return
- setattr(
- delegator,
- "fn",
- getattr(delegator, "_otel_original_fn"),
- )
- delattr(delegator, "_otel_original_fn")
- else:
- logger.debug(
- "Already uninstrumented: %s.%s",
- qualname,
- method_name,
- )
-
- def _uninstrument_instance_method(
- self, estimator: BaseEstimator, method_name: str
- ):
- """Uninstrument an instance method.
-
- Replaces the patched method with the original, and deletes the
- attribute which stored the original method.
-
- Args:
- estimator (BaseEstimator): A class or instance of an ``sklearn``
- estimator.
- method_name (str): The method name of the estimator on which to
- apply a span.
- """
- orig_method_name = "_otel_original_" + method_name
- if isclass(estimator):
- qualname = estimator.__qualname__
- else:
- qualname = estimator.__class__.__qualname__
- if self._check_instrumented(estimator, method_name):
- logger.debug(
- "Uninstrumenting: %s.%s",
- qualname,
- method_name,
- )
- _, orig_method = getattr(estimator, orig_method_name)
- setattr(
- estimator,
- method_name,
- orig_method,
- )
- delattr(estimator, orig_method_name)
- else:
- logger.debug(
- "Already uninstrumented: %s.%s",
- qualname,
- method_name,
- )
-
- def _instrument_class_method(
- self,
- estimator: Type[BaseEstimator],
- method_name: str,
- attributes: Attributes = None,
- ):
- """Instrument an estimator method with a span.
-
- When instrumenting we attach a tuple of (Class, method) to the
- attribute ``_otel_original_`` for each method. This allows
- us to replace the patched with the original in uninstrumentation, but
- also allows proper instrumentation of child classes without
- instrumenting inherited methods twice.
-
- Args:
- estimator (BaseEstimator): A ``BaseEstimator``-derived
- class
- method_name (str): The method name of the estimator on which to
- apply a span.
- attributes (dict): Attributes to attach to the spans.
- """
- if self._check_instrumented(estimator, method_name):
- logger.debug(
- "Already instrumented: %s.%s",
- estimator.__qualname__,
- method_name,
- )
- return
- class_attr = getattr(estimator, method_name)
- delegator = get_delegator(estimator, method_name)
- if isinstance(class_attr, property):
- logger.debug(
- "Not instrumenting found property: %s.%s",
- estimator.__qualname__,
- method_name,
- )
- elif delegator is not None:
- implement_span_delegator(delegator)
- else:
- setattr(
- estimator,
- "_otel_original_" + method_name,
- (estimator, class_attr),
- )
- setattr(
- estimator,
- method_name,
- implement_span_estimator(class_attr, estimator, attributes),
- )
-
- def _unwrap_function(self, function):
- """Fetch the function underlying any decorators"""
- if hasattr(function, "__wrapped__"):
- return self._unwrap_function(function.__wrapped__)
- return function
-
- def _instrument_instance_method(
- self,
- estimator: BaseEstimator,
- method_name: str,
- attributes: Attributes = None,
- ):
- """Instrument an estimator instance method with a span.
-
- When instrumenting we attach a tuple of (Class, method) to the
- attribute ``_otel_original_`` for each method. This allows
- us to replace the patched with the original in unstrumentation.
-
- Args:
- estimator (BaseEstimator): A fitted ``sklearn`` estimator.
- method_name (str): The method name of the estimator on which to
- apply a span.
- attributes (dict): Attributes to attach to the spans.
- """
- if self._check_instrumented(estimator, method_name):
- logger.debug(
- "Already instrumented: %s.%s",
- estimator.__class__.__qualname__,
- method_name,
- )
- return
-
- class_attr = getattr(type(estimator), method_name, None)
- if isinstance(class_attr, property):
- logger.debug(
- "Not instrumenting found property: %s.%s",
- estimator.__class__.__qualname__,
- method_name,
- )
- else:
- method = getattr(estimator, method_name)
- setattr(
- estimator, "_otel_original_" + method_name, (estimator, method)
- )
- setattr(
- estimator,
- method_name,
- implement_span_estimator(method, estimator, attributes),
- )
-
- def _instrument_estimator_attribute(
- self, estimator: BaseEstimator, attributes: Attributes = None
- ):
- """Instrument instance attributes which also contain estimators.
-
- Handle instance attributes which are also estimators, are a list
- (Sequence) of estimators, or are mappings (dictionary) in which
- the values are estimators.
-
- Examples include ``RandomForestClassifier`` and
- ``MultiOutputRegressor`` instances which have attributes
- ``estimators_`` attributes.
-
- Args:
- estimator (BaseEstimator): A fitted ``sklearn`` estimator, with an
- attribute which also contains an estimator or collection of
- estimators.
- attributes (dict): Attributes to attach to the spans.
- """
- attribs = self.recurse_attribs.get(estimator.__class__, [])
- for attrib in attribs:
- attrib_value = getattr(estimator, attrib)
- if isinstance(attrib_value, Sequence):
- for value in attrib_value:
- self.instrument_estimator(
- estimator=value, attributes=attributes
- )
- elif isinstance(attrib_value, MutableMapping):
- for value in attrib_value.values():
- self.instrument_estimator(
- estimator=value, attributes=attributes
- )
- else:
- self.instrument_estimator(
- estimator=attrib_value, attributes=attributes
- )
-
- def _instrument_estimator_namedtuple(
- self, estimator: BaseEstimator, attributes: Attributes = None
- ):
- """Instrument attributes with (name, estimator) tupled components.
-
- Examples include Pipeline and FeatureUnion instances which
- have attributes steps and transformer_list, respectively.
-
- Args:
- estimator: A fitted sklearn estimator, with an attribute which also
- contains an estimator or collection of estimators.
- attributes (dict): Attributes to attach to the spans.
- """
- attribs = self.recurse_namedtuple_attribs.get(estimator.__class__, [])
- for attrib in attribs:
- for _, est in getattr(estimator, attrib):
- self.instrument_estimator(estimator=est, attributes=attributes)
-
- def _uninstrument_estimator_attribute(self, estimator: BaseEstimator):
- """Uninstrument instance attributes which also contain estimators.
-
- Handle instance attributes which are also estimators, are a list
- (Sequence) of estimators, or are mappings (dictionary) in which
- the values are estimators.
-
- Examples include ``RandomForestClassifier`` and
- ``MultiOutputRegressor`` instances which have attributes
- ``estimators_`` attributes.
-
- Args:
- estimator (BaseEstimator): A fitted ``sklearn`` estimator, with an
- attribute which also contains an estimator or collection of
- estimators.
- """
- attribs = self.recurse_attribs.get(estimator.__class__, [])
- for attrib in attribs:
- attrib_value = getattr(estimator, attrib)
- if isinstance(attrib_value, Sequence):
- for value in attrib_value:
- self.uninstrument_estimator(estimator=value)
- elif isinstance(attrib_value, MutableMapping):
- for value in attrib_value.values():
- self.uninstrument_estimator(estimator=value)
- else:
- self.uninstrument_estimator(estimator=attrib_value)
-
- def _uninstrument_estimator_namedtuple(self, estimator: BaseEstimator):
- """Uninstrument attributes with (name, estimator) tupled components.
-
- Examples include Pipeline and FeatureUnion instances which
- have attributes steps and transformer_list, respectively.
-
- Args:
- estimator: A fitted sklearn estimator, with an attribute which also
- contains an estimator or collection of estimators.
- """
- attribs = self.recurse_namedtuple_attribs.get(estimator.__class__, [])
- for attrib in attribs:
- for _, est in getattr(estimator, attrib):
- self.uninstrument_estimator(estimator=est)
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/package.py b/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/package.py
deleted file mode 100644
index 41db461453..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/package.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2020, 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.
-
-
-_instruments = ("scikit-learn ~= 0.24.0",)
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/version.py b/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/version.py
deleted file mode 100644
index deef26c62f..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/version.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2020, 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.47b0.dev"
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt b/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt
deleted file mode 100644
index af341e16ed..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-asgiref==3.7.2
-Deprecated==1.2.14
-importlib-metadata==6.11.0
-iniconfig==2.0.0
-joblib==1.3.2
-numpy==1.24.4
-packaging==24.0
-pluggy==1.5.0
-py-cpuinfo==9.0.0
-pytest==7.4.4
-scikit-learn==0.24.2
-scipy==1.10.1
-threadpoolctl==3.3.0
-tomli==2.0.1
-typing_extensions==4.10.0
-wrapt==1.16.0
-zipp==3.19.2
--e opentelemetry-instrumentation
--e instrumentation/opentelemetry-instrumentation-sklearn
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/tests/__init__.py b/instrumentation/opentelemetry-instrumentation-sklearn/tests/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/tests/fixtures.py b/instrumentation/opentelemetry-instrumentation-sklearn/tests/fixtures.py
deleted file mode 100644
index cf26c0fcf2..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/tests/fixtures.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2020, 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 numpy as np
-from sklearn.datasets import load_iris
-from sklearn.decomposition import PCA, TruncatedSVD
-from sklearn.ensemble import RandomForestClassifier
-from sklearn.model_selection import train_test_split
-from sklearn.pipeline import FeatureUnion, Pipeline
-from sklearn.preprocessing import Normalizer, StandardScaler
-
-X, y = load_iris(return_X_y=True)
-X_train, X_test, y_train, y_test = train_test_split(X, y)
-
-
-def pipeline():
- """A dummy model that has a bunch of components that we can test."""
- model = Pipeline(
- [
- ("scaler", StandardScaler()),
- ("normal", Normalizer()),
- (
- "union",
- FeatureUnion(
- [
- ("pca", PCA(n_components=1)),
- ("svd", TruncatedSVD(n_components=2)),
- ],
- n_jobs=1, # parallelized components won't generate spans
- ),
- ),
- ("class", RandomForestClassifier(n_estimators=10)),
- ]
- )
- model.fit(X_train, y_train)
- return model
-
-
-def random_input():
- """A random record from the feature set."""
- rows = X.shape[0]
- random_row = np.random.choice(rows, size=1)
- return X[random_row, :]
diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/tests/test_sklearn.py b/instrumentation/opentelemetry-instrumentation-sklearn/tests/test_sklearn.py
deleted file mode 100644
index db69761ece..0000000000
--- a/instrumentation/opentelemetry-instrumentation-sklearn/tests/test_sklearn.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# Copyright 2020, 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 sklearn.ensemble import RandomForestClassifier
-
-# pylint: disable=no-name-in-module
-from opentelemetry.instrumentation.sklearn import (
- DEFAULT_EXCLUDE_CLASSES,
- DEFAULT_METHODS,
- SklearnInstrumentor,
- get_base_estimators,
- get_delegator,
-)
-from opentelemetry.test.test_base import TestBase
-from opentelemetry.trace import SpanKind
-
-from .fixtures import pipeline, random_input
-
-
-def assert_instrumented(base_estimators):
- for _, estimator in base_estimators.items():
- for method_name in DEFAULT_METHODS:
- original_method_name = "_otel_original_" + method_name
- if issubclass(estimator, tuple(DEFAULT_EXCLUDE_CLASSES)):
- assert not hasattr(estimator, original_method_name)
- continue
- class_attr = getattr(estimator, method_name, None)
- if isinstance(class_attr, property):
- assert not hasattr(estimator, original_method_name)
- continue
- delegator = None
- if hasattr(estimator, method_name):
- delegator = get_delegator(estimator, method_name)
- if delegator is not None:
- assert hasattr(delegator, "_otel_original_fn")
- elif hasattr(estimator, method_name):
- assert hasattr(estimator, original_method_name)
-
-
-def assert_uninstrumented(base_estimators):
- for _, estimator in base_estimators.items():
- for method_name in DEFAULT_METHODS:
- original_method_name = "_otel_original_" + method_name
- if issubclass(estimator, tuple(DEFAULT_EXCLUDE_CLASSES)):
- assert not hasattr(estimator, original_method_name)
- continue
- class_attr = getattr(estimator, method_name, None)
- if isinstance(class_attr, property):
- assert not hasattr(estimator, original_method_name)
- continue
- delegator = None
- if hasattr(estimator, method_name):
- delegator = get_delegator(estimator, method_name)
- if delegator is not None:
- assert not hasattr(delegator, "_otel_original_fn")
- elif hasattr(estimator, method_name):
- assert not hasattr(estimator, original_method_name)
-
-
-class TestSklearn(TestBase):
- def test_package_instrumentation(self):
- ski = SklearnInstrumentor()
-
- base_estimators = get_base_estimators(packages=["sklearn"])
-
- model = pipeline()
-
- ski.instrument()
- assert_instrumented(base_estimators)
-
- x_test = random_input()
-
- model.predict(x_test)
-
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 8)
- self.memory_exporter.clear()
-
- ski.uninstrument()
- assert_uninstrumented(base_estimators)
-
- model = pipeline()
- x_test = random_input()
-
- model.predict(x_test)
-
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 0)
-
- def test_span_properties(self):
- """Test that we get all of the spans we expect."""
- model = pipeline()
- ski = SklearnInstrumentor()
- ski.instrument_estimator(estimator=model)
-
- x_test = random_input()
-
- model.predict(x_test)
-
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 8)
- span = spans[0]
- self.assertEqual(span.name, "StandardScaler.transform")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[-1].context.span_id)
- span = spans[1]
- self.assertEqual(span.name, "Normalizer.transform")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[-1].context.span_id)
- span = spans[2]
- self.assertEqual(span.name, "PCA.transform")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[4].context.span_id)
- span = spans[3]
- self.assertEqual(span.name, "TruncatedSVD.transform")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[4].context.span_id)
- span = spans[4]
- self.assertEqual(span.name, "FeatureUnion.transform")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[-1].context.span_id)
- span = spans[5]
- self.assertEqual(span.name, "RandomForestClassifier.predict_proba")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[6].context.span_id)
- span = spans[6]
- self.assertEqual(span.name, "RandomForestClassifier.predict")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
- self.assertEqual(span.parent.span_id, spans[-1].context.span_id)
- span = spans[7]
- self.assertEqual(span.name, "Pipeline.predict")
- self.assertEqual(span.kind, SpanKind.INTERNAL)
-
- self.memory_exporter.clear()
-
- # uninstrument
- ski.uninstrument_estimator(estimator=model)
- x_test = random_input()
- model.predict(x_test)
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 0)
-
- def test_attrib_config(self):
- """Test that the attribute config makes spans on the decision trees."""
- model = pipeline()
- attrib_config = {RandomForestClassifier: ["estimators_"]}
- ski = SklearnInstrumentor(
- recurse_attribs=attrib_config,
- exclude_classes=[], # decision trees excluded by default
- )
- ski.instrument_estimator(estimator=model)
-
- x_test = random_input()
- model.predict(x_test)
-
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 8 + model.steps[-1][-1].n_estimators)
-
- self.memory_exporter.clear()
-
- ski.uninstrument_estimator(estimator=model)
- x_test = random_input()
- model.predict(x_test)
- spans = self.memory_exporter.get_finished_spans()
- self.assertEqual(len(spans), 0)
-
- def test_span_attributes(self):
- model = pipeline()
- attributes = {"model_name": "random_forest_model"}
- ski = SklearnInstrumentor()
- ski.instrument_estimator(estimator=model, attributes=attributes)
-
- x_test = random_input()
-
- model.predict(x_test)
-
- spans = self.memory_exporter.get_finished_spans()
- for span in spans:
- assert span.attributes["model_name"] == "random_forest_model"
diff --git a/opentelemetry-contrib-instrumentations/pyproject.toml b/opentelemetry-contrib-instrumentations/pyproject.toml
index 74c28f38cf..f83b9d5ee5 100644
--- a/opentelemetry-contrib-instrumentations/pyproject.toml
+++ b/opentelemetry-contrib-instrumentations/pyproject.toml
@@ -66,7 +66,6 @@ dependencies = [
"opentelemetry-instrumentation-redis==0.47b0.dev",
"opentelemetry-instrumentation-remoulade==0.47b0.dev",
"opentelemetry-instrumentation-requests==0.47b0.dev",
- "opentelemetry-instrumentation-sklearn==0.47b0.dev",
"opentelemetry-instrumentation-sqlalchemy==0.47b0.dev",
"opentelemetry-instrumentation-sqlite3==0.47b0.dev",
"opentelemetry-instrumentation-starlette==0.47b0.dev",
diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py
index 3dfd97e0b2..3f4c78862f 100644
--- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py
+++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py
@@ -152,10 +152,6 @@
"library": "requests ~= 2.0",
"instrumentation": "opentelemetry-instrumentation-requests==0.47b0.dev",
},
- {
- "library": "scikit-learn ~= 0.24.0",
- "instrumentation": "opentelemetry-instrumentation-sklearn==0.47b0.dev",
- },
{
"library": "sqlalchemy",
"instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.47b0.dev",
diff --git a/tox.ini b/tox.ini
index 529e2a76c0..aac890ed7c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -273,10 +273,6 @@ envlist =
pypy3-test-instrumentation-celery
lint-instrumentation-celery
- ; opentelemetry-instrumentation-sklearn
- py3{8}-test-instrumentation-sklearn
- lint-instrumentation-sklearn
-
; opentelemetry-instrumentation-system-metrics
py3{8,9,10,11,12}-test-instrumentation-system-metrics
pypy3-test-instrumentation-system-metrics
@@ -701,12 +697,6 @@ commands_pre =
prometheus: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils
prometheus: pip install -r {toxinidir}/exporter/opentelemetry-exporter-prometheus-remote-write/test-requirements.txt
- sklearn: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api
- sklearn: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions
- sklearn: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk
- sklearn: pip install opentelemetry-test-utils@{env:CORE_REPO}\#egg=opentelemetry-test-utils&subdirectory=tests/opentelemetry-test-utils
- sklearn: pip install -r {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/test-requirements.txt
-
sqlalchemy: pip install opentelemetry-api@{env:CORE_REPO}\#egg=opentelemetry-api&subdirectory=opentelemetry-api
sqlalchemy: pip install opentelemetry-semantic-conventions@{env:CORE_REPO}\#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions
sqlalchemy: pip install opentelemetry-sdk@{env:CORE_REPO}\#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk
@@ -1021,12 +1011,6 @@ commands =
lint-instrumentation-requests: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-requests
lint-instrumentation-requests: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-requests"
- test-instrumentation-sklearn: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn/tests {posargs}
- lint-instrumentation-sklearn: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn
- lint-instrumentation-sklearn: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn
- lint-instrumentation-sklearn: flake8 --config {toxinidir}/.flake8 {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn
- lint-instrumentation-sklearn: sh -c "cd instrumentation && pylint --rcfile ../.pylintrc opentelemetry-instrumentation-sklearn"
-
test-instrumentation-sqlalchemy: pytest {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy/tests {posargs}
lint-instrumentation-sqlalchemy: black --diff --check --config {toxinidir}/pyproject.toml {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy
lint-instrumentation-sqlalchemy: isort --diff --check-only --settings-path {toxinidir}/.isort.cfg {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy