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

Embedded dependencies workflow #165

Merged
merged 10 commits into from
Oct 14, 2020
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
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ lib64/
parts/
sdist/
var/
*.dist-info/
*.egg-info/
.installed.cfg
*.egg
Expand All @@ -33,3 +34,21 @@ nosetests.xml
coverage.xml
*,cover

# Sphinx documentation
docs/_build/

#############
## Custom
#############

# virtualenvs
venv/
ENV/
.venv*
.venv/*

# embedded libs
ext_libs/bin
ext_libs/docs
ext_libs/*/contrib/
ext_libs/*/tests/
24 changes: 24 additions & 0 deletions docs/development/update_dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Dependencies upgrade workflow

This plugin has external dependencies:

- dulwich
- giturlparse
- pathvalidate

Because it's still hard to install Python 3rd party packages from an index (for example <https://pypi.org>), especially on Windows or Mac systems (or even on Linux if we want to do it properly in a virtual environment), those required packages are stored in the `ext_libs` folder.

## Upgrade workflow

Manage versions in the `requirements/embedded.txt` file, then:

```bash
python -m pip install --no-deps -U -r requirements/embedded.txt -t ext_libs
```

Note: even if `dulwich` depends on `certifi` and `urllib3`, we specifally install them since they are already included with QGIS.

## Related links

- <https://gis.stackexchange.com/questions/141320/installing-3rd-party-python-libraries-for-qgis-on-windows>
- <https://github.com/QGIS-Contribution/QGIS-ResourceSharing/issues/112>
2 changes: 1 addition & 1 deletion ext_libs/dulwich/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@

"""Python implementation of the Git file formats and protocols."""

__version__ = (0, 19, 15)
__version__ = (0, 20, 6)
71 changes: 18 additions & 53 deletions ext_libs/dulwich/_diff_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,6 @@
typedef unsigned short mode_t;
#endif

#if PY_MAJOR_VERSION < 3
typedef long Py_hash_t;
#endif

#if PY_MAJOR_VERSION >= 3
#define PyInt_FromLong PyLong_FromLong
#define PyInt_AsLong PyLong_AsLong
#define PyInt_AS_LONG PyLong_AS_LONG
#define PyString_AS_STRING PyBytes_AS_STRING
#define PyString_AsStringAndSize PyBytes_AsStringAndSize
#define PyString_Check PyBytes_Check
#define PyString_CheckExact PyBytes_CheckExact
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#define PyString_FromString PyBytes_FromString
#define PyString_GET_SIZE PyBytes_GET_SIZE
#define PyString_Size PyBytes_Size
#define _PyString_Join _PyBytes_Join
#endif

static PyObject *tree_entry_cls = NULL, *null_entry = NULL,
*defaultdict_cls = NULL, *int_cls = NULL;
static int block_size;
Expand Down Expand Up @@ -118,7 +99,7 @@ static PyObject **tree_entries(char *path, Py_ssize_t path_len, PyObject *tree,
if (!sha)
goto error;
name = PyTuple_GET_ITEM(old_entry, 0);
name_len = PyString_Size(name);
name_len = PyBytes_Size(name);
if (PyErr_Occurred())
goto error;

Expand All @@ -133,18 +114,13 @@ static PyObject **tree_entries(char *path, Py_ssize_t path_len, PyObject *tree,
if (path_len) {
memcpy(new_path, path, path_len);
new_path[path_len] = '/';
memcpy(new_path + path_len + 1, PyString_AS_STRING(name), name_len);
memcpy(new_path + path_len + 1, PyBytes_AS_STRING(name), name_len);
} else {
memcpy(new_path, PyString_AS_STRING(name), name_len);
memcpy(new_path, PyBytes_AS_STRING(name), name_len);
}

#if PY_MAJOR_VERSION >= 3
result[i] = PyObject_CallFunction(tree_entry_cls, "y#OO", new_path,
new_path_len, PyTuple_GET_ITEM(old_entry, 1), sha);
#else
result[i] = PyObject_CallFunction(tree_entry_cls, "s#OO", new_path,
new_path_len, PyTuple_GET_ITEM(old_entry, 1), sha);
#endif
PyMem_Free(new_path);
if (!result[i]) {
goto error;
Expand Down Expand Up @@ -172,7 +148,7 @@ static int entry_path_cmp(PyObject *entry1, PyObject *entry2)
if (!path1)
goto done;

if (!PyString_Check(path1)) {
if (!PyBytes_Check(path1)) {
PyErr_SetString(PyExc_TypeError, "path is not a (byte)string");
goto done;
}
Expand All @@ -181,12 +157,12 @@ static int entry_path_cmp(PyObject *entry1, PyObject *entry2)
if (!path2)
goto done;

if (!PyString_Check(path2)) {
if (!PyBytes_Check(path2)) {
PyErr_SetString(PyExc_TypeError, "path is not a (byte)string");
goto done;
}

result = strcmp(PyString_AS_STRING(path1), PyString_AS_STRING(path2));
result = strcmp(PyBytes_AS_STRING(path1), PyBytes_AS_STRING(path2));

done:
Py_XDECREF(path1);
Expand All @@ -202,11 +178,7 @@ static PyObject *py_merge_entries(PyObject *self, PyObject *args)
char *path_str;
int cmp;

#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTuple(args, "y#OO", &path_str, &path_len, &tree1, &tree2))
#else
if (!PyArg_ParseTuple(args, "s#OO", &path_str, &path_len, &tree1, &tree2))
#endif
return NULL;

entries1 = tree_entries(path_str, path_len, tree1, &n1);
Expand Down Expand Up @@ -270,6 +242,11 @@ static PyObject *py_merge_entries(PyObject *self, PyObject *args)
return result;
}

/* Not all environments define S_ISDIR */
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

