@@ -757,12 +757,109 @@ def test_is_docket_available(self):
757757
758758 assert is_docket_available () is True
759759
760+ def test_is_docket_available_false_when_pydocket_too_old (self , monkeypatch ):
761+ """``is_docket_available()`` must treat pre-0.18.0 pydocket as unavailable.
762+
763+ Older pydocket versions (e.g. 0.16.x, pulled in transitively by
764+ packages like prefect) import cleanly but lack the APIs fastmcp
765+ uses (``docket.dependencies.current_execution``, etc.). Without a
766+ version floor, the check would report available and then crash at
767+ runtime. Simulate by forcing ``importlib.metadata`` to report an
768+ old version.
769+ """
770+ import importlib .metadata
771+
772+ from fastmcp .server import dependencies
773+
774+ original_version = importlib .metadata .version
775+
776+ def fake_version (name : str ) -> str :
777+ if name == "pydocket" :
778+ return "0.16.6"
779+ return original_version (name )
780+
781+ monkeypatch .setattr (dependencies , "_DOCKET_AVAILABLE" , None )
782+ monkeypatch .setattr (importlib .metadata , "version" , fake_version )
783+
784+ assert dependencies .is_docket_available () is False
785+ # The wrapper that actually failed in #3803 must now return None
786+ # instead of raising ImportError on the inner import.
787+ assert dependencies .get_task_context () is None
788+
789+ def test_is_docket_available_false_when_pydocket_not_installed (self , monkeypatch ):
790+ """``is_docket_available()`` returns False when pydocket is absent."""
791+ import importlib .metadata
792+
793+ from fastmcp .server import dependencies
794+
795+ original_version = importlib .metadata .version
796+
797+ def fake_version (name : str ) -> str :
798+ if name == "pydocket" :
799+ raise importlib .metadata .PackageNotFoundError (name )
800+ return original_version (name )
801+
802+ monkeypatch .setattr (dependencies , "_DOCKET_AVAILABLE" , None )
803+ monkeypatch .setattr (importlib .metadata , "version" , fake_version )
804+
805+ assert dependencies .is_docket_available () is False
806+
807+ def test_is_docket_available_false_when_import_broken (self , monkeypatch ):
808+ """Metadata says installed but ``import docket`` fails — treat as unavailable.
809+
810+ Catches the broken/partial-install case where ``importlib.metadata``
811+ still reports a usable version but the package itself isn't actually
812+ importable (corrupted wheel, sys.path weirdness, etc.). Without the
813+ import probe, fastmcp would later crash on its first ``from docket
814+ ...`` instead of falling back gracefully.
815+ """
816+ import builtins
817+
818+ from fastmcp .server import dependencies
819+
820+ original_import = builtins .__import__
821+
822+ def fake_import (name , * args , ** kwargs ):
823+ if name == "docket" or name .startswith ("docket." ):
824+ raise ImportError ("simulated broken docket install" )
825+ return original_import (name , * args , ** kwargs )
826+
827+ monkeypatch .setattr (dependencies , "_DOCKET_AVAILABLE" , None )
828+ monkeypatch .setattr (builtins , "__import__" , fake_import )
829+
830+ assert dependencies .is_docket_available () is False
831+
760832 def test_require_docket_passes_when_installed (self ):
761833 """Test require_docket doesn't raise when docket is installed."""
762834 from fastmcp .server .dependencies import require_docket
763835
764836 require_docket ("test feature" )
765837
838+ def test_require_docket_error_mentions_version_when_too_old (self , monkeypatch ):
839+ """``require_docket()`` distinguishes "missing" from "too old".
840+
841+ When pydocket is installed but pinned below the floor, the install
842+ instructions in the error must point at upgrading pydocket — not at
843+ installing the ``tasks`` extra (which the resolver will treat as a
844+ no-op as long as the lower pin is held by another package).
845+ """
846+ import importlib .metadata
847+
848+ from fastmcp .server import dependencies
849+
850+ original_version = importlib .metadata .version
851+
852+ def fake_version (name : str ) -> str :
853+ if name == "pydocket" :
854+ return "0.16.6"
855+ return original_version (name )
856+
857+ monkeypatch .setattr (dependencies , "_DOCKET_AVAILABLE" , None )
858+ monkeypatch .setattr (importlib .metadata , "version" , fake_version )
859+
860+ with pytest .raises (ImportError , match = "pydocket 0.16.6 is installed" ):
861+ dependencies .require_docket ("CurrentDocket()" )
862+
766863 def test_dependency_class_exists (self ):
767864 """Test Dependency and Depends are importable from fastmcp."""
768865 from fastmcp .dependencies import Dependency , Depends
0 commit comments