Repository: milesgranger/cramjam (language: python)

Issue:
Handle numpy import error if numpy not installed.
Problem
---
It was my expectation to _optionally_ support numpy input/output but would also work fine if the user only wanted to make use of `bytes`/`bytearray`/`Buffer`/`File` objs. This however does not appear to be the case

Example with an environment without numpy installed
```python
>>> import cramjam
>>> compressed = cramjam.snappy.compress(b'bytes')
>>> out = cramjam.snappy.decompress(compressed)
thread '<unnamed>' panicked at 'Failed to import numpy module', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/numpy-0.13.1/src/npyffi/mod.rs:16:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
pyo3_runtime.PanicException: Failed to import numpy module
>>> 
```

Solution?
---
Not sure, but hoping there is a way to not force a numpy install if the user doesn't want it.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 5427405a..e758f6d6 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -138,6 +138,11 @@ jobs:
         pip install cramjam --no-index --find-links dist --force-reinstall
         pip install -r dev-requirements.txt
         make test
+    - name: Test no numpy installed works
+      if: matrix.target == 'x86_64-unknown-linux-gnu'
+      run: |
+        pip uninstall numpy -y
+        python -m pytest tests/test_no_numpy.py
     - name: Upload wheels
       uses: actions/upload-artifact@v2
       with:
diff --git a/Cargo.toml b/Cargo.toml
index 8a383973..c27901c4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cramjam"
-version = "2.3.0"
+version = "2.3.1"
 authors = ["Miles Granger <miles59923@gmail.com>"]
 edition = "2018"
 license-file = "LICENSE"
diff --git a/src/lib.rs b/src/lib.rs
index 1304cc79..6e74c69f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -81,15 +81,15 @@ pub enum BytesType<'a> {
     /// `bytearray`
     #[pyo3(transparent, annotation = "bytearray")]
     ByteArray(RustyPyByteArray<'a>),
-    /// `numpy.array` with `dtype=np.uint8`
-    #[pyo3(transparent, annotation = "numpy")]
-    NumpyArray(RustyNumpyArray<'a>),
     /// [`cramjam.File`](io/struct.RustyFile.html)
     #[pyo3(transparent, annotation = "File")]
     RustyFile(&'a PyCell<RustyFile>),
     /// [`cramjam.Buffer`](io/struct.RustyBuffer.html)
     #[pyo3(transparent, annotation = "Buffer")]
     RustyBuffer(&'a PyCell<RustyBuffer>),
+    /// `numpy.array` with `dtype=np.uint8`
+    #[pyo3(transparent, annotation = "numpy")]
+    NumpyArray(RustyNumpyArray<'a>)
 }
 
 impl<'a> AsBytes for BytesType<'a> {


Test patch:
diff --git a/tests/test_no_numpy.py b/tests/test_no_numpy.py
new file mode 100644
index 00000000..82ed9e51
--- /dev/null
+++ b/tests/test_no_numpy.py
@@ -0,0 +1,23 @@
+import pytest
+import cramjam
+
+
+@pytest.mark.parametrize("obj", (bytes, bytearray, cramjam.Buffer, cramjam.File))
+@pytest.mark.parametrize(
+    "variant_str", ("snappy", "brotli", "lz4", "gzip", "deflate", "zstd")
+)
+def test_no_numpy_installed(tmpdir, obj, variant_str):
+    """
+    These operations should work even when numpy is not installed
+    """
+    if cramjam.File == obj:
+        data = obj(str(tmpdir.join("tmp.txt")))
+        data.write(b"data")
+        data.seek(0)
+    else:
+        data = obj(b"data")
+
+    variant = getattr(cramjam, variant_str)
+    compressed = variant.compress(data)
+    decompressed = variant.decompress(compressed)
+    assert decompressed.read() == b"data"