firestarter 3.2.1__tar.gz → 3.2.2__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.
- {firestarter-3.2.1/firestarter.egg-info → firestarter-3.2.2}/PKG-INFO +1 -1
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/_version.py +3 -3
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/chip_resolver.py +44 -1
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/cli_handlers.py +37 -7
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/database.py +140 -33
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/eprom_info.py +35 -7
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/exceptions.py +6 -0
- {firestarter-3.2.1 → firestarter-3.2.2/firestarter.egg-info}/PKG-INFO +1 -1
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/SOURCES.txt +1 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/scm_file_list.json +1 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/scm_version.json +2 -2
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/__snapshots__/test_characterization.ambr +29 -28
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/plan_corpus.py +11 -1
- firestarter-3.2.2/tests/test_chip_name_resolution.py +199 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_resolver.py +2 -1
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_derive_plan_structural_sentinel.py +20 -34
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_blank_guard_pinning.py +10 -6
- {firestarter-3.2.1 → firestarter-3.2.2}/LICENSE +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/MANIFEST.in +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/README.md +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/__init__.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/address_parser.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/avr_tool.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/channel.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/chip_test.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/codec.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/compare.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/config.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/constants.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/data/chip_database.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/data/pinouts.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/diagnostic_report.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/dip24_2816_erase_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/eprom_operations.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/erase_support.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/firmware.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/flash4_erase_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/frame_parser.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/fw_release_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/hardware.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/ic_layout.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/jp5_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/jumper_table.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/lock_status.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/log_capture.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/logging_utils.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/main.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/messages.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/page_size_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/protection_readability.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/py32_dfu.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/sdp_capability.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/sdp_honesty.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/serial_comm.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/submit.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/timestamps.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/transport_counters.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/utils.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/vpp_display.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter/write_blank_guard.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/dependency_links.txt +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/entry_points.txt +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/requires.txt +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/firestarter.egg-info/top_level.txt +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/pyproject.toml +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/setup.cfg +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/__init__.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/conftest.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fake_chip.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/part_number_delta.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/report_shapes.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/at28c256-full-all-ok-sdp.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/attr01-status-axis-transport-fault.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/gh20-at28c256-fail.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/gh23-w27e257-fail.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/gh28-m27c512-fail.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/gh47-sst27sf512-pass.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/m27c512-full-all-ok.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/m27c512-full-blank-check-bad.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/m27c512-full-canonical-name.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/m27c512-full-comma-joined-name.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/m27c512-full-runs-1.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/prune03-synthesized-fingerprint-match.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/sst27sf512-full-all-ok.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/sst27sf512-six-step-readback-gated.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/sst27sf512-six-step.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/synthetic-arm4-empty-results.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/synthetic-arm4-no-ok.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/uv-slot-write-pass.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/reports/w27e257-full-all-ok.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/shape_ids.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/fixtures/synthetic_nonzero_chip_id.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/golden/build_db_part_specific_constants.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/golden/chip_database_field_inventory.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/golden/wire_dict.json +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_address_parser.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_b15_page_size_corroboration.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_blast_radius_invariance.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_boot_block_hint.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_budget_failure_render.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_bug_characterization.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_build_db_constant_census.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_build_db_inclusion.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_build_db_interpret_timing.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_build_db_pinout_fork.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_canonical_part_number.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_characterization.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_database_field_inventory.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test_blank_check_order.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test_cycle.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test_sdp_leg.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test_timing.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_chip_test_uv_slot_write.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_cli_handlers.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_click_group_gate_hook.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_cobs.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_codec.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_codec_format_message.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_compare.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_config.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_config_band_refusal.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_connect_cost_harness.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_consistency_check.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_coverage_floor_v18.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_database_conversion.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_datasheet_overrides.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_decoder.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_derive_plan_no_drop_sweep.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_dev_group_channel_gating.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_dev_test_cmd.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_dev_tools_channel_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_devtest_firmware_error_propagation.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_dfu_opcode_anchors.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_diagnostic_report.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_dip24_2816_erase_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_endpoint_constants.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_eprom_database.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_eprom_info.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_eprom_operations.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_erase_blank_step_nonregression.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_erase_flag_invariants.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_error_code_seam.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_even_block.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_extra_chips_supplement.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_firmware_install.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_flash4_erase_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_list_failure_vs_empty.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_port_targeting_and_blind_install.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_release_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_update_dead_endpoint.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_update_path_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fw_version_guard.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_fwguard.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_hardware.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_hw_revision_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_ic_layout.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_info_vpp_and_erase.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_jp5_gate.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_jumper_table.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_lock_status_class_partition.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_lock_status_cli.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_lock_status_wire.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_log_capture.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_logging_utils.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_matrix_artifact.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_page_size_alignment_refusal.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_page_size_invariants.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_page_size_write_refusal.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_parse_devtest_issue.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_probe_spurious_setup_ack.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_programming_vcc_census.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protect_flags_doc_measurements.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protection_resolution.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protection_status_catalog.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protection_table_citations.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protocol_not_implemented.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_protocol_not_implemented_production_path.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_provenance.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_pulse_us_override.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_py32_channel_gating.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_py32_dfu.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_py32_flash_map_host.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_py32_pyusb_absent.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_pyusb_api_surface.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_sdp_capability.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_sdp_db_invariant.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_sdp_honesty.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_serial_characterization.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_serial_comm.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_session_lease.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_setup_refusals.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_submit.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_timestamps.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_transport_counters.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_utils.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_uv_mask.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_validate_family_cmd.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_validate_oracle.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_validated_parts_regression_surface.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_vcc_margin_rail.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_vpp_decode_table.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_vpp_rail_classification.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_wire_dict_equivalence.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_blank_guard.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_guard_port_pinning.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_progress.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_response_budget.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_skip_erase_0x0d.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_skip_sdp_unlock.py +0 -0
- {firestarter-3.2.1 → firestarter-3.2.2}/tests/test_write_verify.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firestarter
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: EPROM programmer software for the Relatively Universal ROM Programmer
|
|
5
5
|
Author-email: Henrik Olsson <henols@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/henols/firestarter_app
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (3, 2,
|
|
21
|
+
__version__ = version = '3.2.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 2, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'ga7d562c'
|
|
@@ -10,11 +10,50 @@ Single chokepoint between CLI dispatch and the EPROM database lookup/conversion.
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
12
|
from firestarter.database import EpromDatabase
|
|
13
|
-
from firestarter.exceptions import
|
|
13
|
+
from firestarter.exceptions import (
|
|
14
|
+
AmbiguousChipNameError,
|
|
15
|
+
ChipNotFoundError,
|
|
16
|
+
ChipNotImplementedError,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
_NON_ZERO_WIRE_FIELDS = ("memory-size", "pin-count", "vpp_mv")
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
def _qualified_choices(name: str, db: EpromDatabase) -> list[str]:
|
|
23
|
+
"""The row `name` selects first, then each other row it selects that
|
|
24
|
+
programs differently, all as qualified names. Empty when one row, or rows
|
|
25
|
+
that program the same, are selected."""
|
|
26
|
+
alternatives = db.ambiguous_alternatives(name)
|
|
27
|
+
if not alternatives:
|
|
28
|
+
return []
|
|
29
|
+
raw_config, manufacturer = db.get_eprom_config(name)
|
|
30
|
+
return [db.qualified_chip_name(name, raw_config, manufacturer), *alternatives]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def refuse_if_ambiguous(name: str, db: EpromDatabase) -> None:
|
|
34
|
+
"""Refuse a name that selects chips that program differently: guessing
|
|
35
|
+
one would drive the wrong VPP, algorithm or timing."""
|
|
36
|
+
choices = _qualified_choices(name, db)
|
|
37
|
+
if choices:
|
|
38
|
+
listed = ", ".join(f"'{c}'" for c in choices)
|
|
39
|
+
raise AmbiguousChipNameError(
|
|
40
|
+
f"'{name}' names chips that program differently. Name one: {listed}."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def describe_ambiguity(name: str, db: EpromDatabase) -> str | None:
|
|
45
|
+
"""For display commands, which use the first row: the other rows `name`
|
|
46
|
+
selects, or None."""
|
|
47
|
+
choices = _qualified_choices(name, db)
|
|
48
|
+
if not choices:
|
|
49
|
+
return None
|
|
50
|
+
others = ", ".join(f"'{c}'" for c in choices[1:])
|
|
51
|
+
return (
|
|
52
|
+
f"'{name}' also names chips that program differently: {others}. "
|
|
53
|
+
f"This shows '{choices[0]}'."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
18
57
|
def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
|
|
19
58
|
"""Resolve a chip name to its programmer-config dict.
|
|
20
59
|
|
|
@@ -26,6 +65,9 @@ def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
|
|
|
26
65
|
conversion (``convert_to_programmer`` returns ``{}``) are treated as the
|
|
27
66
|
not-found condition.
|
|
28
67
|
|
|
68
|
+
Raises ``AmbiguousChipNameError`` (a ``ChipNotFoundError``) when the name
|
|
69
|
+
selects rows that program differently; the message names each of them.
|
|
70
|
+
|
|
29
71
|
Raises ``ChipNotImplementedError`` when the chip exists in the database but
|
|
30
72
|
its ``support_status`` is not ``"supported"`` (covers protocol-not-implemented,
|
|
31
73
|
adapter-required, and vpp-exceeds-max). This guard fires BEFORE any wire dict
|
|
@@ -49,6 +91,7 @@ def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
|
|
|
49
91
|
# have a support_status, so raise ChipNotFoundError immediately.
|
|
50
92
|
if raw_config is None:
|
|
51
93
|
raise ChipNotFoundError(name)
|
|
94
|
+
refuse_if_ambiguous(name, db)
|
|
52
95
|
|
|
53
96
|
# Support-status guard: refuse every program-capable operation
|
|
54
97
|
# for non-supported chips BEFORE convert_to_programmer builds any wire dict.
|
|
@@ -38,7 +38,11 @@ from firestarter.channel import (
|
|
|
38
38
|
dev_command_gate_message,
|
|
39
39
|
is_dev_tools_enabled,
|
|
40
40
|
)
|
|
41
|
-
from firestarter.chip_resolver import
|
|
41
|
+
from firestarter.chip_resolver import (
|
|
42
|
+
describe_ambiguity,
|
|
43
|
+
refuse_if_ambiguous,
|
|
44
|
+
resolve_chip,
|
|
45
|
+
)
|
|
42
46
|
from firestarter.chip_test import (
|
|
43
47
|
OP_ID,
|
|
44
48
|
SDP_HOLD_NOT_RUN,
|
|
@@ -160,10 +164,17 @@ def _complete_eprom(
|
|
|
160
164
|
semantics at main.py:39-45.
|
|
161
165
|
""" # noqa: E501
|
|
162
166
|
db = EpromDatabase()
|
|
163
|
-
|
|
164
|
-
|
|
167
|
+
qualified = db.names_needing_qualifier()
|
|
168
|
+
# A row whose name also names chips that program differently is offered
|
|
169
|
+
# by the qualified name that selects it.
|
|
170
|
+
names = (
|
|
171
|
+
qualified.get((e["manufacturer"], e["name"]), e["name"])
|
|
165
172
|
for e in db.get_eproms()
|
|
166
|
-
|
|
173
|
+
)
|
|
174
|
+
return [
|
|
175
|
+
click.shell_completion.CompletionItem(name)
|
|
176
|
+
for name in names
|
|
177
|
+
if name.lower().startswith(incomplete.lower())
|
|
167
178
|
]
|
|
168
179
|
|
|
169
180
|
|
|
@@ -486,7 +497,11 @@ def _list_cmd(app: AppContext) -> None:
|
|
|
486
497
|
"""List all EPROMs in the database."""
|
|
487
498
|
eprom_data_list = app.db.get_eproms()
|
|
488
499
|
if eprom_data_list:
|
|
489
|
-
print_eprom_list_table(
|
|
500
|
+
print_eprom_list_table(
|
|
501
|
+
eprom_data_list,
|
|
502
|
+
app.eprom_presenter.spec_builder,
|
|
503
|
+
app.db.names_needing_qualifier(),
|
|
504
|
+
)
|
|
490
505
|
sys.exit(0)
|
|
491
506
|
sys.exit(1)
|
|
492
507
|
|
|
@@ -504,6 +519,9 @@ def info(app: AppContext, eprom: str, config: bool, adapter: bool) -> None:
|
|
|
504
519
|
logger.error(f"EPROM '{eprom}' not found in database.")
|
|
505
520
|
sys.exit(1)
|
|
506
521
|
|
|
522
|
+
ambiguity = describe_ambiguity(eprom, app.db)
|
|
523
|
+
if ambiguity:
|
|
524
|
+
logger.warning(ambiguity)
|
|
507
525
|
eprom_data_for_programmer = app.db.convert_to_programmer(eprom_details)
|
|
508
526
|
raw_config_data, manufacturer = app.db.get_eprom_config(eprom)
|
|
509
527
|
|
|
@@ -534,7 +552,11 @@ def search(app: AppContext, text: str) -> None:
|
|
|
534
552
|
"""Search for EPROMs in the database."""
|
|
535
553
|
search_results = app.db.search_eprom(text)
|
|
536
554
|
if search_results:
|
|
537
|
-
print_eprom_list_table(
|
|
555
|
+
print_eprom_list_table(
|
|
556
|
+
search_results,
|
|
557
|
+
app.eprom_presenter.spec_builder,
|
|
558
|
+
app.db.names_needing_qualifier(),
|
|
559
|
+
)
|
|
538
560
|
sys.exit(0)
|
|
539
561
|
sys.exit(1)
|
|
540
562
|
|
|
@@ -1505,7 +1527,9 @@ def chip_id(app: AppContext, eprom: str, force: bool) -> None:
|
|
|
1505
1527
|
for ic in found_eproms_for_detected_id
|
|
1506
1528
|
]
|
|
1507
1529
|
print_eprom_list_table(
|
|
1508
|
-
mapped_found_eproms,
|
|
1530
|
+
mapped_found_eproms,
|
|
1531
|
+
app.eprom_presenter.spec_builder,
|
|
1532
|
+
app.db.names_needing_qualifier(),
|
|
1509
1533
|
)
|
|
1510
1534
|
else:
|
|
1511
1535
|
logger.warning(
|
|
@@ -3058,6 +3082,8 @@ def _canonical_part_number(part_number: str | None, raw_token: str) -> str | Non
|
|
|
3058
3082
|
def _strip_paren(s: str) -> str:
|
|
3059
3083
|
return re.sub(r"\([^)]*\)", "", s).strip().lower()
|
|
3060
3084
|
|
|
3085
|
+
# A manufacturer qualifier (PART@MANUFACTURER) is not part of the name.
|
|
3086
|
+
raw_token = raw_token.partition("@")[0].strip()
|
|
3061
3087
|
query = raw_token.lower()
|
|
3062
3088
|
if query == part_number.lower():
|
|
3063
3089
|
return part_number
|
|
@@ -3166,6 +3192,10 @@ def dev_test(app: "AppContext", chip: str, fast: bool, submit: bool) -> None:
|
|
|
3166
3192
|
# still runs the full community-validation sweep below.
|
|
3167
3193
|
if not app.db.get_eprom(chip):
|
|
3168
3194
|
raise ChipNotFoundError(f"{chip}: not found in database")
|
|
3195
|
+
# A name that selects chips that program differently is refused here,
|
|
3196
|
+
# not step by step: every step would be SKIPPED and the report would
|
|
3197
|
+
# name no chip.
|
|
3198
|
+
refuse_if_ambiguous(chip, app.db)
|
|
3169
3199
|
|
|
3170
3200
|
# The chip must be known to be in the DB (see above) before its
|
|
3171
3201
|
# electrical type can be read, so the UV-scope resolution happens here,
|
|
@@ -14,6 +14,7 @@ return the same instance.
|
|
|
14
14
|
import json
|
|
15
15
|
import logging
|
|
16
16
|
import os
|
|
17
|
+
import re
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
from typing import Any
|
|
19
20
|
|
|
@@ -145,6 +146,11 @@ def _read_config_file(filename: str) -> dict:
|
|
|
145
146
|
return {}
|
|
146
147
|
|
|
147
148
|
|
|
149
|
+
def _strip_mode(name: str) -> str:
|
|
150
|
+
"""'DS1245AB(RW)' -> 'ds1245ab': the name without its mode annotation."""
|
|
151
|
+
return re.sub(r"\([^)]*\)", "", name).strip().lower()
|
|
152
|
+
|
|
153
|
+
|
|
148
154
|
class EpromDatabase:
|
|
149
155
|
"""
|
|
150
156
|
Manages the EPROM and pin map database for the Firestarter application.
|
|
@@ -157,6 +163,7 @@ class EpromDatabase:
|
|
|
157
163
|
def __init__(self, skip_local_override: bool = False):
|
|
158
164
|
self.proms: dict[str, Any] = {}
|
|
159
165
|
self.pin_maps: dict[str, Any] = {}
|
|
166
|
+
self._names_needing_qualifier: dict[tuple[str, str], str] | None = None
|
|
160
167
|
self._initialize_database_core(skip_local_override=skip_local_override)
|
|
161
168
|
logger.debug("EpromDatabase initialized.")
|
|
162
169
|
|
|
@@ -426,46 +433,146 @@ class EpromDatabase:
|
|
|
426
433
|
for ic_config in ics
|
|
427
434
|
]
|
|
428
435
|
|
|
436
|
+
def find_chip_configs(self, chip_name: str) -> list[tuple[dict, str]]:
|
|
437
|
+
"""Return every database row `chip_name` selects, as
|
|
438
|
+
(config_dict, manufacturer) pairs in database order.
|
|
439
|
+
|
|
440
|
+
A name matches a row's `part_number` directly, or one of its
|
|
441
|
+
comma-separated aliases. Upstream infoic.xml encodes chip aliases as
|
|
442
|
+
comma-separated lists (e.g. "AT28C256,AT28C256E,AT28HC256"), which
|
|
443
|
+
build_db.py preserves in the part_number field.
|
|
444
|
+
|
|
445
|
+
infoic.xml also annotates many names with a parenthetical mode, such
|
|
446
|
+
as "(RW)", "(TEST)" or "(RW3.3V)" (e.g. "DS1245AB(RW),DS1245Y(RW)"). A
|
|
447
|
+
name matches an annotated alias with the annotation stripped, but only
|
|
448
|
+
when no row matches the name as typed: "DS1220(TEST)" selects the
|
|
449
|
+
(TEST) row, never the (RW) row that "DS1220" also matches.
|
|
450
|
+
|
|
451
|
+
The same part number can name rows of different manufacturers, which
|
|
452
|
+
program differently. `PART@MANUFACTURER` selects one manufacturer's
|
|
453
|
+
rows; see `resolve_manufacturer` for how the manufacturer matches.
|
|
454
|
+
"""
|
|
455
|
+
name, at, wanted = chip_name.partition("@")
|
|
456
|
+
manufacturer_wanted = self.resolve_manufacturer(wanted) if at else None
|
|
457
|
+
if at and manufacturer_wanted is None:
|
|
458
|
+
return []
|
|
459
|
+
query = name.strip().lower()
|
|
460
|
+
query_stripped = _strip_mode(name)
|
|
461
|
+
exact: list[tuple[dict, str]] = []
|
|
462
|
+
stripped: list[tuple[dict, str]] = []
|
|
463
|
+
for manufacturer, ics in self.proms.items():
|
|
464
|
+
if manufacturer_wanted is not None and manufacturer != manufacturer_wanted:
|
|
465
|
+
continue
|
|
466
|
+
for ic_config in ics:
|
|
467
|
+
part_number = ic_config.get("part_number", "")
|
|
468
|
+
aliases = part_number.split(",")
|
|
469
|
+
if query == part_number.lower() or query in (
|
|
470
|
+
a.strip().lower() for a in aliases
|
|
471
|
+
):
|
|
472
|
+
exact.append((ic_config, manufacturer))
|
|
473
|
+
elif query_stripped and query_stripped in (
|
|
474
|
+
_strip_mode(a) for a in aliases
|
|
475
|
+
):
|
|
476
|
+
stripped.append((ic_config, manufacturer))
|
|
477
|
+
return exact or stripped
|
|
478
|
+
|
|
429
479
|
def get_eprom_config(self, chip_name: str):
|
|
430
480
|
"""
|
|
431
481
|
Retrieves the raw configuration data for a specific EPROM by its name.
|
|
432
482
|
Returns (config_dict, manufacturer_str) or (None, None).
|
|
433
483
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
chip aliases as comma-separated lists (e.g.,
|
|
437
|
-
"AT28C256,AT28C256E,AT28HC256"), which build_db.py preserves in
|
|
438
|
-
the part_number field. Without alias-aware lookup, queries like
|
|
439
|
-
`firestarter info AT28C256` failed despite the chip being in the DB.
|
|
440
|
-
|
|
441
|
-
Also handles infoic.xml's parenthetical mode annotations: many
|
|
442
|
-
chip names carry "(RW)", "(TEST)", "(RW3.3V)" etc. (e.g.,
|
|
443
|
-
"DS1245AB(RW),DS1245Y(RW)"). A plain query like "DS1245AB"
|
|
444
|
-
is normalized to match against the paren-stripped alias.
|
|
484
|
+
The first row `find_chip_configs` selects; see there for how a name
|
|
485
|
+
matches.
|
|
445
486
|
"""
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
487
|
+
matches = self.find_chip_configs(chip_name)
|
|
488
|
+
return matches[0] if matches else (None, None)
|
|
489
|
+
|
|
490
|
+
def resolve_manufacturer(self, wanted: str) -> str | None:
|
|
491
|
+
"""The manufacturer `wanted` names: an exact name, ignoring case, else
|
|
492
|
+
the one manufacturer it is a prefix of. None when it names none, or is
|
|
493
|
+
a prefix of more than one."""
|
|
494
|
+
wanted = wanted.strip().lower()
|
|
495
|
+
if not wanted:
|
|
496
|
+
return None
|
|
497
|
+
names = list(self.proms)
|
|
498
|
+
exact = [m for m in names if m.lower() == wanted]
|
|
499
|
+
if exact:
|
|
500
|
+
return exact[0]
|
|
501
|
+
prefixed = [m for m in names if m.lower().startswith(wanted)]
|
|
502
|
+
return prefixed[0] if len(prefixed) == 1 else None
|
|
503
|
+
|
|
504
|
+
def qualified_chip_name(
|
|
505
|
+
self, chip_name: str, ic_config: dict, manufacturer: str
|
|
506
|
+
) -> str:
|
|
507
|
+
"""The name that selects this one row: the row's alias that `chip_name`
|
|
508
|
+
matched, with its mode annotation, then `@` and the manufacturer.
|
|
509
|
+
|
|
510
|
+
The manufacturer is its first word when that word names it alone, so
|
|
511
|
+
the name needs no shell quoting ('27C128@MICROCHIP', not
|
|
512
|
+
'27C128@MICROCHIP memory').
|
|
513
|
+
"""
|
|
514
|
+
name = chip_name.partition("@")[0].strip()
|
|
515
|
+
aliases = [a.strip() for a in ic_config.get("part_number", "").split(",")]
|
|
516
|
+
alias = next(
|
|
517
|
+
(a for a in aliases if a.lower() == name.lower()),
|
|
518
|
+
next(
|
|
519
|
+
(a for a in aliases if _strip_mode(a) == _strip_mode(name)),
|
|
520
|
+
aliases[0],
|
|
521
|
+
),
|
|
522
|
+
)
|
|
523
|
+
first_word = manufacturer.split()[0] if manufacturer.split() else manufacturer
|
|
524
|
+
short = (
|
|
525
|
+
first_word
|
|
526
|
+
if self.resolve_manufacturer(first_word) == manufacturer
|
|
527
|
+
else manufacturer
|
|
528
|
+
)
|
|
529
|
+
return f"{alias}@{short}"
|
|
530
|
+
|
|
531
|
+
def names_needing_qualifier(self) -> dict[tuple[str, str], str]:
|
|
532
|
+
"""Every row with an alias that, typed as shown, also selects rows that
|
|
533
|
+
program differently, keyed (manufacturer, part_number), with the
|
|
534
|
+
qualified name that selects it. Such a row can only be named with
|
|
535
|
+
`@MANUFACTURER`."""
|
|
536
|
+
if self._names_needing_qualifier is None:
|
|
537
|
+
by_name: dict[str, list[tuple[dict, str]]] = {}
|
|
538
|
+
for manufacturer, ics in self.proms.items():
|
|
539
|
+
for ic_config in ics:
|
|
540
|
+
for alias in ic_config.get("part_number", "").split(","):
|
|
541
|
+
by_name.setdefault(alias.strip().lower(), []).append(
|
|
542
|
+
(ic_config, manufacturer)
|
|
543
|
+
)
|
|
544
|
+
needing: dict[tuple[str, str], str] = {}
|
|
545
|
+
for name, rows in by_name.items():
|
|
546
|
+
if len(rows) < 2:
|
|
547
|
+
continue
|
|
548
|
+
wires = [
|
|
549
|
+
self.convert_to_programmer(self._map_data(ic, m)) for ic, m in rows
|
|
550
|
+
]
|
|
551
|
+
if all(w == wires[0] for w in wires[1:]):
|
|
552
|
+
continue
|
|
553
|
+
for ic, m in rows:
|
|
554
|
+
needing.setdefault(
|
|
555
|
+
(m, ic.get("part_number", "")),
|
|
556
|
+
self.qualified_chip_name(name, ic, m),
|
|
557
|
+
)
|
|
558
|
+
self._names_needing_qualifier = needing
|
|
559
|
+
return self._names_needing_qualifier
|
|
451
560
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return ic_config, manufacturer
|
|
468
|
-
return None, None
|
|
561
|
+
def ambiguous_alternatives(self, chip_name: str) -> list[str]:
|
|
562
|
+
"""Name the other rows `chip_name` selects that program differently
|
|
563
|
+
from the one `get_eprom_config` uses, each as a qualified name that
|
|
564
|
+
selects that row alone. Empty when the choice cannot matter.
|
|
565
|
+
"""
|
|
566
|
+
matches = self.find_chip_configs(chip_name)
|
|
567
|
+
if len(matches) < 2:
|
|
568
|
+
return []
|
|
569
|
+
chosen = self.convert_to_programmer(self._map_data(*matches[0]))
|
|
570
|
+
return [
|
|
571
|
+
self.qualified_chip_name(chip_name, ic_config, manufacturer)
|
|
572
|
+
for ic_config, manufacturer in matches[1:]
|
|
573
|
+
if self.convert_to_programmer(self._map_data(ic_config, manufacturer))
|
|
574
|
+
!= chosen
|
|
575
|
+
]
|
|
469
576
|
|
|
470
577
|
def get_eprom(self, chip_name: str):
|
|
471
578
|
"""
|
|
@@ -394,9 +394,17 @@ class EpromConsolePresenter:
|
|
|
394
394
|
|
|
395
395
|
|
|
396
396
|
# --- Helper function for printing EPROM list (for testing/CLI) ---
|
|
397
|
-
def print_eprom_list_table(
|
|
397
|
+
def print_eprom_list_table(
|
|
398
|
+
eproms_data: list,
|
|
399
|
+
spec_builder: EpromSpecBuilder,
|
|
400
|
+
qualified_names: dict[tuple[str, str], str] | None = None,
|
|
401
|
+
):
|
|
398
402
|
"""Prints a list of EPROM data in a table format. For CLI/testing.
|
|
399
403
|
|
|
404
|
+
A row in `qualified_names` (keyed (manufacturer, name)) can only be named
|
|
405
|
+
with `@MANUFACTURER`; its Name cell ends in '*' and a line under the table
|
|
406
|
+
gives an example.
|
|
407
|
+
|
|
400
408
|
Column layout:
|
|
401
409
|
- Name: dynamic width clamped to [13, 20]; names longer than 20 chars are
|
|
402
410
|
truncated with a trailing ellipsis ('…') that counts toward the 20-char cap.
|
|
@@ -413,19 +421,26 @@ def print_eprom_list_table(eproms_data: list, spec_builder: EpromSpecBuilder):
|
|
|
413
421
|
# Compute the widest rendered name (including any [!] suffix) across all rows,
|
|
414
422
|
# then clamp to the [13, 20] range. Names that would exceed 20 chars are
|
|
415
423
|
# truncated to 19 chars + '…' (ellipsis counts toward the 20-char cap).
|
|
416
|
-
|
|
424
|
+
qualified_names = qualified_names or {}
|
|
425
|
+
|
|
426
|
+
def _render_name(raw_name: str, has_bus_config: bool, marked: bool) -> str:
|
|
417
427
|
"""Return the name string as it will be rendered in the table cell."""
|
|
418
428
|
name = raw_name
|
|
419
429
|
if not has_bus_config:
|
|
420
430
|
name = (name[:11] + "[!]") if len(name) > 11 else f"{name}[!]"
|
|
421
|
-
if
|
|
422
|
-
|
|
423
|
-
|
|
431
|
+
cap = 19 if marked else 20
|
|
432
|
+
if len(name) > cap:
|
|
433
|
+
name = name[: cap - 1] + "…" # ellipsis counts toward the cap
|
|
434
|
+
return name + "*" if marked else name
|
|
424
435
|
|
|
425
|
-
|
|
426
|
-
|
|
436
|
+
marked_rows = [
|
|
437
|
+
(ic.get("manufacturer", ""), ic.get("name", "")) in qualified_names
|
|
427
438
|
for ic in eproms_data
|
|
428
439
|
]
|
|
440
|
+
rendered_names = [
|
|
441
|
+
_render_name(ic.get("name", ""), bool(ic.get("bus-config")), marked)
|
|
442
|
+
for ic, marked in zip(eproms_data, marked_rows)
|
|
443
|
+
]
|
|
429
444
|
name_w = max(13, min(20, max((len(n) for n in rendered_names), default=13)))
|
|
430
445
|
|
|
431
446
|
divider = f"+{'':-<{name_w + 1}}+{'':-<18}+{'':-<6}+{'':-<12}+{'':-<13}+{'':-<6}+"
|
|
@@ -462,6 +477,19 @@ def print_eprom_list_table(eproms_data: list, spec_builder: EpromSpecBuilder):
|
|
|
462
477
|
f"| {name: <{name_w}}| {ic.get('manufacturer', ''): <17}|{ic.get('pin-count', 0): >5} | {chip_id_str: <11}| {type_str_display: <12}| {vpp_str: <5}|" # noqa: E501
|
|
463
478
|
)
|
|
464
479
|
logger.info(divider)
|
|
480
|
+
example = next(
|
|
481
|
+
(
|
|
482
|
+
qualified_names[(ic.get("manufacturer", ""), ic.get("name", ""))]
|
|
483
|
+
for ic, marked in zip(eproms_data, marked_rows)
|
|
484
|
+
if marked
|
|
485
|
+
),
|
|
486
|
+
None,
|
|
487
|
+
)
|
|
488
|
+
if example:
|
|
489
|
+
logger.info(
|
|
490
|
+
"* This name also selects chips that program differently. "
|
|
491
|
+
f"Add @MANUFACTURER to choose one, e.g. {example}"
|
|
492
|
+
)
|
|
465
493
|
|
|
466
494
|
|
|
467
495
|
# Standalone test function
|
|
@@ -195,3 +195,9 @@ class ChipNotFoundError(Exception):
|
|
|
195
195
|
"""
|
|
196
196
|
|
|
197
197
|
pass
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class AmbiguousChipNameError(ChipNotFoundError):
|
|
201
|
+
"""Raised when a chip name selects database rows that program differently
|
|
202
|
+
(VPP, algorithm, chip ID or pulse width), so no one chip is named. The
|
|
203
|
+
message lists a qualified name for each of them."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firestarter
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: EPROM programmer software for the Relatively Universal ROM Programmer
|
|
5
5
|
Author-email: Henrik Olsson <henols@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/henols/firestarter_app
|
|
@@ -73,6 +73,7 @@ tests/test_build_db_pinout_fork.py
|
|
|
73
73
|
tests/test_canonical_part_number.py
|
|
74
74
|
tests/test_characterization.py
|
|
75
75
|
tests/test_chip_database_field_inventory.py
|
|
76
|
+
tests/test_chip_name_resolution.py
|
|
76
77
|
tests/test_chip_resolver.py
|
|
77
78
|
tests/test_chip_test.py
|
|
78
79
|
tests/test_chip_test_blank_check_order.py
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"tests/test_canonical_part_number.py",
|
|
120
120
|
"tests/test_characterization.py",
|
|
121
121
|
"tests/test_chip_database_field_inventory.py",
|
|
122
|
+
"tests/test_chip_name_resolution.py",
|
|
122
123
|
"tests/test_chip_resolver.py",
|
|
123
124
|
"tests/test_chip_test.py",
|
|
124
125
|
"tests/test_chip_test_blank_check_order.py",
|