Skip to content

gh-88402: Add new sysconfig variables on Windows (GH-110049) #110049

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

Merged
merged 10 commits into from
Oct 4, 2023
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
57 changes: 57 additions & 0 deletions Include/internal/pycore_importdl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef Py_INTERNAL_IMPORTDL_H
#define Py_INTERNAL_IMPORTDL_H

#include "patchlevel.h" // PY_MAJOR_VERSION

#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif


extern const char *_PyImport_DynLoadFiletab[];

extern PyObject *_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *);

typedef PyObject *(*PyModInitFunction)(void);

/* Max length of module suffix searched for -- accommodates "module.slb" */
#define MAXSUFFIXSIZE 12

#ifdef MS_WINDOWS
#include <windows.h>
typedef FARPROC dl_funcptr;

#ifdef _DEBUG
# define PYD_DEBUG_SUFFIX "_d"
#else
# define PYD_DEBUG_SUFFIX ""
#endif

#ifdef Py_NOGIL
# define PYD_THREADING_TAG "t"
#else
# define PYD_THREADING_TAG ""
#endif

#ifdef PYD_PLATFORM_TAG
# define PYD_SOABI "cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) PYD_THREADING_TAG "-" PYD_PLATFORM_TAG
#else
# define PYD_SOABI "cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) PYD_THREADING_TAG
#endif

#define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX "." PYD_SOABI ".pyd"
#define PYD_UNTAGGED_SUFFIX PYD_DEBUG_SUFFIX ".pyd"

#else
typedef void (*dl_funcptr)(void);
#endif


#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_IMPORTDL_H */
18 changes: 11 additions & 7 deletions Lib/sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,16 +544,20 @@ def _init_posix(vars):
def _init_non_posix(vars):
"""Initialize the module as appropriate for NT"""
# set basic install directories
import _imp
import _winapi
import _sysconfig
vars['LIBDEST'] = get_path('stdlib')
vars['BINLIBDEST'] = get_path('platstdlib')
vars['INCLUDEPY'] = get_path('include')
try:
# GH-99201: _imp.extension_suffixes may be empty when
# HAVE_DYNAMIC_LOADING is not set. In this case, don't set EXT_SUFFIX.
vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
except IndexError:
pass

# Add EXT_SUFFIX, SOABI, and Py_NOGIL
vars.update(_sysconfig.config_vars())
Comment on lines +553 to +554
Copy link
Member

@FFY00 FFY00 Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(note for the future)

I think we can move this to _init_config_vars, I am sure it will end up there soon anyway. The only thing to keep in mind is that we allow the POSIX config vars module (eg. _sysconfigdata__linux_x86_64-linux-gnu) to be overwritten (to help in cross-compilation) by setting _PYTHON_SYSCONFIGDATA_NAME, and we should raise an error if the value given by the user is different from what we computed.
To be even more cautious, I think we should probably save the original sysconfigdata name in the C module, as attributes like sys.implementation, etc., are often monkey patched when cross-compiling. If needed, people working on cross-compilation should also monkey patch the _sysconfig module explicitly, or better, just monkey patch sysconfig.get_config_vars.

You don't need to do it in this PR, I just wanted to write this down, especially to document the non-obvious issue with _PYTHON_SYSCONFIGDATA_NAME.
I don't think relying on Makefile variables is great, for a ton of reasons, meaning the new sysconfig API (GH-103480) will very likely use _sysconfig.config_vars on other platforms, so I expect the _PYTHON_SYSCONFIGDATA_NAME issue to be relevant soon.


vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs'))
if hasattr(sys, 'dllhandle'):
dllhandle = _winapi.GetModuleFileName(sys.dllhandle)
vars['LIBRARY'] = os.path.basename(_safe_realpath(dllhandle))
vars['LDLIBRARY'] = vars['LIBRARY']
vars['EXE'] = '.exe'
vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
Expand Down
24 changes: 21 additions & 3 deletions Lib/test/test_sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
get_path, get_path_names, _INSTALL_SCHEMES,
get_default_scheme, get_scheme_names, get_config_var,
_expand_vars, _get_preferred_schemes, _main)
import _imp
import _osx_support
import _sysconfig


HAS_USER_BASE = sysconfig._HAS_USER_BASE
Expand Down Expand Up @@ -394,6 +396,24 @@ def test_ldshared_value(self):

self.assertIn(ldflags, ldshared)

@unittest.skipIf(not _imp.extension_suffixes(), "stub loader has no suffixes")
def test_soabi(self):
soabi = sysconfig.get_config_var('SOABI')
self.assertIn(soabi, _imp.extension_suffixes()[0])

