hello friends, I'm new to metaflow, and I am tryin...
# ask-metaflow
h
hello friends, I'm new to metaflow, and I am trying to use the
netflix_ext
extension, but I'm getting this strange error:
Copy code
Traceback (most recent call last):
  File "C:\Users\Ruben\generable\test-metaflow\example_flows\baselineflow.py", line 2, in <module>
    from metaflow import FlowSpec, step, Flow, current, pypi, IncludeFile
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\__init__.py", line 54, in <module>
    from metaflow.extension_support import (
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\extension_support\__init__.py", line 798, in <module>
    _all_packages, _pkgs_per_extension_point = _get_extension_packages()
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\extension_support\__init__.py", line 643, in _get_extension_packages
    raise RuntimeError(
RuntimeError: Package at 'C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow_extensions\netflix_ext' should conform to' metaflow_extensions.X.cmd' and not 'metaflow_extensions.cmd' where X is your organization's name for example
I'm running on windows (obviously), but I'm not quite sure how to go about debugging this. would anyone be able to offer some advice/help?
1
v
@dry-beach-38304 any idea?
👍 1
d
hun. I never checked on windows. Let me see what I can find out.
💯 1
ok took a look. Could you rerun with
METAFLOW_DEBUG_EXT=1
as well to give me a better idea. Two things seem to be happening: • it’s for some reason identifying it as a package from a path (and not a distribution) — this part I don’t know why • the code assumes “/” as the path separator which isn’t what windows does — this part I can fix. Note more generally though that we don’t test on Windows so you may find a few other issues as well. I can push a fix for the second issue and check if this assumption is being made elsewhere (likely, I know of lots of places we do “/”.join instead of what we should be doing which is os.path.join
💯 1
h
sorry for the delay - here's the run with the debug env var set:
Copy code
metaflow_extensions: Found packages present at {'C:\\Users\\Ruben\\anaconda3\\envs\\metaflow\\lib\\site-packages\\metaflow_extensions'}
metaflow_extensions: Found extension package 'metaflow-netflixext' at 'C:/Users/Ruben/anaconda3/envs/metaflow/lib/site-packages/metaflow_extensions'...
metaflow_extensions:     Top-level 'netflix_ext' extends 'cmd' with config 'metaflow_extensions.netflix_ext.cmd.mfextinit_netflixext'
metaflow_extensions:     Top-level 'netflix_ext' extends 'config' with config 'metaflow_extensions.netflix_ext.config.mfextinit_netflixext'
metaflow_extensions:     Top-level 'netflix_ext' extends 'plugins' with config 'None'
metaflow_extensions:     Top-level 'netflix_ext' found config file 'metaflow_extensions.netflix_ext.plugins.mfextinit_netflixext'
metaflow_extensions:     Top-level 'netflix_ext' extends 'toplevel' with config 'metaflow_extensions.netflix_ext.toplevel.mfextinit_netflixext'
metaflow_extensions: 'metaflow_extensions' distributions order is ['metaflow-netflixext']
metaflow_extensions: Non installed packages present at {'C:\\Users\\Ruben\\anaconda3\\envs\\metaflow\\lib\\site-packages\\metaflow_extensions'}
metaflow_extensions: Walking path C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow_extensions (package name _pythonpath_0)
Traceback (most recent call last):
  File "C:\Users\Ruben\generable\test-metaflow\example_flows\baselineflow.py", line 2, in <module>
    from metaflow import FlowSpec, step, Flow, current, pypi, IncludeFile
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\__init__.py", line 54, in <module>
    from metaflow.extension_support import (
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\extension_support\__init__.py", line 798, in <module>
    _all_packages, _pkgs_per_extension_point = _get_extension_packages()
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow\extension_support\__init__.py", line 643, in _get_extension_packages
    raise RuntimeError(
RuntimeError: Package at 'C:\Users\Ruben\anaconda3\envs\metaflow\lib\site-packages\metaflow_extensions\netflix_ext' should conform to' metaflow_extensions.X.cmd' and not 'metaflow_extensions.cmd' where X is your organization's name for example
also i guess i'm signing up to be a Windows tester then - no problem here 🙂
d
thx! hehe. Ya, I’ll push a fix for that. Other things may break but let’s see 🙂
💯 1
h
okay, so I made the simple change you pointed out locally to test it out:
Copy code
diff --git a/metaflow/extension_support/__init__.py b/metaflow/extension_support/__init__.py
index 99b7de3..dff661f 100644
--- a/metaflow/extension_support/__init__.py
+++ b/metaflow/extension_support/__init__.py
@@ -614,7 +614,7 @@ def _get_extension_packages():
                 "Walking path %s (package name %s)" % (package_path, package_name)
             )
             package_name_to_path[package_name] = package_path
-            base_depth = len(package_path.split("/"))
+            base_depth = len(package_path.split(os.sep))
             files_to_include = []
             meta_module = None
             for root, dirs, files in os.walk(package_path):
now I'm getting this runtime error in the extension package:
Copy code
Traceback (most recent call last):
  File "c:\users\ruben\generable\metaflow\metaflow\extension_support\plugins.py", line 137, in resolve_plugins
    plugin_module = importlib.import_module(path)
  File "C:\Users\Ruben\anaconda3\envs\metaflow\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "c:\users\ruben\generable\metaflow-nflx-extensions\metaflow_extensions\netflix_ext\plugins\conda\conda_step_decorator.py", line 34, in <module>
    from metaflow.plugins.env_escape import generate_trampolines
  File "c:\users\ruben\generable\metaflow\metaflow\plugins\env_escape\__init__.py", line 38, in <module>
    from .client_modules import create_modules
  File "c:\users\ruben\generable\metaflow\metaflow\plugins\env_escape\client_modules.py", line 9, in <module>
    from .client import Client
  File "c:\users\ruben\generable\metaflow\metaflow\plugins\env_escape\client.py", line 1, in <module>
    import fcntl
ModuleNotFoundError: No module named 'fcntl'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Ruben\generable\test-metaflow\example_flows\baselineflow.py", line 2, in <module>
    from metaflow import FlowSpec, step, Flow, current, pypi, IncludeFile
  File "c:\users\ruben\generable\metaflow\metaflow\__init__.py", line 115, in <module>
    from .plugins.datatools import S3
  File "c:\users\ruben\generable\metaflow\metaflow\plugins\__init__.py", line 132, in <module>
    STEP_DECORATORS = resolve_plugins("step_decorator")
  File "c:\users\ruben\generable\metaflow\metaflow\extension_support\plugins.py", line 139, in resolve_plugins
    raise ValueError(
ValueError: Cannot locate step_decorator plugin 'conda_env_internal' at 'metaflow_extensions.netflix_ext.plugins.conda.conda_step_decorator'
i'll check out if i can do anything locally about that but apparently the
fcntl
package isn't available on windows
oh wait, woops - i guess that exception is originating here: https://github.com/Netflix/metaflow/blob/master/metaflow/plugins/env_escape/client.py#L1
I did this hacky thing to see how far I could get, since I think making process str{out,in,err} nonblocking might be nice to have? i could be wrong:
Copy code
--- a/metaflow/extension_support/__init__.py
+++ b/metaflow/extension_support/__init__.py
@@ -614,7 +614,7 @@ def _get_extension_packages():
                 "Walking path %s (package name %s)" % (package_path, package_name)
             )
             package_name_to_path[package_name] = package_path
-            base_depth = len(package_path.split("/"))
+            base_depth = len(package_path.split(os.sep))
             files_to_include = []
             meta_module = None
             for root, dirs, files in os.walk(package_path):
diff --git a/metaflow/plugins/env_escape/client.py b/metaflow/plugins/env_escape/client.py
index cb004fd..476ec01 100644
--- a/metaflow/plugins/env_escape/client.py
+++ b/metaflow/plugins/env_escape/client.py
@@ -1,6 +1,9 @@
-import fcntl
-import gc
 import os
+
+if os.name != 'nt':
+    import fcntl
+
+import gc
 import importlib
 import itertools
 import select
@@ -178,9 +181,11 @@ class Client(object):

         # Make PIPEs non-blocking; this is helpful to be able to
         # order the messages properly
-        for f in (self._server_process.stdout, self._server_process.stderr):
-            fl = fcntl.fcntl(f, fcntl.F_GETFL)
-            fcntl.fcntl(f, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+        # This functionality doesn't work on Windows
+        if os.name != 'nt':
+            for f in (self._server_process.stdout, self._server_process.stderr):
+                fl = fcntl.fcntl(f, fcntl.F_GETFL)
+                fcntl.fcntl(f, fcntl.F_SETFL, fl | os.O_NONBLOCK)

         # Set up poller
         with self._poller_lock:
diff --git a/metaflow/sidecar/sidecar_subprocess.py b/metaflow/sidecar/sidecar_subprocess.py
index 34d4cc3..a7c9542 100644
--- a/metaflow/sidecar/sidecar_subprocess.py
+++ b/metaflow/sidecar/sidecar_subprocess.py
@@ -7,7 +7,8 @@ import os
 import sys
 import platform

-from fcntl import F_SETFL
+if os.name != 'nt':
+    from fcntl import F_SETFL
 from os import O_NONBLOCK

 from .sidecar_messages import Message, MessageTypes
@@ -102,7 +103,8 @@ class SidecarSubProcess(object):
             self._process = self._start_subprocess(cmdline)

             if self._process is not None:
-                fcntl.fcntl(self._process.stdin, F_SETFL, O_NONBLOCK)
+                if os.name != 'nt':
+                    fcntl.fcntl(self._process.stdin, F_SETFL, O_NONBLOCK)
                 self._poller = poll()
                 self._poller.register(self._process.stdin.fileno(), select.POLLOUT)
after this point, we run into issues here: https://github.com/Netflix/metaflow-nflx-extensions/blob/main/metaflow_extensions/netflix_ext/plugins/conda/env_descr.py#L1374 it looks like this would need a windows-specific API call for doing the proper file locking: https://www.oreilly.com/library/view/python-cookbook/0596001673/ch04s25.html (although I think there are also other solutions here, i'm not quite sure what the best would be)
d
hey. Thanks for hacking at this. I took a quick look and I’ll try to push the following changes soonish: • change to path manipulation to be more windows friendly • replace fcntl by win32api and mvcrt (or something, I found some equivalent packages for our use) for windows. • remove the hard check on Linux/Mac. I don’t have a windows machine so will not be testing on it but if you are willing to give it a shot, I can try to get it working appropriately. I can’t promise that nothing else will break but it may at least get you a bit further.
could you try this whenever you get a chance:
Copy code
from pathlib import PurePath
p = PurePath("C:\\Users\\Ruben\\anaconda3\\envs\\metaflow\\lib\\site-packages\\metaflow_extensions")
p
p.as_posix()
Want to confirm it works the way I expect on a windows machine.
h
apologies for the long delay! I'd be happy to test it out! I actually ended up partitioning my machine to install Ubuntu as well just to bite the bullet, but I can definitely help test if it's still useful
d
yep — still useful. I just want to make sure that snippet of python produces what I expect on windows (I don’t have a windows machine)
h
sounds good! just let me know when you have a PR for me to test 🙂
d
Could you give me the output of the above snippet ?
I am interested in what
p
prints and what
<http://p.as|p.as>_posix()
prints.
h
Woops, sorry for forgetting about that! here it is (running in a conda environment using Anaconda Prompt in Windows):
Copy code
(metaflow) C:\Users\Ruben>python
Python 3.9.18 (main, Sep 11 2023, 14:09:26) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from pathlib import PurePath
>>> p = PurePath("C:\\Users\\Ruben\\anaconda3\\envs\\metaflow\\lib\\site-packages\\metaflow_extensions")
>>> p
PureWindowsPath('C:/Users/Ruben/anaconda3/envs/metaflow/lib/site-packages/metaflow_extensions')
>>> p.as_posix()
'C:/Users/Ruben/anaconda3/envs/metaflow/lib/site-packages/metaflow_extensions'
>>>
d
awesome! Thanks. That is what I expected. I’ll try to make things windows friendly.
💯 1
still working on this.