Search code examples
pythonmatplotliblaravel-10laragon

I get 'RuntimeError: Could not determine home directory' when trying to run Python script on laravel 10


At the time of posting this, I'm using the latest Laragon (Full 6.0) for local server and Laravel 10. I have a project which I need to run a Python script and display the results in a browser. Here is the sample code in my Controller.

use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

public function index()
    {
        $pythonCode = public_path('python/linear-regression-sample.py');
        $process = new Process(['python', $pythonCode]);
        $process->run();
        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }
        $output = $process->getOutput();

        // $scriptPath = public_path('python/linear-regression-sample.py');
        // $output = shell_exec('python ' . $scriptPath);

        // Pass the output to the view
        return view('project.predictions.predictions', ['output' => $output]);
    }

I'm using the built in Laragon Python intepreter (3.10.6). There are 2 methods to run the Python script, one is using Symfony and the other is using shell_exec. Both methods run fine on a simple Python script such as print("Hello, World!"). However, when I import matplotlib (3.8.0), only shell_exec that works while Symfony method gave me a runtime error. Here is the error.

Symfony \ Component \ Process \ Exception \ ProcessFailedException
PHP 8.1.10, 10.24.0
The command "python "C:\laragon\www\DLTP-1\public\python/linear-regression-sample.py"" failed. Exit Code: 1(General error) Working directory: C:\laragon\www\DLTP-1\public Output: 
================ Error Output: ================ 
Traceback (most recent call last): 
  File "C:\laragon\www\DLTP-1\public\python\linear-regression-sample.py", line 6, in <module> 
    import matplotlib.pyplot as plt 
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 990, in <module> 
    dict.update(rcParams, _rc_params_in_file(matplotlib_fname())) 
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 638, in matplotlib_fname 
    for fname in gen_candidates(): 
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 635, in gen_candidates 
    yield os.path.join(get_configdir(), 'matplotlibrc') 
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 348, in wrapper 
    ret = func(**kwargs) 
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 581, in get_configdir 
    return _get_config_or_cache_dir(_get_xdg_config_dir)
  File "C:\laragon\bin\python\python-3.10\lib\site-packages\matplotlib\__init__.py", line 536, in _get_config_or_cache_dir 
    configdir = Path.home() / ".matplotlib" 
  File "C:\laragon\bin\python\python-3.10\lib\pathlib.py", line 1000, in home 
    return cls("~").expanduser() 
  File "C:\laragon\bin\python\python-3.10\lib\pathlib.py", line 1440, in expanduser 
    raise RuntimeError("Could not determine home directory.") RuntimeError: Could not determine home directory.

I may be able to get away with using shell_exec but I still want to know why this happened and how to solve it since this might be helpful for any future project.

And if this is any helpful idk, here is the code snippet of the Python script that might give another hint?

# Import libraries
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import os

# Read file
script_dir = os.path.dirname(os.path.realpath(__file__))
file_path = os.path.join(script_dir, 'Sample Data.xlsx')
data = pd.read_excel(file_path)

The rest of the Python script is just the usual get, train, test data and show graph.

I tried ChatGPT but it kept telling me something about os and I searched it up on the internet, found a stackoverflow post saying to manually set up the os, which I tried but still not working. ChatGPT gave quite similar response btw. Link 1 So in Python script I tried,

# Import libraries
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import os

os.environ['HOME'] = 'C:/laragon/bin/python/python-3.10/lib/site-packages/matplotlib/__init__.py'
# os.environ['HOME'] = 'C:/laragon/bin/python/python-3.10/python.exe'

# Read file
script_dir = os.path.dirname(os.path.realpath(__file__))
file_path = os.path.join(script_dir, 'Sample Data.xlsx')
data = pd.read_excel(file_path)

Then this is the closest related post but is not helpful at all. Link 2

Thanks in advance!

Update: Solved the issue! Below is the code snippet.

# SET THIS OS SETTINGS BEFORE IMPORTING MATPLOTLIB!!!
# Set the custom path
import os
os.environ['MPLCONFIGDIR'] = "C:/laragon/www/DLTP-1/public/python"
# os.environ['MPLCONFIGDIR'] = "< your/custom/path >"

import matplotlib.pyplot as plt

In my case, I had to use 'MPLCONFIGDIR'. Here is the explanation from ChatGPT:

"The environment variable name MPLCONFIGDIR is specific to Matplotlib. Matplotlib looks for this specific environment variable to determine the location of its configuration directory.

The reason why using a different name for the environment variable won't work is that Matplotlib's code is specifically designed to look for the MPLCONFIGDIR variable. When Matplotlib initializes, it checks for the presence of this variable and uses its value as the configuration directory.

If you want to use a different name for the environment variable, you would need to modify the Matplotlib source code to look for that specific variable name instead. This would require making changes to the Matplotlib package itself, which is not recommended unless you have a specific need for it.

In general, it's best to stick with the standard variable name MPLCONFIGDIR when working with Matplotlib. This ensures compatibility with the library and avoids any potential issues or conflicts."


Solution

  • At import, matplotlib internally checks the for a configuration file in the current user's home directory. This is done using pathlib.Path.home. If you follow through the call chains in the source (1, 2, 3, 4), you can see that this ultimately calls os.path.expanduser.

    On Windows, os.path.expanduser relies on certain enviroment variables being set to resolve the home directory. Specifically USERPROFILE or HOMEPATH and HOMEDRIVE.

    It is likely that when the server invokes your script, these variables are not being set.

    You can remedy this by manually setting the USERPROFILE environment variable before importing matplotlib:

    os.environ["USERPROFILE"] = "<home directory of your choosing>"
    
    # Import matplotlib AFTER setting USERPROFILE.
    import matplotlib.pyplot as plt
    

    Note that you don't need to set USERPROFILE to your actual home directory, unless you want configuration files from it to be used.