Turn your Python application into an Android APK
This report summarizes scanner evidence for kivy/python-for-android, a Python project with 8,930 GitHub stars. SynthScan examined 33,434 lines of code across 325 source files, recording 344 pattern matches distributed across 16 syntactic categories. The legacy adjusted pattern signal is 13.4 (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? 296 finding(s).
Are imports declared and package/API names verified? 12 finding(s).
Are wrappers, guards, and repeated structures necessary? 258 finding(s).
Which changes need focused human review? 344 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 344 distinct pattern matches across 16 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 | testapps/on_device_unit_tests/test_app/tools.py | 19 | def run_test_suites_into_buffer(suites): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/tools.py | 26 | def get_images_with_extension(path=APP_PATH, extension='*.png'): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/tools.py | 63 | def get_failed_unittests_from(unittests_output, set_of_tests): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/tools.py | 72 | def skip_if_not_running_from_android_device(func): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/tools.py | 94 | def get_android_python_activity(): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/tools.py | 166 | def setup_lifecycle_callbacks(): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/app_flask.py | 39 | def get_html_for_tested_modules(tested_modules, failed_tests): | CODE |
| LOW | testapps/on_device_unit_tests/test_app/app_kivy.py | 103 | def set_color_for_tested_modules(self, restart=False): | STRING |
| LOW | testapps/on_device_unit_tests/test_app/app_kivy.py | 136 | def test_vibration_with_pyjnius(self, *args): | STRING |
| LOW | tests/test_toolchain.py | 14 | def patch_argparse_print_help(): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 28 | def test_is_mandatory_on_darwin(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 31 | def test_is_mandatory_on_linux(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 34 | def test_installer_is_supported_on_darwin(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 40 | def test_installer_is_supported_on_linux(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 46 | def test_darwin_pkg_config_location(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 49 | def test_linux_pkg_config_location(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 53 | def test_pkg_config_location_property__darwin(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 60 | def test_pkg_config_location_property__linux(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 351 | def test_checker_calls_linux_checker(self, mock_linux_checker): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 361 | def test_checker_calls_darwin_checker(self, mock_darwin_checker): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 370 | def test_checker_raises_on_unsupported_platform(self): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 382 | def test_ask_to_install_user_accepts(self, mock_input): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 392 | def test_ask_to_install_user_declines(self, mock_input): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 402 | def test_ask_to_install_non_interactive(self, mock_info): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 426 | def test_install_when_user_accepts_darwin(self, mock_installer, mock_ask): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 436 | def test_install_when_user_declines(self, mock_info, mock_ask): | CODE |
| LOW⚡ | tests/test_prerequisites.py | 445 | def test_install_is_supported(self): | CODE |
| LOW | tests/test_prerequisites.py | 132 | def test_darwin_pkg_config_location(self, _darwin_get_brew_formula_location_prefix): | CODE |
| LOW | tests/test_prerequisites.py | 279 | def test_default_darwin_prerequisites_set(self): | CODE |
| LOW | tests/test_prerequisites.py | 297 | def test_default_linux_prerequisites_set(self): | CODE |
| LOW | tests/test_prerequisites.py | 324 | def test_is_valid_when_not_met_non_mandatory(self, mock_checker, mock_warning): | CODE |
| LOW | tests/test_prerequisites.py | 338 | def test_is_valid_when_not_met_mandatory(self, mock_checker, mock_error): | CODE |
| LOW | tests/test_prerequisites.py | 415 | def test_install_when_user_accepts_linux(self, mock_info, mock_installer, mock_ask): | CODE |
| LOW | tests/test_prerequisites.py | 458 | def test_darwin_jdk_is_supported_valid_version(self, mock_exists, mock_popen): | CODE |
| LOW | tests/test_prerequisites.py | 474 | def test_darwin_jdk_is_supported_invalid_version(self, mock_exists, mock_popen): | CODE |
| LOW | tests/test_prerequisites.py | 488 | def test_darwin_jdk_is_supported_no_javac(self, mock_exists): | CODE |
| LOW | tests/test_prerequisites.py | 496 | def test_darwin_get_libexec_jdk_path(self, mock_run): | CODE |
| LOW | tests/test_prerequisites.py | 509 | def test_darwin_checker_uses_java_home_env(self, mock_is_supported): | CODE |
| LOW | tests/test_prerequisites.py | 523 | def test_darwin_get_brew_formula_location_prefix_success(self, mock_popen): | CODE |
| LOW | tests/test_prerequisites.py | 539 | def test_darwin_get_brew_formula_location_prefix_failure(self, mock_error, mock_popen): | CODE |
| LOW | tests/test_prerequisites.py | 552 | def test_darwin_get_brew_formula_location_prefix_with_installed_flag(self, mock_popen): | CODE |
| LOW | tests/test_prerequisites.py | 568 | def test_check_and_install_all_met(self, mock_get_prereqs): | CODE |
| LOW | tests/test_prerequisites.py | 589 | def test_check_and_install_some_not_met(self, mock_get_prereqs): | CODE |
| LOW | tests/test_pythonpackage_basic.py | 51 | def test__extract_info_from_package(): | CODE |
| LOW | tests/test_pythonpackage_basic.py | 86 | def test_get_dep_names_of_package(): | STRING |
| LOW | tests/test_pythonpackage_basic.py | 157 | def test_transform_dep_for_pip(): | STRING |
| LOW | tests/test_pythonpackage_basic.py | 195 | def test_parse_as_folder_reference(): | STRING |
| LOW | tests/test_pythonpackage_basic.py | 213 | def test_parse_as_folder_reference_edge_cases(input_ref, expected): | STRING |
| LOW | tests/test_pythonpackage_basic.py | 230 | def test_is_filesystem_path_edge_cases(path, expected): | CODE |
| LOW⚡ | tests/test_pythonpackage_basic.py | 241 | def test_transform_dep_for_pip_with_special_urls(input_dep, expected): | CODE |
| LOW⚡ | tests/test_pythonpackage_basic.py | 246 | def test_transform_dep_for_pip_passthrough(): | CODE |
| LOW⚡ | tests/test_pythonpackage_basic.py | 252 | def test_get_package_name_with_error(): | CODE |
| LOW⚡ | tests/test_pythonpackage_basic.py | 264 | def test_get_dep_names_error_handling(): | CODE |
| LOW⚡ | tests/test_pythonpackage_basic.py | 276 | def test_extract_info_from_package_missing_metadata(): | CODE |
| LOW | tests/test_pythonpackage_basic.py | 317 | def run__get_system_python_executable(self, pybin): | CODE |
| LOW | tests/test_patching.py | 42 | def test_is_platform_case_insensitive(self, mock_uname): | CODE |
| LOW | tests/test_patching.py | 49 | def test_is_platform_mismatch(self, mock_uname): | CODE |
| LOW | tests/test_kivy_bootstrap.py | 56 | def test_get_activity_reflects_the_configured_class(kivy_bootstrap): | CODE |
| LOW⚡ | tests/test_kivy_bootstrap.py | 72 | def test_get_activity_honours_custom_activity_class_name(): | CODE |
| LOW⚡ | tests/test_kivy_bootstrap.py | 81 | def test_get_activity_may_return_none(kivy_bootstrap): | CODE |
| 161 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | setup.py | 75 | except IOError: | CODE |
| HIGH | testapps/on_device_unit_tests/test_app/main.py | 68 | except AttributeError: | CODE |
| HIGH | tests/test_bdistapk.py | 143 | except SystemExit: | CODE |
| HIGH | pythonforandroid/bootstrap.py | 470 | except ValueError: | CODE |
| HIGH | pythonforandroid/build.py | 464 | except Exception: | CODE |
| HIGH | pythonforandroid/build.py | 470 | except ValueError: | CODE |
| HIGH | pythonforandroid/graph.py | 181 | except ValueError: | CODE |
| HIGH | pythonforandroid/logger.py | 115 | except (KeyError, ValueError): | CODE |
| HIGH | pythonforandroid/logger.py | 123 | except Exception: | CODE |
| HIGH | pythonforandroid/recipe.py | 495 | except (sh.ErrorReturnCode_1, sh.ErrorReturnCode_2): | CODE |
| HIGH | pythonforandroid/pythonpackage.py | 564 | except KeyError: | CODE |
| HIGH | …thonforandroid/recipes/android/src/android/activity.py | 213 | except KeyError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | distribute.sh | 3 | # This file is just a shim to report an error messaage if some tool | COMMENT |
| MEDIUM⚡ | tests/test_pythonpackage_basic.py | 268 | # Create a setup.py that will fail | COMMENT |
| MEDIUM | tests/test_util.py | 240 | # Create a test module file | COMMENT |
| MEDIUM | tests/test_logger.py | 142 | # Create a mock error with required attributes | COMMENT |
| MEDIUM | tests/test_logger.py | 187 | # Create a proper exception class that mimics sh.ErrorReturnCode | COMMENT |
| MEDIUM | doc/source/conf.py | 6 | # This file is execfile()d with the current directory set to its | COMMENT |
| MEDIUM | pythonforandroid/pythonpackage.py | 124 | # This function is required by get_package_as_folder() to work | COMMENT |
| MEDIUM | pythonforandroid/pythonpackage.py | 267 | # Create a venv to install into: | COMMENT |
| MEDIUM | pythonforandroid/recipes/hostpython3/__init__.py | 159 | # Create a subdirectory to actually perform the build | COMMENT |
| MEDIUM | pythonforandroid/recipes/hostpython3/__init__.py | 176 | # Create the Setup file. This copying from Setup.dist is | COMMENT |
| MEDIUM | pythonforandroid/recipes/python3/__init__.py | 360 | # Create a subdirectory to actually perform the build | COMMENT |
| MEDIUM | pythonforandroid/recipes/android/src/android/mixer.py | 1 | # This module is, as much a possible, a clone of the pygame | COMMENT |
| MEDIUM | pythonforandroid/recipes/librt/__init__.py | 23 | # Create a temporary folder to add to link path with a fake librt.so: | COMMENT |
| MEDIUM | pythonforandroid/recipes/libpthread/__init__.py | 22 | # Create a temporary folder to add to link path with a fake libpthread.so: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_checkdependencies.py | 11 | CODE | |
| LOW | tests/test_checkdependencies.py | 21 | CODE | |
| LOW | pythonforandroid/bootstrap.py | 20 | CODE | |
| LOW | pythonforandroid/bootstrap.py | 453 | CODE | |
| LOW | pythonforandroid/bootstrap.py | 112 | CODE | |
| LOW | pythonforandroid/bootstrap.py | 251 | CODE | |
| LOW | pythonforandroid/build.py | 899 | CODE | |
| LOW | pythonforandroid/bdistapk.py | 35 | CODE | |
| LOW | pythonforandroid/util.py | 51 | CODE | |
| LOW | pythonforandroid/graph.py | 146 | CODE | |
| LOW | pythonforandroid/logger.py | 131 | CODE | |
| LOW | pythonforandroid/toolchain.py | 198 | CODE | |
| LOW | pythonforandroid/toolchain.py | 942 | CODE | |
| LOW | pythonforandroid/toolchain.py | 1002 | CODE | |
| LOW | pythonforandroid/toolchain.py | 1073 | CODE | |
| LOW | pythonforandroid/recipe.py | 206 | CODE | |
| LOW | pythonforandroid/recipe.py | 324 | CODE | |
| LOW | pythonforandroid/recipe.py | 394 | CODE | |
| LOW | pythonforandroid/recipe.py | 459 | CODE | |
| LOW | pythonforandroid/recipe.py | 557 | CODE | |
| LOW | pythonforandroid/recipe.py | 710 | CODE | |
| LOW | pythonforandroid/recipe.py | 722 | CODE | |
| LOW | pythonforandroid/recipe.py | 1558 | CODE | |
| LOW | pythonforandroid/pythonpackage.py | 120 | CODE | |
| LOW | pythonforandroid/pythonpackage.py | 246 | CODE | |
| LOW | pythonforandroid/pythonpackage.py | 381 | CODE | |
| LOW | pythonforandroid/pythonpackage.py | 472 | CODE | |
| LOW | pythonforandroid/pythonpackage.py | 571 | CODE | |
| LOW | …thonforandroid/recipes/android/src/android/activity.py | 38 | CODE | |
| LOW | …thonforandroid/recipes/android/src/android/activity.py | 52 | CODE | |
| LOW | pythonforandroid/recipes/reportlab/__init__.py | 15 | CODE | |
| LOW | pythonforandroid/recipes/scipy/wrapper.py | 32 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | testapps/on_device_unit_tests/test_app/app_kivy.py | 161 | except Exception as err: | STRING |
| LOW | pythonforandroid/build.py | 464 | except Exception: | CODE |
| LOW | pythonforandroid/build.py | 591 | except Exception: | CODE |
| LOW | pythonforandroid/build.py | 613 | except Exception: | CODE |
| LOW | pythonforandroid/build.py | 644 | except Exception as e: | CODE |
| LOW | pythonforandroid/build.py | 651 | except Exception: | CODE |
| LOW | pythonforandroid/logger.py | 123 | except Exception: | CODE |
| MEDIUM | pythonforandroid/logger.py | 112 | def get_console_width(): | CODE |
| LOW | pythonforandroid/recipe.py | 201 | except Exception as ex: | CODE |
| LOW | pythonforandroid/recipe.py | 1298 | except Exception: | CODE |
| LOW | pythonforandroid/recipe.py | 1356 | except Exception: | CODE |
| LOW | pythonforandroid/recipe.py | 1494 | except Exception: | CODE |
| LOW | …randroid/recipes/android/src/android/display_cutout.py | 67 | except Exception as e: | CODE |
| LOW | …randroid/recipes/android/src/android/display_cutout.py | 91 | except Exception: | CODE |
| LOW | …randroid/recipes/android/src/android/display_cutout.py | 103 | except Exception: | CODE |
| LOW | …randroid/recipes/android/src/android/display_cutout.py | 126 | except Exception: | CODE |
| LOW | …randroid/recipes/android/src/android/display_cutout.py | 227 | except Exception: | CODE |
| MEDIUM | …randroid/recipes/android/src/android/display_cutout.py | 55 | def onApplyWindowInsets(self, view, insets): | CODE |
| LOW | …honforandroid/recipes/android/src/android/broadcast.py | 88 | except Exception as e: | CODE |
| MEDIUM | …honforandroid/recipes/android/src/android/broadcast.py | 84 | def stop(self): | CODE |
| LOW | pythonforandroid/recipes/primp/__init__.py | 27 | except Exception: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | doc/source/conf.py | 1 | # -*- coding: utf-8 -*- | COMMENT |
| LOW | doc/source/conf.py | 81 | version = get_version() | COMMENT |
| LOW | doc/source/conf.py | 101 | COMMENT | |
| LOW | doc/source/conf.py | 121 | #modindex_common_prefix = [] | COMMENT |
| LOW | doc/source/conf.py | 141 | # Add any paths that contain custom themes here, relative to this directory. | COMMENT |
| LOW | doc/source/conf.py | 161 | # relative to this directory. They are copied after the builtin static files, | COMMENT |
| LOW | doc/source/conf.py | 181 | # Additional templates that should be rendered to pages, maps page names to | COMMENT |
| LOW | doc/source/conf.py | 201 | #html_show_copyright = True | COMMENT |
| LOW | doc/source/conf.py | 221 | # The name of a javascript file (relative to the configuration directory) that | COMMENT |
| LOW | doc/source/conf.py | 241 | #'figure_align': 'htbp', | COMMENT |
| LOW | …onforandroid/includes/arm64-v8a/machine/cpu-features.h | 1 | #ifndef _ARM64_CPU_FEATURES | COMMENT |
| LOW | pythonforandroid/recipes/tflite-runtime/__init__.py | 1 | from pythonforandroid.recipe import PythonRecipe, current_directory, \ | COMMENT |
| LOW | pythonforandroid/recipes/libxml2/glob.c | 81 | #include <dirent.h> | COMMENT |
| LOW | pythonforandroid/recipes/libxml2/glob.c | 101 | #define RBRACKET ']' | COMMENT |
| LOW | pythonforandroid/recipes/libxml2/glob.c | 121 | COMMENT | |
| LOW | pythonforandroid/recipes/libxml2/glob.h | 81 | #define GLOB_ABORTED (-2) /* Unignored error. */ | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | testapps/testapp_vispy/main.py | 14 | CODE | |
| LOW | …_device_unit_tests/test_app/tests/test_requirements.py | 20 | CODE | |
| LOW | …_device_unit_tests/test_app/tests/test_requirements.py | 61 | CODE | |
| LOW | …_device_unit_tests/test_app/tests/test_requirements.py | 117 | CODE | |
| LOW | …_device_unit_tests/test_app/tests/test_requirements.py | 276 | CODE | |
| LOW | …_device_unit_tests/test_app/tests/test_requirements.py | 287 | CODE | |
| LOW | testapps/testapp_sqlite_openssl/main.py | 30 | CODE | |
| LOW | testapps/testapp_sqlite_openssl/main.py | 32 | CODE | |
| LOW | testapps/testapp_sqlite_openssl/main.py | 47 | CODE | |
| LOW | tests/test_pythonpackage_basic.py | 52 | CODE | |
| LOW | …thonforandroid/recipes/android/src/android/__init__.py | 8 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | ci/run_emulator_tests.sh | 34 | # Check if all success patterns are present | COMMENT |
| LOW | pythonforandroid/bootstrap.py | 266 | # Check if the bootstap's dependencies have an internal conflict: | COMMENT |
| LOW | pythonforandroid/bootstrap.py | 272 | # Check if bootstrap's dependencies conflict with chosen | COMMENT |
| LOW | pythonforandroid/build.py | 741 | # Output messages about what we're going to do: | COMMENT |
| LOW | pythonforandroid/pythonpackage.py | 462 | # Check if this is either not an url, or a file URL: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pythonforandroid/toolchain.py | 1067 | # WARNING: We should make sure to clean the build directory before building. | COMMENT |
| LOW | pythonforandroid/recipe.py | 879 | .. warning:: don't forget to call `super().__init__()` in any recipe's | STRING |
| LOW | pythonforandroid/recipes/freetype/__init__.py | 128 | # we will make sure to only install it when the library exists | COMMENT |
| LOW | pythonforandroid/recipes/librt/__init__.py | 31 | # Set symlinks, and make sure to update them on every build run: | COMMENT |
| LOW | pythonforandroid/recipes/libpthread/__init__.py | 30 | # Set symlinks, and make sure to update them on every build run: | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | …nforandroid/recipes/android/src/android/permissions.py | 564 | Requests Android permissions. Args: permissions (str): A list of permissions to requests (str) call | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | …honforandroid/recipes/android/src/android/broadcast.py | 1 | # ------------------------------------------------------------------- | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | pythonforandroid/recipes/pyzmq/__init__.py | 50 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_pythonpackage_basic.py | 45 | Lorem Ipsum""" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_util.py | 244 | f.write("def test_function():\n") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …nforandroid/recipes/android/src/android/permissions.py | 621 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …thonforandroid/recipes/android/src/android/darkmode.py | 35 | def set_dark_mode_listener(on_dark_mode_changed: Callable[[bool], None] | None) -> None: | CODE |