Repository: dandi/dandi-cli

Issue:
codespell the code base
works like charm:
```shell
$> codespell dandi
dandi/_version.py:280: unparseable ==> unparsable
dandi/download.py:100: Activites ==> Activities
dandi/models.py:438: alpha-numeric ==> alphanumeric
dandi/utils.py:112: tollerance ==> tolerance
dandi/utils.py:121: tollerance ==> tolerance
dandi/utils.py:133: tollerance ==> tolerance
dandi/cli/cmd_organize.py:52: fils ==> fills, files, file
dandi/cli/base.py:89: parallize ==> parallelize
dandi/tests/fixtures.py:122: commitish ==> committish
dandi/tests/fixtures.py:124: commitish ==> committish
dandi/tests/test_utils.py:98: tollerance ==> tolerance
dandi/tests/test_utils.py:107: tollerance ==> tolerance
dandi/tests/test_utils.py:108: tollerance ==> tolerance
dandi/tests/test_utils.py:111: tollerance ==> tolerance
dandi/tests/test_utils.py:112: tollerance ==> tolerance
dandi/tests/test_utils.py:120: tollerance ==> tolerance
```
so ideally we should 
- [ ] add setup.py to run
- [x] exclude borrowed `dandi/_version.py`
- [x] add it to pre-commit automations, probably as an error (without rewrite) if possible so conscious fixing up happened
- [x] add to linting checks so if some new typo is introduced (someone did not use) - would fail

Discussion:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5cbea257..4e3fa019 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,3 +18,8 @@ repos:
     hooks:
     -   id: isort
         exclude: ^(dandi/_version\.py|dandi/due\.py|versioneer\.py)$
+-   repo: https://github.com/codespell-project/codespell
+    rev: v2.0.0
+    hooks:
+    -   id: codespell
+        exclude: ^(dandi/_version\.py|dandi/due\.py|versioneer\.py)$
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b805b78..6f70c6b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -532,7 +532,7 @@ of a new DANDI API, and improving DX (Development eXperience).
 ## Fixed
 - Caching of multiple functions re-using the same cache -- it could
   have resulted in our case neural data types returned where full metadata
-  was requested, or vise vera
+  was requested, or vice versa
 - Tolerate outdated (before 2.0.0) etelemetry
 
 
diff --git a/dandi/cli/base.py b/dandi/cli/base.py
index c838d0c3..81c38521 100644
--- a/dandi/cli/base.py
+++ b/dandi/cli/base.py
@@ -86,7 +86,7 @@ def devel_debug_option():
     return devel_option(
         "--devel-debug",
         help="For development: do not use pyout callbacks, do not swallow"
-        " exception, do not parallize",
+        " exception, do not parallelize",
         default=False,
         is_flag=True,
     )
