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