Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
77 changes: 77 additions & 0 deletions .github/workflows/pytest-builds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:

solo:
# Matrix builds without pydicom
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install and run pytest
run: |
python -m pip install --upgrade pip
pip install pytest
pytest

pydicom-dev:
# Matrix builds with development pydicom
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install git+https://github.com/pydicom/pydicom
- name: Test with pytest
run: |
pytest

pydicom-release:
# Matrix builds with released pydicom
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pydicom
- name: Test with pytest
run: |
pytest
83 changes: 83 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# .gitignore file

# Backup files
*~
__pycache__/
*.py[cod]
*$py.class

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No __pycache__?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied it from pydicom and added a little bit extra

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to add it then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# Lock files used by the Emacs editor.
.\#*

# Temporary files used by editors.
*.swp
*.orig
*.bak

# A hidden file created by the Mac OS X Finder.
.DS_Store

# python byte-code files
*.pyc
*.pyo

# Build/dist files
dist/*
build/*
distribute*.tar.gz
py3source
.tox/*

# Distribution / packaging
.Python
env/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Docs build
docs/_build/*
doc/_build/*
doc/auto_examples/*
doc/generated/*
doc/reference/generated/*

# coverage.py files
.coverage

# PyCharm IDE files
*.idea*


# jupyter notebooks
*.ipynb
.ipynb_checkpoints/*
tests/test_pixel.py

# mypy
pydicom-data/.mypy_cache/*

# vscode
.vscode/*

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
.pytest_cache
3 changes: 3 additions & 0 deletions data_store/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

from data_store._version import __version__
from data_store.utils import DataStore
54 changes: 54 additions & 0 deletions data_store/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Version information for pydicom-data based on PEP396 and 440."""


import re


__version__ = '1.0.0.dev0'


VERSION_PATTERN = r"""
v?
(?:
(?:(?P<epoch>[0-9]+)!)? # epoch
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
(?P<pre> # pre-release
[-_\.]?
(?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
[-_\.]?
(?P<pre_n>[0-9]+)?
)?
(?P<post> # post release
(?:-(?P<post_n1>[0-9]+))
|
(?:
[-_\.]?
(?P<post_l>post|rev|r)
[-_\.]?
(?P<post_n2>[0-9]+)?
)
)?
(?P<dev> # dev release
[-_\.]?
(?P<dev_l>dev)
[-_\.]?
(?P<dev_n>[0-9]+)?
)?
)
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
"""


def is_canonical(version: str) -> bool:
"""Return True if `version` is a PEP440 conformant version."""
match = re.match(
r'^([1-9]\d*!)?(0|[1-9]\d*)'
r'(\.(0|[1-9]\d*))'
r'*((a|b|rc)(0|[1-9]\d*))'
r'?(\.post(0|[1-9]\d*))'
r'?(\.dev(0|[1-9]\d*))?$', version)

return match is not None


assert is_canonical(__version__)
24 changes: 24 additions & 0 deletions data_store/tests/test_pydicom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Tests for the interface with pydicom."""

import os
from pathlib import Path

import pytest

from pydicom.data import get_testdata_file


class TestPydicom:
"""Test the interface with pydicom works correctly."""
def setup(self):
self.data_path = Path(__file__).resolve().parent.parent.parent / "data"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can simplify similar to other pathlib ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

def test_pydicom_local(self):
"""Test that pydicom gets its own test data."""
fname = "CT_small.dcm"
assert "pydicom/data/test_files" in get_testdata_file(fname)

def test_pydicom_external(self):
"""Test that pydicom uses external data sources first."""
fname = "693_UNCI.dcm"
assert os.fspath(self.data_path / fname) == get_testdata_file(fname)
56 changes: 56 additions & 0 deletions data_store/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Tests for utils.py"""

import os
from pathlib import Path

import pytest

from data_store import DataStore


class TestDataStore:
"""Test the interface for the data sources"""
def setup(self):
self.data_path = Path(__file__).resolve().parent.parent.parent / "data"
self.fname = "693_UNCI.dcm"

def test_data_path(self):
"""Test the path to the data is correct."""
s = DataStore()
assert self.data_path == s.data_path

def test_get_path(self):
"""Test retrieving a file path."""
s = DataStore()
result = s.get_path(self.fname)
assert isinstance(result, str)
assert os.fspath(self.data_path / self.fname) == result

with pytest.raises(ValueError, match=r"No file found named"):
s.get_path("693_UNCI")

def test_get_path_raises_bad_fname(self):
"""Test get_path raises if no matching filename."""
s = DataStore()
msg = r"No file found named 'no matching name.txt'"
with pytest.raises(ValueError, match=msg):
s.get_path("no matching name.txt")

def test_get_path_raises_bad_dtype(self):
"""Test get_path raises if unsupported dtype."""
s = DataStore()
msg = r"No files available for the data type"
with pytest.raises(ValueError, match=msg):
s.get_path(self.fname, dtype=1)

def test_get_paths(self):
"""Test get_paths."""
s = DataStore()
files = s.get_paths('693*')
assert len(files) == 3
assert isinstance(files[0], str)

def test_get_paths_bad_dtype(self):
"""Test get_paths returns empty list if unsupported dtype."""
s = DataStore()
assert [] == s.get_paths("*", dtype=1)
77 changes: 77 additions & 0 deletions data_store/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

from enum import IntEnum
import fnmatch
import os
from pathlib import Path
from typing import List


class DataTypes(IntEnum):
"""Constants for data types."""
DATASET = 0
CHARSET = 1
PALETTE = 2
DICOMDIR = 3


class DataStore:
"""Interface to pydicom-data data storage.

Attributes
----------
data_path : pathlib.Path
The absolute path to the data directory.
"""
def __init__(self) -> None:
"""Initialise a new Interface."""
self.data_path = Path(__file__).resolve().parent.parent / "data"

def get_path(self, name: str, dtype: int = DataTypes.DATASET) -> str:
"""Return the absolute path to the first file with filename `name`

Parameters
----------
name : str
The filename of the file
dtype : int, optional
The type of data to search for, default ``0`` (DICOM dataset).

Returns
-------
str
The absolute path to the first file found with filename `name`.

Raises
------
ValueError
If no file found with a matching filename.
"""
if dtype != DataTypes.DATASET:
raise ValueError("No files available for the data type")

matches = [m for m in self.data_path.glob(name)]
if matches:
return os.fspath(matches[0])

raise ValueError(f"No file found named '{name}'")

def get_paths(self, pattern: str, dtype: int = DataTypes.DATASET) -> List[str]:
"""Return a list of absolute paths for files matching `pattern`.

Parameters
----------
pattern : str
A string pattern to filter the files.
dtype : int, optional
The type of data to search for, default ``0`` (DICOM dataset).

Returns
-------
list of str
A list of absolute paths to files with filenames matching
`pattern`.
"""
if dtype != DataTypes.DATASET:
return []

return [os.fspath(p) for p in self.data_path.glob(pattern)]
Loading