Skip to content

more diagnostics

more diagnostics #5

Workflow file for this run

name: CI Tests
on: [push, pull_request]
jobs:
test:
name: Test ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-22.04, macos-latest, windows-2022]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y zlib1g-dev
- name: Build Windows Libraries
if: runner.os == 'Windows'
uses: ./.github/actions/windows_actions
- name: Install Python dependencies
run: |
pip install cython setuptools numpy narwhals
pip install pandas polars
pip install pytest mypy pytest-mypy-plugins pandas-stubs
- name: Install package
run: pip install .
- name: Diagnose value labels
if: runner.os == 'Linux'
working-directory: /tmp
run: |
python -c "
import pyreadstat, pandas as pd, tempfile, os, subprocess, struct, ctypes, ctypes.util
print('=== Environment ===')
print('pyreadstat location:', pyreadstat.__file__)
import sysconfig; print('CC:', sysconfig.get_config_var('CC'))
import platform; print('glibc:', platform.libc_ver())
# Check linked libraries of the .so
so_dir = os.path.dirname(pyreadstat.__file__)
for f in os.listdir(so_dir):
if '_readstat_parser' in f and f.endswith('.so'):
so_path = os.path.join(so_dir, f)
print(f'\n=== ldd {f} ===')
subprocess.run(['ldd', so_path])
break
# Check iconv availability
print('\n=== iconv check ===')
libc = ctypes.CDLL(ctypes.util.find_library('c'))
try:
conv = libc.iconv_open(b'UTF-8', b'UTF-8')
print(f'iconv_open(UTF-8, UTF-8) = {conv} (should be != -1)')
if conv != -1:
libc.iconv_close(conv)
except Exception as e:
print(f'iconv_open failed: {e}')
# Test write + read value labels
df = pd.DataFrame({'x': [1.0, 2.0], 'y': ['a', 'b']})
path = os.path.join(tempfile.gettempdir(), 'diag_labels.sav')
vvl = {'x': {1.0: 'Male', 2.0: 'Female'}}
pyreadstat.write_sav(df, path, variable_value_labels=vvl)
df2, meta = pyreadstat.read_sav(path)
print('\n=== Read-back results ===')
print('variable_value_labels:', meta.variable_value_labels)
print('value_labels (raw):', meta.value_labels)
print('variable_to_label:', meta.variable_to_label)
# Existing file test
checkout = os.environ.get('GITHUB_WORKSPACE', '')
test_sav = os.path.join(checkout, 'test_data', 'basic', 'sample.sav')
if os.path.exists(test_sav):
_, meta2 = pyreadstat.read_sav(test_sav)
print(f'\n=== Existing file ===')
print('variable_value_labels:', meta2.variable_value_labels)
# Dump file structure (record types)
with open(path, 'rb') as f:
data = f.read()
print(f'\n=== Written file: {len(data)} bytes ===')
print('First 64 bytes hex:', data[:64].hex())
print('Bytes 176-260 hex:', data[176:260].hex())
# Parse SAV record types sequentially
pos = 176 # after header
for i in range(10):
if pos + 4 > len(data): break
rt = struct.unpack('<I', data[pos:pos+4])[0]
print(f' offset {pos}: record_type={rt}, next 16 bytes: {data[pos:pos+16].hex()}')
if rt == 999: break
pos += 4
# skip based on record type
if rt == 2: # variable record
pos += 28 # rest of fixed part
elif rt == 3: # value label
lc = struct.unpack('<I', data[pos:pos+4])[0]
print(f' label_count={lc}')
break
elif rt == 7: # extension
pos += 8 # subtype + size + count header
else:
break
"
- name: Run basic tests
run: python tests/test_basic.py
- name: Run narwhals pandas tests
run: python tests/test_narwhalified.py --backend=pandas
- name: Run narwhals polars tests
run: python tests/test_narwhalified.py --backend=polars
- name: Run HTTP integration tests
run: python tests/test_http_integration.py
- name: Run runtime type tests
run: python tests/test_runtime_types.py
- name: Run typing tests
run: pytest tests/test_typing.yml --mypy-ini-file=tests/test_mypy_setup.ini