diff --git a/ChangeLog b/ChangeLog index abf4b8378..0a7b4f9ec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * python binding add more add_torrent_params fields and an invalid key check * introduce introduce distinct types for peer_class_t, piece_index_t and file_index_t. * fix crash caused by empty bitfield diff --git a/bindings/python/src/converters.cpp b/bindings/python/src/converters.cpp index c7f3691f8..83ad32735 100644 --- a/bindings/python/src/converters.cpp +++ b/bindings/python/src/converters.cpp @@ -109,6 +109,41 @@ struct tuple_to_pair } }; +template +struct dict_to_map +{ + dict_to_map() + { + converter::registry::push_back( + &convertible, &construct, type_id>() + ); + } + + static void* convertible(PyObject* x) + { + return PyDict_Check(x) ? x: nullptr; + } + + static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) + { + void* storage = ((converter::rvalue_from_python_storage< + std::map>*)data)->storage.bytes; + + dict o(borrowed(x)); + std::map m; + + list iterkeys = (list)o.keys(); + int const len = int(boost::python::len(iterkeys)); + for (int i = 0; i < len; i++) + { + object key = iterkeys[i]; + m[extract(key)] = extract(o[key]); + } + new (storage) std::map(m); + data->convertible = storage; + } +}; + template struct vector_to_list { @@ -203,6 +238,7 @@ void bind_converters() to_python_converter>(); to_python_converter>(); to_python_converter(); + to_python_converter, pair_to_tuple>(); to_python_converter, vector_to_list>(); to_python_converter, vector_to_list>(); @@ -223,6 +259,7 @@ void bind_converters() tuple_to_endpoint(); tuple_to_endpoint(); tuple_to_pair(); + dict_to_map(); list_to_vector(); list_to_vector(); list_to_vector(); diff --git a/bindings/python/src/session.cpp b/bindings/python/src/session.cpp index a69ac6d6d..c8c9f414a 100644 --- a/bindings/python/src/session.cpp +++ b/bindings/python/src/session.cpp @@ -206,58 +206,123 @@ namespace void dict_to_add_torrent_params(dict params, add_torrent_params& p) { - // torrent_info objects are always held by a shared_ptr in the python binding - if (params.has_key("ti") && params.get("ti") != boost::python::object()) + list items = params.items(); + int const len = int(boost::python::len(items)); + for (int i = 0; i < len; i++) { - // make a copy here. We don't want to end up holding a python-owned - // object inside libtorrent. If the last reference goes out of scope - // on the C++ side, it will end up freeing the python object - // without holding the GIL and likely crash. - // https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html - p.ti = std::make_shared( - extract(params["ti"])); - } - - - if (params.has_key("info_hash")) - p.info_hash = sha1_hash(bytes(extract(params["info_hash"])).arr.data()); - if (params.has_key("name")) - p.name = extract(params["name"]); - p.save_path = extract(params["save_path"]); - + boost::python::api::object_item item = items[i]; + std::string const key = extract(item[0]); + object const value = item[1]; + // torrent_info objects are always held by a shared_ptr in the + // python binding, skip it if it is a object + if(key == "ti" && value != boost::python::object()) + { + // make a copy here. We don't want to end up holding a python-owned + // object inside libtorrent. If the last reference goes out of scope + // on the C++ side, it will end up freeing the python object + // without holding the GIL and likely crash. + // https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html + p.ti = std::make_shared( + extract(value)); + continue; + } + else if(key == "info_hash") + { + p.info_hash = sha1_hash( + bytes(extract(value)).arr.data()); + continue; + } + else if(key == "name") + { + p.name = extract(value); + continue; + } + else if(key == "save_path") + { + p.save_path = extract(value); + continue; + } #ifndef TORRENT_NO_DEPRECATE - if (params.has_key("resume_data")) - { - std::string resume = extract(params["resume_data"]); - p.resume_data.assign(resume.begin(), resume.end()); + else if(key == "resume_data") + { + std::string resume = extract(value); + p.resume_data.assign(resume.begin(), resume.end()); + continue; + } + else if(key == "uuid") + { + p.uuid = extract(value); + continue; + } +#endif + else if(key == "storage_mode") + { + p.storage_mode = extract(value); + continue; + } + else if(key == "trackers") + { + p.trackers = extract>(value); + continue; + } + else if(key == "url_seeds") + { + p.url_seeds = extract>(value); + continue; + } + else if(key == "http_seeds") + { + p.http_seeds = + extract(value); + continue; + } + else if(key == "dht_nodes") + { + p.dht_nodes = + extract>>(value); + continue; + } + else if(key == "banned_peers") + { + p.banned_peers = extract>(value); + continue; + } + else if(key == "peers") + { + p.peers = extract>(value); + continue; + } + else if(key == "flags") + { + p.flags = extract(value); + continue; + } + else if(key == "trackerid") + { + p.trackerid = extract(value); + continue; + } + else if(key == "url") + { + p.url = extract(value); + continue; + } + else if(key == "renamed_files") + { + p.renamed_files = + extract>(value); + } + else if(key == "file_priorities") + { + p.file_priorities = extract>(value); + } + else + { + PyErr_SetString(PyExc_KeyError, + ("unknown name in torrent params: " + key).c_str()); + throw_error_already_set(); + } } -#endif - if (params.has_key("storage_mode")) - p.storage_mode = extract(params["storage_mode"]); - - if (params.has_key("trackers")) - p.trackers = extract>(params["trackers"]); - if (params.has_key("dht_nodes")) - p.dht_nodes = extract>>(params["dht_nodes"]); - if (params.has_key("http_seeds")) - p.http_seeds = extract>(params["http_seeds"]); - if (params.has_key("peers")) - p.peers = extract>(params["peers"]); - if (params.has_key("banned_peers")) - p.banned_peers = extract>(params["banned_peers"]); - if (params.has_key("flags")) - p.flags = extract(params["flags"]); - if (params.has_key("trackerid")) - p.trackerid = extract(params["trackerid"]); - if (params.has_key("url")) - p.url = extract(params["url"]); -#ifndef TORRENT_NO_DEPRECATE - if (params.has_key("uuid")) - p.uuid = extract(params["uuid"]); -#endif - - if (params.has_key("file_priorities")) - p.file_priorities = extract>(params["file_priorities"]); } namespace diff --git a/bindings/python/test.py b/bindings/python/test.py index 6b44becc7..04458f7f9 100644 --- a/bindings/python/test.py +++ b/bindings/python/test.py @@ -127,6 +127,45 @@ class test_torrent_handle(unittest.TestCase): cs = self.ses.get_cache_info(self.h) self.assertEqual(cs.pieces, []) + def test_unknown_torrent_parameter(self): + self.ses = lt.session({'alert_mask': lt.alert.category_t.all_categories, + 'enable_dht': False}) + try: + self.h = self.ses.add_torrent({'unexpected-key-name': ''}) + self.assertFalse('should have thrown an exception') + except KeyError as e: + print(e) + + def test_torrent_parameter(self): + self.ses = lt.session({'alert_mask': lt.alert.category_t.all_categories, + 'enable_dht': False}) + self.ti = lt.torrent_info('url_seed_multi.torrent'); + self.h = self.ses.add_torrent({ + 'ti': self.ti, + 'save_path': os.getcwd(), + 'trackers': ['http://test.com/announce'], + 'dht_nodes': [('1.2.3.4', 6881), ('4.3.2.1', 6881)], + 'file_priorities': [1,1], + 'http_seeds': ['http://test.com/file3'], + 'url_seeds': ['http://test.com/announce-url'], + 'peers': [('5.6.7.8', 6881)], + 'banned_peers': [('8.7.6.5', 6881)], + 'renamed_files': { 0: 'test.txt', 2: 'test.txt' } + }) + self.st = self.h.status() + self.assertEqual(self.st.save_path, os.getcwd()) + trackers = self.h.trackers(); + self.assertEqual(len(trackers), 1) + self.assertEqual(trackers[0].get('url'), 'http://test.com/announce') + self.assertEqual(trackers[0].get('tier'), 0) + self.assertEqual(self.h.file_priorities(), [1,1]) + self.assertEqual(self.h.http_seeds(),['http://test.com/file3']) + # url_seeds was already set, test that it did not got overwritten + self.assertEqual(self.h.url_seeds(), + ['http://test.com/announce-url/', 'http://test.com/file/']) + self.assertEqual(self.h.piece_priorities(),[4]) + self.assertEqual(self.ti.merkle_tree(),[]) + self.assertEqual(self.st.verified_pieces,[]) class test_torrent_info(unittest.TestCase):