From 3ae3acdefc7f4bfbf5696b1b6848d340fc521888 Mon Sep 17 00:00:00 2001 From: manvi Date: Mon, 4 May 2026 18:16:09 +0530 Subject: [PATCH 1/7] fix: batch pip installs in bootstrap for faster installs --- CHANGELOG.md | 3 ++- .../instrumentation/bootstrap.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6448b92374..1957279f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4438)) ### Fixed - +- `opentelemetry-instrumentation`: Batch all packages into a single `pip install` call in `bootstrap.py` to improve performance + ([#4484](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4484)) - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index 2959789b7c..9a0f4d0c0f 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -31,24 +31,24 @@ def _syscall(func): - def wrapper(package=None): + def wrapper(packages=None): try: - if package: - return func(package) + if packages: + return func(packages) return func() except SubprocessError as exp: cmd = getattr(exp, "cmd", None) if cmd: msg = f'Error calling system command "{" ".join(cmd)}"' - if package: - msg = f'{msg} for package "{package}"' + if packages: + msg = f'{msg} for package "{packages}"' raise RuntimeError(msg) return wrapper @_syscall -def _sys_pip_install(package): +def _sys_pip_install(packages): # explicit upgrade strategy to override potential pip config try: check_call( @@ -60,7 +60,7 @@ def _sys_pip_install(package): "-U", "--upgrade-strategy", "only-if-needed", - package, + *packages, ] ) except CalledProcessError as error: @@ -123,8 +123,9 @@ def _run_requirements(default_instrumentations, libraries): def _run_install(default_instrumentations, libraries): - for lib in _find_installed_libraries(default_instrumentations, libraries): - _sys_pip_install(lib) + libs = list(_find_installed_libraries(default_instrumentations, libraries)) + if libs: + _sys_pip_install(libs) _pip_check(libraries) From f039f1e1b747052ce3aa95ad07da52d5d95388d0 Mon Sep 17 00:00:00 2001 From: manvi Date: Tue, 5 May 2026 10:50:11 +0530 Subject: [PATCH 2/7] update changelog with PR link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1957279f53..08da809a27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `opentelemetry-instrumentation`: Batch all packages into a single `pip install` call in `bootstrap.py` to improve performance - ([#4484](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/4484)) + ([#4530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4530)) - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x From 4316b4e0cabac08813021275c1f8cd0d2af9ba91 Mon Sep 17 00:00:00 2001 From: manvi Date: Tue, 5 May 2026 21:51:04 +0530 Subject: [PATCH 3/7] fix: add --no-upgrade flag to skip -U in pip install --- .../instrumentation/bootstrap.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index 9a0f4d0c0f..7b55f4c4a3 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -31,10 +31,10 @@ def _syscall(func): - def wrapper(packages=None): + def wrapper(packages=None, upgrade=True): try: if packages: - return func(packages) + return func(packages, upgrade) return func() except SubprocessError as exp: cmd = getattr(exp, "cmd", None) @@ -48,21 +48,14 @@ def wrapper(packages=None): @_syscall -def _sys_pip_install(packages): +def _sys_pip_install(packages, upgrade=True): # explicit upgrade strategy to override potential pip config try: - check_call( - [ - sys.executable, - "-m", - "pip", - "install", - "-U", - "--upgrade-strategy", - "only-if-needed", - *packages, - ] - ) + cmd = [sys.executable, "-m", "pip", "install"] + if upgrade: + cmd += ["-U", "--upgrade-strategy", "only-if-needed"] + cmd += [*packages] + check_call(cmd) except CalledProcessError as error: print(error) @@ -122,10 +115,10 @@ def _run_requirements(default_instrumentations, libraries): ) -def _run_install(default_instrumentations, libraries): +def _run_install(default_instrumentations, libraries, upgrade=True): libs = list(_find_installed_libraries(default_instrumentations, libraries)) if libs: - _sys_pip_install(libs) + _sys_pip_install(libs, upgrade=upgrade) _pip_check(libraries) @@ -160,6 +153,11 @@ def run( be piped and appended to a requirements.txt file. """, ) + parser.add_argument( + "--no-upgrade", + action="store_true", + help="Do not upgrade packages with -U flag during install", + ) args = parser.parse_args() if libraries is None: @@ -168,8 +166,9 @@ def run( if default_instrumentations is None: default_instrumentations = gen_default_instrumentations - cmd = { - action_install: _run_install, - action_requirements: _run_requirements, - }[args.action] - cmd(default_instrumentations, libraries) + if args.action == action_install: + _run_install( + default_instrumentations, libraries, upgrade=not args.no_upgrade + ) + else: + _run_requirements(default_instrumentations, libraries) From c711a18da6c656532821dbdf2c661f962b66964f Mon Sep 17 00:00:00 2001 From: manvi Date: Tue, 12 May 2026 14:45:40 +0530 Subject: [PATCH 4/7] fix: address review comments - fix error message and remove no-upgrade flag --- CHANGELOG.md | 7 +-- .../instrumentation/bootstrap.py | 44 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08da809a27..a718ff7d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 > Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-python-contrib+path%3A**%2FCHANGELOG.md&type=code). ## Unreleased - +### Changed +- `opentelemetry-instrumentation`: Batch all packages into a single `pip install` call in `bootstrap.py` to improve performance + ([#4530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4530)) + ### Added - Add `BaggageLogProcessor` to `opentelemetry-processor-baggage` @@ -37,8 +40,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4438)) ### Fixed -- `opentelemetry-instrumentation`: Batch all packages into a single `pip install` call in `bootstrap.py` to improve performance - ([#4530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4530)) - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index 7b55f4c4a3..321b85f8f0 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -31,31 +31,40 @@ def _syscall(func): - def wrapper(packages=None, upgrade=True): + def wrapper(packages=None): try: - if packages: - return func(packages, upgrade) - return func() + if packages is None: + return func() + if not packages: + return None + return func(packages) except SubprocessError as exp: cmd = getattr(exp, "cmd", None) if cmd: msg = f'Error calling system command "{" ".join(cmd)}"' if packages: - msg = f'{msg} for package "{packages}"' + msg = f'{msg} for packages "{", ".join(packages)}"' raise RuntimeError(msg) return wrapper @_syscall -def _sys_pip_install(packages, upgrade=True): +def _sys_pip_install(packages): # explicit upgrade strategy to override potential pip config try: - cmd = [sys.executable, "-m", "pip", "install"] - if upgrade: - cmd += ["-U", "--upgrade-strategy", "only-if-needed"] - cmd += [*packages] - check_call(cmd) + check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "-U", + "--upgrade-strategy", + "only-if-needed", + *packages, + ] + ) except CalledProcessError as error: print(error) @@ -115,10 +124,10 @@ def _run_requirements(default_instrumentations, libraries): ) -def _run_install(default_instrumentations, libraries, upgrade=True): +def _run_install(default_instrumentations, libraries): libs = list(_find_installed_libraries(default_instrumentations, libraries)) if libs: - _sys_pip_install(libs, upgrade=upgrade) + _sys_pip_install(libs) _pip_check(libraries) @@ -153,11 +162,6 @@ def run( be piped and appended to a requirements.txt file. """, ) - parser.add_argument( - "--no-upgrade", - action="store_true", - help="Do not upgrade packages with -U flag during install", - ) args = parser.parse_args() if libraries is None: @@ -167,8 +171,6 @@ def run( default_instrumentations = gen_default_instrumentations if args.action == action_install: - _run_install( - default_instrumentations, libraries, upgrade=not args.no_upgrade - ) + _run_install(default_instrumentations, libraries) else: _run_requirements(default_instrumentations, libraries) From f9243bd88f017da940526b69c6cd9e73b8bbf611 Mon Sep 17 00:00:00 2001 From: manvi Date: Tue, 12 May 2026 15:05:45 +0530 Subject: [PATCH 5/7] fix: address review comments and update tests --- .../tests/test_bootstrap.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/opentelemetry-instrumentation/tests/test_bootstrap.py b/opentelemetry-instrumentation/tests/test_bootstrap.py index 5f4820d10e..65bb2a8504 100644 --- a/opentelemetry-instrumentation/tests/test_bootstrap.py +++ b/opentelemetry-instrumentation/tests/test_bootstrap.py @@ -5,7 +5,7 @@ from io import StringIO from random import sample from unittest import TestCase -from unittest.mock import call, patch +from unittest.mock import patch from opentelemetry.instrumentation import bootstrap from opentelemetry.instrumentation.bootstrap_gen import ( @@ -31,7 +31,6 @@ def setUpClass(cls): [lib["instrumentation"] for lib in libraries], 0.6 ) - # treat 50% of sampled packages as pre-installed cls.installed_instrumentations = sample_packages( cls.installed_libraries, 0.5 ) @@ -78,9 +77,8 @@ def test_run_cmd_print(self): def test_run_cmd_install(self): self.pkg_patcher.start() bootstrap.run() - self.mock_pip_install.assert_has_calls( - [call(i) for i in self.installed_libraries], - any_order=True, + self.mock_pip_install.assert_called_once_with( + self.installed_libraries, ) self.mock_pip_check.assert_called_once() self.pkg_patcher.stop() @@ -88,9 +86,8 @@ def test_run_cmd_install(self): @patch("sys.argv", ["bootstrap", "-a", "install"]) def test_can_override_available_libraries(self): bootstrap.run(libraries=[]) - self.mock_pip_install.assert_has_calls( - [call(i) for i in default_instrumentations], - any_order=True, + self.mock_pip_install.assert_called_once_with( + list(default_instrumentations), ) self.mock_pip_check.assert_called_once() @@ -101,8 +98,5 @@ def test_can_override_available_default_instrumentations(self): return_value=True, ): bootstrap.run(default_instrumentations=[]) - self.mock_pip_install.assert_has_calls( - [call(i) for i in self.installed_libraries], - any_order=True, - ) + self.mock_pip_install.assert_called_once() self.mock_pip_check.assert_called_once() From 3b4970988be45f35414ad18b6f3ec8f9bacf477c Mon Sep 17 00:00:00 2001 From: manvi Date: Fri, 15 May 2026 14:47:03 +0530 Subject: [PATCH 6/7] fix: add towncrier changelog fragment --- .changelog/4530.changed | Bin 0 -> 244 bytes CHANGELOG.md | 4 ---- 2 files changed, 4 deletions(-) create mode 100644 .changelog/4530.changed diff --git a/.changelog/4530.changed b/.changelog/4530.changed new file mode 100644 index 0000000000000000000000000000000000000000..2fbf3f4c87ec06040dbab437eb41f55083a46971 GIT binary patch literal 244 zcmX|*Q4Yc|3`2dCUSJ%6PfoH@jjdv=)mD&pdDu%U_Te^hY$v~OucWL&iwf@yUZ7~z zu1*nttKGZurt%=-TGZ<*r(0!$CTdT@%%7=(p1dPz8;m6tT&8g_){k|csUv6_+xAZh mXJu-qJO0GHUF7jxz|N#4M{dkqp7dd> Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-python-contrib+path%3A**%2FCHANGELOG.md&type=code). ## Unreleased -### Changed -- `opentelemetry-instrumentation`: Batch all packages into a single `pip install` call in `bootstrap.py` to improve performance - ([#4530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4530)) - ### Added - Add `BaggageLogProcessor` to `opentelemetry-processor-baggage` From 3338c38d1e24c3ff3e525f2ad83e6480cc0c4fbc Mon Sep 17 00:00:00 2001 From: manvi Date: Fri, 15 May 2026 14:56:11 +0530 Subject: [PATCH 7/7] fix: restore CHANGELOG.md to original state --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cd0bd7137..3473b1baa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased + ### Added - Add `BaggageLogProcessor` to `opentelemetry-processor-baggage` @@ -46,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#4438](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4438)) ### Fixed + - `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x ([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461)) - `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x