Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • paulmc/fslpy
  • ndcn0236/fslpy
  • seanf/fslpy
3 results
Show changes
Showing
with 331 additions and 100 deletions
The fslpy library The fslpy library
Copyright 2016-2017 University of Oxford, Oxford, UK. Copyright 2016-2023 University of Oxford, Oxford, UK.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
......
include LICENSE include AUTHOR
include CHANGELOG.rst
include COPYRIGHT include COPYRIGHT
include requirements.txt include LICENSE
include requirements-dev.txt include README.rst
include requirements-extra.txt include conftest.py
include pytest.ini recursive-include doc *
recursive-include doc * recursive-include fsl/tests *
recursive-exclude doc/html *
recursive-include tests *
fslpy fslpy
===== =====
.. image:: https://img.shields.io/pypi/v/fslpy.svg
:target: https://pypi.python.org/pypi/fslpy/
.. image:: https://git.fmrib.ox.ac.uk/fsl/fslpy/badges/master/build.svg .. image:: https://anaconda.org/conda-forge/fslpy/badges/version.svg
:target: https://git.fmrib.ox.ac.uk/fsl/fslpy/commits/master/ :target: https://anaconda.org/conda-forge/fslpy
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.1470750.svg
:target: https://doi.org/10.5281/zenodo.1470750
.. image:: https://git.fmrib.ox.ac.uk/fsl/fslpy/badges/master/coverage.svg .. image:: https://git.fmrib.ox.ac.uk/fsl/fslpy/badges/master/coverage.svg
:target: https://git.fmrib.ox.ac.uk/fsl/fslpy/commits/master/ :target: https://git.fmrib.ox.ac.uk/fsl/fslpy/commits/master/
.. image:: https://img.shields.io/pypi/v/fslpy.svg
:target: https://pypi.python.org/pypi/fslpy/
The ``fslpy`` project is a `FSL <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/>`_ The ``fslpy`` project is a `FSL <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/>`_
programming library written in Python. It is used by `FSLeyes programming library written in Python. It is used by `FSLeyes
<https://git.fmrib.ox.ac.uk/fsl/fsleyes/fsleyes/>`_. <https://git.fmrib.ox.ac.uk/fsl/fsleyes/fsleyes/>`_.
``fslpy`` is tested against Python versions 3.10, 3.11, 3.12, and 3.13.
Installation Installation
------------ ------------
...@@ -26,71 +31,107 @@ Install ``fslpy`` and its core dependencies via pip:: ...@@ -26,71 +31,107 @@ Install ``fslpy`` and its core dependencies via pip::
pip install fslpy pip install fslpy
``fslpy`` is also available on `conda-forge <https://conda-forge.org/>`_::
conda install -c conda-forge fslpy
Dependencies Dependencies
------------ ------------
All of the core dependencies of ``fslpy`` are listed in the `requirements.txt All of the core dependencies of ``fslpy`` are listed in the
<requirements.txt>`_ file. `pyproject.toml <pyproject.toml>`_ file.
Some extra dependencies are listed in `requirements.txt Some optional dependencies (labelled ``extra`` in ``pyproject.toml``) provide
<requirements-extra.txt>`_ which provide addditional functionality: addditional functionality:
- ``wxPython``: The `fsl.utils.idle <fsl/utils/idle.py>`_ module has - ``wxPython``: The `fsl.utils.idle <fsl/utils/idle.py>`_ module has
functionality to schedule functions on the ``wx`` idle loop. functionality to schedule functions on the ``wx`` idle loop.
- ``indexed_gzip``: The `fsl.data.image.Image <fsl/data/image.py>`_ class - ``indexed_gzip``: The `fsl.data.image.Image <fsl/data/image.py>`_ class
can use ``indexed_gzip`` to keep large compressed images on disk instead can use ``indexed_gzip`` to keep large compressed images on disk instead
of decompressing and loading them into memory.. of decompressing and loading them into memory..
- ``trimesh``/``rtree``: The `fsl.data.mesh.TriangleMesh <fsl/data/mesh.py>`_ - ``trimesh``/``rtree``: The `fsl.data.mesh.TriangleMesh <fsl/data/mesh.py>`_
class has some methods which use ``trimesh`` to perform geometric queries class has some methods which use ``trimesh`` to perform geometric queries
on the mesh. on the mesh.
- ``Pillow``: The `fsl.data.bitmap.Bitmap <fsl/data/bitmap.py>`_ class uses
``Pillow`` to load image files.
To install these additional dependencies, you first need to install wxPython, If you are using Linux, you need to install wxPython first, as binaries are
which is still in pre-relaes. not available on PyPI. Install wxPython like so, changing the URL for your
specific platform::
- **macOS**: ``pip install --pre wxPython`` pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk2/ubuntu-16.04/ wxpython
- **Linux** (change the URL for your specific platform): ``pip install --only-binary wxpython -f https://extras.wxpython.org/wxPython4/extras/linux/gtk2/ubuntu-16.04/ wxpython``
The ``rtree`` library also assumes that ``libspatialindex`` is installed on Once wxPython has been installed, you can type the following to install the
your system. remaining optional dependencies::
pip install "fslpy[extra]"
Once wxPython has been installed, you can simply type the following to install Dependencies for testing and documentation are also listed in ``pyproject.toml``,
the rest of the extra dependencies:: and are respectively labelled as ``test`` and ``doc``.
pip install fslpy[extras]
Non-Python dependencies
^^^^^^^^^^^^^^^^^^^^^^^
The `fsl.data.dicom <fsl/data/dicom.py>`_ module requires the presence of
Chris Rorden's `dcm2niix <https://github.com/rordenlab/dcm2niix>`_ program.
The ``rtree`` library assumes that ``libspatialindex`` is installed on
your system.
The `fsl.transform.x5 <fsl/transform/x5.py>`_ module uses `h5py
<https://www.h5py.org/>`_, which requires ``libhdf5``.
Documentation Documentation
------------- -------------
API documentation for ``fslpy`` is hosted at
https://open.win.ox.ac.uk/pages/fsl/fslpy/.
``fslpy`` is documented using `sphinx <http://http://sphinx-doc.org/>`_. You ``fslpy`` is documented using `sphinx <http://http://sphinx-doc.org/>`_. You
can build the API documentation by running:: can build the API documentation by running::
python setup.py doc pip install ".[doc]"
sphinx-build doc html
The HTML documentation will be generated and saved in the ``doc/html/`` The HTML documentation will be generated and saved in the ``html/``
directory. directory.
If you are interested in contributing to ``fslpy``, check out the
`contributing guide <doc/contributing.rst>`_.
Tests Tests
----- -----
Run the test suite via:: Run the test suite via::
python setup.py test pip install ".[test]"
pytest
A test report will be generated at ``report.html``, and a code coverage report
will be generated in ``htmlcov/``. Some tests will only pass if the test environment meets certain criteria -
refer to the ``tool.pytest.init_options`` section of
[``pyproject.toml``](pyproject.toml) for a list of [pytest
marks](https://docs.pytest.org/en/7.1.x/example/markers.html) which can be
selectively enabled or disabled.
Contributing
------------
If you are interested in contributing to ``fslpy``, check out the
`contributing guide <doc/contributing.rst>`_.
Credits Credits
......
...@@ -14,42 +14,24 @@ import numpy as np ...@@ -14,42 +14,24 @@ import numpy as np
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption('--niters',
type=int,
action='store',
default=50,
help='Number of test iterations for imagewrapper')
parser.addoption('--testdir', parser.addoption('--testdir',
action='store', action='store',
help='FSLeyes test data directory') help='FSLeyes test data directory')
parser.addoption('--seed', parser.addoption('--seed',
type=int, type=int,
help='Seed for random number generator') help='Seed for random number generator')
@pytest.fixture
def testdir(request):
"""FSLeyes test data directory."""
return op.expanduser(request.config.getoption('--testdir'))
@pytest.fixture
def niters(request):
"""Number of test iterations."""
return request.config.getoption('--niters')
@pytest.fixture @pytest.fixture
def seed(request): def seed(request):
seed = request.config.getoption('--seed') seed = request.config.getoption('--seed')
if seed is None: if seed is None:
seed = np.random.randint(2 ** 32) seed = np.random.randint(2 ** 30)
np.random.seed(seed) np.random.seed(seed)
random .seed(seed) random .seed(seed)
print('Seed for random number generator: {}'.format(seed)) print('Seed for random number generator: {}'.format(seed))
......
/* override table width restrictions */
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: normal;
}
.wy-table-responsive {
margin-bottom: 24px;
max-width: 100%;
overflow: visible;
}
...@@ -12,12 +12,70 @@ ...@@ -12,12 +12,70 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import glob
import itertools as it
import os import os
import os.path as op
import sys import sys
import datetime import datetime
date = datetime.date.today() date = datetime.date.today()
def check_for_missing_stubs():
docdir = op.dirname(__file__)
basedir = op.join(docdir, '..')
modules = []
def tomodname(f):
if f.endswith('.py'):
f = f[:-3]
return op.relpath(op.join(dirpath, f), basedir).replace(op.sep, '.')
for dirpath, dirnames, filenames in os.walk(op.join(basedir, 'fsl')):
for d in dirnames:
if d == '__pycache__':
continue
if len(glob.glob(op.join(dirpath, d, '**', '*.py'), recursive=True)) == 0:
continue
modules.append(tomodname(d))
for f in filenames:
if not f.endswith('.py'):
continue
if f in ('__init__.py', '__main__.py'):
continue
modules.append(tomodname(f))
modules = [m for m in modules if not m.startswith('fsl.tests')]
# import fsl
# modules = recurse(fsl)
# modules = [m.name for m in modules]
# print()
# print()
# print()
for mod in modules:
docfile = op.join(docdir, f'{mod}.rst')
if not op.exists(docfile):
print(f'No doc file found for module: {mod}')
for docfile in glob.glob(op.join(docdir, '*.rst')):
docfile = op.relpath(docfile, basedir)
mod = op.splitext(op.basename(docfile))[0]
if mod not in modules:
print(f'No module found for doc file: {docfile}')
if __name__ == '__main__':
check_for_missing_stubs()
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
...@@ -33,7 +91,8 @@ date = datetime.date.today() ...@@ -33,7 +91,8 @@ date = datetime.date.today()
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.autosummary', 'sphinx.ext.viewcode',
'sphinx.ext.autosummary',
'sphinx.ext.mathjax', 'sphinx.ext.mathjax',
'sphinx.ext.graphviz', 'sphinx.ext.graphviz',
'sphinx.ext.todo', 'sphinx.ext.todo',
...@@ -55,13 +114,13 @@ master_doc = 'index' ...@@ -55,13 +114,13 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'fslpy' project = u'fslpy'
copyright = u'{}, Paul McCarthy, University of Oxford, Oxford, UK'.format( copyright = u'{}, FMRIB Centre, University of Oxford, Oxford, UK'.format(
date.year) date.year)
# Links to other things # Links to other things
rst_epilog = """ rst_epilog = """
.. |fsleyes_apidoc| replace:: FSLeyes .. |fsleyes_apidoc| replace:: FSLeyes
.. _fsleyes_apidoc: http://users.fmrib.ox.ac.uk/~paulmc/fsleyes_apidoc/index.html .. _fsleyes_apidoc: http://users.fmrib.ox.ac.uk/~paulmc/fsleyes/userdoc/latest/index.html
""" """
...@@ -121,6 +180,7 @@ pygments_style = 'sphinx' ...@@ -121,6 +180,7 @@ pygments_style = 'sphinx'
# a list of builtin themes. # a list of builtin themes.
html_theme = 'sphinx_rtd_theme' html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
# documentation. # documentation.
...@@ -148,7 +208,11 @@ html_theme = 'sphinx_rtd_theme' ...@@ -148,7 +208,11 @@ html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = [] html_static_path = ['_static']
html_css_files = [
'theme_overrides.css', # overrides for wide tables in RTD theme
]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
...@@ -353,26 +417,12 @@ epub_exclude_files = ['search.html'] ...@@ -353,26 +417,12 @@ epub_exclude_files = ['search.html']
# special-members flag) # special-members flag)
autoclass_content = 'class' autoclass_content = 'class'
# Document private members and special members (e.g. __init__)
autodocsourc_default_flags = ['private-members', 'special-members']
# Documentation for python modules is in the same order
# as the source code.
autodoc_member_order = 'bysource'
def autodoc_skip_member(app, what, name, obj, skip, options):
# Do not document the _sync_* properties autodoc_default_options = {
# that are added by the props package to 'special-members' : True,
# all SyncableHasProperties classes. 'private-members' : True,
if what == 'class': 'undoc-members' : True,
attName = name.split('.')[-1] 'member-order' : 'bysource',
return skip or attName.startswith('_sync_') }
return skip or False
def setup(app):
app.connect('autodoc-skip-member', autodoc_skip_member)
graphviz_output_format = 'svg' graphviz_output_format = 'svg'
...@@ -9,10 +9,10 @@ Development model ...@@ -9,10 +9,10 @@ Development model
----------------- -----------------
- The master branch should always be stable and ready to release. All - The main branch should always be stable and ready to release. All
development occurs on the master branch. development occurs on the main branch.
- All changes to the master branch occur via merge requests. Individual - All changes to the main branch occur via merge requests. Individual
developers are free to choose their own development workflow in their own developers are free to choose their own development workflow in their own
repositories. repositories.
...@@ -24,6 +24,27 @@ Development model ...@@ -24,6 +24,27 @@ Development model
- Coding conventions are adhered to (unless there is good reason not to). - Coding conventions are adhered to (unless there is good reason not to).
Commit messages
---------------
To aid readability, all commit messages should be prefixed with one or more of
the following labels (this convention has been inherited from `nibabel
<https://github.com/nipy/nibabel>`_):
* *BF* : bug fix
* *RF* : refactoring
* *ENH*: enhancement/new feature
* *BW* : addresses backward-compatibility
* *OPT* : optimization
* *BK* : breaks something and/or tests fail
* *PL* : making pylint happier
* *DOC* : for all kinds of documentation related commits
* *TEST*: for adding or changing tests
* *MNT* : for administrative/maintenance changes
* *CI* : for continuous-integration changes
Version number Version number
-------------- --------------
...@@ -45,10 +66,10 @@ numbers:: ...@@ -45,10 +66,10 @@ numbers::
backwards-incompatible changes. backwards-incompatible changes.
The version number in the ``master`` branch should be of the form The version number in the ``main`` branch should be of the form
``major.minor.patch.dev``, to indicate that any releases made from this branch ``major.minor.patch.dev0``, to indicate that any releases made from this
are development releases (although development releases are not part of the branch are development releases (although development releases are not part of
release model). the release model).
Releases Releases
...@@ -62,7 +83,7 @@ name for minor release ``1.0`` would be ``v1.0``. ...@@ -62,7 +83,7 @@ name for minor release ``1.0`` would be ``v1.0``.
Patches and bugfixes may be added to these release branches as ``patch`` Patches and bugfixes may be added to these release branches as ``patch``
releases. These changes should be made on the master branch like any other releases. These changes should be made on the main branch like any other
change (i.e. via merge requests), and then cherry-picked onto the relevant change (i.e. via merge requests), and then cherry-picked onto the relevant
release branch(es). release branch(es).
...@@ -73,6 +94,44 @@ example, the first release off the ``v1.0`` branch would be tagged with ...@@ -73,6 +94,44 @@ example, the first release off the ``v1.0`` branch would be tagged with
``1.0.1``, ``1.0.2``, etc. ``1.0.1``, ``1.0.2``, etc.
Major/minor releases
^^^^^^^^^^^^^^^^^^^^
Follow this process for major and minor releases. Steps 1 and 2 should be
performed via a merge request onto the main branch, and step 4 via a merge
request onto the relevant minor branch.
1. Update the changelog on the main branch to include the new version number
and release date.
2. On the main branch, update the version number in ``fsl/version.py`` to
a development version of **the next** minor release number. For example,
if you are about to release version ``1.3.0``, the version in the main
branch should be ``1.4.0.dev0``.
3. Create the new minor release branch off the main branch.
4. Update the version number on the release branch. If CI tests fail on the
release branch, postpone the release until they are fixed.
5. Tag the new release on the minor release branch.
Bugfix/patch releases
^^^^^^^^^^^^^^^^^^^^^
Follow this process for patch releases. Step 1 should be performed via
a merge request onto the main branch, and step 2 via a merge request onto
the relevant minor branch.
1. Add the fix to the main branch, along with an updated changelog including
the version number and date for the bugfix release.
2. Cherry-pick the relevant commit(s) from the main branch onto the minor
release branch, and update the version number on the minor release branch.
If CI tests fail on the release branch, go back to step 1.
3. Tag the new release on the minor release branch.
Testing Testing
------- -------
...@@ -81,7 +140,7 @@ Unit and integration tests are currently run with ``py.test`` and ...@@ -81,7 +140,7 @@ Unit and integration tests are currently run with ``py.test`` and
``coverage``. ``coverage``.
- Aim for 100% code coverage. - Aim for 100% code coverage.
- Tests must pass on python 2.7, 3.4, 3.5, and 3.6 - Tests must pass on python 3.5, 3.6, and 3.7.
Coding conventions Coding conventions
...@@ -123,6 +182,7 @@ for a list of error codes): ...@@ -123,6 +182,7 @@ for a list of error codes):
- E302: expected 2 blank lines, found 0 - E302: expected 2 blank lines, found 0
- E303: too many blank lines (3) - E303: too many blank lines (3)
- E701: multiple statements on one line (colon) - E701: multiple statements on one line (colon)
- W504: line break after binary operator
The ``pylint`` tool can be *very* opinionated about how you write your code, The ``pylint`` tool can be *very* opinionated about how you write your code,
...@@ -141,7 +201,7 @@ refactoring and convention messages, and a few select warnings (type ``pylint ...@@ -141,7 +201,7 @@ refactoring and convention messages, and a few select warnings (type ``pylint
To check code with ``flake8`` and ``pylint``, I use the following commands:: To check code with ``flake8`` and ``pylint``, I use the following commands::
flake8 --ignore=E127,E201,E203,E221,E222,E241,E271,E272,E301,E302,E303,E701 fsl flake8 --ignore=E127,E201,E203,E221,E222,E241,E271,E272,E301,E302,E303,E701,W504 fsl
pylint --extension-pkg-whitelist=numpy,wx \ pylint --extension-pkg-whitelist=numpy,wx \
--generated-members=np.int8,np.uint8,np.int16,np.uint16,np.int32,np.uint32,np.int64,np.uint64,np.float32,np.float64,np.float128,wx.PyDeadObjectError \ --generated-members=np.int8,np.uint8,np.int16,np.uint16,np.int32,np.uint32,np.int64,np.uint64,np.float32,np.float64,np.float128,wx.PyDeadObjectError \
--disable=R,C,W0511,W0703,W1202 fsl --disable=R,C,W0511,W0703,W1202 fsl
``fsl.data.bitmap``
===================
.. automodule:: fsl.data.bitmap
:members:
:undoc-members:
:show-inheritance:
``fsl.data.cifti``
==================
.. automodule:: fsl.data.cifti
:members:
:undoc-members:
:show-inheritance:
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
:hidden: :hidden:
fsl.data.atlases fsl.data.atlases
fsl.data.bitmap
fsl.data.cifti
fsl.data.constants fsl.data.constants
fsl.data.dicom fsl.data.dicom
fsl.data.dtifit fsl.data.dtifit
...@@ -12,6 +14,7 @@ ...@@ -12,6 +14,7 @@
fsl.data.featdesign fsl.data.featdesign
fsl.data.featimage fsl.data.featimage
fsl.data.fixlabels fsl.data.fixlabels
fsl.data.freesurfer
fsl.data.gifti fsl.data.gifti
fsl.data.image fsl.data.image
fsl.data.imagewrapper fsl.data.imagewrapper
...@@ -19,8 +22,10 @@ ...@@ -19,8 +22,10 @@
fsl.data.melodicimage fsl.data.melodicimage
fsl.data.mesh fsl.data.mesh
fsl.data.mghimage fsl.data.mghimage
fsl.data.utils
fsl.data.vest fsl.data.vest
fsl.data.volumelabels fsl.data.volumelabels
fsl.data.vtk
.. automodule:: fsl.data .. automodule:: fsl.data
:members: :members:
......
``fsl.data.utils``
==================
.. automodule:: fsl.data.utils
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.Text2Vest``
=========================
.. automodule:: fsl.scripts.Text2Vest
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.Vest2Text``
=========================
.. automodule:: fsl.scripts.Vest2Text
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.fsl_abspath``
===========================
.. automodule:: fsl.scripts.fsl_abspath
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.fsl_apply_x5``
============================
.. automodule:: fsl.scripts.fsl_apply_x5
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.fsl_convert_x5``
==============================
.. automodule:: fsl.scripts.fsl_convert_x5
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.fsl_ents``
========================
.. automodule:: fsl.scripts.fsl_ents
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.imln``
====================
.. automodule:: fsl.scripts.imln
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.imrm``
====================
.. automodule:: fsl.scripts.imrm
:members:
:undoc-members:
:show-inheritance:
``fsl.scripts.imtest``
======================
.. automodule:: fsl.scripts.imtest
:members:
:undoc-members:
:show-inheritance: