summaryrefslogtreecommitdiffstats
path: root/doc/src/snippets/sqldatabase/sqldatabase.cpp
blob: e8ab4b673eead4fc67a308f868780dd204621e50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtGui>
#include <QtSql>

#include <iostream>

using namespace std;

QString tr(const char *text)
{
    return QApplication::translate(text, text);
}

void QSqlDatabase_snippets()
{
    {
//! [0]
    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
    db.setHostName("acidalia");
    db.setDatabaseName("customdb");
    db.setUserName("mojito");
    db.setPassword("J0a1m8");
    bool ok = db.open();
//! [0]
    Q_UNUSED(ok);
    }

    {
//! [1]
    QSqlDatabase db = QSqlDatabase::database();
//! [1]
    }
}

void QSqlField_snippets()
{
#if 0
    {
//! [2]
    QSqlField field("age", QVariant::Int);
    field.setValue(QPixmap());  // WRONG
//! [2]
    }
#endif

    {
//! [3]
    QSqlField field("age", QVariant::Int);
    field.setValue(QString("123"));  // casts QString to int
//! [3]
    }

    {
//! [4]
    QSqlQuery query;
//! [4] //! [5]
    QSqlRecord record = query.record();
//! [5] //! [6]
    QSqlField field = record.field("country");
//! [6]
    }
}

void doSomething(const QString &)
{
}

void QSqlQuery_snippets()
{
    {
    // typical loop
//! [7]
    QSqlQuery query("SELECT country FROM artist");
    while (query.next()) {
        QString country = query.value(0).toString();
        doSomething(country);
    }
//! [7]
    }

    {
    // field index lookup
//! [8]
    QSqlQuery query("SELECT * FROM artist");
    int fieldNo = query.record().indexOf("country");
    while (query.next()) {
        QString country = query.value(fieldNo).toString();
        doSomething(country);
    }
//! [8]
    }

    {
    // named with named
//! [9]
    QSqlQuery query;
    query.prepare("INSERT INTO person (id, forename, surname) "
                  "VALUES (:id, :forename, :surname)");
    query.bindValue(":id", 1001);
    query.bindValue(":forename", "Bart");
    query.bindValue(":surname", "Simpson");
    query.exec();
//! [9]
    }

    {
    // positional with named
//! [10]
    QSqlQuery query;
    query.prepare("INSERT INTO person (id, forename, surname) "
                  "VALUES (:id, :forename, :surname)");
    query.bindValue(0, 1001);
    query.bindValue(1, "Bart");
    query.bindValue(2, "Simpson");
    query.exec();
//! [10]
    }

    {
    // positional 1
//! [11]
    QSqlQuery query;
    query.prepare("INSERT INTO person (id, forename, surname) "
                  "VALUES (?, ?, ?)");
    query.bindValue(0, 1001);
    query.bindValue(1, "Bart");
    query.bindValue(2, "Simpson");
    query.exec();
//! [11]
    }

    {
    // positional 2
//! [12]
    QSqlQuery query;
    query.prepare("INSERT INTO person (id, forename, surname) "
                  "VALUES (?, ?, ?)");
    query.addBindValue(1001);
    query.addBindValue("Bart");
    query.addBindValue("Simpson");
    query.exec();
//! [12]
    }

    {
    // stored
//! [13]
    QSqlQuery query;
    query.prepare("CALL AsciiToInt(?, ?)");
    query.bindValue(0, "A");
    query.bindValue(1, 0, QSql::Out);
    query.exec();
    int i = query.boundValue(1).toInt(); // i is 65
//! [13]
    Q_UNUSED(i);
    }

    QSqlQuery query;

    {
    // examine with named binding
//! [14]
    QMapIterator<QString, QVariant> i(query.boundValues());
    while (i.hasNext()) {
        i.next();
        cout << i.key().toAscii().data() << ": "
             << i.value().toString().toAscii().data() << endl;
    }
//! [14]
    }

    {
    // examine with positional binding
//! [15]
    QList<QVariant> list = query.boundValues().values();
    for (int i = 0; i < list.size(); ++i)
        cout << i << ": " << list.at(i).toString().toAscii().data() << endl;
//! [15]
    }
}

void QSqlQueryModel_snippets()
{
    {
//! [16]
    QSqlQueryModel *model = new QSqlQueryModel;
    model->setQuery("SELECT name, salary FROM employee");
    model->setHeaderData(0, Qt::Horizontal, tr("Name"));
    model->setHeaderData(1, Qt::Horizontal, tr("Salary"));

//! [17]
    QTableView *view = new QTableView;
//! [17] //! [18]
    view->setModel(model);
//! [18] //! [19]
    view->show();
//! [16] //! [19] //! [20]
    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
//! [20]
    }

//! [21]
    QSqlQueryModel model;
    model.setQuery("SELECT * FROM employee");
    int salary = model.record(4).value("salary").toInt();
//! [21]
    Q_UNUSED(salary);

    {
//! [22]
    int salary = model.data(model.index(4, 2)).toInt();
//! [22]
    Q_UNUSED(salary);
    }

    for (int row = 0; row < model.rowCount(); ++row) {
        for (int col = 0; col < model.columnCount(); ++col) {
            qDebug() << model.data(model.index(row, col));
        }
    }
}

class MyModel : public QSqlQueryModel
{
public:
    QVariant data(const QModelIndex &item, int role) const;

    int m_specialColumnNo;
};

//! [23]
QVariant MyModel::data(const QModelIndex &item, int role) const
{
    if (item.column() == m_specialColumnNo) {
        // handle column separately
    }
    return QSqlQueryModel::data(item, role);
}
//! [23]

void QSqlTableModel_snippets()
{
//! [24]
    QSqlTableModel *model = new QSqlTableModel;
    model->setTable("employee");
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    model->select();
    model->removeColumn(0); // don't show the ID
    model->setHeaderData(0, Qt::Horizontal, tr("Name"));
    model->setHeaderData(1, Qt::Horizontal, tr("Salary"));

    QTableView *view = new QTableView;
    view->setModel(model);
    view->show();
//! [24]

    {
//! [25]
    QSqlTableModel model;
    model.setTable("employee");
    QString name = model.record(4).value("name").toString();
//! [25]
    }
}