diff --git a/dandi/cli/cmd_organize.py b/dandi/cli/cmd_organize.py
index 23579f65..e04e5181 100644
--- a/dandi/cli/cmd_organize.py
+++ b/dandi/cli/cmd_organize.py
@@ -49,7 +49,7 @@ def organize(
     """(Re)organize files according to the metadata.
 
     The purpose of this command is to take advantage of metadata contained in
-    the .nwb fils to provide datasets with consistently named files, so their
+    the .nwb files to provide datasets with consistently named files, so their
     naming reflects data they contain. In addition it will also populate
     the dataset level descriptor file dandiset.yaml with some of the
     collected metadata and statistics (e.g. number_of_cells).
diff --git a/dandi/download.py b/dandi/download.py
index be1c09cd..b5bb4cc2 100644
--- a/dandi/download.py
+++ b/dandi/download.py
@@ -97,7 +97,7 @@ def download_generator(
     (as identified by URL)
 
     This function is a generator which would yield records on ongoing activities.
-    Activites include traversal of the remote resource (DANDI archive), download of
+    Activities include traversal of the remote resource (DANDI archive), download of
     individual assets while yielding records (TODO: schema) while validating their
     checksums "on the fly", etc.
 
diff --git a/dandi/models.py b/dandi/models.py
index 2901113c..07c47616 100644
--- a/dandi/models.py
+++ b/dandi/models.py
@@ -435,7 +435,7 @@ class EthicsApproval(DandiBaseModel):
     identifier: Identifier = Field(
         nskey="schema",
         title="Approved protocol identifier",
-        description="Approved Protocol identifier, often a number or alpha-numeric string.",
+        description="Approved Protocol identifier, often a number or alphanumeric string.",
     )
     contactPoint: ContactPoint = Field(
         description="Information about the ethics approval committee.", nskey="schema"
diff --git a/dandi/utils.py b/dandi/utils.py
index 4b5fb20f..0184e33c 100644
--- a/dandi/utils.py
+++ b/dandi/utils.py
@@ -109,7 +109,7 @@ def get_utcnow_datetime(microseconds=True):
         return ret.replace(microsecond=0)
 
 
-def is_same_time(*times, tollerance=1e-6, strip_tzinfo=False):
+def is_same_time(*times, tolerance=1e-6, strip_tzinfo=False):
     """Helper to do comparison between time points
 
     Time zone information gets stripped
@@ -118,8 +118,8 @@ def is_same_time(*times, tollerance=1e-6, strip_tzinfo=False):
 
     Parameters
     ----------
-    tollerance: float, optional
-      Seconds of difference between times to tollerate.  By default difference
+    tolerance: float, optional
+      Seconds of difference between times to tolerate.  By default difference
       up to a microsecond is ok
     """
     assert len(times) >= 2
@@ -130,10 +130,10 @@ def is_same_time(*times, tollerance=1e-6, strip_tzinfo=False):
     ]
 
     # we need to have all pairs
-    tollerance_dt = datetime.timedelta(seconds=tollerance)
+    tolerance_dt = datetime.timedelta(seconds=tolerance)
     return all(
         # if we subtract from smaller - we get negative days etc
-        (t1 - t2 if t1 > t2 else t2 - t1) <= tollerance_dt
+        (t1 - t2 if t1 > t2 else t2 - t1) <= tolerance_dt
         for (t1, t2) in itertools.combinations(norm_times, 2)
     )
 
diff --git a/setup.cfg b/setup.cfg
index c56405a8..6fb9ffc7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -115,3 +115,6 @@ versionfile_source = dandi/_version.py
 versionfile_build = dandi/_version.py
 tag_prefix =
 parentdir_prefix =
+
+[codespell]
+skip = dandi/_version.py,dandi/due.py,versioneer.py
diff --git a/tox.ini b/tox.ini
index 2d82e738..2dbc32c1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,8 +14,10 @@ commands =
 [testenv:lint]
 skip_install = true
 deps =
+    codespell~=2.0
     flake8
 commands =
+    codespell dandi setup.py
     flake8 --config=setup.cfg {posargs} dandi setup.py
 
 [pytest]


Test patch:
diff --git a/dandi/tests/fixtures.py b/dandi/tests/fixtures.py
index b13cfe89..1fab54e4 100644
--- a/dandi/tests/fixtures.py
+++ b/dandi/tests/fixtures.py
@@ -119,9 +119,9 @@ def clirunner():
     yield CliRunner()
 
 
-def get_gitrepo_fixture(url, commitish=None, scope="session"):
+def get_gitrepo_fixture(url, committish=None, scope="session"):
 
-    if commitish:
+    if committish:
         raise NotImplementedError()
 
     @pytest.fixture(scope=scope)
diff --git a/dandi/tests/test_utils.py b/dandi/tests/test_utils.py
index 92c5d8cf..235c4912 100644
--- a/dandi/tests/test_utils.py
+++ b/dandi/tests/test_utils.py
@@ -95,7 +95,7 @@ def test_times_manipulations():
     # Test comparison and round-trips
     assert is_same_time(t0, t0_isoformat, t0_str)
     assert is_same_time(t0, t0_str)
-    assert is_same_time(t0, t0_str, tollerance=0)  # exactly the same
+    assert is_same_time(t0, t0_str, tolerance=0)  # exactly the same
     assert t0_str != t0_isoformat  # " " vs "T"
 
     time.sleep(0.001)  # so there is a definite notable delay, in particular for Windows
@@ -104,12 +104,12 @@ def test_times_manipulations():
     assert is_same_time(t1, t1_epoch)
     # We must not consume more than half a second between start of this test
     # and here
-    assert is_same_time(t0, t1, tollerance=0.5)
-    assert is_same_time(t1, t0, tollerance=0.5)
+    assert is_same_time(t0, t1, tolerance=0.5)
+    assert is_same_time(t1, t0, tolerance=0.5)
     # but must not be exactly the same unless we are way too fast or disregard
     # milliseconds
-    assert not is_same_time(t0, t1, tollerance=0)
-    assert is_same_time(t0, t1_epoch + 100, tollerance=101)
+    assert not is_same_time(t0, t1, tolerance=0)
+    assert is_same_time(t0, t1_epoch + 100, tolerance=101)
 
 
 @pytest.mark.parametrize(
@@ -117,7 +117,7 @@ def test_times_manipulations():
 )
 def test_time_samples(t):
     assert is_same_time(
-        ensure_datetime(t), "2018-09-27 00:29:17-00:00", tollerance=0
+        ensure_datetime(t), "2018-09-27 00:29:17-00:00", tolerance=0
     )  # exactly the same