static PyObject *py_is_tree(PyObject *self, PyObject *args)
{
PyObject *entry, *mode, *result;
Expand All @@ -286,7 +263,7 @@ static PyObject *py_is_tree(PyObject *self, PyObject *args)
result = Py_False;
Py_INCREF(result);
} else {
lmode = PyInt_AsLong(mode);
lmode = PyLong_AsLong(mode);
if (lmode == -1 && PyErr_Occurred()) {
Py_DECREF(mode);
return NULL;
Expand All @@ -305,21 +282,21 @@ static Py_hash_t add_hash(PyObject *get, PyObject *set, char *str, int n)

/* It would be nice to hash without copying str into a PyString, but that
* isn't exposed by the API. */
str_obj = PyString_FromStringAndSize(str, n);
str_obj = PyBytes_FromStringAndSize(str, n);
if (!str_obj)
goto error;
hash = PyObject_Hash(str_obj);
if (hash == -1)
goto error;
hash_obj = PyInt_FromLong(hash);
hash_obj = PyLong_FromLong(hash);
if (!hash_obj)
goto error;

value = PyObject_CallFunctionObjArgs(get, hash_obj, NULL);
if (!value)
goto error;
set_value = PyObject_CallFunction(set, "(Ol)", hash_obj,
PyInt_AS_LONG(value) + n);
PyLong_AS_LONG(value) + n);
if (!set_value)
goto error;

Expand Down Expand Up @@ -372,11 +349,11 @@ static PyObject *py_count_blocks(PyObject *self, PyObject *args)

for (i = 0; i < num_chunks; i++) {
chunk = PyList_GET_ITEM(chunks, i);
if (!PyString_Check(chunk)) {
if (!PyBytes_Check(chunk)) {
PyErr_SetString(PyExc_TypeError, "chunk is not a string");
goto error;
}
if (PyString_AsStringAndSize(chunk, &chunk_str, &chunk_len) == -1)
if (PyBytes_AsStringAndSize(chunk, &chunk_str, &chunk_len) == -1)
goto error;

for (j = 0; j < chunk_len; j++) {
Expand Down Expand Up @@ -420,7 +397,6 @@ moduleinit(void)
PyObject *m, *objects_mod = NULL, *diff_tree_mod = NULL;
PyObject *block_size_obj = NULL;

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_diff_tree", /* m_name */
Expand All @@ -433,9 +409,6 @@ moduleinit(void)
NULL, /* m_free */
};
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule("_diff_tree", py_diff_tree_methods);
#endif
if (!m)
goto error;

Expand All @@ -459,7 +432,7 @@ moduleinit(void)
block_size_obj = PyObject_GetAttrString(diff_tree_mod, "_BLOCK_SIZE");
if (!block_size_obj)
goto error;
block_size = (int)PyInt_AsLong(block_size_obj);
block_size = (int)PyLong_AsLong(block_size_obj);

if (PyErr_Occurred())
goto error;
Expand Down Expand Up @@ -490,16 +463,8 @@ moduleinit(void)
return NULL;
}

#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit__diff_tree(void)
{
return moduleinit();
}
#else
PyMODINIT_FUNC
init_diff_tree(void)
{
moduleinit();
}
#endif
45 changes: 12 additions & 33 deletions ext_libs/dulwich/_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@
#include <stdlib.h>
#include <sys/stat.h>

#if PY_MAJOR_VERSION >= 3
#define PyInt_Check(obj) 0
#define PyInt_CheckExact(obj) 0
#define PyInt_AsLong PyLong_AsLong
#define PyString_AS_STRING PyBytes_AS_STRING
#define PyString_Check PyBytes_Check
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#endif

#if defined(__MINGW32_VERSION) || defined(__APPLE__)
size_t rep_strnlen(char *text, size_t maxlen);
size_t rep_strnlen(char *text, size_t maxlen)
Expand All @@ -56,7 +47,7 @@ static PyObject *sha_to_pyhex(const unsigned char *sha)
hexsha[i*2+1] = bytehex(sha[i] & 0x0F);
}

return PyString_FromStringAndSize(hexsha, 40);
return PyBytes_FromStringAndSize(hexsha, 40);
}

