add check and test for torrent parameter (#1541)

add test and fix for add_torrent_params python binding
This commit is contained in:
Falcosc 2017-01-24 15:26:11 +01:00 committed by Arvid Norberg
parent 32e0028e32
commit c979802ce9
4 changed files with 191 additions and 49 deletions

View File

@ -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 * introduce introduce distinct types for peer_class_t, piece_index_t and
file_index_t. file_index_t.
* fix crash caused by empty bitfield * fix crash caused by empty bitfield

View File

@ -109,6 +109,41 @@ struct tuple_to_pair
} }
}; };
template<class T1, class T2>
struct dict_to_map
{
dict_to_map()
{
converter::registry::push_back(
&convertible, &construct, type_id<std::map<T1, T2>>()
);
}
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<T1, T2>>*)data)->storage.bytes;
dict o(borrowed(x));
std::map<T1, T2> 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<T1>(key)] = extract<T2>(o[key]);
}
new (storage) std::map<T1, T2>(m);
data->convertible = storage;
}
};
template<class T> template<class T>
struct vector_to_list struct vector_to_list
{ {
@ -203,6 +238,7 @@ void bind_converters()
to_python_converter<lt::tcp::endpoint, endpoint_to_tuple<lt::tcp::endpoint>>(); to_python_converter<lt::tcp::endpoint, endpoint_to_tuple<lt::tcp::endpoint>>();
to_python_converter<lt::udp::endpoint, endpoint_to_tuple<lt::udp::endpoint>>(); to_python_converter<lt::udp::endpoint, endpoint_to_tuple<lt::udp::endpoint>>();
to_python_converter<lt::address, address_to_tuple>(); to_python_converter<lt::address, address_to_tuple>();
to_python_converter<std::pair<std::string, int>, pair_to_tuple<std::string, int>>();
to_python_converter<std::vector<lt::stats_metric>, vector_to_list<lt::stats_metric>>(); to_python_converter<std::vector<lt::stats_metric>, vector_to_list<lt::stats_metric>>();
to_python_converter<std::vector<lt::pool_file_status>, vector_to_list<lt::pool_file_status>>(); to_python_converter<std::vector<lt::pool_file_status>, vector_to_list<lt::pool_file_status>>();
@ -223,6 +259,7 @@ void bind_converters()
tuple_to_endpoint<lt::tcp::endpoint>(); tuple_to_endpoint<lt::tcp::endpoint>();
tuple_to_endpoint<lt::udp::endpoint>(); tuple_to_endpoint<lt::udp::endpoint>();
tuple_to_pair<lt::piece_index_t, int>(); tuple_to_pair<lt::piece_index_t, int>();
dict_to_map<lt::file_index_t, std::string>();
list_to_vector<int>(); list_to_vector<int>();
list_to_vector<std::uint8_t>(); list_to_vector<std::uint8_t>();
list_to_vector<std::string>(); list_to_vector<std::string>();

View File

@ -206,58 +206,123 @@ namespace
void dict_to_add_torrent_params(dict params, add_torrent_params& p) 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 list items = params.items();
if (params.has_key("ti") && params.get("ti") != boost::python::object()) 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 boost::python::api::object_item item = items[i];
// object inside libtorrent. If the last reference goes out of scope std::string const key = extract<std::string>(item[0]);
// on the C++ side, it will end up freeing the python object object const value = item[1];
// without holding the GIL and likely crash. // torrent_info objects are always held by a shared_ptr in the
// https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html // python binding, skip it if it is a object
p.ti = std::make_shared<torrent_info>( if(key == "ti" && value != boost::python::object())
extract<torrent_info const&>(params["ti"])); {
} // 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
if (params.has_key("info_hash")) // without holding the GIL and likely crash.
p.info_hash = sha1_hash(bytes(extract<bytes>(params["info_hash"])).arr.data()); // https://mail.python.org/pipermail/cplusplus-sig/2007-June/012130.html
if (params.has_key("name")) p.ti = std::make_shared<torrent_info>(
p.name = extract<std::string>(params["name"]); extract<torrent_info const&>(value));
p.save_path = extract<std::string>(params["save_path"]); continue;
}
else if(key == "info_hash")
{
p.info_hash = sha1_hash(
bytes(extract<bytes>(value)).arr.data());
continue;
}
else if(key == "name")
{
p.name = extract<std::string>(value);
continue;
}
else if(key == "save_path")
{
p.save_path = extract<std::string>(value);
continue;
}
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
if (params.has_key("resume_data")) else if(key == "resume_data")
{ {
std::string resume = extract<std::string>(params["resume_data"]); std::string resume = extract<std::string>(value);
p.resume_data.assign(resume.begin(), resume.end()); p.resume_data.assign(resume.begin(), resume.end());
continue;
}
else if(key == "uuid")
{
p.uuid = extract<std::string>(value);
continue;
}
#endif
else if(key == "storage_mode")
{
p.storage_mode = extract<storage_mode_t>(value);
continue;
}
else if(key == "trackers")
{
p.trackers = extract<std::vector<std::string>>(value);
continue;
}
else if(key == "url_seeds")
{
p.url_seeds = extract<std::vector<std::string>>(value);
continue;
}
else if(key == "http_seeds")
{
p.http_seeds =
extract<decltype(add_torrent_params::http_seeds)>(value);
continue;
}
else if(key == "dht_nodes")
{
p.dht_nodes =
extract<std::vector<std::pair<std::string, int>>>(value);
continue;
}
else if(key == "banned_peers")
{
p.banned_peers = extract<std::vector<lt::tcp::endpoint>>(value);
continue;
}
else if(key == "peers")
{
p.peers = extract<std::vector<lt::tcp::endpoint>>(value);
continue;
}
else if(key == "flags")
{
p.flags = extract<std::uint64_t>(value);
continue;
}
else if(key == "trackerid")
{
p.trackerid = extract<std::string>(value);
continue;
}
else if(key == "url")
{
p.url = extract<std::string>(value);
continue;
}
else if(key == "renamed_files")
{
p.renamed_files =
extract<std::map<lt::file_index_t, std::string>>(value);
}
else if(key == "file_priorities")
{
p.file_priorities = extract<std::vector<std::uint8_t>>(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<storage_mode_t>(params["storage_mode"]);
if (params.has_key("trackers"))
p.trackers = extract<std::vector<std::string>>(params["trackers"]);
if (params.has_key("dht_nodes"))
p.dht_nodes = extract<std::vector<std::pair<std::string, int>>>(params["dht_nodes"]);
if (params.has_key("http_seeds"))
p.http_seeds = extract<std::vector<std::string>>(params["http_seeds"]);
if (params.has_key("peers"))
p.peers = extract<std::vector<tcp::endpoint>>(params["peers"]);
if (params.has_key("banned_peers"))
p.banned_peers = extract<std::vector<tcp::endpoint>>(params["banned_peers"]);
if (params.has_key("flags"))
p.flags = extract<std::uint64_t>(params["flags"]);
if (params.has_key("trackerid"))
p.trackerid = extract<std::string>(params["trackerid"]);
if (params.has_key("url"))
p.url = extract<std::string>(params["url"]);
#ifndef TORRENT_NO_DEPRECATE
if (params.has_key("uuid"))
p.uuid = extract<std::string>(params["uuid"]);
#endif
if (params.has_key("file_priorities"))
p.file_priorities = extract<std::vector<std::uint8_t>>(params["file_priorities"]);
} }
namespace namespace

View File

@ -127,6 +127,45 @@ class test_torrent_handle(unittest.TestCase):
cs = self.ses.get_cache_info(self.h) cs = self.ses.get_cache_info(self.h)
self.assertEqual(cs.pieces, []) 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): class test_torrent_info(unittest.TestCase):