Skip to content

Commit f41d81e

Browse files
authored
feat(cli) - no-docker option
1 parent dc0f2ab commit f41d81e

File tree

4 files changed

+257
-13
lines changed

4 files changed

+257
-13
lines changed

python/rpdk/python/codegen.py

+26-7
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ def __init__(self):
5555
self.package_name = None
5656
self.package_root = None
5757
self._use_docker = None
58+
self._no_docker = None
5859
self._protocol_version = "2.0.0"
5960

6061
def _init_from_project(self, project):
6162
self.namespace = tuple(s.lower() for s in project.type_info)
6263
self.package_name = "_".join(self.namespace)
63-
self._use_docker = project.settings.get("use_docker")
64+
# Check config file for (legacy) 'useDocker' and use_docker settings
65+
self._use_docker = project.settings.get("useDocker") or project.settings.get(
66+
"use_docker"
67+
)
6468
self.package_root = project.root / "src"
6569

6670
def _init_settings(self, project):
@@ -78,14 +82,29 @@ def _init_settings(self, project):
7882
".resource", ".test_entrypoint"
7983
)
8084

81-
self._use_docker = self._use_docker or input_with_validation(
82-
"Use docker for platform-independent packaging (Y/n)?\n",
83-
validate_no,
84-
"This is highly recommended unless you are experienced \n"
85-
"with cross-platform Python packaging.",
86-
)
85+
# If use_docker specified in .rpdk-config file or cli switch
86+
# Ensure only 1 is true, with preference to use_docker
87+
if project.settings.get("use_docker") is True:
88+
self._use_docker = True
89+
self._no_docker = False
90+
# If no_docker specified in .rpdk-config file or cli switch
91+
elif project.settings.get("no_docker") is True:
92+
self._use_docker = False
93+
self._no_docker = True
94+
else:
95+
# If neither no_docker nor use_docker specified in .rpdk-config
96+
# file or cli switch, prompt to use containers or not
97+
self._use_docker = input_with_validation(
98+
"Use docker for platform-independent packaging (Y/n)?\n",
99+
validate_no,
100+
"This is highly recommended unless you are experienced \n"
101+
"with cross-platform Python packaging.",
102+
)
103+
self._no_docker = not self._use_docker
87104

105+
# project.settings will get saved into .rpdk-config by cloudformation-cli
88106
project.settings["use_docker"] = self._use_docker
107+
project.settings["no_docker"] = self._no_docker
89108
project.settings["protocolVersion"] = self._protocol_version
90109

91110
def init(self, project):

python/rpdk/python/parser.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ def setup_subparser(subparsers, parents, python_version, python_version_number):
99
)
1010
parser.set_defaults(language=python_version)
1111

