| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | """ |
|---|
| 5 | "Converters" to render directory/file paths to HTML. |
|---|
| 6 | |
|---|
| 7 | The function `converter` in this module takes a path and returns an |
|---|
| 8 | appropriate converter callable. This takes a single argument, an |
|---|
| 9 | instance of `document.Document`. |
|---|
| 10 | """ |
|---|
| 11 | |
|---|
| 12 | import mimetypes |
|---|
| 13 | import os |
|---|
| 14 | |
|---|
| 15 | import archive_converter |
|---|
| 16 | import directory_converter |
|---|
| 17 | import feature |
|---|
| 18 | import image_converter |
|---|
| 19 | import text_converter |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | class InvalidPath(Exception): |
|---|
| 23 | pass |
|---|
| 24 | |
|---|
| 25 | |
|---|
| 26 | |
|---|
| 27 | def _is_image_path(path): |
|---|
| 28 | """Return `True` if the `path` presumably represents an image, |
|---|
| 29 | else return `False`. Also return `False` if the file can't be |
|---|
| 30 | read. |
|---|
| 31 | |
|---|
| 32 | If the path denotes a directory, the behavior is undefined. |
|---|
| 33 | """ |
|---|
| 34 | mime_type = mimetypes.guess_type(path)[0] |
|---|
| 35 | if mime_type is None: |
|---|
| 36 | return False |
|---|
| 37 | return mime_type.split("/", 1)[0] == "image" |
|---|
| 38 | |
|---|
| 39 | def _is_binary_path(path): |
|---|
| 40 | """Return `True`, if `data` assumedly represents binary data, |
|---|
| 41 | not text data. Else return `False`. Also return `False` if the |
|---|
| 42 | file can't be read. |
|---|
| 43 | |
|---|
| 44 | If the path denotes a directory, the behavior is undefined. |
|---|
| 45 | """ |
|---|
| 46 | |
|---|
| 47 | binary_test_size = 1024 |
|---|
| 48 | try: |
|---|
| 49 | with open(path, "rb") as fobj: |
|---|
| 50 | data = fobj.read(binary_test_size) |
|---|
| 51 | except IOError: |
|---|
| 52 | return False |
|---|
| 53 | if not data: |
|---|
| 54 | return False |
|---|
| 55 | |
|---|
| 56 | threshold = 0.05 |
|---|
| 57 | control_codes = [byte for byte in data |
|---|
| 58 | if ord(byte) < 32 and not byte in "\n\r\t"] |
|---|
| 59 | return (len(control_codes) / float(len(data)) > threshold) |
|---|
| 60 | |
|---|
| 61 | |
|---|
| 62 | def converter(path): |
|---|
| 63 | """Return a suitable converter for filesystem path `path`. |
|---|
| 64 | |
|---|
| 65 | The returned converter may actually be a chain of converters which |
|---|
| 66 | can be used as a simple function. |
|---|
| 67 | """ |
|---|
| 68 | if os.path.isfile(path): |
|---|
| 69 | if _is_image_path(path): |
|---|
| 70 | return image_converter.html |
|---|
| 71 | elif mimetypes.guess_type(path)[0] == 'application/x-tar': |
|---|
| 72 | return archive_converter.tar_file |
|---|
| 73 | elif mimetypes.guess_type(path)[0] == 'application/zip': |
|---|
| 74 | return archive_converter.zip_file |
|---|
| 75 | elif _is_binary_path(path): |
|---|
| 76 | return text_converter.hexdump |
|---|
| 77 | else: |
|---|
| 78 | if feature.pygments: |
|---|
| 79 | return text_converter.highlighted_html |
|---|
| 80 | return text_converter.link_includes_html |
|---|
| 81 | else: |
|---|
| 82 | return text_converter.simple_html |
|---|
| 83 | elif os.path.isdir(path): |
|---|
| 84 | return directory_converter.directory_listing |
|---|
| 85 | else: |
|---|
| 86 | |
|---|
| 87 | raise InvalidPath('invalid path "%s"' % path) |
|---|