eyeprocesspy 0.1.0__py3-none-any.whl

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 (108) hide show
  1. eyeprocesspy/__init__.py +177 -0
  2. eyeprocesspy/adapters.py +148 -0
  3. eyeprocesspy/advanced_process_irt_07.py +515 -0
  4. eyeprocesspy/bayesian_3pl_08.py +67 -0
  5. eyeprocesspy/benchmark_reproducibility_10.py +521 -0
  6. eyeprocesspy/benchmark_stress_09.py +898 -0
  7. eyeprocesspy/compositional_aoi_10.py +948 -0
  8. eyeprocesspy/context_structure_08.py +526 -0
  9. eyeprocesspy/coordinates.py +77 -0
  10. eyeprocesspy/core_plots_10.py +986 -0
  11. eyeprocesspy/dataset.py +157 -0
  12. eyeprocesspy/dynamic_irt.py +1004 -0
  13. eyeprocesspy/engine_adapters.py +587 -0
  14. eyeprocesspy/evidence_graph.py +121 -0
  15. eyeprocesspy/exceptions.py +23 -0
  16. eyeprocesspy/foundation_09.py +1678 -0
  17. eyeprocesspy/frontier_08.py +80 -0
  18. eyeprocesspy/functional_pupil.py +544 -0
  19. eyeprocesspy/gazepoint.py +415 -0
  20. eyeprocesspy/gazepoint_real_10.py +588 -0
  21. eyeprocesspy/gazepoint_workflow_10.py +1870 -0
  22. eyeprocesspy/governance_09.py +1043 -0
  23. eyeprocesspy/grouped_validation_10.py +521 -0
  24. eyeprocesspy/importers.py +322 -0
  25. eyeprocesspy/interoperability_storage_10.py +956 -0
  26. eyeprocesspy/io_validation_10.py +2420 -0
  27. eyeprocesspy/irt.py +1624 -0
  28. eyeprocesspy/irt_validation_07.py +344 -0
  29. eyeprocesspy/legacy_models.py +515 -0
  30. eyeprocesspy/mapping.py +7 -0
  31. eyeprocesspy/measurement_intelligence.py +366 -0
  32. eyeprocesspy/measurement_intelligence_utils_10.py +416 -0
  33. eyeprocesspy/measurement_quality_legacy.py +265 -0
  34. eyeprocesspy/multimodal_staged.py +744 -0
  35. eyeprocesspy/negative_controls_09.py +710 -0
  36. eyeprocesspy/operational_validation_08.py +350 -0
  37. eyeprocesspy/partitioned_storage_10.py +943 -0
  38. eyeprocesspy/plots_completion_08.py +388 -0
  39. eyeprocesspy/plots_functional_pupil.py +49 -0
  40. eyeprocesspy/plots_governance_08.py +151 -0
  41. eyeprocesspy/plots_governance_09.py +163 -0
  42. eyeprocesspy/plots_irt.py +195 -0
  43. eyeprocesspy/plots_irt_08.py +164 -0
  44. eyeprocesspy/plots_legacy_models.py +29 -0
  45. eyeprocesspy/plots_multimodal_staged.py +220 -0
  46. eyeprocesspy/plots_operational_08.py +44 -0
  47. eyeprocesspy/plots_process_irt_07.py +144 -0
  48. eyeprocesspy/plots_process_quality_09.py +56 -0
  49. eyeprocesspy/preprocess_features_09.py +1637 -0
  50. eyeprocesspy/probabilistic_aoi_10.py +855 -0
  51. eyeprocesspy/process_dynamics.py +279 -0
  52. eyeprocesspy/process_governance_08.py +634 -0
  53. eyeprocesspy/process_irt_07.py +803 -0
  54. eyeprocesspy/process_quality_09.py +478 -0
  55. eyeprocesspy/pupil_missingness.py +158 -0
  56. eyeprocesspy/reproducibility_provenance_09.py +735 -0
  57. eyeprocesspy/requested_api_07.py +311 -0
  58. eyeprocesspy/resources/extdata/api-lifecycle-module-policy-0.9.csv +109 -0
  59. eyeprocesspy/resources/extdata/api-lifecycle-registry-0.9.csv +1183 -0
  60. eyeprocesspy/resources/extdata/benchmark-study/LICENSE.txt +1 -0
  61. eyeprocesspy/resources/extdata/benchmark-study/README.md +5 -0
  62. eyeprocesspy/resources/extdata/benchmark-study/SYNTHETIC_DATA.txt +1 -0
  63. eyeprocesspy/resources/extdata/benchmark-study/aoi_definitions.csv +4 -0
  64. eyeprocesspy/resources/extdata/benchmark-study/events.csv +193 -0
  65. eyeprocesspy/resources/extdata/benchmark-study/expected_outputs.csv +12 -0
  66. eyeprocesspy/resources/extdata/benchmark-study/gaze_samples.csv +4801 -0
  67. eyeprocesspy/resources/extdata/benchmark-study/items.csv +9 -0
  68. eyeprocesspy/resources/extdata/benchmark-study/manifest.csv +11 -0
  69. eyeprocesspy/resources/extdata/benchmark-study/participants.csv +13 -0
  70. eyeprocesspy/resources/extdata/benchmark-study/provenance.csv +7 -0
  71. eyeprocesspy/resources/extdata/benchmark-study/pupil_samples.csv +3841 -0
  72. eyeprocesspy/resources/extdata/benchmark-study/quality.csv +97 -0
  73. eyeprocesspy/resources/extdata/benchmark-study/recordings.csv +13 -0
  74. eyeprocesspy/resources/extdata/benchmark-study/responses.csv +97 -0
  75. eyeprocesspy/resources/stan/dynamic_irtree_hidden.stan +128 -0
  76. eyeprocesspy/resources/stan/dynamic_irtree_observed.stan +65 -0
  77. eyeprocesspy/resources/stan/four-channel-response-rt-gaze-pupil.stan +105 -0
  78. eyeprocesspy/resources/stan/functional_pupil_irt.stan +109 -0
  79. eyeprocesspy/resources/stan/gaze_diffusion_irt.stan +127 -0
  80. eyeprocesspy/resources/stan/m0-response-reference-0-10.stan +67 -0
  81. eyeprocesspy/resources/stan/m1-response-rt-reference-0-10.stan +130 -0
  82. eyeprocesspy/resources/stan/m2-man2022-response-rt-gaze-0-10.stan +187 -0
  83. eyeprocesspy/resources/stan/m3-ablation-subset-0-10.stan +144 -0
  84. eyeprocesspy/resources/stan/m3-response-rt-gaze-pupil-0-10.stan +217 -0
  85. eyeprocesspy/resources/stan/m4-trait-conditioned-state-0-11.stan +417 -0
  86. eyeprocesspy/resources/stan/theory_strategy_mixture.stan +70 -0
  87. eyeprocesspy/resources/stan/three-way-response-rt-gaze.stan +78 -0
  88. eyeprocesspy/schema.py +96 -0
  89. eyeprocesspy/semantic_validation_07.py +399 -0
  90. eyeprocesspy/sensitivity_08.py +92 -0
  91. eyeprocesspy/software_paper_evidence_09.py +496 -0
  92. eyeprocesspy/timebase.py +82 -0
  93. eyeprocesspy/validation_atlas_09.py +535 -0
  94. eyeprocesspy/validation_completion_10.py +875 -0
  95. eyeprocesspy/validation_evidence_10.py +698 -0
  96. eyeprocesspy/validation_evidence_programs_09.py +720 -0
  97. eyeprocesspy/validation_extras_09.py +681 -0
  98. eyeprocesspy/validation_orchestration_10.py +1687 -0
  99. eyeprocesspy/validation_orchestration_completion_10.py +1282 -0
  100. eyeprocesspy/validation_program_10.py +706 -0
  101. eyeprocesspy/validation_stress_freeze_09.py +861 -0
  102. eyeprocesspy/vendor_corpus_10.py +1295 -0
  103. eyeprocesspy/vendor_importers_10.py +1512 -0
  104. eyeprocesspy-0.1.0.dist-info/METADATA +234 -0
  105. eyeprocesspy-0.1.0.dist-info/RECORD +108 -0
  106. eyeprocesspy-0.1.0.dist-info/WHEEL +5 -0
  107. eyeprocesspy-0.1.0.dist-info/licenses/LICENSE +1 -0
  108. eyeprocesspy-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,177 @@
