expect-pty 0.7.7 → 0.7.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45e6676261fec4b5bcd7db41298fd513a47da86308a29f53702311a6b5219f59
4
- data.tar.gz: 19354431d871147ebd778424288ea2d3a2f788d92e8674f6c779b8c560b23f1b
3
+ metadata.gz: ad46afe78b754e37c5aa6995b9b1235d732a1f9faf372077b572e584ad186613
4
+ data.tar.gz: 7a3165503b1543396abde0d77713134296b7f2d818911c8bbca9e4b505d85227
5
5
  SHA512:
6
- metadata.gz: efdb068466acf441e74d839e9830d31f59ab18bf989726319dc6f97fb049d34a913ef53fdc399b6287bebc94d3993a5b97fb8fe41965c3c1a5e6ed3fb9aba924
7
- data.tar.gz: c4c4d20ef93293c8958da3ffcf6f2ebc0245b37b07caea6fe5f2af3b9ef73ba29d365134f5c7be1a693a43a3c163724143a805ad4f19d27266df7ec7e9485717
6
+ metadata.gz: 78b96691fcf4e0ab916286bfec7a978c3bb1de6fdc082af0ac3e5408eba3f2119c06789fd867b6f3552460ef0acee1aa45302ebb4081c21943197bd124f714c5
7
+ data.tar.gz: 9c7f6b06eb593608b4474fac1b0a06464cda52965221e815fd921677af1d889cdbed29aefc801d9e08383685517df293a290dc97de92e724b473608b21165c94
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.7.8 - 2026-10-11
6
+
7
+ - 将内部资源所有权对象命名为 ResourceOwner,并统一源码、示例和测试辅助代码的中文注释。
8
+ - 补齐字节前缀算法、脱敏窗口及基准、API 校验和示例辅助方法的契约注释,明确缓存、输入和副作用边界。
9
+
5
10
  ## 0.7.7 - 2026-10-11
6
11
 
7
12
  - 修复人工接管时终端模式交接中断、恢复异常覆盖业务主异常的问题;分别尝试恢复两侧输出、转义规则和终端模式。
@@ -2,14 +2,19 @@
2
2
 
3
3
  module Expect
4
4
  # 计算字节窗口尾部与规则真前缀重合的最长长度;转义暂存和流尾脱敏使用同一边界判断。
5
- # 不持有输入或修改缓冲,调用方可为冻结规则提供前缀表缓存;短规则不建立表。
5
+ # 不持有输入或修改缓冲;仅长候选的扫描需要 KMP 回退表,冻结规则可跨调用缓存该表。
6
6
  # @api private
7
7
  module LiteralPrefix
8
+ # 返回 buffer 后缀与 pattern 真前缀重合的最大字节数;真前缀不包含完整 pattern。
9
+ # 例如 "xAB" 与 "ABC" 返回 2;空输入、长度不足 2 的规则或没有重合时返回 0。
10
+ # 两个字符串须由调用方转为二进制编码;此处不转码、不校验,也不修改它们。
11
+ # prefix_tables 为可选的可变 Hash;仅冻结 pattern 使用缓存,否则为本次调用建立临时表。
8
12
  def self.length(buffer, pattern, prefix_tables = nil)
9
13
  maximum = [pattern.bytesize - 1, buffer.bytesize].min
10
14
  return 0 unless maximum.positive?
11
15
 
12
- # 尾字节不在候选前缀内时无需扫描;最长候选本身命中时也不必构造 KMP 表。
16
+ # 重合前缀的末字节必须等于输入尾字节;排除无法结束在该字节上的更长候选。
17
+ # 最长候选直接命中时不必构造回退表;小于 64 字节的候选改用有界枚举。
13
18
  ending = pattern.rindex(buffer.byteslice(-1, 1), maximum - 1)
14
19
  return 0 unless ending
15
20
 
@@ -19,6 +24,7 @@ module Expect
19
24
 
20
25
  failure = prefix_tables && pattern.frozen? ? (prefix_tables[pattern] ||= []) : []
21
26
  extend_failure_table(pattern, failure, maximum)
27
+ # 只扫描可能重合的尾部;不匹配时沿回退表保留已有重合,避免从每个候选起点重扫。
22
28
  matched = 0
23
29
  index = buffer.bytesize - maximum
24
30
  while index < buffer.bytesize
@@ -30,6 +36,9 @@ module Expect
30
36
  matched
31
37
  end
32
38
 
