From d3d57aa61bb44fd0bdad460c8c173743ca808733 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 30 Jun 2020 00:37:58 +0200 Subject: Improved some common helpers inside the Python API. --- plugins/pychrysalide/common/fnv1a.c | 36 ++++++++++++++--------- plugins/pychrysalide/common/module.c | 8 ++++- plugins/pychrysalide/common/pathname.c | 53 ++++++++++++++++++++++------------ tests/common/fnv1a.py | 25 ++++++++++++++++ tests/common/pathname.py | 33 +++++++++++++++------ 5 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 tests/common/fnv1a.py diff --git a/plugins/pychrysalide/common/fnv1a.c b/plugins/pychrysalide/common/fnv1a.c index c24eb6e..381af78 100644 --- a/plugins/pychrysalide/common/fnv1a.c +++ b/plugins/pychrysalide/common/fnv1a.c @@ -36,6 +36,14 @@ +#define FNV1A_DOC \ + "Python version for Chrysalide of the Fowler-Noll-Vo" \ + " hash function.\n" \ + "\n" \ + "There is no constructor for this class: its methods" \ + " are static methods only." + + /* Détermine l'empreinte FNV1a d'une chaîne de caractères. */ static PyObject *py_fnv1a_hash(PyObject *, PyObject *); @@ -61,6 +69,13 @@ static PyObject *py_fnv1a_hash(PyObject *self, PyObject *args) int ret; /* Bilan de lecture des args. */ fnv64_t value; /* Empreinte calculée */ +#define FNV1A_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + hash, "str, /", \ + METH_VARARGS | METH_STATIC, py_fnv1a, \ + "Compute the FNV-1a hash from a given string." \ +) + ret = PyArg_ParseTuple(args, "s", &str); if (!ret) return NULL; @@ -88,27 +103,24 @@ static PyObject *py_fnv1a_hash(PyObject *self, PyObject *args) PyTypeObject *get_python_fnv1a_type(void) { static PyMethodDef py_fnv1a_methods[] = { - - { "hash", py_fnv1a_hash, - METH_VARARGS | METH_STATIC, - "hash(str, /)\n--\n\nCompute the FNV-1a hash from a given string." - }, + FNV1A_HASH_METHOD, { NULL } - }; static PyTypeObject py_fnv1a_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.core.fnv1a", - .tp_basicsize = sizeof(PyObject), + .tp_name = "pychrysalide.common.fnv1a", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT, + .tp_doc = FNV1A_DOC, - .tp_doc = "Python version for Chrysalide of the Fowler-Noll-Vo hash function.", + .tp_methods = py_fnv1a_methods, - .tp_methods = py_fnv1a_methods + .tp_new = no_python_constructor_allowed, }; @@ -138,8 +150,6 @@ bool ensure_python_fnv1a_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - //type->tp_new = PyType_GenericNew; - if (PyType_Ready(type) != 0) return false; diff --git a/plugins/pychrysalide/common/module.c b/plugins/pychrysalide/common/module.c index e8cda4f..7f7cbb7 100644 --- a/plugins/pychrysalide/common/module.c +++ b/plugins/pychrysalide/common/module.c @@ -50,12 +50,18 @@ bool add_common_module(PyObject *super) bool result; /* Bilan à retourner */ PyObject *module; /* Sous-module mis en place */ +#define PYCHRYSALIDE_COMMON_DOC \ + "This module provides some tiny helpers for different use cases.\n" \ + "\n" \ + "The code for these features is shared between various parts of" \ + " Chrysalide." + static PyModuleDef py_chrysalide_common_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "pychrysalide.common", - .m_doc = "Python module for Chrysalide.common", + .m_doc = PYCHRYSALIDE_COMMON_DOC, .m_size = -1, diff --git a/plugins/pychrysalide/common/pathname.c b/plugins/pychrysalide/common/pathname.c index 8a18ae3..c83c27d 100644 --- a/plugins/pychrysalide/common/pathname.c +++ b/plugins/pychrysalide/common/pathname.c @@ -40,6 +40,13 @@ +#define PYTHON_PATHNAME_DOC \ + "Path manipulations in Python for Chrysalide.\n" \ + "\n" \ + "There is no constructor for this class: its methods" \ + " are static methods only." + + /* Calcule le chemin relatif entre deux fichiers donnés. */ static PyObject *py_build_relative_filename(PyObject *, PyObject *); @@ -69,6 +76,15 @@ static PyObject *py_build_relative_filename(PyObject *self, PyObject *args) int ret; /* Bilan de lecture des args. */ char *relative; /* Chemin d'accès construit */ +#define BUILD_RELATIVE_FILENAME_METHOD PYTHON_METHOD_DEF \ +( \ + build_relative_filename, "ref, target", \ + METH_VARARGS | METH_STATIC, py, \ + "Compute the relative path between two files.\n" \ + "\n" \ + "Both arguments must be strings." \ +) + ret = PyArg_ParseTuple(args, "ss", &ref, &target); if (!ret) return NULL; @@ -104,6 +120,15 @@ static PyObject *py_build_absolute_filename(PyObject *self, PyObject *args) int ret; /* Bilan de lecture des args. */ char *relative; /* Chemin d'accès construit */ +#define BUILD_ABSOLUTE_FILENAME_METHOD PYTHON_METHOD_DEF \ +( \ + build_absolute_filename, "ref, target", \ + METH_VARARGS | METH_STATIC, py, \ + "Compute the absolute path for a file.\n" \ + "\n" \ + "Both arguments must be strings." \ +) + ret = PyArg_ParseTuple(args, "ss", &ref, &target); if (!ret) return NULL; @@ -117,9 +142,7 @@ static PyObject *py_build_absolute_filename(PyObject *self, PyObject *args) else { result = PyUnicode_FromString(relative); - free(relative); - } return result; @@ -142,31 +165,25 @@ static PyObject *py_build_absolute_filename(PyObject *self, PyObject *args) PyTypeObject *get_python_pathname_type(void) { static PyMethodDef py_pathname_methods[] = { - - { "build_relative_filename", py_build_relative_filename, - METH_VARARGS | METH_STATIC, - "build_relative_filename(ref, target, /)\n--\n\nCompute the relative path between two files." - }, - { "build_absolute_filename", py_build_absolute_filename, - METH_VARARGS | METH_STATIC, - "build_absolute_filename(ref, target, /)\n--\n\nCompute the absolute path for a file." - }, + BUILD_RELATIVE_FILENAME_METHOD, + BUILD_ABSOLUTE_FILENAME_METHOD, { NULL } - }; static PyTypeObject py_pathname_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.core.pathname", - .tp_basicsize = sizeof(PyObject), + .tp_name = "pychrysalide.common.pathname", + .tp_basicsize = sizeof(PyObject), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT, + .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "Path manipulations in Python for Chrysalide.", + .tp_doc = PYTHON_PATHNAME_DOC, - .tp_methods = py_pathname_methods + .tp_methods = py_pathname_methods, + + .tp_new = no_python_constructor_allowed, }; @@ -196,8 +213,6 @@ bool ensure_python_pathname_is_registered(void) if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { - //type->tp_new = PyType_GenericNew; - if (PyType_Ready(type) != 0) return false; diff --git a/tests/common/fnv1a.py b/tests/common/fnv1a.py new file mode 100644 index 0000000..2013afa --- /dev/null +++ b/tests/common/fnv1a.py @@ -0,0 +1,25 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.common import fnv1a + + +class TestFnv1a(ChrysalideTestCase): + """TestCase for common.fnv1a*""" + + def testFnv1aConstructor(self): + """Check that no constructor is available for the fnv1a class.""" + + with self.assertRaisesRegex(NotImplementedError, 'Chrysalide does not allow building this kind of object from Python'): + instance = fnv1a() + + + def testFnv1aSamples(self): + """Compute some Fnv1a hashs.""" + + # Test cases from http://isthe.com/chongo/src/fnv/test_fnv.c + + val = fnv1a.hash('') + self.assertEqual(val, 0xcbf29ce484222325) + + val = fnv1a.hash('chongo was here!\n') + self.assertEqual(val, 0x46810940eff5f915) diff --git a/tests/common/pathname.py b/tests/common/pathname.py index 3692434..00a084b 100644 --- a/tests/common/pathname.py +++ b/tests/common/pathname.py @@ -1,21 +1,15 @@ -#!/usr/bin/python3-dbg -# -*- coding: utf-8 -*- - - -# Tests minimalistes pour valider la construction de chemins relatifs et absolus. - from chrysacase import ChrysalideTestCase from pychrysalide.common import pathname -class TestPathNames(ChrysalideTestCase): - """TestCase for common.pathname.build*""" +class TestPathnames(ChrysalideTestCase): + """TestCase for common.pathname*""" @classmethod def setUpClass(cls): - super(TestPathNames, cls).setUpClass() + super(TestPathnames, cls).setUpClass() cls._tests = [ { @@ -93,3 +87,24 @@ class TestPathNames(ChrysalideTestCase): with self.assertRaisesRegex(Exception, 'Relative path is too deep.'): got = build_absolute('/a/b', '../../c') + + + def testPathnameConstructor(self): + """Check that no constructor is available for the pathname class.""" + + with self.assertRaisesRegex(NotImplementedError, 'Chrysalide does not allow building this kind of object from Python'): + + instance = pathname() + + + def testPathnameSamples(self): + """Play with some path samples.""" + + filename = pathname.build_absolute_filename('/tmp/deep', '../myfile') + self.assertEqual(filename, '/myfile') + + filename = pathname.build_absolute_filename('/tmp/deep/', '../myfile') + self.assertEqual(filename, '/tmp/myfile') + + filename = pathname.build_relative_filename('/tmp/deep', '/myfile') + self.assertEqual(filename, '../myfile') -- cgit v0.11.2-87-g4458