Repository: wandb/wandb

Issue:
Installation Fails on python 3.12 windows 11 - [App]: 
### Current Behavior

I have tried to install the package via pip, as written in the site I wrote in the CLI:
``` pip install wandb```
everything almost goes through, but then crashes with error listed below:
```
Lib\site-packages\setuptools\build_meta.py", line 341, in run_setup
          exec(code, locals())
        File "<string>", line 25, in <module>
      ModuleNotFoundError: No module named 'imp'
```


### Expected Behavior

I wished it could finish the installation.

### Steps To Reproduce

_No response_

### Screenshots

_No response_

### Environment

OS: Windows 11

Browsers:

Version: python 3.12


### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py b/wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py
--- a/wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py
+++ b/wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py
@@ -223,9 +223,7 @@ def position_after_whitespace(body, start_position):
 def read_number(source, start, first_code):
     """Reads a number token from the source file, either a float
     or an int depending on whether a decimal point appears.
-
-    Int:   -?(0|[1-9][0-9]*)
-    Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)?"""
+    """
     code = first_code
     body = source.body
     position = start


Test patch:
diff --git a/tests/pytest_tests/system_tests/test_core/test_keras_full.py b/tests/pytest_tests/system_tests/test_core/test_keras_full.py
--- a/tests/pytest_tests/system_tests/test_core/test_keras_full.py
+++ b/tests/pytest_tests/system_tests/test_core/test_keras_full.py
@@ -3,11 +3,14 @@
 import os
 
 import pytest
-import tensorflow as tf
-from tensorflow.keras import backend as K  # noqa: N812
-from tensorflow.keras.layers import Dense, Flatten, Reshape
-from tensorflow.keras.models import Sequential
-from wandb.keras import WandbCallback
+
+pytest.importorskip("tensorflow")
+
+import tensorflow as tf  # noqa: E402
+from tensorflow.keras import backend as K  # noqa: N812, E402
+from tensorflow.keras.layers import Dense, Flatten, Reshape  # noqa: E402
+from tensorflow.keras.models import Sequential  # noqa: E402
+from wandb.keras import WandbCallback  # noqa: E402
 
 
 @pytest.fixture
diff --git a/tests/pytest_tests/system_tests/test_core/test_kfp.py b/tests/pytest_tests/system_tests/test_core/test_kfp.py
--- a/tests/pytest_tests/system_tests/test_core/test_kfp.py
+++ b/tests/pytest_tests/system_tests/test_core/test_kfp.py
@@ -1,162 +1,158 @@
-import inspect
-from importlib import reload
-from unittest.mock import patch
-
-import kfp
-import pytest
-import wandb
-from kfp.components import InputPath, OutputPath, create_component_from_func
-from kfp.components._structures import InputSpec, OutputSpec
-from wandb.integration.kfp import unpatch_kfp, wandb_log
+# TODO: the integration does not work with the latest version of kfp
 
-# todo: skip all file
-pytest.skip(
-    "KFP integration does not work with the latest version of KFP",
-    allow_module_level=True,
-)
+# import inspect
+# from importlib import reload
+# from unittest.mock import patch
 
+# import kfp
+# import pytest
+# import wandb
+# from kfp.components import InputPath, OutputPath, create_component_from_func
+# from kfp.components._structures import InputSpec, OutputSpec
+# from wandb.integration.kfp import unpatch_kfp, wandb_log
 
-def test_get_noop_decorator_if_patching_fails():
-    with patch("kfp.components._python_op.create_component_from_func", None):
-        reload(wandb.integration.kfp)
-        from wandb.integration.kfp import wandb_log
 
-        # Note: I would prefer to just check for a _noop flag, but I'm not sure how to do this
-        # given KFP's design.  This test is a compromise...
-        _noop_string = "NOTE: Because patching failed, this decorator is a no-op."
-        assert (
-            _noop_string in wandb_log.__doc__
-        ), "@wandb_log is not a no-op even though some functions were not able to be patched"
+# def test_get_noop_decorator_if_patching_fails():
+#     with patch("kfp.components._python_op.create_component_from_func", None):
+#         reload(wandb.integration.kfp)
+#         from wandb.integration.kfp import wandb_log
 
-    assert kfp.components is not None
+#         # Note: I would prefer to just check for a _noop flag, but I'm not sure how to do this
+#         # given KFP's design.  This test is a compromise...
+#         _noop_string = "NOTE: Because patching failed, this decorator is a no-op."
+#         assert (
+#             _noop_string in wandb_log.__doc__
+#         ), "@wandb_log is not a no-op even though some functions were not able to be patched"
 
+#     assert kfp.components is not None
 
-def test_noop_decorator_does_not_modify_inputs():
-    with patch("kfp.components._python_op", None):
-        reload(wandb.integration.kfp)
-        from wandb.integration.kfp import wandb_log
-
-        @wandb_log
-        def train_model(
-            x_train_path: InputPath("np_array"),  # noqa: F821
-            y_train_path: InputPath("np_array"),  # noqa: F821
-            model_path: OutputPath("sklearn_model"),  # noqa: F821
-        ):
-            import joblib
-            import numpy as np
-            from sklearn.ensemble import RandomForestClassifier
-
-            with open(x_train_path, "rb") as f:
-                x_train = np.load(f)
-
-            with open(y_train_path, "rb") as f:
-                y_train = np.load(f)
-
-            model = RandomForestClassifier()
-            model.fit(x_train, y_train)
-
-            joblib.dump(model, model_path)
-
-        assert train_model.__annotations__["x_train_path"].type == "np_array"
-        assert train_model.__annotations__["y_train_path"].type == "np_array"
-        assert train_model.__annotations__["model_path"].type == "sklearn_model"
-
-
-def test_correct_annotations_and_signature():
-    @wandb_log
-    def add(a: float, b: float) -> float:
-        return a + b
-
-    def add2(a: float, b: float) -> float:
-        return a + b
-
-    assert add.__annotations__["a"] is add2.__annotations__["a"] is float
-    assert add.__annotations__["b"] is add.__annotations__["b"] is float
-    assert add.__annotations__["return"] is add2.__annotations__["return"] is float
-    assert isinstance(add.__annotations__["mlpipeline_ui_metadata_path"], OutputPath)
-    assert hasattr(add, "__signature__"), "Signature was not applied"
-
-
-def test_decorator_does_not_modify_inputs():
-    @wandb_log
-    def train_model(
-        x_train_path: InputPath("np_array"),  # noqa: F821
-        y_train_path: InputPath("np_array"),  # noqa: F821
-        model_path: OutputPath("sklearn_model"),  # noqa: F821
-    ):
-        import joblib
-        import numpy as np
-        from sklearn.ensemble import RandomForestClassifier
-
-        with open(x_train_path, "rb") as f:
-            x_train = np.load(f)
-
-        with open(y_train_path, "rb") as f:
-            y_train = np.load(f)
-
-        model = RandomForestClassifier()
-        model.fit(x_train, y_train)
-
-        joblib.dump(model, model_path)
-
-    assert train_model.__annotations__["x_train_path"].type == "np_array"
-    assert train_model.__annotations__["y_train_path"].type == "np_array"
-    assert train_model.__annotations__["model_path"].type == "sklearn_model"
-
-
-def test_valid_created_component():
-    @wandb_log
-    def add(a: float, b: float) -> float:
-        return a + b
-
-    def add2(a: float, b: float) -> float:
-        return a + b
-
-    add = create_component_from_func(add)
-    add2 = create_component_from_func(add2)
-
-    add_task = add(1, 2)
-    add2_task = add2(1, 2)
-    add_task_spec = add_task.component_ref.spec
-    add2_task_spec = add2_task.component_ref.spec
-
-    assert add_task_spec.inputs == [InputSpec("a", "Float"), InputSpec("b", "Float")]
-    assert add_task_spec.outputs == [
-        OutputSpec("mlpipeline_ui_metadata", None),
-        OutputSpec("Output", "Float"),
-    ]
-    assert add2_task_spec.inputs == [InputSpec("a", "Float"), InputSpec("b", "Float")]
-    assert add2_task_spec.outputs == [OutputSpec("Output", "Float")]
-
-
-@pytest.mark.xfail(
-    reason="This test sometimes fails in CI (not sure why)", strict=False
-)
-def test_unpatching():
-    assert (
-        inspect.getmodule(kfp.components._python_op.create_component_from_func)
-        is wandb.integration.kfp.kfp_patch
-    )
-    assert (
-        inspect.getmodule(kfp.components._python_op._get_function_source_definition)
-        is wandb.integration.kfp.kfp_patch
-    )
-    assert (
-        inspect.getmodule(kfp.components._python_op.strip_type_hints)
-        is wandb.integration.kfp.kfp_patch
-    )
-
-    unpatch_kfp()
-
-    assert (
-        inspect.getmodule(kfp.components._python_op.create_component_from_func)
-        is kfp.components._python_op
-    )
-    assert (
-        inspect.getmodule(kfp.components._python_op._get_function_source_definition)
-        is kfp.components._python_op
-    )
-    assert (
-        inspect.getmodule(kfp.components._python_op.strip_type_hints)
-        is kfp.components._python_op
-    )
+
+# def test_noop_decorator_does_not_modify_inputs():
+#     with patch("kfp.components._python_op", None):
+#         reload(wandb.integration.kfp)
+#         from wandb.integration.kfp import wandb_log
+
+#         @wandb_log
+#         def train_model(
+#             x_train_path: InputPath("np_array"),  # noqa: F821
+#             y_train_path: InputPath("np_array"),  # noqa: F821
+#             model_path: OutputPath("sklearn_model"),  # noqa: F821
+#         ):
+#             import joblib
+#             import numpy as np
+#             from sklearn.ensemble import RandomForestClassifier
+
+#             with open(x_train_path, "rb") as f:
+#                 x_train = np.load(f)
+
+#             with open(y_train_path, "rb") as f:
+#                 y_train = np.load(f)
+
+#             model = RandomForestClassifier()
+#             model.fit(x_train, y_train)
+
+#             joblib.dump(model, model_path)
+
+#         assert train_model.__annotations__["x_train_path"].type == "np_array"
+#         assert train_model.__annotations__["y_train_path"].type == "np_array"
+#         assert train_model.__annotations__["model_path"].type == "sklearn_model"
+
+
+# def test_correct_annotations_and_signature():
+#     @wandb_log
+#     def add(a: float, b: float) -> float:
+#         return a + b
+
+#     def add2(a: float, b: float) -> float:
+#         return a + b
+
+#     assert add.__annotations__["a"] is add2.__annotations__["a"] is float
+#     assert add.__annotations__["b"] is add.__annotations__["b"] is float
+#     assert add.__annotations__["return"] is add2.__annotations__["return"] is float
+#     assert isinstance(add.__annotations__["mlpipeline_ui_metadata_path"], OutputPath)
+#     assert hasattr(add, "__signature__"), "Signature was not applied"
+
+
+# def test_decorator_does_not_modify_inputs():
+#     @wandb_log
+#     def train_model(
+#         x_train_path: InputPath("np_array"),  # noqa: F821
+#         y_train_path: InputPath("np_array"),  # noqa: F821
+#         model_path: OutputPath("sklearn_model"),  # noqa: F821
+#     ):
+#         import joblib
+#         import numpy as np
+#         from sklearn.ensemble import RandomForestClassifier
+
+#         with open(x_train_path, "rb") as f:
+#             x_train = np.load(f)
+
+#         with open(y_train_path, "rb") as f:
+#             y_train = np.load(f)
+
+#         model = RandomForestClassifier()
+#         model.fit(x_train, y_train)
+
+#         joblib.dump(model, model_path)
+
+#     assert train_model.__annotations__["x_train_path"].type == "np_array"
+#     assert train_model.__annotations__["y_train_path"].type == "np_array"
+#     assert train_model.__annotations__["model_path"].type == "sklearn_model"
+
+
+# def test_valid_created_component():
+#     @wandb_log
+#     def add(a: float, b: float) -> float:
+#         return a + b
+
+#     def add2(a: float, b: float) -> float:
+#         return a + b
+
+#     add = create_component_from_func(add)
+#     add2 = create_component_from_func(add2)
+
+#     add_task = add(1, 2)
+#     add2_task = add2(1, 2)
+#     add_task_spec = add_task.component_ref.spec
+#     add2_task_spec = add2_task.component_ref.spec
+
+#     assert add_task_spec.inputs == [InputSpec("a", "Float"), InputSpec("b", "Float")]
+#     assert add_task_spec.outputs == [
+#         OutputSpec("mlpipeline_ui_metadata", None),
+#         OutputSpec("Output", "Float"),
+#     ]
+#     assert add2_task_spec.inputs == [InputSpec("a", "Float"), InputSpec("b", "Float")]
+#     assert add2_task_spec.outputs == [OutputSpec("Output", "Float")]
+
+
+# @pytest.mark.xfail(
+#     reason="This test sometimes fails in CI (not sure why)", strict=False
+# )
+# def test_unpatching():
+#     assert (
+#         inspect.getmodule(kfp.components._python_op.create_component_from_func)
+#         is wandb.integration.kfp.kfp_patch
+#     )
+#     assert (
+#         inspect.getmodule(kfp.components._python_op._get_function_source_definition)
+#         is wandb.integration.kfp.kfp_patch
+#     )
+#     assert (
+#         inspect.getmodule(kfp.components._python_op.strip_type_hints)
+#         is wandb.integration.kfp.kfp_patch
+#     )
+
+#     unpatch_kfp()
+
+#     assert (
+#         inspect.getmodule(kfp.components._python_op.create_component_from_func)
+#         is kfp.components._python_op
+#     )
+#     assert (
+#         inspect.getmodule(kfp.components._python_op._get_function_source_definition)
+#         is kfp.components._python_op
+#     )
+#     assert (
+#         inspect.getmodule(kfp.components._python_op.strip_type_hints)
+#         is kfp.components._python_op
+#     )
diff --git a/tests/pytest_tests/system_tests/test_core/test_torch_full.py b/tests/pytest_tests/system_tests/test_core/test_torch_full.py
--- a/tests/pytest_tests/system_tests/test_core/test_torch_full.py
+++ b/tests/pytest_tests/system_tests/test_core/test_torch_full.py
@@ -156,6 +156,7 @@ def conv3x3(in_channels, out_channels, **kwargs):
 # TODO(jhr): does not work with --flake-finder
 @pytest.mark.xfail(reason="TODO: fix this test")
 def test_all_logging(relay_server, wandb_init):
+    pytest.importorskip("torch")
     n = 3
     with relay_server() as relay:
         run = wandb_init()
@@ -185,6 +186,7 @@ def test_all_logging(relay_server, wandb_init):
 
 
 def test_embedding_dict_watch(relay_server, wandb_init):
+    pytest.importorskip("torch")
     with relay_server() as relay:
         run = wandb_init()
         model = EmbModelWrapper()
@@ -206,6 +208,7 @@ def test_embedding_dict_watch(relay_server, wandb_init):
 
 @pytest.mark.timeout(120)
 def test_sequence_net(wandb_init):
+    pytest.importorskip("torch")
     run = wandb_init()
     net = Sequence()
     graph = wandb.watch(net, log_graph=True)[0]
@@ -222,6 +225,7 @@ def test_sequence_net(wandb_init):
 
 
 def test_multi_net(wandb_init):
+    pytest.importorskip("torch")
     run = wandb_init()
     net1 = ConvNet()
     net2 = ConvNet()
diff --git a/tests/pytest_tests/system_tests/test_core/test_wandb_tensorflow.py b/tests/pytest_tests/system_tests/test_core/test_wandb_tensorflow.py
--- a/tests/pytest_tests/system_tests/test_core/test_wandb_tensorflow.py
+++ b/tests/pytest_tests/system_tests/test_core/test_wandb_tensorflow.py
@@ -2,11 +2,16 @@
 import platform
 
 import pytest
-import tensorboard.summary.v1 as tb_summary
-import tensorflow as tf
-import wandb
-from tensorboard.plugins.pr_curve import summary as pr_curve_plugin_summary
-from wandb.errors import term
+
+pytest.importorskip("tensorflow")
+
+import tensorboard.summary.v1 as tb_summary  # noqa: E402
+import tensorflow as tf  # noqa: E402
+import wandb  # noqa: E402
+from tensorboard.plugins.pr_curve import (  # noqa: E402
+    summary as pr_curve_plugin_summary,
+)
+from wandb.errors import term  # noqa: E402
 
 PR_CURVE_PANEL_CONFIG = {
     "panel_type": "Vega2",
diff --git a/tests/pytest_tests/unit_tests/test_data_types.py b/tests/pytest_tests/unit_tests/test_data_types.py
--- a/tests/pytest_tests/unit_tests/test_data_types.py
+++ b/tests/pytest_tests/unit_tests/test_data_types.py
@@ -13,7 +13,6 @@
 import wandb
 from bokeh.plotting import figure
 from PIL import Image
-from torchvision.io import read_image
 from wandb import data_types
 from wandb.sdk.data_types.base_types.media import _numpy_arrays_to_lists
 
@@ -1529,6 +1528,9 @@ def test_numpy_arrays_to_list():
 
 
 def test_log_uint8_image():
+    pytest.importorskip("torchvision")
+    from torchvision.io import read_image
+
     with open("temp.png", "wb") as temp:
         # Create and save image
         imarray = np.random.rand(100, 100, 3) * 255
diff --git a/tests/pytest_tests/unit_tests/test_keras.py b/tests/pytest_tests/unit_tests/test_keras.py
--- a/tests/pytest_tests/unit_tests/test_keras.py
+++ b/tests/pytest_tests/unit_tests/test_keras.py
@@ -2,10 +2,24 @@
 
 import pytest
 import wandb
-from tensorflow.keras import backend as K  # noqa: N812
-from tensorflow.keras.layers import LSTM, Concatenate, Dense, Embedding, Flatten, Input
-from tensorflow.keras.models import Model, Sequential
-from wandb.keras import WandbCallback
+
+try:
+    import tensorflow
+except ImportError:
+    pytest.skip("skipping while tf is unavailable", allow_module_level=True)
+
+keras = tensorflow.keras
+from keras import backend as K  # noqa: N812, E402
+from keras.layers import (  # noqa: E402
+    LSTM,
+    Concatenate,
+    Dense,
+    Embedding,
+    Flatten,
+    Input,
+)
+from keras.models import Model, Sequential  # noqa: E402
+from wandb.keras import WandbCallback  # noqa: E402
 
 
 def test_no_init():
diff --git a/tests/pytest_tests/unit_tests/test_launch/test_agent/test_agent.py b/tests/pytest_tests/unit_tests/test_launch/test_agent/test_agent.py
--- a/tests/pytest_tests/unit_tests/test_launch/test_agent/test_agent.py
+++ b/tests/pytest_tests/unit_tests/test_launch/test_agent/test_agent.py
@@ -284,6 +284,7 @@ async def test_thread_finish_no_fail(mocker, clean_agent):
 
 
 @pytest.mark.asyncio
+@pytest.mark.xfail(reason="`assert_called_once` vs `assert <...>.called_once`")
 async def test_thread_finish_sweep_fail(mocker, clean_agent):
     _setup_thread_finish(mocker)
     mock_config = {
@@ -300,11 +301,12 @@ async def test_thread_finish_sweep_fail(mocker, clean_agent):
     agent._jobs = {"thread_1": job}
     await agent.finish_thread_id("thread_1")
     assert len(agent._jobs) == 0
-    assert mocker.api.fail_run_queue_item.called_once
-    assert mock_saver.save_contents.called_once
+    mocker.api.fail_run_queue_item.assert_called_once()
+    mock_saver.save_contents.assert_called_once()
 
 
 @pytest.mark.asyncio
+@pytest.mark.xfail(reason="`assert_called_once` vs `assert <...>.called_once`")
 async def test_thread_finish_run_fail(mocker, clean_agent):
     _setup_thread_finish(mocker)
     mock_config = {
@@ -321,11 +323,12 @@ async def test_thread_finish_run_fail(mocker, clean_agent):
     agent._jobs = {"thread_1": job}
     await agent.finish_thread_id("thread_1")
     assert len(agent._jobs) == 0
-    assert mocker.api.fail_run_queue_item.called_once
-    assert mock_saver.save_contents.called_once
+    mocker.api.fail_run_queue_item.assert_called_once()
+    mock_saver.save_contents.assert_called_once()
 
 
 @pytest.mark.asyncio
+@pytest.mark.xfail(reason="`assert_called_once` vs `assert <...>.called_once`")
 async def test_thread_finish_run_fail_start(mocker, clean_agent):
     _setup_thread_finish(mocker)
     mock_config = {
@@ -341,8 +344,8 @@ async def test_thread_finish_run_fail_start(mocker, clean_agent):
     agent._jobs_lock = MagicMock()
     await agent.finish_thread_id("thread_1")
     assert len(agent._jobs) == 0
-    assert mocker.api.fail_run_queue_item.called_once
-    assert mock_saver.save_contents.called_once
+    mocker.api.fail_run_queue_item.assert_called_once()
+    mock_saver.save_contents.assert_called_once()
 
 
 @pytest.mark.asyncio
diff --git a/tests/pytest_tests/unit_tests/test_launch/test_environment/test_aws.py b/tests/pytest_tests/unit_tests/test_launch/test_environment/test_aws.py
--- a/tests/pytest_tests/unit_tests/test_launch/test_environment/test_aws.py
+++ b/tests/pytest_tests/unit_tests/test_launch/test_environment/test_aws.py
@@ -84,6 +84,7 @@ async def _mock_get_session(*args, **kwargs):
 
 
 @pytest.mark.asyncio
+@pytest.mark.xfail(reason="`assert_has_calls` vs `assert <...>.has_calls`")
 async def test_upload_directory(mocker):
     """Test that we issue the correct api calls to upload files to s3."""
     """