39
+ # 将同一规则的可变回退表补齐到 length 项,已有项不重算,较长的缓存也不截断。
40
+ # failure[i] 是 pattern[0..i] 的最长真前缀与后缀重合长度,供失配时选择下一匹配状态。
41
+ # length 不超过规则的真前缀长度;表只能用于生成它的同一规则内容。
33
42
  def self.extend_failure_table(pattern, failure, length)
34
43
  failure << 0 if failure.empty?
35
44
  matched = failure.last
@@ -49,6 +58,8 @@ module Expect
49
58
  end
50
59
  end
51
60
 
61
+ # 从 maximum 向下枚举短候选,返回首个后缀重合长度,无命中时返回 0。
62
+ # 调用方已限制候选规模;每次比较只切取规则前缀,不创建完整输入的副本或回退表。
52
63
  def self.short_length(buffer, pattern, maximum)
53
64
  maximum.downto(1) do |length|
54
65
  return length if buffer.end_with?(pattern.byteslice(0, length))
@@ -13,7 +13,7 @@ module Expect
13
13
  filter.append(data) + filter.finish(partial: false)
14
14
  end
15
15
 
16
- # pending 保存尚不能安全输出的原字节,hidden 的对应字节用 0/1 表示是否需要遮盖。
16
+ # 复制冻结替换文本与规则;pending 保存尚不能安全输出的原字节,hidden 用 0/1 标记遮盖。
17
17
  def initialize(patterns, replacement: "[FILTERED]")
18
18
  unless replacement.is_a?(String) && !replacement.empty?
19
19
  raise ArgumentError, "replacement must be a nonempty String"
@@ -67,6 +67,7 @@ module Expect
67
67
  private
68
68
 
69
69
  # 输出资格与秘密掩码分开保存;仅遇到被抑制的分片时才分配额外字节图。
70
+ # 三个字节窗口始终对齐;先标记完整命中,再释放最长规则长度减一之外的安全前缀。
70
71
  def append_chunk(data, visible:)
71
72
  raise ArgumentError, "data must be a String" unless data.is_a?(String)
72
73
 
@@ -147,6 +148,7 @@ module Expect
147
148
  end
148
149
 
149
150
  # 仅级别切换后的流需要按输出资格再分段,普通过滤不承担额外区间调用。
151
+ # 将指定掩码区间中可见的字节追加到 output;被抑制的明文会断开 masking,隐藏字节不会。
150
152
  def release_suppressed(output, starting, ending, hidden:)
151
153
  while starting < ending
152
154
  suppressed = @suppressed.getbyte(starting) == 1
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Expect
4
- # 独立保存句柄和 PID 所有权,让终结器无需直接捕获会话即可清理遗弃资源。
5
- # IO 是否关闭与子进程是否退出分别记录;不能仅凭句柄状态清空 PID 或伪造退出状态。
4
+ # 管理 IO 关闭责任和直属子进程归属,供显式关闭与 GC 回收共用。
5
+ # 不直接引用 Session、日志目标或用户回调;句柄关闭与进程退出分别记录。
6
6
  # @api private
7
- class SessionResources
8
- # 缓存已取得的状态;被外部回收的进程仍保留未知状态,不伪造成功。
7
+ class ResourceOwner
8
+ # 缓存已取得的 Process::Status;被外部回收的进程仍保留未知状态,不伪造成功。
9
9
  attr_accessor :status
10
10
  # own 控制句柄关闭责任,owner 控制进程回收责任;fork 后两者不能混为一谈。
11
11
  attr_reader :pid, :reader, :writer, :slave, :owner, :own
@@ -60,7 +60,7 @@ module Expect
60
60
  status
61
61
  end
62
62
 
63
- # GC 兜底关闭所属句柄并终止尚存活的子进程;不执行用户代码或软关闭等待。
63
+ # GC 兜底关闭所属句柄并终止尚存活的子进程;不调用会话日志或转义回调,也不进行软关闭等待。
64
64
  # 可直接用绑定到资源对象的 Method 注册终结器,忽略 Ruby 传来的被回收对象 ID。
65
65
  def finalize(_object_id = nil)
66
66
  return unless owner == Process.pid
@@ -77,7 +77,7 @@ module Expect
77
77
 
78
78
  private
79
79
 
80
- # GC 只做一次非阻塞回收和最多两次信号尝试;失败也把等待交给 detach,不运行用户回调。
80
+ # GC 只做一次非阻塞回收和最多两次信号尝试;失败也把等待交给 detach,不调用会话日志或转义回调。
81
81
  def finalize_child
