Skip to content

Commit

Permalink
[STY] Use black and isort to manage library code style (#758)
Browse files Browse the repository at this point in the history
* Add black and isort to config and README.

* Remove build system requirements.

* Run black and isort on package.

* Fix combined multiline strings.

It's the one thing black always messes up.

* Run black again.

* Replace single-quote strings.

* Fix import.

* isort

* Document our preferred code style.

* Run black again.
  • Loading branch information
tsalo authored Sep 13, 2021
1 parent bb02ac3 commit bb57384
Show file tree
Hide file tree
Showing 50 changed files with 2,186 additions and 1,742 deletions.
13 changes: 9 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Thanks for contributing!
- [ ] Make sure you have docstrings for any new functions
- [ ] Make sure that docstrings are updated for edited functions
- [ ] Make sure you note any issues that will be closed by your PR
- [ ] Make sure your code follows `tedana`'s preferred style by running [black][link_black] and [isort][link_isort].
- [ ] Take a look at the automatically generated readthedocs for your PR (Show all checks -> continuous-documentation/readthedocs -> Details)

### Comprehensive Developer Guide
Expand All @@ -284,11 +285,13 @@ For additional, in-depth information on contributing to `tedana`, please see our
Docstrings should follow [numpydoc][link_numpydoc] convention.
We encourage extensive documentation.

The python code itself should follow [PEP8][link_pep8] convention
whenever possible, with at most about 500 lines of code (not including docstrings) per script.
The Python code itself should follow [PEP8][link_pep8] convention,
with at most about 500 lines of code (not including docstrings) per script.
We use [black][link_black] and [isort][link_isort] to enforce this code style,
and we strongly recommend running both tools on your fork before opening a pull request.

Additionally, we have adopted a purely functional approach in `tedana`, so we
avoid defining our own classes within the library.
Additionally, we have adopted a primarily functional approach in `tedana`,
so we avoid defining our own classes within the library unless absolutely necessary.

Our documentation is written in [ReStructuredText](#writing-in-restructuredtext),
which we explain in more detail below.
Expand Down Expand Up @@ -367,6 +370,8 @@ You're awesome. :wave::smiley:

[link_numpydoc]: https://numpydoc.readthedocs.io/en/latest/format.html
[link_pep8]: https://www.python.org/dev/peps/pep-0008/
[link_black]: https://black.readthedocs.io/en/stable/
[link_isort]: https://pycqa.github.io/isort/
[link_rst_guide]: http://docs.sphinxdocs.com/en/latest/step-1.html

[link_contributors]: https://github.com/ME-ICA/tedana/graphs/contributors
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ multi-echo functional magnetic resonance imaging (fMRI) data.
[![Join the chat at https://gitter.im/ME-ICA/tedana](https://badges.gitter.im/ME-ICA/tedana.svg)](https://gitter.im/ME-ICA/tedana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join our tinyletter mailing list](https://img.shields.io/badge/receive-our%20newsletter%20❤%EF%B8%8F-blueviolet.svg)](https://tinyletter.com/tedana-devs)
[![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)


## About
Expand Down
70 changes: 37 additions & 33 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#
import os
import sys
sys.path.insert(0, os.path.abspath('sphinxext'))

sys.path.insert(0, os.path.abspath("sphinxext"))
sys.path.insert(0, os.path.abspath(os.path.pardir))

from github_link import make_linkcode_resolve
Expand All @@ -39,46 +40,49 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'matplotlib.sphinxext.plot_directive',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.doctest',
'sphinx.ext.ifconfig',
'sphinx.ext.intersphinx',
'sphinx.ext.linkcode',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
'sphinxarg.ext',
"matplotlib.sphinxext.plot_directive",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.doctest",
"sphinx.ext.ifconfig",
"sphinx.ext.intersphinx",
"sphinx.ext.linkcode",
"sphinx.ext.napoleon",
"sphinx.ext.todo",
"sphinxarg.ext",
]

import sphinx
from distutils.version import LooseVersion
if LooseVersion(sphinx.__version__) < LooseVersion('1.4'):
extensions.append('sphinx.ext.pngmath')

if LooseVersion(sphinx.__version__) < LooseVersion("1.4"):
extensions.append("sphinx.ext.pngmath")
else:
extensions.append('sphinx.ext.imgmath')
extensions.append("sphinx.ext.imgmath")

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]

# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"

# The master toctree document.
master_doc = 'index'
master_doc = "index"

# General information about the project.
from datetime import datetime # access current time and date
project = 'tedana'
copyright = '2017-' + datetime.today().strftime("%Y") + ', tedana developers'
author = 'tedana developers'

project = "tedana"
copyright = "2017-" + datetime.today().strftime("%Y") + ", tedana developers"
author = "tedana developers"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
import tedana

version = tedana.__version__
# The full version, including alpha/beta/rc tags.
release = tedana.__version__
Expand All @@ -93,10 +97,10 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
Expand Down Expand Up @@ -124,41 +128,41 @@
#
# installing theme package
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'

html_theme = "sphinx_rtd_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
# documentation.

html_theme_options = {
'includehidden': False,
"includehidden": False,
}

# 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,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]


# https://github.com/rtfd/sphinx_rtd_theme/issues/117
def setup(app):
app.add_css_file('theme_overrides.css')
app.add_css_file("theme_overrides.css")
app.add_js_file("https://cdn.rawgit.com/chrisfilo/zenodo.js/v0.1/zenodo.js")


html_favicon = '_static/tedana_favicon.png'
html_favicon = "_static/tedana_favicon.png"


# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'tedanadoc'
htmlhelp_basename = "tedanadoc"

# The following is used by sphinx.ext.linkcode to provide links to github
linkcode_resolve = make_linkcode_resolve('tedana',
u'https://github.com/me-ica/'
'tedana/blob/{revision}/'
'{package}/{path}#L{lineno}')
linkcode_resolve = make_linkcode_resolve(
"tedana", "https://github.com/me-ica/" "tedana/blob/{revision}/" "{package}/{path}#L{lineno}"
)

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
Expand All @@ -168,5 +172,5 @@ def setup(app):
"matplotlib": ("https://matplotlib.org/", None),
"nibabel": ("https://nipy.org/nibabel/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
"nilearn": ("http://nilearn.github.io/", None)
"nilearn": ("http://nilearn.github.io/", None),
}
4 changes: 4 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ multi-echo functional magnetic resonance imaging (fMRI) data.
:target: https://tinyletter.com/tedana-devs
:alt: Join our tinyletter mailing list

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code style: black


*****
About
Expand Down
29 changes: 13 additions & 16 deletions docs/sphinxext/github_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
import sys
from functools import partial

REVISION_CMD = 'git rev-parse --short HEAD'
REVISION_CMD = "git rev-parse --short HEAD"


def _get_git_revision():
try:
revision = subprocess.check_output(REVISION_CMD.split()).strip()
except (subprocess.CalledProcessError, OSError):
print('Failed to execute git to get revision')
print("Failed to execute git to get revision")
return None
return revision.decode('utf-8')
return revision.decode("utf-8")


def _linkcode_resolve(domain, info, package, url_fmt, revision):
Expand All @@ -38,17 +38,17 @@ def _linkcode_resolve(domain, info, package, url_fmt, revision):

if revision is None:
return
if domain not in ('py', 'pyx'):
if domain not in ("py", "pyx"):
return
if not info.get('module') or not info.get('fullname'):
if not info.get("module") or not info.get("fullname"):
return

class_name = info['fullname'].split('.')[0]
class_name = info["fullname"].split(".")[0]
if type(class_name) != str:
# Python 2 only
class_name = class_name.encode('utf-8')
module = __import__(info['module'], fromlist=[class_name])
obj = attrgetter(info['fullname'])(module)
class_name = class_name.encode("utf-8")
module = __import__(info["module"], fromlist=[class_name])
obj = attrgetter(info["fullname"])(module)

try:
fn = inspect.getsourcefile(obj)
Expand All @@ -62,14 +62,12 @@ def _linkcode_resolve(domain, info, package, url_fmt, revision):
if not fn:
return

fn = os.path.relpath(fn,
start=os.path.dirname(__import__(package).__file__))
fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__))
try:
lineno = inspect.getsourcelines(obj)[1]
except Exception:
lineno = ''
return url_fmt.format(revision=revision, package=package,
path=fn, lineno=lineno)
lineno = ""
return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno)


