Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for tab completion #22

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/source/getting_started/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,14 @@ In this case with :class:`~swiftgalaxy.halo_catalogues.Velociraptor`, we can get

sg.halo_catalogue.masses.mvir

:mod:`swiftgalaxy` supports Python's tab completion features. This means that you can browse the available attributes of objects in an interactive interpreter by starting to type an attribute (or just a trailing dot) and pressing tab twice. A few examples to help start exploring:

- ``sg.<tab><tab>``
- ``sg.gas.<tab><tab>``
- ``sg.halo_catalogue.<tab><tab>``

.. warning::

As currently implemented, using tab completion accesses the attributes of the object that is being searched for attriubtes. This triggers :mod:`swiftsimio`'s lazy-loading functionality. The result is that using tab completion like ``sg.gas.<tab><tab>`` reads *all* gas particle property arrays from the snapshot! This is obviously not desirable behaviour; a better implementation is being sought.

The further features of a :class:`~swiftgalaxy.reader.SWIFTGalaxy` are detailed in the next sections.
36 changes: 34 additions & 2 deletions swiftgalaxy/halo_catalogues.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,23 @@ def _check_multi(self) -> None:
assert self.extra_mask in (None, "bound_only")
return

def __dir__(self) -> list[str]:
"""
Supply a list of attributes of the halo catalogue.

The regular ``dir`` behaviour doesn't index the names of catalogue attributes
because they're attached to the internally maintained ``_catalogue`` attribute,
so we custimize the ``__dir__`` method to list the attribute names. They will
then appear in tab completion, for example.

Returns
-------
out : list
The list of catalogue attribute names.
"""
# use getattr to default to None, e.g. for Standalone
return dir(getattr(self, "_catalogue", None))

def __getattr__(self, attr: str) -> Any:
"""
Exposes the masked halo catalogue.
Expand Down Expand Up @@ -885,6 +902,22 @@ def __repr__(self) -> str:
"""
return self._catalogue.__repr__()

def __dir__(self) -> list[str]:
"""
Supply a list of attributes of the halo catalogue.

The regular ``dir`` behaviour doesn't index the names of catalogue attributes
because they're attached to the internally maintained ``_catalogue`` attribute,
so we custimize the ``__dir__`` method to list the attribute names. They will
then appear in tab completion, for example.

Returns
-------
out : list
The list of catalogue attribute names.
"""
return list(self._catalogue.metadata.present_group_names)


class Velociraptor(_HaloCatalogue):
"""
Expand Down Expand Up @@ -1057,7 +1090,6 @@ def _load(self) -> None:
of interest.
"""
import h5py
from velociraptor.catalogue.catalogue import Catalogue as VelociraptorCatalogue
from velociraptor import load as load_catalogue
from velociraptor.particles import load_groups

Expand All @@ -1068,7 +1100,7 @@ def _load(self) -> None:
else 1.0
)

