@@ -399,6 +399,11 @@ def register_operations(self) -> Dict[str, List[str]]:
399399_TEST_STREAM_QUERY_SCHEMAS = [
400400 _TEST_AGENT_ENGINE_STREAM_QUERY_SCHEMA ,
401401]
402+ _TEST_PACKAGE_DISTRIBUTIONS = {
403+ "requests" : ["requests" ],
404+ "cloudpickle" : ["cloudpickle" ],
405+ "pydantic" : ["pydantic" ],
406+ }
402407
403408
404409def _create_empty_fake_package (package_name : str ) -> str :
@@ -507,6 +512,16 @@ def importlib_metadata_version_mock():
507512 with mock .patch .object (
508513 importlib .metadata , "version"
509514 ) as importlib_metadata_version_mock :
515+
516+ def get_version (pkg ):
517+ versions = {
518+ "requests" : "2.0.0" ,
519+ "cloudpickle" : "3.0.0" ,
520+ "pydantic" : "1.11.1" ,
521+ }
522+ return versions .get (pkg , "unknown" )
523+
524+ importlib_metadata_version_mock .side_effect = get_version
510525 yield importlib_metadata_version_mock
511526
512527
@@ -616,6 +631,14 @@ def unregister_api_methods_mock():
616631 yield unregister_api_methods_mock
617632
618633
634+ def create_fake_object_with_module (module_name ):
635+ class FakeObject :
636+ pass
637+
638+ FakeObject .__module__ = module_name
639+ return FakeObject ()
640+
641+
619642class InvalidCapitalizeEngineWithoutQuerySelf :
620643 """A sample Agent Engine with an invalid query method."""
621644
@@ -2519,3 +2542,69 @@ def test_compare_requirements_with_required_packages(self):
25192542 "missing" : set (),
25202543 },
25212544 }
2545+
2546+ @pytest .mark .usefixtures ("importlib_metadata_version_mock" )
2547+ def test_scan_simple_object (self ):
2548+ """Test scanning an object importing a known third-party package."""
2549+ fake_obj = create_fake_object_with_module ("requests" )
2550+ requirements = _utils .scan_requirements (
2551+ fake_obj ,
2552+ package_distributions = _TEST_PACKAGE_DISTRIBUTIONS ,
2553+ )
2554+ assert requirements == {
2555+ "cloudpickle" : "3.0.0" ,
2556+ "pydantic" : "1.11.1" ,
2557+ "requests" : "2.0.0" ,
2558+ }
2559+
2560+ @pytest .mark .usefixtures ("importlib_metadata_version_mock" )
2561+ def test_scan_object_with_stdlib_module (self ):
2562+ """Test that stdlib modules are ignored by default."""
2563+ fake_obj_stdlib = create_fake_object_with_module ("json" )
2564+ requirements = _utils .scan_requirements (
2565+ fake_obj_stdlib ,
2566+ package_distributions = _TEST_PACKAGE_DISTRIBUTIONS ,
2567+ )
2568+ # Requirements should not contain 'json',
2569+ # because 'json' is a stdlib module.
2570+ assert requirements == {
2571+ "cloudpickle" : "3.0.0" ,
2572+ "pydantic" : "1.11.1" ,
2573+ }
2574+
2575+ @pytest .mark .usefixtures ("importlib_metadata_version_mock" )
2576+ def test_scan_with_default_ignore_modules (self , monkeypatch ):
2577+ """Test implicitly ignoring a module."""
2578+ fake_obj = create_fake_object_with_module ("requests" )
2579+ original_base = _utils ._BASE_MODULES
2580+ monkeypatch .setattr (
2581+ _utils ,
2582+ "_BASE_MODULES" ,
2583+ set (original_base ) | {"requests" },
2584+ )
2585+ requirements = _utils .scan_requirements (
2586+ fake_obj ,
2587+ package_distributions = _TEST_PACKAGE_DISTRIBUTIONS ,
2588+ )
2589+ # Requirements should not contain 'requests',
2590+ # because 'requests' is implicitly ignored in `_BASE_MODULES`.
2591+ assert requirements == {
2592+ "cloudpickle" : "3.0.0" ,
2593+ "pydantic" : "1.11.1" ,
2594+ }
2595+
2596+ @pytest .mark .usefixtures ("importlib_metadata_version_mock" )
2597+ def test_scan_with_explicit_ignore_modules (self ):
2598+ """Test explicitly ignoring a module."""
2599+ fake_obj = create_fake_object_with_module ("requests" )
2600+ requirements = _utils .scan_requirements (
2601+ fake_obj ,
2602+ ignore_modules = ["requests" ],
2603+ package_distributions = _TEST_PACKAGE_DISTRIBUTIONS ,
2604+ )
2605+ # Requirements should not contain 'requests',
2606+ # because 'requests' is explicitly ignored in `ignore_modules`.
2607+ assert requirements == {
2608+ "cloudpickle" : "3.0.0" ,
2609+ "pydantic" : "1.11.1" ,
2610+ }
0 commit comments