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