82
82
  begin
83
83
  reap
@@ -5,7 +5,7 @@ require_relative "interaction"
5
5
 
6
6
  module Expect
7
7
  # 一个真实 PTY 或 IO 会话;缓冲、最近结果和所属进程都保存在本对象中。
8
- # SessionResources 保存可独立回收的资源账本;Matcher、Relay 仅在操作期间借用会话状态。
8
+ # ResourceOwner 保存可独立回收的资源账本;Matcher、Relay 仅在操作期间借用会话状态。
9
9
  # 日志与协议目标属于调用方,显式关闭和 GC 均不得接管这些借用对象。
10
10
  class Session
11
11
  include Logging
@@ -58,7 +58,7 @@ module Expect
58
58
 
59
59
  @slave.raw! if raw
60
60
  from_child = to_parent = nil
61
- Cleanup.always(-> { SessionResources.close_handles(from_child, to_parent) }) do
61
+ Cleanup.always(-> { ResourceOwner.close_handles(from_child, to_parent) }) do
62
62
  # 错误管道的写端在 exec 成功时自动关闭;父进程据此区分成功启动与 exec 前失败。
63
63
  # 管道返回到多重赋值之间也属于接管区间,确保 ensure 始终能找到两个端点。
64
64
  Thread.handle_interrupt(Object => :never) { from_child, to_parent = IO.pipe }
@@ -290,7 +290,7 @@ module Expect
290
290
  if @resources
291
291
  close(graceful:)
292
292
  elsif own
293
- SessionResources.close_handles(reader, writer, slave)
293
+ ResourceOwner.close_handles(reader, writer, slave)
294
294
  end
295
295
  end
296
296
 
@@ -386,7 +386,7 @@ module Expect
386
386
  # 初始化前先登记所有权;参数校验失败也能释放已取得的端点。
387
387
  def initialize_io(reader, writer:, slave: nil, own: false, timeout: nil, write_timeout: nil,
388
388
  buffer_limit: nil, logger: nil, transcript: nil, outputs: [])
389
- @resources = SessionResources.new(reader, writer:, slave:, own:)
389
+ @resources = ResourceOwner.new(reader, writer:, slave:, own:)
390
390
  raise ArgumentError, "reader must be a real IO" unless reader.is_a?(IO) && !reader.closed?
391
391
  raise ArgumentError, "writer must be a real IO" unless writer.is_a?(IO) && !writer.closed?
392
392
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Expect
4
4
  # Gem 与库共用的版本号;独立文件使 gemspec 无需加载完整会话实现。
5
- VERSION = "0.7.7"
5
+ VERSION = "0.7.8"
6
6
  end
data/lib/expect.rb CHANGED
@@ -5,7 +5,7 @@ require "io/console"
5
5
  require "stringio"
6
6
  require_relative "expect/version"
7
7
  require_relative "expect/result"
8
- require_relative "expect/session_resources"
8
+ require_relative "expect/resource_owner"
9
9
  require_relative "expect/pattern"
10
10
  require_relative "expect/pattern_list"
11
11
  require_relative "expect/matcher"
@@ -72,7 +72,7 @@ module Expect
72
72
  if session && (block_given? || !initialized)
73
73
  session.cleanup_session(io, writer:, own:, graceful: initialized && graceful)
74
74
  elsif !session && own
75
- SessionResources.close_handles(io, writer)
75
+ ResourceOwner.close_handles(io, writer)
76
76
  end
77
77
  end
78
78
  Cleanup.always(cleanup) do
data/sig/expect.rbs CHANGED
@@ -1,4 +1,4 @@
1
- # Public Session API; internal matching and relay protocols are documented with @api private.
1
+ # 公开 Session 接口;内部匹配与转接协议在源码中标记为 @api private。
2
2
  module Expect
3
3
  type duration = Numeric | String
4
4
  type pattern = String | Regexp | :eof | :timeout
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expect-pty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - expect-pty contributors
@@ -76,9 +76,9 @@ files:
76
76
  - lib/expect/redactor.rb
77
77
  - lib/expect/relay.rb
78
78
  - lib/expect/relay_writer.rb
79
+ - lib/expect/resource_owner.rb
79
80
  - lib/expect/result.rb
80
81
  - lib/expect/session.rb
81
- - lib/expect/session_resources.rb
82
82
  - lib/expect/version.rb
83
83
  - sig/expect.rbs
84
84
  homepage: https://github.com/gatework/expect-ruby