Search code examples
pythonsveltekitpython-venv

How do I run a python script with virtual .venv from my sveltekit app?


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' },
    });
}


Solution

  • 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.