void sql_intro_snippets()
{
    {
//! [26]
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("bigblue");
    db.setDatabaseName("flightdb");
    db.setUserName("acarlson");
    db.setPassword("1uTbSbAs");
    bool ok = db.open();
//! [26]
    Q_UNUSED(ok);
    }

    {
//! [27]
    QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first");
    QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second");
//! [27]
    }

    {
//! [28]
    QSqlDatabase defaultDB = QSqlDatabase::database();
//! [28] //! [29]
    QSqlDatabase firstDB = QSqlDatabase::database("first");
//! [29] //! [30]
    QSqlDatabase secondDB = QSqlDatabase::database("second");
//! [30]
    }

    {
    // SELECT1
//! [31]
    QSqlQuery query;
    query.exec("SELECT name, salary FROM employee WHERE salary > 50000");
//! [31]

//! [32]
    while (query.next()) {
        QString name = query.value(0).toString();
        int salary = query.value(1).toInt();
        qDebug() << name << salary;
    }
//! [32]
    }

    {
    // FEATURE
//! [33]
    QSqlQuery query;
    int numRows;
    query.exec("SELECT name, salary FROM employee WHERE salary > 50000");

    QSqlDatabase defaultDB = QSqlDatabase::database();
    if (defaultDB.driver()->hasFeature(QSqlDriver::QuerySize)) {
        numRows = query.size();
    } else {
        // this can be very slow
        query.last();
        numRows = query.at() + 1;
    }
//! [33]
    }

    {
    // INSERT1
//! [34]
    QSqlQuery query;
    query.exec("INSERT INTO employee (id, name, salary) "
               "VALUES (1001, 'Thad Beaumont', 65000)");
//! [34]
    }

    {
    // NAMED BINDING
//! [35]
    QSqlQuery query;
    query.prepare("INSERT INTO employee (id, name, salary) "
                  "VALUES (:id, :name, :salary)");
    query.bindValue(":id", 1001);
    query.bindValue(":name", "Thad Beaumont");
    query.bindValue(":salary", 65000);
    query.exec();
//! [35]
    }

    {
    // POSITIONAL BINDING
//! [36]
    QSqlQuery query;
    query.prepare("INSERT INTO employee (id, name, salary) "
                  "VALUES (?, ?, ?)");
    query.addBindValue(1001);
    query.addBindValue("Thad Beaumont");
    query.addBindValue(65000);
    query.exec();
//! [36]
    }

    {
    // UPDATE1
//! [37]
    QSqlQuery query;
    query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003");
//! [37]
    }

    {
    // DELETE1
//! [38]
    QSqlQuery query;
    query.exec("DELETE FROM employee WHERE id = 1007");
//! [38]
    }

    {
    // TRANSACTION
//! [39]
    QSqlDatabase::database().transaction();
    QSqlQuery query;
    query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");
    if (query.next()) {
        int employeeId = query.value(0).toInt();
        query.exec("INSERT INTO project (id, name, ownerid) "
                   "VALUES (201, 'Manhattan Project', "
                   + QString::number(employeeId) + ')');
    }
    QSqlDatabase::database().commit();
//! [39]
    }

    {
    // SQLQUERYMODEL1
//! [40]
    QSqlQueryModel model;
    model.setQuery("SELECT * FROM employee");

    for (int i = 0; i < model.rowCount(); ++i) {
        int id = model.record(i).value("id").toInt();
        QString name = model.record(i).value("name").toString();
        qDebug() << id << name;
    }
//! [40]
    }

    {
    // SQLTABLEMODEL1
//! [41]
    QSqlTableModel model;
    model.setTable("employee");
    model.setFilter("salary > 50000");
    model.setSort(2, Qt::DescendingOrder);
    model.select();

    for (int i = 0; i < model.rowCount(); ++i) {
        QString name = model.record(i).value("name").toString();
        int salary = model.record(i).value("salary").toInt();
        qDebug() << name << salary;
    }
//! [41]
    }

    {
    // SQLTABLEMODEL2
    QSqlTableModel model;
    model.setTable("employee");

//! [42]
    for (int i = 0; i < model.rowCount(); ++i) {
        QSqlRecord record = model.record(i);
        double salary = record.value("salary").toInt();
        salary *= 1.1;
        record.setValue("salary", salary);
        model.setRecord(i, record);
    }
    model.submitAll();
//! [42]

    // SQLTABLEMODEL3
    int row = 1;
    int column = 2;
//! [43]
    model.setData(model.index(row, column), 75000);
    model.submitAll();
//! [43]

    // SQLTABLEMODEL4
//! [44]
    model.insertRows(row, 1);
    model.setData(model.index(row, 0), 1013);
    model.setData(model.index(row, 1), "Peter Gordon");
    model.setData(model.index(row, 2), 68500);
    model.submitAll();
//! [44]

//! [45]
    model.removeRows(row, 5);
//! [45] //! [46]
    model.submitAll();
//! [46]
    }
}

//! [47]
class XyzResult : public QSqlResult
{
public:
    XyzResult(const QSqlDriver *driver)
        : QSqlResult(driver) {}
    ~XyzResult() {}

protected:
    QVariant data(int /* index */) { return QVariant(); }
    bool isNull(int /* index */) { return false; }
    bool reset(const QString & /* query */) { return false; }
    bool fetch(int /* index */) { return false; }
    bool fetchFirst() { return false; }
    bool fetchLast() { return false; }
    int size() { return 0; }
    int numRowsAffected() { return 0; }
    QSqlRecord record() const { return QSqlRecord(); }
};
//! [47]

//! [48]
class XyzDriver : public QSqlDriver
{
public:
    XyzDriver() {}
    ~XyzDriver() {}

