Repository: sdv-dev/RDT (language: python)

Issue:
Create `validate_transformer_code_style` function
A function should be implemented to automatically validate the code style of any new Transformer.

## Function Name and Module

The function should be implemented inside `tests/contributing.py` and should be called `validate_transformer_code_style`

## Inputs

The function should accept a single input:

* `transformer (class or str)`: Transformer class or full Python name of the class (e.g. `DatetimeTransformer` or `"rdt.transformers.time.DatetimeTransformer"`)

## Outputs

The function should return a single `bool` value indicating whether the code style is correct or not.

## Behavior

The function should run all the third party code style checkers (`flake8`, `isort`, `pylint`...) as well custom code analysis checks and report whether all of them pass, as well as feedback about why they do not otherwise.

### List of Checks

The following validations must be performed and reported:



Check | Correct If | What does it check
-- | -- | --
flake8 | flake8 check passes | PEP8 standards and basic static code analysis
isort | isort check passes | Imports are properly sorted
pylint | pylint check passes | Static code analysis and programming style
pydocstyle | pydocstyle check passes | Whether the docstrings are written in the right format
Transformer Name | The Transformer name is correct | Transformer name ends with `Transformer` (e.g. `DatetimeTransformer`)
Unit Tests Exist | The unit tests module exists and is located in the right place | The unit tests module should be located called `tests/unit/transformers/<transformer_family>/<data_type>.py`
Unit Test function names | The unit test function names are correct | The unit test function names should be called `test_<method_name>_<test_case_name>`

### Prints to console

The function prints several messages on the console indicating:
* The path to the source file of the Transformer, which is being analyzed.
* A message indicating whether the code style is correct or not.
* A list of things that were validated and whether they were correct or not (list above).

## Usage Examples

### Success

![image](https://user-images.githubusercontent.com/6129283/134681632-082d2a75-ec57-4fbb-a665-87d346b2899d.png)

### External tool failure

![image1](https://user-images.githubusercontent.com/6129283/134681447-45dbad16-9edf-470a-8ca8-77dc90babc30.png)

### Custom validation failure

![image8](https://user-images.githubusercontent.com/6129283/134681759-2018862c-58d5-4e79-adee-202d72c28667.png)



Discussion / hints:
The current format of our `docstrings` follows the [Google style guide](https://google.github.io/styleguide/pyguide.html), we can add `pydocstyle` and setting the `convention` to `google`. 

However there is a bug in their library which produces `D417 Missing arguments description`, which we do have, and occurs all over our `docstrings` and we will have to ignore the `D417` until this issue is fixed. Reference to the issue: https://github.com/PyCQA/pydocstyle/issues/449

Fix patch:
diff --git a/setup.py b/setup.py
index 5494865b5..8230de218 100644
--- a/setup.py
+++ b/setup.py
@@ -73,6 +73,7 @@
     'flake8-print>=4.0.0,<4.1',
     'isort>=4.3.4,<5',
     'pylint>=2.5.3,<3',
+    'flake8-multiline-containers>=0.0.18,<0.1',
     'flake8-pytest-style>=1.5.0,<2',
     'flake8-quotes>=3.3.0,<4',
     'flake8-expression-complexity>=0.0.9,<0.1',


Test patch:
diff --git a/tests/integration/test_hyper_transformer.py b/tests/integration/test_hyper_transformer.py
index 9b73d905b..3575ef523 100644
--- a/tests/integration/test_hyper_transformer.py
+++ b/tests/integration/test_hyper_transformer.py
@@ -317,9 +317,16 @@ def test_hypertransformer_field_transformers_multi_column_fields():
         ('year_str.value', 'month_str.value', 'day_str.value'): DummyTransformerMultiColumn
     }
     expected_transformed = pd.DataFrame({
-        'year#month#day.value': [9.783072e+17, 1.012608e+18, 1.046650e+18],
+        'year#month#day.value': [
+            9.783072e+17,
+            1.012608e+18,
+            1.046650e+18
+        ],
         'year_str.value#month_str.value#day_str.value.value': [
-            9.783072e+17, 1.012608e+18, 1.046650e+18]
+            9.783072e+17,
+            1.012608e+18,
+            1.046650e+18
+        ]
     })
     expected_reversed = data.copy()
 
diff --git a/tests/unit/test_hyper_transformer.py b/tests/unit/test_hyper_transformer.py
index 4b767b4b8..32ae4ee87 100644
--- a/tests/unit/test_hyper_transformer.py
+++ b/tests/unit/test_hyper_transformer.py
@@ -542,8 +542,14 @@ def get_transformed_data(self, drop=False):
         })
 
         if drop:
