[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[WIP] Refactor arrays in v3 - #1589

Closed
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb
Closed

[WIP] Refactor arrays in v3 #1589
d-v-b wants to merge 32 commits into
zarr-developers:v3from
d-v-b:v3_dvb

Conversation

@d-v-b

@d-v-bd-v-b commented Dec 5, 2023

Copy link
Copy Markdown
Contributor

The goal of this PR is to create a user-facing array class that implements the basic attributes of a numpy array and which abstracts over the structural differences between v2 and v3 arrays. This is very heavily based on the zarrita approach, but I'm modifying things in a lot of places.

Design goals:

  • An array class with .shape, .ndim, .size, etc attributes that are consistent with the array api attributes. I don't plan on adding .device or .mT unless there's an acute need for that, but that's not a deeply considered perspective. The array class will support __getitem__ and __setitem__ for getting and setting numeric data like a normal numpy array, and for attributes as well.
  • The array should have a .metadata attribute that expresses array metadata according to a zarr array specification. The data in .metadata should correspond to the contents of the stored array metadata document. The .metadata class should not perform any behavior besides JSON ser/deserialization.
  • IO routines will execute blocking calls to async IO routines.
    • The IO behavior of the array should be configurable, e.g. w.r.t caching, writing empty chunks, concurrency limits, locking, etc.
  • All of the above should work similarly for arrays based on zarr specs v2 and v3.
  • All of the above should be handled by separate APIs, that together support the array interface.

@pep8speaks

pep8speaks commented Dec 5, 2023

Copy link
Copy Markdown

Hello @d-v-b! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 11:1: E302 expected 2 blank lines, found 1

Line 73:20: E203 whitespace before ':'
Line 73:46: E203 whitespace before ':'
Line 73:72: E203 whitespace before ':'
Line 205:20: E203 whitespace before ':'
Line 205:46: E203 whitespace before ':'
Line 205:72: E203 whitespace before ':'

Line 320:51: E203 whitespace before ':'

Line 90:19: W291 trailing whitespace

Line 47:101: E501 line too long (114 > 100 characters)

Line 48:101: E501 line too long (142 > 100 characters)

Line 173:4: W291 trailing whitespace

Line 302:44: E203 whitespace before ':'
Line 314:30: E203 whitespace before ':'

Comment last updated at 2024-01-07 18:00:00 UTC

d-v-band others added 2 commits December 6, 2023 00:02
Co-authored-by: Joe Hamman <jhamman1@gmail.com>
@joshmoore

Copy link
Copy Markdown
Member

Is the base branch here intended to be main?

@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

@d-v-b
d-v-b changed the base branch from main to v3December 6, 2023 10:46
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

Is the base branch here intended to be main?

nope! It should be against https://github.com/zarr-developers/zarr-python/tree/v3. I will see how fixable that is.

very fixable, it turns out. thanks for spotting this @joshmoore!

@joshmoore

Copy link
Copy Markdown
Member

👍 np

Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/v3.py Outdated
Comment threadzarr/v3/array/base.py Outdated
…ing metadata for v2 and v3; rename zarray to array
@d-v-b

d-v-b commented Dec 6, 2023

Copy link
Copy Markdown
ContributorAuthor

@normanrz if you have the patience for it, I wonder if you could check out my branch and help me understand some tests that are failing due to .attrs deserialization issues? Here's an example traceback. It's certainly due to something I broke, but I want to understand how to un-break it :)

