Repository: zarr-developers/zarr-python

Issue:
Explicitely specified dimension_separator conflicts with default
When using convenience methods with a path/url, specifying the dimension separator causes an error.

#### Minimal, reproducible code sample, a copy-pastable example if possible

```python
import numpy as np
import zarr
data = np.arange(6).reshape((3,2))
url = "/tmp/test.zarr"
zarr.save_array(url, data, storage_options=None, dimension_separator="/")
# >> ValueError: Specified dimension_separtor: /conflicts with store's separator: None
```

#### Problem description

`normalize_store` turns the path/url string into a store object with `_dimension_separator` defaulting to None. The check for conflicts does not take into account that any value other than None should override a default of None.

#### Version and installation information

Please provide the following:

* Value of ``zarr.__version__``: 2.8.3
* Value of ``numcodecs.__version__``: 0.8.0
* Version of Python interpreter: 3.8.10
* Operating system (Linux/Windows/Mac): Linux
* How Zarr was installed (e.g., "using pip into virtual environment", or "using conda"): using pip in conda environment

Also, if you think it might be relevant, please provide the output from ``pip freeze`` or
``conda env export`` depending on which was used to install Zarr.


Discussion:
pep8speaks: Hello @joshmoore! Thanks for opening this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:



* In the file [`zarr/tests/test_convenience.py`](https://github.com/zarr-developers/zarr-python/blob/699730714b169ec80633cb8daa12ff0de75043be/zarr/tests/test_convenience.py):

> [Line 235:35](https://github.com/zarr-developers/zarr-python/blob/699730714b169ec80633cb8daa12ff0de75043be/zarr/tests/test_convenience.py#L235): [E231](https://duckduckgo.com/?q=pep8%20E231) missing whitespace after ','


codecov[bot]: # [Codecov](https://codecov.io/gh/zarr-developers/zarr-python/pull/832?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#832](https://codecov.io/gh/zarr-developers/zarr-python/pull/832?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (6997307) into [master](https://codecov.io/gh/zarr-developers/zarr-python/commit/5b0437537dc6e9067c9627857aacfc7e873439ac?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (5b04375) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master     #832   +/-   ##
=======================================
  Coverage   99.94%   99.94%           
=======================================
  Files          31       31           
  Lines       10681    10687    +6     
=======================================
+ Hits        10675    10681    +6     
  Misses          6        6           
```

| [Impacted Files](https://codecov.io/gh/zarr-developers/zarr-python/pull/832?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [zarr/creation.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/832/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-emFyci9jcmVhdGlvbi5weQ==) | `100.00% <100.00%> (ø)` | |
| [zarr/tests/test\_convenience.py](https://codecov.io/gh/zarr-developers/zarr-python/pull/832/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-emFyci90ZXN0cy90ZXN0X2NvbnZlbmllbmNlLnB5) | `100.00% <100.00%> (ø)` | |


Patch:
diff --git a/requirements_dev_optional.txt b/requirements_dev_optional.txt
index f4f1c59e..e2d5da4a 100644
--- a/requirements_dev_optional.txt
+++ b/requirements_dev_optional.txt
@@ -12,7 +12,7 @@ types-redis
 types-setuptools
 pymongo==3.12.0
 # optional test requirements
-tox==3.24.3
+tox==3.24.4
 coverage
 flake8==3.9.2
 pytest-cov==2.12.1
diff --git a/zarr/creation.py b/zarr/creation.py
index 28f3b7b8..0e2d2041 100644
--- a/zarr/creation.py
+++ b/zarr/creation.py
@@ -126,11 +126,12 @@ def create(shape, chunks=True, dtype=None, compressor='default',
     if dimension_separator is None:
         dimension_separator = getattr(store, "_dimension_separator", None)
     else:
-        if getattr(store, "_dimension_separator", None) != dimension_separator:
+        store_separator = getattr(store, "_dimension_separator", None)
+        if store_separator not in (None, dimension_separator):
             raise ValueError(
                 f"Specified dimension_separtor: {dimension_separator}"
                 f"conflicts with store's separator: "
-                f"{store._dimension_separator}")
+                f"{store_separator}")
     dimension_separator = normalize_dimension_separator(dimension_separator)
 
     # initialize array metadata


Test patch:
diff --git a/zarr/tests/test_convenience.py b/zarr/tests/test_convenience.py
index a5ac40e3..d2bd9103 100644
--- a/zarr/tests/test_convenience.py
+++ b/zarr/tests/test_convenience.py
@@ -18,6 +18,7 @@ from zarr.convenience import (
     open_consolidated,
     save,
     save_group,
+    save_array,
     copy_all,
 )
 from zarr.core import Array
@@ -225,6 +226,17 @@ def test_consolidated_with_chunk_store():
                       chunk_store=chunk_store)
 
 
+@pytest.mark.parametrize("options", (
+    {"dimension_separator": "/"},
+    {"dimension_separator": "."},
+    {"dimension_separator": None},
+))
+def test_save_array_separator(tmpdir, options):
+    data = np.arange(6).reshape((3, 2))
+    url = tmpdir.join("test.zarr")
+    save_array(url, data, **options)
+
+
 class TestCopyStore(unittest.TestCase):
 
     def setUp(self):