Skip to content

Commit

Permalink
Merge pull request #254 from mhvk/remove-testrunner
Browse files Browse the repository at this point in the history
Remove testrunner
  • Loading branch information
mhvk authored Feb 20, 2025
2 parents c6274ff + 7f53c61 commit e8086b8
Show file tree
Hide file tree
Showing 20 changed files with 98 additions and 147 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci_workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ jobs:
# apt_packages: graphviz

- name: Test oldest supported versions and coverage
python: "3.7"
toxenv: py37-test-oldestdeps-alldeps-cov
python: "3.10"
toxenv: py310-test-oldestdeps-alldeps-cov
pip_packages: codecov

- name: Developer version of baseband
python: "3.10"
toxenv: py310-test-basebanddev
python: "3.12"
toxenv: py312-test-basebanddev

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 4 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
version: 2
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true

build:
os: "ubuntu-22.04"
Expand Down
9 changes: 7 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
0.4 (unreleased)
================

- The minimum versions required by baseband-tasks are now python 3.7,
baseband 4.1, and its minimum dependencies (astropy 4.0 and numpy 1.17).
- Numpy >= 2.0 is now supported.
- The minimum versions required by baseband-tasks are now python 3.10 and
baseband 4.2.
- Remove support for the deprecated ``astropy`` test runner. This means it is
no longer possible to test the installed baseband package with
``baseband_tasks.test()`` inside python; instead, one should use
``pytest --pyargs baseband_tasks`` from the command line.

New Features
------------
Expand Down
13 changes: 6 additions & 7 deletions baseband_tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# Licensed under the GPLv3 - see LICENSE
"""Radio baseband data reduction."""

# Packages may add whatever they like to this file, but
# should keep this content at the top.
# ----------------------------------------------------------------------------
from ._astropy_init import * # noqa
# ----------------------------------------------------------------------------
try:
from .version import version as __version__
except ImportError:
__version__ = ''

# Define minima for the documentation, but do not bother to explicitly check.
__minimum_python_version__ = '3.7'
__minimum_baseband_version__ = '4.1'
__minimum_astropy_version__ = '4.0'
__minimum_baseband_version__ = '4.2'
__minimum_astropy_version__ = '5.1'
13 changes: 0 additions & 13 deletions baseband_tasks/_astropy_init.py

This file was deleted.

8 changes: 6 additions & 2 deletions baseband_tasks/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
from astropy import units as u
from astropy.utils.metadata import MetaData

try:
from astropy.utils.compat import COPY_IF_NEEDED
except ImportError:
COPY_IF_NEEDED = False # older astropy requires numpy < 2 anyway.

__all__ = ['Base', 'BaseTaskBase', 'TaskBase', 'PaddedTaskBase',
'SetAttribute', 'Task']
Expand Down Expand Up @@ -467,11 +471,11 @@ def __getitem__(self, item):

return GetSlice(self, item)

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
old_offset = self.tell()
try:
self.seek(0)
return np.array(self.read(), dtype=dtype, copy=False)
return np.array(self.read(), dtype=dtype, copy=copy)
finally:
self.seek(old_offset)

Expand Down
39 changes: 0 additions & 39 deletions baseband_tasks/conftest.py

This file was deleted.

38 changes: 15 additions & 23 deletions baseband_tasks/convolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,17 @@
__all__ = ['ConvolveSamples', 'Convolve']


class ConvolveBase(PaddedTaskBase):
"""Base class for convolutions.
def adjust_response_dims(response, ih):
if response.ndim == 1 and ih.ndim > 1:
response = response.reshape(response.shape[:1]
+ (1,) * (ih.ndim - 1))
else:
check_broadcast_to(response, response.shape[:1] + ih.sample_shape)

Parameters as for `~baseband_tasks.convolution.ConvolveSamples` and
`~baseband_tasks.convolution.Convolve` except allowing to pass through
arguments to `~baseband_tasks.base.PaddedTaskBase`.
"""
def __init__(self, ih, response, *, offset=0, samples_per_frame=None,
**kwargs):
if response.ndim == 1 and ih.ndim > 1:
response = response.reshape(response.shape[:1]
+ (1,) * (ih.ndim - 1))
else:
check_broadcast_to(response, response.shape[:1] + ih.sample_shape)

pad = response.shape[0] - 1
super().__init__(ih, pad_start=pad-offset, pad_end=offset,
samples_per_frame=samples_per_frame, **kwargs)
self._response = response
return response


