##// END OF EJS Templates
run-tests: stuff a `python3.exe` into the test bin directory on Windows...
run-tests: stuff a `python3.exe` into the test bin directory on Windows Windows doesn't have `python3.exe` as part of the python.org distribution, and that broke every script with a shebang after c102b704edb5. Windows itself provides a `python3.exe` app execution alias[1], but it is some sort of reparse point that MSYS is incapable of handling[2]. When run by MSYS, it simply prints $ python3 -V - Cannot open That in turn caused every `hghave` check, and test that invokes shebang scripts directly, to fail. Rather than try to patch up every script call to be invoked with `$PYTHON` (and regress when non Windows developers forget), copying the executable into the test binary directory with the new name just works. Since this directory is prepended to the system PATH value, it also overrides the broken execution alias. (The `_tmpbindir` is used instead of `_bindir` because the latter causes python3.exe to be copied into the repo next to hg.exe when `test-run-tests.t` runs. Something runs with this version of the executable and subsequent runs of `run-tests.py` inside `test-run-tests.t` try to copy over it while it is in use, and fail. This avoids the failures and the clutter.) I didn't conditionalize this on py3 because `python3.exe` needs to be present (for the shebangs) even when running py2 tests. It shouldn't matter to these simple scripts, and I think the intention is to make the test runner use py3 always, even if testing a py2 build. For now, still supporting py2 is helping to clean up the mess that is py3 tests. [1] https://stackoverflow.com/a/57168165 [2] https://stackoverflow.com/questions/59148628/solved-unable-to-run-python-3-7-on-windows-10-permission-denied#comment104524397_59148666 Differential Revision: https://phab.mercurial-scm.org/D9543

File last commit:

