in src/shared/sam/debugger/pythonSamDebug.ts [79:212]
export async function makePythonDebugConfig(
config: SamLaunchRequestArgs
): Promise<PythonDebugConfiguration | PythonCloud9DebugConfiguration> {
if (!config.baseBuildDir) {
throw Error('invalid state: config.baseBuildDir was not set')
}
if (!config.codeRoot) {
// Last-resort attempt to discover the project root (when there is no
// `launch.json` nor `template.yaml`).
config.codeRoot = await getSamProjectDirPathForFile(config?.templatePath ?? config.documentUri!.fsPath)
if (!config.codeRoot) {
// TODO: return error and show it at the caller.
throw Error('missing launch.json, template.yaml, and failed to discover project root')
}
}
config.codeRoot = pathutil.normalize(config.codeRoot)
let manifestPath: string | undefined
if (!config.noDebug) {
const isImageLambda = isImageLambdaConfig(config)
if (!config.useIkpdb) {
// Mounted in the Docker container as: /tmp/lambci_debug_files
config.debuggerPath = globals.context.asAbsolutePath(path.join('resources', 'debugger'))
// NOTE: SAM CLI splits on each *single* space in `--debug-args`!
// Extra spaces will be passed as spurious "empty" arguments :(
const debugArgs = `${DEBUGPY_WRAPPER_PATH} --listen 0.0.0.0:${config.debugPort} --wait-for-client --log-to-stderr`
if (isImageLambda) {
const params = getPythonExeAndBootstrap(config.runtime)
config.debugArgs = [`${params.python} ${debugArgs} ${params.boostrap}`]
} else {
config.debugArgs = [debugArgs]
}
} else {
// -ikpdb-log: https://ikpdb.readthedocs.io/en/1.x/api.html?highlight=log#ikpdb.IKPdbLogger
// n,N: Network (noisy)
// b,B: Breakpoints
// e,E: Expression evaluation
// x,X: Execution
// f,F: Frame
// p,P: Path manipulation
// g,G: Global debugger
//
// Level "G" is not too noisy, and is required because it emits the
// "IKP3db listening on" string (`WAIT_FOR_DEBUGGER_MESSAGES`).
const logArg = getLogger().logLevelEnabled('debug') ? '--ikpdb-log=BEXFPG' : '--ikpdb-log=G'
const ccwd = pathutil.normalize(
getWorkspaceRelativePath(config.codeRoot, { workspaceFolders: [config.workspaceFolder] }) ?? 'error'
)
// NOTE: SAM CLI splits on each *single* space in `--debug-args`!
// Extra spaces will be passed as spurious "empty" arguments :(
//
// -u: (python arg) unbuffered binary stdout/stderr
//
// -ik_ccwd: Must be relative to /var/task, because ikpdb tries to
// resolve filepaths in the Docker container and produces
// nonsense as a "fallback". See `ikp3db.py:normalize_path_in()`:
// https://github.com/cmorisse/ikp3db/blob/eda176a1d4e0b1167466705a26ae4dd5c4188d36/ikp3db.py#L659
// --ikpdb-protocol=vscode:
// For https://github.com/cmorisse/vscode-ikp3db
// Requires ikp3db 1.5 (unreleased): https://github.com/cmorisse/ikp3db/pull/12
config.debugArgs = [
`-m ikp3db --ikpdb-address=0.0.0.0 --ikpdb-port=${config.debugPort} -ik_ccwd=${ccwd} -ik_cwd=/var/task ${logArg}`,
]
}
manifestPath = await makePythonDebugManifest({
isImageLambda: isImageLambda,
samProjectCodeRoot: config.codeRoot,
outputDir: config.baseBuildDir,
useIkpdb: !!config.useIkpdb,
})
}
let pathMappings: PythonPathMapping[]
if (config.lambda?.pathMappings !== undefined) {
pathMappings = config.lambda.pathMappings
} else {
pathMappings = getLocalRootVariants(config.codeRoot).map<PythonPathMapping>(variant => {
return {
localRoot: variant,
remoteRoot: '/var/task',
}
})
}
if (config.useIkpdb) {
// Documentation:
// https://github.com/cmorisse/vscode-ikp3db/blob/master/documentation/debug_configurations_reference.md
return {
...config,
type: 'ikp3db',
request: config.noDebug ? 'launch' : 'attach',
runtimeFamily: RuntimeFamily.Python,
manifestPath: manifestPath,
sam: {
...config.sam,
// Needed to build ikp3db which has a C build step.
// https://github.com/aws/aws-sam-cli/issues/1840
containerBuild: true,
},
// cloud9 debugger fields:
port: config.debugPort ?? -1,
localRoot: config.codeRoot,
remoteRoot: '/var/task',
address: 'localhost',
}
}
// Make debugpy output log information if our loglevel is at 'debug'
if (!config.noDebug && getLogger().logLevelEnabled('debug')) {
config.debugArgs![0] += ' --debug'
}
return {
...config,
type: 'python',
request: config.noDebug ? 'launch' : 'attach',
runtimeFamily: RuntimeFamily.Python,
//
// Python-specific fields.
//
manifestPath: manifestPath,
port: config.debugPort ?? -1,
host: 'localhost',
pathMappings,
// Disable redirectOutput, we collect child process stdout/stderr and
// explicitly write to Debug Console.
redirectOutput: false,
}
}