Repository: dbt-labs/dbt-core

Issue:
Seed compilation fails with non-ascii character in v0.14.0 
## Issue

Dbt seed fails if seed file contains a special character. This is new in v0.14.0, in v0.13.1 it was working for us. This looks like https://github.com/fishtown-analytics/dbt/issues/1616 could be related.

### Issue description
When running `dbt seed`, dbt fails with output:
```
Encountered an error:
Compilation Error in seed nonascii (data/nonascii.csv)
  'ascii' codec can't encode character u'\u2013' in position 6: ordinal not in range(128)
```

### System information
The output of `dbt --version`:
```
installed version: 0.14.0
   latest version: 0.14.0
```

OS: MacOS
Python version: 2.7

### Steps to reproduce
Here's a sample csv:
[nonascii.csv.zip](https://github.com/fishtown-analytics/dbt/files/3433083/nonascii.csv.zip)

It has contents:
```
id
Uh – Oh
```
(note the `–` is not a plain `-`)



Discussion:
Thanks for the report @brian-bk -- I think that #1452 might be relevant here. We'll check it out!

Resolving patch:
diff --git a/core/dbt/clients/agate_helper.py b/core/dbt/clients/agate_helper.py
--- a/core/dbt/clients/agate_helper.py
+++ b/core/dbt/clients/agate_helper.py
@@ -1,11 +1,7 @@
-from codecs import BOM_UTF8
-
 import dbt.compat
 
 import agate
 
-BOM = BOM_UTF8.decode('utf-8')  # '\ufeff'
-
 DEFAULT_TYPE_TESTER = agate.TypeTester(types=[
     agate.data_types.Number(null_values=('null', '')),
     agate.data_types.TimeDelta(null_values=('null', '')),
@@ -46,7 +42,7 @@ def as_matrix(table):
 
 
 def from_csv(abspath):
-    with dbt.compat.open_file(abspath) as fp:
-        if fp.read(1) != BOM:
+    with dbt.compat.open_seed_file(abspath) as fp:
+        if fp.read(len(dbt.compat.BOM_UTF8)) != dbt.compat.BOM_UTF8:
             fp.seek(0)
         return agate.Table.from_csv(fp, column_types=DEFAULT_TYPE_TESTER)
diff --git a/core/dbt/compat.py b/core/dbt/compat.py
--- a/core/dbt/compat.py
+++ b/core/dbt/compat.py
@@ -98,6 +98,20 @@ def open_file(path):
     return open(path, encoding='utf-8')
 
 
+if WHICH_PYTHON == 2:
+    BOM_UTF8 = codecs.BOM_UTF8
+else:
+    BOM_UTF8 = codecs.BOM_UTF8.decode('utf-8')
+
+
+def open_seed_file(path):
+    if WHICH_PYTHON == 2:
+        fp = open(path, 'Urb')
+    else:
+        fp = open(path, encoding='utf-8')
+    return fp
+
+
 if WHICH_PYTHON == 2:
     # In python 2, classmethod and staticmethod do not allow setters, so you
     # can't treat classmethods as first-class objects like you can regular


Test patch:
diff --git a/test/integration/005_simple_seed_test/data-unicode/seed_unicode.csv b/test/integration/005_simple_seed_test/data-unicode/seed_unicode.csv
new file mode 100644
--- /dev/null
+++ b/test/integration/005_simple_seed_test/data-unicode/seed_unicode.csv
@@ -0,0 +1,2 @@
+id
+Uh – Oh
diff --git a/test/integration/005_simple_seed_test/test_simple_seed.py b/test/integration/005_simple_seed_test/test_simple_seed.py
--- a/test/integration/005_simple_seed_test/test_simple_seed.py
+++ b/test/integration/005_simple_seed_test/test_simple_seed.py
@@ -197,3 +197,25 @@ def test_simple_seed(self):
         results = self.run_dbt(["seed"])
         self.assertEqual(len(results),  1)
         self.assertTablesEqual("seed_bom", "seed_expected")
+
+
+class TestSimpleSeedWithUnicode(DBTIntegrationTest):
+
+    @property
+    def schema(self):
+        return "simple_seed_005"
+
+    @property
+    def models(self):
+        return "models"
+
+    @property
+    def project_config(self):
+        return {
+            "data-paths": ['data-unicode']
+        }
+
+    @use_profile('postgres')
+    def test_simple_seed(self):
+        results = self.run_dbt(["seed"])
+        self.assertEqual(len(results),  1)