static PyObject *py_parse_tree(PyObject *self, PyObject *args, PyObject *kw)
Expand All @@ -67,13 +58,8 @@ static PyObject *py_parse_tree(PyObject *self, PyObject *args, PyObject *kw)
PyObject *ret, *item, *name, *sha, *py_strict = NULL;
static char *kwlist[] = {"text", "strict", NULL};

#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTupleAndKeywords(args, kw, "y#|O", kwlist,
&text, &len, &py_strict))
#else
if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|O", kwlist,
&text, &len, &py_strict))
#endif
return NULL;
strict = py_strict ? PyObject_IsTrue(py_strict) : 0;
/* TODO: currently this returns a list; if memory usage is a concern,
Expand All @@ -100,7 +86,7 @@ static PyObject *py_parse_tree(PyObject *self, PyObject *args, PyObject *kw)
}
text++;
namelen = strnlen(text, len - (text - start));
name = PyString_FromStringAndSize(text, namelen);
name = PyBytes_FromStringAndSize(text, namelen);
if (name == NULL) {
Py_DECREF(ret);
return NULL;
Expand Down Expand Up @@ -141,6 +127,11 @@ struct tree_item {
PyObject *tuple;
};

/* Not all environments define S_ISDIR */
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

int cmp_tree_item(const void *_a, const void *_b)
{
const struct tree_item *a = _a, *b = _b;
Expand Down Expand Up @@ -202,7 +193,7 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *args)
}

while (PyDict_Next(entries, &pos, &key, &value)) {
if (!PyString_Check(key)) {
if (!PyBytes_Check(key)) {
PyErr_SetString(PyExc_TypeError, "Name is not a string");
goto error;
}
Expand All @@ -213,18 +204,18 @@ static PyObject *py_sorted_tree_items(PyObject *self, PyObject *args)
}

py_mode = PyTuple_GET_ITEM(value, 0);
if (!PyInt_Check(py_mode) && !PyLong_Check(py_mode)) {
if (!PyLong_Check(py_mode)) {
PyErr_SetString(PyExc_TypeError, "Mode is not an integral type");
goto error;
}

py_sha = PyTuple_GET_ITEM(value, 1);
if (!PyString_Check(py_sha)) {
if (!PyBytes_Check(py_sha)) {
PyErr_SetString(PyExc_TypeError, "SHA is not a string");
goto error;
}
qsort_entries[n].name = PyString_AS_STRING(key);
qsort_entries[n].mode = PyInt_AsLong(py_mode);
qsort_entries[n].name = PyBytes_AS_STRING(key);
qsort_entries[n].mode = PyLong_AsLong(py_mode);

qsort_entries[n].tuple = PyObject_CallFunctionObjArgs(
tree_entry_cls, key, py_mode, py_sha, NULL);
Expand Down Expand Up @@ -267,7 +258,6 @@ moduleinit(void)
{
PyObject *m, *objects_mod, *errors_mod;

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_objects", /* m_name */
Expand All @@ -280,9 +270,6 @@ moduleinit(void)
NULL, /* m_free */
};
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("_objects", py_objects_methods, NULL);
#endif
if (m == NULL) {
return NULL;
}
Expand Down Expand Up @@ -315,16 +302,8 @@ moduleinit(void)
return m;
}

#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit__objects(void)
{
return moduleinit();
}
#else
PyMODINIT_FUNC
init_objects(void)
{
moduleinit();
}
#endif
Loading