nfcscript 0.0.11__tar.gz → 0.0.13__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 (29) hide show
  1. {nfcscript-0.0.11 → nfcscript-0.0.13}/PKG-INFO +10 -7
  2. {nfcscript-0.0.11 → nfcscript-0.0.13}/README.md +9 -6
  3. {nfcscript-0.0.11 → nfcscript-0.0.13}/pyproject.toml +2 -2
  4. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/cli.py +0 -7
  5. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/reader.py +21 -16
  6. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/trace.py +0 -10
  7. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/PKG-INFO +10 -7
  8. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_trace.py +0 -8
  9. {nfcscript-0.0.11 → nfcscript-0.0.13}/setup.cfg +0 -0
  10. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/__init__.py +0 -0
  11. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/assertions.py +0 -0
  12. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/bits.py +0 -0
  13. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/checksum.py +0 -0
  14. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/delay.py +0 -0
  15. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/hex_util.py +0 -0
  16. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfc/nfc_cli.py +0 -0
  17. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/SOURCES.txt +0 -0
  18. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/dependency_links.txt +0 -0
  19. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/entry_points.txt +0 -0
  20. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/requires.txt +0 -0
  21. {nfcscript-0.0.11 → nfcscript-0.0.13}/src/nfcscript.egg-info/top_level.txt +0 -0
  22. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_assertions.py +0 -0
  23. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_bits.py +0 -0
  24. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_checksum.py +0 -0
  25. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_cli.py +0 -0
  26. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_cli_collect.py +0 -0
  27. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_delay.py +0 -0
  28. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_hex_util.py +0 -0
  29. {nfcscript-0.0.11 → nfcscript-0.0.13}/tests/test_reader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nfcscript
3
- Version: 0.0.11
3
+ Version: 0.0.13
4
4
  Summary: A plugin-based core engine for DSL-style NFC testing automation.
5
5
  Author: crthu
6
6
  Requires-Python: >=3.14
@@ -123,14 +123,14 @@ connect()
123
123
  trace.set_layer("DRIVER", True)
124
124
  trace.set_layer("PROTOCOL", True)
125
125
 
126
- # 寻卡
126
+ # 寻卡(自动切换协议解析器)
127
127
  card_info = active()
128
128
  ASSERT_IS_NOT_NONE(card_info, "未扫描到卡片")
129
- print(f"UID: {card_info['uid'].hex().upper()}")
129
+ print(f"UID: {' '.join(f'{b:02X}' for b in card_info.uid)}")
130
130
 
131
131
  # 创建卡片实例
132
132
  from nfctester.registry import CardRegistry
133
- card = CardRegistry.create("mifare", reader=get_reader())
133
+ card = CardRegistry.create("mifare_classic", reader=get_reader())
134
134
 
135
135
  # 关闭连接
136
136
  close()
@@ -142,8 +142,8 @@ close()
142
142
  from nfc import *
143
143
 
144
144
  with session() as reader:
145
- card_info = reader.find()
146
- print(card_info)
145
+ card_info = reader.active()
146
+ print(card_info.uid)
147
147
  # 自动断开连接