    bool hasFeature(DriverFeature /* feature */) const { return false; }
    bool open(const QString & /* db */, const QString & /* user */,
              const QString & /* password */, const QString & /* host */,
              int /* port */, const QString & /* options */)
        { return false; }
    void close() {}
    QSqlResult *createResult() const { return new XyzResult(this); }
};
//! [48]

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QSqlDatabase_snippets();
    QSqlField_snippets();
    QSqlQuery_snippets();
    QSqlQueryModel_snippets();
    QSqlTableModel_snippets();

    XyzDriver driver;
    XyzResult result(&driver);
}
ass="hl kwd">vgetargs1(args, format, &va, FLAG_COMPAT|FLAG_SIZE_T); va_end(va); return retval; } int PyArg_ParseTuple(PyObject *args, const char *format, ...) { int retval; va_list va; va_start(va, format); retval = vgetargs1(args, format, &va, 0); va_end(va); return retval; } int _PyArg_ParseTuple_SizeT(PyObject *args, char *format, ...) { int retval; va_list va; va_start(va, format); retval = vgetargs1(args, format, &va, FLAG_SIZE_T); va_end(va); return retval; } int PyArg_VaParse(PyObject *args, const char *format, va_list va) { va_list lva; Py_VA_COPY(lva, va); return vgetargs1(args, format, &lva, 0); } int _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va) { va_list lva; Py_VA_COPY(lva, va); return vgetargs1(args, format, &lva, FLAG_SIZE_T); } /* Handle cleanup of allocated memory in case of exception */ static int cleanup_ptr(PyObject *self, void *ptr) { if (ptr) { PyMem_FREE(ptr); } return 0; } static int cleanup_buffer(PyObject *self, void *ptr) { Py_buffer *buf = (Py_buffer *)ptr; if (buf) { PyBuffer_Release(buf); } return 0; } static int addcleanup(void *ptr, freelist_t *freelist, destr_t destructor) { int index; index = freelist->first_available; freelist->first_available += 1; freelist->entries[index].item = ptr; freelist->entries[index].destructor = destructor; return 0; } static int cleanreturn(int retval, freelist_t *freelist) { int index; if (retval == 0) { /* A failure occurred, therefore execute all of the cleanup functions. */ for (index = 0; index < freelist->first_available; ++index) { freelist->entries[index].destructor(NULL, freelist->entries[index].item); } } if (freelist->entries_malloced) PyMem_FREE(freelist->entries); return retval; } static int vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags) { char msgbuf[256]; int levels[32]; const char *fname = NULL; const char *message = NULL; int min = -1; int max = 0; int level = 0; int endfmt = 0; const char *formatsave = format; Py_ssize_t i, len; char *msg; int compat = flags & FLAG_COMPAT; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; freelist.entries = static_entries; freelist.first_available = 0; freelist.entries_malloced = 0; assert(compat || (args != (PyObject*)NULL)); flags = flags & ~FLAG_COMPAT; while (endfmt == 0) { int c = *format++; switch (c) { case '(': if (level == 0) max++; level++; if (level >= 30) Py_FatalError("too many tuple nesting levels " "in argument format string"); break; case ')': if (level == 0) Py_FatalError("excess ')' in getargs format"); else level--; break; case '\0': endfmt = 1; break; case ':': fname = format; endfmt = 1; break; case ';': message = format; endfmt = 1; break; case '|': if (level == 0) min = max; break; default: if (level == 0) { if (Py_ISALPHA(Py_CHARMASK(c))) if (c != 'e') /* skip encoded */ max++; } break; } } if (level != 0) Py_FatalError(/* '(' */ "missing ')' in getargs format"); if (min < 0) min = max; format = formatsave; if (max > STATIC_FREELIST_ENTRIES) { freelist.entries = PyMem_NEW(freelistentry_t, max); if (freelist.entries == NULL) { PyErr_NoMemory(); return 0; } freelist.entries_malloced = 1; } if (compat) { if (max == 0) { if (args == NULL) return 1; PyErr_Format(PyExc_TypeError, "%.200s%s takes no arguments", fname==NULL ? "function" : fname, fname==NULL ? "" : "()"); return cleanreturn(0, &freelist); } else if (min == 1 && max == 1) { if (args == NULL) { PyErr_Format(PyExc_TypeError, "%.200s%s takes at least one argument", fname==NULL ? "function" : fname, fname==NULL ? "" : "()"); return cleanreturn(0, &freelist); } msg = convertitem(args, &format, p_va, flags, levels, msgbuf, sizeof(msgbuf), &freelist); if (msg == NULL) return cleanreturn(1, &freelist); seterror(levels[0], msg, levels+1, fname, message); return cleanreturn(0, &freelist); } else { PyErr_SetString(PyExc_SystemError, "old style getargs format uses new features"); return cleanreturn(0, &freelist); } } if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_SystemError, "new style getargs format but argument is not a tuple"); return cleanreturn(0, &freelist); } len = PyTuple_GET_SIZE(args); if (len < min || max < len) { if (message == NULL) PyErr_Format(PyExc_TypeError, "%.150s%s takes %s %d argument%s (%ld given)", fname==NULL ? "function" : fname, fname==NULL ? "" : "()", min==max ? "exactly" : len < min ? "at least" : "at most", len < min ? min : max, (len < min ? min : max) == 1 ? "" : "s", Py_SAFE_DOWNCAST(len, Py_ssize_t, long)); else PyErr_SetString(PyExc_TypeError, message); return cleanreturn(0, &freelist); } for (i = 0; i < len; i++) { if (*format == '|') format++; msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va, flags, levels, msgbuf, sizeof(msgbuf), &freelist); if (msg) { seterror(i+1, msg, levels, fname, msg); return cleanreturn(0, &freelist); } } if (*format != '\0' && !Py_ISALPHA(Py_CHARMASK(*format)) && *format != '(' && *format != '|' && *format != ':' && *format != ';') { PyErr_Format(PyExc_SystemError, "bad format string: %.200s", formatsave); return cleanreturn(0, &freelist); } return cleanreturn(1, &freelist); } static void seterror(Py_ssize_t iarg, const char *msg, int *levels, const char *fname, const char *message) { char buf[512]; int i; char *p = buf; if (PyErr_Occurred()) return; else if (message == NULL) { if (fname != NULL) { PyOS_snprintf(p, sizeof(buf), "%.200s() ", fname); p += strlen(p); } if (iarg != 0) { PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument %" PY_FORMAT_SIZE_T "d", iarg); i = 0; p += strlen(p); while (i < 32 && levels[i] > 0 && (int)(p-buf) < 220) { PyOS_snprintf(p, sizeof(buf) - (p - buf), ", item %d", levels[i]-1); p += strlen(p); i++; } } else { PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument"); p += strlen(p); } PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg); message = buf; } PyErr_SetString(PyExc_TypeError, message); } /* Convert a tuple argument. On entry, *p_format points to the character _after_ the opening '('. On successful exit, *p_format points to the closing ')'. If successful: *p_format and *p_va are updated, *levels and *msgbuf are untouched, and NULL is returned. If the argument is invalid: *p_format is unchanged, *p_va is undefined, *levels is a 0-terminated list of item numbers, *msgbuf contains an error message, whose format is: "must be <typename1>, not <typename2>", where: <typename1> is the name of the expected type, and <typename2> is the name of the actual type, and msgbuf is returned. */ static char * converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags, int *levels, char *msgbuf, size_t bufsize, int toplevel, freelist_t *freelist) { int level = 0; int n = 0; const char *format = *p_format; int i; Py_ssize_t len; for (;;) { int c = *format++; if (c == '(') { if (level == 0) n++; level++; } else if (c == ')') { if (level == 0) break; level--; } else if (c == ':' || c == ';' || c == '\0') break; else if (level == 0 && Py_ISALPHA(Py_CHARMASK(c))) n++; } if (!PySequence_Check(arg) || PyBytes_Check(arg)) { levels[0] = 0; PyOS_snprintf(msgbuf, bufsize, toplevel ? "expected %d arguments, not %.50s" : "must be %d-item sequence, not %.50s", n, arg == Py_None ? "None" : arg->ob_type->tp_name); return msgbuf; } len = PySequence_Size(arg); if (len != n) { levels[0] = 0; if (toplevel) { PyOS_snprintf(msgbuf, bufsize, "expected %d arguments, not %" PY_FORMAT_SIZE_T "d", n, len); } else { PyOS_snprintf(msgbuf, bufsize, "must be sequence of length %d, " "not %" PY_FORMAT_SIZE_T "d", n, len); } return msgbuf; } format = *p_format; for (i = 0; i < n; i++) { char *msg; PyObject *item; item = PySequence_GetItem(arg, i); if (item == NULL) { PyErr_Clear(); levels[0] = i+1; levels[1] = 0; strncpy(msgbuf, "is not retrievable", bufsize); return msgbuf; } msg = convertitem(item, &format, p_va, flags, levels+1, msgbuf, bufsize, freelist); /* PySequence_GetItem calls tp->sq_item, which INCREFs */ Py_XDECREF(item); if (msg != NULL) { levels[0] = i+1; return msg; } } *p_format = format; return NULL; } /* Convert a single item. */ static char * convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags, int *levels, char *msgbuf, size_t bufsize, freelist_t *freelist) { char *msg; const char *format = *p_format; if (*format == '(' /* ')' */) { format++; msg = converttuple(arg, &format, p_va, flags, levels, msgbuf, bufsize, 0, freelist); if (msg == NULL) format++; } else { msg = convertsimple(arg, &format, p_va, flags, msgbuf, bufsize, freelist); if (msg != NULL) levels[0] = 0; } if (msg == NULL) *p_format = format; return msg; } /* Format an error message generated by convertsimple(). */ static char * converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize) { assert(expected != NULL); assert(arg != NULL); PyOS_snprintf(msgbuf, bufsize, "must be %.50s, not %.50s", expected, arg == Py_None ? "None" : arg->ob_type->tp_name); return msgbuf; } #define CONV_UNICODE "(unicode conversion error)" /* Explicitly check for float arguments when integers are expected. Return 1 for error, 0 if ok. */ static int float_argument_error(PyObject *arg) { if (PyFloat_Check(arg)) { PyErr_SetString(PyExc_TypeError, "integer argument expected, got float" ); return 1; } else return 0; } /* Convert a non-tuple argument. Return NULL if conversion went OK, or a string with a message describing the failure. The message is formatted as "must be <desired type>, not <actual type>". When failing, an exception may or may not have been raised. Don't call if a tuple is expected. When you add new format codes, please don't forget poor skipitem() below. */ static char * convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, char *msgbuf, size_t bufsize, freelist_t *freelist) { /* For # codes */ #define FETCH_SIZE int *q=NULL;Py_ssize_t *q2=NULL;\ if (flags & FLAG_SIZE_T) q2=va_arg(*p_va, Py_ssize_t*); \ else q=va_arg(*p_va, int*); #define STORE_SIZE(s) \ if (flags & FLAG_SIZE_T) \ *q2=s; \ else { \ if (INT_MAX < s) { \ PyErr_SetString(PyExc_OverflowError, \ "size does not fit in an int"); \ return converterr("", arg, msgbuf, bufsize); \ } \ *q = (int)s; \ } #define BUFFER_LEN ((flags & FLAG_SIZE_T) ? *q2:*q) #define RETURN_ERR_OCCURRED return msgbuf const char *format = *p_format; char c = *format++; char *sarg; switch (c) { case 'b': { /* unsigned byte -- very short int */ char *p = va_arg(*p_va, char *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsLong(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else if (ival < 0) { PyErr_SetString(PyExc_OverflowError, "unsigned byte integer is less than minimum"); RETURN_ERR_OCCURRED; } else if (ival > UCHAR_MAX) { PyErr_SetString(PyExc_OverflowError, "unsigned byte integer is greater than maximum"); RETURN_ERR_OCCURRED; } else *p = (unsigned char) ival; break; } case 'B': {/* byte sized bitfield - both signed and unsigned values allowed */ char *p = va_arg(*p_va, char *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsUnsignedLongMask(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = (unsigned char) ival; break; } case 'h': {/* signed short int */ short *p = va_arg(*p_va, short *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsLong(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else if (ival < SHRT_MIN) { PyErr_SetString(PyExc_OverflowError, "signed short integer is less than minimum"); RETURN_ERR_OCCURRED; } else if (ival > SHRT_MAX) { PyErr_SetString(PyExc_OverflowError, "signed short integer is greater than maximum"); RETURN_ERR_OCCURRED; } else *p = (short) ival; break; } case 'H': { /* short int sized bitfield, both signed and unsigned allowed */ unsigned short *p = va_arg(*p_va, unsigned short *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsUnsignedLongMask(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = (unsigned short) ival; break; } case 'i': {/* signed int */ int *p = va_arg(*p_va, int *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsLong(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else if (ival > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "signed integer is greater than maximum"); RETURN_ERR_OCCURRED; } else if (ival < INT_MIN) { PyErr_SetString(PyExc_OverflowError, "signed integer is less than minimum"); RETURN_ERR_OCCURRED; } else *p = ival; break; } case 'I': { /* int sized bitfield, both signed and unsigned allowed */ unsigned int *p = va_arg(*p_va, unsigned int *); unsigned int ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = (unsigned int)PyLong_AsUnsignedLongMask(arg); if (ival == (unsigned int)-1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = ival; break; } case 'n': /* Py_ssize_t */ { PyObject *iobj; Py_ssize_t *p = va_arg(*p_va, Py_ssize_t *); Py_ssize_t ival = -1; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; iobj = PyNumber_Index(arg); if (iobj != NULL) { ival = PyLong_AsSsize_t(iobj); Py_DECREF(iobj); } if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; *p = ival; break; } case 'l': {/* long int */ long *p = va_arg(*p_va, long *); long ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsLong(arg); if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = ival; break; } case 'k': { /* long sized bitfield */ unsigned long *p = va_arg(*p_va, unsigned long *); unsigned long ival; if (PyLong_Check(arg)) ival = PyLong_AsUnsignedLongMask(arg); else return converterr("integer<k>", arg, msgbuf, bufsize); *p = ival; break; } #ifdef HAVE_LONG_LONG case 'L': {/* PY_LONG_LONG */ PY_LONG_LONG *p = va_arg( *p_va, PY_LONG_LONG * ); PY_LONG_LONG ival; if (float_argument_error(arg)) RETURN_ERR_OCCURRED; ival = PyLong_AsLongLong(arg); if (ival == (PY_LONG_LONG)-1 && PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = ival; break; } case 'K': { /* long long sized bitfield */ unsigned PY_LONG_LONG *p = va_arg(*p_va, unsigned PY_LONG_LONG *); unsigned PY_LONG_LONG ival; if (PyLong_Check(arg)) ival = PyLong_AsUnsignedLongLongMask(arg); else return converterr("integer<K>", arg, msgbuf, bufsize); *p = ival; break; } #endif case 'f': {/* float */ float *p = va_arg(*p_va, float *); double dval = PyFloat_AsDouble(arg); if (PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = (float) dval; break; } case 'd': {/* double */ double *p = va_arg(*p_va, double *); double dval = PyFloat_AsDouble(arg); if (PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = dval; break; } case 'D': {/* complex double */ Py_complex *p = va_arg(*p_va, Py_complex *); Py_complex cval; cval = PyComplex_AsCComplex(arg); if (PyErr_Occurred()) RETURN_ERR_OCCURRED; else *p = cval; break; } case 'c': {/* char */ char *p = va_arg(*p_va, char *); if (PyBytes_Check(arg) && PyBytes_Size(arg) == 1) *p = PyBytes_AS_STRING(arg)[0]; else if (PyByteArray_Check(arg) && PyByteArray_Size(arg) == 1) *p = PyByteArray_AS_STRING(arg)[0]; else return converterr("a byte string of length 1", arg, msgbuf, bufsize); break; } case 'C': {/* unicode char */ int *p = va_arg(*p_va, int *); int kind; void *data; if (!PyUnicode_Check(arg)) return converterr("a unicode character", arg, msgbuf, bufsize); if (PyUnicode_READY(arg)) RETURN_ERR_OCCURRED; if (PyUnicode_GET_LENGTH(arg) != 1) return converterr("a unicode character", arg, msgbuf, bufsize); kind = PyUnicode_KIND(arg); data = PyUnicode_DATA(arg); *p = PyUnicode_READ(kind, data, 0); break; } case 'p': {/* boolean *p*redicate */ int *p = va_arg(*p_va, int *); int val = PyObject_IsTrue(arg); if (val > 0) *p = 1; else if (val == 0) *p = 0; else RETURN_ERR_OCCURRED; break; } /* XXX WAAAAH! 's', 'y', 'z', 'u', 'Z', 'e', 'w' codes all need to be cleaned up! */ case 'y': {/* any bytes-like object */ void **p = (void **)va_arg(*p_va, char **); char *buf; Py_ssize_t count; if (*format == '*') { if (getbuffer(arg, (Py_buffer*)p, &buf) < 0) return converterr(buf, arg, msgbuf, bufsize); format++; if (addcleanup(p, freelist, cleanup_buffer)) { return converterr( "(cleanup problem)", arg, msgbuf, bufsize); } break; } count = convertbuffer(arg, p, &buf); if (count < 0) return converterr(buf, arg, msgbuf, bufsize); if (*format == '#') { FETCH_SIZE; STORE_SIZE(count); format++; } else { if (strlen(*p) != (size_t)count) { PyErr_SetString(PyExc_ValueError, "embedded null byte"); RETURN_ERR_OCCURRED; } } break; } case 's': /* text string or bytes-like object */ case 'z': /* text string, bytes-like object or None */ { if (*format == '*') { /* "s*" or "z*" */ Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *); if (c == 'z' && arg == Py_None) PyBuffer_FillInfo(p, NULL, NULL, 0, 1, 0); else if (PyUnicode_Check(arg)) { Py_ssize_t len; sarg = PyUnicode_AsUTF8AndSize(arg, &len); if (sarg == NULL) return converterr(CONV_UNICODE, arg, msgbuf, bufsize); PyBuffer_FillInfo(p, arg, sarg, len, 1, 0); } else { /* any bytes-like object */ char *buf; if (getbuffer(arg, p, &buf) < 0) return converterr(buf, arg, msgbuf, bufsize); } if (addcleanup(p, freelist, cleanup_buffer)) { return converterr( "(cleanup problem)", arg, msgbuf, bufsize); } format++; } else if (*format == '#') { /* a string or read-only bytes-like object */ /* "s#" or "z#" */ void **p = (void **)va_arg(*p_va, char **); FETCH_SIZE; if (c == 'z' && arg == Py_None) { *p = NULL; STORE_SIZE(0); } else if (PyUnicode_Check(arg)) { Py_ssize_t len; sarg = PyUnicode_AsUTF8AndSize(arg, &len); if (sarg == NULL) return converterr(CONV_UNICODE, arg, msgbuf, bufsize); *p = sarg; STORE_SIZE(len); } else { /* read-only bytes-like object */ /* XXX Really? */ char *buf; Py_ssize_t count = convertbuffer(arg, p, &buf); if (count < 0) return converterr(buf, arg, msgbuf, bufsize); STORE_SIZE(count); } format++; } else { /* "s" or "z" */ char **p = va_arg(*p_va, char **); Py_ssize_t len; sarg = NULL; if (c == 'z' && arg == Py_None) *p = NULL; else if (PyUnicode_Check(arg)) { sarg = PyUnicode_AsUTF8AndSize(arg, &len); if (sarg == NULL) return converterr(CONV_UNICODE, arg, msgbuf, bufsize); if (strlen(sarg) != (size_t)len) { PyErr_SetString(PyExc_ValueError, "embedded null character"); RETURN_ERR_OCCURRED; } *p = sarg; } else return converterr(c == 'z' ? "str or None" : "str", arg, msgbuf, bufsize); } break; } case 'u': /* raw unicode buffer (Py_UNICODE *) */ case 'Z': /* raw unicode buffer or None */ { Py_UNICODE **p = va_arg(*p_va, Py_UNICODE **); if (*format == '#') { /* "u#" or "Z#" */ FETCH_SIZE; if (c == 'Z' && arg == Py_None) { *p = NULL; STORE_SIZE(0); } else if (PyUnicode_Check(arg)) { Py_ssize_t len; *p = PyUnicode_AsUnicodeAndSize(arg, &len); if (*p == NULL) RETURN_ERR_OCCURRED; STORE_SIZE(len); } else return converterr(c == 'Z' ? "str or None" : "str", arg, msgbuf, bufsize); format++; } else { /* "u" or "Z" */ if (c == 'Z' && arg == Py_None) *p = NULL; else if (PyUnicode_Check(arg)) { Py_ssize_t len; *p = PyUnicode_AsUnicodeAndSize(arg, &len); if (*p == NULL) RETURN_ERR_OCCURRED; if (Py_UNICODE_strlen(*p) != (size_t)len) { PyErr_SetString(PyExc_ValueError, "embedded null character"); RETURN_ERR_OCCURRED; } } else return converterr(c == 'Z' ? "str or None" : "str", arg, msgbuf, bufsize); } break; } case 'e': {/* encoded string */ char **buffer; const char *encoding; PyObject *s; int recode_strings; Py_ssize_t size; const char *ptr; /* Get 'e' parameter: the encoding name */ encoding = (const char *)va_arg(*p_va, const char *); if (encoding == NULL) encoding = PyUnicode_GetDefaultEncoding(); /* Get output buffer parameter: 's' (recode all objects via Unicode) or 't' (only recode non-string objects) */ if (*format == 's') recode_strings = 1; else if (*format == 't') recode_strings = 0; else return converterr( "(unknown parser marker combination)", arg, msgbuf, bufsize); buffer = (char **)va_arg(*p_va, char **); format++; if (buffer == NULL) return converterr("(buffer is NULL)", arg, msgbuf, bufsize); /* Encode object */ if (!recode_strings && (PyBytes_Check(arg) || PyByteArray_Check(arg))) { s = arg; Py_INCREF(s); if (PyObject_AsCharBuffer(s, &ptr, &size) < 0) return converterr("(AsCharBuffer failed)", arg, msgbuf, bufsize); } else { PyObject *u; /* Convert object to Unicode */ u = PyUnicode_FromObject(arg); if (u == NULL) return converterr( "string or unicode or text buffer", arg, msgbuf, bufsize); /* Encode object; use default error handling */ s = PyUnicode_AsEncodedString(u, encoding, NULL); Py_DECREF(u); if (s == NULL) return converterr("(encoding failed)", arg, msgbuf, bufsize); if (!PyBytes_Check(s)) { Py_DECREF(s); return converterr( "(encoder failed to return bytes)", arg, msgbuf, bufsize); } size = PyBytes_GET_SIZE(s); ptr = PyBytes_AS_STRING(s); if (ptr == NULL) ptr = ""; } /* Write output; output is guaranteed to be 0-terminated */ if (*format == '#') { /* Using buffer length parameter '#': - if *buffer is NULL, a new buffer of the needed size is allocated and the data copied into it; *buffer is updated to point to the new buffer; the caller is responsible for PyMem_Free()ing it after usage - if *buffer is not NULL, the data is copied to *buffer; *buffer_len has to be set to the size of the buffer on input; buffer overflow is signalled with an error; buffer has to provide enough room for the encoded string plus the trailing 0-byte - in both cases, *buffer_len is updated to the size of the buffer /excluding/ the trailing 0-byte */ FETCH_SIZE; format++; if (q == NULL && q2 == NULL) { Py_DECREF(s); return converterr( "(buffer_len is NULL)", arg, msgbuf, bufsize); } if (*buffer == NULL) { *buffer = PyMem_NEW(char, size + 1); if (*buffer == NULL) { Py_DECREF(s); PyErr_NoMemory(); RETURN_ERR_OCCURRED; } if (addcleanup(*buffer, freelist, cleanup_ptr)) { Py_DECREF(s); return converterr( "(cleanup problem)", arg, msgbuf, bufsize); } } else { if (size + 1 > BUFFER_LEN) { Py_DECREF(s); return converterr( "(buffer overflow)", arg, msgbuf, bufsize); } } memcpy(*buffer, ptr, size+1); STORE_SIZE(size); } else { /* Using a 0-terminated buffer: - the encoded string has to be 0-terminated for this variant to work; if it is not, an error raised - a new buffer of the needed size is allocated and the data copied into it; *buffer is updated to point to the new buffer; the caller is responsible for PyMem_Free()ing it after usage */ if ((Py_ssize_t)strlen(ptr) != size) { Py_DECREF(s); return converterr( "encoded string without NULL bytes", arg, msgbuf, bufsize); } *buffer = PyMem_NEW(char, size + 1); if (*buffer == NULL) { Py_DECREF(s); PyErr_NoMemory(); RETURN_ERR_OCCURRED; } if (addcleanup(*buffer, freelist, cleanup_ptr)) { Py_DECREF(s); return converterr("(cleanup problem)", arg, msgbuf, bufsize); } memcpy(*buffer, ptr, size+1); } Py_DECREF(s); break; } case 'S': { /* PyBytes object */ PyObject **p = va_arg(*p_va, PyObject **); if (PyBytes_Check(arg)) *p = arg; else return converterr("bytes", arg, msgbuf, bufsize); break; } case 'Y': { /* PyByteArray object */ PyObject **p = va_arg(*p_va, PyObject **); if (PyByteArray_Check(arg)) *p = arg; else return converterr("bytearray", arg, msgbuf, bufsize); break; } case 'U': { /* PyUnicode object */ PyObject **p = va_arg(*p_va, PyObject **); if (PyUnicode_Check(arg)) { if (PyUnicode_READY(arg) == -1) RETURN_ERR_OCCURRED; *p = arg; } else return converterr("str", arg, msgbuf, bufsize); break; } case 'O': { /* object */ PyTypeObject *type; PyObject **p; if (*format == '!') { type = va_arg(*p_va, PyTypeObject*); p = va_arg(*p_va, PyObject **); format++; if (PyType_IsSubtype(arg->ob_type, type)) *p = arg; else return converterr(type->tp_name, arg, msgbuf, bufsize); } else if (*format == '&') { typedef int (*converter)(PyObject *, void *); converter convert = va_arg(*p_va, converter); void *addr = va_arg(*p_va, void *); int res; format++; if (! (res = (*convert)(arg, addr))) return converterr("(unspecified)", arg, msgbuf, bufsize); if (res == Py_CLEANUP_SUPPORTED && addcleanup(addr, freelist, convert) == -1) return converterr("(cleanup problem)", arg, msgbuf, bufsize); } else { p = va_arg(*p_va, PyObject **); *p = arg; } break; } case 'w': { /* "w*": memory buffer, read-write access */ void **p = va_arg(*p_va, void **); if (*format != '*') return converterr( "invalid use of 'w' format character", arg, msgbuf, bufsize); format++; /* Caller is interested in Py_buffer, and the object supports it directly. */ if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) { PyErr_Clear(); return converterr("read-write bytes-like object", arg, msgbuf, bufsize); } if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C')) { PyBuffer_Release((Py_buffer*)p); return converterr("contiguous buffer", arg, msgbuf, bufsize); } if (addcleanup(p, freelist, cleanup_buffer)) { return converterr( "(cleanup problem)", arg, msgbuf, bufsize); } break; } default: return converterr("impossible<bad format char>", arg, msgbuf, bufsize); } *p_format = format; return NULL; #undef FETCH_SIZE #undef STORE_SIZE #undef BUFFER_LEN #undef RETURN_ERR_OCCURRED } static Py_ssize_t convertbuffer(PyObject *arg, void **p, char **errmsg) { PyBufferProcs *pb = Py_TYPE(arg)->tp_as_buffer; Py_ssize_t count; Py_buffer view; *errmsg = NULL; *p = NULL; if (pb != NULL && pb->bf_releasebuffer != NULL) { *errmsg = "read-only bytes-like object"; return -1; } if (getbuffer(arg, &view, errmsg) < 0) return -1; count = view.len; *p = view.buf; PyBuffer_Release(&view); return count; } static int getbuffer(PyObject *arg, Py_buffer *view, char **errmsg) { if (PyObject_GetBuffer(arg, view, PyBUF_SIMPLE) != 0) { *errmsg = "bytes-like object"; return -1; } if (!PyBuffer_IsContiguous(view, 'C')) { PyBuffer_Release(view); *errmsg = "contiguous buffer"; return -1; } return 0; } /* Support for keyword arguments donated by Geoff Philbrick <philbric@delphi.hks.com> */ /* Return false (0) for error, else true. */ int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, char **kwlist, ...) { int retval; va_list va; if ((args == NULL || !PyTuple_Check(args)) || (keywords != NULL && !PyDict_Check(keywords)) || format == NULL || kwlist == NULL) { PyErr_BadInternalCall(); return 0; } va_start(va, kwlist); retval = vgetargskeywords(args, keywords, format, kwlist, &va, 0); va_end(va); return retval; } int _PyArg_ParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, char **kwlist, ...) { int retval; va_list va; if ((args == NULL || !PyTuple_Check(args)) || (keywords != NULL && !PyDict_Check(keywords)) || format == NULL || kwlist == NULL) { PyErr_BadInternalCall(); return 0; } va_start(va, kwlist); retval = vgetargskeywords(args, keywords, format, kwlist, &va, FLAG_SIZE_T); va_end(va); return retval; } int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *keywords, const char *format, char **kwlist, va_list va) { int retval; va_list lva; if ((args == NULL || !PyTuple_Check(args)) || (keywords != NULL && !PyDict_Check(keywords)) || format == NULL || kwlist == NULL) { PyErr_BadInternalCall(); return 0; } Py_VA_COPY(lva, va); retval = vgetargskeywords(args, keywords, format, kwlist, &lva, 0); return retval; } int _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args, PyObject *keywords, const char *format, char **kwlist, va_list va) { int retval; va_list lva; if ((args == NULL || !PyTuple_Check(args)) || (keywords != NULL && !PyDict_Check(keywords)) || format == NULL || kwlist == NULL) { PyErr_BadInternalCall(); return 0; } Py_VA_COPY(lva, va); retval = vgetargskeywords(args, keywords, format, kwlist, &lva, FLAG_SIZE_T); return retval; } int PyArg_ValidateKeywordArguments(PyObject *kwargs) { if (!PyDict_Check(kwargs)) { PyErr_BadInternalCall(); return 0; } if (!_PyDict_HasOnlyStringKeys(kwargs)) { PyErr_SetString(PyExc_TypeError, "keyword arguments must be strings"); return 0; } return 1; } #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':') static int vgetargskeywords(PyObject *args, PyObject *keywords, const char *format, char **kwlist, va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; const char *fname, *msg, *custom_msg, *keyword; int min = INT_MAX; int max = INT_MAX; int i, len; Py_ssize_t nargs, nkeywords; PyObject *current_arg; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; freelist.entries = static_entries; freelist.first_available = 0; freelist.entries_malloced = 0; assert(args != NULL && PyTuple_Check(args)); assert(keywords == NULL || PyDict_Check(keywords)); assert(format != NULL); assert(kwlist != NULL); assert(p_va != NULL); /* grab the function name or custom error msg first (mutually exclusive) */ fname = strchr(format, ':'); if (fname) { fname++; custom_msg = NULL; } else { custom_msg = strchr(format,';'); if (custom_msg) custom_msg++; } /* scan kwlist and get greatest possible nbr of args */ for (len=0; kwlist[len]; len++) continue; if (len > STATIC_FREELIST_ENTRIES) { freelist.entries = PyMem_NEW(freelistentry_t, len); if (freelist.entries == NULL) { PyErr_NoMemory(); return 0; } freelist.entries_malloced = 1; } nargs = PyTuple_GET_SIZE(args); nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords); if (nargs + nkeywords > len) { PyErr_Format(PyExc_TypeError, "%s%s takes at most %d argument%s (%zd given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", len, (len == 1) ? "" : "s", nargs + nkeywords); return cleanreturn(0, &freelist); } /* convert tuple args and keyword args in same loop, using kwlist to drive process */ for (i = 0; i < len; i++) { keyword = kwlist[i]; if (*format == '|') { if (min != INT_MAX) { PyErr_SetString(PyExc_RuntimeError, "Invalid format string (| specified twice)"); return cleanreturn(0, &freelist); } min = i; format++; if (max != INT_MAX) { PyErr_SetString(PyExc_RuntimeError, "Invalid format string ($ before |)"); return cleanreturn(0, &freelist); } } if (*format == '$') { if (max != INT_MAX) { PyErr_SetString(PyExc_RuntimeError, "Invalid format string ($ specified twice)"); return cleanreturn(0, &freelist); } max = i; format++; if (max < nargs) { PyErr_Format(PyExc_TypeError, "Function takes %s %d positional arguments" " (%d given)", (min != INT_MAX) ? "at most" : "exactly", max, nargs); return cleanreturn(0, &freelist); } } if (IS_END_OF_FORMAT(*format)) { PyErr_Format(PyExc_RuntimeError, "More keyword list entries (%d) than " "format specifiers (%d)", len, i); return cleanreturn(0, &freelist); } current_arg = NULL; if (nkeywords) { current_arg = PyDict_GetItemString(keywords, keyword); } if (current_arg) { --nkeywords; if (i < nargs) { /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "Argument given by name ('%s') " "and position (%d)", keyword, i+1); return cleanreturn(0, &freelist); } } else if (nkeywords && PyErr_Occurred()) return cleanreturn(0, &freelist); else if (i < nargs) current_arg = PyTuple_GET_ITEM(args, i); if (current_arg) { msg = convertitem(current_arg, &format, p_va, flags, levels, msgbuf, sizeof(msgbuf), &freelist); if (msg) { seterror(i+1, msg, levels, fname, custom_msg); return cleanreturn(0, &freelist); } continue; } if (i < min) { PyErr_Format(PyExc_TypeError, "Required argument " "'%s' (pos %d) not found", keyword, i+1); return cleanreturn(0, &freelist); } /* current code reports success when all required args * fulfilled and no keyword args left, with no further * validation. XXX Maybe skip this in debug build ? */ if (!nkeywords) return cleanreturn(1, &freelist); /* We are into optional args, skip thru to any remaining * keyword args */ msg = skipitem(&format, p_va, flags); if (msg) { PyErr_Format(PyExc_RuntimeError, "%s: '%s'", msg, format); return cleanreturn(0, &freelist); } } if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) { PyErr_Format(PyExc_RuntimeError, "more argument specifiers than keyword list entries " "(remaining format:'%s')", format); return cleanreturn(0, &freelist); } /* make sure there are no extraneous keyword arguments */ if (nkeywords > 0) { PyObject *key, *value; Py_ssize_t pos = 0; while (PyDict_Next(keywords, &pos, &key, &value)) { int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, "keywords must be strings"); return cleanreturn(0, &freelist); } for (i = 0; i < len; i++) { if (!PyUnicode_CompareWithASCIIString(key, kwlist[i])) { match = 1; break; } } if (!match) { PyErr_Format(PyExc_TypeError, "'%U' is an invalid keyword " "argument for this function", key); return cleanreturn(0, &freelist); } } } return cleanreturn(1, &freelist); } static char * skipitem(const char **p_format, va_list *p_va, int flags) { const char *format = *p_format; char c = *format++; switch (c) { /* * codes that take a single data pointer as an argument * (the type of the pointer is irrelevant) */ case 'b': /* byte -- very short int */ case 'B': /* byte as bitfield */ case 'h': /* short int */ case 'H': /* short int as bitfield */ case 'i': /* int */ case 'I': /* int sized bitfield */ case 'l': /* long int */ case 'k': /* long int sized bitfield */ #ifdef HAVE_LONG_LONG case 'L': /* PY_LONG_LONG */ case 'K': /* PY_LONG_LONG sized bitfield */ #endif case 'n': /* Py_ssize_t */ case 'f': /* float */ case 'd': /* double */ case 'D': /* complex double */ case 'c': /* char */ case 'C': /* unicode char */ case 'p': /* boolean predicate */ case 'S': /* string object */ case 'Y': /* string object */ case 'U': /* unicode string object */ { (void) va_arg(*p_va, void *); break; } /* string codes */ case 'e': /* string with encoding */ { (void) va_arg(*p_va, const char *); if (!(*format == 's' || *format == 't')) /* after 'e', only 's' and 't' is allowed */ goto err; format++; /* explicit fallthrough to string cases */ } case 's': /* string */ case 'z': /* string or None */ case 'y': /* bytes */ case 'u': /* unicode string */ case 'Z': /* unicode string or None */ case 'w': /* buffer, read-write */ { (void) va_arg(*p_va, char **); if (*format == '#') { if (flags & FLAG_SIZE_T) (void) va_arg(*p_va, Py_ssize_t *); else (void) va_arg(*p_va, int *); format++; } else if ((c == 's' || c == 'z' || c == 'y') && *format == '*') { format++; } break; } case 'O': /* object */ { if (*format == '!') { format++; (void) va_arg(*p_va, PyTypeObject*); (void) va_arg(*p_va, PyObject **); } else if (*format == '&') { typedef int (*converter)(PyObject *, void *); (void) va_arg(*p_va, converter); (void) va_arg(*p_va, void *); format++; } else { (void) va_arg(*p_va, PyObject **); } break; } case '(': /* bypass tuple, not handled at all previously */ { char *msg; for (;;) { if (*format==')') break; if (IS_END_OF_FORMAT(*format)) return "Unmatched left paren in format " "string"; msg = skipitem(&format, p_va, flags); if (msg) return msg; } format++; break; } case ')': return "Unmatched right paren in format string"; default: err: return "impossible<bad format char>"; } *p_format = format; return NULL; } int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) { Py_ssize_t i, l; PyObject **o; va_list vargs; #ifdef HAVE_STDARG_PROTOTYPES va_start(vargs, max); #else va_start(vargs); #endif assert(min >= 0); assert(min <= max); if (!PyTuple_Check(args)) { va_end(vargs); PyErr_SetString(PyExc_SystemError, "PyArg_UnpackTuple() argument list is not a tuple"); return 0; } l = PyTuple_GET_SIZE(args); if (l < min) { if (name != NULL) PyErr_Format( PyExc_TypeError, "%s expected %s%zd arguments, got %zd", name, (min == max ? "" : "at least "), min, l); else PyErr_Format( PyExc_TypeError, "unpacked tuple should have %s%zd elements," " but has %zd", (min == max ? "" : "at least "), min, l); va_end(vargs); return 0; } if (l > max) { if (name != NULL) PyErr_Format( PyExc_TypeError, "%s expected %s%zd arguments, got %zd", name, (min == max ? "" : "at most "), max, l); else PyErr_Format( PyExc_TypeError, "unpacked tuple should have %s%zd elements," " but has %zd", (min == max ? "" : "at most "), max, l); va_end(vargs); return 0; } for (i = 0; i < l; i++) { o = va_arg(vargs, PyObject **); *o = PyTuple_GET_ITEM(args, i); } va_end(vargs); return 1; } /* For type constructors that don't take keyword args * * Sets a TypeError and returns 0 if the args/kwargs is * not empty, returns 1 otherwise */ int _PyArg_NoKeywords(const char *funcname, PyObject *kw) { if (kw == NULL) return 1; if (!PyDict_CheckExact(kw)) { PyErr_BadInternalCall(); return 0; } if (PyDict_Size(kw) == 0) return 1; PyErr_Format(PyExc_TypeError, "%s does not take keyword arguments", funcname); return 0; } int _PyArg_NoPositional(const char *funcname, PyObject *args) { if (args == NULL) return 1; if (!PyTuple_CheckExact(args)) { PyErr_BadInternalCall(); return 0; } if (PyTuple_GET_SIZE(args) == 0) return 1; PyErr_Format(PyExc_TypeError, "%s does not take positional arguments", funcname); return 0; } #ifdef __cplusplus }; #endif