Skip to content

Commit 07afaa4

Browse files
committed
--requirements-file option
1 parent e0d03d6 commit 07afaa4

File tree

4 files changed

+216
-72
lines changed

4 files changed

+216
-72
lines changed

rsconnect/environment.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def from_dict(
114114
def create_python_environment(
115115
cls,
116116
directory: str,
117-
force_generate: bool = False,
117+
requirements_file: typing.Optional[str] = "requirements.txt",
118118
python: typing.Optional[str] = None,
119119
override_python_version: typing.Optional[str] = None,
120120
app_file: typing.Optional[str] = None,
@@ -125,8 +125,8 @@ def create_python_environment(
125125
If no Python executable is provided, the current system Python executable is used.
126126
127127
:param directory: the project directory to inspect.
128-
:param force_generate: force generating "requirements.txt" to snapshot the environment
129-
packages even if it already exists.
128+
:param requirements_file: requirements file name relative to the project directory. If None,
129+
capture the environment via pip freeze.
130130
:param python: the Python executable of the environment to use for inspection.
131131
:param override_python_version: the Python version required by the project.
132132
:param app_file: the main application file to use for inspection.
@@ -138,9 +138,8 @@ def create_python_environment(
138138
else:
139139
module_file = app_file
140140

141-
# click.secho(' Deploying %s to server "%s"' % (directory, connect_server.url))
142141
_warn_on_ignored_manifest(directory)
143-
_warn_if_no_requirements_file(directory)
142+
_warn_if_no_requirements_file(directory, requirements_file)
144143
_warn_if_environment_directory(directory)
145144

146145
python_version_requirement = pyproject.detect_python_version_requirement(directory)
@@ -163,7 +162,7 @@ def create_python_environment(
163162
python_version_requirement = f"=={override_python_version}"
164163

165164
# with cli_feedback("Inspecting Python environment"):
166-
environment = cls._get_python_env_info(module_file, python, force_generate)
165+
environment = cls._get_python_env_info(module_file, python, requirements_file=requirements_file)
167166
environment.python_version_requirement = python_version_requirement
168167

169168
if override_python_version:
@@ -181,29 +180,33 @@ def create_python_environment(
181180
# Derive allow_uv from selection
182181
environment.package_manager_allow_uv = selected_package_manager is PackageInstaller.UV
183182

184-
if force_generate:
183+
if requirements_file is None:
185184
_warn_on_ignored_requirements(directory, environment.filename)
186185

187186
return environment
188187

189188
@classmethod
190189
def _get_python_env_info(
191-
cls, file_name: str, python: typing.Optional[str], force_generate: bool = False
190+
cls,
191+
file_name: str,
192+
python: typing.Optional[str],
193+
requirements_file: typing.Optional[str] = "requirements.txt",
192194
) -> "Environment":
193195
"""
194196
Gathers the python and environment information relating to the specified file
195197
with an eye to deploy it.
196198
197199
:param file_name: the primary file being deployed.
198200
:param python: the optional name of a Python executable.
199-
:param force_generate: force generating "requirements.txt" or "environment.yml",
200-
even if it already exists.
201+
:param requirements_file: which requirements file to read. If None, generate via pip freeze.
201202
:return: information about the version of Python in use plus some environmental
202203
stuff.
203204
"""
204205
python = which_python(python)
205206
logger.debug("Python: %s" % python)
206-
environment = cls._inspect_environment(python, os.path.dirname(file_name), force_generate=force_generate)
207+
environment = cls._inspect_environment(
208+
python, os.path.dirname(file_name), requirements_file=requirements_file
209+
)
207210
if environment.error:
208211
raise RSConnectException(environment.error)
209212
logger.debug("Python: %s" % python)
@@ -215,21 +218,16 @@ def _inspect_environment(
215218
cls,
216219
python: str,
217220
directory: str,
218-
force_generate: bool = False,
221+
requirements_file: typing.Optional[str] = "requirements.txt",
219222
check_output: typing.Callable[..., bytes] = subprocess.check_output,
220223
) -> "Environment":
221224
"""Run the environment inspector using the specified python binary.
222225
223226
Returns a dictionary of information about the environment,
224227
or containing an "error" field if an error occurred.
225228
"""
226-
flags: typing.List[str] = []
227-
if force_generate:
228-
flags.append("f")
229-
230229
args = [python, "-m", "rsconnect.subprocesses.inspect_environment"]
231-
if flags:
232-
args.append("-" + "".join(flags))
230+
args.extend(["--requirements-file", requirements_file or "none"])
233231
args.append(directory)
234232

235233
try:
@@ -321,17 +319,28 @@ def _warn_on_ignored_manifest(directory: str) -> None:
321319
)
322320

323321

324-
def _warn_if_no_requirements_file(directory: str) -> None:
322+
def _warn_if_no_requirements_file(directory: str, requirements_file: typing.Optional[str]) -> None:
325323
"""
326-
Checks for the existence of a file called requirements.txt in the given directory.
327-
If it's not there, a warning will be printed.
324+
Check that a requirements file exists, and that it lives inside the deployment directory.
328325
329326
:param directory: the directory to check in.
327+
:param requirements_file: the name of the requirements file, or None to skip the check.
330328
"""
331-
if not os.path.exists(os.path.join(directory, "requirements.txt")):
329+
if requirements_file is None:
330+
return
331+
332+
directory_path = pathlib.Path(directory)
333+
requirements_file_path = directory_path / pathlib.Path(requirements_file)
334+
if directory_path not in requirements_file_path.parents:
335+
click.secho(
336+
" Warning: The requirements file '%s' is outside of the deployment directory.\n" % requirements_file,
337+
fg="red",
338+
)
339+
340+
if not requirements_file_path.exists():
332341
click.secho(
333342
" Warning: Capturing the environment using 'pip freeze'.\n"
334-
" Consider creating a requirements.txt file instead.",
343+
" Consider creating a %s file instead." % requirements_file,
335344
fg="yellow",
336345
)
337346

0 commit comments

Comments
 (0)