extract text from any document. no muss. no fuss.
This report summarizes scanner evidence for deanmalmgren/textract, a HTML project with 4,727 GitHub stars. SynthScan examined 14,281 lines of code across 147 source files, recording 90 pattern matches distributed across 11 syntactic categories. The legacy adjusted pattern signal is 8.2 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 61 finding(s).
Are imports declared and package/API names verified? 15 finding(s).
Are wrappers, guards, and repeated structures necessary? 61 finding(s).
Which changes need focused human review? 90 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 90 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_special_filenames.py | 49 | def test_unbalanced_parentheses(self): | CODE |
| LOW | tests/test_special_filenames.py | 54 | def test_shell_injection_chars(self): | CODE |
| LOW | tests/test_special_filenames.py | 70 | def test_combined_special_chars(self): | CODE |
| LOW | tests/test_txt.py | 15 | def test_extensionless_filenames(self): | CODE |
| LOW | tests/test_pdf.py | 94 | def test_standardized_text_cli(self): | CODE |
| LOW | tests/test_pdf.py | 110 | def test_standardized_text_python(self): | CODE |
| LOW | tests/test_pdf.py | 146 | def test_unknown_method_raises(self): | CODE |
| LOW | tests/test_pdf.py | 151 | def test_pdftotext_fallback_to_pdfminer(self): | CODE |
| LOW | tests/test_pdf.py | 162 | def test_pdftotext_preserves_non_latin_text(self): | CODE |
| LOW | tests/test_pdf.py | 176 | def test_pdftotext_ignores_input_encoding(self): | CODE |
| LOW | tests/test_platform_limitations.py | 12 | def test_docs_mention_every_limitation(self): | CODE |
| LOW | tests/test_exceptions.py | 25 | def test_unsupported_extension_cli(self): | CODE |
| LOW | tests/test_exceptions.py | 38 | def test_unsupported_extension_python(self): | CODE |
| LOW | tests/test_exceptions.py | 47 | def test_missing_filename_cli(self): | CODE |
| LOW | tests/test_exceptions.py | 58 | def test_missing_filename_python(self): | CODE |
| LOW | tests/test_exceptions.py | 74 | def test_missing_module_python(self): | CODE |
| LOW | tests/test_exceptions.py | 98 | def test_shell_parser_run_passes_startupinfo_on_windows(self): | CODE |
| LOW | tests/test_html.py | 24 | def test_explicit_input_encoding(self): | CODE |
| LOW | tests/test_html.py | 29 | def test_invalid_input_encoding(self): | CODE |
| LOW | tests/test_xlsx.py | 10 | def test_standardized_text_python(self): | CODE |
| LOW | tests/test_eml.py | 9 | def test_explicit_input_encoding(self): | CODE |
| LOW | tests/test_eml.py | 14 | def test_invalid_input_encoding(self): | CODE |
| LOW | tests/test_source_input.py | 103 | def test_process_bytes_matches_filename(self): | CODE |
| LOW | tests/test_source_input.py | 110 | def test_process_stream_matches_filename(self): | CODE |
| LOW | tests/test_source_input.py | 127 | def test_missing_extension_raises(self): | CODE |
| LOW | tests/test_source_input.py | 131 | def test_csv_streams_without_buffering_full_input(self): | CODE |
| LOW | tests/test_source_input.py | 144 | def test_csv_crlf_quoted_newlines_match_across_paths(self): | CODE |
| LOW | tests/test_source_input.py | 156 | def test_process_stream_leaves_caller_stream_open(self): | CODE |
| LOW | tests/test_source_input.py | 165 | def test_csv_streams_from_stdin_pipe(self): | CODE |
| LOW | tests/test_source_input.py | 210 | def test_streaming_uses_less_memory_than_buffering(self): | CODE |
| LOW | tests/test_json.py | 16 | def test_character_sets_python(self): | CODE |
| LOW | tests/test_json.py | 34 | def test_explicit_input_encoding(self): | CODE |
| LOW | tests/test_json.py | 39 | def test_invalid_input_encoding(self): | CODE |
| LOW | tests/base.py | 67 | def _files_equal_ignore_blank_lines(file1: str, file2: str) -> bool: | CODE |
| LOW | tests/base.py | 101 | def _generate_file_diff_message(file1: str, file2: str) -> str: | CODE |
| LOW | tests/base.py | 112 | def _generate_bytes_diff_message(actual: bytes, expected: bytes, label: str) -> str: | CODE |
| LOW | tests/base.py | 198 | def standardized_text_filename(self): | CODE |
| LOW | tests/base.py | 216 | def test_standardized_text_cli(self): | CODE |
| LOW | tests/base.py | 228 | def test_standardized_text_python(self): | CODE |
| LOW | tests/base.py | 299 | def assert_input_encoding_respected(self, filename_root, encoding): | CODE |
| LOW | tests/base.py | 314 | def assert_invalid_input_encoding_raises(self, filename_root, wrong_encoding): | CODE |
| LOW | tests/test_csv.py | 9 | def test_explicit_input_encoding(self): | CODE |
| LOW | tests/test_csv.py | 14 | def test_invalid_input_encoding(self): | CODE |
| LOW | tests/test_epub.py | 10 | def _ignore_xml_as_html_warning(test_method): | CODE |
| LOW | tests/test_epub.py | 32 | def test_standardized_text_python(self): | CODE |
| LOW | textract/parsers/__init__.py | 151 | def _get_available_extensions(): | CODE |
| LOW | textract/parsers/audio.py | 28 | def _pocketsphinx_signature_invalid() -> bool: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_html.py | 0 | a valid-but-wrong input_encoding must raise a friendly error instead of leaking a raw unicodedecodeerror. | STRING |
| HIGH | tests/test_eml.py | 0 | a valid-but-wrong input_encoding must raise a friendly error instead of leaking a raw unicodedecodeerror. | STRING |
| HIGH | tests/test_json.py | 0 | a valid-but-wrong input_encoding must raise a friendly error instead of leaking a raw unicodedecodeerror. | STRING |
| HIGH | tests/test_csv.py | 0 | a valid-but-wrong input_encoding must raise a friendly error instead of leaking a raw unicodedecodeerror. | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | textract/__init__.py | 1 | CODE | |
| LOW | textract/__init__.py | 2 | CODE | |
| LOW | textract/__init__.py | 2 | CODE | |
| LOW | textract/__init__.py | 2 | CODE | |
| LOW | textract/__init__.py | 3 | CODE | |
| LOW | textract/exceptions.py | 3 | CODE | |
| LOW | textract/parsers/jpg_parser.py | 1 | CODE | |
| LOW | textract/parsers/mp3_parser.py | 1 | CODE | |
| LOW | textract/parsers/wav_parser.py | 1 | CODE | |
| LOW | textract/parsers/png_parser.py | 1 | CODE | |
| LOW | textract/parsers/tiff_parser.py | 1 | CODE | |
| LOW | textract/parsers/utils.py | 5 | CODE | |
| LOW | textract/parsers/ogg_parser.py | 1 | CODE | |
| LOW | textract/parsers/doc_parser.py | 1 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/base.py | 325 | except exceptions.InvalidInputEncoding: | CODE |
| HIGH | textract/parsers/audio.py | 46 | except ImportError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | textract/__init__.py | 7 | __all__ = [ | CODE |
| LOW | textract/parsers/jpg_parser.py | 3 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/mp3_parser.py | 3 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/wav_parser.py | 3 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/png_parser.py | 3 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/tiff_parser.py | 3 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/gif_parser.py | 54 | __all__ = ["Parser"] | CODE |
| LOW | textract/parsers/ogg_parser.py | 3 | __all__ = ["Parser"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | textract/parsers/xlsx_parser.py | 9 | CODE | |
| LOW | textract/parsers/html_parser.py | 84 | CODE | |
| LOW | textract/parsers/html_parser.py | 118 | CODE | |
| LOW | textract/parsers/odt_parser.py | 30 | CODE | |
| LOW | textract/parsers/xls_parser.py | 9 | CODE | |
| LOW | textract/parsers/utils.py | 98 | CODE | |
| LOW | textract/parsers/audio.py | 89 | CODE | |
| LOW | textract/parsers/epub_parser.py | 40 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | textract/cli.py | 42 | # This function is necessary to enable autodocumentation of the script | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/test_redhat_family_install.sh | 13 | # Usage: | COMMENT |
| LOW | scripts/docker_smoke_test.sh | 5 | # Usage: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/test_redhat_family_install.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | .github/actions/setup/action.yml | 41 | uses: actions/cache@v4 | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | textract/parsers/gif_parser.py | 21 | # Check if this is an animated GIF (n_frames > 1) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/epub/raw_text.txt | 4 | Welcome here! All the text have ben generate with the Samuel L lorem ipsum. | CODE |