def test_metainfo_success(self): """Compare metainfo() output to torrent files generated by other software""" # List directories in DATA_DIR/files which will be used for this test. Matching # torrent files generated by another software must be found in DATA_DIR/torrent files/ test_cases = ['single_file', 'multiple_files'] for directory in test_cases: dirpath = os.path.join(DATA_DIR, 'files', directory) torrent_file = os.path.join(DATA_DIR, 'torrent_files', directory + '.torrent') with self.subTest(directory=directory): with open(torrent_file, "rb") as f: m_target = bdecode(f.read()) piece_length = m_target[b'info'][b'piece length'] announce_list = [['http://example.com:8080/announce']] m_computed = metainfo(dirpath, piece_length, announce_list) if b'files' in m_target[b'info']: info_keys = [b'files'] else: info_keys = [b'length'] info_keys += [b'pieces'] for key in info_keys: self.assertEqual(m_computed[b'info'][key], m_target[b'info'][key])
def test_Bdecode_failure(self): fail_cases = [ # Invalid string lengths b"4:spa", b"2:spa" b"-1:x", b"-4:spam", b"0-4:spam", b"0:e", # Dictionary key must be a string b"di42e4:spame", # Dictionary with no terminating "e" b"d4:spami42e4:spomi43e", # Wrong key ordering b"d3:tow3:moo4:spam4:eggse", # Invalid integers b"i2Fe", b"ie", b"i42", b"i--23e", b"i1+1e", b"i1,2e", b"i1.2e", b"i1.0e", b"i1,0e", b"i-0e", b"i01e", # List with no terminating "e" b"li42ei43e" ] for testCase in fail_cases: with self.subTest(testCase=testCase): with self.assertRaises(ValueError, msg="case '{0}'".format(testCase)): print(bdecode(testCase))
async def _query_tracker(self, url: str) -> Iterator[Tuple[str, int]]: """Send the request to the tracker""" self._logger.debug("request: {}".format(url)) async with aiohttp.ClientSession() as session: async with session.get(url) as response: if response.status != 200: raise ConnectionError d = bdecode(await response.read()) return map(decode_ipv4, split(d[b'peers'], 6))
def test_bencode_bdecode(self): # Decode whole Torrent files torrent_dir = os.path.join(DATA_DIR, "torrent_files") for file in os.listdir(torrent_dir): filename = os.path.join(torrent_dir, os.fsdecode(file)) with open(filename, "rb") as f: with self.subTest(filename=filename): s = f.read() self.assertEqual(s, bencode(bdecode(s)))
def _from_file(cls, torrent_file: str): """Create an instance of Torrent from a metainfo file""" with open(torrent_file, "rb") as f: m = bdecode(f.read()) # TODO: check type of optional items if validate_structure(m, METAINFO_SINGLE_FILE): single_file_mode = True elif validate_structure(m, METAINFO_MULTIPLE_FILES): single_file_mode = False else: raise ValueError("Invalid torrent file") info = m[b'info'] info_hash = sha1(bencode(info)).digest() if b'announce-list' in m: announce = m[b'announce-list'] else: announce = [[m[b'announce']]] announce_decoded = [] for trackers in announce: announce_decoded.append(list(map(lambda x: x.decode('utf-8'), trackers))) name = info[b'name'].decode("utf-8") hashes = split(info[b'pieces'], sha1().digest_size) piece_length = info[b'piece length'] contents = [] if single_file_mode: contents.append(("", sanitize(name), info[b"length"])) else: for file_dict in info[b'files']: path_str = [sanitize(b.decode("utf-8")) for b in file_dict[b'path']] directory = os.path.join("", *path_str[:-1]) filename = path_str[-1] contents.append((directory, filename, file_dict[b'length'])) return cls(announce_decoded, name, contents, info_hash, hashes, piece_length)
def test_Bdecode_success(self): for (key, value) in validBEncodings.items(): with self.subTest(case=key, expected=value, result=bdecode(key)): self.assertEqual(bdecode(key), value)