@@ -142,7 +143,7 @@ async def _mock_get_session(*args, **kwargs):
     )
     await environment.upload_dir(source_dir, "s3://bucket/key")
     assert client.upload_file.call_count == 8
-    assert client.upload_file.has_calls(
+    client.upload_file.assert_has_calls(
         [
             mocker.call(
                 os.path.join(source_dir, "Dockerfile"),
diff --git a/tests/pytest_tests/unit_tests/test_launch/test_runner/test_safe_watch.py b/tests/pytest_tests/unit_tests/test_launch/test_runner/test_safe_watch.py
--- a/tests/pytest_tests/unit_tests/test_launch/test_runner/test_safe_watch.py
+++ b/tests/pytest_tests/unit_tests/test_launch/test_runner/test_safe_watch.py
@@ -49,6 +49,7 @@ def event_factory(resource_version):
 # loop after stop() is called.
 @pytest.mark.timeout(60)
 @pytest.mark.asyncio
+@pytest.mark.xfail(reason="This test is flaky.")
 async def test_safe_watch():
     """Test that safewatch wraps properly.
 
diff --git a/tests/pytest_tests/unit_tests/test_lib/test_redir.py b/tests/pytest_tests/unit_tests/test_lib/test_redir.py
--- a/tests/pytest_tests/unit_tests/test_lib/test_redir.py
+++ b/tests/pytest_tests/unit_tests/test_lib/test_redir.py
@@ -183,6 +183,7 @@ def test_numpy(cls, capfd):
 @pytest.mark.timeout(5)
 def test_print_torch_model(cls, capfd):
     # https://github.com/wandb/wandb/issues/2097
+    pytest.importorskip("torch")
     import torch
 
     with capfd.disabled():
diff --git a/tests/pytest_tests/unit_tests/test_metaflow.py b/tests/pytest_tests/unit_tests/test_metaflow.py
--- a/tests/pytest_tests/unit_tests/test_metaflow.py
+++ b/tests/pytest_tests/unit_tests/test_metaflow.py
@@ -6,10 +6,15 @@
 if platform.system() == "Windows":
     pytest.skip("metaflow does not support native Windows", allow_module_level=True)
 
-import pandas as pd
-from metaflow import FlowSpec, step
-from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier
-from wandb.integration.metaflow import wandb_log, wandb_track, wandb_use
+pytest.importorskip("metaflow")
+
+import pandas as pd  # noqa: E402
+from metaflow import FlowSpec, step  # noqa: E402
+from sklearn.ensemble import (  # noqa: E402
+    GradientBoostingClassifier,
+    RandomForestClassifier,
+)
+from wandb.integration.metaflow import wandb_log, wandb_track, wandb_use  # noqa: E402
 
 try:
     import torch
@@ -161,6 +166,8 @@ def test_track_sklearn_model():
 
 
 def test_track_pytorch_model():
+    pytest.importorskip("torch")
+
     class Net(nn.Module):
         def __init__(self):
             super().__init__()
@@ -237,6 +244,8 @@ def test_use_datasets():
 
 
 def test_use_models():
+    pytest.importorskip("torch")
+
     rf_clf = RandomForestClassifier()
     assert wandb_use("rf_clf", rf_clf, testing=True, models=True) == "models"
     assert wandb_use("rf_clf", rf_clf, testing=True, models=False) is None
diff --git a/tests/pytest_tests/unit_tests/test_profiler.py b/tests/pytest_tests/unit_tests/test_profiler.py
--- a/tests/pytest_tests/unit_tests/test_profiler.py
+++ b/tests/pytest_tests/unit_tests/test_profiler.py
@@ -4,6 +4,7 @@
 
 
 def test_profiler_without_init():
+    pytest.importorskip("torch")
     import torch
 
     with pytest.raises(UsageError) as e_info:
diff --git a/tests/pytest_tests/unit_tests/test_ssl.py b/tests/pytest_tests/unit_tests/test_ssl.py
--- a/tests/pytest_tests/unit_tests/test_ssl.py
+++ b/tests/pytest_tests/unit_tests/test_ssl.py
@@ -45,12 +45,11 @@ def do_GET(self):  # noqa: N802
             self.wfile.write(b"Hello, world!")
 
     httpd = http.server.HTTPServer(("localhost", 0), MyServer)
-    httpd.socket = ssl.wrap_socket(
-        httpd.socket,
-        keyfile=ssl_creds.key,
-        certfile=ssl_creds.cert,
-        server_side=True,
-    )
+
+    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+    context.load_cert_chain(certfile=ssl_creds.cert, keyfile=ssl_creds.key)
+
+    httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
 
     threading.Thread(target=httpd.serve_forever, daemon=True).start()
 
diff --git a/tests/pytest_tests/unit_tests/test_torch.py b/tests/pytest_tests/unit_tests/test_torch.py
--- a/tests/pytest_tests/unit_tests/test_torch.py
+++ b/tests/pytest_tests/unit_tests/test_torch.py
@@ -1,7 +1,10 @@
 import pytest
-import torch
-import torch.nn as nn
-import wandb
+
+pytest.importorskip("torch")
+
+import torch  # noqa: E402
+import torch.nn as nn  # noqa: E402
+import wandb  # noqa: E402
 
 
 def test_nested_shape():
diff --git a/tests/pytest_tests/unit_tests/test_util.py b/tests/pytest_tests/unit_tests/test_util.py
--- a/tests/pytest_tests/unit_tests/test_util.py
+++ b/tests/pytest_tests/unit_tests/test_util.py
@@ -13,23 +13,19 @@
 import plotly
 import pytest
 import requests
-import tensorflow as tf
 import wandb
 import wandb.errors as errors
 from wandb import util
 
-try:
-    import torch
-except ImportError:
-    pass
-
-
 ###############################################################################
 # Test util.json_friendly
 ###############################################################################
 
 
 def pt_variable(nested_list, requires_grad=True):
+    pytest.importorskip("torch")
+    import torch
+
     v = torch.autograd.Variable(torch.Tensor(nested_list))
     v.requires_grad = requires_grad
     return v
@@ -91,6 +87,9 @@ def json_friendly_test(orig_data, obj):
     ],
 )
 def test_pytorch_json_nd(array_shape):
+    pytest.importorskip("torch")
+    import torch
+
     a = nested_list(*array_shape)
     json_friendly_test(a, torch.Tensor(a))
     json_friendly_test(a, pt_variable(a))
@@ -112,6 +111,9 @@ def test_pytorch_json_nd(array_shape):
     ],
 )
 def test_tensorflow_json_nd(array_shape):
+    pytest.importorskip("tensorflow")
+    import tensorflow as tf
+
     a = nested_list(*array_shape)
     json_friendly_test(a, tf.convert_to_tensor(a))
     v = tf.Variable(tf.convert_to_tensor(a))
@@ -457,6 +459,9 @@ def test_make_tarfile():
 
 
 def test_is_tf_tensor():
+    pytest.importorskip("tensorflow")
+    import tensorflow as tf
+
     assert util.is_tf_tensor(tf.constant(1))
     assert not util.is_tf_tensor(tf.Variable(1))
     assert not util.is_tf_tensor(1)
@@ -464,6 +469,9 @@ def test_is_tf_tensor():
 
 
 def test_is_pytorch_tensor():
+    pytest.importorskip("torch")
+    import torch
+
     assert util.is_pytorch_tensor(torch.tensor(1))
     assert not util.is_pytorch_tensor(1)
     assert not util.is_pytorch_tensor(None)
diff --git a/tests/pytest_tests/unit_tests/tests_launch/test_job.py b/tests/pytest_tests/unit_tests/tests_launch/test_job.py
--- a/tests/pytest_tests/unit_tests/tests_launch/test_job.py
+++ b/tests/pytest_tests/unit_tests/tests_launch/test_job.py
@@ -57,7 +57,7 @@ def mock_download_code(root):
     mock_launch_project.project_dir = tmp_path / proj_path
 
     job.configure_launch_project(mock_launch_project)
-    assert mock_launch_project.set_entry_point.called_with(["python3", new_fname])
+    mock_launch_project.set_entry_point.assert_called_with(["python3", new_fname])
     assert job._entrypoint == ["python3", new_fname]
 
 
@@ -106,7 +106,7 @@ def mock_download_code(root):
     mock_launch_project.project_dir = tmp_path / proj_path
 
     job.configure_launch_project(mock_launch_project)
-    assert mock_launch_project.set_entry_point.called_with(["python3", new_fname])
+    mock_launch_project.set_entry_point.assert_called_with(["python3", new_fname])
     assert job._entrypoint == ["python3", new_fname]
 
 
diff --git a/tests/pytest_tests/unit_tests_old/test_keras.py b/tests/pytest_tests/unit_tests_old/test_keras.py
deleted file mode 100644
--- a/tests/pytest_tests/unit_tests_old/test_keras.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import pytest
-import wandb
-from tensorflow.keras import backend as K
-from tensorflow.keras.layers import Dense, Flatten, Reshape
-from tensorflow.keras.models import Sequential
-from wandb.keras import WandbCallback
-
-
-@pytest.fixture
-def dummy_model(request):
-    K.clear_session()
-    multi = request.node.get_closest_marker("multiclass")
-    image_output = request.node.get_closest_marker("image_output")
-    if multi:
-        loss = "categorical_crossentropy"
-    else:
-        loss = "binary_crossentropy"
-    nodes = 1 if not multi else 10
-    if image_output:
-        nodes = 300
-    model = Sequential()
-    model.add(Flatten(input_shape=(10, 10, 3)))
-    model.add(Dense(nodes, activation="sigmoid"))
-    if image_output:
-        model.add(Dense(nodes, activation="relu"))
-        model.add(Reshape((10, 10, 3)))
-    model.compile(optimizer="adam", loss=loss, metrics=["accuracy"])
-    return model
-
-
-@pytest.fixture
-def dummy_data(request):
-    multi = request.node.get_closest_marker("multiclass")
-    image_output = request.node.get_closest_marker("image_output")
-    cats = 10 if multi else 1
-    import numpy as np
-
-    data = np.random.randint(255, size=(100, 10, 10, 3))
-    labels = np.random.randint(2, size=(100, cats))
-    if image_output:
-        labels = data
-    return data, labels
-
-
-def test_keras_resume_best_metric(
-    dummy_model, dummy_data, live_mock_server, test_settings
-):
-    res = live_mock_server.set_ctx({"resume": True})
-    print("CTX AFTER UPDATE", res)
-    print("GET RIGHT AWAY", live_mock_server.get_ctx())
-    wandb.init(reinit=True, resume="allow", settings=test_settings)
-    print("Summary", dict(wandb.run.summary))
-    print("Config", dict(wandb.run.config))
-    assert WandbCallback().best == 0.5
-    wandb.finish()
diff --git a/tests/pytest_tests/unit_tests_old/test_tb_watcher.py b/tests/pytest_tests/unit_tests_old/test_tb_watcher.py
--- a/tests/pytest_tests/unit_tests_old/test_tb_watcher.py
+++ b/tests/pytest_tests/unit_tests_old/test_tb_watcher.py
@@ -9,6 +9,7 @@
     reason="TODO: Windows is legitimately busted",
 )
 def test_tb_watcher_save_row_custom_chart(mocked_run, tbwatcher_util):
+    pytest.importorskip("tensorflow")
     pytest.importorskip("tensorboard.summary.v1")
     import tensorboard.summary.v1 as tb_summary
     import tensorflow as tf