Browse Source

Enhancement & features in python_if + other stuff

- Adds str & repr to RPNIterExpr
- Adds richcompare to RPNExpr
- Adds tests
Yann Weber 9 months ago
parent
commit
daafebb989
9 changed files with 229 additions and 41 deletions
  1. 4
    0
      python_const.c
  2. 153
    33
      python_if.c
  3. 32
    2
      python_rpnexpr.c
  4. 2
    0
      python_rpnexpr.h
  5. 8
    0
      python_rpntoken.c
  6. 5
    4
      rpn_if_default.c
  7. 2
    1
      rpn_if_default.h
  8. 22
    0
      tests/tests_rpniter.py
  9. 1
    1
      tests/tests_rpniter_seq.py

+ 4
- 0
python_const.c View File

@@ -14,6 +14,10 @@ PyModuleDef rpnconstmodule = {
14 14
 
15 15
 /* Various named tuple definition, instanciated in python_rpnexpr module init */
16 16
 static PyStructSequence_Field params_fields[] = {
17
+	{
18
+		.name = "argc",
19
+		.doc = "Number of arguments expected by expressions",
20
+	},
17 21
 	{	.name = "pos_flag",
18 22
 		.doc = "Position flag, indicating coordinate system",
19 23
 	},

+ 153
- 33
python_if.c View File

@@ -270,10 +270,11 @@ with given arguments");
270 270
 /**@brief Returns the parameters in a named tuple */
271 271
 PyObject *rpnif_get_params(PyObject *self)
272 272
 {
273
+	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
273 274
 	PyObject *res, *val;
274 275
 	rpn_if_default_data_t *params;
275 276
 
276
-	params = (rpn_if_default_data_t*)(((PyRPNIterExpr_t*)self)->rif->params->data);
277
+	params = (rpn_if_default_data_t*)(expr_self->rif->params->data);
277 278
 
278 279
 	short expt_sizes[2];
279 280
 	if(rpn_if_sizes_from_flag(params->pos_flag, params->res_flag, expt_sizes) < 0)
@@ -287,12 +288,15 @@ PyObject *rpnif_get_params(PyObject *self)
287 288
 	{
288 289
 		return NULL;
289 290
 	}
290
-	val = PyLong_FromLong(params->pos_flag);
291
+	val = PyLong_FromLong(expr_self->rif->params->rpn_argc);
291 292
 	PyStructSequence_SET_ITEM(res, 0, val);
292 293
 
293
-	val = PyLong_FromLong(params->res_flag);
294
+	val = PyLong_FromLong(params->pos_flag);
294 295
 	PyStructSequence_SET_ITEM(res, 1, val);
295 296
 
297
+	val = PyLong_FromLong(params->res_flag);
298
+	PyStructSequence_SET_ITEM(res, 2, val);
299
+
296 300
 	if(params->pos_flag == RPN_IF_POSITION_XDIM)
297 301
 	{
298 302
 		expt_sizes[0] = params->size_lim[0] + 1;
@@ -304,7 +308,7 @@ PyObject *rpnif_get_params(PyObject *self)
304 308
 		Py_DECREF(res);
305 309
 		return NULL;
306 310
 	}
307
-	PyStructSequence_SET_ITEM(res, 2, lim);
311
+	PyStructSequence_SET_ITEM(res, 3, lim);
308 312
 
309 313
 	for(Py_ssize_t i=0; i<expt_sizes[0]; i++)
310 314
 	{
@@ -315,7 +319,7 @@ PyObject *rpnif_get_params(PyObject *self)
315 319
 	if(!params->const_val)
316 320
 	{
317 321
 		Py_INCREF(Py_None);
318
-		PyTuple_SET_ITEM(res, 3, Py_None);
322
+		PyTuple_SET_ITEM(res, 4, Py_None);
319 323
 	}
320 324
 	else
321 325
 	{
@@ -325,7 +329,7 @@ PyObject *rpnif_get_params(PyObject *self)
325 329
 			Py_DECREF(res);
326 330
 			return NULL;
327 331
 		}
328
-		PyStructSequence_SET_ITEM(res, 3, values);
332
+		PyStructSequence_SET_ITEM(res, 4, values);
329 333
 		for(Py_ssize_t i=0; i<expt_sizes[1]; i++)
330 334
 		{
331 335
 			val = PyLong_FromRpnValue_t(params->const_val[i]);
@@ -385,7 +389,7 @@ PyObject *rpnif_keys(PyObject *self)
385 389
 		case RPN_IF_POSITION_XDIM:
386 390
 			for(size_t i=0; i<rif_data->size_lim[0];i++)
387 391
 			{
388
-				snprintf(xdim_key, 64, "P%ld", i);
392
+				snprintf(xdim_key, 64, "D%ld", i);
389 393
 				_ret_append(xdim_key);
390 394
 			}
391 395
 			break;
@@ -509,7 +513,7 @@ PyObject* rpnif_expr_item(PyObject *self, Py_ssize_t idx)
509 513
 	{
510 514
 		idx = expr_self->rif->params->rpn_sz - 1 + idx;
511 515
 	}
512
-	if(idx < 0 || idx >= expr_self->rif->params->rpn_sz)
516
+	if(idx < 0 || (size_t) idx >= expr_self->rif->params->rpn_sz)
513 517
 	{
514 518
 		PyErr_Format(PyExc_IndexError,
515 519
 				"No expression %ld with given options",
@@ -599,7 +603,7 @@ static Py_ssize_t _rpnif_subscript_idx(PyObject *self, PyObject *_key)
599 603
 			char possible_key[64];
600 604
 			for(size_t i=0; i<ndim; i++)
601 605
 			{
602
-				snprintf(possible_key, 64, "P%ld", i);
606
+				snprintf(possible_key, 64, "D%ld", i);
603 607
 				if(!strcmp(possible_key, key))
604 608
 				{
605 609
 					idx = i;
@@ -750,23 +754,145 @@ void rpnif_releasebuffer(PyObject *self, Py_buffer *view)
750 754
 
751 755
 PyObject* rpnif_str(PyObject *self)
752 756
 {
753
-	PyErr_SetString(PyExc_NotImplementedError,
754
-		"str Not implemented");
755
-	return NULL;
756
-	/**@todo TODO write the function */
757
-	Py_RETURN_NONE;
757
+	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
758
+	rpn_if_default_data_t *rif_data = \
759
+		(rpn_if_default_data_t*)expr_self->rif->params->data;
760
+	const size_t rpn_sz = expr_self->rif->params->rpn_sz;
761
+	const char pyfmt[] = "%VA%ld=%U=\"%S\";";
762
+
763
+	size_t i;
764
+	PyObject *buf, *tmp, *items;
765
+
766
+	buf = NULL;
767
+	items = rpnif_items(self);
768
+	if(PyErr_Occurred()) { return NULL; }
769
+
770
+
771
+	for(i=0; i<rpn_sz; i++)
772
+	{
773
+		PyObject *key, *value, *elt;
774
+		elt = PyTuple_GET_ITEM(items, i);
775
+		key = PyTuple_GET_ITEM(elt, 0);
776
+		value = PyTuple_GET_ITEM(elt, 1);
777
+
778
+		tmp = PyUnicode_FromFormat(pyfmt,
779
+				buf, "",
780
+				i, key, value);
781
+		if(buf) { Py_DECREF(buf); }
782
+		if(PyErr_Occurred()) { return NULL; }
783
+		buf = tmp;
784
+	}
785
+
786
+
787
+	switch(rif_data->res_flag)
788
+	{
789
+		case RPN_IF_RES_CONST:
790
+			tmp = PyUnicode_FromFormat("%U A%ld=C(*)=%lld",
791
+					buf, i+1, rif_data->const_val[0]);
792
+			Py_DECREF(buf);
793
+			buf=tmp;
794
+			break;
795
+		case RPN_IF_RES_CONST_RGBA:
796
+			tmp = PyUnicode_FromFormat("%U A%ld=R(*)=%lld A%ld=G(*)=%lld A%ld=B(*)=%lld A%ld=A(*)=%lld",
797
+					buf,
798
+					i+1, rif_data->const_val[0],
799
+					i+2, rif_data->const_val[1],
800
+					i+3, rif_data->const_val[2],
801
+					i+4, rif_data->const_val[3]);
802
+			Py_DECREF(buf);
803
+			buf=tmp;
804
+			break;
805
+	}
806
+
807
+	Py_INCREF(buf);
808
+	return buf;
809
+}
810
+
811
+
812
+static PyObject* _rpnif_expr_repr(PyObject *self)
813
+{
814
+	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
815
+	rpn_if_default_data_t *rif_data = \
816
+		(rpn_if_default_data_t*)expr_self->rif->params->data;
817
+
818
+	PyObject *buf, *tmp, *items, *const_res;
819
+	const char pyfmt[] = "%V\"%U\":\"%S\"%V";
820
+
821
+	buf = const_res = NULL;
822
+
823
+	items = rpnif_items(self);
824
+	if(PyErr_Occurred()) { return NULL; }
825
+
826
+#define _const_res_key ",\"const_res\":"
827
+	switch(rif_data->res_flag)
828
+	{
829
+		case RPN_IF_RES_CONST:
830
+			const_res = PyUnicode_FromFormat(
831
+					_const_res_key "{\"r\":%llu}}",
832
+					rif_data->const_val[0]);
833
+			break;
834
+		/*
835
+		case RPN_IF_RES_CONST_RGB:
836
+			const_res = PyUnicode_FromFormat(
837
+					"\"const_res\": {\"r\":%llu, \"g\":%llu, \"b\":%llu}}",
838
+					rif_data->const_val[0],
839
+					rif_data->const_val[1],
840
+					rif_data->const_val[2])
841
+			break;
842
+		*/
843
+		case RPN_IF_RES_CONST_RGBA:
844
+			const_res = PyUnicode_FromFormat(
845
+					_const_res_key \
846
+					"{\"r\":%llu,\"g\":%llu,\"b\":%llu,\"a\":%llu}}",
847
+					rif_data->const_val[0],
848
+					rif_data->const_val[1],
849
+					rif_data->const_val[2],
850
+					rif_data->const_val[3]);
851
+			break;
852
+		default:
853
+			const_res = PyUnicode_FromFormat("}");
854
+			break;
855
+	}
856
+#undef _const_res_key
857
+
858
+	const size_t rpn_sz = expr_self->rif->params->rpn_sz;
859
+
860
+	for(size_t i=0; i<rpn_sz; i++)
861
+	{
862
+		PyObject *key, *value, *elt;
863
+		elt = PyTuple_GET_ITEM(items, i);
864
+		key = PyTuple_GET_ITEM(elt, 0);
865
+		value = PyTuple_GET_ITEM(elt, 1);
866
+
867
+		tmp = PyUnicode_FromFormat(pyfmt,
868
+				buf, "{", key, value,
869
+				i==rpn_sz-1?const_res:NULL, ",");
870
+		if(buf)
871
+		{
872
+			Py_DECREF(buf);
873
+		}
874
+		if(PyErr_Occurred())
875
+		{
876
+			return NULL;
877
+		}
878
+		buf = tmp;
879
+	}
880
+
881
+
882
+	return buf;
758 883
 }
759 884
 
760 885
 PyObject* rpnif_repr(PyObject *self)
761 886
 {
762 887
 	PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
763
-	char *buff;
764
-	size_t sz;
765 888
 	rpn_if_default_data_t *rif_data = (rpn_if_default_data_t*)expr_self->rif->params->data;
766 889
 
767 890
 	char *str_pos, *str_res;
768 891
 	char tmp[64];
769 892
 
893
+	PyObject *expr_repr = _rpnif_expr_repr(self);
894
+	if(PyErr_Occurred()) { return NULL; }
895
+
770 896
 	switch(rif_data->pos_flag)
771 897
 	{
772 898
 		case RPN_IF_POSITION_XY:
@@ -776,7 +902,7 @@ PyObject* rpnif_repr(PyObject *self)
776 902
 			str_pos = "LINEAR";
777 903
 			break;
778 904
 		case RPN_IF_POSITION_XDIM:
779
-			snprintf(tmp, 64, "XDIM[%ld]", rif_data->size_lim[0]);
905
+			snprintf(tmp, 64, "XDIM%ld", rif_data->size_lim[0]);
780 906
 			str_pos = tmp;
781 907
 			break;
782 908
 		default:
@@ -785,6 +911,8 @@ PyObject* rpnif_repr(PyObject *self)
785 911
 			return NULL;
786 912
 	}
787 913
 
914
+	PyObject *const_res = NULL;
915
+
788 916
 	switch(rif_data->res_flag)
789 917
 	{
790 918
 		case RPN_IF_RES_BOOL:
@@ -792,6 +920,7 @@ PyObject* rpnif_repr(PyObject *self)
792 920
 			break;
793 921
 		case RPN_IF_RES_CONST:
794 922
 			str_res = "CONST";
923
+			const_res = PyTuple_New(1);
795 924
 			break;
796 925
 		case RPN_IF_RES_CONST_RGBA:
797 926
 			str_res = "CONST_RGBA";
@@ -813,24 +942,15 @@ PyObject* rpnif_repr(PyObject *self)
813 942
 					"UNKOWN RES_FLAG2");
814 943
 			return NULL;
815 944
 	}
816
-
817
-	sz = snprintf(NULL, 0,
818
-			"<RPNIterExpr pos_flag:%s res_flag:%s expr_sz:%ld>",
819
-			str_pos, str_res, expr_self->rif->params->rpn_sz);
820 945
 	
821
-	buff = malloc(sizeof(char) * (sz + 1));
822
-	if(!buff)
823
-	{
824
-		PyErr_Format(PyExc_RuntimeError, "Error allocating RPNIterExpr repr : ",
825
-				strerror(errno));
826
-		return NULL;
827
-	}
946
+	PyObject *res;
947
+
948
+	const_res = const_res?const_res:Py_None;
828 949
 
829
-	sz = snprintf(buff, sz+1,
830
-			"<RPNIterExpr pos_flag:%s res_flag:%s expr_sz:%ld>",
831
-			str_pos, str_res, expr_self->rif->params->rpn_sz);
832
-	PyObject *res = Py_BuildValue("s", buff);
833
-	free(buff);
950
+	res = PyUnicode_FromFormat(
951
+			"<RPNIterExpr pos_flag:%s res_flag:%s expr:'%U' const_res:%S>",
952
+			str_pos, str_res, expr_repr, const_res);
953
+	if(PyErr_Occurred()) { return NULL; }
834 954
 	return res;
835 955
 }
836 956
 

+ 32
- 2
python_rpnexpr.c View File

@@ -53,16 +53,21 @@ PySequenceMethods RPNExpr_seq_methods = {
53 53
 	.sq_ass_item = rpnexpr_token_ass_item,
54 54
 };
55 55
 
56
+
56 57
 PyTypeObject RPNExprType = {
57 58
 	PyVarObject_HEAD_INIT(NULL, 0)
58 59
 	.tp_name = "pyrpn.RPNExpr",
59
-	.tp_doc = "RPN expression evaluator",
60
+	.tp_doc = "RPN expression evaluator\n\
61
+\n\
62
+Instanciation :\n\
63
+RPNExpr(expression:str, args_count:int, stack_size:int=16)",
60 64
 	.tp_basicsize = sizeof(PyRPNExpr_t),
61 65
 	.tp_itemsize = 0,
62 66
 	.tp_del = rpnexpr_del,
63 67
 	.tp_repr = rpnexpr_repr,
64 68
 	.tp_str = rpnexpr_str,
65 69
 	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
70
+	.tp_richcompare = rpnexpr_richcompare,
66 71
 	.tp_methods = RPNExpr_methods,
67 72
 	.tp_as_sequence = &RPNExpr_seq_methods,
68 73
 	.tp_members = RPNExpr_members,
@@ -691,8 +696,9 @@ PyObject* rpnexpr_mutate(PyObject* slf, PyObject *args, PyObject *kwds)
691 696
 		return NULL;
692 697
 	}
693 698
 
694
-	if(!py_params)
699
+	if(!py_params || py_params == Py_None)
695 700
 	{
701
+		if(py_params == Py_None) { Py_DECREF(Py_None); }
696 702
 		memcpy(&params, &rpn_mutation_params_default,
697 703
 				sizeof(rpn_mutation_params_t));
698 704
 	}
@@ -763,6 +769,30 @@ PyObject* rpnexpr_repr(PyObject *self)
763 769
 	return res;
764 770
 }
765 771
 
772
+
773
+PyObject* rpnexpr_richcompare(PyObject *_self, PyObject *other, int op)
774
+{
775
+	PyObject *sta, *stb, *res;
776
+
777
+	if(!PyObject_IsInstance(other, (PyObject*)&RPNExprType))
778
+	{
779
+		PyErr_Format(PyExc_TypeError,
780
+				"Can only be compared with RPNExpr");
781
+		return NULL;
782
+	}
783
+
784
+	sta = rpnexpr_getstate(_self, NULL);
785
+	stb = rpnexpr_getstate(other, NULL);
786
+
787
+	res = PyObject_RichCompare(sta, stb, op);
788
+
789
+	Py_DECREF(sta);
790
+	Py_DECREF(stb);
791
+
792
+	return res;
793
+}
794
+
795
+
766 796
 PyObject* rpnexpr_random(PyObject *cls, PyObject *args, PyObject *kwds)
767 797
 {
768 798
 	long long int args_count, expr_sz;

+ 2
- 0
python_rpnexpr.h View File

@@ -191,6 +191,8 @@ PyObject* rpnexpr_repr(PyObject *self);
191 191
  */
192 192
 PyObject* rpnexpr_str(PyObject *self);
193 193
 
194
+PyObject* rpnexpr_richcompare(PyObject *_self, PyObject *other, int op);
195
+
194 196
 /**@brief Return a new Python str with a random RPN expression
195 197
  * @param mod pyrpn module object
196 198
  * @param args Position arguments in Python list

+ 8
- 0
python_rpntoken.c View File

@@ -217,6 +217,14 @@ PyObject* rpntoken_richcompare(PyObject *_self, PyObject *_other, int op)
217 217
 	self = (RPNToken_t*)_self;
218 218
 	other = (RPNToken_t*)_other;
219 219
 
220
+
221
+	if(!PyObject_IsInstance(_other, (PyObject*)&RPNTokenType))
222
+	{
223
+		PyErr_Format(PyExc_TypeError,
224
+				"Can only be compared with RPNToken subtypes");
225
+		return NULL;
226
+	}
227
+
220 228
 	int cmp = self->value.type - other->value.type;
221 229
 	if(cmp == 0)
222 230
 	{

+ 5
- 4
rpn_if_default.c View File

@@ -1,7 +1,8 @@
1 1
 #include "rpn_if_default.h"
2 2
 
3 3
 rpn_if_param_t* rpn_if_default_params(short pos_flag, short res_flag,
4
-	const size_t *lim, const rpn_value_t *val, unsigned char rpn_stack_sz)
4
+	const size_t *lim, const rpn_value_t *res_const,
5
+	unsigned char rpn_stack_sz)
5 6
 {
6 7
 	rpn_if_param_t *res;
7 8
 	rpn_if_default_data_t *data;
@@ -85,13 +86,13 @@ rpn_if_param_t* rpn_if_default_params(short pos_flag, short res_flag,
85 86
 				pos_flag);
86 87
 			return NULL;
87 88
 	}
88
-	if(const_val_sz && !val)
89
+	if(const_val_sz && !res_const)
89 90
 	{
90 91
 		fprintf(stderr,
91 92
 			"Missing values when creating if params");
92 93
 		return NULL;
93 94
 	}
94
-	else if(!const_val_sz && val)
95
+	else if(!const_val_sz && res_const)
95 96
 	{
96 97
 		//Warning
97 98
 	}
@@ -119,7 +120,7 @@ rpn_if_param_t* rpn_if_default_params(short pos_flag, short res_flag,
119 120
 
120 121
 	if(const_val_sz)
121 122
 	{
122
-		memcpy(data->const_val, val, const_val_sz);
123
+		memcpy(data->const_val, res_const, const_val_sz);
123 124
 	}
124 125
 	else
125 126
 	{

+ 2
- 1
rpn_if_default.h View File

@@ -107,7 +107,8 @@ struct rpn_if_default_data_s
107 107
  * @todo Implementation/testing
108 108
  */
109 109
 rpn_if_param_t* rpn_if_default_params(short pos_flag, short res_flag,
110
-	const size_t *lim, const rpn_value_t *val, unsigned char rpn_stack_sz);
110
+	const size_t *lim, const rpn_value_t *res_const,
111
+	unsigned char rpn_stack_sz);
111 112
 
112 113
 /** Fetch size limit and const values array sizes given flag values
113 114
  *  @param pos_flag (@ref ifs_if_default_posflag)

+ 22
- 0
tests/tests_rpniter.py View File

@@ -146,6 +146,28 @@ class TestRPNIterInit(unittest.TestCase):
146 146
                     self.assertEqual(len(arr), expt_sz)
147 147
                     arr += 0x11111111 # check writing to all bytes
148 148
 
149
+    def test_str(self):
150
+        """ Test string representation of rif """
151
+
152
+        tests = [[pyrpn.const.POS_XY, pyrpn.const.RESULT_RGB, (1024,1024)],
153
+                 [pyrpn.const.POS_LINEAR, pyrpn.const.RESULT_CONST_RGBA, (1024,), (255,0,0,255)],
154
+                 [pyrpn.const.POS_XY, pyrpn.const.RESULT_CONST_RGBA, (640,480), (255,0,0,255)],
155
+                 [pyrpn.const.POS_LINEAR, pyrpn.const.RESULT_RGBA, (1024,)],
156
+                 [pyrpn.const.POS_XDIM, pyrpn.const.RESULT_RGB, (4, 2, 2, 640, 480)],
157
+        ]
158
+        for args in tests:
159
+            rif = pyrpn.RPNIterExpr(*args)
160
+            [expr.mutate(n_mutations=5) for expr in rif]
161
+            codes = {spl[1]:spl[2].strip('"')
162
+                     for spl in [s.split('=') for s in str(rif).split(';')
163
+                                 if len(s.strip())]}
164
+            argc = rif.get_params().argc
165
+            for key, orig in rif.items():
166
+                expr = pyrpn.RPNExpr(codes[key], argc)
167
+                with self.subTest(rif=rif, key=key, orig=orig, clone=expr):
168
+                    self.assertEqual(orig, expr)
169
+
170
+
149 171
 if __name__ == '__main__':
150 172
     unittest.main()
151 173
 

+ 1
- 1
tests/tests_rpniter_seq.py View File

@@ -70,7 +70,7 @@ class TestRPNIterMapping(unittest.TestCase):
70 70
                  ([pyrpn.const.POS_LINEAR, pyrpn.const.RESULT_RGBA, (1024,)],
71 71
                    ['X', 'R', 'G', 'B', 'A']),
72 72
                  ([pyrpn.const.POS_XDIM, pyrpn.const.RESULT_RGB, (4, 2, 2, 640, 480)],
73
-                     ['P0', 'P1', 'P2', 'P3', 'R', 'G', 'B']),
73
+                     ['D0', 'D1', 'D2', 'D3', 'R', 'G', 'B']),
74 74
         ]
75 75
         for args, keys in tests:
76 76
             rif=pyrpn.RPNIterExpr(*args)

Loading…
Cancel
Save