diff --git a/.black b/.black new file mode 100644 index 0000000..aedc909 --- /dev/null +++ b/.black @@ -0,0 +1,17 @@ +target-version = ['py310'] +exclude = ''' +( + /( + \.eggs + | \.git + | \.mypy_cache + | \.tox + | \.venv + | _build + | build + | dist + | docs + | .history + )/ +) +''' diff --git a/.codecov.yaml b/.codecov.yaml index bd2cef3..8fe09b7 100644 --- a/.codecov.yaml +++ b/.codecov.yaml @@ -4,3 +4,8 @@ coverage: project: default: threshold: 0.2% + +codecov: + require_ci_to_pass: false + notify: + wait_for_ci: true diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..cf3fcbe --- /dev/null +++ b/.codespellrc @@ -0,0 +1,15 @@ +[codespell] +skip = *.asdf,*.fits,*.fts,*.header,*.json,*.xsh,*cache*,*egg*,*extern*,.git,.idea,.tox,_build,*truncated,*.svg,.asv_env,.history +ignore-words-list = + afile, + alog, + nd, + nin, + observ, + ot, + precess + precessed, + requestor, + sav, + te, + upto, diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..6e20457 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,30 @@ +[run] +omit = + drms/conftest.py + drms/*setup_package* + drms/extern/* + drms/version* + */drms/conftest.py + */drms/*setup_package* + */drms/extern/* + */drms/version* + +[report] +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + # Don't complain about packages we have installed + except ImportError + # Don't complain if tests don't hit assertions + raise AssertionError + raise NotImplementedError + # Don't complain about script hooks + def main(.*): + # Ignore branches that don't pertain to this version of Python + pragma: py{ignore_python_version} + # Don't complain about IPython completion helper + def _ipython_key_completions_ + # typing.TYPE_CHECKING is False at runtime + if TYPE_CHECKING: + # Ignore typing overloads + @overload diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..3874a52 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,31 @@ +{ + "template": "https://github.com/sunpy/package-template", + "commit": "c0e51ac3283d88346534da0aac441f37f624b283", + "checkout": null, + "context": { + "cookiecutter": { + "package_name": "drms", + "module_name": "drms", + "short_description": "Access HMI, AIA and MDI data from the Standford JSOC DRMS", + "author_name": "The SunPy Community", + "author_email": "sunpy@googlegroups.com", + "project_url": "https://sunpy.org", + "license": "BSD 2-Clause", + "minimum_python_version": "3.10", + "use_compiled_extensions": "n", + "enable_dynamic_dev_versions": "y", + "include_example_code": "n", + "include_cruft_update_github_workflow": "y", + "_sphinx_theme": "alabaster", + "_parent_project": "", + "_install_requires": "", + "_copy_without_render": [ + "docs/_templates", + "docs/_static", + ".github/workflows/sub_package_update.yml" + ], + "_template": "https://github.com/sunpy/package-template" + } + }, + "directory": null +} diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..a0f9eab --- /dev/null +++ b/.flake8 @@ -0,0 +1,27 @@ +[flake8] +ignore = + # missing-whitespace-around-operator + E225 + # missing-whitespace-around-arithmetic-operator + E226 + # line-too-long + E501 + # unused-import + F401 + # undefined-local-with-import-star + F403 + # redefined-while-unused + F811 + # Line break occurred before a binary operator + W503, + # Line break occurred after a binary operator + W504 +max-line-length = 110 +exclude = + .git + __pycache__ + docs/conf.py + build + drms/__init__.py +rst-directives = + plot diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34344fb..e755edb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ on: - '!*pre*' - '!*post*' pull_request: + # Allow manual runs through the web UI workflow_dispatch: schedule: # ┌───────── minute (0 - 59) @@ -35,9 +36,23 @@ jobs: posargs: -n auto envs: | - linux: py312 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + sdist_verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: python -m pip install -U --user build + - run: python -m build . --sdist + - run: python -m pip install -U --user twine + - run: python -m twine check dist/* test: - needs: [core] + needs: [core, sdist_verify] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main with: submodules: false @@ -48,14 +63,16 @@ jobs: - windows: py10 - macos: py311 - linux: py310-oldestdeps + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} docs: needs: [core] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main with: + default_python: '3.12' submodules: false pytest: false - default_python: '3.12' toxdeps: "tox-pypi-filter" libraries: | apt: @@ -64,10 +81,9 @@ jobs: - linux: build_docs online: - if: "!startsWith(github.event.ref, 'refs/tags/v')" - needs: [docs] uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main with: + default_python: '3.12' submodules: false coverage: codecov toxdeps: "tox-pypi-filter" @@ -75,21 +91,23 @@ jobs: envs: | - linux: py312-online - linux: py312-sunpy + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} publish: - # Build wheels when pushing to any branch - # publish_pure_python.yml will only publish if tagged ^v.* + # Build wheels on PRs only when labelled. Releases will only be published if tagged ^v.* + # see https://github-actions-workflows.openastronomy.org/en/latest/publish.html#upload-to-pypi if: | + github.event_name != 'pull_request' || ( - github.event_name != 'pull_request' - ) || ( github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Run publish') ) - needs: [test] + needs: [test, docs] uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@main with: - test_extras: 'dev' + python-version: '3.12' + test_extras: 'tests' test_command: 'pytest -p no:warnings --doctest-rst --pyargs drms' submodules: false secrets: diff --git a/.github/workflows/sub_package_update.yml b/.github/workflows/sub_package_update.yml new file mode 100644 index 0000000..7455847 --- /dev/null +++ b/.github/workflows/sub_package_update.yml @@ -0,0 +1,79 @@ +# This template is taken from the cruft example code, for further information please see: +# https://cruft.github.io/cruft/#automating-updates-with-github-actions +name: Automatic Update from package template +permissions: + contents: write + pull-requests: write + +on: + # Allow manual runs through the web UI + workflow_dispatch: + schedule: + # ┌───────── minute (0 - 59) + # │ ┌───────── hour (0 - 23) + # │ │ ┌───────── day of the month (1 - 31) + # │ │ │ ┌───────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────── day of the week (0 - 6 or SUN-SAT) + - cron: '0 7 * * 1' # Every Monday at 7am UTC + +jobs: + update: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + include: + - add-paths: . + body: apply the changes to this repo. + branch: cruft/update + commit-message: "Automatic package template update" + title: Updates from the package template + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Cruft + run: python -m pip install git+https://github.com/Cadair/cruft@patch-p1 + + - name: Check if update is available + continue-on-error: false + id: check + run: | + CHANGES=0 + if [ -f .cruft.json ]; then + if ! cruft check; then + CHANGES=1 + fi + else + echo "No .cruft.json file" + fi + + echo "has_changes=$CHANGES" >> "$GITHUB_OUTPUT" + + - name: Run update if available + if: steps.check.outputs.has_changes == '1' + run: | + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + git config --global user.name "${{ github.actor }}" + + cruft update --skip-apply-ask --refresh-private-variables + git restore --staged . + + - name: Create pull request + if: steps.check.outputs.has_changes == '1' + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: ${{ matrix.add-paths }} + commit-message: ${{ matrix.commit-message }} + branch: ${{ matrix.branch }} + delete-branch: true + branch-suffix: timestamp + title: ${{ matrix.title }} + body: | + This is an autogenerated PR, which will ${{ matrix.body }}. + [Cruft](https://cruft.github.io/cruft/) has detected updates from the Package Template diff --git a/.gitignore b/.gitignore index 6a2621c..110d24e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,11 +24,12 @@ parts/ sdist/ var/ wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST - +drms/_version.py # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. @@ -42,15 +43,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover +*.py,cover .hypothesis/ .pytest_cache/ -junit/ +cover/ # Translations *.mo @@ -60,6 +63,7 @@ junit/ *.log local_settings.py db.sqlite3 +db.sqlite3-journal # Flask stuff: instance/ @@ -70,18 +74,47 @@ instance/ # Sphinx documentation docs/_build/ +# automodapi +docs/api +docs/sg_execution_times.rst # PyBuilder +.pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version +# IPython +profile_default/ +ipython_config.py -# celery beat schedule file +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff celerybeat-schedule +celerybeat.pid # SageMath parsed files *.sage.py @@ -95,10 +128,6 @@ ENV/ env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - # Rope project settings .ropeproject @@ -108,8 +137,22 @@ venv.bak/ # mypy .mypy_cache/ -### https://raw.github.com/github/gitignore/master/Global/OSX.gitignore +# Pyre type checker +.pyre/ + +# IDE +# PyCharm +.idea + +# Spyder project settings +.spyderproject +.spyproject +### VScode: https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore +.vscode/* +.vs/* + +### https://raw.github.com/github/gitignore/master/Global/OSX.gitignore .DS_Store .AppleDouble .LSOverride @@ -117,7 +160,6 @@ venv.bak/ # Icon must ends with two \r. Icon - # Thumbnails ._* @@ -126,7 +168,6 @@ Icon .Trashes ### Linux: https://raw.githubusercontent.com/github/gitignore/master/Global/Linux.gitignore - *~ # temporary files which can be created if a process still has a handle open of a deleted file @@ -141,7 +182,8 @@ Icon # .nfs files are created when an open file is removed but is still being accessed .nfs* -### MacOS: https://raw.githubusercontent.com/github/gitignore/master/Global/macOS.gitignore +# pytype static type analyzer +.pytype/ # General .DS_Store @@ -197,11 +239,7 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -### VScode: https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore -.vscode/* -.vs/* - -### Extra Python Items and DRMS Specific +### Extra Python Items and SunPy Specific .hypothesis .pytest_cache docs/_build @@ -215,9 +253,6 @@ examples/jsoc.stanford.edu/ jsoc.stanford.edu/ docs/sg_execution_times.rst -### Pycharm(?) -.idea - # Release script .github_cache @@ -225,3 +260,10 @@ docs/sg_execution_times.rst .history *.orig .tmp +node_modules/ +package-lock.json +package.json +.prettierrc + +# Log files generated by 'vagrant up' +*.log diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..62e8c62 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,16 @@ +[settings] +balanced_wrapping = true +skip = + docs/conf.py + drms/__init__.py +default_section = THIRDPARTY +include_trailing_comma = true +known_astropy = astropy, asdf +known_sunpy = sunpy +known_first_party = drms +length_sort = false +length_sort_sections = stdlib +line_length = 110 +multi_line_output = 3 +no_lines_before = LOCALFOLDER +sections = STDLIB, THIRDPARTY, ASTROPY, SUNPY, FIRSTPARTY, LOCALFOLDER diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70be7a3..d3ad645 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,49 +1,34 @@ repos: - - repo: https://github.com/PyCQA/docformatter - rev: v1.7.5 - hooks: - - id: docformatter - args: [--in-place, --pre-summary-newline, --make-summary-multi] - - repo: https://github.com/PyCQA/autoflake - rev: v2.3.1 - hooks: - - id: autoflake - args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable'] - exclude: ".*(.fits|.fts|.fit|.txt|tca.*|extern.*|.rst|.md|docs/conf.py)$" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.5.0' + rev: "v0.6.4" hooks: - id: ruff args: ['--fix', '--unsafe-fixes'] - - repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - exclude: ".*(.fits|.fts|.fit|.txt|.csv)$" + - id: ruff-format - repo: https://github.com/PyCQA/isort rev: 5.13.2 hooks: - id: isort exclude: ".*(.fits|.fts|.fit|.txt|.csv)$" - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: - id: check-ast - id: check-case-conflict - id: trailing-whitespace - exclude: ".*(.fits|.fts|.fit|.txt|.csv)$" - - id: mixed-line-ending - exclude: ".*(.fits|.fts|.fit|.txt|.csv)$" - - id: end-of-file-fixer - exclude: ".*(.fits|.fts|.fit|.txt|.csv)$" + exclude: ".*(.fits|.fts|.fit|.header|.txt)$" - id: check-yaml - id: debug-statements + - id: check-added-large-files + args: ["--enforce-all", "--maxkb=1054"] + - id: end-of-file-fixer + exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*|.json)$|^CITATION.rst$" + - id: mixed-line-ending + exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*)$" - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell - additional_dependencies: - - tomli ci: autofix_prs: false autoupdate_schedule: "quarterly" diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3d9312d --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,29 @@ +version: 2 + +build: + os: ubuntu-lts-latest + tools: + python: "mambaforge-latest" + jobs: + post_checkout: + - git fetch --unshallow || true + pre_install: + - git update-index --assume-unchanged .rtd-environment.yml docs/conf.py + +conda: + environment: .rtd-environment.yml + +sphinx: + builder: html + configuration: docs/conf.py + fail_on_warning: false + +formats: + - htmlzip + +python: + install: + - method: pip + extra_requirements: + - docs + path: . diff --git a/.rtd-environment.yml b/.rtd-environment.yml new file mode 100644 index 0000000..770b614 --- /dev/null +++ b/.rtd-environment.yml @@ -0,0 +1,7 @@ +name: drms +channels: + - conda-forge +dependencies: + - python=3.12 + - pip + - graphviz!=2.42.*,!=2.43.* diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..707968c --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,87 @@ +target-version = "py310" +line-length = 120 +exclude = [ + "drms/version.py", + ".git,", + "__pycache__", + "__pypackages__", + "_build", + ".bzr", + ".direnv", + ".eggs", + ".git-rewrite", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "buck-out", + "build", + "dist", + "node_modules", + "paper/create_joss_figure.py", + "tools/**", + "venv", +] + +[lint] +select = [ + "E", + "F", + "W", + "UP", + "PT", + "RET", + "TID", + "PLE", + "NPY", + "RUF", + "PGH", + "PTH", + "BLE", + "FBT", + "B", + "A", + "C4", + "T20", + "RSE", + "ERA", +] +extend-ignore = [ + # pycodestyle (E, W) + "E501", # LineTooLong # TODO! fix + # pytest (PT) + "PT001", # Always use pytest.fixture() + "PT004", # Fixtures which don't return anything should have leading _ + "PT007", # Parametrize should be lists of tuples # TODO! fix + "PT011", # Too broad exception assert # TODO! fix + "PT023", # Always use () on pytest decorators +] + +[lint.per-file-ignores] +# Part of configuration, not a package. +"setup.py" = ["INP001"] +"conftest.py" = ["INP001"] +"docs/conf.py" = [ + "E402" # Module imports not at top of file +] +"docs/*.py" = [ + "INP001", # Implicit-namespace-package. The examples are not a package. +] +"__init__.py" = ["E402", "F401", "F403"] +"test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] +"examples/*.py" = [ + "T201", + # We need print in our examples +] +"examples/plot_hmi_modes.py" = [ + "E741", # Ambiguous variable name +] + +[lint.pydocstyle] +convention = "numpy" diff --git a/MANIFEST.in b/MANIFEST.in index 153dcb8..ab1f5d3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,11 @@ -prune paper -# This subpackage is only used in development checkouts and should not be included in built tarballs +# Exclude specific files +# All files which are tracked by git and not explicitly excluded here are included by setuptools_scm +# Prune folders +prune build +prune docs/_build +prune docs/api +global-exclude *.pyc *.o + +# This subpackage is only used in development checkouts +# and should not be included in built tarballs prune drms/_dev diff --git a/README.rst b/README.rst index ead1987..4532d91 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,8 @@ drms ==== +Access HMI, AIA and MDI data from the Standford JSOC DRMS. + `Docs `__ | `Tutorial `__ | `Github `__ | @@ -26,12 +28,46 @@ For more information or to ask questions about ``drms``, check out: - `drms Documentation `__ - `SunPy Chat `__ +License +------- + +This project is Copyright (c) The SunPy Community and licensed under +the terms of the BSD 2-Clause license. This package is based upon +the `Openastronomy packaging guide `_ +which is licensed under the BSD 3-clause licence. See the licenses folder for +more information. + Contributing ------------ -If you would like to get involved, read our `Newcomers' guide `__. - -Help is always welcome so let us know what you like to work on, or check out the `issues page `__ for a list of known outstanding items. +We love contributions! drms is open source, +built on open source, and we'd love to have you hang out in our community. + +**Imposter syndrome disclaimer**: We want your help. No, really. + +There may be a little voice inside your head that is telling you that you're not +ready to be an open source contributor; that your skills aren't nearly good +enough to contribute. What could you possibly offer a project like this one? + +We assure you - the little voice in your head is wrong. If you can write code at +all, you can contribute code to open source. Contributing to open source +projects is a fantastic way to advance one's coding skills. Writing perfect code +isn't the measure of a good developer (that would disqualify all of us!); it's +trying to create something, making mistakes, and learning from those +mistakes. That's how we all improve, and we are happy to help others learn. + +Being an open source contributor doesn't just mean writing code, either. You can +help out by writing documentation, tests, or even giving feedback about the +project (and yes - that includes giving feedback about the contribution +process). Some of these contributions may be the most valuable to the project as +a whole, because you're coming to the project with fresh eyes, so you can see +the errors and assumptions that seasoned contributors have glossed over. + +Note: This disclaimer was originally written by +`Adrienne Lowe `_ for a +`PyCon talk `_, and was adapted by +drms based on its use in the README file for the +`MetPy project `_. Citation -------- diff --git a/docs/conf.py b/docs/conf.py index 8db95cb..bd8a4ed 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,8 @@ -""" -Configuration file for the Sphinx documentation builder. -""" +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config import os import datetime @@ -8,17 +10,23 @@ from sunpy_sphinx_theme import PNG_ICON -from drms import __version__ # -- Project information ----------------------------------------------------- -project = "drms" -author = "The SunPy Project" -copyright = f"{datetime.datetime.now().year}, {author}" # NOQA: A001 + # The full version, including alpha/beta/rc tags +from drms import __version__ + release = __version__ -is_development = ".dev" in __version__ + +project = "drms" +author = "The SunPy Community" +copyright = f"{datetime.datetime.now().year}, {author}" # noqa: A001 # -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named "sphinx.ext.*") or your custom +# ones. extensions = [ "hoverxref.extension", "sphinx_copybutton", @@ -36,11 +44,25 @@ "sphinx.ext.todo", "sphinx.ext.viewcode", ] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ["_templates"] # NOQA: ERA001 + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. automodapi_toctreedirnm = "generated/api" exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: source_suffix = ".rst" + +# The master toctree document. master_doc = "index" -default_role = "obj" + +# Treat everything in single ` as a Python reference. +default_role = "py:obj" # -- Options for hoverxref ----------------------------------------------------- if os.environ.get("READTHEDOCS"): diff --git a/docs/index.rst b/docs/index.rst index b07fd42..0101879 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,6 +11,7 @@ Python library for accessing HMI, AIA and MDI data from the Joint Science Operat .. toctree:: :maxdepth: 2 + :caption: Contents: intro tutorial diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..2119f51 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 0cc7015..77de7b4 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -224,8 +224,6 @@ The following, for example, only downloads the first file of the request: .. code-block:: python >>> export_request.download(out_dir, index=0) # doctest: +REMOTE_DATA - record url download - 0 hmi.V_45s[2016.04.01_00:00:00_TAI][2]{Dopplerg... http://jsoc.stanford.edu/SUM58/D803708321/S000... ... Being a direct ``as-is`` export, there are no keyword data written to any FITS headers. If you need keyword data added to the headers, you have to use the ``fits`` export protocol instead, which is described below. diff --git a/drms/__init__.py b/drms/__init__.py index 17a5273..594c830 100644 --- a/drms/__init__.py +++ b/drms/__init__.py @@ -20,12 +20,12 @@ datefmt="%Y-%m-%d %H:%M:%S", ) -from .client import Client, ExportRequest, SeriesInfo # NOQA: E402 -from .config import ServerConfig, register_server # NOQA: E402 -from .exceptions import DrmsError, DrmsExportError, DrmsOperationNotSupported, DrmsQueryError # NOQA: E402 -from .json import HttpJsonClient, HttpJsonRequest, JsocInfoConstants # NOQA: E402 -from .utils import to_datetime # NOQA: E402 -from .version import version as __version__ # NOQA: E402 +from .client import Client, ExportRequest, SeriesInfo +from .config import ServerConfig, register_server +from .exceptions import DrmsError, DrmsExportError, DrmsOperationNotSupported, DrmsQueryError +from .json import HttpJsonClient, HttpJsonRequest, JsocInfoConstants +from .utils import to_datetime +from .version import version as __version__ def _get_bibtex(): diff --git a/drms/client.py b/drms/client.py index f85172c..491fe77 100644 --- a/drms/client.py +++ b/drms/client.py @@ -11,7 +11,6 @@ import pandas as pd from drms import logger - from .exceptions import DrmsExportError, DrmsOperationNotSupported, DrmsQueryError from .json import HttpJsonClient from .utils import _extract_series_name, _pd_to_numeric_coerce, _split_arg diff --git a/drms/data/README.rst b/drms/data/README.rst new file mode 100644 index 0000000..382f6e7 --- /dev/null +++ b/drms/data/README.rst @@ -0,0 +1,6 @@ +Data directory +============== + +This directory contains data files included with the package source +code distribution. Note that this is intended only for relatively small files +- large files should be externally hosted and downloaded as needed. diff --git a/drms/json.py b/drms/json.py index 9556ad6..852ef32 100644 --- a/drms/json.py +++ b/drms/json.py @@ -4,7 +4,6 @@ from urllib.request import HTTPError, urlopen from drms import logger - from .config import ServerConfig, _server_configs from .utils import _split_arg diff --git a/drms/tests/__init__.py b/drms/tests/__init__.py index e69de29..92ea707 100644 --- a/drms/tests/__init__.py +++ b/drms/tests/__init__.py @@ -0,0 +1,3 @@ +""" +This module contains package tests. +""" diff --git a/drms/tests/test_jsoc_query.py b/drms/tests/test_jsoc_query.py index 032aef4..659cb25 100644 --- a/drms/tests/test_jsoc_query.py +++ b/drms/tests/test_jsoc_query.py @@ -97,7 +97,10 @@ def test_query_invalid_series(jsoc_client): @pytest.mark.remote_data() @pytest.mark.parametrize( "query", - ["hmi.v_45s[2014.01.01_00:00:35_TAI-2014.01.01_01:00:35_TAI]", "hmi.M_720s[2011.04.14_00:30:00_TAI/6h@2h]"], + [ + "hmi.v_45s[2014.01.01_00:00:35_TAI-2014.01.01_01:00:35_TAI]", + "hmi.M_720s[2011.04.14_00:30:00_TAI/6h@2h]", + ], ) def test_query_hexadecimal_strings(query): # Exercise the part of client.py that deals with hexadecimal strings diff --git a/drms/version.py b/drms/version.py index bd90735..9eef714 100644 --- a/drms/version.py +++ b/drms/version.py @@ -1,5 +1,5 @@ # NOTE: First try _dev.scm_version if it exists and setuptools_scm is installed -# This file is not included in muse wheels/tarballs, so otherwise it will +# This file is not included in wheels/tarballs, so otherwise it will # fall back on the generated _version module. try: try: diff --git a/examples/plot_synoptic_mr.py b/examples/plot_synoptic_mr.py index f79cb7b..a5da520 100644 --- a/examples/plot_synoptic_mr.py +++ b/examples/plot_synoptic_mr.py @@ -7,6 +7,7 @@ """ import matplotlib.pyplot as plt + from astropy.io import fits import drms diff --git a/pyproject.toml b/pyproject.toml index c5527d5..3fa4fcc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,116 +1,62 @@ [build-system] requires = [ - "setuptools", - "setuptools_scm[toml]", + "setuptools>=62.1", + "setuptools_scm[toml]>=6.2", "wheel", - "oldest-supported-numpy", ] -build-backend = 'setuptools.build_meta' - -[tool.black] -line-length = 120 -target-version = ['py310'] -exclude = ''' -( - /( - \.eggs - | \.git - | \.mypy_cache - | \.tox - | \.venv - | _build - | build - | dist - | docs - | .history - )/ -) -''' - -[tool.isort] -profile = "black" -line_length = 120 -length_sort = "False" -length_sort_sections = "stdlib" - -[tool.ruff] -target-version = "py310" -line-length = 120 -exclude=[ - "__pycache__", - "__pypackages__", - "_build", - ".bzr", - ".direnv", - ".eggs", - ".git-rewrite", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "buck-out", - "build", - "dist", - "node_modules", - "paper/create_joss_figure.py", - "tools/**", - "venv", +build-backend = "setuptools.build_meta" + +[project] +name = "drms" +description = "Access HMI, AIA and MDI data from the Standford JSOC DRMS" +requires-python = ">=3.10" +readme = { file = "README.rst", content-type = "text/x-rst" } +license = { file = "licenses/LICENSE.rst" } +authors = [ + { name = "The SunPy Community", email = "sunpy@googlegroups.com" }, ] - -[tool.ruff.lint] -# Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -select = [ - "E", - "F", - "W", - "UP", - "PT", - "RET", - "TID", - "PLE", - "NPY", - "RUF", - "PGH", - "PTH", - "BLE", - "FBT", - "B", - "A", - "COM", - "C4", - "T20", - "RSE", - "ERA", -] -extend-ignore = [ - "E501", # Line too long +dependencies = [ + "numpy>=1.23.5", + "pandas>=1.5.1", + "packaging>=23.0" ] - -[tool.ruff.lint.per-file-ignores] -"examples/*.py" = [ - "T201", - # We need print in our examples +dynamic = [ "version" ] + +[project.optional-dependencies] +tests = [ + "pytest", + "pytest-astropy", + "pytest-doctestplus", + "pytest-cov", + "pytest-xdist", + "astropy", ] -"examples/plot_hmi_modes.py" = [ - "E741", # Ambiguous variable name +docs = [ + "astropy", + "matplotlib", + "sphinx", + "sphinx-automodapi", + "sphinx-changelog", + "sphinx-copybutton", + "sphinx-gallery", + "sphinx-hoverxref", + "sphinxext-opengraph", + "sunpy-sphinx-theme", + "sphinx-automodapi", ] +[project.urls] +repository = "https://sunpy.org" -[tool.ruff.lint.pydocstyle] -convention = "numpy" +[tool.setuptools] +zip-safe = false +include-package-data = true -[tool.ruff.lint.flake8-quotes] -docstring-quotes = "double" +[tool.setuptools.packages.find] +include = ["drms*"] +exclude = ["drms._dev*"] -[tool.codespell] -skip = "*.asdf,*.fits,*.fts,*.header,*.json,*.xsh,*cache*,*egg*,*extern*,.git,.idea,.tox,_build,*truncated,*.svg,.asv_env,.history" -ignore-words-list = "sav,requestor" +[tool.setuptools_scm] +write_to = "drms/_version.py" [ tool.gilesbot ] [ tool.gilesbot.pull_requests ] diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..e2bf1c7 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,49 @@ +[pytest] +minversion = 7.0 +testpaths = + drms + docs +norecursedirs = + .tox + build + docs/_build + docs/generated + *.egg-info + examples + drms/_dev + .history + drms/extern +doctest_plus = enabled +doctest_optionflags = + NORMALIZE_WHITESPACE + FLOAT_CMP + ELLIPSIS +text_file_format = rst +addopts = + --doctest-rst + -p no:unraisableexception + -p no:threadexception +markers = + remote_data: marks this test function as needing remote data. + jsoc: marks the test function as needing a connection to JSOC. + kis: marks the test function as needing a connection to KIS. + flaky: from sunpy +remote_data_strict = True +log_cli=true +log_level=INFO +filterwarnings = + # Turn all warnings into errors so they do not pass silently. + error + # Do not fail on pytest config issues (i.e. missing plugins) but do show them + always::pytest.PytestConfigWarning + # A list of warnings to ignore follows. If you add to this list, you MUST + # add a comment or ideally a link to an issue that explains why the warning + # is being ignored + # This is due to dependencies building with a numpy version different from + # the local installed numpy version, but should be fine + # See https://github.com/numpy/numpy/issues/15748#issuecomment-598584838 + ignore:numpy.ufunc size changed:RuntimeWarning + ignore:numpy.ndarray size changed:RuntimeWarning + # https://github.com/pytest-dev/pytest-cov/issues/557 + ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning + ignore:.*is deprecated and slated for removal in Python 3:DeprecationWarning diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 4b98710..0000000 --- a/setup.cfg +++ /dev/null @@ -1,92 +0,0 @@ -[metadata] -name = drms -provides = drms -description = Access HMI, AIA and MDI data from the Standford JSOC DRMS -long_description = file: README.rst -long_description_content_type = text/x-rst -author = The SunPy Community -author_email = sunpy@googlegroups.com -license = BSD 2-Clause -license_files = LICENSE.rst -url = https://sunpy.org -edit_on_github = True -github_project = sunpy/drms -platform = any -keywords = solar physics, solar, science, data -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Topic :: Scientific/Engineering :: Astronomy - Topic :: Scientific/Engineering :: Physics - -[options] -zip_safe = False -python_requires = >=3.10 -packages = find: -include_package_data = True -setup_requires = - setuptools_scm -install_requires= - numpy - pandas - packaging - -[options.extras_require] -tests = - pytest-astropy - tox -docs = - astropy - matplotlib - sphinx - sphinx-automodapi - sphinx-changelog - sphinx-copybutton - sphinx-gallery - sphinx-hoverxref - sphinxext-opengraph - sunpy-sphinx-theme - # Remove next line when fixed in towncrier; see https://github.com/twisted/towncrier/issues/528 - importlib-resources<6 - -[options.packages.find] -exclude = drms._dev - -[options.entry_points] -console_scripts = - drms = drms.main:main - -[tool:pytest] -testpaths = "drms" "docs" -norecursedirs = ".tox" "build" "docs[\/]_build" "docs[\/]generated" "*.egg-info" "examples" ".history" "paper" "drms[\/]_dev" -log_cli=true -log_level=INFO -doctest_plus = enabled -doctest_optionflags = NORMALIZE_WHITESPACE FLOAT_CMP ELLIPSIS -addopts = --doctest-rst -p no:unraisableexception -p no:threadexception -markers = - remote_data: marks this test function as needing remote data. - jsoc: marks the test function as needing a connection to JSOC. - kis: marks the test function as needing a connection to KIS. - flaky: from sunpy -remote_data_strict = True -filterwarnings = - error - # Do not fail on pytest config issues (i.e. missing plugins) but do show them - always::pytest.PytestConfigWarning - # This is due to dependencies building with a numpy version different from - # the local installed numpy version, but should be fine - # See https://github.com/numpy/numpy/issues/15748#issuecomment-598584838 - ignore:numpy.ufunc size changed:RuntimeWarning - ignore:numpy.ndarray size changed:RuntimeWarning - # https://github.com/pytest-dev/pytest-cov/issues/557 - ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning - ignore:.*is deprecated and slated for removal in Python 3:DeprecationWarning diff --git a/setup.py b/setup.py index 9bfacfa..c823345 100644 --- a/setup.py +++ b/setup.py @@ -1,29 +1,4 @@ #!/usr/bin/env python -from pathlib import Path -from itertools import chain - from setuptools import setup -try: - # Recommended for setuptools 61.0.0+ - # (though may disappear in the future) - from setuptools.config.setupcfg import read_configuration -except ImportError: - from setuptools.config import read_configuration - -################################################################################ -# Programmatically generate some extras combos. -################################################################################ - -extras = read_configuration("setup.cfg")["options"]["extras_require"] -# Dev is everything -extras["dev"] = list(chain(*list(extras.values()))) -# All is everything but tests and docs -exclude_keys = ("tests", "docs", "dev") -ex_extras = dict([i for i in list(extras.items()) if i[0] not in exclude_keys]) -# Concatenate all the values together for 'all' -extras["all"] = list(chain.from_iterable(list(ex_extras.values()))) -setup( - extras_require=extras, - use_scm_version={"write_to": Path("drms") / "_version.py"}, -) +setup() diff --git a/tox.ini b/tox.ini index 6055f3e..f08d4b0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,37 +1,53 @@ [tox] min_version = 4.0 +requires = + tox-pypi-filter>=0.14 envlist = - py{310,311,312}{,-online,-sunpy,-oldestdeps} - build_docs + py{310,311,312}{,-online,-sunpy} + py312-devdeps + py310-oldestdeps codestyle -requires = - tox-pypi-filter + build_docs [testenv] pypi_filter = https://raw.githubusercontent.com/sunpy/sunpy/main/.test_package_pins.txt -# Run the tests in a temporary directory to make sure that we don't import drms from the source tree -changedir = .tmp/{envname} +# Run the tests in a temporary directory to make sure that we don't import +# the package from the source tree +change_dir = .tmp/{envname} description = run tests + oldestdeps: with the oldest supported version of key dependencies + devdeps: with the latest developer version of key dependencies online: that require remote data sunpy: run the sunpy jsoc tests to ensure we dont break them +pass_env = + # A variable to tell tests we are on a CI system + CI + # Custom compiler locations (such as ccache) + CC + # Location of locales (needed by sphinx on some systems) + LOCALE_ARCHIVE + # If the user has set a LC override we should follow it + LC_ALL + # HTTP Proxy + HTTP_PROXY + HTTPS_PROXY + NO_PROXY setenv = MPLBACKEND = agg COLUMNS = 180 PYTEST_COMMAND = pytest -vvv -s -ra --pyargs drms --cov-report=xml --cov=drms --cov-config={toxinidir}/setup.cfg {toxinidir}/docs build_docs,online: HOME = {envtmpdir} JSOC_EMAIL = jsoc@sunpy.org -passenv = - HTTP_PROXY - HTTPS_PROXY - NO_PROXY - CIRCLECI deps = + # For packages which publish nightly wheels this will pull the latest nightly + devdeps: numpy>=0.0.dev0 + # Packages without nightly wheels will be built from source like this + # devdeps: git+https://github.com/ndcube/ndcube + oldestdeps: minimum_dependencies + # The following indicates which extras_require will be installed pytest-xdist pytest-timeout - # Oldest deps we pin against - oldestdeps: numpy<2.0 - oldestdeps: pandas<2.0 # These are specific extras we use to run the sunpy tests. sunpy: git+https://github.com/sunpy/sunpy sunpy: beautifulsoup4 @@ -43,21 +59,27 @@ deps = sunpy: tqdm sunpy: zeep extras = - dev -commands = + tests +commands_pre = + oldestdeps: minimum_dependencies drms --filename requirements-min.txt + oldestdeps: pip install -r requirements-min.txt pip freeze --all --no-input - sunpy: pytest -vvv -s -ra --pyargs sunpy.net.jsoc --timeout=120 --remote-data=any {posargs} - !online: {env:PYTEST_COMMAND} {posargs} - online: {env:PYTEST_COMMAND} --timeout=120 --remote-data=any {posargs} - -[testenv:build_docs] -changedir = docs -description = Invoke sphinx-build to build the HTML docs -extras = dev commands = - pip freeze --all --no-input - sphinx-build -j 1 --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs} - python -c 'import pathlib; print("Documentation available under file://\{0\}".format(pathlib.Path(r"{toxinidir}") / "docs" / "_build" / "html"/ "index.html"))' + # To amend the pytest command for different factors you can add a line + # which starts with a factor like `online: --remote-data=any \` + # If you have no factors which require different commands this is all you need: + pytest \ + -vvv \ + -r fEs \ + !sunpy: --pyargs drms \ + sunpy: --pyargs sunpy.net.jsoc \ + --cov-report=xml \ + --cov=drms \ + --cov-config={toxinidir}/.coveragerc \ + online: --timeout=120 \ + online,sunpy: --remote-data=any \ + !sunpy: {toxinidir}/docs \ + {posargs} [testenv:codestyle] pypi_filter = @@ -67,4 +89,15 @@ deps = pre-commit commands = pre-commit install-hooks - pre-commit run --verbose --all-files --show-diff-on-failure + pre-commit run --color always --all-files --show-diff-on-failure + +[testenv:build_docs] +description = invoke sphinx-build to build the HTML docs +change_dir = + docs +extras = + docs +commands = + pip freeze --all --no-input + sphinx-build -j 1 --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs} + python -c 'import pathlib; print("Documentation available under file://\{0\}".format(pathlib.Path(r"{toxinidir}") / "docs" / "_build" / "html"/ "index.html"))'