Skip to content

Commit 8685466

Browse files
authored
Merge pull request #53 from ISA-tools/test_upgrade
Test upgrade
2 parents a946af3 + d8b33b5 commit 8685466

21 files changed

Lines changed: 267 additions & 172 deletions

.github/workflows/package.yml

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,54 @@ on:
66
- v*.*.*
77

88
jobs:
9-
109
wheel:
1110
runs-on: ubuntu-latest
12-
name: Build universal wheel
11+
name: Build wheel
1312
steps:
1413
- name: Checkout code
15-
uses: actions/checkout@v2
14+
uses: actions/checkout@v4
1615
with:
1716
submodules: true
18-
- name: Setup Python 3.9
19-
uses: actions/setup-python@v2
17+
- name: Setup Python 3.14
18+
uses: actions/setup-python@v5
2019
with:
21-
python-version: 3.9
20+
python-version: '3.14'
21+
cache: pip
2222
- name: Install build requirements
23-
run: python -m pip install -r ci/requirements.txt
23+
run: |
24+
python -m pip install -U pip wheel setuptools
25+
python -m pip install -r ci/requirements.txt
2426
- name: Build wheel
25-
run: python setup.py bdist_wheel
27+
run: python -m build --wheel
2628
- name: Store built wheels
27-
uses: actions/upload-artifact@v2
29+
uses: actions/upload-artifact@v4
2830
with:
29-
name: dist
31+
name: dist-wheel
3032
path: dist/*
3133

3234
sdist:
3335
runs-on: ubuntu-latest
3436
name: Build source distribution
3537
steps:
3638
- name: Checkout code
37-
uses: actions/checkout@v2
39+
uses: actions/checkout@v4
3840
with:
3941
submodules: true
40-
- name: Set up Python 3.9
41-
uses: actions/setup-python@v2
42+
- name: Set up Python 3.14
43+
uses: actions/setup-python@v5
4244
with:
43-
python-version: 3.9
45+
python-version: '3.14'
46+
cache: pip
4447
- name: Install build requirements
45-
run: python -m pip install -r ci/requirements.txt
46-
- name: Build wheel distribution
47-
run: python setup.py sdist
48+
run: |
49+
python -m pip install -U pip wheel setuptools
50+
python -m pip install -r ci/requirements.txt
51+
- name: Build source distribution
52+
run: python -m build --sdist
4853
- name: Store built wheels
49-
uses: actions/upload-artifact@v2
54+
uses: actions/upload-artifact@v4
5055
with:
51-
name: dist
56+
name: dist-sdist
5257
path: dist/*
5358

5459
upload:
@@ -60,13 +65,14 @@ jobs:
6065
- wheel
6166
steps:
6267
- name: Download built distributions
63-
uses: actions/download-artifact@v2
68+
uses: actions/download-artifact@v4
6469
with:
65-
name: dist
70+
pattern: dist-*
6671
path: dist
72+
merge-multiple: true
6773
- name: Publish distributions to PyPI
6874
if: startsWith(github.ref, 'refs/tags/v')
69-
uses: pypa/gh-action-pypi-publish@master
75+
uses: pypa/gh-action-pypi-publish@release/v1
7076
with:
7177
user: __token__
7278
password: ${{ secrets.PYPI_API_TOKEN }}
@@ -80,7 +86,7 @@ jobs:
8086
needs: upload
8187
steps:
8288
- name: Checkout code
83-
uses: actions/checkout@v1
89+
uses: actions/checkout@v4
8490
with:
8591
submodules: true
8692
- name: Release a Changelog

.github/workflows/test.yml

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,45 @@ on:
55
- pull_request
66

77
jobs:
8-
98
test-linux:
109
name: Test (Linux)
1110
runs-on: ubuntu-latest
1211
env:
13-
OS: Linux
12+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1413
strategy:
1514
matrix:
1615
python-version:
17-
- '3.6'
18-
- '3.7'
19-
- '3.8'
2016
- '3.9'
2117
- '3.10'
18+
- '3.11'
19+
- '3.12'
20+
- '3.13'
21+
- '3.14'
2222
steps:
2323
- name: Checkout code
24-
uses: actions/checkout@v2
24+
uses: actions/checkout@v4
2525
with:
2626
submodules: true
2727
- name: Setup Python ${{ matrix.python-version }}
28-
uses: actions/setup-python@v2
28+
uses: actions/setup-python@v5
2929
with:
3030
python-version: ${{ matrix.python-version }}
31-
- name: Update pip
32-
run: python -m pip install -U pip wheel setuptools
33-
- name: Install Python requirements
34-
run: python -m pip install -r ci/requirements.txt
35-
- name: Install library
36-
run: python -m pip install .
37-
- name: Install test requirements
38-
run: python -m pip install -U -r tests/requirements.txt
31+
cache: pip
32+
- name: Install dependencies
33+
run: |
34+
python -m pip install -U pip wheel setuptools
35+
python -m pip install -r ci/requirements.txt
36+
python -m pip install -e .[test]
3937
- name: Test with coverage
4038
run: python -m coverage run -m unittest discover -vv
4139
- name: Upload to Codecov
42-
uses: codecov/codecov-action@v1
40+
if: ${{ env.CODECOV_TOKEN != '' }}
41+
uses: codecov/codecov-action@v4
4342
with:
4443
flags: ${{ matrix.python-version }}
4544
name: test-python-${{ matrix.python-version }}
4645
fail_ci_if_error: true
47-
token: ${{ secrets.CODECOV_TOKEN }}
46+
token: ${{ env.CODECOV_TOKEN }}
47+
- name: Skip Codecov upload
48+
if: ${{ env.CODECOV_TOKEN == '' }}
49+
run: echo "CODECOV_TOKEN is not set; skipping Codecov upload."

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ celerybeat-schedule
8686
# virtualenv
8787
venv/
8888
ENV/
89+
.venv/
90+
.venv310/
8991

9092
# Spyder project settings
9193
.spyderproject

ci/requirements.txt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
coverage
2-
codecov
3-
green
4-
pip
5-
setuptools
6-
wheel
7-
twine
1+
build >=1.2,<2
2+
coverage >=7.6,<8
3+
pip >=25,<26
4+
setuptools >=69
5+
twine >=6,<7
6+
wheel >=0.45,<1
87
pyinstaller>=3.6 ; sys_platform == 'win32'

mzml2isa/_fs.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Helpers for importing PyFilesystem without noisy deprecation warnings."""
2+
3+
import warnings
4+
5+
6+
with warnings.catch_warnings():
7+
warnings.filterwarnings(
8+
"ignore",
9+
message=r"pkg_resources is deprecated as an API\.",
10+
category=UserWarning,
11+
)
12+
import fs
13+
import fs.errors
14+
import fs.path

mzml2isa/_impl.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
1-
"""Conditional imports of optional dependencies.
2-
"""
1+
"""Compatibility helpers for supported Python versions."""
32

4-
# --- Available Cache --------------------------------------------------------
5-
6-
try:
7-
from functools import cache
8-
except ImportError:
9-
from functools import lru_cache
10-
cache = lru_cache(maxsize=None)
11-
12-
try:
13-
from functools import cached_property
14-
except ImportError:
15-
from cached_property import cached_property
3+
from contextlib import contextmanager
4+
from functools import cache
5+
from functools import cached_property
166

177

188
# --- Available XML parser ---------------------------------------------------
@@ -47,15 +37,8 @@ def get_parent(element, tree):
4737

4838
# --- Available package resources --------------------------------------------
4939

50-
try:
51-
import importlib.resources as importlib_resources
52-
except ImportError:
53-
import importlib_resources
54-
55-
try:
56-
from importlib.resources import files as resource_files
57-
except ImportError:
58-
from importlib_resources import files as resource_files
40+
import importlib.resources as importlib_resources
41+
from importlib.resources import files as resource_files
5942

6043

6144
# --- Optional progress bar --------------------------------------------------
@@ -64,3 +47,25 @@ def get_parent(element, tree):
6447
import tqdm
6548
except ImportError:
6649
tqdm = None
50+
51+
52+
@contextmanager
53+
def pronto_no_multiprocessing():
54+
"""Force pronto to parse ontologies without creating multiprocessing pools."""
55+
import pronto.utils.pool as pronto_pool
56+
57+
thread_pool = pronto_pool._ThreadPool
58+
pronto_pool._ThreadPool = None
59+
try:
60+
yield
61+
finally:
62+
pronto_pool._ThreadPool = thread_pool
63+
64+
65+
def load_pronto_ontology(pronto_module, filename):
66+
"""Load a pronto ontology, falling back to sequential parsing if needed."""
67+
try:
68+
return pronto_module.Ontology(filename)
69+
except PermissionError:
70+
with pronto_no_multiprocessing():
71+
return pronto_module.Ontology(filename)

mzml2isa/imzml.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import pronto
1717

1818
from . import ontologies
19-
from ._impl import importlib_resources
19+
from ._impl import cache, importlib_resources, load_pronto_ontology
2020
from .mzml import _CVParameter, MzMLFile
2121

2222

@@ -33,10 +33,13 @@ class ImzMLFile(MzMLFile):
3333
}
3434
)
3535

36-
with warnings.catch_warnings(record=True):
37-
warnings.simplefilter('ignore', pronto.warnings.SyntaxWarning)
38-
with importlib_resources.path(ontologies.__name__, "imagingMS.obo") as filename:
39-
_VOCABULARY = pronto.Ontology(filename)
36+
@classmethod
37+
@cache
38+
def _default_vocabulary(cls):
39+
with warnings.catch_warnings(record=True):
40+
warnings.simplefilter("ignore", pronto.warnings.SyntaxWarning)
41+
with importlib_resources.path(ontologies.__name__, "imagingMS.obo") as filename:
42+
return load_pronto_ontology(pronto, filename)
4043

4144
@classmethod
4245
def _assay_parameters(cls):

mzml2isa/isa.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"""
1616
import os
1717
import csv
18-
import pkg_resources
1918
import re
2019
import sys
2120
import functools
21+
import copy
2222
from collections import ChainMap
2323

2424
from . import (
@@ -61,12 +61,13 @@ def __init__(self, out_dir, name, usermeta=None, **kwargs):
6161
"a_imzML.txt" in your custom template directory. If None, the uses the
6262
ones shipping with mzml2isa, compatible with MetaboLights [default: None]
6363
"""
64-
usermeta = kwargs.get('usermeta', None)
64+
usermeta = kwargs.get('usermeta', usermeta)
6565
template_default = resource_files(templates.__name__)
6666
template_directory = kwargs.get('template_directory') or template_default
6767

6868
# Create one or several study files / one or several study section in investigation
69-
self.usermeta = usermeta or {}
69+
self.usermeta = copy.deepcopy(usermeta) if usermeta is not None else {}
70+
self._set_default_usermeta(name)
7071
self.isa_env = {
7172
'out_dir': out_dir,
7273
'Study Identifier': name,
@@ -86,6 +87,23 @@ def __init__(self, out_dir, name, usermeta=None, **kwargs):
8687
self.isa_env['Converter'] = __name__
8788
self.isa_env['Converter version'] = __version__
8889

90+
def _set_default_usermeta(self, name):
91+
"""Populate minimal study metadata required by ISA validators."""
92+
defaults = {
93+
'investigation': {
94+
'title': name,
95+
'description': "Generated from mzML metadata by mzml2isa.",
96+
},
97+
'study': {
98+
'title': name,
99+
'description': "Generated from mzML metadata by mzml2isa.",
100+
},
101+
}
102+
for section, values in defaults.items():
103+
target = self.usermeta.setdefault(section, {})
104+
for key, value in values.items():
105+
target.setdefault(key, value)
106+
89107
def write(self, metalist, datatype, **kwargs):
90108
"""Generate and write the ISA files
91109

0 commit comments

Comments
 (0)