Skip to content

Commit ae7839b

Browse files
bpo-45211: Move helpers from getpath.c to internal API. (gh-28550)
This accomplishes 2 things: * consolidates some common code between getpath.c and getpathp.c * makes the helpers available to code in other files FWIW, the signature of the join_relfile() function (in fileutils.c) intentionally mirrors that of Windows' PathCchCombineEx(). Note that this change is mostly moving code around. No behavior is meant to change. https://bugs.python.org/issue45211
1 parent e5f13ce commit ae7839b

File tree

8 files changed

+164
-100
lines changed

8 files changed

+164
-100
lines changed

Include/internal/pycore_fileutils.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ extern "C" {
1010

1111
#include <locale.h> /* struct lconv */
1212

13+
// This is used after getting NULL back from Py_DecodeLocale().
14+
#define DECODE_LOCALE_ERR(NAME, LEN) \
15+
((LEN) == (size_t)-2) \
16+
? _PyStatus_ERR("cannot decode " NAME) \
17+
: _PyStatus_NO_MEMORY()
18+
1319
PyAPI_DATA(int) _Py_HasFileSystemDefaultEncodeErrors;
1420

1521
PyAPI_FUNC(int) _Py_DecodeUTF8Ex(
@@ -33,6 +39,9 @@ PyAPI_FUNC(wchar_t*) _Py_DecodeUTF8_surrogateescape(
3339
Py_ssize_t arglen,
3440
size_t *wlen);
3541

42+
extern int
43+
_Py_wstat(const wchar_t *, struct stat *);
44+
3645
PyAPI_FUNC(int) _Py_GetForceASCII(void);
3746

3847
/* Reset "force ASCII" mode (if it was initialized).
@@ -65,6 +74,12 @@ extern int _Py_EncodeNonUnicodeWchar_InPlace(
6574
Py_ssize_t size);
6675
#endif
6776

77+
extern wchar_t * _Py_join_relfile(const wchar_t *dirname,
78+
const wchar_t *relfile);
79+
extern int _Py_add_relfile(wchar_t *dirname,
80+
const wchar_t *relfile,
81+
size_t bufsize);
82+
6883
#ifdef __cplusplus
6984
}
7085
#endif

Include/internal/pycore_pystate.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ _Py_IsMainInterpreter(PyInterpreterState *interp)
3030
}
3131

3232

33+
static inline const PyConfig *
34+
_Py_GetMainConfig(void)
35+
{
36+
PyInterpreterState *interp = _PyRuntime.interpreters.main;
37+
if (interp == NULL) {
38+
return NULL;
39+
}
40+
return _PyInterpreterState_GetConfig(interp);
41+
}
42+
43+
3344
/* Only handle signals on the main thread of the main interpreter. */
3445
static inline int
3546
_Py_ThreadCanHandleSignals(PyInterpreterState *interp)

Lib/test/test_embed.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
434434
'pathconfig_warnings': 1,
435435
'_init_main': 1,
436436
'_isolated_interpreter': 0,
437-
'use_frozen_modules': False,
437+
'use_frozen_modules': 0,
438438
}
439439
if MS_WINDOWS:
440440
CONFIG_COMPAT.update({

Modules/getpath.c

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,6 @@ extern "C" {
115115

116116
#define BUILD_LANDMARK L"Modules/Setup.local"
117117

118-
#define DECODE_LOCALE_ERR(NAME, LEN) \
119-
((LEN) == (size_t)-2) \
120-
? _PyStatus_ERR("cannot decode " NAME) \
121-
: _PyStatus_NO_MEMORY()
122-
123118
#define PATHLEN_ERR() _PyStatus_ERR("path configuration: path too long")
124119

125120
typedef struct {
@@ -149,23 +144,6 @@ static const wchar_t delimiter[2] = {DELIM, '\0'};
149144
static const wchar_t separator[2] = {SEP, '\0'};
150145

151146

152-
/* Get file status. Encode the path to the locale encoding. */
153-
static int
154-
_Py_wstat(const wchar_t* path, struct stat *buf)
155-
{
156-
int err;
157-
char *fname;
158-
fname = _Py_EncodeLocaleRaw(path, NULL);
159-
if (fname == NULL) {
160-
errno = EINVAL;
161-
return -1;
162-
}
163-
err = stat(fname, buf);
164-
PyMem_RawFree(fname);
165-
return err;
166-
}
167-
168-
169147
static void
170148
reduce(wchar_t *dir)
171149
{
@@ -235,28 +213,18 @@ isdir(const wchar_t *filename)
235213
static PyStatus
236214
joinpath(wchar_t *path, const wchar_t *path2, size_t path_len)
237215
{
238-
size_t n;
239-
if (!_Py_isabs(path2)) {
240-
n = wcslen(path);
241-
if (n >= path_len) {
216+
if (_Py_isabs(path2)) {
217+
if (wcslen(path2) >= path_len) {
242218
return PATHLEN_ERR();
243219
}
244-
245-
if (n > 0 && path[n-1] != SEP) {
246-
path[n++] = SEP;
247-
}
220+
wcscpy(path, path2);
248221
}
249222
else {
250-
n = 0;
251-
}
252-
253-
size_t k = wcslen(path2);
254-
if (n + k >= path_len) {
255-
return PATHLEN_ERR();
223+
if (_Py_add_relfile(path, path2, path_len) < 0) {
224+
return PATHLEN_ERR();
225+
}
226+
return _PyStatus_OK();
256227
}
257-
wcsncpy(path + n, path2, k);
258-
path[n + k] = '\0';
259-
260228
return _PyStatus_OK();
261229
}
262230

@@ -283,23 +251,7 @@ joinpath2(const wchar_t *path, const wchar_t *path2)
283251
if (_Py_isabs(path2)) {
284252
return _PyMem_RawWcsdup(path2);
285253
}
286-
287-
size_t len = wcslen(path);
288-
int add_sep = (len > 0 && path[len - 1] != SEP);
289-
len += add_sep;
290-
len += wcslen(path2);
291-
292-
wchar_t *new_path = PyMem_RawMalloc((len + 1) * sizeof(wchar_t));
293-
if (new_path == NULL) {
294-
return NULL;
295-
}
296-
297-
wcscpy(new_path, path);
298-
if (add_sep) {
299-
wcscat(new_path, separator);
300-
}
301-
wcscat(new_path, path2);
302-
return new_path;
254+
return _Py_join_relfile(path, path2);
303255
}
304256

305257

PC/getpathp.c

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
#include "Python.h"
8383
#include "pycore_initconfig.h" // PyStatus
8484
#include "pycore_pathconfig.h" // _PyPathConfig
85+
#include "pycore_fileutils.h" // _Py_add_relfile()
8586
#include "osdefs.h" // SEP, ALTSEP
8687
#include <wchar.h>
8788

@@ -115,10 +116,6 @@
115116
* with a semicolon separated path prior to calling Py_Initialize.
116117
*/
117118

118-
#ifndef LANDMARK
119-
# define LANDMARK L"lib\\os.py"
120-
#endif
121-
122119
#define INIT_ERR_BUFFER_OVERFLOW() _PyStatus_ERR("buffer overflow")
123120

124121

@@ -216,7 +213,7 @@ exists(const wchar_t *filename)
216213
Assumes 'filename' MAXPATHLEN+1 bytes long -
217214
may extend 'filename' by one character. */
218215
static int
219-
ismodule(wchar_t *filename, int update_filename)
216+
ismodule(wchar_t *filename)
220217
{
221218
size_t n;
222219

@@ -231,9 +228,8 @@ ismodule(wchar_t *filename, int update_filename)
231228
filename[n] = L'c';
232229
filename[n + 1] = L'\0';
233230
exist = exists(filename);
234-
if (!update_filename) {
235-
filename[n] = L'\0';
236-
}
231+
// Drop the 'c' we just added.
232+
filename[n] = L'\0';
237233
return exist;
238234
}
239235
return 0;
@@ -253,7 +249,7 @@ ismodule(wchar_t *filename, int update_filename)
253249
static void
254250
join(wchar_t *buffer, const wchar_t *stuff)
255251
{
256-
if (FAILED(PathCchCombineEx(buffer, MAXPATHLEN+1, buffer, stuff, 0))) {
252+
if (_Py_add_relfile(buffer, stuff, MAXPATHLEN+1) < 0) {
257253
Py_FatalError("buffer overflow in getpathp.c's join()");
258254
}
259255
}
@@ -273,30 +269,37 @@ canonicalize(wchar_t *buffer, const wchar_t *path)
273269
return _PyStatus_OK();
274270
}
275271

276-
277-
/* gotlandmark only called by search_for_prefix, which ensures
278-
'prefix' is null terminated in bounds. join() ensures
279-
'landmark' can not overflow prefix if too long. */
280272
static int
281-
gotlandmark(const wchar_t *prefix, const wchar_t *landmark)
273+
is_stdlibdir(wchar_t *stdlibdir)
282274
{
283-
wchar_t filename[MAXPATHLEN+1];
284-
memset(filename, 0, sizeof(filename));
285-
wcscpy_s(filename, Py_ARRAY_LENGTH(filename), prefix);
286-
join(filename, landmark);
287-
return ismodule(filename, FALSE);
275+
wchar_t *filename = stdlibdir;
276+
#ifndef LANDMARK
277+
# define LANDMARK L"os.py"
278+
#endif
279+
/* join() ensures 'landmark' can not overflow prefix if too long. */
280+
join(filename, LANDMARK);
281+
return ismodule(filename);
288282
}
289283

290-
291284
/* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd.
292285
assumption provided by only caller, calculate_path() */
293286
static int
294-
search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path, const wchar_t *landmark)
287+
search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path)
295288
{
296-
/* Search from argv0_path, until landmark is found */
297-
wcscpy_s(prefix, MAXPATHLEN + 1, argv0_path);
289+
/* Search from argv0_path, until LANDMARK is found.
290+
We guarantee 'prefix' is null terminated in bounds. */
291+
wcscpy_s(prefix, MAXPATHLEN+1, argv0_path);
292+
wchar_t stdlibdir[MAXPATHLEN+1];
293+
wcscpy_s(stdlibdir, Py_ARRAY_LENGTH(stdlibdir), prefix);
294+
/* We initialize with the longest possible path, in case it doesn't fit.
295+
This also gives us an initial SEP at stdlibdir[wcslen(prefix)]. */
296+
join(stdlibdir, L"lib");
298297
do {
299-
if (gotlandmark(prefix, landmark)) {
298+
assert(stdlibdir[wcslen(prefix)] == SEP);
299+
/* Due to reduce() and our initial value, this result
300+
is guaranteed to fit. */
301+
wcscpy(&stdlibdir[wcslen(prefix) + 1], L"lib");
302+
if (is_stdlibdir(stdlibdir)) {
300303
return 1;
301304
}
302305
reduce(prefix);
@@ -758,7 +761,7 @@ calculate_home_prefix(PyCalculatePath *calculate,
758761
reduce(prefix);
759762
calculate->home = prefix;
760763
}
761-
else if (search_for_prefix(prefix, argv0_path, LANDMARK)) {
764+
else if (search_for_prefix(prefix, argv0_path)) {
762765
calculate->home = prefix;
763766
}
764767
else {
@@ -936,7 +939,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
936939
lookBuf[nchars] = L'\0';
937940
/* Up one level to the parent */
938941
reduce(lookBuf);
939-
if (search_for_prefix(prefix, lookBuf, LANDMARK)) {
942+
if (search_for_prefix(prefix, lookBuf)) {
940943
break;
941944
}
942945
/* If we are out of paths to search - give up */

0 commit comments

Comments
 (0)