148
148
  ```
149
149
 
@@ -167,7 +167,7 @@ with session() as reader:
167
167
  |------|------|
168
168
  | `connect(port, reader_type)` | 连接读卡器 |
169
169
  | `get_reader()` | 获取当前读卡器实例 |
170
- | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 uid/atq/sak |
170
+ | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 CardInfo (uid/atq/sak),自动切换协议解析器 |
171
171
  | `transceive(data, tx_crc, rx_crc)` | 底层帧交互 |
172
172
  | `transceive_bits(data, last_tx_bits, tx_crc, rx_crc)` | 位控制帧交互 |
173
173
  | `reqa()` | ISO14443-A REQA |
@@ -185,6 +185,9 @@ with session() as reader:
185
185
  |------|------|
186
186
  | `trace.set_layer(layer, enable)` | 开启/关闭追踪层 |
187
187
  | `trace.set_level(level)` | 设置日志级别 |
188
+ | `trace.set_parse(level)` | 设置解析级别 (0=关闭, 1=hex+摘要) |
189
+ | `trace.add_sink(fn)` | 注册结构化 trace 事件回调 |
190
+ | `trace.remove_sink(fn)` | 移除已注册的回调 |
188
191
  | `trace.info(msg)` / `trace.error(msg)` / `trace.warning(msg)` / `trace.success(msg)` / `trace.debug(msg)` | 输出日志 |
189
192
 
190
193
  ### assertions.py
@@ -113,14 +113,14 @@ connect()
113
113
  trace.set_layer("DRIVER", True)
114
114
  trace.set_layer("PROTOCOL", True)
115
115
 
116
- # 寻卡
116
+ # 寻卡(自动切换协议解析器)
117
117
  card_info = active()
118
118
  ASSERT_IS_NOT_NONE(card_info, "未扫描到卡片")
119
- print(f"UID: {card_info['uid'].hex().upper()}")
119
+ print(f"UID: {' '.join(f'{b:02X}' for b in card_info.uid)}")
120
120
 
121
121
  # 创建卡片实例
122
122
  from nfctester.registry import CardRegistry
123
- card = CardRegistry.create("mifare", reader=get_reader())
123
+ card = CardRegistry.create("mifare_classic", reader=get_reader())
124
124
 
125
125
  # 关闭连接
126
126
  close()
@@ -132,8 +132,8 @@ close()
132
132
  from nfc import *
133
133
 
134
134
  with session() as reader:
135
- card_info = reader.find()
136
- print(card_info)
135
+ card_info = reader.active()
136
+ print(card_info.uid)
137
137
  # 自动断开连接
138
138
  ```
139
139
 
@@ -157,7 +157,7 @@ with session() as reader:
157
157
  |------|------|
158
158
  | `connect(port, reader_type)` | 连接读卡器 |
159
159
  | `get_reader()` | 获取当前读卡器实例 |
160
- | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 uid/atq/sak |
160
+ | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 CardInfo (uid/atq/sak),自动切换协议解析器 |
161
161
  | `transceive(data, tx_crc, rx_crc)` | 底层帧交互 |
162
162
  | `transceive_bits(data, last_tx_bits, tx_crc, rx_crc)` | 位控制帧交互 |
163
163
  | `reqa()` | ISO14443-A REQA |
@@ -175,6 +175,9 @@ with session() as reader:
175
175
  |------|------|
176
176
  | `trace.set_layer(layer, enable)` | 开启/关闭追踪层 |
177
177
  | `trace.set_level(level)` | 设置日志级别 |
178
+ | `trace.set_parse(level)` | 设置解析级别 (0=关闭, 1=hex+摘要) |
179
+ | `trace.add_sink(fn)` | 注册结构化 trace 事件回调 |
180
+ | `trace.remove_sink(fn)` | 移除已注册的回调 |
178
181
  | `trace.info(msg)` / `trace.error(msg)` / `trace.warning(msg)` / `trace.success(msg)` / `trace.debug(msg)` | 输出日志 |
179
182
 
180
183
  ### assertions.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nfcscript"
3
- version = "0.0.11"
3
+ version = "0.0.13"
4
4
  description = "A plugin-based core engine for DSL-style NFC testing automation."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -34,7 +34,7 @@ dev = [
34
34
  ]
35
35
 
36
36
  [tool.bumpversion]
37
- current_version = "0.0.11"
37
+ current_version = "0.0.13"
38
38
  commit = true
39
39
  tag = true
40
40
  allow_dirty = true
@@ -59,8 +59,6 @@ def main():
59
59
  parser.add_argument("--trace-protocol", action="store_true", help="开启 PROTOCOL 层追踪")
60
60
  parser.add_argument("--trace-parse", type=int, default=None, choices=[0, 1, 2],
61
61
  help="解析级别: 0=关闭 1=简单 2=复杂 (默认: NFC_TRACE_PARSE 或 1)")
62
- parser.add_argument("--trace-card-type", default=None,
63
- help="卡片类型标识 (默认: NFC_TRACE_CARD_TYPE)")
64
62
  parser.add_argument("--trace-level", default="INFO", help="日志级别 (默认: INFO)")
65
63
 
66
64
  args, extra = parser.parse_known_args()
@@ -87,7 +85,6 @@ def main():
87
85
  trace_driver = os.environ.get("NFC_TRACE_DRIVER", "").lower() in ("1", "true", "yes")
88
86
  trace_protocol = os.environ.get("NFC_TRACE_PROTOCOL", "").lower() in ("1", "true", "yes")
