Skip to content

Commit abebd5c

Browse files
author
rchevrier
committed
Update documentation
1 parent cfab26c commit abebd5c

7 files changed

Lines changed: 108 additions & 19 deletions

File tree

docs/advanced.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Remote file system listing can be quite long. Implementations are usually
4848
shipped with layouts for an improved listing speed. See the
4949
{ref}`Layout <layout>` introduction if listing performance becomes an issue.
5050
51+
(disable-layouts)=
5152
5253
## Disable layouts
5354

docs/getting_started.md

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ given criterias
4848

4949
```{code-cell}
5050
from fcollections.implementations import NetcdfFilesDatabaseSwotLRL2
51+
5152
fc = NetcdfFilesDatabaseSwotLRL2(path)
5253
fc.list_files(cycle_number=1)
5354
```
@@ -83,6 +84,8 @@ ds = fc.query(selected_variables=['ssha'])
8384
list(ds.variables)
8485
```
8586

87+
### Filter types
88+
8689
Each implementation has its own filters. By order of availability, the user
8790
should consult:
8891

@@ -95,23 +98,42 @@ should consult:
9598
fc.query?
9699
```
97100

101+
### Filter values
102+
103+
Possible values for a given filter can be displayed
104+
105+
```{code-cell}
106+
fc.filter_values('version')
107+
```
108+
109+
Only filters whose information are contained in the intermediated folders can be
110+
scanned in a quick way, other will trigger a full scan. As such, to ensure
111+
optimal performance, this method should be called with the layouts enabled, with
112+
files organized with folders (see the [advanced section](#disable-layouts)), and
113+
on filters whose information is encoded in the folders.
114+
98115
## Access metadata
99116

100117
The database can display information about the variables and attributes
101118
contained in the files' collection using the ``variables_info`` method
102119

103120
```{code-cell}
104-
fc.variables_info(subset='Expert')
121+
# Use the enumeration name for filtering a specific subset
122+
fc.variables_info()
105123
```
106124

107125
It will offer a simple collapsible tree view with multiple levels of nesting
108126
depending on the data you manipulate
109127

110-
In order to return consistent metadata, the method ensures that only one
111-
homogeneous subset is selected. In case you handle unmixable data (for example
112-
Expert and Unsmoothed datasets), you must give proper filters on the subset
113-
partitioning keys ``fc.unmixer.partition_keys``. If these filters are missing,
114-
an error with the possible choices will be raised.
128+
## Subsets
129+
130+
### Errors on mixed subsets
131+
132+
In order to return consistent results, most methods must work on an homogeneous
133+
subset of data. In case multiple subsets are mixed (for example Expert and
134+
Unsmoothed datasets), proper filters matching the partitioning keys must be
135+
given. If these filters are missing, an error with the possible choices will be
136+
raised.
115137

116138
```{code-cell}
117139
:tags: [raises-exception]
@@ -123,7 +145,32 @@ ds.to_netcdf(f'{path}/SWOT_L2_LR_SSH_Unsmoothed_001_012_20240101T030000_20240101
123145
fc.variables_info()
124146
```
125147

148+
### Compatibility matrix
149+
150+
The following table summarizes which methods can work on mixed data. Most
151+
methods need homogeneous data and will require filtering the subset.
152+
153+
| Method | Works on mixed data ? |
154+
|--------------------|-----------------------|
155+
| ``list_files`` | Yes |
156+
| ``variables_info`` | No |
157+
| ``filter_values`` | No |
158+
| ``query`` | No |
159+
| ``map`` | No |
160+
161+
### Listing subsets
162+
163+
Subsets that are on the file system can be listed using the
164+
{meth}`subsets <fcollections.core.FilesDatabase.subsets>` property.
165+
166+
```{code-cell}
167+
fc.subsets
168+
```
169+
170+
One of the returned choices must be selected and used as a filter to work on an
171+
homogeneous dataset.
172+
126173
```{code-cell}
127-
# Use the enumeration name for filtering
174+
# Use the enumeration name for filtering a specific subset
128175
fc.variables_info(subset='Expert')
129176
```

src/fcollections/core/_listing.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ def children(self) -> tp.Iterator[INode]:
264264
The child nodes, either files or folders
265265
"""
266266

267+
@abc.abstractmethod
268+
def clear(self):
269+
"""Clear child nodes."""
270+
267271

268272
class FileNode(INode):
269273
"""File node of a file system tree."""
@@ -281,6 +285,9 @@ def children(self) -> tp.Iterator[INode]:
281285
"""
282286
return []
283287

288+
def clear(self):
289+
pass
290+
284291

285292
class DirNode(INode):
286293
"""Directory node of a file system tree.
@@ -326,6 +333,9 @@ def children(self) -> tp.Iterable[INode]:
326333
self._children = list(self._compute_children())
327334
return self._children
328335

336+
def clear(self):
337+
self._children = None
338+
329339
def _compute_children(self) -> tp.Iterator[INode]:
330340
# return list of FileNode or DirNode instances
331341
# Block of code extracted from fsspec and simplified (no topbottom
@@ -798,6 +808,7 @@ def walk(node: INode, visitor: IVisitor) -> tp.Iterator[tp.Any]:
798808
return
799809

800810
for child in node.children():
811+
logger.debug("child %s", child.name)
801812
yield from walk(child, visitor.advance(result))
802813

803814

@@ -929,6 +940,7 @@ def discover(
929940
# to modify the Layout interface
930941
layout.set_filters(**filters)
931942

943+
self.root_node.clear()
932944
if enable_layouts:
933945
logger.debug("Using layouts to speed up listing")
934946
visitor = LayoutVisitor(self.layouts, stat_fields)

src/fcollections/implementations/_l2_lr_ssh.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232

3333
SWOT_L2_PATTERN = re.compile(
34-
r"SWOT_(?P<level>.*)_LR_SSH_(?P<subset>.*)_(?P<cycle_number>\d{3})_(?P<pass_number>\d{3})_"
34+
r"SWOT_L2_LR_SSH_(?P<subset>.*)_(?P<cycle_number>\d{3})_(?P<pass_number>\d{3})_"
3535
r"(?P<time>\d{8}T\d{6}_\d{8}T\d{6})_(?P<version>P[I|G][A-Z]\d{1}_\d{2}).nc"
3636
)
3737

@@ -519,15 +519,12 @@ def __init__(self):
519519
FileNameFieldPeriod(
520520
"time", "%Y%m%dT%H%M%S", "_", description=DESCRIPTIONS["time"]
521521
),
522-
FileNameFieldEnum(
523-
"level", ProductLevel, description=DESCRIPTIONS["level"]
524-
),
525522
FileNameFieldEnum(
526523
"subset", ProductSubset, description=DESCRIPTIONS["subset"]
527524
),
528525
L2VersionField("version"),
529526
],
530-
generation_string="SWOT_{level!f}_LR_SSH_{subset!f}_{cycle_number:>03d}_{pass_number:>03d}_{time!f}_{version!f}.nc",
527+
generation_string="SWOT_L2_LR_SSH_{subset!f}_{cycle_number:>03d}_{pass_number:>03d}_{time!f}_{version!f}.nc",
531528
)
532529

533530

@@ -544,17 +541,17 @@ def __init__(self):
544541
AVISO_L2_LR_SSH_LAYOUT = Layout(
545542
[
546543
FileNameConvention(
547-
re.compile(r"(?P<version>P[I|G][A-Z]\d{1})"),
544+
re.compile(r"^(?P<version>P[I|G][A-Z]\d{1})$"),
548545
[_ADAPTED_L2_FIELD],
549546
"{version!f}",
550547
),
551548
FileNameConvention(
552-
re.compile(r"(?P<subset>.*)"),
549+
re.compile(r"^(?P<subset>.*)$"),
553550
[FileNameConventionSwotL2().get_field("subset")],
554551
"{subset!f}",
555552
),
556553
FileNameConvention(
557-
re.compile(r"cycle_(?P<cycle_number>\d{3})"),
554+
re.compile(r"^cycle_(?P<cycle_number>\d{3})$"),
558555
[FileNameConventionSwotL2().get_field("cycle_number")],
559556
"cycle_{cycle_number:0>3d}",
560557
),
@@ -572,7 +569,7 @@ class BasicNetcdfFilesDatabaseSwotLRL2(FilesDatabase, PeriodMixin):
572569
sort_keys = "time"
573570

574571
# These keys determines an homogeneous subset
575-
unmixer = SubsetsUnmixer(partition_keys=["level", "subset"])
572+
unmixer = SubsetsUnmixer(partition_keys=["subset"])
576573
# We expect multiple versions in an homogeneous subset. Only one half orbit
577574
# record is tolerated so we deduplicate the multiple version with an
578575
# autopick

tests/core/test_listing.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ def test_file_node_no_children():
4949
assert len(node.children()) == 0
5050

5151

52+
def test_dir_node_clear():
53+
node = DirNode("foo", {"name": "foo"}, MemoryFileSystem(), 0)
54+
node.children()
55+
assert node._children is not None
56+
node.clear()
57+
assert node._children is None
58+
59+
5260
@pytest.fixture(scope="session")
5361
def filepaths() -> list[str]:
5462
# The files that we will use to test the listing and filtering using the

tests/implementations/collections/test_l2_lr_ssh.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,3 +678,29 @@ def test_list_swot_lr_l2_layout(
678678
assert set(map(tuple, actual.to_numpy())) == set(
679679
map(tuple, expected.to_numpy())
680680
)
681+
682+
683+
def test_subsets(l2_lr_ssh_dir_empty_files_layout: Path):
684+
685+
expected = [
686+
{"subset": ProductSubset.Basic},
687+
{"subset": ProductSubset.Unsmoothed},
688+
{"subset": ProductSubset.Expert},
689+
]
690+
691+
db = NetcdfFilesDatabaseSwotLRL2(l2_lr_ssh_dir_empty_files_layout)
692+
assert len(db.subsets) == len(expected)
693+
assert all([x in expected for x in db.subsets])
694+
695+
696+
def test_subsets_flat(l2_lr_ssh_dir_empty_files: Path):
697+
698+
expected = [
699+
{"subset": ProductSubset.Basic},
700+
{"subset": ProductSubset.Unsmoothed},
701+
{"subset": ProductSubset.Expert},
702+
]
703+
704+
db = NetcdfFilesDatabaseSwotLRL2(l2_lr_ssh_dir_empty_files)
705+
assert len(db.subsets) == len(expected)
706+
assert all([x in expected for x in db.subsets])

tests/implementations/test_docstrings.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def sst_db(tmpdir, scope="module"):
4343
"cycle_number",
4444
"pass_number",
4545
"time",
46-
"level",
4746
"subset",
4847
],
4948
),
@@ -86,7 +85,6 @@ def test_list_files_docstring(db, keywords, request):
8685
"cycle_number",
8786
"pass_number",
8887
"time",
89-
"level",
9088
"subset",
9189
"right_swath",
9290
],
@@ -135,7 +133,7 @@ def test_query_docstring(db, keywords, request):
135133
@pytest.mark.parametrize(
136134
"db, keywords",
137135
[
138-
("swot_lr_l2_db", ["metadata describing the variables", "level", "subset"]),
136+
("swot_lr_l2_db", ["metadata describing the variables", "subset"]),
139137
("swot_lr_l3_db", ["metadata describing the variables"]),
140138
("nadir_db", ["metadata describing the variables", "resolution", "sensor"]),
141139
("sst_db", ["metadata describing the variables"]),

0 commit comments

Comments
 (0)