##// END OF EJS Templates
typing: attempt to remove @overloads in the platform module for stdlib methods...
typing: attempt to remove @overloads in the platform module for stdlib methods This is mostly successful, as examining util.pyi, posix.pyi, and windows.pyi after a pytype run shows that the type overloads for `oslink`, `readlink`, `removedirs`, `rename`, `split`, and `unlink` have been removed. (Some of these still have an @overload, but the differences are the variable names, not the types.) However, @overloads remain for `abspath` and `normpath` for some reason. It's useful to redefine these methods for the type checking phase because in addition to excluding str and PathLike variants, some of these functions have optional args in stdlib that aren't implemented in the custom implementation on Windows, and we want the type checking to flag that instead of assuming it's an allowable overload everywhere. One last quirk I noticed that I can't explain- `pycompat.TYPE_CHECKING` is always False, so the conditionals need to check `typing.TYPE_CHECKING` directly. I tried dropping the custom code for assigning `pycompat.TYPE_CHECKING` and simply did `from typing import TYPE_CHECKING` directly in pycompat.py, and used `pycompat.TYPE_CHECKING` for the conditional here... and pytype complained that `pycompat` doesn't have the `TYPE_CHECKING` variable.

File last commit:

r49674:bf6cdda9 default
r50713:3fd5824f default
Show More
dirs.c
322 lines | 7.1 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"
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
/*
* 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. */
val = PyLong_FromLong(0x1eadbeef);
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;
}
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
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;
}
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 if (only_tracked) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 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;
}
dirstate-item: move the C implementation to the same logic...
r48760 if (!(((dirstateItemObject *)value)->flags &
dirstate_flag_wc_tracked))
Yuya Nishihara
parsers: switch to policy importer...
r32372 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.
*/
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
PyObject *dirs = NULL, *source = NULL;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 int only_tracked = 0;
Yuya Nishihara
parsers: switch to policy importer...
r32372 int ret = -1;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static char *keywords_name[] = {"map", "only_tracked", NULL};
Yuya Nishihara
parsers: switch to policy importer...
r32372
self->dict = NULL;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
keywords_name, &source, &only_tracked))
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
dirs = PyDict_New();
if (dirs == NULL)
return -1;
if (source == NULL)
ret = 0;
else if (PyDict_Check(source))
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
else if (only_tracked)
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyErr_SetString(PyExc_ValueError,
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 "`only_tracked` is only supported "
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "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);
}