-            return data.drop(['integer', 'float', 'categorical', 'bool',
-                             'datetime', 'integer.out'], axis=1)
+            return data.drop([
+                'integer',
+                'float',
+                'categorical',
+                'bool',
+                'datetime',
+                'integer.out'
+            ], axis=1)
 
         return data
 
diff --git a/tests/unit/transformers/test_categorical.py b/tests/unit/transformers/test_categorical.py
index a156964bc..615bbeb29 100644
--- a/tests/unit/transformers/test_categorical.py
+++ b/tests/unit/transformers/test_categorical.py
@@ -32,10 +32,24 @@ def test__get_intervals(self):
 
         # Asserts
         expected_intervals = {
-            'foo': (0, 0.5, 0.25, 0.5 / 6),
-            'bar': (0.5, 0.8333333333333333, 0.6666666666666666, 0.05555555555555555),
-            'tar': (0.8333333333333333, 0.9999999999999999, 0.9166666666666666,
-                    0.027777777777777776)
+            'foo': (
+                0,
+                0.5,
+                0.25,
+                0.5 / 6
+            ),
+            'bar': (
+                0.5,
+                0.8333333333333333,
+                0.6666666666666666,
+                0.05555555555555555
+            ),
+            'tar': (
+                0.8333333333333333,
+                0.9999999999999999,
+                0.9166666666666666,
+                0.027777777777777776
+            )
         }
         assert result[0] == expected_intervals
 
@@ -49,11 +63,26 @@ def test_fit(self):
 
         # Asserts
         expected_intervals = {
-            'foo': (0, 0.5, 0.25, 0.5 / 6),
-            'bar': (0.5, 0.8333333333333333, 0.6666666666666666, 0.05555555555555555),
-            'tar': (0.8333333333333333, 0.9999999999999999, 0.9166666666666666,
-                    0.027777777777777776)
+            'foo': (
+                0,
+                0.5,
+                0.25,
+                0.5 / 6
+            ),
+            'bar': (
+                0.5,
+                0.8333333333333333,
+                0.6666666666666666,
+                0.05555555555555555
+            ),
+            'tar': (
+                0.8333333333333333,
+                0.9999999999999999,
+                0.9166666666666666,
+                0.027777777777777776
+            )
         }
+
         assert transformer.intervals == expected_intervals
 
     def test__get_value_no_fuzzy(self):
@@ -127,11 +156,26 @@ def test_reverse_transform_array(self):
 
         # Asserts
         expected_intervals = {
-            'foo': (0, 0.5, 0.25, 0.5 / 6),
-            'bar': (0.5, 0.8333333333333333, 0.6666666666666666, 0.05555555555555555),
-            'tar': (0.8333333333333333, 0.9999999999999999, 0.9166666666666666,
-                    0.027777777777777776)
+            'foo': (
+                0,
+                0.5,
+                0.25,
+                0.5 / 6
+            ),
+            'bar': (
+                0.5,
+                0.8333333333333333,
+                0.6666666666666666,
+                0.05555555555555555
+            ),
+            'tar': (
+                0.8333333333333333,
+                0.9999999999999999,
+                0.9166666666666666,
+                0.027777777777777776
+            )
         }
+
         assert transformer.intervals == expected_intervals
 
         expect = pd.Series(data)
diff --git a/tests/unit/transformers/test_numerical.py b/tests/unit/transformers/test_numerical.py
index 8ec6c5a84..3d13bd2a2 100644
--- a/tests/unit/transformers/test_numerical.py
+++ b/tests/unit/transformers/test_numerical.py
@@ -421,12 +421,14 @@ def test__reverse_transform_rounding_none_with_nulls(self):
         - First column of the input array as entered, replacing the indicated value with a Nan.
         """
         # Setup
-        data = pd.DataFrame([
+        data = [
             [0., 0.],
             [1.2, 0.],
             [3.45, 1.],
             [6.789, 0.],
-        ], columns=['a', 'b'])
+        ]
+
+        data = pd.DataFrame(data, columns=['a', 'b'])
 
         # Run
         transformer = NumericalTransformer()