12-
parser.add_argument(
12+
group = parser.add_mutually_exclusive_group()
13+
14+
group.add_argument(
1315
"-d",
1416
"--use-docker",
1517
action="store_true",
@@ -18,6 +20,13 @@ def setup_subparser(subparsers, parents, python_version, python_version_number):
1820
with cross-platform Python packaging.""",
1921
)
2022

23+
group.add_argument(
24+
"--no-docker",
25+
action="store_true",
26+
help="""Generally not recommended unless you are experienced
27+
with cross-platform Python packaging""",
28+
)
29+
2130
return parser
2231

2332

tests/plugin/codegen_test.py

+141-3
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,32 @@ def resource_project(tmp_path):
108108
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
109109
)
110110
with patch_plugins, patch_wizard:
111-
project.init(TYPE_NAME, PythonLanguagePlugin.NAME)
111+
project.init(
112+
TYPE_NAME,
113+
PythonLanguagePlugin.NAME,
114+
settings={"use_docker": False, "no_docker": True},
115+
)
116+
return project
117+
118+
119+
@pytest.fixture
120+
def resource_project_use_docker(tmp_path):
121+
project = Project(root=tmp_path)
122+
123+
patch_plugins = patch.dict(
124+
"rpdk.core.plugin_registry.PLUGIN_REGISTRY",
125+
{PythonLanguagePlugin.NAME: lambda: PythonLanguagePlugin},
126+
clear=True,
127+
)
128+
patch_wizard = patch(
129+
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
130+
)
131+
with patch_plugins, patch_wizard:
132+
project.init(
133+
TYPE_NAME,
134+
PythonLanguagePlugin.NAME,
135+
settings={"use_docker": True, "no_docker": False},
136+
)
112137
return project
113138

114139

@@ -125,7 +150,32 @@ def hook_project(tmp_path):
125150
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
126151
)
127152
with patch_plugins, patch_wizard:
128-
project.init_hook(TYPE_NAME, PythonLanguagePlugin.NAME)
153+
project.init_hook(
154+
TYPE_NAME,
155+
PythonLanguagePlugin.NAME,
156+
settings={"use_docker": False, "no_docker": True},
157+
)
158+
return project
159+
160+
161+
@pytest.fixture
162+
def hook_project_use_docker(tmp_path):
163+
project = Project(root=tmp_path)
164+
165+
patch_plugins = patch.dict(
166+
"rpdk.core.plugin_registry.PLUGIN_REGISTRY",
167+
{PythonLanguagePlugin.NAME: lambda: PythonLanguagePlugin},
168+
clear=True,
169+
)
170+
patch_wizard = patch(
171+
"rpdk.python.codegen.input_with_validation", autospec=True, side_effect=[False]
172+
)
173+
with patch_plugins, patch_wizard:
174+
project.init_hook(
175+
TYPE_NAME,
176+
PythonLanguagePlugin.NAME,
177+
settings={"use_docker": True, "no_docker": False},
178+
)
129179
return project
130180

131181

@@ -172,6 +222,7 @@ def test__remove_build_artifacts_file_not_found(tmp_path):
172222
def test_initialize_resource(resource_project):
173223
assert resource_project.settings == {
174224
"use_docker": False,
225+
"no_docker": True,
175226
"protocolVersion": "2.0.0",
176227
}
177228

@@ -209,8 +260,53 @@ def test_initialize_resource(resource_project):
209260
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())
210261

211262

263+
def test_initialize_resource_use_docker(resource_project_use_docker):
264+
assert resource_project_use_docker.settings == {
265+
"use_docker": True,
266+
"no_docker": False,
267+
"protocolVersion": "2.0.0",
268+
}
269+
270+
files = get_files_in_project(resource_project_use_docker)
271+
assert set(files) == {
272+
".gitignore",
273+
".rpdk-config",
274+
"README.md",
275+
"foo-bar-baz.json",
276+
"requirements.txt",
277+
f"{os.path.join('example_inputs', 'inputs_1_create.json')}",
278+
f"{os.path.join('example_inputs', 'inputs_1_invalid.json')}",
279+
f"{os.path.join('example_inputs', 'inputs_1_update.json')}",
280+
"example_inputs",
281+
"src",
282+
f"{os.path.join('src', 'foo_bar_baz')}",
283+
f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}",
284+
f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}",
285+
"template.yml",
286+
}
287+
288+
assert "__pycache__" in files[".gitignore"].read_text()
289+
assert SUPPORT_LIB_NAME in files["requirements.txt"].read_text()
290+
291+
readme = files["README.md"].read_text()
292+
assert resource_project_use_docker.type_name in readme
293+
assert SUPPORT_LIB_PKG in readme
294+
assert "handlers.py" in readme
295+
assert "models.py" in readme
296+
297+
assert resource_project_use_docker.entrypoint in files["template.yml"].read_text()
298+
299+
# this is a rough check the generated Python code is valid as far as syntax
300+
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}"].read_text())
301+
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())
302+
303+
212304
def test_initialize_hook(hook_project):
213-
assert hook_project.settings == {"use_docker": False, "protocolVersion": "2.0.0"}
305+
assert hook_project.settings == {
306+
"use_docker": False,
307+
"no_docker": True,
308+
"protocolVersion": "2.0.0",
309+
}
214310

215311
files = get_files_in_project(hook_project)
216312
assert set(files) == {
@@ -242,6 +338,43 @@ def test_initialize_hook(hook_project):
242338
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())
243339

244340

341+
def test_initialize_hook_use_docker(hook_project_use_docker):
342+
assert hook_project_use_docker.settings == {
343+
"use_docker": True,
344+
"no_docker": False,
345+
"protocolVersion": "2.0.0",
346+
}
347+
348+
files = get_files_in_project(hook_project_use_docker)
349+
assert set(files) == {
350+
".gitignore",
351+
".rpdk-config",
352+
"README.md",
353+
"foo-bar-baz.json",
354+
"requirements.txt",
355+
"src",
356+
f"{os.path.join('src', 'foo_bar_baz')}",
357+
f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}",
358+
f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}",
359+
"template.yml",
360+
}
361+
362+
assert "__pycache__" in files[".gitignore"].read_text()
363+
assert SUPPORT_LIB_NAME in files["requirements.txt"].read_text()
364+
365+
readme = files["README.md"].read_text()
366+
assert hook_project_use_docker.type_name in readme
367+
assert SUPPORT_LIB_PKG in readme
368+
assert "handlers.py" in readme
369+
assert "models.py" in readme
370+
371+
assert hook_project_use_docker.entrypoint in files["template.yml"].read_text()
372+
373+
# this is a rough check the generated Python code is valid as far as syntax
374+
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', '__init__.py')}"].read_text())
375+
ast.parse(files[f"{os.path.join('src', 'foo_bar_baz', 'handlers.py')}"].read_text())
376+
377+
245378
def test_generate_resource(resource_project):
246379
resource_project.load_schema()
247380
before = get_files_in_project(resource_project)
@@ -406,6 +539,7 @@ def test__pip_build_called_process_error(tmp_path):
406539

407540
def test__build_pip(plugin):
408541
plugin._use_docker = False
542+
plugin._no_docker = True
409543

410544
patch_pip = patch.object(plugin, "_pip_build", autospec=True)
411545
patch_docker = patch.object(plugin, "_docker_build", autospec=True)
@@ -455,6 +589,7 @@ def test__build_pip_windows(plugin):
455589

456590
def test__build_docker(plugin):
457591
plugin._use_docker = True
592+
plugin._no_docker = False
458593

459594
patch_pip = patch.object(plugin, "_pip_build", autospec=True)
460595
patch_docker = patch.object(plugin, "_docker_build", autospec=True)
@@ -468,6 +603,7 @@ def test__build_docker(plugin):
468603
# Test _build_docker on Linux/Unix-like systems
469604
def test__build_docker_posix(plugin):
470605
plugin._use_docker = True
606+
plugin._no_docker = False
471607

472608
patch_pip = patch.object(plugin, "_pip_build", autospec=True)
473609
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)
@@ -493,6 +629,7 @@ def test__build_docker_posix(plugin):
493629
# Test _build_docker on Windows
494630
def test__build_docker_windows(plugin):
495631
plugin._use_docker = True
632+
plugin._no_docker = False
496633

497634
patch_pip = patch.object(plugin, "_pip_build", autospec=True)
498635
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)
@@ -518,6 +655,7 @@ def test__build_docker_windows(plugin):
518655
# Test _build_docker if geteuid fails
519656
def test__build_docker_no_euid(plugin):
520657
plugin._use_docker = True
658+
plugin._no_docker = False
521659

522660
patch_pip = patch.object(plugin, "_pip_build", autospec=True)
523661
patch_from_env = patch("rpdk.python.codegen.docker.from_env", autospec=True)

0 commit comments

Comments
 (0)