Repository: PrefectHQ/prefect

Issue:
2.10.17 failure starting prefect agent on a windows machine
### First check

- [X] I added a descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Prefect documentation for this issue.
- [X] I checked that this issue is related to Prefect and not one of its dependencies.

### Bug summary

prefect agent won't start in 2.10.17 but it does in 2.10.16

### Reproduction

```python3
(venv) C:\Users\jkang1\PycharmProjects\site_analytics\venv\Scripts>prefect agent start --pool default-agent-pool
```


### Error

```python3
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Scripts\prefect.exe\__main__.py", line 4, in <module>
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\__init__.py", line 37, in <module>
    from prefect.states import State
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\states.py", line 14, in <module>
    from prefect.client.schemas import State as State
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\__init__.py", line 4, in <module>
    from .actions import BlockTypeUpdate, StateCreate
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\actions.py", line 9, in <module>
    import prefect.client.schemas.objects as objects
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\objects.py", line 20, in <module>
    from prefect._internal.schemas.bases import ObjectBaseModel, PrefectBaseModel
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\schemas\bases.py", line 18, in <module>
    from prefect._internal.compatibility.experimental import experiment_enabled
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\compatibility\experimental.py", line 19, in <module>
    from prefect.settings import PREFECT_EXPERIMENTAL_WARN, SETTING_VARIABLES, Setting
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\settings.py", line 70, in <module>
    from prefect._internal.compatibility.deprecated import generate_deprecation_message
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\compatibility\deprecated.py", line 21, in <module>
    from prefect.utilities.importtools import (
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\utilities\importtools.py", line 17, in <module>
    from prefect.utilities.filesystem import filename, is_local_path, tmpchdir
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\utilities\filesystem.py", line 5, in <module>
    import resource
ModuleNotFoundError: No module named 'resource'
```


### Versions

```Text
If I try to call it I receive the same error:

(venv) C:\Users\jkang1\PycharmProjects\site_analytics\venv\Scripts>prefect version
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Scripts\prefect.exe\__main__.py", line 4, in <module>
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\__init__.py", line 37, in <module>
    from prefect.states import State
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\states.py", line 14, in <module>
    from prefect.client.schemas import State as State
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\__init__.py", line 4, in <module>
    from .actions import BlockTypeUpdate, StateCreate
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\actions.py", line 9, in <module>
    import prefect.client.schemas.objects as objects
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\client\schemas\objects.py", line 20, in <module>
    from prefect._internal.schemas.bases import ObjectBaseModel, PrefectBaseModel
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\schemas\bases.py", line 18, in <module>
    from prefect._internal.compatibility.experimental import experiment_enabled
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\compatibility\experimental.py", line 19, in <module>
    from prefect.settings import PREFECT_EXPERIMENTAL_WARN, SETTING_VARIABLES, Setting
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\settings.py", line 70, in <module>
    from prefect._internal.compatibility.deprecated import generate_deprecation_message
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\_internal\compatibility\deprecated.py", line 21, in <module>
    from prefect.utilities.importtools import (
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\utilities\importtools.py", line 17, in <module>
    from prefect.utilities.filesystem import filename, is_local_path, tmpchdir
  File "C:\Users\jkang1\PycharmProjects\site_analytics\venv\Lib\site-packages\prefect\utilities\filesystem.py", line 5, in <module>
    import resource
ModuleNotFoundError: No module named 'resource'

When I downgrade to 2.10.16 and run it:
(venv) C:\Users\jkang1\PycharmProjects\site_analytics\venv\Scripts>prefect version
Version:             2.10.16
API version:         0.8.4
Python version:      3.11.3
Git commit:          6cd7c3ee
Built:               Tue, Jun 20, 2023 2:59 PM
OS/Arch:             win32/AMD64
Profile:             remote-desktop
Server type:         cloud
```


### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/src/prefect/utilities/filesystem.py b/src/prefect/utilities/filesystem.py
--- a/src/prefect/utilities/filesystem.py
+++ b/src/prefect/utilities/filesystem.py
@@ -2,7 +2,6 @@
 Utilities for working with file systems
 """
 import os
-import resource
 import pathlib
 from contextlib import contextmanager
 from pathlib import Path, PureWindowsPath
@@ -122,6 +121,21 @@ def relative_path_to_current_platform(path_str: str) -> Path:
     return Path(PureWindowsPath(path_str).as_posix())
 
 
-def get_open_file_limit():
-    soft_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
-    return soft_limit
+def get_open_file_limit() -> int:
+    """Get the maximum number of open files allowed for the current process"""
+
+    try:
+        if os.name == "nt":
+            import ctypes
+
+            return ctypes.cdll.ucrtbase._getmaxstdio()
+        else:
+            import resource
+
+            soft_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
+            return soft_limit
+    except Exception:
+        # Catch all exceptions, as ctypes can raise several errors
+        # depending on what went wrong. Return a safe default if we
+        # can't get the limit from the OS.
+        return 200


Test patch:
diff --git a/tests/utilities/test_filesystem.py b/tests/utilities/test_filesystem.py
--- a/tests/utilities/test_filesystem.py
+++ b/tests/utilities/test_filesystem.py
@@ -1,9 +1,14 @@
+import os
 import sys
 from pathlib import Path, PosixPath, WindowsPath
 
 import pytest
 
-from prefect.utilities.filesystem import filter_files, relative_path_to_current_platform
+from prefect.utilities.filesystem import (
+    filter_files,
+    relative_path_to_current_platform,
+    get_open_file_limit,
+)
 
 
 class TestFilterFiles:
@@ -149,3 +154,64 @@ def test_paths_on_windows(self, path_str, expected):
 
         assert isinstance(new_path, WindowsPath)
         assert str(new_path) == expected
+
+
+def test_get_open_file_limit():
+    """
+    Test that we can get the open file limit. Although this is a simple test, it
+    ensures that the function works as expected on both Windows and Unix.
+    """
+
+    limit = get_open_file_limit()
+
+    # The functions that check the open file limit on either Windows or Unix
+    # have an 'Any' return type, so this assertion ensures any changes to the
+    # function don't break its contract.
+    assert type(limit) == int
+
+    # It shouldn't be possible to have a negative open file limit.
+    assert limit >= 0
+
+    # The open file limit should not equal the default value of 200
+    # returned if an error occurs.
+    assert limit != 200
+
+
+@pytest.mark.skipif(os.name == "nt", reason="This is a Unix-specific test")
+def test_get_open_file_limit_exception_unix(monkeypatch):
+    import resource
+
+    def mock_getrlimit(*args, **kwargs):
+        raise OSError
+
+    monkeypatch.setattr(resource, "getrlimit", mock_getrlimit)
+
+    assert get_open_file_limit() == 200
+
+
+@pytest.mark.skipif(os.name != "nt", reason="This is a Windows-specific test")
+def test_get_open_file_limit_exception_windows(monkeypatch):
+    import ctypes
+
+    # General error when calling _getmaxstdio
+    def mock_getmaxstdio_error(*args, **kwargs):
+        raise OSError
+
+    # Either ucrtbase or _getmaxstdio is missing
+    def mock_getmaxstdio_missing(*args, **kwargs):
+        raise AttributeError
+
+    # Invalid argument(s) passed to _getmaxstdio
+    def mock_getmaxstdio_invalid_args(*args, **kwargs):
+        raise ValueError
+
+    monkeypatch.setattr(ctypes.cdll.ucrtbase, "_getmaxstdio", mock_getmaxstdio_error)
+    assert get_open_file_limit() == 200
+
+    monkeypatch.setattr(ctypes.cdll.ucrtbase, "_getmaxstdio", mock_getmaxstdio_missing)
+    assert get_open_file_limit() == 200
+
+    monkeypatch.setattr(
+        ctypes.cdll.ucrtbase, "_getmaxstdio", mock_getmaxstdio_invalid_args
+    )
+    assert get_open_file_limit() == 200