Skip to content

Commit 242cdff

Browse files
authored
Package sdist properly (#86)
* Add timestamps to test logging * Test sdist installs and works on all platforms * Fix sdist included files * Separate out sdist job
1 parent b0d2ef7 commit 242cdff

File tree

6 files changed

+84
-45
lines changed

6 files changed

+84
-45
lines changed

.github/workflows/code.yml

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ jobs:
3030
- name: Lint
3131
run: flake8
3232

33+
sdist:
34+
runs-on: "ubuntu-latest"
35+
steps:
36+
- name: Checkout Source
37+
uses: actions/checkout@v2
38+
with:
39+
# require history to get back to last tag for version number of branches
40+
fetch-depth: 0
41+
submodules: true
42+
43+
- name: Build Sdist
44+
run: pipx run build --sdist .
45+
46+
- name: Upload Sdist
47+
uses: actions/upload-artifact@v2
48+
with:
49+
name: dist
50+
path: dist/*
51+
3352
build:
3453
strategy:
3554
fail-fast: false
@@ -50,9 +69,6 @@ jobs:
5069
- os: ubuntu-latest
5170
cov_file: /output/coverage.xml
5271
results_file: /output/pytest-results.xml
53-
# Build an sdist on linux so it has the right line endings
54-
- os: ubuntu-latest
55-
sdist: true
5672

5773
name: build/${{ matrix.os }}/${{ matrix.python }}
5874
runs-on: ${{ matrix.os }}
@@ -74,10 +90,6 @@ jobs:
7490
# Pin cibuildwheel due to https://github.com/pypa/cibuildwheel/issues/962
7591
run: pip install build cibuildwheel>=2.3.1
7692

77-
- name: Build Sdist
78-
if: matrix.sdist
79-
run: python -m build --sdist .
80-
8193
- name: Build Wheel
8294
run: cibuildwheel --output-dir dist
8395
env:
@@ -86,14 +98,16 @@ jobs:
8698
CIBW_BUILD: ${{ matrix.python }}*64
8799
CIBW_TEST_EXTRAS: dev
88100
CIBW_TEST_COMMAND: pytest {project}/tests --cov-report xml:${{ matrix.cov_file }} --junit-xml=${{ matrix.results_file }}
101+
# Run with faulthandler and -s in the hope we get a stack trace on seg fault on windows...
102+
CIBW_TEST_COMMAND_WINDOWS: python -X faulthandler -m pytest -s {project}/tests --cov-report xml:${{ matrix.cov_file }} --junit-xml=${{ matrix.results_file }}
89103
# Disable auditwheel as it isn't compatible with setuptools_dso approach
90104
# https://github.com/mdavidsaver/setuptools_dso/issues/17
91105
CIBW_REPAIR_WHEEL_COMMAND: ""
92106
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
93107
CIBW_ENVIRONMENT_LINUX: SETUPTOOLS_DSO_PLAT_NAME=manylinux2014_x86_64
94108
CIBW_SKIP: "*-musllinux*" # epicscorelibs doesn't build on musllinux platforms
95109

96-
- name: Upload Wheel and Sdist
110+
- name: Upload Wheel
97111
uses: actions/upload-artifact@v2
98112
with:
99113
name: dist
@@ -129,8 +143,28 @@ jobs:
129143
with:
130144
files: artifacts/**/*.xml
131145

146+
test-sdist:
147+
needs: [sdist]
148+
strategy:
149+
fail-fast: false
150+
matrix:
151+
os: [ubuntu-latest, windows-latest, macos-latest]
152+
python: [cp36, cp37, cp38, cp39, cp310]
153+
154+
runs-on: ${{ matrix.os }}
155+
156+
steps:
157+
- uses: actions/download-artifact@v2
158+
with:
159+
name: dist
160+
path: dist
161+
162+
- name: Install sdist in a venv and check cli works
163+
run: pipx run --spec dist/*.tar.gz pythonSoftIOC --version
164+
shell: bash
165+
132166
release:
133-
needs: [build]
167+
needs: [build, sdist]
134168
runs-on: ubuntu-latest
135169
# upload to PyPI and make a release on every tag
136170
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')

setup.cfg

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ dev =
4848
cothread; sys_platform != "win32"
4949
p4p
5050

51+
# Include the OS specific files in the sdist, even
52+
# if not packages by setup.py (as we only build sdist on one arch)
53+
[options.data_files]
54+
iocStats/devIocStats = iocStats/devIocStats/*.c, iocStats/devIocStats/*.h
55+
iocStats/devIocStats/os/default = iocStats/devIocStats/os/default/*
56+
iocStats/devIocStats/os/Darwin = iocStats/devIocStats/os/Darwin/*
57+
iocStats/devIocStats/os/WIN32 = iocStats/devIocStats/os/WIN32/*
58+
iocStats/devIocStats/os/Linux = iocStats/devIocStats/os/Linux/*
59+
5160
[flake8]
5261
max-line-length = 80
5362
extend-ignore =

setup.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
"devIocStatsWaveform.c",
2424
"devIocStatsSub.c",
2525
"devIocStatsTest.c",
26-
"devIocStats.h",
2726
]
2827

2928
devIocStats_OSD = [
@@ -39,30 +38,19 @@
3938
"osdSystemInfo.c",
4039
"osdHostInfo.c",
4140
"osdPIDInfo.c",
42-
"devIocStatsOSD.h",
4341
]
4442

4543
devIocStats_src = os.path.join("iocStats", "devIocStats")
4644
devIocStats_os = os.path.join(devIocStats_src, "os", get_config_var('OS_CLASS'))
4745
devIocStats_default = os.path.join(devIocStats_src, "os", "default")
4846

49-
def _add_file(f):
50-
if f.endswith(".h"):
51-
# Only add header files if making an sdist
52-
# https://github.com/pypa/packaging-problems/issues/84#issuecomment-383718492
53-
should_add = "sdist" in sys.argv
54-
else:
55-
should_add = True
56-
if should_add:
57-
sources.append(f)
58-
5947
for f in devIocStats_OSI:
60-
_add_file(os.path.join(devIocStats_src, f))
48+
sources.append(os.path.join(devIocStats_src, f))
6149
for f in devIocStats_OSD:
6250
if os.path.exists(os.path.join(devIocStats_os, f)):
63-
_add_file(os.path.join(devIocStats_os, f))
51+
sources.append(os.path.join(devIocStats_os, f))
6452
else:
65-
_add_file(os.path.join(devIocStats_default, f))
53+
sources.append(os.path.join(devIocStats_default, f))
6654

6755
# Extension with all our C code
6856
ext = Extension(

tests/conftest.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime
12
import atexit
23
import os
34
import random
@@ -20,7 +21,7 @@
2021
WAVEFORM_LENGTH = 40
2122

2223
# Default timeout for many operations across testing
23-
TIMEOUT = 5 # Seconds
24+
TIMEOUT = 10 # Seconds
2425

2526
# Address for multiprocessing Listener/Client pair
2627
ADDRESS = ("localhost", 2345)
@@ -29,6 +30,12 @@ def create_random_prefix():
2930
"""Create 12-character random string, for generating unique Device Names"""
3031
return "".join(random.choice(string.ascii_uppercase) for _ in range(12))
3132

33+
# Can't use logging as it's not multiprocess safe, and
34+
# alteratives are overkill
35+
def log(*args):
36+
print(datetime.now().strftime("%H:%M:%S"), *args)
37+
38+
3239
class SubprocessIOC:
3340
def __init__(self, ioc_py):
3441
self.pv_prefix = create_random_prefix()

tests/test_record_values.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from conftest import (
1010
requires_cothread,
1111
WAVEFORM_LENGTH,
12+
log,
1213
select_and_recv,
1314
TIMEOUT
1415
)
@@ -30,7 +31,6 @@
3031
"string and so lets test it and prove that shall we?"
3132

3233

33-
3434
def record_func_names(fixture_value):
3535
"""Provide a nice name for the record_func fixture"""
3636
return fixture_value.__name__
@@ -461,7 +461,7 @@ def run_test_function(
461461
creation_func in [builder.WaveformOut, builder.WaveformIn]
462462
and expected_value.dtype in [numpy.float64, numpy.int32]
463463
):
464-
print(
464+
log(
465465
"caget cannot distinguish between a waveform with 1 "
466466
"element and a scalar value, and so always returns a "
467467
"scalar. Therefore we skip this check.")
@@ -760,16 +760,16 @@ def none_value_test_func(self, record_func, queue):
760760
builder.LoadDatabase()
761761
softioc.iocInit(dispatcher)
762762

763-
print("CHILD: Soft IOC started, about to .set(None)")
763+
log("CHILD: Soft IOC started, about to .set(None)")
764764

765765
try:
766766
record.set(None)
767-
print("CHILD: Uh-OH! No exception thrown when setting None!")
767+
log("CHILD: Uh-OH! No exception thrown when setting None!")
768768
except Exception as e:
769-
print("CHILD: Putting exception into queue", e)
769+
log("CHILD: Putting exception into queue %s", e)
770770
queue.put(e)
771771
else:
772-
print("CHILD: No exception raised when using None as value!")
772+
log("CHILD: No exception raised when using None as value!")
773773
queue.put(Exception("FAIL: No exception raised during .set()"))
774774

775775
@requires_cothread
@@ -785,14 +785,14 @@ def test_value_none_rejected_set_after_init(self, record_func_reject_none):
785785

786786
process.start()
787787

788-
print("PARENT: Child process started, waiting for returned exception")
788+
log("PARENT: Child process started, waiting for returned exception")
789789

790790
try:
791791
exception = queue.get(timeout=TIMEOUT)
792792

793793
assert isinstance(exception, self.expected_exceptions)
794794
finally:
795-
print("PARENT: Issuing terminate to child process")
795+
log("PARENT: Issuing terminate to child process")
796796
process.terminate()
797797
process.join(timeout=TIMEOUT)
798798
if process.exitcode is None:

tests/test_records.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from conftest import (
7+
log,
78
create_random_prefix,
89
requires_cothread,
910
_clear_records,
@@ -376,14 +377,14 @@ def on_update_done(_):
376377

377378
conn.send("R") # "Ready"
378379

379-
print("CHILD: Sent R over Connection to Parent")
380+
log("CHILD: Sent R over Connection to Parent")
380381

381382
# Keep process alive while main thread runs CAGET
382383
if conn.poll(TIMEOUT):
383384
val = conn.recv()
384385
assert val == "D", "Did not receive expected Done character"
385386

386-
print("CHILD: Received exit command, child exiting")
387+
log("CHILD: Received exit command, child exiting")
387388

388389
def on_update_runner(self, creation_func, always_update, put_same_value):
389390
parent_conn, child_conn = multiprocessing.Pipe()
@@ -397,15 +398,15 @@ def on_update_runner(self, creation_func, always_update, put_same_value):
397398

398399
process.start()
399400

400-
print("PARENT: Child started, waiting for R command")
401+
log("PARENT: Child started, waiting for R command")
401402

402403
from cothread.catools import caget, caput, _channel_cache
403404

404405
try:
405406
# Wait for message that IOC has started
406407
select_and_recv(parent_conn, "R")
407408

408-
print("PARENT: received R command")
409+
log("PARENT: received R command")
409410

410411

411412
# Suppress potential spurious warnings
@@ -416,7 +417,7 @@ def on_update_runner(self, creation_func, always_update, put_same_value):
416417
# value to force processing to occur
417418
count = 1
418419

419-
print("PARENT: begining While loop")
420+
log("PARENT: begining While loop")
420421

421422
while count < 4:
422423
put_ret = caput(
@@ -426,26 +427,26 @@ def on_update_runner(self, creation_func, always_update, put_same_value):
426427
)
427428
assert put_ret.ok, f"caput did not succeed: {put_ret.errorcode}"
428429

429-
print(f"PARENT: completed caput with count {count}")
430+
log(f"PARENT: completed caput with count {count}")
430431

431432
count += 1
432433

433-
print("PARENT: Put'ing to DONE record")
434+
log("PARENT: Put'ing to DONE record")
434435

435436
caput(
436437
device_name + ":ON-UPDATE-DONE",
437438
1,
438439
wait=True,
439440
)
440441

441-
print("PARENT: Waiting for C command")
442+
log("PARENT: Waiting for C command")
442443

443444
# Wait for action record to process, so we know all the callbacks
444445
# have finished processing (This assumes record callbacks are not
445446
# re-ordered, and will run in the same order as the caputs we sent)
446447
select_and_recv(parent_conn, "C")
447448

448-
print("PARENT: Received C command")
449+
log("PARENT: Received C command")
449450

450451
ret_val = caget(
451452
device_name + ":ON-UPDATE-COUNTER-RECORD",
@@ -454,7 +455,7 @@ def on_update_runner(self, creation_func, always_update, put_same_value):
454455
assert ret_val.ok, \
455456
f"caget did not succeed: {ret_val.errorcode}, {ret_val}"
456457

457-
print(f"PARENT: Received val from COUNTER: {ret_val}")
458+
log(f"PARENT: Received val from COUNTER: {ret_val}")
458459

459460

460461
# Expected value is either 3 (incremented once per caput)
@@ -469,10 +470,10 @@ def on_update_runner(self, creation_func, always_update, put_same_value):
469470
# Suppress potential spurious warnings
470471
_channel_cache.purge()
471472

472-
print("PARENT:Sending Done command to child")
473+
log("PARENT:Sending Done command to child")
473474
parent_conn.send("D") # "Done"
474475
process.join(timeout=TIMEOUT)
475-
print(f"PARENT: Join completed with exitcode {process.exitcode}")
476+
log(f"PARENT: Join completed with exitcode {process.exitcode}")
476477
if process.exitcode is None:
477478
pytest.fail("Process did not terminate")
478479

0 commit comments

Comments
 (0)