Skip to content

Commit ab4c51b

Browse files
tomwhitejeromekelleher
authored andcommitted
Revert 'Drop Python 3.9' #287
1 parent c7850fc commit ab4c51b

File tree

10 files changed

+45
-23
lines changed

10 files changed

+45
-23
lines changed

.github/workflows/cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- uses: actions/checkout@v4
1919
- uses: actions/setup-python@v5
2020
with:
21-
python-version: '3.10'
21+
python-version: '3.9'
2222
- name: Install dependencies
2323
run: |
2424
python -m pip install --upgrade pip

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
# Use macos-13 because pip binary packages for ARM aren't
2525
# available for many dependencies
2626
os: [macos-13, macos-14, ubuntu-latest]
27-
python-version: ["3.10", "3.11", "3.12"]
27+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2828
exclude:
2929
# Just run macos tests on one Python version
3030
- os: macos-13
@@ -33,6 +33,8 @@ jobs:
3333
python-version: "3.11"
3434
- os: macos-13
3535
python-version: "3.12"
36+
- os: macos-14
37+
python-version: "3.9"
3638
- os: macos-14
3739
python-version: "3.10"
3840
- os: macos-14

bio2zarr/core.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import multiprocessing
88
import os
99
import os.path
10+
import sys
1011
import threading
1112
import time
13+
import warnings
1214

1315
import humanfriendly
1416
import numcodecs
@@ -214,6 +216,22 @@ def setup_progress_counter(counter):
214216
_progress_counter = counter
215217

216218

219+
def warn_py39_mac():
220+
if sys.platform == "darwin" and sys.version_info[:2] == (3, 9):
221+
warnings.warn(
222+
"There is a known issue with bio2zarr on MacOS Python 3.9 "
223+
"in which OS-level named semaphores are leaked. "
224+
"You will also probably see warnings like 'There appear to be N "
225+
"leaked semaphore objects at shutdown'. "
226+
"While this is likely harmless for a few runs, it could lead to "
227+
"issues if you do a lot of conversion. To get prevent this issue "
228+
"either: (1) use --worker-processes=0 or (2) upgrade to a newer "
229+
"Python version. See https://github.com/sgkit-dev/bio2zarr/issues/209 "
230+
"for more details.",
231+
stacklevel=2,
232+
)
233+
234+
217235
class ParallelWorkManager(contextlib.AbstractContextManager):
218236
def __init__(self, worker_processes=1, progress_config=None):
219237
# Need to specify this explicitly to suppport Macs and
@@ -226,6 +244,7 @@ def __init__(self, worker_processes=1, progress_config=None):
226244
# production. See note on the SynchronousExecutor class.
227245
self.executor = SynchronousExecutor()
228246
else:
247+
warn_py39_mac()
229248
self.executor = cf.ProcessPoolExecutor(
230249
max_workers=worker_processes,
231250
mp_context=ctx,

bio2zarr/plink.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@ def validate(bed_path, zarr_path):
185185
assert call_genotype.shape[2] == 2
186186

187187
row_id = 0
188-
for bed_row, zarr_row in zip(bed_genotypes, call_genotype, strict=True):
188+
for bed_row, zarr_row in zip(bed_genotypes, call_genotype):
189189
# print("ROW", row_id)
190190
# print(bed_row, zarr_row)
191191
row_id += 1
192-
for bed_call, zarr_call in zip(bed_row, zarr_row, strict=True):
192+
for bed_call, zarr_call in zip(bed_row, zarr_row):
193193
if bed_call == -127:
194194
assert list(zarr_call) == [-1, -1]
195195
elif bed_call == 0:

bio2zarr/typing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from pathlib import Path
2+
from typing import Union
23

3-
PathType = str | Path
4+
PathType = Union[str, Path]

bio2zarr/vcf2zarr/icf.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def scan_vcf(path, target_num_partitions, *, local_alleles):
289289
samples=[Sample(sample_id) for sample_id in vcf.samples],
290290
contigs=[
291291
Contig(contig_id, length)
292-
for contig_id, length in zip(vcf.seqnames, contig_lengths, strict=True)
292+
for contig_id, length in zip(vcf.seqnames, contig_lengths)
293293
],
294294
filters=filters,
295295
fields=fields,
@@ -764,9 +764,7 @@ def chunks(self, partition_id, start_chunk=0):
764764
chunk_cumulative_records = self.chunk_record_index(partition_id)
765765
chunk_num_records = np.diff(chunk_cumulative_records)
766766
for count, cumulative in zip(
767-
chunk_num_records[start_chunk:],
768-
chunk_cumulative_records[start_chunk + 1 :],
769-
strict=True,
767+
chunk_num_records[start_chunk:], chunk_cumulative_records[start_chunk + 1 :]
770768
):
771769
path = partition_path / f"{cumulative}"
772770
chunk = self.read_chunk(path)

bio2zarr/vcf2zarr/vcz.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,6 @@ def encode_alleles_partition(self, partition_index):
760760
for ref, alt in zip(
761761
ref_field.iter_values(partition.start, partition.stop),
762762
alt_field.iter_values(partition.start, partition.stop),
763-
strict=True,
764763
):
765764
j = alleles.next_buffer_row()
766765
alleles.buff[j, :] = constants.STR_FILL

bio2zarr/vcf2zarr/verification.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def assert_format_val_equal(vcf_val, zarr_val, vcf_type, vcf_number):
114114
assert isinstance(vcf_val, np.ndarray)
115115
if vcf_type in ("String", "Character"):
116116
assert len(vcf_val) == len(zarr_val)
117-
for v, z in zip(vcf_val, zarr_val, strict=True):
117+
for v, z in zip(vcf_val, zarr_val):
118118
if vcf_number == "1":
119119
assert v == z
120120
else:

bio2zarr/vcf_utils.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from collections.abc import Sequence
88
from dataclasses import dataclass
99
from enum import Enum
10-
from typing import IO, Any
10+
from typing import IO, Any, Optional, Union
1111

1212
import cyvcf2
1313
import humanfriendly
@@ -33,7 +33,7 @@ def get_file_offset(vfp: int) -> int:
3333
return vfp >> 16 & address_mask
3434

3535

36-
def read_bytes_as_value(f: IO[Any], fmt: str, nodata: Any | None = None) -> Any:
36+
def read_bytes_as_value(f: IO[Any], fmt: str, nodata: Optional[Any] = None) -> Any:
3737
"""Read bytes using a `struct` format string and return the unpacked data value.
3838
3939
Parameters
@@ -85,8 +85,8 @@ class Region:
8585
"""
8686

8787
contig: str
88-
start: int | None = None
89-
end: int | None = None
88+
start: Optional[int] = None
89+
end: Optional[int] = None
9090

9191
def __post_init__(self):
9292
if self.start is not None:
@@ -194,7 +194,9 @@ def get_first_locus_in_bin(csi: CSIIndex, bin: int) -> int:
194194
return (bin - first_bin_on_level) * (max_span // level_size) + 1
195195

196196

197-
def read_csi(file: PathType, storage_options: dict[str, str] | None = None) -> CSIIndex:
197+
def read_csi(
198+
file: PathType, storage_options: Optional[dict[str, str]] = None
199+
) -> CSIIndex:
198200
"""Parse a CSI file into a `CSIIndex` object.
199201
200202
Parameters
@@ -309,7 +311,7 @@ def offsets(self) -> Any:
309311

310312

311313
def read_tabix(
312-
file: PathType, storage_options: dict[str, str] | None = None
314+
file: PathType, storage_options: Optional[dict[str, str]] = None
313315
) -> TabixIndex:
314316
"""Parse a tabix file into a `TabixIndex` object.
315317
@@ -450,7 +452,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
450452
return False
451453

452454
def contig_record_counts(self):
453-
d = dict(zip(self.sequence_names, self.index.record_counts, strict=True))
455+
d = dict(zip(self.sequence_names, self.index.record_counts))
454456
if self.file_type == VcfFileType.BCF:
455457
d = {k: v for k, v in d.items() if v > 0}
456458
return d
@@ -481,8 +483,8 @@ def _filter_empty_and_refine(self, regions):
481483

482484
def partition_into_regions(
483485
self,
484-
num_parts: int | None = None,
485-
target_part_size: None | int | str = None,
486+
num_parts: Optional[int] = None,
487+
target_part_size: Union[None, int, str] = None,
486488
):
487489
if num_parts is None and target_part_size is None:
488490
raise ValueError("One of num_parts or target_part_size must be specified")

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies = [
2424
"cyvcf2",
2525
"bed_reader",
2626
]
27-
requires-python = ">=3.10"
27+
requires-python = ">=3.9"
2828
classifiers = [
2929
"Development Status :: 4 - Beta",
3030
"License :: OSI Approved :: Apache Software License",
@@ -35,6 +35,7 @@ classifiers = [
3535
"Intended Audience :: Science/Research",
3636
"Programming Language :: Python",
3737
"Programming Language :: Python :: 3",
38+
"Programming Language :: Python :: 3.9",
3839
"Programming Language :: Python :: 3.10",
3940
"Programming Language :: Python :: 3.11",
4041
"Programming Language :: Python :: 3.12",
@@ -73,8 +74,8 @@ testpaths = "tests"
7374
addopts = "--cov=bio2zarr --cov-report term-missing"
7475

7576
[tool.ruff]
76-
# Assume Python 3.10
77-
target-version = "py310"
77+
# Assume Python 3.9
78+
target-version = "py39"
7879

7980
# Same as Black.
8081
line-length = 88

0 commit comments

Comments
 (0)