1717from unittest .mock import Mock , call , patch
1818
1919from opentelemetry .instrumentation .auto_instrumentation import _load
20+ from opentelemetry .instrumentation .dependencies import (
21+ DependencyConflict ,
22+ )
2023from opentelemetry .instrumentation .environment_variables import (
2124 OTEL_PYTHON_CONFIGURATOR ,
2225 OTEL_PYTHON_DISABLED_INSTRUMENTATIONS ,
@@ -210,7 +213,7 @@ def test_load_distro_error(self, iter_mock, isinstance_mock):
210213 @patch (
211214 "opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
212215 )
213- def test_load_instrumentors (self , iter_mock , dep_mock ):
216+ def test_load_instrumentors (self , iter_mock , mock_dep ):
214217 # Mock opentelemetry_pre_instrument entry points
215218 # pylint: disable=too-many-locals
216219 pre_ep_mock1 = Mock ()
@@ -256,7 +259,7 @@ def test_load_instrumentors(self, iter_mock, dep_mock):
256259 (post_ep_mock1 , post_ep_mock2 ),
257260 ]
258261 # No dependency conflict
259- dep_mock .return_value = None
262+ mock_dep .return_value = None
260263 _load ._load_instrumentors (distro_mock )
261264 # All opentelemetry_pre_instrument entry points should be loaded
262265 pre_mock1 .assert_called_once ()
@@ -281,43 +284,67 @@ def test_load_instrumentors(self, iter_mock, dep_mock):
281284 @patch (
282285 "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
283286 )
287+ @patch ("opentelemetry.instrumentation.auto_instrumentation._load._logger" )
284288 @patch (
285289 "opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
286290 )
287- def test_load_instrumentors_dep_conflict (self , iter_mock , dep_mock ): # pylint: disable=no-self-use
291+ def test_load_instrumentors_dep_conflict (
292+ self , iter_mock , mock_logger , mock_dep
293+ ): # pylint: disable=no-self-use
288294 ep_mock1 = Mock ()
289- ep_mock1 .name = "instr1"
295+ ep_mock1 .name = "instr1" # disabled
290296
291297 ep_mock2 = Mock ()
292298 ep_mock2 .name = "instr2"
293299
294300 ep_mock3 = Mock ()
295- ep_mock3 .name = "instr3"
301+ ep_mock3 .name = "instr3" # disabled
296302
297303 ep_mock4 = Mock ()
298- ep_mock4 .name = "instr4"
304+ ep_mock4 .name = "instr4" # dependency conflict
299305
300306 distro_mock = Mock ()
301307
302308 iter_mock .return_value = (ep_mock1 , ep_mock2 , ep_mock3 , ep_mock4 )
309+ print ((ep_mock1 , ep_mock2 , ep_mock3 , ep_mock4 ))
303310 # If a dependency conflict is raised, that instrumentation should not be loaded, but others still should.
304- dep_mock .side_effect = [None , "DependencyConflict" ]
311+ dependency_conflict = DependencyConflict ("1.2.3" , None )
312+ mock_dep .side_effect = [None , dependency_conflict ]
305313 _load ._load_instrumentors (distro_mock )
306314 distro_mock .load_instrumentor .assert_has_calls (
307315 [
308316 call (ep_mock2 , skip_dep_check = True ),
309317 ]
310318 )
311319 distro_mock .load_instrumentor .assert_called_once ()
320+ mock_logger .debug .assert_has_calls (
321+ [
322+ call (
323+ "Instrumentation skipped for library %s" ,
324+ ep_mock1 .name ,
325+ ),
326+ call ("Instrumented %s" , ep_mock2 .name ),
327+ call (
328+ "Instrumentation skipped for library %s" ,
329+ ep_mock3 .name ,
330+ ),
331+ call (
332+ "Skipping instrumentation %s: %s" ,
333+ ep_mock4 .name ,
334+ dependency_conflict ,
335+ ),
336+ ]
337+ )
312338
313339 @patch (
314340 "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
315341 )
342+ @patch ("opentelemetry.instrumentation.auto_instrumentation._load._logger" )
316343 @patch (
317344 "opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
318345 )
319346 def test_load_instrumentors_import_error_does_not_stop_everything (
320- self , iter_mock , dep_mock
347+ self , iter_mock , mock_logger , mock_dep
321348 ):
322349 ep_mock1 = Mock (name = "instr1" )
323350 ep_mock2 = Mock (name = "instr2" )
@@ -331,7 +358,7 @@ def test_load_instrumentors_import_error_does_not_stop_everything(
331358 (ep_mock1 , ep_mock2 ),
332359 (),
333360 ]
334- dep_mock .return_value = None
361+ mock_dep .return_value = None
335362
336363 _load ._load_instrumentors (distro_mock )
337364
@@ -342,14 +369,23 @@ def test_load_instrumentors_import_error_does_not_stop_everything(
342369 ]
343370 )
344371 self .assertEqual (distro_mock .load_instrumentor .call_count , 2 )
372+ mock_logger .exception .assert_any_call (
373+ "Importing of %s failed, skipping it" ,
374+ ep_mock1 .name ,
375+ )
376+
377+ mock_logger .debug .assert_any_call ("Instrumented %s" , ep_mock2 .name )
345378
346379 @patch (
347380 "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
348381 )
382+ @patch ("opentelemetry.instrumentation.auto_instrumentation._load._logger" )
349383 @patch (
350384 "opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
351385 )
352- def test_load_instrumentors_raises_exception (self , iter_mock , dep_mock ):
386+ def test_load_instrumentors_raises_exception (
387+ self , iter_mock , mock_logger , mock_dep
388+ ):
353389 ep_mock1 = Mock (name = "instr1" )
354390 ep_mock2 = Mock (name = "instr2" )
355391
@@ -362,7 +398,7 @@ def test_load_instrumentors_raises_exception(self, iter_mock, dep_mock):
362398 (ep_mock1 , ep_mock2 ),
363399 (),
364400 ]
365- dep_mock .return_value = None
401+ mock_dep .return_value = None
366402
367403 with self .assertRaises (ValueError ):
368404 _load ._load_instrumentors (distro_mock )
@@ -374,6 +410,51 @@ def test_load_instrumentors_raises_exception(self, iter_mock, dep_mock):
374410 )
375411 self .assertEqual (distro_mock .load_instrumentor .call_count , 1 )
376412
413+ @patch (
414+ "opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
415+ )
416+ @patch ("opentelemetry.instrumentation.auto_instrumentation._load._logger" )
417+ @patch (
418+ "opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
419+ )
420+ def test_load_instrumentors_module_not_found_error (
421+ self , iter_mock , mock_logger , mock_dep
422+ ):
423+ ep_mock1 = Mock ()
424+ ep_mock1 .name = "instr1"
425+
426+ ep_mock2 = Mock ()
427+ ep_mock2 .name = "instr2"
428+
429+ distro_mock = Mock ()
430+
431+ mock_dep .return_value = None
432+
433+ distro_mock .load_instrumentor .side_effect = [
434+ ModuleNotFoundError ("No module named 'fake_module'" ),
435+ None ,
436+ ]
437+
438+ iter_mock .side_effect = [(), (ep_mock1 , ep_mock2 ), ()]
439+
440+ _load ._load_instrumentors (distro_mock )
441+
442+ distro_mock .load_instrumentor .assert_has_calls (
443+ [
444+ call (ep_mock1 , skip_dep_check = True ),
445+ call (ep_mock2 , skip_dep_check = True ),
446+ ]
447+ )
448+ self .assertEqual (distro_mock .load_instrumentor .call_count , 2 )
449+
450+ mock_logger .debug .assert_any_call (
451+ "Skipping instrumentation %s: %s" ,
452+ "instr1" ,
453+ "No module named 'fake_module'" ,
454+ )
455+
456+ mock_logger .debug .assert_any_call ("Instrumented %s" , ep_mock2 .name )
457+
377458 def test_load_instrumentors_no_entry_point_mocks (self ):
378459 distro_mock = Mock ()
379460 _load ._load_instrumentors (distro_mock )
0 commit comments