Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-awkward for openSUSE:Factory checked in at 2023-06-21 22:39:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-awkward (Old) and /work/SRC/openSUSE:Factory/.python-awkward.new.15902 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-awkward" Wed Jun 21 22:39:17 2023 rev:23 rq:1094142 version:2.2.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-awkward/python-awkward.changes 2023-06-11 19:58:33.148343665 +0200 +++ /work/SRC/openSUSE:Factory/.python-awkward.new.15902/python-awkward.changes 2023-06-21 22:40:16.106518960 +0200 @@ -1,0 +2,11 @@ +Tue Jun 20 16:41:07 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 2.2.3: + * feat!: deprecate to_NumpyForm's ``dtype` argument + * feat!: deprecate the time __unit__ parameter + * fix: don't project records during broadcasting; push index down + * fix: protect ak.to_parquet against memory explosion when args are swapped. + * fix: str of KeyError for <3.11 + * fix: support axis != -1 for record reduction + +------------------------------------------------------------------- Old: ---- awkward-2.2.2.tar.gz New: ---- awkward-2.2.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-awkward.spec ++++++ --- /var/tmp/diff_new_pack.py76tX/_old 2023-06-21 22:40:16.730522715 +0200 +++ /var/tmp/diff_new_pack.py76tX/_new 2023-06-21 22:40:16.734522739 +0200 @@ -16,10 +16,10 @@ # -%define awkward_cpp_version 16 +%define awkward_cpp_version 17 %{?sle15_python_module_pythons} Name: python-awkward -Version: 2.2.2 +Version: 2.2.3 Release: 0 Summary: Manipulate arrays of complex data structures as easily as Numpy License: BSD-3-Clause ++++++ awkward-2.2.2.tar.gz -> awkward-2.2.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/PKG-INFO new/awkward-2.2.3/PKG-INFO --- old/awkward-2.2.2/PKG-INFO 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/PKG-INFO 2023-06-15 17:47:29.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: awkward -Version: 2.2.2 +Version: 2.2.3 Summary: Manipulate JSON-like data with NumPy-like idioms. Project-URL: Bug Tracker, https://github.com/scikit-hep/awkward-1.0/issues Project-URL: Chat, https://gitter.im/Scikit-HEP/awkward-array @@ -36,7 +36,7 @@ Classifier: Topic :: Software Development Classifier: Topic :: Utilities Requires-Python: >=3.7 -Requires-Dist: awkward-cpp==16 +Requires-Dist: awkward-cpp==17 Requires-Dist: importlib-resources; python_version < '3.9' Requires-Dist: numpy>=1.17.0 Requires-Dist: packaging diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/pyproject.toml new/awkward-2.2.3/pyproject.toml --- old/awkward-2.2.2/pyproject.toml 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/pyproject.toml 2023-06-15 17:47:29.000000000 +0200 @@ -7,7 +7,7 @@ [project] name = "awkward" -version = "2.2.2" +version = "2.2.3" description = "Manipulate JSON-like data with NumPy-like idioms." license = { text = "BSD-3-Clause" } requires-python = ">=3.7" @@ -40,7 +40,7 @@ "Topic :: Utilities", ] dependencies = [ - "awkward_cpp==16", + "awkward_cpp==17", "importlib_resources;python_version < \"3.9\"", "numpy>=1.17.0", "packaging", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_backends/backend.py new/awkward-2.2.3/src/awkward/_backends/backend.py --- old/awkward-2.2.2/src/awkward/_backends/backend.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_backends/backend.py 2023-06-15 17:47:29.000000000 +0200 @@ -59,8 +59,6 @@ message = error.str.decode(errors="surrogateescape") - assert not error.pass_through - if error.attempt != ak._util.kSliceNone: message += f" while attempting to get index {error.attempt}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_broadcasting.py new/awkward-2.2.3/src/awkward/_broadcasting.py --- old/awkward-2.2.2/src/awkward/_broadcasting.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_broadcasting.py 2023-06-15 17:47:29.000000000 +0200 @@ -934,7 +934,13 @@ ) def broadcast_any_indexed(): - nextinputs = [x.project() if isinstance(x, IndexedArray) else x for x in inputs] + # The `apply` function may exit at the level of a `RecordArray`. We can avoid projection + # of the record array in such cases, in favour of a deferred carry. This can be done by + # "pushing" the `IndexedArray` _into_ the record (i.e., wrapping each `content`). + nextinputs = [ + x._push_inside_record_or_project() if isinstance(x, IndexedArray) else x + for x in inputs + ] return apply_step( backend, nextinputs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_connect/cling.py new/awkward-2.2.3/src/awkward/_connect/cling.py --- old/awkward-2.2.2/src/awkward/_connect/cling.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_connect/cling.py 2023-06-15 17:47:29.000000000 +0200 @@ -446,7 +446,7 @@ def togenerator(form, flatlist_as_rvec): if isinstance(form, ak.forms.EmptyForm): - return togenerator(form.to_NumpyForm(np.dtype(np.float64)), flatlist_as_rvec) + return togenerator(form.to_NumpyForm(primitive="float64"), flatlist_as_rvec) elif isinstance(form, ak.forms.NumpyForm): if len(form.inner_shape) == 0: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_connect/jax/reducers.py new/awkward-2.2.3/src/awkward/_connect/jax/reducers.py --- old/awkward-2.2.2/src/awkward/_connect/jax/reducers.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_connect/jax/reducers.py 2023-06-15 17:47:29.000000000 +0200 @@ -54,6 +54,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: raise RuntimeError("Cannot differentiate through argmin") @@ -78,6 +80,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: raise RuntimeError("Cannot differentiate through argmax") @@ -102,6 +106,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: raise RuntimeError("Cannot differentiate through count_zero") @@ -126,6 +132,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: raise RuntimeError("Cannot differentiate through count_nonzero") @@ -146,6 +154,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -179,6 +189,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -214,6 +226,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -242,6 +256,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -292,6 +308,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -351,6 +369,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_connect/numba/arrayview.py new/awkward-2.2.3/src/awkward/_connect/numba/arrayview.py --- old/awkward-2.2.2/src/awkward/_connect/numba/arrayview.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_connect/numba/arrayview.py 2023-06-15 17:47:29.000000000 +0200 @@ -106,7 +106,7 @@ def to_numbatype(form): if isinstance(form, ak.forms.EmptyForm): - return to_numbatype(form.to_NumpyForm(np.dtype(np.float64))) + return to_numbatype(form.to_NumpyForm(primitive="float64")) elif isinstance(form, ak.forms.NumpyForm): if len(form.inner_shape) == 0: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_errors.py new/awkward-2.2.3/src/awkward/_errors.py --- old/awkward-2.2.2/src/awkward/_errors.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_errors.py 2023-06-15 17:47:29.000000000 +0200 @@ -1,6 +1,7 @@ # BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE from __future__ import annotations +import builtins import sys import threading import warnings @@ -31,6 +32,11 @@ return self.func(*self.args, **self.kwargs) +class KeyError(builtins.KeyError): + def __str__(self): + return super(Exception, self).__str__() + + class ErrorContext: # Any other threads should get a completely independent _slate. _slate = threading.local() @@ -87,6 +93,9 @@ + "\n\nSee if this has been reported at https://github.com/scikit-hep/awkward/issues" ) new_exception.__cause__ = exception + elif issubclass(cls, builtins.KeyError): + new_exception = KeyError(self.format_exception(exception)) + new_exception.__cause__ = exception else: new_exception = cls(self.format_exception(exception)) new_exception.__cause__ = exception diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_kernels.py new/awkward-2.2.3/src/awkward/_kernels.py --- old/awkward-2.2.2/src/awkward/_kernels.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_kernels.py 2023-06-15 17:47:29.000000000 +0200 @@ -23,7 +23,6 @@ class KernelError(Protocol): filename: str | None # pylint: disable=E0602 str: str | None - pass_through: bool attempt: int id: int @@ -174,7 +173,6 @@ def __init__(self): self.str = None self.filename = None - self.pass_through = False self.attempt = ak._util.kSliceNone self.id = ak._util.kSliceNone diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_reducers.py new/awkward-2.2.3/src/awkward/_reducers.py --- old/awkward-2.2.2/src/awkward/_reducers.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_reducers.py 2023-06-15 17:47:29.000000000 +0200 @@ -39,6 +39,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: ... @@ -74,6 +76,53 @@ return given_dtype +def apply_positional_corrections( + reduced: ak.contents.NumpyArray, + parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, +): + if shifts is None: + assert ( + parents.nplike is reduced.backend.index_nplike + and starts.nplike is reduced.backend.index_nplike + ) + reduced.backend.maybe_kernel_error( + reduced.backend[ + "awkward_NumpyArray_reduce_adjust_starts_64", + reduced.dtype.type, + parents.dtype.type, + starts.dtype.type, + ]( + reduced.data, + reduced.length, + parents.data, + starts.data, + ) + ) + else: + assert ( + parents.nplike is reduced.backend.index_nplike + and starts.nplike is reduced.backend.index_nplike + and shifts.nplike is reduced.backend.index_nplike + ) + reduced.backend.maybe_kernel_error( + reduced._backend[ + "awkward_NumpyArray_reduce_adjust_starts_shifts_64", + reduced.dtype.type, + parents.dtype.type, + starts.dtype.type, + shifts.dtype.type, + ]( + reduced.data, + reduced.length, + parents.data, + starts.data, + shifts.data, + ) + ) + + class ArgMin(KernelReducer): name: Final = "argmin" preferred_dtype: Final = np.int64 @@ -83,6 +132,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -121,7 +172,9 @@ outlength, ) ) - return ak.contents.NumpyArray(result, backend=array.backend) + result_array = ak.contents.NumpyArray(result, backend=array.backend) + apply_positional_corrections(result_array, parents, starts, shifts) + return result_array class ArgMax(KernelReducer): @@ -133,6 +186,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -171,7 +226,9 @@ outlength, ) ) - return ak.contents.NumpyArray(result, backend=array.backend) + result_array = ak.contents.NumpyArray(result, backend=array.backend) + apply_positional_corrections(result_array, parents, starts, shifts) + return result_array class Count(KernelReducer): @@ -183,6 +240,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -210,6 +269,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -261,6 +322,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -363,6 +426,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -452,6 +517,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -503,6 +570,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -584,6 +653,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) @@ -690,6 +761,8 @@ self, array: ak.contents.NumpyArray, parents: ak.index.Index, + starts: ak.index.Index, + shifts: ak.index.Index | None, outlength: ShapeItem, ) -> ak.contents.NumpyArray: assert isinstance(array, ak.contents.NumpyArray) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/_regularize.py new/awkward-2.2.3/src/awkward/_regularize.py --- old/awkward-2.2.2/src/awkward/_regularize.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/_regularize.py 2023-06-15 17:47:29.000000000 +0200 @@ -50,30 +50,6 @@ return not isinstance(obj, (str, bytes)) and isinstance(obj, Sequence) -def regularize_path(path): - """ - Converts pathlib Paths into plain string paths (for all versions of Python). - """ - is_path = False - - if isinstance(path, getattr(os, "PathLike", ())): - is_path = True - path = os.fspath(path) - - elif hasattr(path, "__fspath__"): - is_path = True - path = path.__fspath__() - - elif path.__class__.__module__ == "pathlib": - import pathlib - - if isinstance(path, pathlib.Path): - is_path = True - path = str(path) - - return is_path, path - - def regularize_axis(axis: SupportsInt | None) -> AxisMaybeNone: if axis is None: return None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/contents/indexedarray.py new/awkward-2.2.3/src/awkward/contents/indexedarray.py --- old/awkward-2.2.2/src/awkward/contents/indexedarray.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/contents/indexedarray.py 2023-06-15 17:47:29.000000000 +0200 @@ -1136,3 +1136,18 @@ return self.index.is_equal_to( other.index, index_dtype, numpyarray ) and self.content.is_equal_to(other.content, index_dtype, numpyarray) + + def _push_inside_record_or_project(self) -> Self | ak.contents.RecordArray: + if self.content.is_record: + return ak.contents.RecordArray( + contents=[ + ak.contents.IndexedArray.simplified(self._index, c) + for c in self.content.contents + ], + fields=self.content._fields, + length=self.length, + backend=self._backend, + parameters=parameters_union(self.content._parameters, self._parameters), + ) + else: + return self.project() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/contents/listoffsetarray.py new/awkward-2.2.3/src/awkward/contents/listoffsetarray.py --- old/awkward-2.2.2/src/awkward/contents/listoffsetarray.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/contents/listoffsetarray.py 2023-06-15 17:47:29.000000000 +0200 @@ -1577,6 +1577,8 @@ ) nextparents = Index64.empty(nextlen, index_nplike) + # n.b. awkward_ListOffsetArray_reduce_local_nextparents_64 always returns parents that are + # monotonically increasing (because it is local) assert ( nextparents.nplike is index_nplike and self._offsets.nplike is index_nplike @@ -1704,6 +1706,7 @@ maxcount, ) ) + maxnextparents = index_nplike.index_as_shape_item(_maxnextparents[0]) nextstarts = Index64.empty(maxnextparents + 1, index_nplike) assert nextstarts.nplike is index_nplike and nextparents.nplike is index_nplike diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/contents/numpyarray.py new/awkward-2.2.3/src/awkward/contents/numpyarray.py --- old/awkward-2.2.2/src/awkward/contents/numpyarray.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/contents/numpyarray.py 2023-06-15 17:47:29.000000000 +0200 @@ -1128,48 +1128,7 @@ assert self.is_contiguous assert self._data.ndim == 1 - out = reducer.apply(self, parents, outlength) - - if reducer.needs_position: - if shifts is None: - assert ( - parents.nplike is self._backend.index_nplike - and starts.nplike is self._backend.index_nplike - ) - self._backend.maybe_kernel_error( - self._backend[ - "awkward_NumpyArray_reduce_adjust_starts_64", - out.data.dtype.type, - parents.dtype.type, - starts.dtype.type, - ]( - out.data, - outlength, - parents.data, - starts.data, - ) - ) - else: - assert ( - parents.nplike is self._backend.index_nplike - and starts.nplike is self._backend.index_nplike - and shifts.nplike is self._backend.index_nplike - ) - self._backend.maybe_kernel_error( - self._backend[ - "awkward_NumpyArray_reduce_adjust_starts_shifts_64", - out.data.dtype.type, - parents.dtype.type, - starts.dtype.type, - shifts.dtype.type, - ]( - out.data, - outlength, - parents.data, - starts.data, - shifts.data, - ) - ) + out = reducer.apply(self, parents, starts, shifts, outlength) if mask: outmask = ak.index.Index8.empty(outlength, self._backend.index_nplike) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/contents/recordarray.py new/awkward-2.2.3/src/awkward/contents/recordarray.py --- old/awkward-2.2.2/src/awkward/contents/recordarray.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/contents/recordarray.py 2023-06-15 17:47:29.000000000 +0200 @@ -906,35 +906,46 @@ ) ) else: + # Positional reducers ultimately need to do more work when rebuilding the result + # so asking for a mask doesn't help us! + reducer_should_mask = mask and not reducer.needs_position + # Convert parents into offsets to build a list for axis=1 reduction offsets = ak.index.Index64.empty(outlength + 1, self._backend.index_nplike) assert ( offsets.nplike is self._backend.index_nplike and parents.nplike is self._backend.index_nplike ) + # `parents` are possibly non monotonic increasing, so we must re-order the result + # This happens naturally for the `NumpyArray` reducers. + carry = ak.index.Index64.empty(outlength, self._backend.index_nplike) + + # Note: if we knew that `negaxis == depth` exclusively for this layout, we could use + # the simpler `ListOffsetArray_reduce_local_outoffsets_64`. However, if our parent was reduced, + # we would still see `negaxis == depth`, so this kernel has to be used instead. + assert carry.nplike is self._backend.index_nplike self._backend.maybe_kernel_error( self._backend[ - "awkward_ListOffsetArray_reduce_local_outoffsets_64", + "awkward_RecordArray_reduce_nonlocal_outoffsets_64", offsets.dtype.type, + carry.dtype.type, parents.dtype.type, ]( offsets.data, + carry.data, parents.data, parents.length, outlength, ) ) - layout_to_reduce = ak.contents.ListOffsetArray(offsets, self) - - # Positional reducers ultimately need to do more work when rebuilding the result - # so asking for a mask doesn't help us! - reducer_should_mask = mask and not reducer.needs_position out = _apply_record_reducer( reducer_recordclass, - layout_to_reduce, + ak.contents.ListOffsetArray(offsets, self), reducer_should_mask, behavior, ) + out = out._carry(carry, allow_lazy=True) + if out.is_option and not reducer_should_mask: reason = ( "reducer is positional" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/forms/emptyform.py new/awkward-2.2.3/src/awkward/forms/emptyform.py --- old/awkward-2.2.2/src/awkward/forms/emptyform.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/forms/emptyform.py 2023-06-15 17:47:29.000000000 +0200 @@ -1,6 +1,8 @@ # BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE from __future__ import annotations +from inspect import signature + import awkward as ak from awkward._errors import deprecate from awkward._nplikes.shape import ShapeItem @@ -53,8 +55,34 @@ def __eq__(self, other) -> bool: return isinstance(other, EmptyForm) and self._form_key == other._form_key - def to_NumpyForm(self, dtype): - return ak.forms.numpyform.from_dtype(dtype, parameters=self._parameters) + def to_NumpyForm(self, *args, **kwargs): + def legacy_impl(dtype): + deprecate( + f"the `dtype` parameter in {type(self).__name__}.to_NumpyForm is deprecated, " + f"in favour of a new `primitive` argument. Pass `primitive` by keyword to opt-in to the new behavior.", + version="2.4.0", + ) + return ak.forms.numpyform.from_dtype(dtype, parameters=self._parameters) + + def new_impl(*, primitive): + return ak.forms.numpyform.NumpyForm(primitive, parameters=self._parameters) + + dispatch_table = [ + new_impl, + legacy_impl, + ] + for func in dispatch_table: + sig = signature(func) + try: + bound_arguments = sig.bind(*args, **kwargs) + except TypeError: + continue + else: + return func(*bound_arguments.args, **bound_arguments.kwargs) + raise AssertionError( + f"{type(self).__name__}.to_NumpyForm accepts either the new `primitive` argument as a keyword-only " + f"argument, or the legacy `dtype` argument as positional or keyword" + ) def purelist_parameter(self, key): if self._parameters is None or key not in self._parameters: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/forms/numpyform.py new/awkward-2.2.3/src/awkward/forms/numpyform.py --- old/awkward-2.2.2/src/awkward/forms/numpyform.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/forms/numpyform.py 2023-06-15 17:47:29.000000000 +0200 @@ -2,6 +2,7 @@ from collections.abc import Iterable import awkward as ak +from awkward._errors import deprecate from awkward._nplikes.numpylike import NumpyMetadata from awkward._parameters import type_parameters_equal from awkward._typing import final @@ -11,14 +12,26 @@ np = NumpyMetadata.instance() -def from_dtype(dtype, parameters=None): +def from_dtype(dtype, parameters=None, *, time_units_as_parameter: bool = UNSET): if dtype.subdtype is None: inner_shape = () else: inner_shape = dtype.shape dtype = dtype.subdtype[0] - if issubclass(dtype.type, (np.datetime64, np.timedelta64)): + if time_units_as_parameter is UNSET: + time_units_as_parameter = True + + if time_units_as_parameter: + deprecate( + "from_dtype conversion of temporal units to generic `datetime64` and `timedelta64` types is deprecated, " + "pass `time_units_as_parameter=False` to disable this warning.", + version="2.4.0", + ) + + if time_units_as_parameter and issubclass( + dtype.type, (np.datetime64, np.timedelta64) + ): unit, step = np.datetime_data(dtype) if unit != "generic": unitstr = ("" if step == 1 else str(step)) + unit diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/operations/ak_from_avro_file.py new/awkward-2.2.3/src/awkward/operations/ak_from_avro_file.py --- old/awkward-2.2.2/src/awkward/operations/ak_from_avro_file.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/operations/ak_from_avro_file.py 2023-06-15 17:47:29.000000000 +0200 @@ -1,8 +1,7 @@ # BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE __all__ = ("from_avro_file",) -# from awkward._typing import Type -import pathlib +from os import PathLike, fsdecode import awkward as ak from awkward._nplikes.numpylike import NumpyMetadata @@ -15,7 +14,7 @@ ): """ Args: - file (string or file-like object): Avro file to be read as Awkward Array. + file (path-like or file-like object): Avro file to be read as Awkward Array. limit_entries (int): The number of rows of the Avro file to be read into the Awkward Array. debug_forth (bool): If True, prints the generated Forth code for debugging. highlevel (bool): If True, return an #ak.Array; otherwise, return @@ -40,8 +39,8 @@ "debug_forth": debug_forth, }, ): - if isinstance(file, pathlib.Path): - file = str(file) + if isinstance(file, (str, bytes, PathLike)): + file = fsdecode(file) if isinstance(file, str): with open(file, "rb") as opened_file: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/operations/ak_from_json.py new/awkward-2.2.3/src/awkward/operations/ak_from_json.py --- old/awkward-2.2.2/src/awkward/operations/ak_from_json.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/operations/ak_from_json.py 2023-06-15 17:47:29.000000000 +0200 @@ -399,7 +399,7 @@ def _get_reader(source): - if not isinstance(source, pathlib.Path) and isinstance(source, str): + if isinstance(source, str): source = source.encode("utf8", errors="surrogateescape") if isinstance(source, bytes): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/operations/ak_to_json.py new/awkward-2.2.3/src/awkward/operations/ak_to_json.py --- old/awkward-2.2.2/src/awkward/operations/ak_to_json.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/operations/ak_to_json.py 2023-06-15 17:47:29.000000000 +0200 @@ -1,8 +1,8 @@ # BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE __all__ = ("to_json",) import json -import pathlib from numbers import Number +from os import PathLike, fsdecode from urllib.parse import urlparse from awkward_cpp.lib import _ext @@ -33,7 +33,7 @@ """ Args: array: Array-like data (anything #ak.to_layout recognizes). - file (None, str/pathlib.Path, or file-like object): If None, this function returns + file (None, path-like, or file-like object): If None, this function returns JSON-encoded bytes. Otherwise, this function has no return value. If a string/pathlib.Path, this function opens a file with that name, writes JSON data, and closes the file. If that path has a URI protocol (like @@ -205,8 +205,8 @@ ) if file is not None: - if isinstance(file, (str, pathlib.Path)): - parsed_url = urlparse(file) + if isinstance(file, (str, bytes, PathLike)): + parsed_url = urlparse(fsdecode(file)) if parsed_url.scheme == "" or parsed_url.netloc == "": def opener(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/src/awkward/operations/ak_to_parquet.py new/awkward-2.2.3/src/awkward/operations/ak_to_parquet.py --- old/awkward-2.2.2/src/awkward/operations/ak_to_parquet.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/src/awkward/operations/ak_to_parquet.py 2023-06-15 17:47:29.000000000 +0200 @@ -2,6 +2,7 @@ __all__ = ("to_parquet",) from collections.abc import Mapping, Sequence +from os import fsdecode import awkward as ak from awkward._nplikes.numpylike import NumpyMetadata @@ -39,8 +40,8 @@ """ Args: array: Array-like data (anything #ak.to_layout recognizes). - destination (str): Name of the output file, file path, or remote URL passed to - [fsspec.core.url_to_fs](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.core.url_to_fs) + destination (path-like): Name of the output file, file path, or + remote URL passed to [fsspec.core.url_to_fs](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.core.url_to_fs) for remote writing. list_to32 (bool): If True, convert Awkward lists into 32-bit Arrow lists if they're small enough, even if it means an extra conversion. Otherwise, @@ -291,6 +292,13 @@ if parquet_extra_options is None: parquet_extra_options = {} + try: + destination = fsdecode(destination) + except TypeError: + raise TypeError( + f"'destination' argument of 'ak.to_parquet' must be a path-like, not {type(destination).__name__} ('array' argument is first; 'destination' second)" + ) from None + fs, destination = fsspec.core.url_to_fs(destination, **(storage_options or {})) metalist = [] with pyarrow_parquet.ParquetWriter( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/tests/test_0914_types_and_forms.py new/awkward-2.2.3/tests/test_0914_types_and_forms.py --- old/awkward-2.2.2/tests/test_0914_types_and_forms.py 2023-06-05 20:34:57.000000000 +0200 +++ new/awkward-2.2.3/tests/test_0914_types_and_forms.py 2023-06-15 17:47:29.000000000 +0200 @@ -1554,132 +1554,197 @@ "parameters": {"__unit__": "s", "x": 123}, } - assert ak.forms.numpyform.from_dtype(np.dtype("bool")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "bool", - } - assert ak.forms.numpyform.from_dtype(np.dtype("int8")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "int8", - } - assert ak.forms.numpyform.from_dtype(np.dtype("uint8")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "uint8", - } - assert ak.forms.numpyform.from_dtype(np.dtype("int16")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "int16", - } - assert ak.forms.numpyform.from_dtype(np.dtype("uint16")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "uint16", - } - assert ak.forms.numpyform.from_dtype(np.dtype("int32")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "int32", - } - assert ak.forms.numpyform.from_dtype(np.dtype("uint32")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "uint32", - } - assert ak.forms.numpyform.from_dtype(np.dtype("int64")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "int64", - } - assert ak.forms.numpyform.from_dtype(np.dtype("uint64")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "uint64", - } - if hasattr(np, "float16"): - assert ak.forms.numpyform.from_dtype(np.dtype("float16")).to_dict( + with pytest.warns(DeprecationWarning): + assert ak.forms.numpyform.from_dtype(np.dtype("bool")).to_dict( verbose=False ) == { "class": "NumpyArray", - "primitive": "float16", + "primitive": "bool", } - assert ak.forms.numpyform.from_dtype(np.dtype("float32")).to_dict( - verbose=False - ) == { - "class": "NumpyArray", - "primitive": "float32", - } - assert ak.forms.numpyform.from_dtype(np.dtype("float64")).to_dict( - verbose=False - ) == { - "class": "NumpyArray", - "primitive": "float64", - } - if hasattr(np, "float128"): - assert ak.forms.numpyform.from_dtype(np.dtype("float128")).to_dict( + assert ak.forms.numpyform.from_dtype(np.dtype("int8")).to_dict( verbose=False ) == { "class": "NumpyArray", - "primitive": "float128", + "primitive": "int8", } - assert ak.forms.numpyform.from_dtype(np.dtype("complex64")).to_dict( - verbose=False - ) == { - "class": "NumpyArray", - "primitive": "complex64", - } - assert ak.forms.numpyform.from_dtype(np.dtype("complex128")).to_dict( - verbose=False - ) == { - "class": "NumpyArray", - "primitive": "complex128", - } - if hasattr(np, "complex256"): - assert ak.forms.numpyform.from_dtype(np.dtype("complex256")).to_dict( + assert ak.forms.numpyform.from_dtype(np.dtype("uint8")).to_dict( verbose=False ) == { "class": "NumpyArray", - "primitive": "complex256", + "primitive": "uint8", } - assert ak.forms.numpyform.from_dtype(np.dtype("M8")).to_dict(verbose=False) == { - "class": "NumpyArray", - "primitive": "datetime64", - } - assert ak.forms.numpyform.from_dtype(np.dtype("M8[s]")).to_dict(verbose=False) == { + assert ak.forms.numpyform.from_dtype(np.dtype("int16")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "int16", + } + assert ak.forms.numpyform.from_dtype(np.dtype("uint16")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "uint16", + } + assert ak.forms.numpyform.from_dtype(np.dtype("int32")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "int32", + } + assert ak.forms.numpyform.from_dtype(np.dtype("uint32")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "uint32", + } + assert ak.forms.numpyform.from_dtype(np.dtype("int64")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "int64", + } + assert ak.forms.numpyform.from_dtype(np.dtype("uint64")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "uint64", + } + if hasattr(np, "float16"): + assert ak.forms.numpyform.from_dtype(np.dtype("float16")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "float16", + } + assert ak.forms.numpyform.from_dtype(np.dtype("float32")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "float32", + } + assert ak.forms.numpyform.from_dtype(np.dtype("float64")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "float64", + } + if hasattr(np, "float128"): + assert ak.forms.numpyform.from_dtype(np.dtype("float128")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "float128", + } + assert ak.forms.numpyform.from_dtype(np.dtype("complex64")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "complex64", + } + assert ak.forms.numpyform.from_dtype(np.dtype("complex128")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "complex128", + } + if hasattr(np, "complex256"): + assert ak.forms.numpyform.from_dtype(np.dtype("complex256")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "complex256", + } + assert ak.forms.numpyform.from_dtype(np.dtype("M8")).to_dict(verbose=False) == { + "class": "NumpyArray", + "primitive": "datetime64", + } + assert ak.forms.numpyform.from_dtype(np.dtype("M8[s]")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "datetime64", + "parameters": {"__unit__": "s"}, + } + assert ak.forms.numpyform.from_dtype( + np.dtype("M8[s]"), parameters={"x": 123} + ).to_dict(verbose=False) == { + "class": "NumpyArray", + "primitive": "datetime64", + "parameters": {"__unit__": "s", "x": 123}, + } + assert ak.forms.numpyform.from_dtype(np.dtype("m8")).to_dict(verbose=False) == { + "class": "NumpyArray", + "primitive": "timedelta64", + } + assert ak.forms.numpyform.from_dtype(np.dtype("m8[s]")).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "timedelta64", + "parameters": {"__unit__": "s"}, + } + assert ak.forms.numpyform.from_dtype( + np.dtype("m8[s]"), parameters={"x": 123} + ).to_dict(verbose=False) == { + "class": "NumpyArray", + "primitive": "timedelta64", + "parameters": {"__unit__": "s", "x": 123}, + } + assert ak.forms.numpyform.from_dtype(np.dtype(("bool", (1, 2, 3)))).to_dict( + verbose=False + ) == { + "class": "NumpyArray", + "primitive": "bool", + "inner_shape": [1, 2, 3], + } + + assert ak.forms.numpyform.from_dtype( + np.dtype("M8"), time_units_as_parameter=False + ).to_dict(verbose=False) == { "class": "NumpyArray", "primitive": "datetime64", - "parameters": {"__unit__": "s"}, } assert ak.forms.numpyform.from_dtype( - np.dtype("M8[s]"), parameters={"x": 123} + np.dtype("M8[s]"), time_units_as_parameter=False ).to_dict(verbose=False) == { "class": "NumpyArray", - "primitive": "datetime64", - "parameters": {"__unit__": "s", "x": 123}, + "primitive": "datetime64[s]", } - assert ak.forms.numpyform.from_dtype(np.dtype("m8")).to_dict(verbose=False) == { + assert ak.forms.numpyform.from_dtype( + np.dtype("M8[s]"), time_units_as_parameter=False, parameters={"x": 123} + ).to_dict(verbose=False) == { "class": "NumpyArray", - "primitive": "timedelta64", + "primitive": "datetime64[s]", + "parameters": {"x": 123}, } - assert ak.forms.numpyform.from_dtype(np.dtype("m8[s]")).to_dict(verbose=False) == { + assert ak.forms.numpyform.from_dtype( + np.dtype("m8"), time_units_as_parameter=False + ).to_dict(verbose=False) == { "class": "NumpyArray", "primitive": "timedelta64", - "parameters": {"__unit__": "s"}, } assert ak.forms.numpyform.from_dtype( - np.dtype("m8[s]"), parameters={"x": 123} + np.dtype("m8[s]"), time_units_as_parameter=False ).to_dict(verbose=False) == { "class": "NumpyArray", - "primitive": "timedelta64", - "parameters": {"__unit__": "s", "x": 123}, + "primitive": "timedelta64[s]", } - assert ak.forms.numpyform.from_dtype(np.dtype(("bool", (1, 2, 3)))).to_dict( - verbose=False - ) == { + assert ak.forms.numpyform.from_dtype( + np.dtype("m8[s]"), parameters={"x": 123}, time_units_as_parameter=False + ).to_dict(verbose=False) == { "class": "NumpyArray", - "primitive": "bool", - "inner_shape": [1, 2, 3], + "primitive": "timedelta64[s]", + "parameters": {"x": 123}, } with pytest.raises(TypeError): - ak.forms.from_dtype(np.dtype("O")).to_dict(verbose=False) - with pytest.raises(TypeError): - ak.forms.from_dtype(np.dtype([("one", np.int64), ("two", np.float64)])).to_dict( + ak.forms.from_dtype(np.dtype("O"), time_units_as_parameter=False).to_dict( verbose=False ) + with pytest.raises(TypeError): + ak.forms.from_dtype( + np.dtype([("one", np.int64), ("two", np.float64)]), + time_units_as_parameter=False, + ).to_dict(verbose=False) assert ak.forms.from_dict("bool").to_dict(verbose=False) == { "class": "NumpyArray", "primitive": "bool", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/tests/test_2503_deprecate_to_numpyform.py new/awkward-2.2.3/tests/test_2503_deprecate_to_numpyform.py --- old/awkward-2.2.2/tests/test_2503_deprecate_to_numpyform.py 1970-01-01 01:00:00.000000000 +0100 +++ new/awkward-2.2.3/tests/test_2503_deprecate_to_numpyform.py 2023-06-15 17:47:29.000000000 +0200 @@ -0,0 +1,22 @@ +# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE + +import numpy as np +import pytest + +import awkward as ak + + +def test_dtype_deprecated(): + form = ak.forms.EmptyForm() + with pytest.warns( + DeprecationWarning, + match=r"the `dtype` parameter in EmptyForm\.to_NumpyForm is deprecated", + ): + next_form = form.to_NumpyForm(dtype=np.dtype(np.int64)) + assert next_form.primitive == "int64" + + +def test_primitive(): + form = ak.forms.EmptyForm() + next_form = form.to_NumpyForm(primitive="int64") + assert next_form.primitive == "int64" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/tests/test_2512_record_array_carry.py new/awkward-2.2.3/tests/test_2512_record_array_carry.py --- old/awkward-2.2.2/tests/test_2512_record_array_carry.py 1970-01-01 01:00:00.000000000 +0100 +++ new/awkward-2.2.3/tests/test_2512_record_array_carry.py 2023-06-15 17:47:29.000000000 +0200 @@ -0,0 +1,160 @@ +# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE + +import numpy as np + +import awkward as ak + + +def _reduce_max_masked(array, mask): + assert mask + j = ak.from_regular( + ak.argmax(array["1"], axis=1, keepdims=True, mask_identity=True) + ) + return ak.flatten(array[j], axis=1) + + +behavior = {} +behavior[ak.max, "pair"] = _reduce_max_masked + + +def test_axis_0(): + content = ak.contents.ListArray( + ak.index.Index64([0, 2]), + ak.index.Index64([2, 4]), + ak.contents.ListOffsetArray( + ak.index.Index64([0, 3, 6, 9, 11]), + ak.contents.RecordArray( + [ + ak.contents.NumpyArray(np.arange(11, dtype=np.int64)), + ak.contents.NumpyArray( + np.array( + [ + 0.0, + 2.0, + 4.0, + 6.0, + 8.0, + 10.0, + 1.0, + 14.0, + 16.0, + 18.0, + 20.0, + ], + dtype=np.float64, + ) + ), + ], + fields=None, + parameters={"__record__": "pair"}, + ), + ), + ) + + result = ak.max( + content, + axis=0, + keepdims=True, + mask_identity=True, + behavior=behavior, + highlevel=False, + ) + + expected_result = ak.contents.ListArray( + ak.index.Index64([0]), + ak.index.Index64([2]), + ak.contents.ListArray( + ak.index.Index64([0, 3]), + ak.index.Index64([3, 6]), + ak.contents.IndexedOptionArray( + ak.index.Index64([1, 5, 9, 3, 7, 10]), + ak.contents.RecordArray( + [ + ak.contents.NumpyArray( + np.array([0, 6, 3, 9, 1, 7, 4, 10, 2, 8, 5], dtype=np.int64) + ), + ak.contents.NumpyArray( + np.array( + [0, 1, 6, 18, 2, 14, 8, 20, 4, 16, 10], + dtype=np.float64, + ) + ), + ], + fields=None, + parameters={"__record__": "pair"}, + ), + ), + ), + ) + assert result.is_equal_to(expected_result) + + +def test_axis_1(): + content = ak.contents.ListArray( + ak.index.Index64([0, 2]), + ak.index.Index64([2, 4]), + ak.contents.ListOffsetArray( + ak.index.Index64([0, 3, 6, 9, 11]), + ak.contents.RecordArray( + [ + ak.contents.NumpyArray(np.arange(11, dtype=np.int64)), + ak.contents.NumpyArray( + np.array( + [ + 0.0, + 2.0, + 4.0, + 6.0, + 8.0, + 10.0, + 1.0, + 14.0, + 16.0, + 18.0, + 20.0, + ], + dtype=np.float64, + ) + ), + ], + fields=None, + parameters={"__record__": "pair"}, + ), + ), + ) + + result = ak.max( + content, + axis=1, + keepdims=True, + mask_identity=True, + behavior=behavior, + highlevel=False, + ) + + expected_result = ak.contents.RegularArray( + ak.contents.ListArray( + ak.index.Index64([0, 3]), + ak.index.Index64([3, 6]), + ak.contents.IndexedOptionArray( + ak.index.Index64([1, 5, 9, 3, 7, 10]), + ak.contents.RecordArray( + [ + ak.contents.NumpyArray( + np.array([0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8], dtype=np.int64) + ), + ak.contents.NumpyArray( + np.array( + [0, 6, 1, 18, 2, 8, 14, 20, 4, 10, 16], + dtype=np.float64, + ) + ), + ], + fields=None, + parameters={"__record__": "pair"}, + ), + ), + ), + size=1, + ) + assert result.is_equal_to(expected_result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/awkward-2.2.2/tests/test_2518_datetime_units_as_parameter.py new/awkward-2.2.3/tests/test_2518_datetime_units_as_parameter.py --- old/awkward-2.2.2/tests/test_2518_datetime_units_as_parameter.py 1970-01-01 01:00:00.000000000 +0100 +++ new/awkward-2.2.3/tests/test_2518_datetime_units_as_parameter.py 2023-06-15 17:47:29.000000000 +0200 @@ -0,0 +1,32 @@ +# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE + +import numpy as np +import pytest + +import awkward as ak + + +def test_no_parameter(): + dtype = np.dtype("datetime64[15us]") + with pytest.warns( + DeprecationWarning, match=r"from_dtype conversion of temporal units" + ): + form = ak.forms.numpyform.from_dtype(dtype) + assert form.primitive == "datetime64" + assert form.parameters == {"__unit__": "15us"} + + +def test_true_parameter(): + dtype = np.dtype("datetime64[15us]") + with pytest.warns( + DeprecationWarning, match=r"from_dtype conversion of temporal units" + ): + form = ak.forms.numpyform.from_dtype(dtype, time_units_as_parameter=True) + assert form.primitive == "datetime64" + assert form.parameters == {"__unit__": "15us"} + + +def test_false_parameter(): + dtype = np.dtype("datetime64[15us]") + form = ak.forms.numpyform.from_dtype(dtype, time_units_as_parameter=False) + assert form.primitive == "datetime64[15us]"