mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
ab3c0206d7
WindmillFinder's ModuleSpec lacked origin, so __file__ was never set on loaded modules. inspect.getfile() then raised "is a built-in module", breaking typeguard's @typechecked and anything else that introspects module source. Use spec_from_file_location() which sets origin correctly. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: hugocasa <hugo@casademont.ch>
93 lines
3.6 KiB
Python
93 lines
3.6 KiB
Python
import sys
|
|
import os
|
|
from importlib.abc import MetaPathFinder, Loader
|
|
from importlib.machinery import ModuleSpec, SourceFileLoader
|
|
from importlib.util import spec_from_file_location
|
|
import time
|
|
|
|
# Injected by backend: maps script path -> temp storage hash so preview jobs
|
|
# resolve relative imports from not-yet-deployed local content. Empty ({}) for
|
|
# deployed runs.
|
|
TEMP_SCRIPT_REFS = TEMP_SCRIPT_REFS_PLACEHOLDER
|
|
|
|
class WindmillLoader(Loader):
|
|
def __init__(self, path):
|
|
self.path = path
|
|
|
|
def create_module(self, spec):
|
|
return None
|
|
|
|
def exec_module(self, module):
|
|
module.__path__ = self.path
|
|
return None
|
|
|
|
|
|
class WindmillFinder(MetaPathFinder):
|
|
@classmethod
|
|
def find_spec(cls, name, path, target=None):
|
|
splitted = name.split(".")
|
|
|
|
if splitted[0] != "f" and splitted[0] != "u":
|
|
return None
|
|
l = len(splitted) # noqa: E741
|
|
if l <= 2:
|
|
return ModuleSpec(name, WindmillLoader(name))
|
|
elif l > 2:
|
|
|
|
script_path = "/".join(splitted)
|
|
folder = os.getcwd() + "/tmp/" + "/".join(splitted[:-1])
|
|
fullpath = folder + "/" + splitted[-1] + ".py"
|
|
|
|
if os.path.exists(fullpath):
|
|
return spec_from_file_location(name, fullpath)
|
|
|
|
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {os.environ.get('WM_TOKEN')}",
|
|
"User-Agent": "windmill/beta"
|
|
}
|
|
|
|
query_params = "?cache_folders=true"
|
|
runnable_id = os.environ.get('WM_RUNNABLE_ID')
|
|
if runnable_id:
|
|
query_params += f"&cache_key={runnable_id}"
|
|
temp_hash = TEMP_SCRIPT_REFS.get(script_path) if TEMP_SCRIPT_REFS else None
|
|
if temp_hash:
|
|
query_params += f"&temp_script_hash={temp_hash}"
|
|
url = f"{os.environ.get('BASE_INTERNAL_URL')}/api/w/{os.environ.get('WM_WORKSPACE')}/scripts/raw/p/{script_path}.py{query_params}"
|
|
|
|
req = urllib.request.Request(url, None, headers)
|
|
|
|
for attempt in range(4): # 0, 1, 2, 3 = up to 3 retries
|
|
try:
|
|
req_start = time.time()
|
|
with urllib.request.urlopen(req) as response:
|
|
os.makedirs(folder, exist_ok=True)
|
|
r = response.read().decode("utf-8")
|
|
if r == "WINDMILL_IS_FOLDER":
|
|
return ModuleSpec(name, WindmillLoader(name))
|
|
with open(fullpath, "w+") as f:
|
|
f.write(r)
|
|
return spec_from_file_location(name, fullpath)
|
|
except urllib.error.HTTPError as e:
|
|
duration = time.time() - req_start
|
|
if e.code != 404:
|
|
print(f"Error fetching script {script_path}: HTTP {e.code} - {e.reason} - {duration}s")
|
|
return ModuleSpec(name, WindmillLoader(name))
|
|
except Exception as e:
|
|
duration = time.time() - req_start
|
|
# Check if this is errno 104 (Connection reset by peer) and we have retries left
|
|
if (hasattr(e, 'errno') and e.errno == 104) and attempt < 3:
|
|
print(f"Connection reset (errno 104) fetching script {script_path}, retrying in 3s (attempt {attempt + 1}/3)")
|
|
time.sleep(3)
|
|
continue
|
|
print(f"Error fetching script {script_path}: {e} - {duration}s")
|
|
return ModuleSpec(name, WindmillLoader(name))
|
|
|
|
|
|
|
|
sys.meta_path.append(WindmillFinder)
|