1 /*
2  Author: Michael Droettboom
3          mdroe@stsci.edu
4 */
5 
6 #define NO_IMPORT_ARRAY
7 
8 #include "astropy_wcs/pyutil.h"
9 #include "astropy_wcs/str_list_proxy.h"
10 
11 /***************************************************************************
12  * List-of-units proxy object
13  ***************************************************************************/
14 
15 #define MAXSIZE 68
16 #define ARRAYSIZE 72
17 
18 static PyTypeObject PyUnitListProxyType;
19 
20 typedef struct {
21   PyObject_HEAD
22   /*@null@*/ /*@shared@*/ PyObject* pyobject;
23   Py_ssize_t size;
24   char (*array)[ARRAYSIZE];
25   PyObject* unit_class;
26 } PyUnitListProxy;
27 
28 static void
PyUnitListProxy_dealloc(PyUnitListProxy * self)29 PyUnitListProxy_dealloc(
30     PyUnitListProxy* self) {
31 
32   PyObject_GC_UnTrack(self);
33   Py_XDECREF(self->pyobject);
34   Py_TYPE(self)->tp_free((PyObject*)self);
35 }
36 
37 /*@null@*/ static PyObject *
PyUnitListProxy_new(PyTypeObject * type,PyObject * args,PyObject * kwds)38 PyUnitListProxy_new(
39     PyTypeObject* type,
40     /*@unused@*/ PyObject* args,
41     /*@unused@*/ PyObject* kwds) {
42 
43   PyUnitListProxy* self = NULL;
44 
45   self = (PyUnitListProxy*)type->tp_alloc(type, 0);
46   if (self != NULL) {
47     self->pyobject = NULL;
48     self->unit_class = NULL;
49   }
50   return (PyObject*)self;
51 }
52 
53 static int
PyUnitListProxy_traverse(PyUnitListProxy * self,visitproc visit,void * arg)54 PyUnitListProxy_traverse(
55     PyUnitListProxy* self,
56     visitproc visit,
57     void *arg) {
58 
59   Py_VISIT(self->pyobject);
60   Py_VISIT(self->unit_class);
61   return 0;
62 }
63 
64 static int
PyUnitListProxy_clear(PyUnitListProxy * self)65 PyUnitListProxy_clear(
66     PyUnitListProxy *self) {
67 
68   Py_CLEAR(self->pyobject);
69   Py_CLEAR(self->unit_class);
70 
71   return 0;
72 }
73 
74 /*@null@*/ PyObject *
PyUnitListProxy_New(PyObject * owner,Py_ssize_t size,char (* array)[ARRAYSIZE])75 PyUnitListProxy_New(
76     /*@shared@*/ PyObject* owner,
77     Py_ssize_t size,
78     char (*array)[ARRAYSIZE]) {
79 
80   PyUnitListProxy* self = NULL;
81   PyObject *units_module;
82   PyObject *units_dict;
83   PyObject *unit_class;
84 
85   units_module = PyImport_ImportModule("astropy.units");
86   if (units_module == NULL) {
87     return NULL;
88   }
89 
90   units_dict = PyModule_GetDict(units_module);
91   if (units_dict == NULL) {
92     return NULL;
93   }
94 
95   unit_class = PyDict_GetItemString(units_dict, "Unit");
96   if (unit_class == NULL) {
97     PyErr_SetString(PyExc_RuntimeError, "Could not import Unit class");
98     return NULL;
99   }
100 
101   Py_INCREF(unit_class);
102 
103   self = (PyUnitListProxy*)PyUnitListProxyType.tp_alloc(
104       &PyUnitListProxyType, 0);
105   if (self == NULL) {
106     return NULL;
107   }
108 
109   Py_XINCREF(owner);
110   self->pyobject = owner;
111   self->size = size;
112   self->array = array;
113   self->unit_class = unit_class;
114   return (PyObject*)self;
115 }
116 
117 static Py_ssize_t
PyUnitListProxy_len(PyUnitListProxy * self)118 PyUnitListProxy_len(
119     PyUnitListProxy* self) {
120 
121   return self->size;
122 }
123 
124 static PyObject*
_get_unit(PyObject * unit_class,PyObject * unit)125 _get_unit(
126     PyObject *unit_class,
127     PyObject *unit) {
128 
129   PyObject *args;
130   PyObject *kw;
131   PyObject *result;
132 
133   kw = Py_BuildValue("{s:s,s:s}", "format", "fits", "parse_strict", "warn");
134   if (kw == NULL) {
135       return NULL;
136   }
137 
138   args = PyTuple_New(1);
139   if (args == NULL) {
140       Py_DECREF(kw);
141       return NULL;
142   }
143   PyTuple_SetItem(args, 0, unit);
144   Py_INCREF(unit);
145 
146   result = PyObject_Call(unit_class, args, kw);
147 
148   Py_DECREF(args);
149   Py_DECREF(kw);
150   return result;
151 }
152 
153 /*@null@*/ static PyObject*
PyUnitListProxy_getitem(PyUnitListProxy * self,Py_ssize_t index)154 PyUnitListProxy_getitem(
155     PyUnitListProxy* self,
156     Py_ssize_t index) {
157 
158   PyObject *value;
159   PyObject *result;
160 
161   if (index >= self->size || index < 0) {
162     PyErr_SetString(PyExc_IndexError, "index out of range");
163     return NULL;
164   }
165 
166   value = PyUnicode_FromString(self->array[index]);
167 
168   result = _get_unit(self->unit_class, value);
169 
170   Py_DECREF(value);
171   return result;
172 }
173 
174 static PyObject*
PyUnitListProxy_richcmp(PyObject * a,PyObject * b,int op)175 PyUnitListProxy_richcmp(
176 	PyObject *a,
177 	PyObject *b,
178 	int op){
179   PyUnitListProxy *lhs, *rhs;
180   Py_ssize_t idx;
181   int equal = 1;
182   assert(a != NULL && b != NULL);
183   if (!PyObject_TypeCheck(a, &PyUnitListProxyType) ||
184       !PyObject_TypeCheck(b, &PyUnitListProxyType)) {
185     Py_RETURN_NOTIMPLEMENTED;
186   }
187   if (op != Py_EQ && op != Py_NE) {
188     Py_RETURN_NOTIMPLEMENTED;
189   }
190 
191   /* The actual comparison of the two objects. unit_class is ignored because
192    * it's not an essential property of the instances.
193    */
194   lhs = (PyUnitListProxy *)a;
195   rhs = (PyUnitListProxy *)b;
196   if (lhs->size != rhs->size) {
197     equal = 0;
198   }
199   for (idx = 0; idx < lhs->size && equal == 1; idx++) {
200     if (strncmp(lhs->array[idx], rhs->array[idx], ARRAYSIZE) != 0) {
201       equal = 0;
202     }
203   }
204   if ((op == Py_EQ && equal == 1) ||
205       (op == Py_NE && equal == 0)) {
206     Py_RETURN_TRUE;
207   } else {
208     Py_RETURN_FALSE;
209   }
210 }
211 
212 static int
PyUnitListProxy_setitem(PyUnitListProxy * self,Py_ssize_t index,PyObject * arg)213 PyUnitListProxy_setitem(
214     PyUnitListProxy* self,
215     Py_ssize_t index,
216     PyObject* arg) {
217 
218   PyObject* value;
219   PyObject* unicode_value;
220   PyObject* bytes_value;
221 
222   if (index >= self->size || index < 0) {
223     PyErr_SetString(PyExc_IndexError, "index out of range");
224     return -1;
225   }
226 
227   value = _get_unit(self->unit_class, arg);
228   if (value == NULL) {
229     return -1;
230   }
231 
232   unicode_value = PyObject_CallMethod(value, "to_string", "s", "fits");
233   if (unicode_value == NULL) {
234     Py_DECREF(value);
235     return -1;
236   }
237   Py_DECREF(value);
238 
239   if (PyUnicode_Check(unicode_value)) {
240     bytes_value = PyUnicode_AsASCIIString(unicode_value);
241     if (bytes_value == NULL) {
242       Py_DECREF(unicode_value);
243       return -1;
244     }
245     Py_DECREF(unicode_value);
246   } else {
247     bytes_value = unicode_value;
248   }
249 
250   strncpy(self->array[index], PyBytes_AsString(bytes_value), MAXSIZE);
251   Py_DECREF(bytes_value);
252 
253   return 0;
254 }
255 
256 /*@null@*/ static PyObject*
PyUnitListProxy_repr(PyUnitListProxy * self)257 PyUnitListProxy_repr(
258     PyUnitListProxy* self) {
259 
260   return str_list_proxy_repr(self->array, self->size, MAXSIZE);
261 }
262 
263 static PySequenceMethods PyUnitListProxy_sequence_methods = {
264   (lenfunc)PyUnitListProxy_len,
265   NULL,
266   NULL,
267   (ssizeargfunc)PyUnitListProxy_getitem,
268   NULL,
269   (ssizeobjargproc)PyUnitListProxy_setitem,
270   NULL,
271   NULL,
272   NULL,
273   NULL
274 };
275 
276 static PyTypeObject PyUnitListProxyType = {
277   PyVarObject_HEAD_INIT(NULL, 0)
278   "astropy.wcs.UnitListProxy", /*tp_name*/
279   sizeof(PyUnitListProxy),  /*tp_basicsize*/
280   0,                          /*tp_itemsize*/
281   (destructor)PyUnitListProxy_dealloc, /*tp_dealloc*/
282   0,                          /*tp_print*/
283   0,                          /*tp_getattr*/
284   0,                          /*tp_setattr*/
285   0,                          /*tp_compare*/
286   (reprfunc)PyUnitListProxy_repr, /*tp_repr*/
287   0,                          /*tp_as_number*/
288   &PyUnitListProxy_sequence_methods, /*tp_as_sequence*/
289   0,                          /*tp_as_mapping*/
290   0,                          /*tp_hash */
291   0,                          /*tp_call*/
292   (reprfunc)PyUnitListProxy_repr, /*tp_str*/
293   0,                          /*tp_getattro*/
294   0,                          /*tp_setattro*/
295   0,                          /*tp_as_buffer*/
296   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
297   0,                          /* tp_doc */
298   (traverseproc)PyUnitListProxy_traverse, /* tp_traverse */
299   (inquiry)PyUnitListProxy_clear, /* tp_clear */
300   (richcmpfunc)PyUnitListProxy_richcmp, /* tp_richcompare */
301   0,                          /* tp_weaklistoffset */
302   0,                          /* tp_iter */
303   0,                          /* tp_iternext */
304   0,                          /* tp_methods */
305   0,                          /* tp_members */
306   0,                          /* tp_getset */
307   0,                          /* tp_base */
308   0,                          /* tp_dict */
309   0,                          /* tp_descr_get */
310   0,                          /* tp_descr_set */
311   0,                          /* tp_dictoffset */
312   0,                          /* tp_init */
313   0,                          /* tp_alloc */
314   PyUnitListProxy_new,      /* tp_new */
315 };
316 
317 
318 int
set_unit_list(PyObject * owner,const char * propname,PyObject * value,Py_ssize_t len,char (* dest)[ARRAYSIZE])319 set_unit_list(
320     PyObject* owner,
321     const char* propname,
322     PyObject* value,
323     Py_ssize_t len,
324     char (*dest)[ARRAYSIZE]) {
325 
326   PyObject*  unit  = NULL;
327   PyObject*  proxy = NULL;
328   Py_ssize_t i        = 0;
329 
330   if (check_delete(propname, value)) {
331     return -1;
332   }
333 
334   if (!PySequence_Check(value)) {
335     PyErr_Format(
336         PyExc_TypeError,
337         "'%s' must be a sequence of strings",
338         propname);
339     return -1;
340   }
341 
342   if (PySequence_Size(value) != len) {
343     PyErr_Format(
344         PyExc_ValueError,
345         "len(%s) must be %u",
346         propname,
347         (unsigned int)len);
348     return -1;
349   }
350 
351   proxy = PyUnitListProxy_New(owner, len, dest);
352   if (proxy == NULL) {
353       return -1;
354   }
355 
356   for (i = 0; i < len; ++i) {
357     unit = PySequence_GetItem(value, i);
358     if (unit == NULL) {
359       Py_DECREF(proxy);
360       return -1;
361     }
362 
363     if (PySequence_SetItem(proxy, i, unit) == -1) {
364       Py_DECREF(proxy);
365       Py_DECREF(unit);
366       return -1;
367     }
368 
369     Py_DECREF(unit);
370   }
371 
372   Py_DECREF(proxy);
373 
374   return 0;
375 }
376 
377 
378 int
_setup_unit_list_proxy_type(PyObject * m)379 _setup_unit_list_proxy_type(
380     /*@unused@*/ PyObject* m) {
381 
382   if (PyType_Ready(&PyUnitListProxyType) < 0) {
383     return 1;
384   }
385 
386   return 0;
387 }
388