89
87
  trace_parse = os.environ.get("NFC_TRACE_PARSE", "1")
90
- trace_card_type = os.environ.get("NFC_TRACE_CARD_TYPE")
91
88
  trace_width = os.environ.get("NFC_TRACE_WIDTH")
92
89
 
93
90
  if args.trace_level != "INFO":
@@ -98,15 +95,11 @@ def main():
98
95
  trace_protocol = True
99
96
  if args.trace_parse is not None:
100
97
  trace_parse = str(args.trace_parse)
101
- if args.trace_card_type is not None:
102
- trace_card_type = args.trace_card_type
103
98
 
104
99
  trace.set_level(trace_level)
105
100
  trace.set_layer("DRIVER", trace_driver)
106
101
  trace.set_layer("PROTOCOL", trace_protocol)
107
102
  trace.set_parse(_parse_level(trace_parse))
108
- if trace_card_type is not None:
109
- trace.set_card_type(trace_card_type)
110
103
  if trace_width is not None:
111
104
  os.environ["CRFT_TRACE_WIDTH"] = trace_width
112
105
 
@@ -2,6 +2,7 @@ import os
2
2
  from contextlib import contextmanager
3
3
  from nfctester.drivers.card_reader import CardInfo, TransceiveBits
4
4
  from nfctester.registry import CardReaderRegistry
5
+ from nfctester.trace import trace as _trace
5
6
  from .assertions import ASSERT_EQUAL, ASSERT_IS_NOT_NONE, ASSERT_LEN
6
7
  from .checksum import GET_BCC
7
8
  from .hex_util import FORMAT_HEX
