Repository: pyjanitor-devs/pyjanitor

Issue:
[ENH/BUG] Assertions used in place of raising errors in various functions
* `concatenate_columns`
* `deconcatenate_column`
* `DataDescription`
* `clean_names`
* `encode_categorical`
* `label_encode`
* `fill_empty`



Discussion:
@zbarry Should **all** assertions raise `JanitorError` instead?
So far, `JanitorError` has been used as a catch-all. We can definitely start there, but maybe some judicious use of `ValueError` could be helpful. 
@ericmjl sure. So should all assert statements in the codebase raise exceptions instead? Or are there only certain asserts that should raise?

Makes sense to use `ValueError` at times. `JanitorError` makes it abundantly clear where the error is originating, but `ValueError` is more helpful.
Yes, I think the assertions should be changed to errors being raised.
Got it, I can take this issue on! I'll report back if I find some pattern of use that might warrant specific exceptions etc
Alrighty! Thanks @hectormz! :smile:
@hectormz - I’ve been replacing assertions with standard Python errors, yeah. I haven’t used JanitorError myself afaicr

Resolving patch:
diff --git a/janitor/functions.py b/janitor/functions.py
--- a/janitor/functions.py
+++ b/janitor/functions.py
@@ -363,16 +363,14 @@ def encode_categorical(
     """
     if isinstance(column_names, list) or isinstance(column_names, tuple):
         for col in column_names:
-            assert col in df.columns, JanitorError(
-                "{col} missing from dataframe columns!".format(col=col)
-            )
+            if col not in df.columns:
+                raise JanitorError(f"{col} missing from dataframe columns!")
             df[col] = pd.Categorical(df[col])
     elif isinstance(column_names, str):
-        assert column_names in df.columns, JanitorError(
-            "{column_names} missing from dataframe columns!".format(
-                column_names=column_names
+        if column_names not in df.columns:
+            raise JanitorError(
+                f"{column_names} missing from dataframe columns!"
             )
-        )
         df[column_names] = pd.Categorical(df[column_names])
     else:
         raise JanitorError(
@@ -421,14 +419,12 @@ def label_encode(
     le = LabelEncoder()
     if isinstance(column_names, list) or isinstance(column_names, tuple):
         for col in column_names:
-            assert col in df.columns, JanitorError(
-                f"{col} missing from column_names"
-            )  # noqa: E501
+            if col not in df.columns:
+                raise JanitorError(f"{col} missing from column_names")
             df[f"{col}_enc"] = le.fit_transform(df[col])
     elif isinstance(column_names, str):
-        assert column_names in df.columns, JanitorError(
-            f"{column_names} missing from column_names"
-        )  # noqa: E501
+        if column_names not in df.columns:
+            raise JanitorError(f"{column_names} missing from column_names")
         df[f"{column_names}_enc"] = le.fit_transform(df[column_names])
     else:
         raise JanitorError(
@@ -836,14 +832,14 @@ def fill_empty(
     """
     if isinstance(column_names, list) or isinstance(column_names, tuple):
         for col in column_names:
-            assert (
-                col in df.columns
-            ), "{col} missing from dataframe columns!".format(col=col)
+            if col not in df.columns:
+                raise JanitorError(f"{col} missing from dataframe columns!")
             df[col] = df[col].fillna(value)
     else:
-        assert (
-            column_names in df.columns
-        ), "{col} missing from dataframe columns!".format(col=column_names)
+        if column_names not in df.columns:
+            raise JanitorError(
+                f"{column_names} missing from dataframe columns!"
+            )
         df[column_names] = df[column_names].fillna(value)
 
     return df
@@ -1050,10 +1046,12 @@ def deconcatenate_column(
 
         # TODO: I suspect this should become a test
         # instead of a defensive check?
-        assert (
-            len(df.columns)
-            == len(df_original.columns) + len(new_column_names) - 1
-        ), "number of columns after deconcatenation is incorrect"
+        if len(df.columns) != (
+            len(df_original.columns) + len(new_column_names) - 1
+        ):
+            raise JanitorError(
+                "number of columns after deconcatenation is incorrect"
+            )
     return df
 
 
diff --git a/janitor/io.py b/janitor/io.py
--- a/janitor/io.py
+++ b/janitor/io.py
@@ -4,6 +4,7 @@
 
 import pandas as pd
 
+from .errors import JanitorError
 from .utils import deprecated_alias
 
 
@@ -23,8 +24,10 @@ def read_csvs(
         original pandas `read_csv`.
     """
     # Sanitize input
-    assert files_path is not None
-    assert len(files_path) != 0
+    if files_path is None:
+        raise JanitorError("`None` provided for `files_path`")
+    if len(files_path) == 0:
+        raise JanitorError("0 length `files_path` provided")
 
     # Read the csv files
     # String to file/folder or file pattern provided
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -26,9 +26,8 @@ def generate_long_description() -> str:
 
     # Find pypi-doc comments in README
     indices = [m.start() for m in re.finditer(".. pypi-doc", readme)]
-    assert (
-        len(indices) % 2 == 0
-    ), "Odd number of `.. pypi-doc` comments in README"
+    if len(indices) % 2 != 0:
+        raise Exception("Odd number of `.. pypi-doc` comments in README")
 
     # Loop through pairs of comments and save text between pairs
     long_description = ""


Test patch:
diff --git a/tests/functions/test_encode_categorical.py b/tests/functions/test_encode_categorical.py
--- a/tests/functions/test_encode_categorical.py
+++ b/tests/functions/test_encode_categorical.py
@@ -18,14 +18,14 @@ def test_encode_categorical(df):
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_encode_categorical_missing_column(df):
-    with pytest.raises(AssertionError):
+    with pytest.raises(JanitorError):
         df.encode_categorical("aloha")
 
 
 @pytest.mark.functions
 @given(df=df_strategy())
 def test_encode_categorical_missing_columns(df):
-    with pytest.raises(AssertionError):
+    with pytest.raises(JanitorError):
         df.encode_categorical(["animals@#$%^", "cities", "aloha"])
 
 
diff --git a/tests/functions/test_label_encode.py b/tests/functions/test_label_encode.py
--- a/tests/functions/test_label_encode.py
+++ b/tests/functions/test_label_encode.py
@@ -14,7 +14,7 @@ def test_single_column_label_encode():
 
 @pytest.mark.functions
 def test_single_column_fail_label_encode():
-    with pytest.raises(AssertionError):
+    with pytest.raises(JanitorError):
         pd.DataFrame(
             {"a": ["hello", "hello", "sup"], "b": [1, 2, 3]}
         ).label_encode(