a3-python 0.1.19__tar.gz → 0.1.21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. {a3_python-0.1.19 → a3_python-0.1.21}/PKG-INFO +1 -1
  2. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/analyzer.py +30 -15
  3. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/templates/a3-pr-scan.yml +1 -0
  4. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/templates/a3-scheduled-scan.yml +1 -0
  5. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/symbolic_vm.py +79 -70
  6. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/values.py +90 -14
  7. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/PKG-INFO +1 -1
  8. {a3_python-0.1.19 → a3_python-0.1.21}/pyproject.toml +1 -1
  9. {a3_python-0.1.19 → a3_python-0.1.21}/MANIFEST.in +0 -0
  10. {a3_python-0.1.19 → a3_python-0.1.21}/README.md +0 -0
  11. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/__init__.py +0 -0
  12. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/__main__.py +0 -0
  13. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/__init__.py +0 -0
  14. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/abstraction.py +0 -0
  15. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/advanced.py +0 -0
  16. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/assume_guarantee.py +0 -0
  17. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/bayesian_fp_scorer.py +0 -0
  18. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/boolean_programs.py +0 -0
  19. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/cegar_refinement.py +0 -0
  20. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/cegis.py +0 -0
  21. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/certificate_core.py +0 -0
  22. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/context_aware_verification.py +0 -0
  23. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/deep_barrier_theory.py +0 -0
  24. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/dsos_sdsos.py +0 -0
  25. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/enhanced_barrier_theory.py +0 -0
  26. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/extreme_verification.py +0 -0
  27. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/fast_barrier_filters.py +0 -0
  28. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/foundations.py +0 -0
  29. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/guard_to_barrier.py +0 -0
  30. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/houdini.py +0 -0
  31. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/hscc2004.py +0 -0
  32. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/hybrid_barrier.py +0 -0
  33. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/ic3_pdr.py +0 -0
  34. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/ice.py +0 -0
  35. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/ice_learning.py +0 -0
  36. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/impact_lazy.py +0 -0
  37. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/int_bmc.py +0 -0
  38. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/interpolation_imc.py +0 -0
  39. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/invariants.py +0 -0
  40. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/kitchensink_taxonomy.py +0 -0
  41. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/lasserre_hierarchy.py +0 -0
  42. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/learned_invariants.py +0 -0
  43. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/learning.py +0 -0
  44. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/papers_11_to_15_complete.py +0 -0
  45. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/papers_16_to_20_complete.py +0 -0
  46. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/papers_1_to_5_complete.py +0 -0
  47. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/papers_6_to_10_complete.py +0 -0
  48. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/parrilo_sos_sdp.py +0 -0
  49. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/path_validation.py +0 -0
  50. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/pdr_spacer.py +0 -0
  51. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/positivstellensatz.py +0 -0
  52. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/predicate_abstraction.py +0 -0
  53. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/program_analysis.py +0 -0
  54. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/quick_precheck.py +0 -0
  55. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/ranking.py +0 -0
  56. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/ranking_synthesis.py +0 -0
  57. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sos_safety.py +0 -0
  58. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sos_toolbox.py +0 -0
  59. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sos_unified.py +0 -0
  60. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sostools.py +0 -0
  61. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/spacer_chc.py +0 -0
  62. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sparse_sos.py +0 -0
  63. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/step_relation.py +0 -0
  64. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/stochastic_barrier.py +0 -0
  65. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/sygus_synthesis.py +0 -0
  66. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/synthesis.py +0 -0
  67. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/synthesis_engine.py +0 -0
  68. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/templates.py +0 -0
  69. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/type_inference_verification.py +0 -0
  70. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/barriers/unified_sota_912.py +0 -0
  71. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/__init__.py +0 -0
  72. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/affine_loop_model.py +0 -0
  73. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/call_graph.py +0 -0
  74. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/control_flow.py +0 -0
  75. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/dataflow.py +0 -0
  76. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cfg/loop_analysis.py +0 -0
  77. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/__init__.py +0 -0
  78. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/agentic_triage.py +0 -0
  79. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/baseline.py +0 -0
  80. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/config.py +0 -0
  81. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/init_cmd.py +0 -0
  82. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/sarif.py +0 -0
  83. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/ci/triage.py +0 -0
  84. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/cli.py +0 -0
  85. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/confidence_interval.py +0 -0
  86. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/confidence_scoring.py +0 -0
  87. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/__init__.py +0 -0
  88. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/__init__.py +0 -0
  89. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/abstract_values.py +0 -0
  90. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/contracts.py +0 -0
  91. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/deferred.py +0 -0
  92. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/device_analyzer.py +0 -0
  93. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/intervals.py +0 -0
  94. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/__init__.py +0 -0
  95. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/accelerators.py +0 -0
  96. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/amp.py +0 -0
  97. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/autograd.py +0 -0
  98. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/backends.py +0 -0
  99. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/core.py +0 -0
  100. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/cuda.py +0 -0
  101. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/data.py +0 -0
  102. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/distributed.py +0 -0
  103. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/distributions.py +0 -0
  104. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/experimental.py +0 -0
  105. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/export_compile.py +0 -0
  106. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/fft.py +0 -0
  107. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/hub_package.py +0 -0
  108. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/jit.py +0 -0
  109. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/linalg.py +0 -0
  110. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/nn_functional.py +0 -0
  111. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/nn_modules.py +0 -0
  112. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/onnx.py +0 -0
  113. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/optim.py +0 -0
  114. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/profiler.py +0 -0
  115. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/quantization.py +0 -0
  116. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/registry.py +0 -0
  117. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/sparse.py +0 -0
  118. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/special.py +0 -0
  119. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/tensor.py +0 -0
  120. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/barriers/torch/utils.py +0 -0
  121. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/base.py +0 -0
  122. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/builtin_relations.py +0 -0
  123. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/checker.py +0 -0
  124. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/relations.py +0 -0
  125. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/schema.py +0 -0
  126. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/security.py +0 -0
  127. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/security_lattice.py +0 -0
  128. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/stdlib.py +0 -0
  129. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/stdlib_module_relations.py +0 -0
  130. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/stdlib_stubs.py +0 -0
  131. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/contracts/torch_contracts.py +0 -0
  132. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/__init__.py +0 -0
  133. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/concolic.py +0 -0
  134. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/constraint_solver.py +0 -0
  135. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/hybrid.py +0 -0
  136. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/lockstep.py +0 -0
  137. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/path_condition.py +0 -0
  138. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/selective_concolic.py +0 -0
  139. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/stochastic_replay.py +0 -0
  140. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/dse/value_flow.py +0 -0
  141. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/evaluation/__init__.py +0 -0
  142. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/evaluation/deduplication.py +0 -0
  143. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/evaluation/repo_list.py +0 -0
  144. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/evaluation/scanner.py +0 -0
  145. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/fp_context.py +0 -0
  146. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/frontend/__init__.py +0 -0
  147. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/frontend/entry_points.py +0 -0
  148. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/frontend/loader.py +0 -0
  149. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/__init__.py +0 -0
  150. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/ast_guard_analysis.py +0 -0
  151. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/bmc.py +0 -0
  152. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/bytecode_summaries.py +0 -0
  153. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/concrete_vm.py +0 -0
  154. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/crash_summaries.py +0 -0
  155. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/framework_mocks.py +0 -0
  156. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/intent_detector.py +0 -0
  157. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/interprocedural_barriers.py +0 -0
  158. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/interprocedural_bugs.py +0 -0
  159. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/interprocedural_guards.py +0 -0
  160. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/interprocedural_taint.py +0 -0
  161. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/intraprocedural_taint.py +0 -0
  162. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/invariant_integration.py +0 -0
  163. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/oracles.py +0 -0
  164. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/security_tracker.py +0 -0
  165. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/security_tracker_lattice.py +0 -0
  166. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/sota_interprocedural.py +0 -0
  167. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/sota_intraprocedural.py +0 -0
  168. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/state.py +0 -0
  169. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/summaries.py +0 -0
  170. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/semantics/termination_integration.py +0 -0
  171. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/stochastic_risk.py +0 -0
  172. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/__init__.py +0 -0
  173. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/assert_fail.py +0 -0
  174. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/bounds.py +0 -0
  175. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/collection_bugs.py +0 -0
  176. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/data_race.py +0 -0
  177. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/deadlock.py +0 -0
  178. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/div_zero.py +0 -0
  179. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/double_free.py +0 -0
  180. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/exception_bugs.py +0 -0
  181. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/fp_domain.py +0 -0
  182. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/info_leak.py +0 -0
  183. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/integer_overflow.py +0 -0
  184. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/iterator_invalid.py +0 -0
  185. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/memory_leak.py +0 -0
  186. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/non_termination.py +0 -0
  187. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/null_ptr.py +0 -0
  188. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/panic.py +0 -0
  189. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/registry.py +0 -0
  190. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/__init__.py +0 -0
  191. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/cleartext.py +0 -0
  192. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/code_injection.py +0 -0
  193. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/command_injection.py +0 -0
  194. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/config.py +0 -0
  195. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/crypto.py +0 -0
  196. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/deserialization.py +0 -0
  197. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/filesystem.py +0 -0
  198. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/injection.py +0 -0
  199. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/lattice_detectors.py +0 -0
  200. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/path_injection.py +0 -0
  201. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/regex.py +0 -0
  202. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/sql_injection.py +0 -0
  203. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/ssrf.py +0 -0
  204. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/webapp.py +0 -0
  205. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/xml.py +0 -0
  206. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/xss.py +0 -0
  207. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/security/xxe.py +0 -0
  208. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/send_sync.py +0 -0
  209. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/stack_overflow.py +0 -0
  210. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/timing_channel.py +0 -0
  211. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/type_confusion.py +0 -0
  212. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/uninit_memory.py +0 -0
  213. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/unsafe/use_after_free.py +0 -0
  214. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/__init__.py +0 -0
  215. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/heap.py +0 -0
  216. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/taint.py +0 -0
  217. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/taint_lattice.py +0 -0
  218. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python/z3model/type_tracking.py +0 -0
  219. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/SOURCES.txt +0 -0
  220. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/dependency_links.txt +0 -0
  221. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/entry_points.txt +0 -0
  222. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/requires.txt +0 -0
  223. {a3_python-0.1.19 → a3_python-0.1.21}/a3_python.egg-info/top_level.txt +0 -0
  224. {a3_python-0.1.19 → a3_python-0.1.21}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: a3-python