def test_library(self):
library = sysconfig.get_config_var('LIBRARY')
ldlibrary = sysconfig.get_config_var('LDLIBRARY')
major, minor = sys.version_info[:2]
if sys.platform == 'win32':
self.assertTrue(library.startswith(f'python{major}{minor}'))
self.assertTrue(library.endswith('.dll'))
self.assertEqual(library, ldlibrary)
else:
self.assertTrue(library.startswith(f'libpython{major}.{minor}'))
self.assertTrue(library.endswith('.a'))
self.assertTrue(ldlibrary.startswith(f'libpython{major}.{minor}'))

@unittest.skipUnless(sys.platform == "darwin", "test only relevant on MacOSX")
@requires_subprocess()
def test_platform_in_subprocess(self):
Expand Down Expand Up @@ -472,10 +492,8 @@ def test_srcdir_independent_of_cwd(self):

@unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
'EXT_SUFFIX required for this test')
@unittest.skipIf(not _imp.extension_suffixes(), "stub loader has no suffixes")
def test_EXT_SUFFIX_in_vars(self):
import _imp
if not _imp.extension_suffixes():
self.skipTest("stub loader has no suffixes")
vars = sysconfig.get_config_vars()
self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0])

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add new variables to :py:meth:`sysconfig.get_config_vars` on Windows:
``LIBRARY``, ``LDLIBRARY``, ``LIBDIR``, ``SOABI``, and ``Py_NOGIL``.
1 change: 1 addition & 0 deletions Modules/Setup
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ PYTHONPATH=$(COREPYTHONPATH)
#math mathmodule.c
#mmap mmapmodule.c
#select selectmodule.c
#_sysconfig _sysconfig.c

# XML
#_elementtree _elementtree.c
Expand Down
1 change: 1 addition & 0 deletions Modules/Setup.bootstrap.in
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ errno errnomodule.c
_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c
itertools itertoolsmodule.c
_sre _sre/sre.c
_sysconfig _sysconfig.c
_thread _threadmodule.c
time timemodule.c
_typing _typingmodule.c
Expand Down
98 changes: 98 additions & 0 deletions Modules/_sysconfig.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// _sysconfig provides data for the Python sysconfig module

#ifndef Py_BUILD_CORE_BUILTIN
# define Py_BUILD_CORE_MODULE 1
#endif

#include "Python.h"

#include "pycore_importdl.h" // _PyImport_DynLoadFiletab
#include "pycore_long.h" // _PyLong_GetZero, _PyLong_GetOne


/*[clinic input]
module _sysconfig
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=0a7c02d3e212ac97]*/

#include "clinic/_sysconfig.c.h"

#ifdef MS_WINDOWS
static int
add_string_value(PyObject *dict, const char *key, const char *str_value)
{
PyObject *value = PyUnicode_FromString(str_value);
if (value == NULL) {
return -1;
}
int err = PyDict_SetItemString(dict, key, value);
Py_DECREF(value);
return err;
}
#endif

/*[clinic input]
_sysconfig.config_vars

Returns a dictionary containing build variables intended to be exposed by sysconfig.
[clinic start generated code]*/

static PyObject *
_sysconfig_config_vars_impl(PyObject *module)
/*[clinic end generated code: output=9c41cdee63ea9487 input=391ff42f3af57d01]*/
{
PyObject *config = PyDict_New();
if (config == NULL) {
return NULL;
}

#ifdef MS_WINDOWS
if (add_string_value(config, "EXT_SUFFIX", PYD_TAGGED_SUFFIX) < 0) {
Py_DECREF(config);
return NULL;
}
if (add_string_value(config, "SOABI", PYD_SOABI) < 0) {
Py_DECREF(config);
return NULL;
}
#endif

#ifdef Py_NOGIL
PyObject *py_nogil = _PyLong_GetOne();
#else
PyObject *py_nogil = _PyLong_GetZero();
#endif
if (PyDict_SetItemString(config, "Py_NOGIL", py_nogil) < 0) {
Py_DECREF(config);
return NULL;
}

return config;
}

PyDoc_STRVAR(sysconfig__doc__,
"A helper for the sysconfig module.");

static struct PyMethodDef sysconfig_methods[] = {
_SYSCONFIG_CONFIG_VARS_METHODDEF
{NULL, NULL}
};

static PyModuleDef_Slot sysconfig_slots[] = {
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{0, NULL}
};

static PyModuleDef sysconfig_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "_sysconfig",
.m_doc = sysconfig__doc__,
.m_methods = sysconfig_methods,
.m_slots = sysconfig_slots,
};

PyMODINIT_FUNC
PyInit__sysconfig(void)
{
return PyModuleDef_Init(&sysconfig_module);
}
22 changes: 22 additions & 0 deletions Modules/clinic/_sysconfig.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions PC/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern PyObject* PyInit__sha1(void);
extern PyObject* PyInit__sha2(void);
extern PyObject* PyInit__sha3(void);
extern PyObject* PyInit__statistics(void);
extern PyObject* PyInit__sysconfig(void);
extern PyObject* PyInit__typing(void);
extern PyObject* PyInit__blake2(void);
extern PyObject* PyInit_time(void);
Expand Down Expand Up @@ -102,6 +103,7 @@ struct _inittab _PyImport_Inittab[] = {
{"_sha2", PyInit__sha2},
{"_sha3", PyInit__sha3},
{"_blake2", PyInit__blake2},
{"_sysconfig", PyInit__sysconfig},
{"time", PyInit_time},
{"_thread", PyInit__thread},
{"_tokenize", PyInit__tokenize},
Expand Down
3 changes: 2 additions & 1 deletion PCbuild/pythoncore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@
<ClInclude Include="..\Include\internal\pycore_hashtable.h" />
<ClInclude Include="..\Include\internal\pycore_identifier.h" />
<ClInclude Include="..\Include\internal\pycore_import.h" />
<ClInclude Include="..\Include\internal\pycore_importdl.h" />
<ClInclude Include="..\Include\internal\pycore_initconfig.h" />
<ClInclude Include="..\Include\internal\pycore_interp.h" />
<ClInclude Include="..\Include\internal\pycore_intrinsics.h" />
Expand Down Expand Up @@ -367,7 +368,6 @@
<ClInclude Include="..\PC\errmap.h" />
<ClInclude Include="..\PC\pyconfig.h" />
<ClInclude Include="..\Python\condvar.h" />
<ClInclude Include="..\Python\importdl.h" />
<ClInclude Include="..\Python\stdlib_module_names.h" />
<ClInclude Include="..\Python\thread_nt.h" />
</ItemGroup>
Expand Down Expand Up @@ -438,6 +438,7 @@
<ClCompile Include="..\Modules\signalmodule.c" />
<ClCompile Include="..\Modules\_statisticsmodule.c" />
<ClCompile Include="..\Modules\symtablemodule.c" />
<ClCompile Include="..\Modules\_sysconfig.c" />
<ClCompile Include="..\Modules\_threadmodule.c" />
<ClCompile Include="..\Modules\_tracemalloc.c" />
<ClCompile Include="..\Modules\_typingmodule.c" />
Expand Down
9 changes: 6 additions & 3 deletions PCbuild/pythoncore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,6 @@
<ClInclude Include="..\PC\pyconfig.h">
<Filter>PC</Filter>
</ClInclude>
<ClInclude Include="..\Python\importdl.h">
<Filter>Python</Filter>
</ClInclude>
<ClInclude Include="..\Python\stdlib_module_names.h">
<Filter>Python</Filter>
</ClInclude>
Expand Down Expand Up @@ -633,6 +630,9 @@
<ClInclude Include="..\Include\internal\pycore_import.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_importdl.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_initconfig.h">
<Filter>Include\internal</Filter>
</ClInclude>
Expand Down Expand Up @@ -959,6 +959,9 @@
<ClCompile Include="..\Modules\symtablemodule.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_sysconfig.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_threadmodule.c">
<Filter>Modules</Filter>
</ClCompile>
Expand Down
2 changes: 1 addition & 1 deletion Python/dynload_hpux.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <errno.h>

#include "Python.h"
#include "importdl.h"
#include "pycore_importdl.h"

#if defined(__hp9000s300)
#define FUNCNAME_PATTERN "_%.20s_%.200s"
Expand Down
2 changes: 1 addition & 1 deletion Python/dynload_shlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "Python.h"
#include "pycore_interp.h" // _PyInterpreterState.dlopenflags
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "importdl.h"
#include "pycore_importdl.h"

#include <sys/types.h>
#include <sys/stat.h>
Expand Down
2 changes: 1 addition & 1 deletion Python/dynload_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
not present. */

#include "Python.h"
#include "importdl.h"
#include "pycore_importdl.h"


const char *_PyImport_DynLoadFiletab[] = {NULL};
Loading