diff --git a/.travis.yml b/.travis.yml index b3e486b..2c38cc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ # # Here we execute the Python 3.6 tests; Python 3.4 and 3.5 tests are run by # cibuildwheel, inside the manylinux1 Docker image. + +# We also run tests for memory leaks (which require a debug build of python) +# using python3.4. Specifically we install python3.4-dbg, the only python3 +# runtime with debug support present on the travis-ci platform at the moment. # # See https://mail.python.org/pipermail/wheel-builders/2017-August/000285.html @@ -21,6 +25,12 @@ matrix: python: "3.6" services: - docker + - sudo: required + language: python + python: "3.4" + services: + - docker + env: RUN_DEBUG_PYTHON=true - os: osx osx_image: xcode8.3 @@ -34,10 +44,23 @@ env: install: - if [ $TRAVIS_OS_NAME = osx ]; then brew upgrade python; fi + - | + if [ $RUN_DEBUG_PYTHON = true ]; then + sudo apt-get install python3.4-dbg + virtualenv -p python3.4-dbg /tmp/env + . /tmp/env/bin/activate + fi - pip3 install -r requirements-test.txt script: - - pip3 install . && pytest tests && cd docs; make doctest; cd - + - pip3 install . + - pytest tests + - pushd docs + - make doctest -e PYTHON=$(which python3) + - popd + # cibuildwheel's docker might get confused and throw an ImportMismatchError, + # so we remove bytecode files before running it. + - find . -name __pycache__ -exec rm -rf {} + - | pip3 install cibuildwheel==0.6.0 cibuildwheel --output-dir wheelhouse diff --git a/docs/dumps.rst b/docs/dumps.rst index 0f074a9..b083d4f 100644 --- a/docs/dumps.rst +++ b/docs/dumps.rst @@ -294,9 +294,9 @@ .. doctest:: - >>> mode = DM_UNIX_TIME + >>> mode = DM_UNIX_TIME | DM_NAIVE_IS_UTC >>> dumps([now, now.date(), now.time()], datetime_mode=mode) - '[1472409071.084418,1472335200.0,73871.084418]' + '[1472409071.084418,1472342400.0,73871.084418]' >>> unixtime = float(dumps(now, datetime_mode=mode)) >>> datetime.fromtimestamp(unixtime, here) == now True @@ -306,9 +306,9 @@ .. doctest:: - >>> mode = DM_UNIX_TIME | DM_ONLY_SECONDS + >>> mode = DM_UNIX_TIME | DM_NAIVE_IS_UTC | DM_ONLY_SECONDS >>> dumps([now, now.date(), now.time()], datetime_mode=mode) - '[1472409071,1472335200,73871]' + '[1472409071,1472342400,73871]' It can be used combined with :data:`DM_SHIFT_TO_UTC` to obtain the timestamp of the corresponding UTC_ time: diff --git a/rapidjson.cpp b/rapidjson.cpp index 316e44e..fcd0d26 100644 --- a/rapidjson.cpp +++ b/rapidjson.cpp @@ -434,24 +434,26 @@ RawJSON_dealloc(RawJSON* self) } -static int -RawJSON_init(RawJSON* self, PyObject* args, PyObject* kwds) +static PyObject* +RawJSON_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { - static char * kwlist[] = { + PyObject* self; + self = type->tp_alloc(type, 0); + static char* kwlist[] = { "value", NULL }; PyObject* value = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "U", kwlist, &value)) - return -1; + return NULL; - self->value = value; - Py_INCREF(self->value); + ((RawJSON*) self)->value = value; - return 0; -} + Py_INCREF(value); + return self; +} static PyMemberDef RawJSON_members[] = { {"value", @@ -506,9 +508,9 @@ static PyTypeObject RawJSON_Type = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc) RawJSON_init, /* tp_init */ + 0, /* tp_init */ 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + RawJSON_new, /* tp_new */ }; diff --git a/requirements-test.txt b/requirements-test.txt index 90566ee..3523458 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,3 @@ pytest >= 3 pytz +sphinx diff --git a/tests/test_refs_count.py b/tests/test_refs_count.py index 6f99ef9..4d9e921 100644 --- a/tests/test_refs_count.py +++ b/tests/test_refs_count.py @@ -160,3 +160,25 @@ class Foo: del value rc1 = sys.gettotalrefcount() assert (rc1 - rc0) < THRESHOLD + + +@pytest.mark.skipif(not hasattr(sys, 'gettotalrefcount'), reason='Non-debug Python') +def test_rawjson_constructor(): + raw_json = rj.RawJSON('["foo", "bar"]') + rc0 = sys.gettotalrefcount() + for i in range(1000): + value = '"foobar"' + raw_json.__init__(value) + del value + rc1 = sys.gettotalrefcount() + assert (rc1 - rc0) < THRESHOLD + + +@pytest.mark.skipif(not hasattr(sys, 'gettotalrefcount'), reason='Non-debug Python') +def test_rawjson_new(): + rc0 = sys.gettotalrefcount() + for i in range(1000): + raw_json = rj.RawJSON('["foo", "bar"]') + del raw_json + rc1 = sys.gettotalrefcount() + assert (rc1 - rc0) < THRESHOLD