FLAME  devel
 All Classes Functions Variables Typedefs Enumerations Pages
modconfig.cpp
1 
2 #include <list>
3 #include <sstream>
4 
5 #include "flame/base.h"
6 
7 #include "pyflame.h"
8 
9 #define NO_IMPORT_ARRAY
10 #define PY_ARRAY_UNIQUE_SYMBOL FLAME_PyArray_API
11 #define NPY_NO_DEPRECATED_API NPY_1_6_API_VERSION
12 #include <numpy/ndarrayobject.h>
13 
23 void List2Config(Config& ret, PyObject *list, unsigned depth)
24 {
25  if(depth>3)
26  throw std::runtime_error("too deep for Dict2Config");
27 
28  PyRef<> iter(PyObject_GetIter(list));
29  while(true) {
30  PyObject *item = PyIter_Next(iter.py());
31  if(!item) break;
32  PyRef<> itemref(item);
33  const char *kname;
34  PyObject *value;
35  if(!PyArg_ParseTuple(item, "sO", &kname, &value))
36  throw std::runtime_error("list item is not a tuple?");
37 
38  if(PyArray_Check(value)) { // array as vector<double>
39  PyRef<> arr(PyArray_ContiguousFromAny(value, NPY_DOUBLE, 0, 2));
40  double *buf = (double*)PyArray_DATA(arr.py());
41  std::vector<double> temp(PyArray_SIZE(arr.py()));
42  std::copy(buf, buf+temp.size(), temp.begin());
43 
44  ret.swap<std::vector<double> >(kname, temp);
45 
46  } else if(PyNumber_Check(value)) { // scalar as double
47  PyRef<> dval(PyNumber_Float(value));
48  double val = PyFloat_AsDouble(dval.py());
49  ret.set<double>(kname, val);
50 
51  } else if(PyUnicode_Check(value) || (PY_MAJOR_VERSION < 3 && PyBytes_Check(value))) { // string
52  PyRef<> valref(value, borrow());
53  PyCString sval(valref);
54  const char *val = sval.c_str();
55 
56  ret.set<std::string>(kname, val);
57 
58  } else if(PySequence_Check(value)) { // list of dict
59  Py_ssize_t N = PySequence_Size(value);
60 
61  Config::vector_t output;
62  output.reserve(N);
63 
64  for(Py_ssize_t i=0; i<N; i++) {
65  PyRef<> elem(PySequence_GetItem(value, i));
66 
67  if(PyDict_Check(elem.py())) {
68  elem.reset(PyMapping_Items(elem.py()));
69  }
70  if(!PyList_Check(elem.py())) {
71  PyTypeObject *valuetype = (PyTypeObject*)PyObject_Type(elem.py());
72  throw std::invalid_argument(SB()<<"lists must contain only dict or list of tuples, not "<<valuetype->tp_name);
73  }
74 
75  output.push_back(ret.new_scope());
76 
77  List2Config(output.back(), elem.py(), depth+1); // inheirt parent scope
78  }
79 
80  ret.set<Config::vector_t>(kname, output);
81 
82  } else {
83  PyTypeObject *valuetype = (PyTypeObject*)PyObject_Type(value);
84  throw std::invalid_argument(SB()<<"Must be a dict, not "<<valuetype->tp_name);
85  }
86  }
87 }
88 
89 namespace {
90 
91 PyObject* pydirname(PyObject *obj)
92 {
93  if(!obj) return NULL;
94 
95  PyRef<> ospath(PyImport_ImportModule("os.path"));
96  return PyObject_CallMethod(ospath.py(), "dirname", "O", obj);
97 }
98 
99 struct confval : public boost::static_visitor<PyRef<> >
100 {
101  PyRef<> operator()(double v) const
102  {
103  return PyRef<>(PyFloat_FromDouble(v));
104  }
105 
106  PyRef<> operator()(const std::string& v) const
107  {
108  return PyRef<>(PyString_FromString(v.c_str()));
109  }
110 
111  PyRef<> operator()(const std::vector<double>& v) const
112  {
113  npy_intp dims[] = {(npy_intp)v.size()};
114  PyRef<> obj(PyArray_SimpleNew(1, dims, NPY_DOUBLE));
115  std::copy(v.begin(), v.end(), (double*)PyArray_DATA(obj.py()));
116  return obj;
117  }
118 
119  PyRef<> operator()(const Config::vector_t& v) const
120  {
121  PyRef<> L(PyList_New(v.size()));
122 
123  for(size_t i=0, N=v.size(); i<N; i++)
124  {
125  PyList_SetItem(L.py(), i, conf2dict(&v[i]));
126  }
127  return L;
128  }
129 };
130 
131 } // namespace
132 
133 PyObject* conf2dict(const Config *conf)
134 {
135  if(!conf)
136  return NULL;
137  PyRef<> list(PyList_New(0));
138 
139  for(Config::const_iterator it=conf->begin(), end=conf->end();
140  it!=end; ++it)
141  {
142  PyRef<> val(boost::apply_visitor(confval(), it->second));
143  PyRef<> tup(Py_BuildValue("sO", it->first.c_str(), val.py()));
144  if(PyList_Append(list.py(), tup.py()))
145  throw std::runtime_error("Failed to insert into dictionary from conf2dict");
146  }
147 
148  return list.releasePy();
149 }
150 
151 Config* list2conf(PyObject *dict)
152 {
153  if(!PyList_Check(dict))
154  throw std::invalid_argument("Not a list");
155  std::unique_ptr<Config> conf(new Config);
156  List2Config(*conf, dict);
157  return conf.release();
158 }
159 
160 PyObject* PyGLPSPrint(PyObject *, PyObject *args)
161 {
162  try {
163  PyObject *inp;
164  if(!PyArg_ParseTuple(args, "O", &inp))
165  return NULL;
166 
167  PyRef<> list;
168  if(PyDict_Check(inp)) {
169  list.reset(PyMapping_Items(inp));
170  inp = list.py();
171  }
172  if(!PyList_Check(inp))
173  return PyErr_Format(PyExc_ValueError, "argument must be dict or list of tuples");
174 
175  Config conf;
176  List2Config(conf, inp);
177  std::ostringstream strm;
178  GLPSPrint(strm, conf);
179  return PyString_FromString(strm.str().c_str());
180  }CATCH()
181 }
182 
183 #ifndef PY_SSIZE_T_CLEAN
184 #error the following assumes ssize_t is used
185 #endif
186 
187 Config* PyGLPSParse2Config(PyObject *, PyObject *args, PyObject *kws)
188 {
189  PyObject *conf = NULL, *extra_defs = Py_None;
190  const char *path = NULL;
191  const char *pnames[] = {"config", "path", "extra", NULL};
192  if(!PyArg_ParseTupleAndKeywords(args, kws, "O|zO", (char**)pnames, &conf, &path, &extra_defs))
193  return NULL;
194 
195  GLPSParser parser;
196 
197  if(extra_defs==Py_None) {
198  // no-op
199  } else if(PyDict_Check(extra_defs)) {
200  PyObject *key, *value;
201  Py_ssize_t pos = 0;
202 
203  while(PyDict_Next(extra_defs, &pos, &key, &value)) {
204  PyRef<> keyx(key, borrow());
205  PyCString keystr(keyx);
206 
207  Config::value_t curval;
208 
209  if(PyNumber_Check(value)) {
210  PyRef<> pyf(PyNumber_Float(value));
211  curval = PyFloat_AsDouble(pyf.py());
212 
213  } else if(PyString_Check(value)) {
214  PyRef<> valuex(value, borrow());
215  PyCString valstr(valuex);
216 
217  curval = valstr.c_str();
218 
219  } else {
220  PyErr_SetString(PyExc_ValueError, "extra {} can contain only numbers or strings");
221  return NULL;
222  }
223 
224  parser.setVar(keystr.c_str(), curval);
225  }
226  } else {
227  PyErr_SetString(PyExc_ValueError, "'extra' must be a dict");
228  return NULL;
229  }
230 
231  PyGetBuf buf;
232  std::unique_ptr<Config> C;
233 
234  PyRef<> listref;
235 
236  if(PyObject_HasAttrString(conf, "read")) { // file-like
237  PyCString pyname;
238 
239  if(!path && PyObject_HasAttrString(conf, "name")) {
240  path = pyname.c_str(pydirname(PyObject_GetAttrString(conf, "name")));
241  }
242 
243  PyRef<> pybytes(PyObject_CallMethod(conf, "read", ""));
244  if(!buf.get(pybytes.py())) {
245  PyErr_SetString(PyExc_TypeError, "read() must return a buffer");
246  return NULL;
247  }
248  C.reset(parser.parse_byte((const char*)buf.data(), buf.size(), path));
249 
250  } else if(buf.get(conf)) {
251  C.reset(parser.parse_byte((const char*)buf.data(), buf.size(), path));
252 
253 #if PY_MAJOR_VERSION >= 3
254  } else if(PyUnicode_Check(conf)) { // py3 str (aka unicode) doesn't implement buffer iface
255  PyCString buf;
256  const char *cbuf = buf.c_str(conf);
257 
258  C.reset(parser.parse_byte(cbuf, strlen(cbuf), path));
259 #endif
260 
261  } else {
262  if(PyDict_Check(conf)) {
263  listref.reset(PyMapping_Items(conf));
264  conf = listref.py();
265  }
266  if(PyList_Check(conf)) {
267  C.reset(list2conf(conf));
268 
269  } else {
270  throw std::invalid_argument("'config' must be dict, list of tuples, or byte buffer");
271  }
272  }
273 
274  return C.release();
275 }
276 
277 PyObject* PyGLPSParse(PyObject *unused, PyObject *args, PyObject *kws)
278 {
279  try{
280  return conf2dict(PyGLPSParse2Config(unused, args, kws));
281  }CATCH()
282 }
void setVar(const std::string &name, const Config::value_t &v)
Pre-define variable.
Definition: config.cpp:352
void set(const std::string &name, typename boost::call_traits< typename detail::is_config_value< T >::type >::param_type val)
Definition: config.h:175
Interface to lattice file parser.
Definition: config.h:240
void swap(const std::string &name, typename boost::call_traits< typename detail::is_config_value< T >::type >::reference val)
Definition: config.h:192
Associative configuration container.
Definition: config.h:66
boost::variant< double, std::vector< double >, std::string, std::vector< Config > > value_t
An individual value (double, double[], string, or Config[])
Definition: config.h:75
const_iterator end() const
one after the last element
Definition: config.h:220
Config new_scope() const
Create a new inner scope.
Definition: config.cpp:91
values_t::const_iterator const_iterator
const_iterator
Definition: config.h:215
Config * parse_byte(const char *s, size_t len, const char *path=NULL)
Parse from byte buffer.
Definition: config.cpp:405
const_iterator begin() const
The first element.
Definition: config.h:218