@@ -102,7 +103,7 @@ def active(low_layer: bool = False, ignore_error: bool = False, reqa_cmd: int =
102
103
  # 手动底层寻卡流程
103
104
  # 1. REQA
104
105
  res_reqa = reqa(cmd=reqa_cmd)
105
- if not res_reqa:
106
+ if not res_reqa.data:
106
107
  return None
107
108
  atq = res_reqa.data
108
109
 
@@ -110,7 +111,7 @@ def active(low_layer: bool = False, ignore_error: bool = False, reqa_cmd: int =
110
111
  full_uid = []
111
112
  for cl in [1, 2, 3]:
112
113
  res = anticoll(cl_level=cl, nvb=0x20)
113
- if not res or not res.data:
114
+ if not res.data:
114
115
  return None
115
116
 
116
117
  data = res.data
@@ -146,7 +147,10 @@ def active(low_layer: bool = False, ignore_error: bool = False, reqa_cmd: int =
146
147
  sak = sak_res[0] if sak_res else 0
147
148
  break
148
149
 
149
- return CardInfo(uid=full_uid, atq=atq, sak=sak)
150
+ info = CardInfo(uid=full_uid, atq=atq, sak=sak)
151
+ atqa = int.from_bytes(atq, "little")
152
+ _trace.set_parser(atqa, sak)
153
+ return info
150
154
 
151
155
 
152
156
  def transceive(data: list[int], tx_crc: bool = True, rx_crc: bool = True) -> list[int]:
@@ -167,7 +171,7 @@ def transceive(data: list[int], tx_crc: bool = True, rx_crc: bool = True) -> lis
167
171
  return res.data if res and res.data else []
168
172
 
169
173
 
170
- def transceive_bits(data: list[int], last_tx_bits: int = 0, tx_crc: bool = True, rx_crc: bool = True) -> TransceiveBits | None:
174
+ def transceive_bits(data: list[int], last_tx_bits: int = 0, tx_crc: bool = True, rx_crc: bool = True) -> TransceiveBits:
171
175
  """
172
176
  使用底层帧交互方式发送数据,并支持对最后一个字节进行位控制。
173
177
 
@@ -178,14 +182,15 @@ def transceive_bits(data: list[int], last_tx_bits: int = 0, tx_crc: bool = True,
178
182
  rx_crc: 接收时是否自动校验 CRC。False 则原样接收不校验。
179
183
 
180
184
  Returns:
181
- TransceiveBits | None: 包含 data 和 bits 属性,失败返回 None。
185
+ TransceiveBits: 包含 data 和 bits 属性,失败返回空的 TransceiveBits(data=[], bits=0)。
182
186
  """
183
187
  _state.ensure_connected()
184
188
  reader = _state.reader
185
- return reader.transceive(data, last_tx_bits=last_tx_bits, tx_crc=tx_crc, rx_crc=rx_crc)
189
+ res = reader.transceive(data, last_tx_bits=last_tx_bits, tx_crc=tx_crc, rx_crc=rx_crc)
190
+ return res if res else TransceiveBits(data=[], bits=0)
186
191
 
187
192
 
188
- def reqa(cmd: int = 0x26) -> TransceiveBits | None:
193
+ def reqa(cmd: int = 0x26) -> TransceiveBits:
189
194
  """
190
195
  ISO14443-A REQA (7 bits)。
191
196
 
@@ -193,27 +198,27 @@ def reqa(cmd: int = 0x26) -> TransceiveBits | None:
193
198
  cmd: REQA 命令字节,默认 0x26。
194
199
 
195
200
  Returns:
196
- TransceiveBits | None: 包含 data 和 bits 属性,失败返回 None。
201
+ TransceiveBits: 包含 data 和 bits 属性,失败返回空的 TransceiveBits(data=[], bits=0)。
197
202
  """
198
203
  return transceive_bits([cmd], last_tx_bits=7, tx_crc=False, rx_crc=False)
199
204
 
200
205
 
201
- def wupa() -> TransceiveBits | None:
206
+ def wupa() -> TransceiveBits:
202
207
  """
203
208
  ISO14443-A WUPA (7 bits)。
204
209
 
205
210
  Returns:
206
- TransceiveBits | None: 包含 data 和 bits 属性,失败返回 None。
211
+ TransceiveBits: 包含 data 和 bits 属性,失败返回空的 TransceiveBits(data=[], bits=0)。
207
212
  """
208
213
  return transceive_bits([0x52], last_tx_bits=7, tx_crc=False, rx_crc=False)
209
214
 
210
215
 
211
- def halt() -> list[int] | None:
216
+ def halt() -> list[int]:
212
217
  """
213
218
  ISO14443-A HALT (Standard Frame)。
214
219
 
215
220
  Returns:
216
- list[int] | None: 返回响应数据,失败返回 None。
221
+ list[int]: 返回响应数据。
217
222
  """
218
223
  return transceive([0x50, 0x00], tx_crc=True, rx_crc=True)
219
224
 
@@ -237,7 +242,7 @@ def _get_sel_cmd(cl_level: int) -> int:
237
242
  return sel_map[cl_level]
238
243
 
239
244
 
240
- def anticoll(cl_level: int, nvb: int = 0x20, uid_prefix: list[int] = []) -> TransceiveBits | None:
245
+ def anticoll(cl_level: int, nvb: int = 0x20, uid_prefix: list[int] = []) -> TransceiveBits:
241
246
  """
242
247
  ISO14443-A ANTICOLL (Anti-collision)。
243
248
 
@@ -247,13 +252,13 @@ def anticoll(cl_level: int, nvb: int = 0x20, uid_prefix: list[int] = []) -> Tran
247
252
  uid_prefix: 已知的部分 UID (0-4 字节)。
248
253
 
249
254
  Returns:
250
- TransceiveBits | None: 包含 data 和 bits 属性,失败返回 None。
255
+ TransceiveBits: 包含 data 和 bits 属性,失败返回空的 TransceiveBits(data=[], bits=0)。
251
256
  """
252
257
  cmd = [_get_sel_cmd(cl_level), nvb] + uid_prefix
253
258
  return transceive_bits(cmd, last_tx_bits=0, tx_crc=False, rx_crc=False)
254
259
 
255
260
 
256
- def select(cl_level: int, uid: list[int]) -> list[int] | None:
261
+ def select(cl_level: int, uid: list[int]) -> list[int]:
257
262
  """
258
263
  ISO14443-A SELECT (Standard Frame)。
259
264
 
@@ -262,7 +267,7 @@ def select(cl_level: int, uid: list[int]) -> list[int] | None:
262
267
  uid: 完整 5 字节 UID (包含 BCC)。
263
268
 
264
269
  Returns:
265
- list[int] | None: 返回响应数据,失败返回 None。
270
+ list[int]: 返回响应数据。
266
271
  """
267
272
  cmd = [_get_sel_cmd(cl_level), 0x70] + uid
268
273
  return transceive(cmd, tx_crc=True, rx_crc=True)
@@ -39,16 +39,6 @@ def set_parse(level: int = 1):
39
39
  _trace.set_parse(level)
40
40
 
41
41
 
42
- def set_card_type(card_type: str):
43
- """
44
- 设置当前卡片类型,注入到所有协议解析器的 state 中。
45
-
46
- Args:
47
- card_type: 卡片类型标识,如 "mifare_classic_1k", "ntag213"
48
- """
49
- _trace.set_card_type(card_type)
50
-
51
-
52
42
  def info(msg: str):
53
43
  """输出 INFO 级别日志"""
54
44
  _trace.info(msg)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nfcscript
3
- Version: 0.0.11
3
+ Version: 0.0.13
4
4
  Summary: A plugin-based core engine for DSL-style NFC testing automation.
5
5
  Author: crthu
6
6
  Requires-Python: >=3.14
@@ -123,14 +123,14 @@ connect()
123
123
  trace.set_layer("DRIVER", True)
124
124
  trace.set_layer("PROTOCOL", True)
125
125
 
126
- # 寻卡
126
+ # 寻卡(自动切换协议解析器)
127
127
  card_info = active()
128
128
  ASSERT_IS_NOT_NONE(card_info, "未扫描到卡片")
129
- print(f"UID: {card_info['uid'].hex().upper()}")
129
+ print(f"UID: {' '.join(f'{b:02X}' for b in card_info.uid)}")
130
130
 
131
131
  # 创建卡片实例
132
132
  from nfctester.registry import CardRegistry
133
- card = CardRegistry.create("mifare", reader=get_reader())
133
+ card = CardRegistry.create("mifare_classic", reader=get_reader())
134
134
 
135
135
  # 关闭连接
136
136
  close()
@@ -142,8 +142,8 @@ close()
142
142
  from nfc import *
143
143
 
144
144
  with session() as reader:
145
- card_info = reader.find()
146
- print(card_info)
145
+ card_info = reader.active()
146
+ print(card_info.uid)
147
147
  # 自动断开连接
148
148
  ```
149
149
 
@@ -167,7 +167,7 @@ with session() as reader:
167
167
  |------|------|
168
168
  | `connect(port, reader_type)` | 连接读卡器 |
169
169
  | `get_reader()` | 获取当前读卡器实例 |
170
- | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 uid/atq/sak |
170
+ | `active(low_layer, ignore_error, reqa_cmd)` | 寻卡,返回 CardInfo (uid/atq/sak),自动切换协议解析器 |
171
171
  | `transceive(data, tx_crc, rx_crc)` | 底层帧交互 |
172
172
  | `transceive_bits(data, last_tx_bits, tx_crc, rx_crc)` | 位控制帧交互 |
173
173
  | `reqa()` | ISO14443-A REQA |
@@ -185,6 +185,9 @@ with session() as reader:
185
185
  |------|------|
186
186
  | `trace.set_layer(layer, enable)` | 开启/关闭追踪层 |
187
187
  | `trace.set_level(level)` | 设置日志级别 |
188
+ | `trace.set_parse(level)` | 设置解析级别 (0=关闭, 1=hex+摘要) |
189
+ | `trace.add_sink(fn)` | 注册结构化 trace 事件回调 |
190
+ | `trace.remove_sink(fn)` | 移除已注册的回调 |
188
191
  | `trace.info(msg)` / `trace.error(msg)` / `trace.warning(msg)` / `trace.success(msg)` / `trace.debug(msg)` | 输出日志 |
189
192
 
190
193
  ### assertions.py
@@ -37,14 +37,6 @@ class TestSetParse:
37
37
  mock_trace.set_parse.assert_called_once_with(2)
38
38
 
39
39
 
40
- class TestSetCardType:
41
-
42
- @patch("nfc.trace._trace")
43
- def test_set_card_type(self, mock_trace):
44
- trace.set_card_type("mifare_classic_1k")
45
- mock_trace.set_card_type.assert_called_once_with("mifare_classic_1k")
46
-
47
-
48
40
  class TestLogFunctions:
49
41
 
50
42
  @patch("nfc.trace._trace")
File without changes
File without changes
File without changes
File without changes