From cc8ca1484fc94eb13c5e983f7f01a4265dca30c6 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 29 Jul 2025 18:48:19 -0700 Subject: [PATCH 1/7] first debug version --- samcli/lib/utils/file_observer.py | 34 +++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/samcli/lib/utils/file_observer.py b/samcli/lib/utils/file_observer.py index 72864e3059f..10c062292ca 100644 --- a/samcli/lib/utils/file_observer.py +++ b/samcli/lib/utils/file_observer.py @@ -3,6 +3,7 @@ """ import logging +import os import platform import threading import uuid @@ -183,11 +184,15 @@ def _on_change(self, resources: List[str], package_type: str) -> None: package_type: str determine if the changed resource is a source code path or an image name """ + LOG.debug("Acquiring lock for _on_change to process %s %s resource changes: %s", + len(resources), package_type, resources) with self._watch_lock: changed_functions: List[FunctionConfig] = [] for resource in resources: if self._observed_functions[package_type].get(resource, None): changed_functions += self._observed_functions[package_type][resource] + LOG.debug("Acquired lock and processing %s changed functions from %s resources", + len(changed_functions), len(resources)) self._input_on_change(changed_functions) def watch(self, function_config: FunctionConfig) -> None: @@ -204,9 +209,13 @@ def watch(self, function_config: FunctionConfig) -> None: ObserverException: if not able to observe the input function source path/image """ + LOG.debug("Acquiring lock for watch to observe %s function: %s", + function_config.packagetype, function_config.name) with self._watch_lock: if self.get_resources.get(function_config.packagetype, None): resources = self.get_resources[function_config.packagetype](function_config) + LOG.debug("Acquired lock for watch, observing %s resources for function %s: %s", + len(resources), function_config.name, resources) for resource in resources: functions = self._observed_functions[function_config.packagetype].get(resource, []) functions += [function_config] @@ -415,6 +424,17 @@ class SingletonFileObserver(metaclass=Singleton): A Singleton class that will observe some file system paths for any change for multiple purposes. """ + # Ignore patterns for file watching to avoid infinite loops and unnecessary events + DEFAULT_IGNORE_PATTERNS = [ + "*/node_modules/*/*", # Ignore changes within packages, but allow new packages + "*.tmp", # Ignore temporary files + "*.temp", # Ignore temporary files + "*/.git/*", # Ignore git directories + "*/__pycache__/*", # Ignore Python cache + "*.pyc", # Ignore Python compiled files + "*.log" # Ignore log files + ] + def __init__(self) -> None: """ Initialize the file observer @@ -424,12 +444,22 @@ def __init__(self) -> None: self._observed_watches: Dict[str, ObservedWatch] = {} self._watch_dog_observed_paths: Dict[str, List[str]] = {} self._observer: BaseObserver = Observer() + + # Use environment variable to control ignore patterns (default: enabled) + use_ignore_patterns = os.environ.get("SAM_USE_IGNORE_PATTERN", "true").lower() == "true" + ignore_patterns = self.DEFAULT_IGNORE_PATTERNS if use_ignore_patterns else [] + + if use_ignore_patterns: + LOG.debug("File observer using ignore patterns: %s", ignore_patterns) + else: + LOG.debug("File observer ignore patterns disabled via SAM_CLI_FILE_OBSERVER_IGNORE_PATTERNS") + self._code_modification_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( - patterns=["*"], ignore_patterns=[], ignore_directories=False + patterns=["*"], ignore_patterns=ignore_patterns, ignore_directories=False ) self._code_deletion_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( - patterns=["*"], ignore_patterns=[], ignore_directories=False + patterns=["*"], ignore_patterns=ignore_patterns, ignore_directories=False ) self._code_modification_handler.on_modified = self.on_change # type: ignore From b5ae2c56393d3103c5d57094df0ab14d9c64ffeb Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 22 Aug 2025 17:36:21 -0700 Subject: [PATCH 2/7] add --no-watch flag --- .../local/cli_common/invoke_context.py | 17 ++++++++-- samcli/commands/local/start_api/cli.py | 10 ++++++ .../commands/local/start_api/core/options.py | 1 + samcli/lib/providers/sam_function_provider.py | 25 +++++++++----- samcli/local/lambdafn/runtime.py | 33 ++++++++++++++----- 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/samcli/commands/local/cli_common/invoke_context.py b/samcli/commands/local/cli_common/invoke_context.py index 5cef9e6ac7b..a9b45e7c726 100644 --- a/samcli/commands/local/cli_common/invoke_context.py +++ b/samcli/commands/local/cli_common/invoke_context.py @@ -103,6 +103,7 @@ def __init__( invoke_images: Optional[str] = None, mount_symlinks: Optional[bool] = False, no_mem_limit: Optional[bool] = False, + no_watch: Optional[bool] = False, ) -> None: """ Initialize the context @@ -204,6 +205,7 @@ def __init__( self._mount_symlinks: Optional[bool] = mount_symlinks self._no_mem_limit = no_mem_limit + self._no_watch = no_watch # Note(xinhol): despite self._function_provider and self._stacks are initialized as None # they will be assigned with a non-None value in __enter__() and @@ -245,9 +247,17 @@ def __enter__(self) -> "InvokeContext": if self._docker_volume_basedir: _function_providers_args[self._containers_mode].append(True) - self._function_provider = _function_providers_class[self._containers_mode]( - *_function_providers_args[self._containers_mode] - ) + # For RefreshableSamFunctionProvider, we need to pass additional parameters including no_watch + if self._containers_mode == ContainersMode.WARM: + # Create RefreshableSamFunctionProvider with no_watch parameter + self._function_provider = RefreshableSamFunctionProvider( + *_function_providers_args[self._containers_mode], + no_watch=self._no_watch + ) + else: + self._function_provider = _function_providers_class[self._containers_mode]( + *_function_providers_args[self._containers_mode] + ) self._env_vars_value = self._get_env_vars_value(self._env_vars_file) self._container_env_vars_value = self._get_env_vars_value(self._container_env_vars_file) @@ -415,6 +425,7 @@ def lambda_runtime(self) -> LambdaRuntime: image_builder, mount_symlinks=self._mount_symlinks, no_mem_limit=self._no_mem_limit, + no_watch=self._no_watch, ), ContainersMode.COLD: LambdaRuntime( self._container_manager, diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index bd9d36ead53..aeef043eeed 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -95,6 +95,12 @@ required_param_lists=[["ssl_cert_file"]], help="Path to SSL key file (default: None)", ) +@click.option( + "--no-watch", + is_flag=True, + default=False, + help="Disable file watching. Template file and nested stacks will not be monitored for changes.", +) @invoke_common_options @warm_containers_common_options @local_common_options @@ -141,6 +147,7 @@ def cli( terraform_plan_file, ssl_cert_file, ssl_key_file, + no_watch, no_memory_limit, ): """ @@ -178,6 +185,7 @@ def cli( ssl_cert_file, ssl_key_file, no_memory_limit, + no_watch, ) # pragma: no cover @@ -211,6 +219,7 @@ def do_cli( # pylint: disable=R0914 ssl_cert_file, ssl_key_file, no_mem_limit, + no_watch, ): """ Implementation of the ``cli`` method, just separated out for unit testing purposes @@ -257,6 +266,7 @@ def do_cli( # pylint: disable=R0914 invoke_images=processed_invoke_images, add_host=add_host, no_mem_limit=no_mem_limit, + no_watch=no_watch, ) as invoke_context: ssl_context = (ssl_cert_file, ssl_key_file) if ssl_cert_file else None service = LocalApiService( diff --git a/samcli/commands/local/start_api/core/options.py b/samcli/commands/local/start_api/core/options.py index 750b8c6d15c..b998881de55 100644 --- a/samcli/commands/local/start_api/core/options.py +++ b/samcli/commands/local/start_api/core/options.py @@ -43,6 +43,7 @@ "add_host", "invoke_image", "disable_authorizer", + "no_watch", ] CONFIGURATION_OPTION_NAMES: List[str] = ["config_env", "config_file"] + SAVE_PARAMS_OPTIONS diff --git a/samcli/lib/providers/sam_function_provider.py b/samcli/lib/providers/sam_function_provider.py index 5787fb409f8..cb424a9410f 100644 --- a/samcli/lib/providers/sam_function_provider.py +++ b/samcli/lib/providers/sam_function_provider.py @@ -800,6 +800,7 @@ def __init__( global_parameter_overrides: Optional[Dict] = None, use_raw_codeuri: bool = False, ignore_code_extraction_warnings: bool = False, + no_watch: bool = False, ) -> None: """ Initialize the class with SAM template data. The SAM template passed to this provider is assumed @@ -831,9 +832,14 @@ def __init__( self.parent_templates_paths.append(stack.location) self.is_changed = False - self._observer = FileObserver(self._set_templates_changed) - self._observer.start() - self._watch_stack_templates(stacks) + + # Only initialize file watcher if no_watch is False + if not no_watch: + self._observer = FileObserver(self._set_templates_changed) + self._observer.start() + self._watch_stack_templates(stacks) + else: + self._observer = None @property def stacks(self) -> List[Stack]: @@ -896,15 +902,17 @@ def _set_templates_changed(self, paths: List[str]) -> None: ", ".join(paths), ) self.is_changed = True - for stack in self._stacks: - self._observer.unwatch(stack.location) + if self._observer: + for stack in self._stacks: + self._observer.unwatch(stack.location) def _watch_stack_templates(self, stacks: List[Stack]) -> None: """ initialize the list of stack template watchers """ - for stack in stacks: - self._observer.watch(stack.location) + if self._observer: + for stack in stacks: + self._observer.watch(stack.location) def _refresh_loaded_functions(self) -> None: """ @@ -934,4 +942,5 @@ def stop_observer(self) -> None: """ Stop Observing. """ - self._observer.stop() + if self._observer: + self._observer.stop() diff --git a/samcli/local/lambdafn/runtime.py b/samcli/local/lambdafn/runtime.py index 82fb3d8334c..77de72592f2 100644 --- a/samcli/local/lambdafn/runtime.py +++ b/samcli/local/lambdafn/runtime.py @@ -395,7 +395,7 @@ class WarmLambdaRuntime(LambdaRuntime): warm containers life cycle. """ - def __init__(self, container_manager, image_builder, observer=None, mount_symlinks=False, no_mem_limit=False): + def __init__(self, container_manager, image_builder, observer=None, mount_symlinks=False, no_mem_limit=False, no_watch=False): """ Initialize the Local Lambda runtime @@ -405,13 +405,23 @@ def __init__(self, container_manager, image_builder, observer=None, mount_symlin Instance of the ContainerManager class that can run a local Docker container image_builder samcli.local.docker.lambda_image.LambdaImage Instance of the LambdaImage class that can create am image - warm_containers bool - Determines if the warm containers is enabled or not. + observer + Optional observer for file watching + mount_symlinks bool + Optional. True if symlinks should be mounted in the container + no_mem_limit bool + Optional. True if memory limit should be disabled + no_watch bool + Optional. True if file watching should be disabled """ self._function_configs = {} self._containers = {} + self._no_watch = no_watch - self._observer = observer if observer else LambdaFunctionObserver(self._on_code_change) + if no_watch: + self._observer = None + else: + self._observer = observer if observer else LambdaFunctionObserver(self._on_code_change) super().__init__(container_manager, image_builder, mount_symlinks=mount_symlinks, no_mem_limit=no_mem_limit) @@ -453,7 +463,8 @@ def create( if container: self._container_manager.stop(container) self._containers.pop(exist_function_config.full_path, None) - self._observer.unwatch(exist_function_config) + if self._observer is not None: + self._observer.unwatch(exist_function_config) elif container and container.is_created(): LOG.info("Reuse the created warm container for Lambda function '%s'", function_config.full_path) return container @@ -468,8 +479,10 @@ def create( ) debug_context = None - self._observer.watch(function_config) - self._observer.start() + # Only watch and start observer if file watching is enabled + if self._observer is not None: + self._observer.watch(function_config) + self._observer.start() container = super().create( function_config, debug_context, container_host, container_host_interface, extra_hosts @@ -545,7 +558,8 @@ def clean_running_containers_and_related_resources(self): LOG.debug("Terminate running warm container for Lambda Function '%s'", function_name) self._container_manager.stop(container) self._clean_decompressed_paths() - self._observer.stop() + if self._observer is not None: + self._observer.stop() def _on_code_change(self, functions): """ @@ -566,7 +580,8 @@ def _on_code_change(self, functions): function_full_path, resource, ) - self._observer.unwatch(function_config) + if self._observer is not None: + self._observer.unwatch(function_config) self._function_configs.pop(function_full_path, None) container = self._containers.get(function_full_path, None) if container: From 2f0b11b25875153f5e623ad2b2ef073134a80128 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 26 Sep 2025 18:21:29 -0700 Subject: [PATCH 3/7] add no-watch flag --- .../local/cli_common/invoke_context.py | 20 +++---- samcli/commands/local/start_api/cli.py | 2 +- samcli/commands/local/start_lambda/cli.py | 10 ++++ .../local/start_lambda/core/options.py | 1 + samcli/lib/providers/sam_function_provider.py | 4 +- samcli/lib/utils/file_observer.py | 25 +-------- samcli/local/lambdafn/runtime.py | 7 ++- .../local/cli_common/test_invoke_context.py | 10 +++- .../unit/commands/local/start_api/test_cli.py | 52 +++++++++++++++++++ .../commands/local/start_lambda/test_cli.py | 51 ++++++++++++++++++ .../unit/commands/samconfig/test_samconfig.py | 4 ++ 11 files changed, 144 insertions(+), 42 deletions(-) diff --git a/samcli/commands/local/cli_common/invoke_context.py b/samcli/commands/local/cli_common/invoke_context.py index a9b45e7c726..9ffe6e038cc 100644 --- a/samcli/commands/local/cli_common/invoke_context.py +++ b/samcli/commands/local/cli_common/invoke_context.py @@ -236,28 +236,22 @@ def __enter__(self) -> "InvokeContext": ContainersMode.WARM: RefreshableSamFunctionProvider, ContainersMode.COLD: SamFunctionProvider, } - _function_providers_args: Dict[ContainersMode, List[Any]] = { ContainersMode.WARM: [self._stacks, self._parameter_overrides, self._global_parameter_overrides], ContainersMode.COLD: [self._stacks], } - # don't resolve the code URI immediately if we passed in docker vol by passing True for use_raw_codeuri # this way at the end the code URI will get resolved against the basedir option if self._docker_volume_basedir: _function_providers_args[self._containers_mode].append(True) + if self._no_watch: + _function_providers_args[self._containers_mode].extend([False, True]) + elif self._no_watch: + _function_providers_args[self._containers_mode].extend([False, False, True]) - # For RefreshableSamFunctionProvider, we need to pass additional parameters including no_watch - if self._containers_mode == ContainersMode.WARM: - # Create RefreshableSamFunctionProvider with no_watch parameter - self._function_provider = RefreshableSamFunctionProvider( - *_function_providers_args[self._containers_mode], - no_watch=self._no_watch - ) - else: - self._function_provider = _function_providers_class[self._containers_mode]( - *_function_providers_args[self._containers_mode] - ) + self._function_provider = _function_providers_class[self._containers_mode]( + *_function_providers_args[self._containers_mode] + ) self._env_vars_value = self._get_env_vars_value(self._env_vars_file) self._container_env_vars_value = self._get_env_vars_value(self._container_env_vars_file) diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index aeef043eeed..eec0ba14fd0 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -99,7 +99,7 @@ "--no-watch", is_flag=True, default=False, - help="Disable file watching. Template file and nested stacks will not be monitored for changes.", + help="Disable file watching. Local code changes will not reset running docker container.", ) @invoke_common_options @warm_containers_common_options diff --git a/samcli/commands/local/start_lambda/cli.py b/samcli/commands/local/start_lambda/cli.py index f4df9156a00..f0f62f69181 100644 --- a/samcli/commands/local/start_lambda/cli.py +++ b/samcli/commands/local/start_lambda/cli.py @@ -59,6 +59,12 @@ ) @skip_prepare_infra_option @service_common_options(3001) +@click.option( + "--no-watch", + is_flag=True, + default=False, + help="Disable file watching. Local code changes will not reset running docker container.", +) @invoke_common_options @warm_containers_common_options @local_common_options @@ -101,6 +107,7 @@ def cli( hook_name, skip_prepare_infra, terraform_plan_file, + no_watch, no_memory_limit, ): """ @@ -134,6 +141,7 @@ def cli( invoke_image, hook_name, no_memory_limit, + no_watch, ) # pragma: no cover @@ -163,6 +171,7 @@ def do_cli( # pylint: disable=R0914 invoke_image, hook_name, no_mem_limit, + no_watch, ): """ Implementation of the ``cli`` method, just separated out for unit testing purposes @@ -209,6 +218,7 @@ def do_cli( # pylint: disable=R0914 add_host=add_host, invoke_images=processed_invoke_images, no_mem_limit=no_mem_limit, + no_watch=no_watch, ) as invoke_context: service = LocalLambdaService(lambda_invoke_context=invoke_context, port=port, host=host) service.start() diff --git a/samcli/commands/local/start_lambda/core/options.py b/samcli/commands/local/start_lambda/core/options.py index 4eecf904830..2c38342338c 100644 --- a/samcli/commands/local/start_lambda/core/options.py +++ b/samcli/commands/local/start_lambda/core/options.py @@ -38,6 +38,7 @@ "add_host", "invoke_image", "no_memory_limit", + "no_watch", ] ARTIFACT_LOCATION_OPTIONS: List[str] = [ diff --git a/samcli/lib/providers/sam_function_provider.py b/samcli/lib/providers/sam_function_provider.py index cb424a9410f..91d7b19026f 100644 --- a/samcli/lib/providers/sam_function_provider.py +++ b/samcli/lib/providers/sam_function_provider.py @@ -800,7 +800,7 @@ def __init__( global_parameter_overrides: Optional[Dict] = None, use_raw_codeuri: bool = False, ignore_code_extraction_warnings: bool = False, - no_watch: bool = False, + no_watch: Optional[bool] = False, ) -> None: """ Initialize the class with SAM template data. The SAM template passed to this provider is assumed @@ -835,7 +835,7 @@ def __init__( # Only initialize file watcher if no_watch is False if not no_watch: - self._observer = FileObserver(self._set_templates_changed) + self._observer: Optional[FileObserver] = FileObserver(self._set_templates_changed) self._observer.start() self._watch_stack_templates(stacks) else: diff --git a/samcli/lib/utils/file_observer.py b/samcli/lib/utils/file_observer.py index 10c062292ca..3ca59fd7281 100644 --- a/samcli/lib/utils/file_observer.py +++ b/samcli/lib/utils/file_observer.py @@ -3,7 +3,6 @@ """ import logging -import os import platform import threading import uuid @@ -424,17 +423,6 @@ class SingletonFileObserver(metaclass=Singleton): A Singleton class that will observe some file system paths for any change for multiple purposes. """ - # Ignore patterns for file watching to avoid infinite loops and unnecessary events - DEFAULT_IGNORE_PATTERNS = [ - "*/node_modules/*/*", # Ignore changes within packages, but allow new packages - "*.tmp", # Ignore temporary files - "*.temp", # Ignore temporary files - "*/.git/*", # Ignore git directories - "*/__pycache__/*", # Ignore Python cache - "*.pyc", # Ignore Python compiled files - "*.log" # Ignore log files - ] - def __init__(self) -> None: """ Initialize the file observer @@ -445,21 +433,12 @@ def __init__(self) -> None: self._watch_dog_observed_paths: Dict[str, List[str]] = {} self._observer: BaseObserver = Observer() - # Use environment variable to control ignore patterns (default: enabled) - use_ignore_patterns = os.environ.get("SAM_USE_IGNORE_PATTERN", "true").lower() == "true" - ignore_patterns = self.DEFAULT_IGNORE_PATTERNS if use_ignore_patterns else [] - - if use_ignore_patterns: - LOG.debug("File observer using ignore patterns: %s", ignore_patterns) - else: - LOG.debug("File observer ignore patterns disabled via SAM_CLI_FILE_OBSERVER_IGNORE_PATTERNS") - self._code_modification_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( - patterns=["*"], ignore_patterns=ignore_patterns, ignore_directories=False + patterns=["*"], ignore_patterns=[], ignore_directories=False ) self._code_deletion_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( - patterns=["*"], ignore_patterns=ignore_patterns, ignore_directories=False + patterns=["*"], ignore_patterns=[], ignore_directories=False ) self._code_modification_handler.on_modified = self.on_change # type: ignore diff --git a/samcli/local/lambdafn/runtime.py b/samcli/local/lambdafn/runtime.py index 77de72592f2..d177784955c 100644 --- a/samcli/local/lambdafn/runtime.py +++ b/samcli/local/lambdafn/runtime.py @@ -395,7 +395,12 @@ class WarmLambdaRuntime(LambdaRuntime): warm containers life cycle. """ - def __init__(self, container_manager, image_builder, observer=None, mount_symlinks=False, no_mem_limit=False, no_watch=False): + def __init__(self, container_manager, + image_builder, + observer=None, + mount_symlinks=False, + no_mem_limit=False, + no_watch=False): """ Initialize the Local Lambda runtime diff --git a/tests/unit/commands/local/cli_common/test_invoke_context.py b/tests/unit/commands/local/cli_common/test_invoke_context.py index cec05c9d8a6..6871bff870b 100644 --- a/tests/unit/commands/local/cli_common/test_invoke_context.py +++ b/tests/unit/commands/local/cli_common/test_invoke_context.py @@ -717,7 +717,7 @@ def test_must_create_runner_using_warm_containers( self.assertEqual(result, runner_mock) WarmLambdaRuntimeMock.assert_called_with( - container_manager_mock, image_mock, mount_symlinks=False, no_mem_limit=False + container_manager_mock, image_mock, mount_symlinks=False, no_mem_limit=False, no_watch=False ) lambda_image_patch.assert_called_once_with(download_mock, True, True, invoke_images=None) LocalLambdaMock.assert_called_with( @@ -1401,7 +1401,13 @@ def test_must_pass_custom_region(self, add_account_id_to_global_mock, get_stacks class TestInvokeContext_add_account_id_to_global(TestCase): - def test_must_work_with_no_token(self): + @patch("samcli.commands.local.cli_common.invoke_context.get_boto_client_provider_with_config") + def test_must_work_with_no_token(self, get_boto_client_provider_with_config_mock): + # Mock the STS client to raise an exception (simulating no credentials). Or test might pickup system credential + sts_mock = Mock() + sts_mock.get_caller_identity.side_effect = Exception("No credentials") + get_boto_client_provider_with_config_mock.return_value.return_value = sts_mock + invoke_context = InvokeContext("template_file") invoke_context._add_account_id_to_global() self.assertIsNone(invoke_context._global_parameter_overrides) diff --git a/tests/unit/commands/local/start_api/test_cli.py b/tests/unit/commands/local/start_api/test_cli.py index 58b8d28a0f5..a3fe4f3a208 100644 --- a/tests/unit/commands/local/start_api/test_cli.py +++ b/tests/unit/commands/local/start_api/test_cli.py @@ -57,6 +57,7 @@ def setUp(self): self.container_host_interface = "127.0.0.1" self.invoke_image = () self.no_mem_limit = False + self.no_watch = False @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") @patch("samcli.commands.local.lib.local_api_service.LocalApiService") @@ -99,6 +100,7 @@ def test_cli_must_setup_context_and_start_service(self, local_api_service_mock, add_host=self.add_host, invoke_images={}, no_mem_limit=self.no_mem_limit, + no_watch=self.no_watch, ) local_api_service_mock.assert_called_with( @@ -112,6 +114,55 @@ def test_cli_must_setup_context_and_start_service(self, local_api_service_mock, service_mock.start.assert_called_with() + @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") + @patch("samcli.commands.local.lib.local_api_service.LocalApiService") + def test_cli_must_pass_no_watch_flag_when_set(self, local_api_service_mock, invoke_context_mock): + # Mock the __enter__ method to return a object inside a context manager + context_mock = Mock() + invoke_context_mock.return_value.__enter__.return_value = context_mock + + service_mock = Mock() + local_api_service_mock.return_value = service_mock + + # Set no_watch to True + self.no_watch = True + self.warm_containers = None + self.debug_function = None + self.disable_authorizer = False + + self.call_cli() + + # Verify that no_watch=True was passed to InvokeContext + invoke_context_mock.assert_called_with( + template_file=self.template, + function_identifier=None, + env_vars_file=self.env_vars, + docker_volume_basedir=self.docker_volume_basedir, + docker_network=self.docker_network, + log_file=self.log_file, + skip_pull_image=self.skip_pull_image, + debug_ports=self.debug_ports, + debug_args=self.debug_args, + debugger_path=self.debugger_path, + container_env_vars_file=self.container_env_vars, + parameter_overrides=self.parameter_overrides, + layer_cache_basedir=self.layer_cache_basedir, + force_image_build=self.force_image_build, + aws_region=self.region_name, + aws_profile=self.profile, + warm_container_initialization_mode=self.warm_containers, + debug_function=self.debug_function, + shutdown=self.shutdown, + container_host=self.container_host, + container_host_interface=self.container_host_interface, + add_host=self.add_host, + invoke_images={}, + no_mem_limit=self.no_mem_limit, + no_watch=True, # Verify this is True + ) + + service_mock.start.assert_called_with() + @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") @patch("samcli.commands.local.lib.local_api_service.LocalApiService") def test_must_raise_if_no_api_defined(self, local_api_service_mock, invoke_context_mock): @@ -231,4 +282,5 @@ def call_cli(self): disable_authorizer=self.disable_authorizer, add_host=self.add_host, no_mem_limit=self.no_mem_limit, + no_watch=self.no_watch, ) diff --git a/tests/unit/commands/local/start_lambda/test_cli.py b/tests/unit/commands/local/start_lambda/test_cli.py index 75531ba255f..7dc760af878 100644 --- a/tests/unit/commands/local/start_lambda/test_cli.py +++ b/tests/unit/commands/local/start_lambda/test_cli.py @@ -46,6 +46,7 @@ def setUp(self): self.invoke_image = () self.hook_name = None self.no_mem_limit = False + self.no_watch = False @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") @patch("samcli.commands.local.lib.local_lambda_service.LocalLambdaService") @@ -86,12 +87,61 @@ def test_cli_must_setup_context_and_start_service(self, local_lambda_service_moc add_host=self.add_host, invoke_images={}, no_mem_limit=self.no_mem_limit, + no_watch=self.no_watch, ) local_lambda_service_mock.assert_called_with(lambda_invoke_context=context_mock, port=self.port, host=self.host) service_mock.start.assert_called_with() + @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") + @patch("samcli.commands.local.lib.local_lambda_service.LocalLambdaService") + def test_cli_must_pass_no_watch_flag_when_set(self, local_lambda_service_mock, invoke_context_mock): + # Mock the __enter__ method to return a object inside a context manager + context_mock = Mock() + invoke_context_mock.return_value.__enter__.return_value = context_mock + + service_mock = Mock() + local_lambda_service_mock.return_value = service_mock + + # Set no_watch to True + self.no_watch = True + self.warm_containers = None + self.debug_function = None + + self.call_cli() + + # Verify that no_watch=True was passed to InvokeContext + invoke_context_mock.assert_called_with( + template_file=self.template, + function_identifier=None, + env_vars_file=self.env_vars, + container_env_vars_file=self.container_env_vars, + docker_volume_basedir=self.docker_volume_basedir, + docker_network=self.docker_network, + log_file=self.log_file, + skip_pull_image=self.skip_pull_image, + debug_ports=self.debug_ports, + debug_args=self.debug_args, + debugger_path=self.debugger_path, + parameter_overrides=self.parameter_overrides, + layer_cache_basedir=self.layer_cache_basedir, + force_image_build=self.force_image_build, + aws_region=self.region_name, + aws_profile=self.profile, + warm_container_initialization_mode=self.warm_containers, + debug_function=self.debug_function, + shutdown=self.shutdown, + container_host=self.container_host, + container_host_interface=self.container_host_interface, + add_host=self.add_host, + invoke_images={}, + no_mem_limit=self.no_mem_limit, + no_watch=True, # Verify this is True + ) + + service_mock.start.assert_called_with() + @parameterized.expand( [ (InvalidSamDocumentException("bad template"), "bad template"), @@ -188,4 +238,5 @@ def call_cli(self): invoke_image=self.invoke_image, hook_name=self.hook_name, no_mem_limit=self.no_mem_limit, + no_watch=self.no_watch, ) diff --git a/tests/unit/commands/samconfig/test_samconfig.py b/tests/unit/commands/samconfig/test_samconfig.py index 666a54d0e32..bcbc5b9c69e 100644 --- a/tests/unit/commands/samconfig/test_samconfig.py +++ b/tests/unit/commands/samconfig/test_samconfig.py @@ -724,6 +724,7 @@ def test_local_start_api(self, do_cli_mock): None, None, False, + False, ) @patch("samcli.commands.local.start_lambda.cli.do_cli") @@ -789,6 +790,7 @@ def test_local_start_lambda(self, do_cli_mock): ("image",), None, False, + False, ) @patch("samcli.lib.cli_validation.image_repository_validation._is_all_image_funcs_provided") @@ -1681,6 +1683,7 @@ def test_override_with_cli_params(self, do_cli_mock): ("image",), None, False, + False, ) @patch("samcli.commands.local.start_lambda.cli.do_cli") @@ -1780,6 +1783,7 @@ def test_override_with_cli_params_and_envvars(self, do_cli_mock): ("image",), None, True, + False, ) @patch("samcli.commands.validate.validate.do_cli") From 1b7aff8353fb908ef11f81f1b1e06ec3936010f8 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 29 Sep 2025 10:10:51 -0700 Subject: [PATCH 4/7] black lint fix --- samcli/lib/providers/sam_function_provider.py | 2 +- samcli/lib/utils/file_observer.py | 30 +++++++++++++------ samcli/local/lambdafn/runtime.py | 9 ++---- schema/samcli.json | 14 +++++++-- .../local/cli_common/test_invoke_context.py | 2 +- .../commands/local/start_lambda/test_cli.py | 2 +- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/samcli/lib/providers/sam_function_provider.py b/samcli/lib/providers/sam_function_provider.py index 91d7b19026f..ff046e8f9da 100644 --- a/samcli/lib/providers/sam_function_provider.py +++ b/samcli/lib/providers/sam_function_provider.py @@ -832,7 +832,7 @@ def __init__( self.parent_templates_paths.append(stack.location) self.is_changed = False - + # Only initialize file watcher if no_watch is False if not no_watch: self._observer: Optional[FileObserver] = FileObserver(self._set_templates_changed) diff --git a/samcli/lib/utils/file_observer.py b/samcli/lib/utils/file_observer.py index 3ca59fd7281..86fe54139a3 100644 --- a/samcli/lib/utils/file_observer.py +++ b/samcli/lib/utils/file_observer.py @@ -183,15 +183,22 @@ def _on_change(self, resources: List[str], package_type: str) -> None: package_type: str determine if the changed resource is a source code path or an image name """ - LOG.debug("Acquiring lock for _on_change to process %s %s resource changes: %s", - len(resources), package_type, resources) + LOG.debug( + "Acquiring lock for _on_change to process %s %s resource changes: %s", + len(resources), + package_type, + resources, + ) with self._watch_lock: changed_functions: List[FunctionConfig] = [] for resource in resources: if self._observed_functions[package_type].get(resource, None): changed_functions += self._observed_functions[package_type][resource] - LOG.debug("Acquired lock and processing %s changed functions from %s resources", - len(changed_functions), len(resources)) + LOG.debug( + "Acquired lock and processing %s changed functions from %s resources", + len(changed_functions), + len(resources), + ) self._input_on_change(changed_functions) def watch(self, function_config: FunctionConfig) -> None: @@ -208,13 +215,18 @@ def watch(self, function_config: FunctionConfig) -> None: ObserverException: if not able to observe the input function source path/image """ - LOG.debug("Acquiring lock for watch to observe %s function: %s", - function_config.packagetype, function_config.name) + LOG.debug( + "Acquiring lock for watch to observe %s function: %s", function_config.packagetype, function_config.name + ) with self._watch_lock: if self.get_resources.get(function_config.packagetype, None): resources = self.get_resources[function_config.packagetype](function_config) - LOG.debug("Acquired lock for watch, observing %s resources for function %s: %s", - len(resources), function_config.name, resources) + LOG.debug( + "Acquired lock for watch, observing %s resources for function %s: %s", + len(resources), + function_config.name, + resources, + ) for resource in resources: functions = self._observed_functions[function_config.packagetype].get(resource, []) functions += [function_config] @@ -432,7 +444,7 @@ def __init__(self) -> None: self._observed_watches: Dict[str, ObservedWatch] = {} self._watch_dog_observed_paths: Dict[str, List[str]] = {} self._observer: BaseObserver = Observer() - + self._code_modification_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( patterns=["*"], ignore_patterns=[], ignore_directories=False ) diff --git a/samcli/local/lambdafn/runtime.py b/samcli/local/lambdafn/runtime.py index d177784955c..bd3231623f6 100644 --- a/samcli/local/lambdafn/runtime.py +++ b/samcli/local/lambdafn/runtime.py @@ -395,12 +395,9 @@ class WarmLambdaRuntime(LambdaRuntime): warm containers life cycle. """ - def __init__(self, container_manager, - image_builder, - observer=None, - mount_symlinks=False, - no_mem_limit=False, - no_watch=False): + def __init__( + self, container_manager, image_builder, observer=None, mount_symlinks=False, no_mem_limit=False, no_watch=False + ): """ Initialize the Local Lambda runtime diff --git a/schema/samcli.json b/schema/samcli.json index 5203d60bc7a..4de51e048a1 100644 --- a/schema/samcli.json +++ b/schema/samcli.json @@ -617,7 +617,7 @@ "properties": { "parameters": { "title": "Parameters for the local start api command", - "description": "Available parameters for the local start api command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the local start api command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\n* no_watch:\nDisable file watching. Local code changes will not reset running docker container.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -668,6 +668,11 @@ "type": "string", "description": "Path to SSL key file (default: None)" }, + "no_watch": { + "title": "no_watch", + "type": "boolean", + "description": "Disable file watching. Local code changes will not reset running docker container." + }, "template_file": { "title": "template_file", "type": "string", @@ -842,7 +847,7 @@ "properties": { "parameters": { "title": "Parameters for the local start lambda command", - "description": "Available parameters for the local start lambda command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the local start lambda command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* no_watch:\nDisable file watching. Local code changes will not reset running docker container.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -872,6 +877,11 @@ "description": "Local port number to listen on (default: '3001')", "default": 3001 }, + "no_watch": { + "title": "no_watch", + "type": "boolean", + "description": "Disable file watching. Local code changes will not reset running docker container." + }, "template_file": { "title": "template_file", "type": "string", diff --git a/tests/unit/commands/local/cli_common/test_invoke_context.py b/tests/unit/commands/local/cli_common/test_invoke_context.py index 6871bff870b..c87d869dfbc 100644 --- a/tests/unit/commands/local/cli_common/test_invoke_context.py +++ b/tests/unit/commands/local/cli_common/test_invoke_context.py @@ -1407,7 +1407,7 @@ def test_must_work_with_no_token(self, get_boto_client_provider_with_config_mock sts_mock = Mock() sts_mock.get_caller_identity.side_effect = Exception("No credentials") get_boto_client_provider_with_config_mock.return_value.return_value = sts_mock - + invoke_context = InvokeContext("template_file") invoke_context._add_account_id_to_global() self.assertIsNone(invoke_context._global_parameter_overrides) diff --git a/tests/unit/commands/local/start_lambda/test_cli.py b/tests/unit/commands/local/start_lambda/test_cli.py index 7dc760af878..615b49d359a 100644 --- a/tests/unit/commands/local/start_lambda/test_cli.py +++ b/tests/unit/commands/local/start_lambda/test_cli.py @@ -108,7 +108,7 @@ def test_cli_must_pass_no_watch_flag_when_set(self, local_lambda_service_mock, i self.no_watch = True self.warm_containers = None self.debug_function = None - + self.call_cli() # Verify that no_watch=True was passed to InvokeContext From dafd82141cb9125fb4b35109ae6568b934de940a Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 13 Oct 2025 16:19:33 -0700 Subject: [PATCH 5/7] feedback --- .../local/cli_common/invoke_context.py | 8 +++++++ samcli/commands/local/cli_common/options.py | 7 ++++++ samcli/commands/local/start_api/cli.py | 6 ----- samcli/commands/local/start_lambda/cli.py | 6 ----- schema/samcli.json | 24 +++++++++---------- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/samcli/commands/local/cli_common/invoke_context.py b/samcli/commands/local/cli_common/invoke_context.py index 9ffe6e038cc..67758f183bd 100644 --- a/samcli/commands/local/cli_common/invoke_context.py +++ b/samcli/commands/local/cli_common/invoke_context.py @@ -240,6 +240,14 @@ def __enter__(self) -> "InvokeContext": ContainersMode.WARM: [self._stacks, self._parameter_overrides, self._global_parameter_overrides], ContainersMode.COLD: [self._stacks], } + + # ignore no_watch if _containers_mode is cold + if self._no_watch and self._containers_mode == ContainersMode.COLD: + self._no_watch = False + LOG.info( + "Warning: you supplied --no-watch but you did not specify --warm-containers, --no-watch will be ignored" + ) + # don't resolve the code URI immediately if we passed in docker vol by passing True for use_raw_codeuri # this way at the end the code URI will get resolved against the basedir option if self._docker_volume_basedir: diff --git a/samcli/commands/local/cli_common/options.py b/samcli/commands/local/cli_common/options.py index a1544814214..cca8ed68c23 100644 --- a/samcli/commands/local/cli_common/options.py +++ b/samcli/commands/local/cli_common/options.py @@ -264,6 +264,13 @@ def warm_containers_common_options(f): type=click.STRING, multiple=False, ), + click.option( + "--no-watch", + is_flag=True, + default=False, + help="Disable file watching when --warm-containers is set. " + "Local code changes will not reset running docker container.", + ), ] # Reverse the list to maintain ordering of options in help text printed with --help diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index eec0ba14fd0..46cb67ef1c2 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -95,12 +95,6 @@ required_param_lists=[["ssl_cert_file"]], help="Path to SSL key file (default: None)", ) -@click.option( - "--no-watch", - is_flag=True, - default=False, - help="Disable file watching. Local code changes will not reset running docker container.", -) @invoke_common_options @warm_containers_common_options @local_common_options diff --git a/samcli/commands/local/start_lambda/cli.py b/samcli/commands/local/start_lambda/cli.py index f0f62f69181..f7bf2115c41 100644 --- a/samcli/commands/local/start_lambda/cli.py +++ b/samcli/commands/local/start_lambda/cli.py @@ -59,12 +59,6 @@ ) @skip_prepare_infra_option @service_common_options(3001) -@click.option( - "--no-watch", - is_flag=True, - default=False, - help="Disable file watching. Local code changes will not reset running docker container.", -) @invoke_common_options @warm_containers_common_options @local_common_options diff --git a/schema/samcli.json b/schema/samcli.json index 4de51e048a1..f9a64615c1b 100644 --- a/schema/samcli.json +++ b/schema/samcli.json @@ -617,7 +617,7 @@ "properties": { "parameters": { "title": "Parameters for the local start api command", - "description": "Available parameters for the local start api command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\n* no_watch:\nDisable file watching. Local code changes will not reset running docker container.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the local start api command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* no_watch:\nDisable file watching when --warm-containers is set. Local code changes will not reset running docker container.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -668,11 +668,6 @@ "type": "string", "description": "Path to SSL key file (default: None)" }, - "no_watch": { - "title": "no_watch", - "type": "boolean", - "description": "Disable file watching. Local code changes will not reset running docker container." - }, "template_file": { "title": "template_file", "type": "string", @@ -759,6 +754,11 @@ "type": "string", "description": "Optional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args." }, + "no_watch": { + "title": "no_watch", + "type": "boolean", + "description": "Disable file watching when --warm-containers is set. Local code changes will not reset running docker container." + }, "shutdown": { "title": "shutdown", "type": "boolean", @@ -847,7 +847,7 @@ "properties": { "parameters": { "title": "Parameters for the local start lambda command", - "description": "Available parameters for the local start lambda command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* no_watch:\nDisable file watching. Local code changes will not reset running docker container.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the local start lambda command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing additional environment variables to be set within the container when used in a debugging session locally.\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* no_watch:\nDisable file watching when --warm-containers is set. Local code changes will not reset running docker container.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -877,11 +877,6 @@ "description": "Local port number to listen on (default: '3001')", "default": 3001 }, - "no_watch": { - "title": "no_watch", - "type": "boolean", - "description": "Disable file watching. Local code changes will not reset running docker container." - }, "template_file": { "title": "template_file", "type": "string", @@ -968,6 +963,11 @@ "type": "string", "description": "Optional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args." }, + "no_watch": { + "title": "no_watch", + "type": "boolean", + "description": "Disable file watching when --warm-containers is set. Local code changes will not reset running docker container." + }, "shutdown": { "title": "shutdown", "type": "boolean", From dba82a3ae6a853930214ade4269d82e6a49f7162 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 8 May 2026 15:45:37 -0700 Subject: [PATCH 6/7] chore: black formatting for runtime.py post-merge Apply black formatting after merge of develop into hot-swap-fix. --- samcli/local/lambdafn/runtime.py | 1 - 1 file changed, 1 deletion(-) diff --git a/samcli/local/lambdafn/runtime.py b/samcli/local/lambdafn/runtime.py index 660b9539338..94efe75327b 100644 --- a/samcli/local/lambdafn/runtime.py +++ b/samcli/local/lambdafn/runtime.py @@ -644,7 +644,6 @@ def create( return container - def _on_invoke_done(self, container): """ Cleanup the created resources, just before the invoke function ends. From 7763e3c129a55658c34fd8a9193e5bb4ea19c5be Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 8 May 2026 15:52:35 -0700 Subject: [PATCH 7/7] chore: post-review cleanups for --no-watch - Add no_watch docstring entry to RefreshableSamFunctionProvider. - Initialize self._observer = None up front in RefreshableSamFunctionProvider so the type annotation is unconditional and the no-watch branch is implied. - Drop incidental whitespace-only hunks (blank line in invoke_context.__enter__, blank line in SingletonFileObserver.__init__) flagged during review. --- samcli/commands/local/cli_common/invoke_context.py | 1 + samcli/lib/providers/sam_function_provider.py | 9 +++++---- samcli/lib/utils/file_observer.py | 1 - 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/samcli/commands/local/cli_common/invoke_context.py b/samcli/commands/local/cli_common/invoke_context.py index 1e6850a9799..39557e46f36 100644 --- a/samcli/commands/local/cli_common/invoke_context.py +++ b/samcli/commands/local/cli_common/invoke_context.py @@ -257,6 +257,7 @@ def __enter__(self) -> "InvokeContext": ContainersMode.WARM: RefreshableSamFunctionProvider, ContainersMode.COLD: SamFunctionProvider, } + _function_providers_args: Dict[ContainersMode, List[Any]] = { ContainersMode.WARM: [self._stacks, self._parameter_overrides, self._global_parameter_overrides], ContainersMode.COLD: [self._stacks], diff --git a/samcli/lib/providers/sam_function_provider.py b/samcli/lib/providers/sam_function_provider.py index 70758a62782..b1a9513b5c3 100644 --- a/samcli/lib/providers/sam_function_provider.py +++ b/samcli/lib/providers/sam_function_provider.py @@ -910,6 +910,8 @@ def __init__( Note(xinhol): use_raw_codeuri is temporary to fix a bug, and will be removed for a permanent solution. :param bool ignore_code_extraction_warnings: Ignores Log warnings :param tuple function_logical_ids: Optional tuple of function logical IDs to filter by + :param bool no_watch: If True, skip creating the FileObserver entirely. The provider will not + detect template changes and stack/function refreshes will not be triggered. """ # Store function_logical_ids before calling super().__init__ @@ -932,13 +934,12 @@ def __init__( self.is_changed = False - # Only initialize file watcher if no_watch is False + self._observer: Optional[FileObserver] = None + # Only initialize file watcher when --no-watch is not set if not no_watch: - self._observer: Optional[FileObserver] = FileObserver(self._set_templates_changed) + self._observer = FileObserver(self._set_templates_changed) self._observer.start() self._watch_stack_templates(stacks) - else: - self._observer = None @property def stacks(self) -> List[Stack]: diff --git a/samcli/lib/utils/file_observer.py b/samcli/lib/utils/file_observer.py index 3bb5c48c4cd..52f6d42f78a 100644 --- a/samcli/lib/utils/file_observer.py +++ b/samcli/lib/utils/file_observer.py @@ -444,7 +444,6 @@ def __init__(self) -> None: self._observed_watches: Dict[str, ObservedWatch] = {} self._watch_dog_observed_paths: Dict[str, List[str]] = {} self._observer: BaseObserver = Observer() - self._code_modification_handler: PatternMatchingEventHandler = PatternMatchingEventHandler( patterns=["*"], ignore_patterns=[], ignore_directories=False )