3
- Version: 0.1.19
3
+ Version: 0.1.21
4
4
  Summary: Catch real Python bugs before production — 99%+ accuracy, Z3 symbolic execution, LLM-powered false-positive filtering, zero-config GitHub CI
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -2135,13 +2135,13 @@ class Analyzer:
2135
2135
  var_names = list(loop.modified_variables)
2136
2136
 
2137
2137
  # Generate sample positive/negative examples
2138
- positive = set()
2139
- negative = set()
2138
+ positive = []
2139
+ negative = []
2140
2140
  implications = []
2141
2141
 
2142
2142
  # Add simple examples (heuristic)
2143
- positive.add({v: 0 for v in var_names})
2144
- positive.add({v: 1 for v in var_names})
2143
+ positive.append({v: 0 for v in var_names})
2144
+ positive.append({v: 1 for v in var_names})
2145
2145
 
2146
2146
  # Try ICE learning
2147
2147
  result = learn_ice_invariant(
@@ -2912,13 +2912,19 @@ class Analyzer:
2912
2912
  # Compile the module
2913
2913
  module_code = compile(source, str(filepath), 'exec')
2914
2914
 
2915
- # Search for the function in the module's constants
2916
- for const in module_code.co_consts:
2917
- if isinstance(const, types.CodeType):
2918
- if const.co_name == function_name:
2919
- return const
2915
+ # Search recursively for the function (handles class methods too)
2916
+ def _find_code(code_obj, name):
2917
+ for const in code_obj.co_consts:
2918
+ if isinstance(const, types.CodeType):
2919
+ if const.co_name == name:
2920
+ return const
2921
+ # Recurse into nested code objects (e.g., class bodies)
2922
+ result = _find_code(const, name)
2923
+ if result is not None:
2924
+ return result
2925
+ return None
2920
2926
 
2921
- return None
2927
+ return _find_code(module_code, function_name)
2922
2928
  except Exception as e:
2923
2929
  if self.verbose:
2924
2930
  print(f" Error extracting function {function_name}: {e}")
@@ -2974,8 +2980,9 @@ class Analyzer:
2974
2980
  if isinstance(const, types.CodeType):
2975
2981
  name = const.co_name
2976
2982
 
2977
- # Skip module-level code, lambdas, and comprehensions
2978
- if name in ('<module>', '<lambda>', '<listcomp>', '<dictcomp>', '<setcomp>', '<genexpr>'):
2983
+ # Skip module-level code, lambdas, comprehensions, and
2984
+ # compiler-generated annotation functions (Python 3.14+ PEP 649)
2985
+ if name in ('<module>', '<lambda>', '<listcomp>', '<dictcomp>', '<setcomp>', '<genexpr>', '__annotate__'):
2979
2986
  continue
2980
2987
 
2981
2988
  # Check if this is a class definition
@@ -3050,8 +3057,9 @@ class Analyzer:
3050
3057
  all_functions = {}
3051
3058
  for const in module_code.co_consts:
3052
3059
  if isinstance(const, types.CodeType):
3053
- # Skip module-level code and lambdas
3054
- if const.co_name == '<module>' or const.co_name == '<lambda>':
3060
+ # Skip module-level code, lambdas, and compiler-generated
3061
+ # annotation functions (Python 3.14+ PEP 649)
3062
+ if const.co_name in ('<module>', '<lambda>', '__annotate__'):
3055
3063
  continue
3056
3064
  # Skip class definitions (they have __classdict__ in cellvars)
3057
3065
  if '__classdict__' in const.co_cellvars:
@@ -3061,7 +3069,14 @@ class Analyzer:
3061
3069
  # Check if there's any executable code at module level beyond function definitions
3062
3070
  import dis
3063
3071
  has_executable_code = False
3064
- function_def_opcodes = {'MAKE_FUNCTION', 'STORE_NAME', 'STORE_GLOBAL', 'LOAD_CONST'}
3072
+ # Include annotation bookkeeping opcodes (Python 3.14 PEP 649) so that
3073
+ # annotation-only modules are still correctly classified as library files
3074
+ function_def_opcodes = {
3075
+ 'MAKE_FUNCTION', 'STORE_NAME', 'STORE_GLOBAL', 'LOAD_CONST',
3076
+ 'MAKE_CELL', 'BUILD_SET', 'SET_ADD', 'POP_TOP',
3077
+ 'SET_FUNCTION_ATTRIBUTE', 'LOAD_SMALL_INT', 'LOAD_NAME',
3078
+ 'PUSH_NULL', 'SETUP_ANNOTATIONS',
3079
+ }
3065
3080
 
3066
3081
  for instr in dis.get_instructions(module_code):
3067
3082
  # Skip RESUME and function definition opcodes
@@ -25,6 +25,7 @@ permissions:
25
25
  contents: read
26
26
  security-events: write # needed for SARIF upload
27
27
  pull-requests: write # needed for PR comments (optional)
28
+ models: read # needed for GitHub Models (agentic triage)
28
29
 
29
30
  jobs:
30
31
  a3-scan:
@@ -20,6 +20,7 @@ permissions:
20
20
  contents: write # needed to update baseline
21
21
  security-events: write # needed for SARIF upload
22
22
  issues: write # needed for auto-issue creation
23
+ models: read # needed for GitHub Models (agentic triage)
23
24
 
24
25
  jobs:
25
26
  a3-full-scan:
@@ -511,6 +511,12 @@ class SymbolicVM:
511
511
  self.verbose = verbose
512
512
  self.paths: List[SymbolicPath] = []
513
513
  self._instruction_cache: Dict[int, Dict[int, dis.Instruction]] = {}
514
+ self._obj_id_counter = 0
515
+
516
+ def fresh_obj_id(self) -> int:
517
+ """Return a fresh unique object ID for heap allocation."""
518
+ self._obj_id_counter += 1
519
+ return self._obj_id_counter
514
520
 
515
521
  def _solver_maybe_sat(self) -> bool:
516
522
  """
@@ -772,7 +778,16 @@ class SymbolicVM:
772
778
  print(f" Top of stack: {frame.operand_stack[-3:]}")
773
779
 
774
780
  # Increment per-instruction step counter (k-step reachability index).
775
- state.step_count += 1
781
+ # Skip counting annotation bookkeeping instructions (Python 3.14 PEP 649)
782
+ # so that type annotations don't consume the exploration budget.
783
+ _annotation_bookkeeping = (
784
+ instruction.opname == 'SETUP_ANNOTATIONS'
785
+ or (instruction.opname == 'STORE_NAME' and instruction.argval in ('__annotate__', '__annotations__', '__conditional_annotations__'))
786
+ or (instruction.opname == 'LOAD_NAME' and instruction.argval == '__conditional_annotations__')
787
+ or (instruction.opname == 'SET_ADD' and hasattr(frame, 'locals') and '__conditional_annotations__' in getattr(frame, 'locals', {}))
788
+ )
789
+ if not _annotation_bookkeeping:
790
+ state.step_count += 1
776
791
 
777
792
  self._execute_instruction(state, frame, instruction)
778
793
  path.trace.append(f"{instruction.offset:4d}: {instruction.opname} {instruction.argrepr}")
@@ -1201,13 +1216,13 @@ class SymbolicVM:
1201
1216
  """Compute the next instruction offset."""
1202
1217
  code = frame.code
1203
1218
 
1204
- # Cache instructions to avoid repeated dis.get_instructions calls
1205
- # This is a major performance optimization for large functions
1219
+ # Use a SEPARATE cache from _instruction_cache (which stores offset→Instruction).
1220
+ # This cache stores offset→next_offset (int→int).
1206
1221
  cache_key = id(code)
1207
- if not hasattr(self, '_instruction_cache'):
1208
- self._instruction_cache = {}
1222
+ if not hasattr(self, '_next_offset_cache'):
1223
+ self._next_offset_cache = {}
1209
1224
 
1210
- if cache_key not in self._instruction_cache:
1225
+ if cache_key not in self._next_offset_cache:
1211
1226
  instructions = list(dis.get_instructions(code))
1212
1227
  # Build offset -> next_offset map for O(1) lookup
1213
1228
  offset_map = {}
@@ -1216,9 +1231,9 @@ class SymbolicVM:
1216
1231
  offset_map[inst.offset] = instructions[i + 1].offset
1217
1232
  else:
1218
1233
  offset_map[inst.offset] = len(code.co_code)
1219
- self._instruction_cache[cache_key] = offset_map
1234
+ self._next_offset_cache[cache_key] = offset_map
1220
1235
 
1221
- offset_map = self._instruction_cache[cache_key]
1236
+ offset_map = self._next_offset_cache[cache_key]
1222
1237
  return offset_map.get(instr.offset, instr.offset + 2)
1223
1238
 
1224
1239
  def _set_security_detection_flag(self, state: SymbolicMachineState, violation):
@@ -2744,8 +2759,9 @@ class SymbolicVM:
2744
2759
  return
2745
2760
  self.solver.pop()
2746
2761
 
2747
- # Unpack elements and push onto stack (in order)
2748
- for i in range(count):
2762
+ # Unpack elements and push onto stack in REVERSE order
2763
+ # so that TOS = first element (matches CPython semantics)
2764
+ for i in range(count - 1, -1, -1):
2749
2765
  if i in seq_obj.elements:
2750
2766
  frame.operand_stack.append(seq_obj.elements[i])
2751
2767
  else:
@@ -2754,12 +2770,12 @@ class SymbolicVM:
2754
2770
  frame.operand_stack.append(sym_elem)
2755
2771
  else:
2756
2772
  # Sequence object not found in heap - create symbolic unpacked values
2757
- for i in range(count):
2773
+ for i in range(count - 1, -1, -1):
2758
2774
  sym_elem = SymbolicValue(ValueTag.OBJ, z3.Int(f"unpack_unknown_{id(seq)}_{i}"))
2759
2775
  frame.operand_stack.append(sym_elem)
2760
2776
  else:
2761
2777
  # Could not extract concrete obj_id - create symbolic unpacked values
2762
- for i in range(count):
2778
+ for i in range(count - 1, -1, -1):
2763
2779
  sym_elem = SymbolicValue(ValueTag.OBJ, z3.Int(f"unpack_symbolic_{id(seq)}_{i}"))
2764
2780
  frame.operand_stack.append(sym_elem)
2765
2781
 
@@ -2767,15 +2783,16 @@ class SymbolicVM:
2767
2783
 
2768
2784
  elif opname == "STORE_SUBSCR":
2769
2785
  # STORE_SUBSCR: Implements container[index] = value
2770
- # Stack: value, container, index → (empty)
2771
- # Pops all three and stores value at container[index]
2786
+ # CPython stack layout: [..., value, container, index]
2787
+ # TOS=index, TOS1=container, TOS2=value
2788
+ # Pop order: index, container, value
2772
2789
  if len(frame.operand_stack) < 3:
2773
2790
  state.exception = "StackUnderflow"
2774
2791
  return
2775
2792
 
2776
- value = frame.operand_stack.pop()
2777
- container = frame.operand_stack.pop()
2778
- index = frame.operand_stack.pop()
2793
+ index = frame.operand_stack.pop() # TOS
2794
+ container = frame.operand_stack.pop() # TOS1
2795
+ value = frame.operand_stack.pop() # TOS2
2779
2796
 
2780
2797
  # Check for None misuse on container
2781
2798
  self.solver.push()
@@ -5515,11 +5532,16 @@ class SymbolicVM:
5515
5532
  # argval is an index into a common constants table
5516
5533
  const_repr = instr.argrepr
5517
5534
 
5518
- # Common exception types
5535
+ # Common exception types (including NotImplementedError which is
5536
+ # raised by compiler-generated __annotate__ functions in Python 3.14+)
5519
5537
  exception_types = ['AssertionError', 'RuntimeError', 'ValueError', 'TypeError',
5520
5538
  'KeyError', 'IndexError', 'AttributeError', 'ImportError',
5521
5539
  'NameError', 'OSError', 'IOError', 'ZeroDivisionError',
5522
5540
  'StopIteration', 'StopAsyncIteration', 'SystemExit',
5541
+ 'NotImplementedError', 'OverflowError', 'UnicodeError',
5542
+ 'UnicodeDecodeError', 'UnicodeEncodeError',
5543
+ 'FileNotFoundError', 'PermissionError', 'TimeoutError',
5544
+ 'ConnectionError', 'BrokenPipeError',
5523
5545
  'BaseException', 'Exception']
5524
5546
 
5525
5547
  matched_exc = None
@@ -5913,66 +5935,52 @@ class SymbolicVM:
5913
5935
  state.exception = "InfeasiblePath"
5914
5936
 
5915
5937
  elif opname == "PUSH_EXC_INFO":
5916
- # Push exception info onto stack when entering exception handler
5917
- # In Python 3.11+, this pushes the current exception onto the stack
5918
- # Stack layout: [exc_type, exc_value, exc_traceback, ...]
5938
+ # Python 3.11+: PUSH_EXC_INFO pushes (prev_exc, new_exc)
5939
+ # prev_exc is the previous exception (None if none), new_exc is the current one
5919
5940
  if state.exception:
5920
- # Push exception info as symbolic values
5921
- # Look up the exception type from builtins to get the correct payload
5941
+ # Create the exception value with proper type info
5922
5942
  if state.exception in frame.builtins:
5923
- exc_type_val = frame.builtins[state.exception]
5943
+ exc_val = frame.builtins[state.exception]
5924
5944
  else:
5925
- # Unknown exception, use a generic marker
5926
- exc_type_val = SymbolicValue(ValueTag.OBJ, z3.IntVal(-2))
5927
- exc_type_val._exception_type = state.exception
5945
+ exc_val = SymbolicValue(ValueTag.OBJ, z3.IntVal(-2))
5946
+ exc_val._exception_type = state.exception
5928
5947
 
5929
- exc_value_val = state.exception_value if state.exception_value else SymbolicValue.none()
5930
- exc_tb_val = SymbolicValue.none() # Traceback representation (simplified)
5931
-
5932
- frame.operand_stack.extend([exc_type_val, exc_value_val, exc_tb_val])
5948
+ # Push prev_exc (simplified as None) and current exception
5949
+ prev_exc = SymbolicValue.none()
5950
+ frame.operand_stack.append(prev_exc) # prev_exc
5951
+ frame.operand_stack.append(exc_val) # current exc
5933
5952
  frame.instruction_offset = self._next_offset(frame, instr)
5934
5953
 
5935
5954
  elif opname == "CHECK_EXC_MATCH":
5936
- # Check if TOS (exception type to match) matches current exception
5937
- # Stack before: [..., exc_type, exc_value, exc_tb, match_type]
5938
- # Stack after: [..., exc_type, exc_value, exc_tb, match_result]
5939
- if len(frame.operand_stack) < 4:
5955
+ # Python 3.11+: CHECK_EXC_MATCH (left, right -- left, bool)
5956
+ # TOS = match_type (exception class to compare against)
5957
+ # TOS1 = exc_value (stays on stack)
5958
+ # Result: pushes boolean indicating whether exc matches match_type
5959
+ if len(frame.operand_stack) < 2:
5940
5960
  state.exception = "StackUnderflow"
5941
5961
  return
5942
5962
 
5943
- match_type = frame.operand_stack.pop()
5944
- # exc_tb, exc_value, exc_type are still on stack
5945
-
5946
- # Get the actual exception from the stack
5947
- if len(frame.operand_stack) >= 3:
5948
- exc_tb = frame.operand_stack[-1]
5949
- exc_value = frame.operand_stack[-2]
5950
- exc_type = frame.operand_stack[-3]
5951
-
5952
- # Check if exception matches by comparing payloads symbolically
5953
- # Exception types are represented as OBJ with distinct payload values
5954
- if exc_type.tag == ValueTag.OBJ and match_type.tag == ValueTag.OBJ:
5955
- # Create symbolic comparison: exc_type.payload == match_type.payload
5956
- matches_expr = exc_type.payload == match_type.payload
5957
- match_result = SymbolicValue(ValueTag.BOOL, matches_expr)
5958
- frame.operand_stack.append(match_result)
5959
- else:
5960
- # Unknown types, conservatively assume it could match
5961
- frame.operand_stack.append(SymbolicValue.bool(True))
5963
+ match_type = frame.operand_stack.pop() # TOS = type to match
5964
+ exc_value = frame.operand_stack[-1] # TOS1 = stays on stack
5965
+
5966
+ # Compare exception types
5967
+ if hasattr(exc_value, '_exception_type') and hasattr(match_type, '_exception_type'):
5968
+ matches = exc_value._exception_type == match_type._exception_type
5969
+ frame.operand_stack.append(SymbolicValue.bool(matches))
5970
+ elif exc_value.tag == ValueTag.OBJ and match_type.tag == ValueTag.OBJ:
5971
+ matches_expr = exc_value.payload == match_type.payload
5972
+ match_result = SymbolicValue(ValueTag.BOOL, matches_expr)
5973
+ frame.operand_stack.append(match_result)
5962
5974
  else:
5963
- state.exception = "StackUnderflow"
5964
- return
5975
+ # Unknown types, conservatively assume it could match
5976
+ frame.operand_stack.append(SymbolicValue.bool(True))
5965
5977
 
5966
5978
  frame.instruction_offset = self._next_offset(frame, instr)
5967
5979
 
5968
5980
  elif opname == "POP_EXCEPT":
5969
- # Pop exception state and clear current exception
5970
- # This happens when exiting an exception handler successfully
5971
- # Stack: [exc_type, exc_value, exc_tb, ...] -> [...]
5972
- if len(frame.operand_stack) >= 3:
5973
- frame.operand_stack.pop() # exc_tb
5974
- frame.operand_stack.pop() # exc_value
5975
- frame.operand_stack.pop() # exc_type
5981
+ # Python 3.11+: POP_EXCEPT pops one value (the exception)
5982
+ if frame.operand_stack:
5983
+ frame.operand_stack.pop()
5976
5984
 
5977
5985
  # Clear exception state
5978
5986
  state.exception = None
@@ -6015,11 +6023,11 @@ class SymbolicVM:
6015
6023
  elif var_index < num_varnames + num_cellvars:
6016
6024
  # This is a cellvar (variable in this function that inner functions will reference)
6017
6025
  cell_index = var_index - num_varnames
6018
- frame.cells[cell_index] = None # Initialize as empty cell
6026
+ frame.cells[cell_index] = SymbolicValue.none() # Initialize as empty cell
6019
6027
  else:
6020
6028
  # This is a freevar (variable from outer scope)
6021
6029
  freevar_index = var_index - num_varnames - num_cellvars
6022
- frame.freevars[freevar_index] = None # Initialize as empty
6030
+ frame.freevars[freevar_index] = SymbolicValue.none() # Initialize as empty
6023
6031
 
6024
6032
  frame.instruction_offset = self._next_offset(frame, instr)
6025
6033
 
@@ -6757,7 +6765,7 @@ class SymbolicVM:
6757
6765
 
6758
6766
  # Convert to boolean using is_true helper
6759
6767
  # is_true returns z3.ExprRef (BoolRef), we need to wrap it in a SymbolicValue
6760
- bool_expr = is_true(val, state)
6768
+ bool_expr = is_true(val, self.solver)
6761
6769
  bool_val = SymbolicValue(
6762
6770
  ValueTag.BOOL,
6763
6771
  z3.If(bool_expr, z3.IntVal(1), z3.IntVal(0))
@@ -6835,11 +6843,12 @@ class SymbolicVM:
6835
6843
  state.exception = "StackUnderflow"
6836
6844
  return
6837
6845
 
6838
- value = frame.operand_stack.pop()
6839
- func = frame.operand_stack[-1] # Keep function on stack
6846
+ func = frame.operand_stack.pop() # TOS = function
6847
+ attr = frame.operand_stack.pop() # TOS1 = attribute value (consumed)
6848
+ frame.operand_stack.append(func) # Push function back on stack
6840
6849
 
6841
6850
  # For symbolic execution, we don't need to track these attributes yet
6842
- # Just consume the value and keep the function
6851
+ # Just consume the attr value and keep the function
6843
6852
 
6844
6853
  frame.instruction_offset = self._next_offset(frame, instr)
6845
6854
 
@@ -7822,7 +7831,7 @@ class SymbolicVM:
7822
7831
  # Converts list to tuple (structural operation)
7823
7832
  # Symbolically: create a fresh tuple object
7824
7833
  tuple_id = z3.Int(f"intrinsic_tuple_{instr.offset}_{id(frame)}")
7825
- tuple_obj = SymbolicValue(ValueTag.OBJ, tuple_id)
7834
+ tuple_obj = SymbolicValue(ValueTag.TUPLE, tuple_id) # ✓ Correct: TUPLE not OBJ
7826
7835
  frame.operand_stack.append(tuple_obj)
7827
7836
  frame.instruction_offset = self._next_offset(frame, instr)
7828
7837
  else:
@@ -131,7 +131,7 @@ class SymbolicValue:
131
131
  sym = z3.Bool(name)
132
132
  if solver:
133
133
  solver.add(True)
134
- return SymbolicValue.bool(z3.If(sym, z3.IntVal(1), z3.IntVal(0)))
134
+ return SymbolicValue.bool(sym)
135
135
 
136
136
  @staticmethod
137
137
  def fresh_obj(name: str, solver: z3.Solver = None) -> 'SymbolicValue':
@@ -495,8 +495,23 @@ def binary_op_floordiv(left: SymbolicValue, right: SymbolicValue, solver: z3.Sol
495
495
  z3.And(right.is_float(), right.as_float() == z3.RealVal(0))
496
496
  )
497
497
 
498
- # For int // int, use integer division; for any float involvement, use real division
499
- result_int = left.as_int() / right.as_int()
498
+ # For int // int, use Python floor division.
499
+ # Z3's integer `/` is Euclidean division (remainder always non-negative),
500
+ # which differs from Python's floor division when the divisor is negative.
501
+ # Python: 7 // -2 = -4, but Z3: 7 / -2 = -3
502
+ # Fix: adjust when divisor is negative and there's a nonzero remainder.
503
+ # Guard: Z3's `%` requires IntSort, so only build this path for int payloads.
504
+ if z3.is_int(left.payload) and z3.is_int(right.payload):
505
+ euclid_div = left.as_int() / right.as_int()
506
+ euclid_mod = left.as_int() % right.as_int()
507
+ result_int = z3.If(
508
+ z3.And(right.as_int() < 0, euclid_mod != 0),
509
+ euclid_div - 1,
510
+ euclid_div
511
+ )
512
+ else:
513
+ # Float inputs: int path unused; dummy with correct sort for z3.If
514
+ result_int = z3.IntVal(0)
500
515
 
501
516
  # Convert to float using Z3 sort checking
502
517
  left_val = left.payload
@@ -512,7 +527,9 @@ def binary_op_floordiv(left: SymbolicValue, right: SymbolicValue, solver: z3.Sol
512
527
  else:
513
528
  right_as_float = right_val
514
529
 
515
- result_float = left_as_float / right_as_float
530
+ # For float floor division, compute floor of the real quotient.
531
+ # z3.ToInt is floor for reals, then convert back to Real for float result.
532
+ result_float = z3.ToReal(z3.ToInt(left_as_float / right_as_float))
516
533
 
517
534
  is_numeric_float = z3.Or(both_floats, int_float, float_int)
518
535
  result_payload = z3.If(is_numeric_float, result_float, result_int)
@@ -552,8 +569,22 @@ def binary_op_mod(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver)
552
569
  z3.And(right.is_float(), right.as_float() == z3.RealVal(0))
553
570
  )
554
571
 
555
- # For int % int, use integer modulo
556
- result_int = left.as_int() % right.as_int()
572
+ # For int % int, use Python floor modulo.
573
+ # Z3's integer `%` is Euclidean mod (always non-negative),
574
+ # which differs from Python's floor mod when the divisor is negative.
575
+ # Python: 7 % -2 = -1, but Z3: 7 % -2 = 1
576
+ # Fix: adjust when divisor is negative and there's a nonzero remainder.
577
+ # Guard: Z3's `%` requires IntSort, so only build this path for int payloads.
578
+ if z3.is_int(left.payload) and z3.is_int(right.payload):
579
+ euclid_mod = left.as_int() % right.as_int()
580
+ result_int = z3.If(
581
+ z3.And(right.as_int() < 0, euclid_mod != 0),
582
+ euclid_mod + right.as_int(),
583
+ euclid_mod
584
+ )
585
+ else:
586
+ # Float inputs: int path unused; dummy with correct sort for z3.If
587
+ result_int = z3.IntVal(0)
557
588
 
558
589
  # For float modulo, we approximate (Z3 doesn't have built-in real modulo)
559
590
  # Convert to float using Z3 sort checking
@@ -759,16 +790,26 @@ def compare_op_lt(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver)
759
790
  """Symbolic less-than comparison."""
760
791
  # Accept OBJ types (conservative overapproximation for soundness)
761
792
  # OBJ might be anything, including comparable types
793
+ # Also accept float and mixed int/float comparisons
762
794
  type_ok = z3.Or(
763
795
  z3.And(left.is_int(), right.is_int()),
796
+ z3.And(left.is_float(), right.is_float()),
797
+ z3.And(left.is_int(), right.is_float()),
798
+ z3.And(left.is_float(), right.is_int()),
764
799
  left.is_obj(),
765
800
  right.is_obj()
766
801
  )
767
802
  # When OBJ involved, return nondeterministic result (sound overapproximation)
803
+ # For float comparisons, as_float() returns payload; Z3 auto-coerces Int→Real
804
+ any_float = z3.Or(left.is_float(), right.is_float())
768
805
  result_val = z3.If(
769
806
  z3.Or(left.is_obj(), right.is_obj()),
770
807
  z3.Int(f"cmp_lt_obj_{id(left)}_{id(right)}"),
771
- z3.If(left.as_int() < right.as_int(), z3.IntVal(1), z3.IntVal(0))
808
+ z3.If(
809
+ any_float,
810
+ z3.If(left.as_float() < right.as_float(), z3.IntVal(1), z3.IntVal(0)),
811
+ z3.If(left.as_int() < right.as_int(), z3.IntVal(1), z3.IntVal(0))
812
+ )
772
813
  )
773
814
  return SymbolicValue(ValueTag.BOOL, result_val), type_ok
774
815
 
@@ -776,16 +817,25 @@ def compare_op_lt(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver)
776
817
  def compare_op_le(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver) -> tuple[SymbolicValue, z3.ExprRef]:
777
818
  """Symbolic less-than-or-equal comparison."""
778
819
  # Accept OBJ types (conservative overapproximation for soundness)
820
+ # Also accept float and mixed int/float comparisons
779
821
  type_ok = z3.Or(
780
822
  z3.And(left.is_int(), right.is_int()),
823
+ z3.And(left.is_float(), right.is_float()),
824
+ z3.And(left.is_int(), right.is_float()),
825
+ z3.And(left.is_float(), right.is_int()),
781
826
  left.is_obj(),
782
827
  right.is_obj()
783
828
  )
784
829
  # When OBJ involved, return nondeterministic result (sound overapproximation)
830
+ any_float = z3.Or(left.is_float(), right.is_float())
785
831
  result_val = z3.If(
786
832
  z3.Or(left.is_obj(), right.is_obj()),
787
833
  z3.Int(f"cmp_le_obj_{id(left)}_{id(right)}"),
788
- z3.If(left.as_int() <= right.as_int(), z3.IntVal(1), z3.IntVal(0))
834
+ z3.If(
835
+ any_float,
836
+ z3.If(left.as_float() <= right.as_float(), z3.IntVal(1), z3.IntVal(0)),
837
+ z3.If(left.as_int() <= right.as_int(), z3.IntVal(1), z3.IntVal(0))
838
+ )
789
839
  )
790
840
  return SymbolicValue(ValueTag.BOOL, result_val), type_ok
791
841
 
@@ -899,16 +949,25 @@ def compare_op_ne(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver)
899
949
  def compare_op_gt(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver) -> tuple[SymbolicValue, z3.ExprRef]:
900
950
  """Symbolic greater-than comparison."""
901
951
  # Accept OBJ types (conservative overapproximation for soundness)
952
+ # Also accept float and mixed int/float comparisons
902
953
  type_ok = z3.Or(
903
954
  z3.And(left.is_int(), right.is_int()),
955
+ z3.And(left.is_float(), right.is_float()),
956
+ z3.And(left.is_int(), right.is_float()),
957
+ z3.And(left.is_float(), right.is_int()),
904
958
  left.is_obj(),
905
959
  right.is_obj()
906
960
  )
907
961
  # When OBJ involved, return nondeterministic result (sound overapproximation)
962
+ any_float = z3.Or(left.is_float(), right.is_float())
908
963
  result_val = z3.If(
909
964
  z3.Or(left.is_obj(), right.is_obj()),
910
965
  z3.Int(f"cmp_gt_obj_{id(left)}_{id(right)}"),
911
- z3.If(left.as_int() > right.as_int(), z3.IntVal(1), z3.IntVal(0))
966
+ z3.If(
967
+ any_float,
968
+ z3.If(left.as_float() > right.as_float(), z3.IntVal(1), z3.IntVal(0)),
969
+ z3.If(left.as_int() > right.as_int(), z3.IntVal(1), z3.IntVal(0))
970
+ )
912
971
  )
913
972
  return SymbolicValue(ValueTag.BOOL, result_val), type_ok
914
973
 
@@ -916,16 +975,25 @@ def compare_op_gt(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver)
916
975
  def compare_op_ge(left: SymbolicValue, right: SymbolicValue, solver: z3.Solver) -> tuple[SymbolicValue, z3.ExprRef]:
917
976
  """Symbolic greater-than-or-equal comparison."""
918
977
  # Accept OBJ types (conservative overapproximation for soundness)
978
+ # Also accept float and mixed int/float comparisons
919
979
  type_ok = z3.Or(
920
980
  z3.And(left.is_int(), right.is_int()),
981
+ z3.And(left.is_float(), right.is_float()),
982
+ z3.And(left.is_int(), right.is_float()),
983
+ z3.And(left.is_float(), right.is_int()),
921
984
  left.is_obj(),
922
985
  right.is_obj()
923
986
  )
924
987
  # When OBJ involved, return nondeterministic result (sound overapproximation)
988
+ any_float = z3.Or(left.is_float(), right.is_float())
925
989
  result_val = z3.If(
926
990
  z3.Or(left.is_obj(), right.is_obj()),
927
991
  z3.Int(f"cmp_ge_obj_{id(left)}_{id(right)}"),
928
- z3.If(left.as_int() >= right.as_int(), z3.IntVal(1), z3.IntVal(0))
992
+ z3.If(
993
+ any_float,
994
+ z3.If(left.as_float() >= right.as_float(), z3.IntVal(1), z3.IntVal(0)),
995
+ z3.If(left.as_int() >= right.as_int(), z3.IntVal(1), z3.IntVal(0))
996
+ )
929
997
  )
930
998
  return SymbolicValue(ValueTag.BOOL, result_val), type_ok
931
999
 
@@ -992,8 +1060,14 @@ def is_true(value: SymbolicValue, solver: z3.Solver) -> z3.ExprRef:
992
1060
  value.payload == z3.IntVal(0)
993
1061
  )
994
1062
 
995
- # Value is false if it's None, False, or 0
996
- is_false = z3.Or(is_none, is_false_bool, is_zero_int)
1063
+ # Float 0.0 False
1064
+ is_zero_float = z3.And(
1065
+ value.tag == z3.IntVal(ValueTag.FLOAT.value),
1066
+ value.payload == z3.RealVal(0)
1067
+ )
1068
+
1069
+ # Value is false if it's None, False, 0, or 0.0
1070
+ is_false = z3.Or(is_none, is_false_bool, is_zero_int, is_zero_float)
997
1071
 
998
1072
  # Value is true if it's not false
999
1073
  return z3.Not(is_false)
@@ -1112,9 +1186,11 @@ def binary_op_subscript(container: SymbolicValue, index: SymbolicValue, heap, so
1112
1186
  solver.pop()
1113
1187
  return result, type_ok, bounds_violated, none_misuse
1114
1188
 
1115
- # Symbolic dict or symbolic key: conservatively assume may raise KeyError
1189
+ # Symbolic dict or symbolic key: create symbolic bounds check
1190
+ # KeyError is possible but not certain (e.g., if key is validated)
1116
1191
  result = SymbolicValue.fresh_int("dict_subscript_symbolic", solver)
1117
- bounds_violated = z3.BoolVal(True) # Conservative: may raise KeyError
1192
+ bounds_violated = z3.Bool(f"dict_key_missing_{id(container)}_{id(index)}")
1193
+ # Add constraint: allow both paths (key exists / key missing)
1118
1194
  solver.pop()
1119
1195
  return result, type_ok, bounds_violated, none_misuse
1120
1196
  solver.pop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: a3-python
3
- Version: 0.1.19
3
+ Version: 0.1.21
4
4
  Summary: Catch real Python bugs before production — 99%+ accuracy, Z3 symbolic execution, LLM-powered false-positive filtering, zero-config GitHub CI
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "a3-python"
7
- version = "0.1.19"
7
+ version = "0.1.21"
8
8
  description = "Catch real Python bugs before production — 99%+ accuracy, Z3 symbolic execution, LLM-powered false-positive filtering, zero-config GitHub CI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes