Skip to content

Commit 2358ff3

Browse files
committed
Merge remote-tracking branch 'origin/main' into cv_remove_py3.9
2 parents 413aafc + 8f5b384 commit 2358ff3

24 files changed

+1332
-979
lines changed

.github/workflows/publish_fgpyo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
ref: ${{ github.ref_name }}
9595

9696
- name: Generate a Changelog
97-
uses: orhun/git-cliff-action@v3
97+
uses: orhun/git-cliff-action@v4
9898
id: git-cliff
9999
with:
100100
config: pyproject.toml

.github/workflows/tests.yml

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: test
1+
name: Static Analysis, Unit Tests, and Documentation
22

33
on:
44
push:
@@ -8,6 +8,9 @@ on:
88
- "!**"
99
workflow_call:
1010

11+
env:
12+
UV_VERSION: 0.8.22
13+
1114

1215
permissions:
1316
pull-requests: write
@@ -19,58 +22,31 @@ jobs:
1922
matrix:
2023
PYTHON_VERSION: ["3.10", "3.11", "3.12", "3.13"]
2124
steps:
22-
- uses: actions/checkout@v4
23-
24-
- name: Set up Python ${{matrix.PYTHON_VERSION}}
25-
uses: actions/setup-python@v5
26-
with:
27-
python-version: ${{matrix.PYTHON_VERSION}}
25+
- name: Checkout
26+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2827

29-
- name: Get full Python version
30-
id: full-python-version
31-
run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
32-
3328
- name: Install uv
34-
uses: astral-sh/setup-uv@v5
35-
36-
- name: Set up cache
37-
uses: actions/cache@v4
38-
id: cache
29+
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
3930
with:
40-
path: .venv
41-
key: venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('**/uv.lock') }}
42-
43-
- name: Ensure cache is healthy
44-
if: steps.cache.outputs.cache-hit == 'true'
45-
run: uv run pip --version >/dev/null 2>&1 || rm -rf .venv
46-
47-
- name: Check that the lock file is up to date
48-
run: uv lock --check
31+
version: "${{ env.UV_VERSION }}"
32+
python-version: "${{ matrix.PYTHON_VERSION }}"
33+
enable-cache: 'true'
34+
cache-suffix: "${{ matrix.PYTHON_VERSION }}"
4935

5036
- name: Install dependencies
51-
run: uv sync --no-install-project
52-
53-
- name: Install project
54-
run: uv pip install --group dev --group docs
55-
56-
- name: Style checking
57-
run: uv run ruff format --check fgpyo tests
58-
59-
- name: Run lint
60-
run: uv run ruff check fgpyo tests
61-
62-
- name: Run mypy
63-
run: uv run mypy fgpyo tests --config=pyproject.toml
37+
run: |
38+
uv sync --locked
6439
65-
- name: Run pytest
66-
run: uv run pytest --cov=fgpyo --cov-report=xml --cov-branch
40+
- name: Test the library
41+
run: |
42+
uv run poe check-all
6743
6844
- name: Run docs
6945
run: |
7046
set -euo pipefail
71-
uv run mkdocs build --strict
47+
uv run poe build-docs
7248
7349
- name: Upload code coverage
74-
uses: codecov/codecov-action@v4.5.0
50+
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
7551
with:
7652
token: ${{ secrets.CODECOV_TOKEN }}

.github/workflows/wheels.yml

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,40 @@
1-
name: build wheels
1+
name: Build Wheels
22

33
on:
44
pull_request:
55
workflow_call:
66
workflow_dispatch:
77

8+
env:
9+
UV_VERSION: 0.8.22
10+
811
jobs:
912
build-wheels:
10-
name: Build wheels for ${{ matrix.python }}
13+
name: Build wheels for ${{ matrix.PYTHON_VERSION }}
1114
runs-on: ubuntu-latest
1215
strategy:
1316
matrix:
14-
python: ["3.10", "3.11", "3.12", "3.13"]
17+
PYTHON_VERSION: ["3.9", "3.10", "3.11", "3.12", "3.13"]
1518

1619
steps:
17-
- uses: actions/checkout@v4
20+
- name: Checkout
21+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
1822

19-
- name: Set up Python ${{ matrix.python }}
20-
uses: actions/setup-python@v5
21-
with:
22-
python-version: ${{ matrix.python }}
23-
2423
- name: Install uv
25-
uses: astral-sh/setup-uv@v5
24+
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
25+
with:
26+
version: "${{ env.UV_VERSION }}"
27+
python-version: "${{ matrix.PYTHON_VERSION }}"
28+
enable-cache: 'true'
29+
cache-suffix: "${{ matrix.PYTHON_VERSION }}"
2630

2731
- name: Build wheels
28-
run: uv build --wheel -o wheelhouse
32+
run: |
33+
uv build --wheel -o wheelhouse
2934
3035
- name: Upload wheels
31-
uses: actions/upload-artifact@v4
36+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
3237
with:
33-
name: fgpyo-wheels-${{ matrix.python }}
38+
name: fgpyo-wheels-${{ matrix.PYTHON_VERSION }}
3439
path: ./wheelhouse/fgpyo*.whl
3540
if-no-files-found: error

ci/check.sh

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,13 @@
11
#!/usr/bin/env bash
22

3-
function banner() {
4-
echo
5-
echo "================================================================================"
6-
echo -e "$*"
7-
echo "================================================================================"
8-
echo
9-
}
10-
11-
#####################################################################
12-
# Takes two parameters, a "name" and a "command".
13-
# Runs the command and prints out whether it succeeded or failed, and
14-
# also tracks a list of failed steps in $failures.
15-
#####################################################################
16-
function run() {
17-
local name=$1
18-
local cmd=$2
19-
20-
banner "Running $name [$cmd]"
21-
set +e
22-
$cmd
23-
exit_code=$?
24-
set -e
25-
26-
if [[ $exit_code == 0 ]]; then
27-
echo Passed "$name": "[$cmd]"
28-
else
29-
echo Failed "$name": "[$cmd]"
30-
if [ -z "$failures" ]; then
31-
failures="$failures $name"
32-
else
33-
failures="$failures, $name"
34-
fi
35-
fi
36-
}
37-
383
# Require uv
394
if [ ! -x "$(command -v uv)" ]; then
405
echo >&2 "Error: 'uv' not found."
416
echo >&2 "Install via https://docs.astral.sh/uv/getting-started/installation/"
427
exit 1
438
fi
449

45-
run "Checking lockfile" "uv lock --check"
46-
run "Updating environment" "uv sync --locked"
47-
run "Style Checking" "uv run ruff format fgpyo tests"
48-
run "Linting" "uv run ruff check --fix fgpyo tests"
49-
run "Type Checking" "uv run mypy fgpyo tests --config pyproject.toml"
50-
run "Unit Tests" "uv run pytest -vv -r sx tests"
51-
run "Make docs" "uv run mkdocs build --strict"
10+
set -euo pipefail
5211

53-
if [ -z "$failures" ]; then
54-
banner "Checks Passed"
55-
else
56-
banner "Checks Failed with failures in: $failures"
57-
exit 1
58-
fi
12+
uv run poe fix-and-check-all
13+
uv run poe build-docs
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""MkDocs hook to strip doctest flags from rendered documentation.
2+
3+
Doctest flags like `# doctest: +SKIP` are useful for controlling doctest execution,
4+
but they clutter the documentation. This hook removes them from the rendered HTML.
5+
"""
6+
7+
import re
8+
from typing import Any
9+
10+
# Pattern to match doctest flags like: # doctest: +SKIP, # doctest: +ELLIPSIS, etc.
11+
# Also handles multiple flags like: # doctest: +SKIP, +ELLIPSIS
12+
DOCTEST_FLAG_PATTERN = re.compile(r"\s*#\s*doctest:\s*[+\w,\s]+")
13+
14+
15+
def on_page_content(html: str, **kwargs: Any) -> str:
16+
"""Remove doctest flags from page content.
17+
18+
Args:
19+
html: The rendered HTML content of the page.
20+
**kwargs: Additional keyword arguments passed by MkDocs.
21+
22+
Returns:
23+
The HTML content with doctest flags removed.
24+
"""
25+
return DOCTEST_FLAG_PATTERN.sub("", html)

fgpyo/collections/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
True
1818
>>> is_sorted([1, 2, 4, 3])
1919
False
20+
2021
```
2122
2223
## Examples of a "Peekable" Iterator
@@ -34,7 +35,10 @@
3435
>>> from fgpyo.collections import PeekableIterator
3536
>>> piter = PeekableIterator(iter([]))
3637
>>> piter.peek()
38+
Traceback (most recent call last):
39+
...
3740
StopIteration
41+
3842
```
3943
4044
A peekable iterator will return the next item before consuming it.
@@ -47,6 +51,7 @@
4751
1
4852
>>> [j for j in piter]
4953
[2, 3]
54+
5055
```
5156
5257
The [`can_peek()`][fgpyo.collections.PeekableIterator.can_peek] function can be used to determine if
@@ -63,7 +68,10 @@
6368
>>> piter.peek() if piter.can_peek() else -1
6469
-1
6570
>>> next(piter)
71+
Traceback (most recent call last):
72+
...
6673
StopIteration
74+
6775
```
6876
6977
[`PeekableIterator`][fgpyo.collections.PeekableIterator]'s constructor supports creation from

fgpyo/fasta/builder.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,39 @@
99
Writing a FASTA with two contigs each with 100 bases:
1010
1111
```python
12-
>>> from fgpyo.fasta.builder import FastaBuilder
13-
>>> builder = FastaBuilder()
14-
>>> builder.add("chr10").add("AAAAAAAAAA", 10)
15-
>>> builder.add("chr11").add("GGGGGGGGGG", 10)
16-
>>> builder.to_file(path = pathlib.Path("test.fasta"))
12+
>>> from pathlib import Path
13+
>>> from fgpyo.fasta.builder import FastaBuilder
14+
>>> builder = FastaBuilder()
15+
>>> builder.add("chr10").add("AAAAAAAAAA", 10) # doctest: +ELLIPSIS
16+
<fgpyo.fasta.builder.ContigBuilder object at ...>
17+
>>> builder = builder.add("chr11").add("GGGGGGGGGG", 10)
18+
>>> fasta_path = Path(getfixture("tmp_path")) / "test.fasta"
19+
>>> builder.to_file(path=fasta_path) # doctest: +SKIP
20+
1721
```
1822
1923
Writing a FASTA with one contig with 100 A's and 50 T's:
2024
2125
```python
22-
>>> from fgpyo.fasta.builder import FastaBuilder
23-
>>> builder = FastaBuilder()
24-
>>> builder.add("chr10").add("AAAAAAAAAA", 10).add("TTTTTTTTTT", 5)
25-
>>> builder.to_file(path = pathlib.Path("test.fasta"))
26+
>>> from fgpyo.fasta.builder import FastaBuilder
27+
>>> builder = FastaBuilder()
28+
>>> builder.add("chr10").add("AAAAAAAAAA", 10).add("TTTTTTTTTT", 5) # doctest: +ELLIPSIS
29+
<fgpyo.fasta.builder.ContigBuilder object at ...>
30+
>>> builder.to_file(path=fasta_path) # doctest: +SKIP
31+
2632
```
2733
2834
Add bases to existing contig:
2935
3036
```python
31-
>>> from fgpyo.fasta.builder import FastaBuilder
32-
>>> builder = FastaBuilder()
33-
>>> contig_one = builder.add("chr10").add("AAAAAAAAAA", 1)
34-
>>> contig_one.add("NNN", 1)
35-
>>> contig_one.bases
36-
'AAAAAAAAAANNN'
37+
>>> from fgpyo.fasta.builder import FastaBuilder
38+
>>> builder = FastaBuilder()
39+
>>> contig_one = builder.add("chr10").add("AAAAAAAAAA", 1)
40+
>>> contig_one.add("NNN", 1) # doctest: +ELLIPSIS
41+
<fgpyo.fasta.builder.ContigBuilder object at ...>
42+
>>> contig_one.bases
43+
'AAAAAAAAAANNN'
44+
3745
```
3846
3947
"""

0 commit comments

Comments
 (0)