Python process launching
This report summarizes scanner evidence for amoffat/sh, a Python project with 7,245 GitHub stars. SynthScan examined 8,489 lines of code across 18 source files, recording 112 pattern matches distributed across 9 syntactic categories. The legacy adjusted pattern signal is 15.4 (Elevated 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? 90 finding(s).
Are imports declared and package/API names verified? 1 finding(s).
Are wrappers, guards, and repeated structures necessary? 89 finding(s).
Which changes need focused human review? 112 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 112 distinct pattern matches across 9 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 |
|---|---|---|---|---|
| HIGH | tests/sh_test.py | 366 | except ErrorReturnCode_3: | CODE |
| HIGH | tests/sh_test.py | 3181 | except TimeoutException: | CODE |
| HIGH | tests/sh_test.py | 3372 | except sig_exception: | CODE |
| HIGH | tests/sh_test.py | 3690 | except StopIteration: | CODE |
| HIGH | tests/sh_test.py | 3465 | except Exception: | CODE |
| HIGH | tests/sh_test.py | 3470 | except Exception: | CODE |
| HIGH | src/sh/__init__.py | 419 | except KeyError: | CODE |
| HIGH | src/sh/__init__.py | 1026 | except UnsupportedOperation: | CODE |
| HIGH | src/sh/__init__.py | 3043 | except OSError: | CODE |
| HIGH | src/sh/__init__.py | 3123 | except UnicodeDecodeError: | CODE |
| HIGH | src/sh/__init__.py | 3513 | except KeyError: | CODE |
| HIGH | src/sh/__init__.py | 2380 | except ProcessLookupError: | CODE |
| HIGH | src/sh/__init__.py | 2175 | except OSError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/sh_test.py | 172 | def create_stdin_transform_test(transform="upper"): | CODE |
| LOW | tests/sh_test.py | 350 | def test_exit_code_with_hasattr(self): | CODE |
| LOW | tests/sh_test.py | 369 | def test_exit_code_from_exception(self): | CODE |
| LOW | tests/sh_test.py | 732 | def test_incompatible_special_args(self): | CODE |
| LOW | tests/sh_test.py | 817 | def test_command_wrapper_equivalence(self): | CODE |
| LOW | tests/sh_test.py | 823 | def test_doesnt_execute_directories(self): | CODE |
| LOW | tests/sh_test.py | 859 | def test_multiple_args_short_option(self): | CODE |
| LOW | tests/sh_test.py | 875 | def test_multiple_args_long_option(self): | CODE |
| LOW | tests/sh_test.py | 955 | def test_incremental_composition(self): | CODE |
| LOW | tests/sh_test.py | 1036 | def test_custom_separator_space(self): | CODE |
| LOW | tests/sh_test.py | 1118 | def test_background_exception(self): | CODE |
| LOW | tests/sh_test.py | 1221 | def test_err_to_out_and_sys_stdout(self): | CODE |
| LOW | tests/sh_test.py | 1331 | def test_out_and_err_redirection(self): | CODE |
| LOW | tests/sh_test.py | 1385 | def test_err_redirection_actual_file(self): | CODE |
| LOW | tests/sh_test.py | 1449 | def test_bake_boolean_override(self): | CODE |
| LOW | tests/sh_test.py | 1496 | def test_bake_args_come_first(self): | CODE |
| LOW | tests/sh_test.py | 1930 | def test_async_return_cmd_exc(self): | CODE |
| LOW | tests/sh_test.py | 1943 | def test_handle_both_out_and_err(self): | CODE |
| LOW | tests/sh_test.py | 2033 | def test_for_generator_to_err(self): | CODE |
| LOW | tests/sh_test.py | 2160 | def test_generator_and_callback(self): | CODE |
| LOW | tests/sh_test.py | 2685 | def test_decode_error_handling(self): | CODE |
| LOW | tests/sh_test.py | 2712 | def test_signal_exception_sigusr(self): | CODE |
| LOW | tests/sh_test.py | 2837 | def test_baked_command_can_be_printed(self): | CODE |
| LOW | tests/sh_test.py | 2842 | def test_baked_command_can_be_printed_with_whitespace_args(self): | CODE |
| LOW | tests/sh_test.py | 2855 | def test_baked_command_can_be_printed_with_whitespace_in_options(self): | CODE |
| LOW | tests/sh_test.py | 2904 | def test_done_callback_no_deadlock(self): | CODE |
| LOW | tests/sh_test.py | 2938 | def test_new_session_new_group(self): | CODE |
| LOW | tests/sh_test.py | 2985 | def session_false_group_false(pid, pgid, sid, test_pid): | CODE |
| LOW | tests/sh_test.py | 3059 | def test_stdin_unbuffered_bufsize(self): | CODE |
| LOW | tests/sh_test.py | 3102 | def test_stdin_newline_bufsize(self): | CODE |
| LOW | tests/sh_test.py | 3148 | def test_custom_timeout_signal(self): | CODE |
| LOW | tests/sh_test.py | 3166 | def test_timeout_race_condition_process_exit(self): | CODE |
| LOW | tests/sh_test.py | 3215 | def test_partially_applied_callback(self): | CODE |
| LOW | tests/sh_test.py | 3244 | def test_grandchild_no_sighup(self): | CODE |
| LOW | tests/sh_test.py | 3289 | def test_unchecked_producer_failure(self): | CODE |
| LOW | tests/sh_test.py | 3312 | def test_unchecked_pipeline_failure(self): | CODE |
| LOW | tests/sh_test.py | 3351 | def test_bad_sig_raise_exception(self): | CODE |
| LOW | tests/sh_test.py | 3377 | def test_ok_code_ignores_bad_sig_exception(self): | CODE |
| LOW | tests/sh_test.py | 3410 | def test_uid_drops_supplementary_groups(self): | CODE |
| LOW | tests/sh_test.py | 3545 | def test_percent_doesnt_fail_logging(self): | CODE |
| LOW | tests/sh_test.py | 3678 | def test_stop_iteration_doesnt_block(self): | CODE |
| LOW | tests/sh_test.py | 3694 | def test_threaded_with_contexts(self): | CODE |
| LOW | tests/sh_test.py | 3856 | def test_set_in_parent_function(self): | CODE |
| LOW | tests/sh_test.py | 3881 | def test_command_with_baked_call_args(self): | CODE |
| LOW | tests/sh_test.py | 522 | def test_manual_stdin_iterable(self): | STRING |
| LOW | tests/sh_test.py | 1555 | def test_stdout_callback_no_wait(self): | STRING |
| LOW | tests/sh_test.py | 1583 | def test_stdout_callback_line_buffered(self): | STRING |
| LOW | tests/sh_test.py | 1603 | def test_stdout_callback_line_unbuffered(self): | STRING |
| LOW | tests/sh_test.py | 1624 | def test_stdout_callback_buffered(self): | STRING |
| LOW | tests/sh_test.py | 1644 | def test_stdout_callback_with_input(self): | STRING |
| LOW | tests/sh_test.py | 1665 | def test_stdout_callback_exit(self): | STRING |
| LOW | tests/sh_test.py | 1689 | def test_stdout_callback_terminate(self): | STRING |
| LOW | tests/sh_test.py | 1727 | def test_stdout_callback_kill(self): | STRING |
| LOW | tests/sh_test.py | 2411 | def test_change_stdout_buffering(self): | STRING |
| LOW | tests/sh_test.py | 2532 | def test_timeout_wait_overstep(self): | STRING |
| LOW | tests/sh_test.py | 2542 | def test_timeout_wait_negative(self): | STRING |
| LOW | tests/sh_test.py | 2580 | def test_failure_with_large_output(self): | STRING |
| LOW | tests/sh_test.py | 3640 | def test_signal_exception_aliases(self): | STRING |
| LOW | src/sh/__init__.py | 985 | def output_redirect_is_filename(out): | CODE |
| LOW | src/sh/__init__.py | 1056 | def output_iterator_validator(passed_kwargs, merged_kwargs): | CODE |
| 8 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dev_scripts/changelog_extract.py | 10 | CODE | |
| LOW | src/sh/__init__.py | 377 | CODE | |
| LOW | src/sh/__init__.py | 1580 | CODE | |
| LOW | src/sh/__init__.py | 1629 | CODE | |
| LOW | src/sh/__init__.py | 2655 | CODE | |
| LOW | src/sh/__init__.py | 2723 | CODE | |
| LOW | src/sh/__init__.py | 2792 | CODE | |
| LOW | src/sh/__init__.py | 3049 | CODE | |
| LOW | src/sh/__init__.py | 172 | CODE | |
| LOW | src/sh/__init__.py | 733 | CODE | |
| LOW | src/sh/__init__.py | 1861 | CODE | |
| LOW | src/sh/__init__.py | 3285 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/sh_test.py | 3465 | except Exception: | CODE |
| LOW | tests/sh_test.py | 3470 | except Exception: | CODE |
| MEDIUM | src/sh/__init__.py | 911 | def queue_connector(): | CODE |
| LOW | src/sh/__init__.py | 1710 | except Exception as e: | CODE |
| LOW | src/sh/__init__.py | 2196 | except Exception: # noqa: E722 | CODE |
| LOW | src/sh/__init__.py | 2203 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/sh/__init__.py | 6 | # =============================================================================== | COMMENT |
| MEDIUM | src/sh/__init__.py | 26 | # =============================================================================== | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/sh/__init__.py | 1 | """ | COMMENT |
| LOW | src/sh/__init__.py | 1181 | "out": None, # redirect STDOUT | COMMENT |
| LOW | src/sh/__init__.py | 1221 | "decode_errors": "strict", | COMMENT |
| LOW | src/sh/__init__.py | 1241 | # stdin ttys, if ttys are being used | COMMENT |
| LOW | src/sh/__init__.py | 1921 | COMMENT | |
| LOW | src/sh/__init__.py | 2061 | os.close(close_pipe_write) | COMMENT |
| LOW | src/sh/__init__.py | 2541 | """polls if our child process has completed, without blocking. this | COMMENT |
| LOW | src/sh/__init__.py | 3001 | # this can be demonstrated with the following program: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/sh/__init__.py | 1687 | # if it's true, it has no value, just pass the name | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/sh/__init__.py | 2085 | # python ignores SIGPIPE by default. we must make sure to put | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/sh_test.py | 799 | CODE |