I am able to run the python script from my sveltekit app using exec using the code below. const command = `python "${pythonScriptPath}" ${plateID} "${formattedProdDate}"`; is the
command where I run the python script. I need to run this python script in a virtual environment.
In my python script I created a virtual environment by added an empty .venv folder and running pipenv install. When I am in the directory I run pipenv run python get-pdf.py string date
which will run the python script in the virtual environment.
So I tried to modify where I call the script in my sveltekit app const command = `pipenv run python "${pythonScriptPath}" ${plateID} "${formattedProdDate}"
but when I do this it creates a virtual environemnt for my sveltekit app instead of running the virtual environment I already have installed. How can I run the python script using the virtual environment that I already have installed inside the directory where the python script is?
import fs from 'fs/promises';
import { exec } from 'node:child_process';
import { prisma } from "$lib/server/prisma.js";
import { format } from 'date-fns';
export async function GET({ url }: { url: { searchParams: URLSearchParams } }) {
const plateID = url.searchParams.get('plateID') || '';
console.log('Plate id:', plateID);
// Retrieve production end date from the database
const prodEndDateRaw = await prisma.$queryRaw<{ PROD_END: Date }[]>`
SELECT PROD_END
FROM LTDB.LTDB.PIECE_TIMES pt
WHERE ID_PIECE = ${plateID}
`;
// Type casting and null check for prodEndDate
const prodEndDate = prodEndDateRaw[0]?.PROD_END as Date | undefined;
if (prodEndDate) {
// Format production end date
const formattedProdDate = format(prodEndDate, 'yyyy-MM-dd');
console.log(formattedProdDate);
const pythonScriptPath = 'C:\\scripts\\HTLReportScript\\get-pdf.py';
// Execute the command to run the Python script inside the virtual environment
const command = `python "${pythonScriptPath}" ${plateID} "${formattedProdDate}"`;
try {
console.log('Executing Python script...');
await new Promise<void>((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing Python script: ${error.message}`);
console.error('Python script stderr:', stderr);
reject(error);
} else {
console.log('Python script completed:', stdout);
resolve();
}
});
});
} catch (error: any) {
console.error('Error:', error.message);
return createServerErrorResponse('Internal Server Error');
}
// Read the file from the local destination
const localDestination = `C:\\scripts\\HTLReportScript\\Piece_${plateID}.pdf`;
const fileData = await readFile(localDestination);
// Set the appropriate headers for the response
const headers = {
'Content-Disposition': `attachment; filename=Piece_${plateID}.pdf`,
'Content-Type': 'application/pdf',
};
// Delete the file after sending it to the client
// await fs.unlink(localDestination);
// Send the file back to the client
return new Response(fileData, { headers });
} else {
console.error('Error: Production end date not found');
return createServerErrorResponse('Internal Server Error');
}
}
async function readFile(filePath: string): Promise<Buffer> {
try {
const data = await fs.readFile(filePath);
return data;
} catch (error: any) {
console.error(`Error reading file: ${error.message}`);
throw new Error('Internal Server Error');
}
}
function createServerErrorResponse(message: string) {
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
-----SOLUTION thanks to Mikko Ohtamaa------
Thanks to Mikko I was able to get this fixed. I had to run the virtual environment by going into the directory where the python script was. I decided to keep my script into C\Scripts on both my dev machine and the server so I did not have to make any configurations when I moved the build to the server. I changed const command = `python "${pythonScriptPath}" ${plateID} "${formattedProdDate}"```` to
const command = cd C:\\scripts\\HTLReportScript && C:\\scripts\\HTLReportScript\\.venv\\Scripts\\activate && python get-pdf.py ${plateID} ${formattedProdDate}
;``` This opens the virtual env and then runs the python code. Here is the updated code
import fs from 'fs/promises';
import { exec } from 'node:child_process';
import { prisma } from "$lib/server/prisma.js";
import { format } from 'date-fns';
export async function GET({ url }: { url: { searchParams: URLSearchParams } }) {
const plateID = url.searchParams.get('plateID') || '';
console.log('Plate id:', plateID);
// Retrieve production end date from the database
const prodEndDateRaw = await prisma.$queryRaw<{ PROD_END: Date }[]>`
SELECT PROD_END
FROM LTDB.LTDB.PIECE_TIMES pt
WHERE ID_PIECE = ${plateID}
`;
// Type casting and null check for prodEndDate
const prodEndDate = prodEndDateRaw[0]?.PROD_END as Date | undefined;
if (prodEndDate) {
// Format production end date
const formattedProdDate = format(prodEndDate, 'yyyy-MM-dd');
console.log(formattedProdDate);
const pythonScriptPath = 'C:\\scripts\\HTLReportScript\\get-pdf.py';
// Execute the command to run the Python script inside the virtual environment
const command = `cd C:\\scripts\\HTLReportScript && C:\\scripts\\HTLReportScript\\.venv\\Scripts\\activate && python get-pdf.py ${plateID} ${formattedProdDate}`;
try {
console.log('Executing Python script...');
await new Promise<void>((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing Python script: ${error.message}`);
console.error('Python script stderr:', stderr);
reject(error);
} else {
console.log('Python script completed:', stdout);
resolve();
}
});
});
} catch (error: any) {
console.error('Error:', error.message);
return createServerErrorResponse('Internal Server Error');
}
// Read the file from the local destination
const localDestination = `C:\\scripts\\HTLReportScript\\Piece_${plateID}.pdf`;
const fileData = await readFile(localDestination);
// Set the appropriate headers for the response
const headers = {
'Content-Disposition': `attachment; filename=Piece_${plateID}.pdf`,
'Content-Type': 'application/pdf',
};
// Delete the file after sending it to the client
await fs.unlink(localDestination);
// Send the file back to the client
return new Response(fileData, { headers });
} else {
console.error('Error: Production end date not found');
return createServerErrorResponse('Internal Server Error');
}
}
async function readFile(filePath: string): Promise<Buffer> {
try {
const data = await fs.readFile(filePath);
return data;
} catch (error: any) {
console.error(`Error reading file: ${error.message}`);
throw new Error('Internal Server Error');
}
}
function createServerErrorResponse(message: string) {
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
You can directly call python
command in the virtualenv using an absolute path. This way python
is always the correct virtualenv one. Each virtualenv drops a new python
binary in its directory.
For example I have
/Users/moo/Library/Caches/pypoetry/virtualenvs/trade-executor-8Oz1GdY1-py3.10/bin/python
And I can run any Python script with this virtual environment by:
/Users/moo/Library/Caches/pypoetry/virtualenvs/trade-executor-8Oz1GdY1-py3.10/bin/python myscript.py
Hope this helps.