class ConvolveSamples(ConvolveBase):
class ConvolveSamples(PaddedTaskBase):
"""Convolve a time stream with a response, in the time domain.
Parameters
Expand All @@ -56,8 +45,9 @@ class ConvolveSamples(ConvolveBase):
Convolve : convolution in the Fourier domain (usually faster)
"""
def __init__(self, ih, response, *, offset=0, samples_per_frame=None):
# Just get rid of **kwargs in ConvolveBase.
super().__init__(ih, response, offset=offset,
self._response = adjust_response_dims(response, ih)
pad = self._response.shape[0] - 1
super().__init__(ih, pad_start=pad-offset, pad_end=offset,
samples_per_frame=samples_per_frame)

def task(self, data):
Expand All @@ -72,7 +62,7 @@ def task(self, data):
return result


class Convolve(ConvolveBase):
class Convolve(PaddedTaskBase):
"""Convolve a time stream with a response, in the Fourier domain.
The convolution is done via multiplication in the Fourier domain, which
Expand Down Expand Up @@ -102,8 +92,10 @@ class Convolve(ConvolveBase):
"""

def __init__(self, ih, response, *, offset=0, samples_per_frame=None):
self._response = adjust_response_dims(response, ih)
pad = self._response.shape[0] - 1
FFT = fft_maker.get()
super().__init__(ih, response=response, offset=offset,
super().__init__(ih, pad_start=pad-offset, pad_end=offset,
samples_per_frame=samples_per_frame,
next_fast_len=FFT.next_fast_len)
# Initialize FFTs for fine channelization and the inverse.
Expand Down
9 changes: 8 additions & 1 deletion baseband_tasks/fourier/pyfftw.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,11 @@ def __repr__(self):
self._repr_kwargs.update(self._fftw_kwargs)
return super().__repr__()

next_fast_len = staticmethod(pyfftw.next_fast_len)
@staticmethod
def next_fast_len(n):
"""Smallest composite of primes between 2 and 13 which is >= n.
Defers to `pyfftw.next_fast_len`. The primes 11 and 13 can
occur at most once.
"""
return pyfftw.next_fast_len(n)
11 changes: 8 additions & 3 deletions baseband_tasks/functions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Licensed under the GPLv3 - see LICENSE
import numpy as np

try:
from numpy._core import defchararray
except ImportError:
from numpy.core import defchararray

from .base import TaskBase, simplify_shape


Expand Down Expand Up @@ -43,7 +48,7 @@ def _default_polarization(self, ih):
if not hasattr(ih, 'polarization'):
return None

return np.core.defchararray.add(ih.polarization, ih.polarization)
return defchararray.add(ih.polarization, ih.polarization)

def _repr_item(self, key, default, value=None):
if key == 'polarization':
Expand Down Expand Up @@ -115,8 +120,8 @@ def _default_polarization(self, ih):

# Given check_shape, ih.polarization is guaranteed to have 2 distinct
# items, which are guaranteed to be along first axis.
return np.core.defchararray.add(ih.polarization[[0, 1, 0, 1]],
ih.polarization[[0, 1, 1, 0]])
return defchararray.add(ih.polarization[[0, 1, 0, 1]],
ih.polarization[[0, 1, 1, 0]])

def _repr_item(self, key, default, value=None):
if (key == 'polarization' and hasattr(self.ih, 'polarization')
Expand Down
14 changes: 7 additions & 7 deletions baseband_tasks/io/psrfits/hdu.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from astropy.utils import lazyproperty
import numpy as np
import operator

from .psrfits_htm_parser import HDU_TEMPLATES


Expand Down Expand Up @@ -148,7 +149,7 @@ def frequency(self):

freq = c_freq + (np.arange(1, n_chan + 1)
- ((n_chan + 1) // 2)) * chan_bw
return u.Quantity(freq, u.MHz, copy=False)
return freq << u.MHz

@frequency.setter
def frequency(self, freq):
Expand All @@ -159,7 +160,7 @@ def frequency(self, freq):
# we assume the frequency resolution is the same across the band.
freq_pad = np.insert(freq, 0, 2 * freq[0] - freq[1])
c_chan = freq_pad[((n_chan + 1) // 2)]
bw = freq_pad.ptp()
bw = np.ptp(freq_pad)
self.hdu.header['OBSNCHAN'] = n_chan
self.hdu.header['OBSFREQ'] = c_chan
self.hdu.header['OBSBW'] = bw
Expand Down Expand Up @@ -394,7 +395,7 @@ def polarization(self, value):
@property
def frequency(self):
if 'DAT_FREQ' in self.data.names:
freqs = u.Quantity(self.data['DAT_FREQ'][0], u.MHz, copy=False)
freqs = self.data['DAT_FREQ'][0] << u.MHz
else:
freqs = super().frequency

Expand Down Expand Up @@ -501,8 +502,7 @@ def verify(self):

# Check frequency
if 'DAT_FREQ' in self.data.names:
freqs = u.Quantity(self.data['DAT_FREQ'],
u.MHz, copy=False)
freqs = self.data['DAT_FREQ'] << u.MHz
assert np.array_equiv(freqs[0], freqs), \
"Frequencies are not all the same for different rows."

Expand Down Expand Up @@ -532,7 +532,7 @@ def start_time(self):
if "OFFS_SUB" in self.data.names:
offset0 = (self.data['OFFS_SUB'][0]
- self.data['TSUBINT'][0] * self.samples_per_frame / 2)
start_time += u.Quantity(offset0, u.s, copy=False)
start_time += offset0 << u.s

return start_time

Expand Down Expand Up @@ -562,7 +562,7 @@ def sample_rate(self):
# NOTE we are assuming TSUBINT is uniform; tested in verify,
# but as individual numbers seem to vary, take the mean.
# TODO: check whether there really isn't a better way!.
sample_time = u.Quantity(self.data['TSUBINT'], u.s).mean()
sample_time = (self.data['TSUBINT'] << u.s).mean()
return 1.0 / sample_time

@sample_rate.setter
Expand Down
17 changes: 10 additions & 7 deletions baseband_tasks/phases/phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from astropy.coordinates import Angle, Longitude
from astropy.time.utils import day_frac

from baseband_tasks.base import COPY_IF_NEEDED

__all__ = ['Phase', 'FractionalPhase']

Expand Down Expand Up @@ -190,7 +191,7 @@ def __new__(cls, phase1, phase2=None, copy=True, subok=False):
phase1 = phase1.view(cls)
return phase1.copy() if copy else phase1

phase1 = Angle(phase1, cls._unit, copy=False)
phase1 = Angle(phase1, cls._unit, copy=COPY_IF_NEEDED)

if phase2 is not None:
if isinstance(phase2, Phase):
Expand All @@ -199,7 +200,7 @@ def __new__(cls, phase1, phase2=None, copy=True, subok=False):
phase2 = phase2.view(cls)
return phase2

phase2 = Angle(phase2, cls._unit, copy=False)
phase2 = Angle(phase2, cls._unit, copy=COPY_IF_NEEDED)

return cls.from_angles(phase1, phase2)

Expand Down Expand Up @@ -531,8 +532,11 @@ def max(self, axis=None, out=None, keepdims=False):
def ptp(self, axis=None, out=None, keepdims=False):
"""Peak to peak (maximum - minimum) along a given axis.
This is similar to :meth:`~numpy.ndarray.ptp`, but adapted to ensure
This is similar to :func:`~numpy.ptp`, but adapted to ensure
that the full precision is used.
.. note:: for numpy >= 2.0, ``np.ptp(phase)`` no longer defers
to this method.
"""
if out is not None:
raise ValueError("An `out` argument is not yet supported.")
Expand Down Expand Up @@ -676,8 +680,7 @@ def __array_ufunc__(self, function, method, *inputs, **kwargs):
or function is np.divide and i_self == 0)
and basic_phase_out):
try:
other = u.Quantity(inputs[1-i_self], u.dimensionless_unscaled,
copy=False).value
other = (inputs[1-i_self] << u.dimensionless_unscaled).value
if function is np.multiply:
return self.from_angles(self['int'], self['frac'],
factor=other, out=phase_out)
Expand Down Expand Up @@ -732,8 +735,8 @@ def __array_ufunc__(self, function, method, *inputs, **kwargs):
elif function in {np.absolute, np.fabs} and basic_phase_out:
# Go via view to avoid having to deal with imaginary.
v = self.view(np.ndarray)
return self.from_angles(u.Quantity(v['int'], u.cycle, copy=False),
u.Quantity(v['frac'], u.cycle, copy=False),
return self.from_angles(v['int'] << u.cycle,
v['frac'] << u.cycle,
factor=np.sign(v['int'] + v['frac']),
out=phase_out)

Expand Down
Loading

0 comments on commit e8086b8

Please sign in to comment.