Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialise environment from global Python variables #989

Merged
merged 7 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions bindings/python/scripts/dlite-validate
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import json
import re
from pathlib import Path

import dlite


def parse(url, driver=None, options="mode=r", id=None):
"""Loads an instance from storage.
Expand All @@ -22,6 +20,8 @@ def parse(url, driver=None, options="mode=r", id=None):
Returns:
A new instance.
"""
import dlite

loc = url.split("#", 1)[0].split("?",1)[0]
if driver is None:
driver = Path(loc).suffix.lstrip('.').lower()
Expand Down Expand Up @@ -102,6 +102,15 @@ def main():

args = parser.parse_args()

if not args.debug:
# Turn off DLite warnings about behavior changes
import __main__
__main__.DLITE_BEHAVIOR = True

# Delay importing dlite to after options are processed such that it is
# possible to configure DLite by settings variables in __main__.
import dlite

for path in args.storage_path:
dlite.storage_path.append(Path(path).resolve())

Expand Down
7 changes: 7 additions & 0 deletions src/config-paths.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,12 @@
#define dlite_TEMPLATES "@dlite_TEMPLATES@"
#define dlite_STORAGES "@dlite_STORAGES@"

/* dlite-pyembed.c does not imports config.h to avoid conflicts with Python .h
Include configurations needed by dlite-pyembed.c here */
#cmakedefine HAVE_SETENV
#cmakedefine HAVE__PUTENV_S
#cmakedefine HAVE_UNSETENV



#endif /* _DLITE_CONFIG_PATHS_H */
2 changes: 1 addition & 1 deletion src/dlite-behavior.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ int dlite_behavior_get(const char *name)
const char *ver = dlite_get_version(); // current version
b->value = (strcmp_semver(ver, b->version_new) >= 0) ? 1 : 0;

dlite_warnx("Behavior `%s` is not configured. "
dlite_warnx("Behavior change `%s` is not configured. "
"It will be enabled by default from v%s. "
"See https://sintef.github.io/dlite/user_guide/configure_behavior_changes.html for more info.",
b->name, b->version_new);
Expand Down
172 changes: 104 additions & 68 deletions src/pyembed/dlite-pyembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,85 +141,121 @@ void dlite_pyembed_initialise(void)
{
PyembedGlobals *g = get_globals();

if (!g->initialised &&
(!Py_IsInitialized() || !dlite_behavior_get("singleInterpreter"))) {
if (!g->initialised) {
g->initialised = 1;

#if defined(HAVE_SETENV) || defined(HAVE__PUTENV_S)
if (Py_IsInitialized()) {
/* Set environment variables from global variables in Python
starting with "DLITE_" */
PyObject *maindict = dlite_python_maindict();
PyObject *key, *value;
Py_ssize_t pos = 0;

while (PyDict_Next(maindict, &pos, &key, &value)) {
if (PyUnicode_Check(key)) {
const char *ckey = PyUnicode_AsUTF8AndSize(key, NULL);
assert(ckey);
if (strncmp(ckey, "DLITE_", 6) == 0) {
if (PyBool_Check(value)) {
if (PyObject_IsTrue(value)) setenv(ckey, "", 1);
} else if (PyLong_Check(value)) {
long v = PyLong_AsLong(value);
char cval[32];
snprintf(cval, sizeof(cval), "%ld", v);
setenv(ckey, cval, 1);
} else if (PyUnicode_Check(value)) {
const char *cval = PyUnicode_AsUTF8AndSize(value, NULL);
setenv(ckey, cval, 1);
} else {
dlite_warnx("Unsupported type for value of global variable `%s`. "
"Should be bool, str or int.", ckey);
}
}
}
}
}
#endif

if (!Py_IsInitialized() || !dlite_behavior_get("singleInterpreter")) {
/*
Initialise new Python interpreter

/*
Python 3.8 and later implements the new Python
Initialisation Configuration.
Python 3.8 and later implements the new Python
Initialisation Configuration.

More features were added in the following releases,
like `config.safe_path`, which was added in Python 3.11
More features were added in the following releases,
like `config.safe_path`, which was added in Python 3.11

The old Py_SetProgramName() was deprecated in Python 3.11.
The old Py_SetProgramName() was deprecated in Python 3.11.

In DLite, we switch to the new Python Initialisation
Configuration from Python 3.11.
*/
PyObject *sys=NULL, *sys_path=NULL, *path=NULL;
In DLite, we switch to the new Python Initialisation
Configuration from Python 3.11.
*/
PyObject *sys=NULL, *sys_path=NULL, *path=NULL;
#if PY_VERSION_HEX >= 0x030b0000 /* Python >= 3.11 */
/* New Python Initialisation Configuration */
PyStatus status;
PyConfig config;

PyConfig_InitPythonConfig(&config);
config.isolated = 0;
config.safe_path = 0;
config.use_environment = 1;
config.user_site_directory = 1;

/* If dlite is called from a python, reparse arguments to avoid
that they are stripped off... */
if (Py_IsInitialized()) {
int argc=0;
wchar_t **argv=NULL;
Py_GetArgcArgv(&argc, &argv);
config.parse_argv = 1;
status = PyConfig_SetArgv(&config, argc, argv);
if (PyStatus_Exception(status))
FAIL("failed configuring pyembed arguments");
}
/* New Python Initialisation Configuration */
PyStatus status;
PyConfig config;

PyConfig_InitPythonConfig(&config);
config.isolated = 0;
config.safe_path = 0;
config.use_environment = 1;
config.user_site_directory = 1;

/* If dlite is called from a python, reparse arguments to avoid
that they are stripped off... */
if (Py_IsInitialized()) {
int argc=0;
wchar_t **argv=NULL;
Py_GetArgcArgv(&argc, &argv);
config.parse_argv = 1;
status = PyConfig_SetArgv(&config, argc, argv);
if (PyStatus_Exception(status))
FAIL("failed configuring pyembed arguments");
}

status = PyConfig_SetBytesString(&config, &config.program_name, "dlite");
if (PyStatus_Exception(status))
FAIL("failed configuring pyembed program name");
status = PyConfig_SetBytesString(&config, &config.program_name, "dlite");
if (PyStatus_Exception(status))
FAIL("failed configuring pyembed program name");

status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
if (PyStatus_Exception(status))
FAIL("failed clearing pyembed config");
status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
if (PyStatus_Exception(status))
FAIL("failed clearing pyembed config");
#else
/* Old Initialisation */
wchar_t *progname;
/* Old Initialisation */
wchar_t *progname;

Py_Initialize();
Py_Initialize();

if (!(progname = Py_DecodeLocale("dlite", NULL))) {
dlite_err(1, "allocation/decoding failure");
return;
}
Py_SetProgramName(progname);
PyMem_RawFree(progname);
#endif

if (dlite_use_build_root()) {
if (!(sys = PyImport_ImportModule("sys")))
FAIL("cannot import sys");
if (!(sys_path = PyObject_GetAttrString(sys, "path")))
FAIL("cannot access sys.path");
if (!PyList_Check(sys_path))
FAIL("sys.path is not a list");
if (!(path = PyUnicode_FromString(dlite_PYTHONPATH)))
FAIL("cannot create python object for dlite_PYTHONPATH");
if (PyList_Insert(sys_path, 0, path))
FAIL1("cannot insert %s into sys.path", dlite_PYTHONPATH);
}
if (!(progname = Py_DecodeLocale("dlite", NULL))) {
dlite_err(1, "allocation/decoding failure");
return;
}
Py_SetProgramName(progname);
PyMem_RawFree(progname);
#endif

if (dlite_use_build_root()) {
if (!(sys = PyImport_ImportModule("sys")))
FAIL("cannot import sys");
if (!(sys_path = PyObject_GetAttrString(sys, "path")))
FAIL("cannot access sys.path");
if (!PyList_Check(sys_path))
FAIL("sys.path is not a list");
if (!(path = PyUnicode_FromString(dlite_PYTHONPATH)))
FAIL("cannot create python object for dlite_PYTHONPATH");
if (PyList_Insert(sys_path, 0, path))
FAIL1("cannot insert %s into sys.path", dlite_PYTHONPATH);
}

g->initialised = 1;
fail:
Py_XDECREF(sys);
Py_XDECREF(sys_path);
Py_XDECREF(path);
fail:
Py_XDECREF(sys);
Py_XDECREF(sys_path);
Py_XDECREF(path);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/pyembed/dlite-pyembed.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

*/

//#define Py_LIMITED_API 0x03080000
#include <Python.h>

/* Python pulls in a lot of defines that conflicts with utils/config.h */
Expand Down
1 change: 1 addition & 0 deletions src/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ check_symbol_exists(strncasecmp string.h HAVE_STRNCASECMP)
check_symbol_exists(_strnicmp string.h HAVE__STRNICMP)

check_symbol_exists(setenv stdlib.h HAVE_SETENV)
check_symbol_exists(_putenv_s stdlib.h HAVE__PUTENV_S)
check_symbol_exists(unsetenv stdlib.h HAVE_UNSETENV)

check_symbol_exists(realpath stdlib.h HAVE_REALPATH)
Expand Down
13 changes: 13 additions & 0 deletions src/utils/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ typedef int make_iso_compilers_happy;
#endif


/* setenv() - change or add an environment variable */
#ifndef HAVE_SETENV
# ifdef HAVE__PUTENV_S
int setenv(const char *name, const char *value, int overwrite)
{
if (overwrite || getenv(name))
return _putenv(name, value);
return 0;
}
# endif
#endif


/* strdup() - duplicate a string */
#if !defined(HAVE_STRDUP) && !defined(HAVE__STRDUP)
char *strdup(const char *s)
Expand Down
10 changes: 10 additions & 0 deletions src/utils/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@
#error "system is missing definition of size_t"
#endif

/*
* compat/setenv.c
*/

/** setenv() - change or add an environment variable */
#if !defined(HAVE_SETENV) && defined(HAVE__PUTENV_S)
#define HAVE_SETENV
int setenv(const char *name, const char *value, int overwrite);
#endif

/*
* compat/realpath.c
*/
Expand Down
1 change: 1 addition & 0 deletions src/utils/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#cmakedefine HAVE__STRNICMP

#cmakedefine HAVE_SETENV
#cmakedefine HAVE__PUTENV_S
#cmakedefine HAVE_UNSETENV

#cmakedefine HAVE_REALPATH
Expand Down