Skip to content

Commit 77f2346

Browse files
use sys.stdlib_module_names in restrictions. Move dynamic import warning to only apply to non-passed through modules (#1222)
1 parent 3fd1c7e commit 77f2346

File tree

4 files changed

+25
-74
lines changed

4 files changed

+25
-74
lines changed

temporalio/worker/workflow_sandbox/_importer.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,6 @@ def _import(
201201

202202
# Check module restrictions and passthrough modules
203203
if full_name not in sys.modules:
204-
# Issue a warning if appropriate
205-
if (
206-
self.restriction_context.in_activation
207-
and self._is_import_notification_policy_applied(
208-
temporalio.workflow.SandboxImportNotificationPolicy.WARN_ON_DYNAMIC_IMPORT
209-
)
210-
):
211-
warnings.warn(
212-
f"Module {full_name} was imported after initial workflow load."
213-
)
214-
215204
# Make sure not an entirely invalid module
216205
self._assert_valid_module(full_name)
217206

@@ -237,6 +226,17 @@ def _import(
237226
setattr(sys.modules[parent], child, sys.modules[full_name])
238227
# All children of this module that are on the original sys
239228
# modules but not here and are passthrough
229+
else:
230+
# Issue a warning if appropriate
231+
if (
232+
self.restriction_context.in_activation
233+
and self._is_import_notification_policy_applied(
234+
temporalio.workflow.SandboxImportNotificationPolicy.WARN_ON_DYNAMIC_IMPORT
235+
)
236+
):
237+
warnings.warn(
238+
f"Module {full_name} was imported after initial workflow load."
239+
)
240240

241241
# If the module is __temporal_main__ and not already in sys.modules,
242242
# we load it from whatever file __main__ was originally in

temporalio/worker/workflow_sandbox/_restrictions.py

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
cast,
3535
)
3636

37-
from typing_extensions import Self
38-
3937
try:
4038
import pydantic
4139
import pydantic_core
@@ -529,44 +527,6 @@ def with_child_unrestricted(self, *child_path: str) -> SandboxMatcher:
529527
}
530528
)
531529

532-
# sys.stdlib_module_names is only available on 3.10+, so we hardcode here. A
533-
# test will fail if this list doesn't match the latest Python version it was
534-
# generated against, spitting out the expected list. This is a string instead
535-
# of a list of strings due to black wanting to format this to one item each
536-
# line in a list.
537-
_stdlib_module_names = (
538-
"__future__,_abc,_aix_support,_ast,_asyncio,_bisect,_blake2,_bootsubprocess,_bz2,_codecs,"
539-
"_codecs_cn,_codecs_hk,_codecs_iso2022,_codecs_jp,_codecs_kr,_codecs_tw,_collections,"
540-
"_collections_abc,_compat_pickle,_compression,_contextvars,_crypt,_csv,_ctypes,_curses,"
541-
"_curses_panel,_datetime,_dbm,_decimal,_elementtree,_frozen_importlib,_frozen_importlib_external,"
542-
"_functools,_gdbm,_hashlib,_heapq,_imp,_io,_json,_locale,_lsprof,_lzma,_markupbase,"
543-
"_md5,_msi,_multibytecodec,_multiprocessing,_opcode,_operator,_osx_support,_overlapped,"
544-
"_pickle,_posixshmem,_posixsubprocess,_py_abc,_pydecimal,_pyio,_queue,_random,_scproxy,"
545-
"_sha1,_sha256,_sha3,_sha512,_signal,_sitebuiltins,_socket,_sqlite3,_sre,_ssl,_stat,"
546-
"_statistics,_string,_strptime,_struct,_symtable,_thread,_threading_local,_tkinter,"
547-
"_tokenize,_tracemalloc,_typing,_uuid,_warnings,_weakref,_weakrefset,_winapi,_zoneinfo,"
548-
"abc,aifc,antigravity,argparse,array,ast,asynchat,asyncio,asyncore,atexit,audioop,"
549-
"base64,bdb,binascii,bisect,builtins,bz2,cProfile,calendar,cgi,cgitb,chunk,cmath,cmd,"
550-
"code,codecs,codeop,collections,colorsys,compileall,concurrent,configparser,contextlib,"
551-
"contextvars,copy,copyreg,crypt,csv,ctypes,curses,dataclasses,datetime,dbm,decimal,"
552-
"difflib,dis,distutils,doctest,email,encodings,ensurepip,enum,errno,faulthandler,fcntl,"
553-
"filecmp,fileinput,fnmatch,fractions,ftplib,functools,gc,genericpath,getopt,getpass,"
554-
"gettext,glob,graphlib,grp,gzip,hashlib,heapq,hmac,html,http,idlelib,imaplib,imghdr,"
555-
"imp,importlib,inspect,io,ipaddress,itertools,json,keyword,lib2to3,linecache,locale,"
556-
"logging,lzma,mailbox,mailcap,marshal,math,mimetypes,mmap,modulefinder,msilib,msvcrt,"
557-
"multiprocessing,netrc,nis,nntplib,nt,ntpath,nturl2path,numbers,opcode,operator,optparse,"
558-
"os,ossaudiodev,pathlib,pdb,pickle,pickletools,pipes,pkgutil,platform,plistlib,poplib,"
559-
"posix,posixpath,pprint,profile,pstats,pty,pwd,py_compile,pyclbr,pydoc,pydoc_data,"
560-
"pyexpat,queue,quopri,random,re,readline,reprlib,resource,rlcompleter,runpy,sched,"
561-
"secrets,select,selectors,shelve,shlex,shutil,signal,site,smtpd,smtplib,sndhdr,socket,"
562-
"socketserver,spwd,sqlite3,sre_compile,sre_constants,sre_parse,ssl,stat,statistics,"
563-
"string,stringprep,struct,subprocess,sunau,symtable,sys,sysconfig,syslog,tabnanny,"
564-
"tarfile,telnetlib,tempfile,termios,textwrap,this,threading,time,timeit,tkinter,token,"
565-
"tokenize,tomllib,trace,traceback,tracemalloc,tty,turtle,turtledemo,types,typing,unicodedata,"
566-
"unittest,urllib,uu,uuid,venv,warnings,wave,weakref,webbrowser,winreg,winsound,wsgiref,"
567-
"xdrlib,xml,xmlrpc,zipapp,zipfile,zipimport,zlib,zoneinfo"
568-
)
569-
570530
SandboxRestrictions.passthrough_modules_maximum = (
571531
SandboxRestrictions.passthrough_modules_with_temporal
572532
| {
@@ -575,7 +535,7 @@ def with_child_unrestricted(self, *child_path: str) -> SandboxMatcher:
575535
# manually setting sys.modules["os.path"]) they have certain child
576536
# expectations.
577537
v
578-
for v in _stdlib_module_names.split(",")
538+
for v in sys.stdlib_module_names
579539
if v != "sys"
580540
}
581541
)

tests/worker/workflow_sandbox/test_restrictions.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
SandboxMatcher,
1414
SandboxRestrictions,
1515
_RestrictedProxy,
16-
_stdlib_module_names,
1716
)
1817

1918

@@ -31,8 +30,8 @@ def test_workflow_sandbox_stdlib_module_names():
3130
code_lines[-1] += mod_name
3231
code = '_stdlib_module_names = (\n "' + '"\n "'.join(code_lines) + '"\n)'
3332
# TODO(cretz): Point releases may add modules :-(
34-
assert (
35-
actual_names == _stdlib_module_names
33+
assert actual_names == ",".join(
34+
sys.stdlib_module_names
3635
), f"Expecting names as {actual_names}. In code as:\n{code}"
3736

3837

tests/worker/workflow_sandbox/test_runner.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -602,27 +602,19 @@ async def test_workflow_sandbox_import_errors(client: Client):
602602
workflows=[LazyImportWorkflow],
603603
workflow_runner=SandboxedWorkflowRunner(restrictions),
604604
) as worker:
605-
with pytest.warns() as recorder:
606-
with pytest.raises(WorkflowFailureError) as err:
607-
await client.execute_workflow(
608-
LazyImportWorkflow.run,
609-
id=f"workflow-{uuid.uuid4()}",
610-
task_queue=worker.task_queue,
611-
)
612-
613-
assert isinstance(err.value.cause, ApplicationError)
614-
assert err.value.cause.type == "UnintentionalPassthroughError"
615-
assert (
616-
"Module tests.worker.workflow_sandbox.testmodules.lazy_module was not intentionally passed through to the sandbox."
617-
== err.value.cause.message
605+
with pytest.raises(WorkflowFailureError) as err:
606+
await client.execute_workflow(
607+
LazyImportWorkflow.run,
608+
id=f"workflow-{uuid.uuid4()}",
609+
task_queue=worker.task_queue,
618610
)
619611

620-
_assert_expected_warnings(
621-
recorder,
622-
{
623-
"Module tests.worker.workflow_sandbox.testmodules.lazy_module was imported after initial workflow load.",
624-
},
625-
)
612+
assert isinstance(err.value.cause, ApplicationError)
613+
assert err.value.cause.type == "UnintentionalPassthroughError"
614+
assert (
615+
"Module tests.worker.workflow_sandbox.testmodules.lazy_module was not intentionally passed through to the sandbox."
616+
== err.value.cause.message
617+
)
626618

627619

628620
@workflow.defn

0 commit comments

Comments
 (0)