r44290:a47ccdcc default
r46684:cc0b332a default
Show More
dirs.c
327 lines | 7.0 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
dirs.c - dynamic directory diddling for dirstates
Copyright 2013 Facebook
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 #include <string.h>
Gregory Szorc
cext: reorder #include...
r34439
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include "util.h"
#ifdef IS_PY3K
Martin von Zweigbergk
dirs: fix out-of-bounds access in Py3...
r44290 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
Yuya Nishihara
parsers: switch to policy importer...
r32372 #else
#define PYLONG_VALUE(o) PyInt_AS_LONG(o)
#endif
/*
* This is a multiset of directory names, built from the files that
* appear in a dirstate or manifest.
*
* A few implementation notes:
*
* We modify Python integers for refcounting, but those integers are
* never visible to Python code.
*/
Augie Fackler
dirs: tag a struct as not being formattable...
r43506 /* clang-format off */
Yuya Nishihara
parsers: switch to policy importer...
r32372 typedef struct {
PyObject_HEAD
PyObject *dict;
} dirsObject;
Augie Fackler
dirs: tag a struct as not being formattable...
r43506 /* clang-format on */
Yuya Nishihara
parsers: switch to policy importer...
r32372
static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
{
while (pos != -1) {
if (path[pos] == '/')
break;
pos -= 1;
}
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 if (pos == -1) {
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 return 0;
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
return pos;
}
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 /* Mercurial will fail to run on directory hierarchies deeper than
* this constant, so we should try and keep this constant as big as
* possible.
*/
#define MAX_DIRS_DEPTH 2048
Yuya Nishihara
parsers: switch to policy importer...
r32372 static int _addpath(PyObject *dirs, PyObject *path)
{
const char *cpath = PyBytes_AS_STRING(path);
Py_ssize_t pos = PyBytes_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 size_t num_slashes = 0;
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* This loop is super critical for performance. That's why we inline
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 * access to Python structs instead of going through a supported API.
* The implementation, therefore, is heavily dependent on CPython
* implementation details. We also commit violations of the Python
* "protocol" such as mutating immutable objects. But since we only
* mutate objects created in this function or in other well-defined
* locations, the references are known so these violations should go
* unnoticed. */
Yuya Nishihara
parsers: switch to policy importer...
r32372 while ((pos = _finddir(cpath, pos - 1)) != -1) {
PyObject *val;
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 ++num_slashes;
if (num_slashes > MAX_DIRS_DEPTH) {
PyErr_SetString(PyExc_ValueError,
"Directory hierarchy too deep.");
goto bail;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372
Augie Fackler
dirs: reject consecutive slashes in paths...
r43799 /* Sniff for trailing slashes, a marker of an invalid input. */
if (pos > 0 && cpath[pos - 1] == '/') {
PyErr_SetString(
PyExc_ValueError,
"found invalid consecutive slashes in path");
goto bail;
}
Yuya Nishihara
dirs: remove mutable string optimization at all...
r43503 key = PyBytes_FromStringAndSize(cpath, pos);
if (key == NULL)
goto bail;
Yuya Nishihara
parsers: switch to policy importer...
r32372
val = PyDict_GetItem(dirs, key);
if (val != NULL) {
PYLONG_VALUE(val) += 1;
Yuya Nishihara
dirs: remove mutable string optimization at all...
r43503 Py_CLEAR(key);
Yuya Nishihara
parsers: switch to policy importer...
r32372 break;
}
/* Force Python to not reuse a small shared int. */
#ifdef IS_PY3K
val = PyLong_FromLong(0x1eadbeef);
#else
val = PyInt_FromLong(0x1eadbeef);
#endif
if (val == NULL)
goto bail;
PYLONG_VALUE(val) = 1;
ret = PyDict_SetItem(dirs, key, val);
Py_DECREF(val);
if (ret == -1)
goto bail;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
static int _delpath(PyObject *dirs, PyObject *path)
{
char *cpath = PyBytes_AS_STRING(path);
Py_ssize_t pos = PyBytes_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
while ((pos = _finddir(cpath, pos - 1)) != -1) {
PyObject *val;
key = PyBytes_FromStringAndSize(cpath, pos);
if (key == NULL)
goto bail;
val = PyDict_GetItem(dirs, key);
if (val == NULL) {
PyErr_SetString(PyExc_ValueError,
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "expected a value, found none");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
if (--PYLONG_VALUE(val) <= 0) {
if (PyDict_DelItem(dirs, key) == -1)
goto bail;
} else
break;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
{
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(source, &pos, &key, &value)) {
if (!PyBytes_Check(key)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
return -1;
}
if (skipchar) {
if (!dirstate_tuple_check(value)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
if (((dirstateTupleObject *)value)->state == skipchar)
continue;
}
if (_addpath(dirs, key) == -1)
return -1;
}
return 0;
}
static int dirs_fromiter(PyObject *dirs, PyObject *source)
{
PyObject *iter, *item = NULL;
int ret;
iter = PyObject_GetIter(source);
if (iter == NULL)
return -1;
while ((item = PyIter_Next(iter)) != NULL) {
if (!PyBytes_Check(item)) {
PyErr_SetString(PyExc_TypeError, "expected string");
break;
}
if (_addpath(dirs, item) == -1)
break;
Py_CLEAR(item);
}
ret = PyErr_Occurred() ? -1 : 0;
Py_DECREF(iter);
Py_XDECREF(item);
return ret;
}
/*
* Calculate a refcounted set of directory names for the files in a
* dirstate.
*/
static int dirs_init(dirsObject *self, PyObject *args)
{
PyObject *dirs = NULL, *source = NULL;
char skipchar = 0;
int ret = -1;
self->dict = NULL;
if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
return -1;
dirs = PyDict_New();
if (dirs == NULL)
return -1;
if (source == NULL)
ret = 0;
else if (PyDict_Check(source))
ret = dirs_fromdict(dirs, source, skipchar);
else if (skipchar)
PyErr_SetString(PyExc_ValueError,
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "skip character is only supported "
"with a dict source");
Yuya Nishihara
parsers: switch to policy importer...
r32372 else
ret = dirs_fromiter(dirs, source);
if (ret == -1)
Py_XDECREF(dirs);
else
self->dict = dirs;
return ret;
}
PyObject *dirs_addpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
return NULL;
if (_addpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
return NULL;
if (_delpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static int dirs_contains(dirsObject *self, PyObject *value)
{
return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
}
static void dirs_dealloc(dirsObject *self)
{
Py_XDECREF(self->dict);
PyObject_Del(self);
}
static PyObject *dirs_iter(dirsObject *self)
{
return PyObject_GetIter(self->dict);
}
static PySequenceMethods dirs_sequence_methods;
static PyMethodDef dirs_methods[] = {
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
{"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
{NULL} /* Sentinel */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
Yuya Nishihara
parsers: switch to policy importer...
r32372
void dirs_module_init(PyObject *mod)
{
dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
dirsType.tp_name = "parsers.dirs";
dirsType.tp_new = PyType_GenericNew;
dirsType.tp_basicsize = sizeof(dirsObject);
dirsType.tp_dealloc = (destructor)dirs_dealloc;
dirsType.tp_as_sequence = &dirs_sequence_methods;
dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
dirsType.tp_doc = "dirs";
dirsType.tp_iter = (getiterfunc)dirs_iter;
dirsType.tp_methods = dirs_methods;
dirsType.tp_init = (initproc)dirs_init;
if (PyType_Ready(&dirsType) < 0)
return;
Py_INCREF(&dirsType);
PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
}