def make_linkcode_resolve(package, url_fmt):
Expand All @@ -84,5 +82,4 @@ def make_linkcode_resolve(package, url_fmt):
'{path}#L{lineno}')
"""
revision = _get_git_revision()
return partial(_linkcode_resolve, revision=revision, package=package,
url_fmt=url_fmt)
return partial(_linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt)
25 changes: 25 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[tool.black]
line-length = 99
target-version = ['py37']
include = '\.pyi?$'
exclude = '''
(
/(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.github
| \.hg
| \.pytest_cache
| _build
| build
| dist
)/
| get_version.py
| versioneer.py
)
'''

[tool.isort]
profile = "black"
multi_line_output = 3
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ parentdir_prefix =
[flake8]
max-line-length = 99
exclude=*build/
ignore = E126,E402,W503,W504
ignore = E203,E402,W503
per-file-ignores =
*/__init__.py:F401
docstring-convention = numpy

[tool:pytest]
log_cli = true
36 changes: 19 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def main():
""" Install entry-point """
"""Install entry-point"""
import versioneer
from io import open
from os import path as op
Expand All @@ -24,29 +24,29 @@ def main():
REQUIRES,
TESTS_REQUIRES,
EXTRA_REQUIRES,
PYTHON_REQUIRES
PYTHON_REQUIRES,
)

pkg_data = {
'tedana': [
'tests/data/*',
'reporting/data/*',
'reporting/data/html/*',
'resources/config/*',
"tedana": [
"tests/data/*",
"reporting/data/*",
"reporting/data/html/*",
"resources/config/*",
]
}

root_dir = op.dirname(op.abspath(getfile(currentframe())))

with open(op.join(root_dir, 'README.md'), encoding='utf-8') as f:
with open(op.join(root_dir, "README.md"), encoding="utf-8") as f:
long_description = f.read()

version = None
cmdclass = {}
if op.isfile(op.join(root_dir, 'tedana', 'VERSION')):
with open(op.join(root_dir, 'tedana', 'VERSION')) as vfile:
if op.isfile(op.join(root_dir, "tedana", "VERSION")):
with open(op.join(root_dir, "tedana", "VERSION")) as vfile:
version = vfile.readline().strip()
pkg_data['tedana'].insert(0, 'VERSION')
pkg_data["tedana"].insert(0, "VERSION")

if version is None:
version = versioneer.get_version()
Expand All @@ -71,16 +71,18 @@ def main():
install_requires=REQUIRES,
tests_require=TESTS_REQUIRES,
extras_require=EXTRA_REQUIRES,
entry_points={'console_scripts': [
't2smap=tedana.workflows.t2smap:_main',
'tedana=tedana.workflows.tedana:_main'
]},
entry_points={
"console_scripts": [
"t2smap=tedana.workflows.t2smap:_main",
"tedana=tedana.workflows.tedana:_main",
]
},
packages=find_packages(exclude=("tests",)),
package_data=pkg_data,
zip_safe=False,
cmdclass=cmdclass
cmdclass=cmdclass,
)


if __name__ == '__main__':
if __name__ == "__main__":
main()
Loading

0 comments on commit bb57384

Please sign in to comment.