Here's an example traceback:
python
store = MemoryStore('memory://4904285632')
@pytest.mark.asyncio
async def test_resize(store: Store):
data = np.zeros((16, 18), dtype="uint16")
a = await AsyncArray.create(
store / "resize",
shape=data.shape,
chunk_shape=(10, 10),
dtype=data.dtype,
chunk_key_encoding=("v2", "."),
fill_value=1,
)
await _AsyncArrayProxy(a)[:16, :18].set(data)
> assert await store.get_async("resize/0.0") is not None
E assert None is not None
zarr/tests/test_codecs_v3.py:935: AssertionError
__________________________________________________________________ test_update_attributes_array __________________________________________________________________
+ Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 341, in from_call
| result: Optional[TResult] = func()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 262, in <lambda>
| lambda: ihook(item=item, **kwds), when=when, reraise=reraise
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 177, in pytest_runtest_call
| raise e
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 169, in pytest_runtest_call
| item.runtest()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1792, in runtest
| self.ihook.pytest_pyfunc_call(pyfuncitem=self)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 493, in __call__
| return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 115, in _hookexec
| return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 152, in _multicall
| return outcome.get_result()
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 114, in get_result
| raise exc.with_traceback(exc.__traceback__)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 77, in _multicall
| res = hook_impl.function(*args)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
| result = testfunction(**testargs)
| File "/Users/bennettd/dev/zarr-python/zarr/tests/test_codecs_v3.py", line 983, in test_update_attributes_array
| a = Array.open(store / "update_attributes")
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 455, in open
| async_array = sync(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 67, in sync
| raise return_result
| File "/Users/bennettd/dev/zarr-python/zarr/v3/sync.py", line 30, in _runner
| result_box[0] = await coro
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 218, in open
| return cls.from_json(
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 197, in from_json
| metadata = ArrayMetadata.from_json(zarr_json)
| File "/Users/bennettd/dev/zarr-python/zarr/v3/array/v3.py", line 101, in from_json
| return make_cattr().structure(zarr_json, cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 58, in structure_ArrayMetadata
| if errors: raise __c_cve('While structuring ' + 'ArrayMetadata', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring ArrayMetadata (1 sub-exception)
+-+---------------- 1 ----------------
| Exception Group Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.array.v3.ArrayMetadata-68>", line 30, in structure_ArrayMetadata
| res['codecs'] = __c_structure_codecs(o['codecs'], __c_type_codecs)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 545, in _structure_list
| raise IterableValidationError(
| cattrs.errors.IterableValidationError: While structuring list[zarr.v3.metadata.CodecMetadata] (1 sub-exception)
| Structuring class ArrayMetadata @ attribute codecs
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'
| | During handling of the above exception, another exception occurred:
| | Exception Group Traceback (most recent call last):
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 535, in _structure_list
| res.append(handler(e, elem_type))
| File "/Users/bennettd/dev/zarr-python/zarr/v3/common.py", line 57, in _structure_codec_metadata
| return converter.structure(d, codec_metadata_cls)
| File "/Users/bennettd/dev/zarr-python/.venv/lib/python3.9/site-packages/cattrs/converters.py", line 334, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 20, in structure_BytesCodecMetadata
| except Exception as exc: raise __c_cve('While structuring ' + 'BytesCodecMetadata', [exc], __cl)
| cattrs.errors.ClassValidationError: While structuring BytesCodecMetadata (1 sub-exception)
| Structuring list[zarr.v3.metadata.CodecMetadata] @ index 0
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure zarr.v3.codecs.bytes.BytesCodecMetadata-68>", line 17, in structure_BytesCodecMetadata
| return __cl(
| TypeError: __init__() got an unexpected keyword argument 'name'

d-v-band others added 2 commits December 6, 2023 14:32
@d-v-b

d-v-b commented Dec 9, 2023

Copy link
Copy Markdown
ContributorAuthor

an update about this effort:

  • I have unified the chunk read / write APIs for v2 and v3 arrays. The strategy here was to remove the _write_chunk, _read_chunk methods from both v2 and v3 classes and combine the logic into free-standing functions, which the v2 and v3 array classes can call. See the write_chunk / read_chunk functions, which, in this branch, are defined in chunk.py. Ripping methods off of the array classes ends up swelling the function signature of those routines, but it also exposes the "true" signature of the function, which at least for me is useful for understanding the code and ultimately refactoring.
  • I am starting to apply this same surgical technique to the codecs. In v3, a CodecPipeline is initialized with a RuntimeConfiguration, but this pipeline will be used by an array that already has a RuntimeConfiguration. See here. The same piece of data appearing in two places in a function call is a request for simplification. So I removed the RuntimeConfiguration from the construction of CodecPipeline, and as a consequence all of the codec methods like encode and decode require a config argument, which is an instance of RuntimeConfiguration. As with the chunk io story above, this makes the function signatures of these routines bigger, but also more explicit about their true parameterization. In particular, I think only the sharding routines use the RuntimeConfiguration at all, so it might make sense to make the RuntimeConfiguration a keyword-only argument that defaults to None.

I think it's going well, but I don't have any outlook on when this will be done, so don't consider this effort a blocker to immediate v3 efforts.

@jhammanjhamman added this to the 3.0.0.alpha milestone Apr 6, 2024
@jhamman

Copy link
Copy Markdown
Member

@d-v-b - I think @normanrz's PR (#1857) superseded this effort. Feel free to re-open if I have that wrong.

@jhammanjhamman closed this May 17, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants

@d-v-b@pep8speaks@joshmoore@jhamman@normanrz