self._catalogue: "VelociraptorCatalogue" = load_catalogue(
self._catalogue = load_catalogue(
self.velociraptor_files["properties"], mask=np.array(self._halo_index)
)
groups = load_groups(
Expand Down
52 changes: 52 additions & 0 deletions swiftgalaxy/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ def __init__(self, coordinates: Union[dict, cosmo_array], masks: dict) -> None:
self._masks: dict = masks
return

def __dir__(self) -> list[str]:
"""
Supply a list of attributes of the :class:`~swiftgalaxy.reader._CoordinateHelper`.

The regular ``dir`` behaviour doesn't index the names of the coordinates
because these are stored in a ``dict`` held by the class, so we customize
the ``__dir__`` method to list the coordinate names. They will then appear in
tab completion, for example.

Returns
-------
out : list
List of coordinate name strings.
"""
return list(self._masks.keys())

def __getattr__(self, attr: str) -> cosmo_array:
"""
Get a coordinate array using attribute (dot) syntax.
Expand Down Expand Up @@ -321,6 +337,24 @@ def __repr__(self) -> str:
"""
return self.__str__()

def __dir__(self) -> list[str]:
"""
Supply a list of attributes of the
:class:`~swiftgalaxy.reader._SWIFTNamedColumnDatasetHelper`.

The regular ``dir`` behaviour doesn't index the names of the named columns
because these are stored in the
:class:`~swiftsimio.reader.__SWIFTNamedColumnDataset` held by the class, so we
customize the ``__dir__`` method to list the named column names. They will then
appear in tab completion, for example.

Returns
-------
out : list
List of named column name strings.
"""
return self._named_column_dataset.named_columns

def __getattribute__(self, attr: str) -> Any:
"""
Get an attribute of the object.
Expand Down Expand Up @@ -547,6 +581,24 @@ class _SWIFTGroupDatasetHelper(object):
mygalaxy.gas.cylindrical_velocities.z
"""

def __dir__(self) -> list[str]:
"""
Supply a list of attributes of the
:class:`~swiftgalaxy.reader._SWIFTGroupDatasetHelper`.

The regular ``dir`` behaviour doesn't index the names of the particle datasets
because these are stored in the
:class:`~swiftsimio.reader.__SWIFTGroupDataset` held by the class, so we
customize the ``__dir__`` method to list the particle dataset names. They will
then appear in tab completion, for example.

Returns
-------
out : list
List of named column name strings.
"""
return self._particle_dataset.group_metadata.field_names

def __init__(
self, particle_dataset: "__SWIFTGroupDataset", swiftgalaxy: "SWIFTGalaxy"
) -> None:
Expand Down
37 changes: 37 additions & 0 deletions tests/test_creation.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,68 @@
class TestSWIFTGalaxyCreation:
def test_sg_creation(self, sg):
"""
Make sure we can create a SWIFTGalaxy without error.
"""
pass # fixture created SWIFTGalaxy

def test_soap_creation(self, soap):
"""
Make sure we can create a SOAP without error.
"""
pass # fixture created SOAP interface

def test_vr_creation(self, vr):
"""
Make sure we can create a Velociraptor without error.
"""
pass # fixture created Velociraptor interface

def test_caesar_creation(self, caesar):
"""
Make sure we can create a Caesar without error.
"""
pass # fixture created Caesar interface

def test_sa_creation(self, sa):
"""
Make sure we can create a Standalone without error.
"""
pass # fixture created Standalone interface

def test_sg_soap_creation(self, sg_soap):
"""
Make sure we can create a SWIFTGalaxy with SOAP without error.
"""
pass # fixture created SWIFTGalaxy with SOAP interface

def test_sg_vr_creation(self, sg_vr):
"""
Make sure we can create a SWIFTGalaxy with velociraptor without error.
"""
pass # fixture created SWIFTGalaxy with Velociraptor interface

def test_sg_caesar_creation(self, sg_caesar):
"""
Make sure we can create a SWIFTGalaxy with Caesar without error.
"""
pass # fixture created SWIFTGalaxy with Caesar interface

def test_sg_sa_creation(self, sg_sa):
"""
Make sure we can create a SWIFTGalaxy with Standalone without error.
"""
pass # fixture created SWIFTGalaxy with Standalone interface

def test_dir_for_tab_completion(self, sg):
"""
Check that particle dataset names and named column names get injected into
the namespace for tab completion by the __dir__ methods of the relevant classes.
"""
for prop in ("coordinates", "masses", "hydrogen_ionization_fractions"):
assert prop in dir(sg.gas)
for prop in ("neutral", "ionized"):
assert prop in dir(sg.gas.hydrogen_ionization_fractions)


class TestSWIFTGalaxiesCreation:
def test_sgs_creation(self, sgs):
Expand Down
12 changes: 12 additions & 0 deletions tests/test_derived_coordinates.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,3 +596,15 @@ def test_single_particle_produces_array_cylindrical(
).shape
is not tuple()
)

def test_dir_for_tab_completion(self, sg):
"""
Check that we can see coordinate names when using dir on the helper class.
We don't test for an exhaustive list, as long as some appear all will.
"""
for coord in ("x", "xyz"):
assert coord in dir(sg.gas.cartesian_coordinates)
for coord in ("r", "lon", "radius"):
assert coord in dir(sg.gas.spherical_coordinates)
for coord in ("rho", "z", "azimuth"):
assert coord in dir(sg.gas.cylindrical_coordinates)
32 changes: 32 additions & 0 deletions tests/test_halo_catalogues.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,15 @@ def test_masking_catalogue(self, vr_multi):
vr_multi._mask_multi_galaxy(0)
self.test_catalogue_exposed(vr_multi)

def test_dir_for_tab_completion(self, vr):
"""
Check that we add catalogue properties to the namespace directory.

Just check a couple, don't need to be exhaustive.
"""
for prop in ("energies", "metallicity", "temperature"):
assert prop in dir(vr)


class TestVelociraptorWithSWIFTGalaxy:
"""
Expand Down Expand Up @@ -671,6 +680,16 @@ def test_spatial_mask_applied(self, caesar, toysnap):
)[particle_type]
)

def test_dir_for_tab_completion(self, caesar):
"""
Check that we add catalogue properties to the namespace directory.

Just check a couple, don't need to be exhaustive.
"""
# picked these to be common between halo and galaxy catalogues:
for prop in ("glist", "pos", "radii"):
assert prop in dir(caesar)


class TestCaesarWithSWIFTGalaxy:
"""
Expand Down Expand Up @@ -859,6 +878,19 @@ def test_masking_catalogue(self, soap_multi):
soap_multi._mask_multi_galaxy(0)
self.test_catalogue_exposed(soap_multi)

def test_dir_for_tab_completion(self, soap):
"""
Check that we add catalogue properties to the namespace directory.

Just check a couple, don't need to be exhaustive.
"""
for prop in (
"exclusive_sphere_100kpc",
"input_halos",
"spherical_overdensity_bn98",
):
assert prop in dir(soap)


class TestSOAPWithSWIFTGalaxy:
"""
Expand Down