1
+ """eyeprocesspy: Python parity implementation of R eyeprocess 0.11.1."""
2
+ __version__ = "0.1.0"
3
+ __r_reference_version__ = "0.11.1"
4
+
5
+ from .exceptions import *
6
+ from .schema import eye_schema, schema_table, empty_eye_table, standardize_eye_table, validate_eye_table, canonical_table_names, new_coordinate_space
7
+ from .mapping import eye_mapping
8
+ from .dataset import EyeDataset, new_eye_dataset, is_eye_dataset, validate_eye_dataset, get_eye_table, set_eye_table, append_eye_table, add_provenance, provenance_manifest, compact_eye_dataset
9
+ from .timebase import EyeClockTransform, estimate_sampling_rate, normalize_timebase, audit_timebase, align_clock, estimate_clock_transform, apply_clock_transform
10
+ from .coordinates import register_coordinate_space, coordinate_space, convert_coordinates, audit_coordinate_spaces
11
+
12
+ from .importers import validate_eye_mapping, infer_eye_mapping, read_eye_generic
13
+ from .adapters import register_eye_adapter, unregister_eye_adapter, supported_eye_formats, detect_eye_format, read_eye_export, read_eye_folder, combine_eye_datasets, remap_recording_ids
14
+
15
+ from .gazepoint import (is_gazepoint_export, gp_identify_export_type, gp_profile_export, gp_list_export_fields, gp_validate_export, read_gazepoint, read_gazepoint_gaze, read_gazepoint_fixations, read_gazepoint_events, read_gazepoint_folder, gp_pair_exports, gp_match_recordings, gp_match_biometrics, gp_audit_file_pairs, read_gazepoint_biometrics, read_gazepoint_combined, gp_parse_user_events, gp_parse_media_events)
16
+ register_eye_adapter("gazepoint", is_gazepoint_export, read_gazepoint, gp_validate_export, priority=100, overwrite=True)
17
+
18
+ from .irt import *
19
+ from .plots_irt import *
20
+ from .measurement_intelligence import *
21
+
22
+ __all__ = [n for n in globals() if not n.startswith('_')]
23
+ from .dynamic_irt import *
24
+
25
+ __all__ = [n for n in globals() if not n.startswith('_')]
26
+ from .process_irt_07 import *
27
+
28
+ __all__ = [n for n in globals() if not n.startswith('_')]
29
+ from .advanced_process_irt_07 import *
30
+ from .irt_validation_07 import *
31
+ from .plots_process_irt_07 import *
32
+
33
+ __all__ = [n for n in globals() if not n.startswith('_')]
34
+
35
+ from .semantic_validation_07 import *
36
+ from .requested_api_07 import *
37
+
38
+ __all__ = [n for n in globals() if not n.startswith("_")]
39
+
40
+ from .context_structure_08 import *
41
+ from .frontier_08 import *
42
+ from .sensitivity_08 import *
43
+ from .bayesian_3pl_08 import *
44
+ from .plots_irt_08 import *
45
+
46
+ __all__ = [n for n in globals() if not n.startswith("_")]
47
+
48
+ from .multimodal_staged import *
49
+ from .plots_multimodal_staged import *
50
+ from .legacy_models import *
51
+ from .plots_legacy_models import *
52
+
53
+ __all__ = [n for n in globals() if not n.startswith("_")]
54
+
55
+ from .functional_pupil import *
56
+ from .plots_functional_pupil import *
57
+
58
+ __all__ = [n for n in globals() if not n.startswith("_")]
59
+
60
+ from .engine_adapters import *
61
+
62
+ __all__ = [n for n in globals() if not n.startswith("_")]
63
+
64
+ from .process_quality_09 import *
65
+ from .plots_process_quality_09 import *
66
+
67
+ __all__ = [n for n in globals() if not n.startswith("_")]
68
+
69
+ from .measurement_quality_legacy import *
70
+ from .pupil_missingness import *
71
+ from .process_dynamics import *
72
+ from .evidence_graph import *
73
+
74
+ __all__ = [n for n in globals() if not n.startswith('_')]
75
+
76
+ from .process_governance_08 import *
77
+
78
+ __all__ = [n for n in globals() if not n.startswith('_')]
79
+ from .plots_governance_08 import *
80
+ __all__ = [n for n in globals() if not n.startswith('_')]
81
+
82
+ from .operational_validation_08 import *
83
+ from .plots_operational_08 import *
84
+
85
+ __all__ = [n for n in globals() if not n.startswith('_')]
86
+
87
+ from .governance_09 import *
88
+ from .plots_governance_09 import *
89
+
90
+ __all__ = [n for n in globals() if not n.startswith('_')]
91
+
92
+ from .foundation_09 import *
93
+ __all__ = [n for n in globals() if not n.startswith("_")]
94
+
95
+ from .preprocess_features_09 import *
96
+ __all__ = [n for n in globals() if not n.startswith("_")]
97
+
98
+ from .io_validation_10 import *
99
+ __all__ = [n for n in globals() if not n.startswith("_")]
100
+
101
+ from .gazepoint_real_10 import *
102
+ __all__ = [n for n in globals() if not n.startswith("_")]
103
+
104
+ from .gazepoint_workflow_10 import *
105
+ __all__ = [n for n in globals() if not n.startswith("_")]
106
+
107
+ from .interoperability_storage_10 import *
108
+ __all__ = [n for n in globals() if not n.startswith("_")]
109
+
110
+ from .validation_program_10 import *
111
+ __all__ = [n for n in globals() if not n.startswith("_")]
112
+
113
+ from .validation_evidence_10 import *
114
+ __all__ = [n for n in globals() if not n.startswith("_")]
115
+
116
+ from .grouped_validation_10 import *
117
+ __all__ = [n for n in globals() if not n.startswith("_")]
118
+
119
+ from .validation_completion_10 import *
120
+ __all__ = [n for n in globals() if not n.startswith("_")]
121
+
122
+ from .validation_orchestration_10 import *
123
+ __all__ = [n for n in globals() if not n.startswith("_")]
124
+
125
+ from .validation_orchestration_completion_10 import *
126
+ __all__ = [n for n in globals() if not n.startswith("_")]
127
+
128
+ from .partitioned_storage_10 import *
129
+ __all__ = [n for n in globals() if not n.startswith("_")]
130
+
131
+ from .benchmark_reproducibility_10 import *
132
+ __all__ = [n for n in globals() if not n.startswith("_")]
133
+
134
+ from .vendor_importers_10 import *
135
+ __all__ = [n for n in globals() if not n.startswith("_")]
136
+
137
+ from .vendor_corpus_10 import *
138
+ __all__ = [n for n in globals() if not n.startswith("_")]
139
+
140
+ from .core_plots_10 import *
141
+ __all__ = [n for n in globals() if not n.startswith("_")]
142
+
143
+ from .measurement_intelligence_utils_10 import *
144
+ __all__ = [n for n in globals() if not n.startswith("_")]
145
+
146
+ from .probabilistic_aoi_10 import *
147
+ __all__ = [n for n in globals() if not n.startswith("_")]
148
+
149
+ from .compositional_aoi_10 import *
150
+ __all__ = [n for n in globals() if not n.startswith("_")]
151
+
152
+ from .plots_completion_08 import *
153
+ __all__ = [n for n in globals() if not n.startswith("_")]
154
+
155
+ from .validation_extras_09 import *
156
+ __all__ = [n for n in globals() if not n.startswith("_")]
157
+
158
+ from .negative_controls_09 import *
159
+ __all__ = [n for n in globals() if not n.startswith("_")]
160
+
161
+ from .benchmark_stress_09 import *
162
+ __all__ = [n for n in globals() if not n.startswith("_")]
163
+
164
+ from .reproducibility_provenance_09 import *
165
+ __all__ = [n for n in globals() if not n.startswith("_")]
166
+
167
+ from .software_paper_evidence_09 import *
168
+ __all__ = [n for n in globals() if not n.startswith("_")]
169
+
170
+ from .validation_evidence_programs_09 import *
171
+ __all__ = [n for n in globals() if not n.startswith("_")]
172
+
173
+ from .validation_stress_freeze_09 import *
174
+ __all__ = [n for n in globals() if not n.startswith("_")]
175
+
176
+ from .validation_atlas_09 import *
177
+ __all__ = [n for n in globals() if not n.startswith("_")]
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime, timezone
4
+ from pathlib import Path
5
+ import re
6
+ import warnings
7
+ from typing import Callable
8
+ import numpy as np
9
+ import pandas as pd
10
+
11
+ from .dataset import EyeDataset, add_provenance, is_eye_dataset, new_eye_dataset, validate_eye_dataset
12
+ from .schema import canonical_table_names
13
+ from .importers import _read_delimited, read_eye_generic
14
+
15
+ _ADAPTERS: dict[str, dict] = {}
16
+
17
+
18
+ def _now_utc():
19
+ return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
20
+
21
+
22
+ def register_eye_adapter(name, detect, read, validate=None, priority=0, overwrite=False):
23
+ """Register a vendor/custom reader using the frozen R adapter contract."""
24
+ if not isinstance(name,str) or not name: raise TypeError("`name` must be a single character value.")
25
+ if not callable(detect): raise TypeError("`detect` must be a function.")
26
+ if not callable(read): raise TypeError("`read` must be a function.")
27
+ if validate is not None and not callable(validate): raise TypeError("`validate` must be NULL/None or a function.")
28
+ if not overwrite and name in _ADAPTERS: raise ValueError(f"Adapter `{name}` is already registered.")
29
+ _ADAPTERS[name]={"name":name,"detect":detect,"read":read,"validate":validate,"priority":float(priority),"registered_at":_now_utc()}
30
+ return name
31
+
32
+
33
+ def unregister_eye_adapter(name):
34
+ if not isinstance(name,str) or not name: raise TypeError("`name` must be a single character value.")
35
+ _ADAPTERS.pop(name,None)
36
+ return name
37
+
38
+
39
+ def supported_eye_formats():
40
+ if not _ADAPTERS: return pd.DataFrame()
41
+ out=pd.DataFrame([{"name":a["name"],"priority":a["priority"],"has_validator":a["validate"] is not None,"registered_at":a["registered_at"]} for a in _ADAPTERS.values()])
42
+ return out.sort_values(["priority","name"],ascending=[False,True],kind="stable").reset_index(drop=True)
43
+
44
+
45
+ def detect_eye_format(path, inspect_rows=20, candidates=None):
46
+ p=Path(path)
47
+ if not p.exists(): raise FileNotFoundError(f"Path does not exist: {path}")
48
+ adapters=_ADAPTERS if candidates is None else {k:v for k,v in _ADAPTERS.items() if k in set(candidates)}
49
+ if not adapters: raise ValueError("No eye-data adapters are registered.")
50
+ rows=[]
51
+ for a in adapters.values():
52
+ try:
53
+ score=a["detect"](path,inspect_rows=inspect_rows)
54
+ if isinstance(score,(bool,np.bool_)): score=1.0 if score else 0.0
55
+ score=float(np.ravel(score)[0]); score=score if np.isfinite(score) else 0.0
56
+ except Exception:
57
+ score=0.0
58
+ rows.append({"format":a["name"],"confidence":max(0.0,min(1.0,score)),"priority":a["priority"]})
59
+ return pd.DataFrame(rows).sort_values(["confidence","priority","format"],ascending=[False,False,True],kind="stable").reset_index(drop=True)
60
+
61
+
62
+ def read_eye_export(path,vendor="auto",confidence_threshold=0.55,**kwargs):
63
+ detection=None
64
+ if vendor=="auto":
65
+ detection=detect_eye_format(path)
66
+ if detection.empty or float(detection.iloc[0].confidence)<confidence_threshold:
67
+ raise ValueError(f"No adapter reached the required confidence threshold ({confidence_threshold}). Use `read_eye_generic()` with an explicit mapping.")
68
+ if len(detection)>1 and detection.iloc[0].confidence==detection.iloc[1].confidence:
69
+ warnings.warn(f"Format detection is tied; selecting `{detection.iloc[0].format}` by adapter priority.",RuntimeWarning,stacklevel=2)
70
+ vendor=str(detection.iloc[0].format)
71
+ if vendor not in _ADAPTERS: raise ValueError(f"Unknown adapter `{vendor}`.")
72
+ out=_ADAPTERS[vendor]["read"](path,**kwargs)
73
+ if isinstance(out,EyeDataset): out.format_detection=detection
74
+ return out
75
+
76
+
77
+ def read_eye_folder(path,recursive=False,vendor="auto",pattern=None,combine=True,**kwargs):
78
+ p=Path(path)
79
+ if not p.is_dir(): raise FileNotFoundError(f"Directory does not exist: {path}")
80
+ files=[q for q in (p.rglob('*') if recursive else p.glob('*')) if q.is_file() and (pattern is None or re.search(pattern,q.name))]
81
+ if not files: raise ValueError(f"No files found in: {path}")
82
+ if not combine: return [read_eye_export(f,vendor=vendor,**kwargs) for f in files]
83
+ objs=[]
84
+ for f in files:
85
+ try: obj=read_eye_export(f,vendor=vendor,**kwargs)
86
+ except Exception: continue
87
+ if is_eye_dataset(obj): objs.append(obj)
88
+ if not objs: raise ValueError("No files in the folder could be imported.")
89
+ return combine_eye_datasets(objs)
90
+
91
+
92
+ def remap_recording_ids(x,mapping):
93
+ if not is_eye_dataset(x): raise TypeError("Expected an `eye_dataset` object.")
94
+ if not isinstance(mapping,dict): mapping=dict(mapping)
95
+ out=x.copy()
96
+ for name in canonical_table_names():
97
+ d=out[name].copy()
98
+ if "recording_id" in d.columns and not d.empty:
99
+ d["recording_id"]=d["recording_id"].map(lambda z:mapping.get(z,z))
100
+ out[name]=d
101
+ return add_provenance(out,"remap_recording_ids","dataset",";".join(f"{a}->{b}" for a,b in mapping.items()))
102
+
103
+
104
+ def combine_eye_datasets(*xs,resolve_ids=True):
105
+ if len(xs)==1 and isinstance(xs[0],(list,tuple)) and not is_eye_dataset(xs[0]): xs=tuple(xs[0])
106
+ if not xs or not all(is_eye_dataset(x) for x in xs): raise TypeError("All inputs must be `eye_dataset` objects.")
107
+ xs=[x.copy() for x in xs]
108
+ if resolve_ids:
109
+ seen=set()
110
+ for i,x in enumerate(xs,1):
111
+ ids=[str(v) for v in x["recordings"].recording_id.dropna().unique()]
112
+ duplicates=[v for v in ids if v in seen]
113
+ if duplicates: xs[i-1]=remap_recording_ids(x,{v:f"{v}_set{i}" for v in duplicates})
114
+ seen.update(str(v) for v in xs[i-1]["recordings"].recording_id.dropna().unique())
115
+ tables={}
116
+ for name in canonical_table_names():
117
+ frames=[x[name] for x in xs]
118
+ nonempty=[f for f in frames if not f.empty]
119
+ tables[name]=pd.concat(nonempty,ignore_index=True,sort=False) if nonempty else (frames[0].copy() if frames else pd.DataFrame())
120
+ identity={"recordings":"recording_id","streams":"stream_id","coordinate_spaces":"coordinate_space_id","aoi_definitions":"aoi_id","calibrations":"calibration_id"}
121
+ for name,key in identity.items():
122
+ if not tables[name].empty and key in tables[name].columns:
123
+ tables[name]=tables[name].drop_duplicates(subset=[key],keep="first").reset_index(drop=True)
124
+ raw=[]; metadata={}
125
+ for x in xs:
126
+ if isinstance(x.raw,dict): raw.extend(x.raw.items())
127
+ elif isinstance(x.raw,list): raw.extend(x.raw)
128
+ if isinstance(x.vendor_metadata,dict): metadata.update(x.vendor_metadata)
129
+ out=new_eye_dataset(**tables,raw=raw,vendor_metadata=metadata,validate=False)
130
+ out=add_provenance(out,"combine_datasets","dataset",f"Combined {len(xs)} datasets.")
131
+ out.validation=validate_eye_dataset(out)
132
+ return out
133
+
134
+
135
+ def _detect_generic_delimited(path,inspect_rows=20):
136
+ p=Path(path)
137
+ if p.is_dir() or p.suffix.lower() not in {".csv",".tsv",".txt",".asc"}: return 0.0
138
+ try: d=_read_delimited(path,nrows=min(2,int(inspect_rows)))
139
+ except Exception: return 0.0
140
+ return 0.1 if d.shape[1]>=2 else 0.0
141
+
142
+
143
+ def _register_builtin_adapters():
144
+ # Only adapters whose corresponding reader is actually implemented are registered.
145
+ # This deliberately avoids advertising unimplemented vendor parity.
146
+ register_eye_adapter("generic",_detect_generic_delimited,read_eye_generic,priority=1,overwrite=True)
147
+
148
+ _register_builtin_adapters()