collections.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. #include "pocketpy/collections.h"
  2. namespace pkpy
  3. {
  4. struct PyDequeIter // Iterator for the deque type
  5. {
  6. PyObject *ref;
  7. bool is_reversed;
  8. std::deque<PyObject *>::iterator begin, end, current;
  9. std::deque<PyObject *>::reverse_iterator rbegin, rend, rcurrent;
  10. PyDequeIter(PyObject *ref, std::deque<PyObject *>::iterator begin, std::deque<PyObject *>::iterator end)
  11. : ref(ref), begin(begin), end(end), current(begin)
  12. {
  13. this->is_reversed = false;
  14. }
  15. PyDequeIter(PyObject *ref, std::deque<PyObject *>::reverse_iterator rbegin, std::deque<PyObject *>::reverse_iterator rend)
  16. : ref(ref), rbegin(rbegin), rend(rend), rcurrent(rbegin)
  17. {
  18. this->is_reversed = true;
  19. }
  20. void _gc_mark() const { PK_OBJ_MARK(ref); }
  21. static void _register(VM *vm, PyObject *mod, PyObject *type);
  22. };
  23. void PyDequeIter::_register(VM *vm, PyObject *mod, PyObject *type)
  24. {
  25. // Iterator for the deque type
  26. vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
  27. vm->bind_notimplemented_constructor<PyDequeIter>(type);
  28. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj)
  29. { return obj; });
  30. vm->bind__next__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj) -> unsigned
  31. {
  32. PyDequeIter& self = _CAST(PyDequeIter&, obj);
  33. if(self.is_reversed){
  34. if(self.rcurrent == self.rend) return 0;
  35. vm->s_data.push(*self.rcurrent);
  36. ++self.rcurrent;
  37. return 1;
  38. }
  39. else{
  40. if(self.current == self.end) return 0;
  41. vm->s_data.push(*self.current);
  42. ++self.current;
  43. return 1;
  44. } });
  45. }
  46. struct PyDeque
  47. {
  48. PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen); // constructor
  49. // PyDeque members
  50. std::deque<PyObject *> dequeItems;
  51. int maxlen = -1; // -1 means unbounded
  52. bool bounded = false; // if true, maxlen is not -1
  53. void insertObj(bool front, bool back, int index, PyObject *item); // insert at index, used purely for internal purposes: append, appendleft, insert methods
  54. PyObject *popObj(bool front, bool back, PyObject *item, VM *vm); // pop at index, used purely for internal purposes: pop, popleft, remove methods
  55. int findIndex(VM *vm, PyObject *obj, int start, int stop); // find the index of the given object in the deque
  56. // Special methods
  57. static void _register(VM *vm, PyObject *mod, PyObject *type); // register the type
  58. void _gc_mark() const; // needed for container types, mark all objects in the deque for gc
  59. };
  60. void PyDeque::_register(VM *vm, PyObject *mod, PyObject *type)
  61. {
  62. vm->bind(type, "__new__(cls, iterable=None, maxlen=None)",
  63. [](VM *vm, ArgsView args)
  64. {
  65. Type cls_t = PK_OBJ_GET(Type, args[0]);
  66. PyObject *iterable = args[1];
  67. PyObject *maxlen = args[2];
  68. return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen);
  69. });
  70. // gets the item at the given index, if index is negative, it will be treated as index + len(deque)
  71. // if the index is out of range, IndexError will be thrown --> required for [] operator
  72. vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  73. {
  74. PyDeque &self = _CAST(PyDeque &, _0);
  75. i64 index = CAST(i64, _1);
  76. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  77. return self.dequeItems[index];
  78. });
  79. // sets the item at the given index, if index is negative, it will be treated as index + len(deque)
  80. // if the index is out of range, IndexError will be thrown --> required for [] operator
  81. vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1, PyObject* _2)
  82. {
  83. PyDeque &self = _CAST(PyDeque&, _0);
  84. i64 index = CAST(i64, _1);
  85. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  86. self.dequeItems[index] = _2;
  87. });
  88. // erases the item at the given index, if index is negative, it will be treated as index + len(deque)
  89. // if the index is out of range, IndexError will be thrown --> required for [] operator
  90. vm->bind__delitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  91. {
  92. PyDeque &self = _CAST(PyDeque&, _0);
  93. i64 index = CAST(i64, _1);
  94. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  95. self.dequeItems.erase(self.dequeItems.begin() + index);
  96. });
  97. vm->bind__len__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  98. {
  99. PyDeque &self = _CAST(PyDeque&, _0);
  100. return (i64)self.dequeItems.size();
  101. });
  102. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  103. {
  104. PyDeque &self = _CAST(PyDeque &, _0);
  105. return vm->new_user_object<PyDequeIter>(_0, self.dequeItems.begin(), self.dequeItems.end());
  106. });
  107. vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  108. {
  109. if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
  110. const PyDeque &self = _CAST(PyDeque&, _0);
  111. SStream ss;
  112. ss << "deque([";
  113. vm->_repr_recursion_set.insert(_0);
  114. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  115. {
  116. ss << CAST(Str&, vm->py_repr(*it));
  117. if (it != self.dequeItems.end() - 1) ss << ", ";
  118. }
  119. vm->_repr_recursion_set.erase(_0);
  120. self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
  121. return VAR(ss.str());
  122. });
  123. // enables comparison between two deques, == and != are supported
  124. vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  125. {
  126. const PyDeque &self = _CAST(PyDeque&, _0);
  127. if(!vm->is_user_type<PyDeque>(_0)) return vm->NotImplemented;
  128. const PyDeque &other = _CAST(PyDeque&, _1);
  129. if (self.dequeItems.size() != other.dequeItems.size()) return vm->False;
  130. for (int i = 0; i < self.dequeItems.size(); i++){
  131. if (vm->py_ne(self.dequeItems[i], other.dequeItems[i])) return vm->False;
  132. }
  133. return vm->True;
  134. });
  135. // clear the deque
  136. vm->bind(type, "clear(self) -> None",
  137. [](VM *vm, ArgsView args)
  138. {
  139. PyDeque &self = _CAST(PyDeque &, args[0]);
  140. self.dequeItems.clear();
  141. return vm->None;
  142. });
  143. // extend the deque with the given iterable
  144. vm->bind(type, "extend(self, iterable) -> None",
  145. [](VM *vm, ArgsView args)
  146. {
  147. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  148. PyDeque &self = _CAST(PyDeque &, args[0]);
  149. PyObject *it = vm->py_iter(args[1]); // strong ref
  150. PyObject *obj = vm->py_next(it);
  151. while (obj != vm->StopIteration)
  152. {
  153. self.insertObj(false, true, -1, obj);
  154. obj = vm->py_next(it);
  155. }
  156. return vm->None;
  157. });
  158. // append at the end of the deque
  159. vm->bind(type, "append(self, item) -> None",
  160. [](VM *vm, ArgsView args)
  161. {
  162. PyDeque &self = _CAST(PyDeque &, args[0]);
  163. PyObject *item = args[1];
  164. self.insertObj(false, true, -1, item);
  165. return vm->None;
  166. });
  167. // append at the beginning of the deque
  168. vm->bind(type, "appendleft(self, item) -> None",
  169. [](VM *vm, ArgsView args)
  170. {
  171. PyDeque &self = _CAST(PyDeque &, args[0]);
  172. PyObject *item = args[1];
  173. self.insertObj(true, false, -1, item);
  174. return vm->None;
  175. });
  176. // pop from the end of the deque
  177. vm->bind(type, "pop(self) -> PyObject",
  178. [](VM *vm, ArgsView args)
  179. {
  180. PyDeque &self = _CAST(PyDeque &, args[0]);
  181. if (self.dequeItems.empty())
  182. {
  183. vm->IndexError("pop from an empty deque");
  184. return vm->None;
  185. }
  186. return self.popObj(false, true, nullptr, vm);
  187. });
  188. // pop from the beginning of the deque
  189. vm->bind(type, "popleft(self) -> PyObject",
  190. [](VM *vm, ArgsView args)
  191. {
  192. PyDeque &self = _CAST(PyDeque &, args[0]);
  193. if (self.dequeItems.empty())
  194. {
  195. vm->IndexError("pop from an empty deque");
  196. return vm->None;
  197. }
  198. return self.popObj(true, false, nullptr, vm);
  199. });
  200. // shallow copy of the deque
  201. vm->bind(type, "copy(self) -> deque",
  202. [](VM *vm, ArgsView args)
  203. {
  204. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  205. PyDeque &self = _CAST(PyDeque &, args[0]);
  206. PyObject *newDequeObj = vm->new_user_object<PyDeque>(vm, vm->None, vm->None); // create the empty deque
  207. PyDeque &newDeque = _CAST(PyDeque &, newDequeObj); // cast it to PyDeque so we can use its methods
  208. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  209. newDeque.insertObj(false, true, -1, *it);
  210. return newDequeObj;
  211. });
  212. // NEW: counts the number of occurrences of the given object in the deque
  213. vm->bind(type, "count(self, obj) -> int",
  214. [](VM *vm, ArgsView args)
  215. {
  216. PyDeque &self = _CAST(PyDeque &, args[0]);
  217. PyObject *obj = args[1];
  218. int cnt = 0, sz = self.dequeItems.size();
  219. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  220. {
  221. if (vm->py_eq((*it), obj))
  222. cnt++;
  223. if (sz != self.dequeItems.size())// mutating the deque during iteration is not allowed
  224. vm->RuntimeError("deque mutated during iteration");
  225. }
  226. return VAR(cnt);
  227. });
  228. // NEW: extends the deque from the left
  229. vm->bind(type, "extendleft(self, iterable) -> None",
  230. [](VM *vm, ArgsView args)
  231. {
  232. auto _lock = vm->heap.gc_scope_lock();
  233. PyDeque &self = _CAST(PyDeque &, args[0]);
  234. PyObject *it = vm->py_iter(args[1]); // strong ref
  235. PyObject *obj = vm->py_next(it);
  236. while (obj != vm->StopIteration)
  237. {
  238. self.insertObj(true, false, -1, obj);
  239. obj = vm->py_next(it);
  240. }
  241. return vm->None;
  242. });
  243. // NEW: returns the index of the given object in the deque
  244. vm->bind(type, "index(self, obj, start=None, stop=None) -> int",
  245. [](VM *vm, ArgsView args)
  246. {
  247. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  248. PyDeque &self = _CAST(PyDeque &, args[0]);
  249. PyObject *obj = args[1];
  250. int start = CAST_DEFAULT(int, args[2], 0);
  251. int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size());
  252. int index = self.findIndex(vm, obj, start, stop);
  253. if (index < 0) vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
  254. return VAR(index);
  255. });
  256. // NEW: returns the index of the given object in the deque
  257. vm->bind(type, "__contains__(self, obj) -> bool",
  258. [](VM *vm, ArgsView args)
  259. {
  260. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  261. PyDeque &self = _CAST(PyDeque &, args[0]);
  262. PyObject *obj = args[1];
  263. int start = 0, stop = self.dequeItems.size(); // default values
  264. int index = self.findIndex(vm, obj, start, stop);
  265. if (index != -1)
  266. return VAR(true);
  267. return VAR(false);
  268. });
  269. // NEW: inserts the given object at the given index
  270. vm->bind(type, "insert(self, index, obj) -> None",
  271. [](VM *vm, ArgsView args)
  272. {
  273. PyDeque &self = _CAST(PyDeque &, args[0]);
  274. int index = CAST(int, args[1]);
  275. PyObject *obj = args[2];
  276. if (self.bounded && self.dequeItems.size() == self.maxlen)
  277. vm->IndexError("deque already at its maximum size");
  278. else
  279. self.insertObj(false, false, index, obj); // this index shouldn't be fixed using vm->normalized_index, pass as is
  280. return vm->None;
  281. });
  282. // NEW: removes the first occurrence of the given object from the deque
  283. vm->bind(type, "remove(self, obj) -> None",
  284. [](VM *vm, ArgsView args)
  285. {
  286. PyDeque &self = _CAST(PyDeque &, args[0]);
  287. PyObject *obj = args[1];
  288. PyObject *removed = self.popObj(false, false, obj, vm);
  289. if (removed == nullptr)
  290. vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
  291. return vm->None;
  292. });
  293. // NEW: reverses the deque
  294. vm->bind(type, "reverse(self) -> None",
  295. [](VM *vm, ArgsView args)
  296. {
  297. PyDeque &self = _CAST(PyDeque &, args[0]);
  298. if (self.dequeItems.empty() || self.dequeItems.size() == 1)
  299. return vm->None; // handle trivial cases
  300. int sz = self.dequeItems.size();
  301. for (int i = 0; i < sz / 2; i++)
  302. {
  303. PyObject *tmp = self.dequeItems[i];
  304. self.dequeItems[i] = self.dequeItems[sz - i - 1]; // swapping
  305. self.dequeItems[sz - i - 1] = tmp;
  306. }
  307. return vm->None;
  308. });
  309. // NEW: rotates the deque by n steps
  310. vm->bind(type, "rotate(self, n=1) -> None",
  311. [](VM *vm, ArgsView args)
  312. {
  313. PyDeque &self = _CAST(PyDeque &, args[0]);
  314. int n = CAST(int, args[1]);
  315. if (n != 0 && !self.dequeItems.empty()) // trivial case
  316. {
  317. PyObject *tmp; // holds the object to be rotated
  318. int direction = n > 0 ? 1 : -1;
  319. n = abs(n);
  320. n = n % self.dequeItems.size(); // make sure n is in range
  321. while (n--)
  322. {
  323. if (direction == 1)
  324. {
  325. tmp = self.dequeItems.back();
  326. self.dequeItems.pop_back();
  327. self.dequeItems.push_front(tmp);
  328. }
  329. else
  330. {
  331. tmp = self.dequeItems.front();
  332. self.dequeItems.pop_front();
  333. self.dequeItems.push_back(tmp);
  334. }
  335. }
  336. }
  337. return vm->None;
  338. });
  339. // NEW: getter and setter of property `maxlen`
  340. vm->bind_property(
  341. type, "maxlen: int",
  342. [](VM *vm, ArgsView args)
  343. {
  344. PyDeque &self = _CAST(PyDeque &, args[0]);
  345. if (self.bounded)
  346. return VAR(self.maxlen);
  347. return vm->None;
  348. },
  349. [](VM *vm, ArgsView args)
  350. {
  351. vm->AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable");
  352. return vm->None;
  353. });
  354. // NEW: support pickle
  355. vm->bind(type, "__getnewargs__(self) -> tuple[list, int]",
  356. [](VM *vm, ArgsView args)
  357. {
  358. PyDeque &self = _CAST(PyDeque &, args[0]);
  359. Tuple ret(2);
  360. List list;
  361. for (PyObject *obj : self.dequeItems)
  362. {
  363. list.push_back(obj);
  364. }
  365. ret[0] = VAR(std::move(list));
  366. if (self.bounded)
  367. ret[1] = VAR(self.maxlen);
  368. else
  369. ret[1] = vm->None;
  370. return VAR(ret);
  371. });
  372. }
  373. /// @brief initializes a new PyDeque object, actual initialization is done in __init__
  374. PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen)
  375. {
  376. if (maxlen != vm->None) // fix the maxlen first
  377. {
  378. int tmp = CAST(int, maxlen);
  379. if (tmp < 0)
  380. vm->ValueError("maxlen must be non-negative");
  381. else
  382. {
  383. this->maxlen = tmp;
  384. this->bounded = true;
  385. }
  386. }
  387. else
  388. {
  389. this->bounded = false;
  390. this->maxlen = -1;
  391. }
  392. if (iterable != vm->None)
  393. {
  394. this->dequeItems.clear(); // clear the deque
  395. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  396. PyObject *it = vm->py_iter(iterable); // strong ref
  397. PyObject *obj = vm->py_next(it);
  398. while (obj != vm->StopIteration)
  399. {
  400. this->insertObj(false, true, -1, obj);
  401. obj = vm->py_next(it);
  402. }
  403. }
  404. }
  405. int PyDeque::findIndex(VM *vm, PyObject *obj, int start, int stop)
  406. {
  407. // the following code is special purpose normalization for this method, taken from CPython: _collectionsmodule.c file
  408. if (start < 0)
  409. {
  410. start = this->dequeItems.size() + start; // try to fix for negative indices
  411. if (start < 0)
  412. start = 0;
  413. }
  414. if (stop < 0)
  415. {
  416. stop = this->dequeItems.size() + stop; // try to fix for negative indices
  417. if (stop < 0)
  418. stop = 0;
  419. }
  420. if (stop > this->dequeItems.size())
  421. stop = this->dequeItems.size();
  422. if (start > stop)
  423. start = stop; // end of normalization
  424. PK_ASSERT(start >= 0 && start <= this->dequeItems.size() && stop >= 0 && stop <= this->dequeItems.size() && start <= stop); // sanity check
  425. int loopSize = std::min((int)(this->dequeItems.size()), stop);
  426. int sz = this->dequeItems.size();
  427. for (int i = start; i < loopSize; i++)
  428. {
  429. if (vm->py_eq(this->dequeItems[i], obj))
  430. return i;
  431. if (sz != this->dequeItems.size())// mutating the deque during iteration is not allowed
  432. vm->RuntimeError("deque mutated during iteration");
  433. }
  434. return -1;
  435. }
  436. /// @brief pops or removes an item from the deque
  437. /// @param front if true, pop from the front of the deque
  438. /// @param back if true, pop from the back of the deque
  439. /// @param item if front and back is not set, remove the first occurrence of item from the deque
  440. /// @param vm is needed for the py_eq
  441. /// @return PyObject* if front or back is set, this is a pop operation and we return a PyObject*, if front and back are not set, this is a remove operation and we return the removed item or nullptr
  442. PyObject *PyDeque::popObj(bool front, bool back, PyObject *item, VM *vm)
  443. {
  444. // error handling
  445. if (front && back)
  446. throw std::runtime_error("both front and back are set"); // this should never happen
  447. if (front || back)
  448. {
  449. // front or back is set, we don't care about item, this is a pop operation and we return a PyObject*
  450. if (this->dequeItems.empty())
  451. throw std::runtime_error("pop from an empty deque"); // shouldn't happen
  452. PyObject *obj;
  453. if (front)
  454. {
  455. obj = this->dequeItems.front();
  456. this->dequeItems.pop_front();
  457. }
  458. else
  459. {
  460. obj = this->dequeItems.back();
  461. this->dequeItems.pop_back();
  462. }
  463. return obj;
  464. }
  465. else
  466. {
  467. // front and back are not set, we care about item, this is a remove operation and we return the removed item or nullptr
  468. int sz = this->dequeItems.size();
  469. for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
  470. {
  471. bool found = vm->py_eq((*it), item);
  472. if (sz != this->dequeItems.size()) // mutating the deque during iteration is not allowed
  473. vm->IndexError("deque mutated during iteration");
  474. if (found)
  475. {
  476. PyObject *obj = *it; // keep a reference to the object for returning
  477. this->dequeItems.erase(it);
  478. return obj;
  479. }
  480. }
  481. return nullptr; // not found
  482. }
  483. }
  484. /// @brief inserts an item into the deque
  485. /// @param front if true, insert at the front of the deque
  486. /// @param back if true, insert at the back of the deque
  487. /// @param index if front and back are not set, insert at the given index
  488. /// @param item the item to insert
  489. /// @return true if the item was inserted successfully, false if the deque is bounded and is already at its maximum size
  490. void PyDeque::insertObj(bool front, bool back, int index, PyObject *item) // assume index is not fixed using the vm->normalized_index
  491. {
  492. // error handling
  493. if (front && back)
  494. throw std::runtime_error("both front and back are set"); // this should never happen
  495. if (front || back)
  496. {
  497. // front or back is set, we don't care about index
  498. if (this->bounded)
  499. {
  500. if (this->maxlen == 0)
  501. return; // bounded and maxlen is 0, so we can't append
  502. else if (this->dequeItems.size() == this->maxlen)
  503. {
  504. if (front)
  505. this->dequeItems.pop_back(); // remove the last item
  506. else if (back)
  507. this->dequeItems.pop_front(); // remove the first item
  508. }
  509. }
  510. if (front)
  511. this->dequeItems.emplace_front(item);
  512. else if (back)
  513. this->dequeItems.emplace_back(item);
  514. }
  515. else
  516. {
  517. // front and back are not set, we care about index
  518. if (index < 0)
  519. index = this->dequeItems.size() + index; // try fixing for negative indices
  520. if (index < 0) // still negative means insert at the beginning
  521. this->dequeItems.push_front(item);
  522. else if (index >= this->dequeItems.size()) // still out of range means insert at the end
  523. this->dequeItems.push_back(item);
  524. else
  525. this->dequeItems.insert((this->dequeItems.begin() + index), item); // insert at the given index
  526. }
  527. }
  528. /// @brief marks the deque items for garbage collection
  529. void PyDeque::_gc_mark() const
  530. {
  531. for (PyObject *obj : this->dequeItems)
  532. PK_OBJ_MARK(obj);
  533. }
  534. /// @brief registers the PyDeque class
  535. void add_module_collections(VM *vm)
  536. {
  537. PyObject *mod = vm->new_module("collections");
  538. vm->register_user_class<PyDeque>(mod, "deque");
  539. vm->register_user_class<PyDequeIter>(mod, "_deque_iter");
  540. CodeObject_ code = vm->compile(kPythonLibs_collections, "collections.py", EXEC_MODE);
  541. vm->_exec(code, mod);
  542. }
  543. } // namespace pkpypkpy