Skip to content

Commit

Permalink
Updates from the package template (#260) (#262)
Browse files Browse the repository at this point in the history
Co-authored-by: DanRyanIrish <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nabil Freij <[email protected]>
  • Loading branch information
4 people authored Nov 11, 2024
1 parent d89c36a commit a28532a
Show file tree
Hide file tree
Showing 18 changed files with 246 additions and 183 deletions.
5 changes: 3 additions & 2 deletions .cruft.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"template": "https://github.com/sunpy/package-template",
"commit": "aec53b81aed2e7e534045e59303d82712fe82fb1",
"commit": "4ca8e60aac805d5f736de80c45ae0aba96b4cb85",
"checkout": null,
"context": {
"cookiecutter": {
Expand All @@ -16,7 +16,8 @@
"enable_dynamic_dev_versions": "y",
"include_example_code": "n",
"include_cruft_update_github_workflow": "y",
"_sphinx_theme": "alabaster",
"use_extended_ruff_linting": "y",
"_sphinx_theme": "sunpy",
"_parent_project": "",
"_install_requires": "",
"_copy_without_render": [
Expand Down
48 changes: 31 additions & 17 deletions .github/workflows/sub_package_update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ jobs:
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

Expand All @@ -55,25 +47,47 @@ jobs:
echo "has_changes=$CHANGES" >> "$GITHUB_OUTPUT"
- name: Run update if available
id: cruft_update
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
cruft_output=$(cruft update --skip-apply-ask --refresh-private-variables)
echo $cruft_output
git restore --staged .
- name: Create pull request
if [[ "$cruft_output" == *"Failed to cleanly apply the update, there may be merge conflicts."* ]]; then
echo merge_conflicts=1 >> $GITHUB_OUTPUT
else
echo merge_conflicts=0 >> $GITHUB_OUTPUT
fi
- name: Check if only .cruft.json is modified
id: cruft_json
if: steps.check.outputs.has_changes == '1'
run: |
git status --porcelain=1
if [[ "$(git status --porcelain=1)" == " M .cruft.json" ]]; then
echo "Only .cruft.json is modified. Exiting workflow early."
echo "has_changes=0" >> "$GITHUB_OUTPUT"
else
echo "has_changes=1" >> "$GITHUB_OUTPUT"
fi
- name: Create pull request
if: steps.cruft_json.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 }}
add-paths: "."
commit-message: "Automatic package template update"
branch: "cruft/update"
delete-branch: true
branch-suffix: timestamp
title: ${{ matrix.title }}
draft: ${{ steps.cruft_update.outputs.merge_conflicts == '1' }}
title: "Updates from the package template"
body: |
This is an autogenerated PR, which will ${{ matrix.body }}.
[Cruft](https://cruft.github.io/cruft/) has detected updates from the Package Template
This is an autogenerated PR, which will applies the latest changes from the [SunPy Package Template](https://github.com/sunpy/package-template).
If this pull request has been opened as a draft there are conflicts which need fixing.
**To run the CI on this pull request you will need to close it and reopen it.**
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
repos:
# This should be before any formatting hooks like isort
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.7.1"
hooks:
- id: ruff
args: ["--fix"]
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.1
hooks:
Expand Down
69 changes: 57 additions & 12 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
target-version = "py310"
line-length = 110
line-length = 120
exclude = [
".git,",
"__pycache__",
Expand All @@ -8,30 +8,75 @@ exclude = [
]

[lint]
select = ["E", "F", "W", "UP", "PT"]
select = [
"E",
"F",
"W",
"UP",
"PT",
"BLE",
"A",
"C4",
"INP",
"PIE",
"T20",
"RET",
"TID",
"PTH",
"PD",
"PLC",
"PLE",
"FLY",
"NPY",
"PERF",
"RUF",
]
extend-ignore = [
# pycodestyle (E, W)
"E501", # LineTooLong # TODO! fix
"E501", # ignore line length will use a formatter instead
# pyupgrade (UP)
"UP038", # Use | in isinstance - not compatible with models and is slower
# 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
# flake8-pie (PIE)
"PIE808", # Disallow passing 0 as the first argument to range
# flake8-use-pathlib (PTH)
"PTH123", # open() should be replaced by Path.open()
# Ruff (RUF)
"RUF003", # Ignore ambiguous quote marks, doesn't allow ' in comments
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
"RUF013", # PEP 484 prohibits implicit `Optional`
"RUF015", # Prefer `next(iter(...))` over single element slice
]

[lint.per-file-ignores]
# Part of configuration, not a package.
"setup.py" = ["INP001"]
"conftest.py" = ["INP001"]
"setup.py" = [
"INP001", # File is part of an implicit namespace package.
]
"conftest.py" = [
"INP001", # File is part of an implicit namespace package.
]
"docs/conf.py" = [
"E402" # Module imports not at top of file
"E402" # Module imports not at top of file
]
"docs/*.py" = [
"INP001", # Implicit-namespace-package. The examples are not a package.
"INP001", # File is part of an implicit namespace package.
]
"examples/**.py" = [
"T201", # allow use of print in examples
"INP001", # File is part of an implicit namespace package.
]
"__init__.py" = [
"E402", # Module level import not at top of cell
"F401", # Unused import
"F403", # from {name} import * used; unable to detect undefined names
"F405", # {name} may be undefined, or defined from star imports
]
"test_*.py" = [
"E402", # Module level import not at top of cell
]
"__init__.py" = ["E402", "F401", "F403"]
"test_*.py" = ["B011", "D", "E402", "PGH001", "S101"]

[lint.pydocstyle]
convention = "numpy"
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
automodapi_toctreedirnm = "generated/api"

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

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand Down Expand Up @@ -103,7 +103,7 @@
# 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"] # NOQA: ERA001
# html_static_path = ["_static"]

# By default, when rendering docstrings for classes, sphinx.ext.autodoc will
# make docs with the class-level docstring and the class-method docstrings,
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ tests = [
]
docs = [
"sphinx",
"sphinx-automodapi",
"sunpy-sphinx-theme",
"packaging",
"sphinx_automodapi",
"sphinx-changelog",
"sphinx-gallery",
Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ filterwarnings =
ignore: invalid value encountered in true_divide
# https://github.com/pytest-dev/pytest-cov/issues/557
ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning
ignore:Please use astropy.wcs.wcsapi.high_level_api.values_to_high_level_objects:DeprecationWarning
4 changes: 2 additions & 2 deletions sunraster/_dev/scm_version.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Try to use setuptools_scm to get the current version; this is only used
# in development installations from the git repository.
import os.path
from pathlib import Path

try:
from setuptools_scm import get_version

version = get_version(root=os.path.join("..", ".."), relative_to=__file__)
version = get_version(root=Path("../.."), relative_to=__file__)
except ImportError:
raise
except Exception as e:
Expand Down
97 changes: 48 additions & 49 deletions sunraster/extern/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(self, header=None, comments=None, axes=None, data_shape=None):
axes = dict(axes)
if not set(axes.keys()).issubset(set(header_keys)):
raise ValueError("All axes must correspond to a value in header under the same key.")
self._axes = dict([(key, self._sanitize_axis_value(axis, header[key], key)) for key, axis in axes.items()])
self._axes = {key: self._sanitize_axis_value(axis, header[key], key) for key, axis in axes.items()}

def _sanitize_axis_value(self, axis, value, key):
if axis is None:
Expand All @@ -100,7 +100,7 @@ def _sanitize_axis_value(self, axis, value, key):
shape_error_msg = f"{key} must have shape {tuple(self.shape[axis])} as it is associated with axes {axis}"
if len(axis) == 1 and not hasattr(value, "__len__") or len(axis) != 1 and not hasattr(value, "shape"):
raise TypeError(shape_error_msg)
elif len(axis) == 1:
if len(axis) == 1:
meta_shape = (len(value),)
else:
meta_shape = value.shape
Expand Down Expand Up @@ -189,51 +189,50 @@ def __getitem__(self, item):
# If item is single string, slicing is simple.
if isinstance(item, str):
return super().__getitem__(item)
elif self.shape is None:
if self.shape is None:
raise TypeError("Meta object does not have a shape and so cannot be sliced.")
else:
new_meta = copy.deepcopy(self)
# Convert item to array of ints and slices for consistent behaviour.
if isinstance(item, (numbers.Integral, slice)):
item = [item]
item = np.array(list(item) + [slice(None)] * (len(self.shape) - len(item)), dtype=object)

# Edit data shape and calculate which axis will be dropped.
dropped_axes = np.zeros(len(self.shape), dtype=bool)
new_shape = new_meta.shape
for i, axis_item in enumerate(item):
if isinstance(axis_item, numbers.Integral):
dropped_axes[i] = True
elif isinstance(axis_item, slice):
start = axis_item.start
if start is None:
start = 0
if start < 0:
start = self.shape[i] - start
stop = axis_item.stop
if stop is None:
stop = self.shape[i]
if stop < 0:
stop = self.shape[i] - stop
new_shape[i] = stop - start
else:
raise TypeError("Unrecognized slice type. " "Must be an int, slice and tuple of the same.")
new_meta._data_shape = new_shape[np.invert(dropped_axes)]

# Calculate the cumulative number of dropped axes.
cumul_dropped_axes = np.cumsum(dropped_axes)

# Slice all metadata associated with axes.
for key, value in self.items():
axis = self.axes.get(key, None)
if axis is not None:
new_item = tuple(item[axis])
new_value = value[new_item[0]] if len(new_item) == 1 else value[new_item]
new_axis = np.array([-1 if isinstance(i, numbers.Integral) else a for i, a in zip(new_item, axis)])
new_axis -= cumul_dropped_axes[axis]
new_axis = new_axis[new_axis >= 0]
if len(new_axis) == 0:
new_axis = None
new_meta.add(key, new_value, self.comments.get(key, None), new_axis, overwrite=True)

return new_meta
new_meta = copy.deepcopy(self)
# Convert item to array of ints and slices for consistent behaviour.
if isinstance(item, (numbers.Integral, slice)):
item = [item]
item = np.array(list(item) + [slice(None)] * (len(self.shape) - len(item)), dtype=object)

# Edit data shape and calculate which axis will be dropped.
dropped_axes = np.zeros(len(self.shape), dtype=bool)
new_shape = new_meta.shape
for i, axis_item in enumerate(item):
if isinstance(axis_item, numbers.Integral):
dropped_axes[i] = True
elif isinstance(axis_item, slice):
start = axis_item.start
if start is None:
start = 0
if start < 0:
start = self.shape[i] - start
stop = axis_item.stop
if stop is None:
stop = self.shape[i]
if stop < 0:
stop = self.shape[i] - stop
new_shape[i] = stop - start
else:
raise TypeError("Unrecognized slice type. " "Must be an int, slice and tuple of the same.")
new_meta._data_shape = new_shape[np.invert(dropped_axes)]

# Calculate the cumulative number of dropped axes.
cumul_dropped_axes = np.cumsum(dropped_axes)

# Slice all metadata associated with axes.
for key, value in self.items():
axis = self.axes.get(key, None)
if axis is not None:
new_item = tuple(item[axis])
new_value = value[new_item[0]] if len(new_item) == 1 else value[new_item]
new_axis = np.array([-1 if isinstance(i, numbers.Integral) else a for i, a in zip(new_item, axis)])
new_axis -= cumul_dropped_axes[axis]
new_axis = new_axis[new_axis >= 0]
if len(new_axis) == 0:
new_axis = None
new_meta.add(key, new_value, self.comments.get(key, None), new_axis, overwrite=True)

return new_meta
Loading

0 comments on commit a28532a

Please sign in to comment.