# Buggy code
def __inspect_table(self, table):
    # Start timer
    start = datetime.datetime.now()

    # Prepare vars
    errors = []
    headers = None
    row_number = 0
    fatal_error = False
    checks = copy(self.__checks)
    source = table["source"]
    stream = table["stream"]
    schema = table["schema"]
    extra = table["extra"]

    # Prepare table
    try:
        stream.open()
        sample = stream.sample
        headers = stream.headers
        if self.__filter_checks(checks, type="schema"):
            if schema is None and self.__infer_schema:
                schema = Schema(infer(headers, sample))
        if schema is None:
            checks = self.__filter_checks(checks, type="schema", inverse=True)
    except Exception as exception:
        fatal_error = True
        message = str(exception)
        if isinstance(exception, tabulator.exceptions.SourceError):
            code = "source-error"
        elif isinstance(exception, tabulator.exceptions.SchemeError):
            code = "scheme-error"
        elif isinstance(exception, tabulator.exceptions.FormatError):
            code = "format-error"
        elif isinstance(exception, tabulator.exceptions.EncodingError):
            code = "encoding-error"
        elif isinstance(exception, tabulator.exceptions.IOError):
            code = "io-error"
        elif isinstance(exception, tabulator.exceptions.HTTPError):
            code = "http-error"
        else:
            raise
        errors.append(
            {
                "row": None,
                "code": code,
                "message": message,
                "row-number": None,
                "column-number": None,
            }
        )

    # Prepare columns
    if not fatal_error:
        columns = []
        fields = [None] * len(headers)
        if schema is not None:
            fields = schema.fields
        iterator = zip_longest(headers, fields, fillvalue=_FILLVALUE)
        for number, (header, field) in enumerate(iterator, start=1):
            column = {"number": number}
            if header is not _FILLVALUE:
                column["header"] = header
            if field is not _FILLVALUE:
                column["field"] = field
            columns.append(column)

    # Head checks
    if not fatal_error:
        head_checks = self.__filter_checks(checks, context="head")
        for check in head_checks:
            if not columns:
                break
            check["func"](errors, columns, sample)
        for error in errors:
            error["row"] = None

    # Body checks
    if not fatal_error:
        states = {}
        colmap = {column["number"]: column for column in columns}
        body_checks = self.__filter_checks(checks, context="body")
        with stream:
            for row_number, headers, row in stream.iter(extended=True):
                columns = []
                iterator = zip_longest(headers, row, fillvalue=_FILLVALUE)
                for number, (header, value) in enumerate(iterator, start=1):
                    colref = colmap.get(number, {})
                    column = {"number": number}
                    if header is not _FILLVALUE:
                        column["header"] = colref.get("header", header)
                    if "field" in colref:
                        column["field"] = colref["field"]
                    if value is not _FILLVALUE:
                        column["value"] = value
                    columns.append(column)
                for check in body_checks:
                    if not columns:
                        break
                    state = states.setdefault(check["code"], {})
                    check["func"](errors, columns, row_number, state)
                for error in reversed(errors):
                    if "row" in error:
                        break
                    error["row"] = row
                if row_number >= self.__row_limit:
                    break
                if len(errors) >= self.__error_limit:
                    break

    # Stop timer
    stop = datetime.datetime.now()

    # Compose report
    errors = errors[: self.__error_limit]
    report = copy(extra)
    report.update(
        {
            "time": round((stop - start).total_seconds(), 3),
            "valid": not bool(errors),
            "error-count": len(errors),
            "row-count": row_number,
            "headers": headers,
            "source": source,
            "errors": errors,
        }
    )

    return report


# Traceback (UnicodeDecodeError)
Traceback (most recent call last):
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/celery/app/trace.py", line 368, in trace_task
R = retval = fun(*args, **kwargs)
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/celery/app/trace.py", line 623, in __protected_call__
return self.run(*args, **kwargs)
File "/home/adria/dev/pyenvs/gt/src/goodtables.io/goodtablesio/tasks/validate.py", line 41, in validate
report = inspector.inspect(validation_conf['source'], preset='nested')
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/goodtables/inspector.py", line 106, in inspect
report = task.get()
File "/usr/lib/python3.5/multiprocessing/pool.py", line 608, in get
raise self._value
File "/usr/lib/python3.5/multiprocessing/pool.py", line 119, in worker
result = (True, func(*args, **kwds))
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/goodtables/inspector.py", line 145, in __inspect_table
stream.open()
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/tabulator/stream.py", line 206, in open
self.__extract_sample()
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/tabulator/stream.py", line 356, in __extract_sample
number, headers, row = next(self.__parser.extended_rows)
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/tabulator/parsers/csv.py", line 89, in __iter_extended_rows
sample, dialect = self.__prepare_dialect(self.__chars)
File "/home/adria/dev/pyenvs/gt/lib/python3.5/site-packages/tabulator/parsers/csv.py", line 100, in __prepare_dialect
sample.append(next(stream))
File "/home/adria/dev/pyenvs/gt/lib/python3.5/codecs.py", line 321, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcd in position 313: invalid continuation byte
[2017-05-10 16:28:30,081: ERROR/PoolWorker-1] Task goodtablesio.tasks.validate[0e8b8e8b-42dd-4d74-9510-132ccd100839] raised unexpected: UnicodeDecodeError('utf-8', b'CVE_ENT,CVE_MUN,CVE_LOC,NOM_LOC\r"01","001","0001","AGUASCALIENTES"\n"01","001","0094","GRANJA ADELITA"\n"01","001","0096","AGUA AZUL"\n"01","001","0100","RANCHO ALEGRE"\n"01","001","0102","LOS ARBOLITOS [RANCHO]"\n"01","001","0104","ARDILLAS DE ABAJO (LAS ARDILLAS)"\n"01","001","0106","ARELLANO"\n"01","001","0112","BAJ\xcdO LOS V\xc1ZQUEZ"\n"01","001","0113","BAJ\xcdO DE MONTORO"\n"01","001","0114","RESIDENCIAL SAN NICOL\xc1S [BA\xd1OS LA CANTERA]"\n"01","001","0120","BUENAVISTA DE PE\xd1UELAS"\n"01","001","0121","CABECITA 3 MAR\xcdAS (RANCHO NUEVO)"\n"01","001","0125","CA\xd1ADA GRANDE DE COTORINA"\n"01","001","0126","CA\xd1ADA HONDA [ESTACI\xd3N]"\n"01","001","0127","LOS CA\xd1OS"\n"01","001","0128","EL CARI\xd1\xc1N"\n"01","001","0129","EL CARMEN [GRANJA]"\n"01","001","0135","EL CEDAZO (CEDAZO DE SAN ANTONIO)"\n"01","001","0138","CENTRO DE ARRIBA (EL TARAY)"\n"01","001","0139","CIENEGUILLA (LA LUMBRERA)"\n"01","001","0141","COBOS"\n"01","001","0144","EL COLORADO (EL SOYATAL)"\n"01","001","0146","EL CONEJAL"\n"01","001","0157","COTORINA DE ABAJO"\n"01","001","0162","COYOTES"\n"01","001","0166","LA HUERTA (LA CRUZ)"\n"01","001","0170","CUAUHT\xc9MOC (LAS PALOMAS)"\n"01","001","0171","LOS CUERVOS (LOS OJOS DE AGUA)"\n"01","001","0172","SAN JOS\xc9 [GRANJA]"\n"01","001","0176","LA CHIRIPA"\n"01","001","0182","DOLORES"\n"01","001","0183","LOS DOLORES"\n"01","001","0190","EL DURAZNILLO"\n"01","001","0191","LOS DUR\xd3N"\n"01","001","0197","LA ESCONDIDA"\n"01","001","0201","DESDE EL CORAZ\xd3N DEL FRUTO"\n"01","001","0207","VALLE REDONDO"\n"01","001","0209","LA FORTUNA"\n"01","001","0212","LOMAS DEL GACHUP\xcdN"\n"01","001","0213","EL CARMEN (GALLINAS G\xdcERAS) [RANCHO]"\n"01","001","0216","LA GLORIA"\n"01","001","0226","HACIENDA NUEVA"\n"01","001","0227","LA HACIENDITA (LA ESPERANZA)"\n"01","001","0228","LA HERRADA"\n"01","001","0230","DON ABRAHAM [RANCHO]"\n"01","001","0231","PUERTA DE LOS HOYOS"\n"01","001","0236","LAS JABONERAS"\n"01","001","0237","JALTOMATE"\n"01","001","0239","GENERAL JOS\xc9 MAR\xcdA MORELOS Y PAV\xd3N (CA\xd1ADA HONDA)"\n"01","001","0253","LOS LIRIOS"\n"01","001","0256","LA LOMA DE LOS NEGRITOS"\n"01","001","0265","EL MALACATE"\n"01","001","0270","LA MASCOTA"\n"01","001","0272","MATAMOROS"\n"01","001","0279","SAN ISIDRO"\n"01","001","0280","SAN JOS\xc9"\n"01","001","0283","EL MOLINO"\n"01","001","0285","MONTORO"\n"01","001","0291","LOS NEGRITOS"\n"01","001","0292","EL NI\xc1GARA"\n"01","001","0293","NORIAS DE OJOCALIENTE"\n"01","001","0296","EL OCOTE"\n"01","001","0297","COMUNIDAD EL ROC\xcdO"\n"01","001","0309","LAS PALOMAS"\n"01","001","0315","PE\xd1UELAS (EL CIENEGAL)"\n"01","001","0321","PIEDRAS CHINAS"\n"01","001","0329","PRESA DE GUADALUPE"\n"01","001","0336","SOLEDAD DE ARRIBA"\n"01","001","0340","LA PUERTA (GRANJAS CARI\xd1\xc1N)"\n"01","001","0345","EL REFUGIO DE PE\xd1UELAS"\n"01","001","0347","EL REFUGIO I"\n"01","001","0353","EL RODEO', 313, 314, 'invalid continuation byte')

# Fixed code
def __inspect_table(self, table):
    # Start timer
    start = datetime.datetime.now()

    # Prepare vars
    errors = []
    headers = None
    row_number = 0
    fatal_error = False
    checks = copy(self.__checks)
    source = table["source"]
    stream = table["stream"]
    schema = table["schema"]
    extra = table["extra"]

    # Prepare table
    try:
        stream.open()
        sample = stream.sample
        headers = stream.headers
        if self.__filter_checks(checks, type="schema"):
            if schema is None and self.__infer_schema:
                schema = Schema(infer(headers, sample))
        if schema is None:
            checks = self.__filter_checks(checks, type="schema", inverse=True)
    except Exception as exception:
        fatal_error = True
        error = self.__compose_error_from_exception(exception)
        errors.append(error)

    # Prepare columns
    if not fatal_error:
        columns = []
        fields = [None] * len(headers)
        if schema is not None:
            fields = schema.fields
        iterator = zip_longest(headers, fields, fillvalue=_FILLVALUE)
        for number, (header, field) in enumerate(iterator, start=1):
            column = {"number": number}
            if header is not _FILLVALUE:
                column["header"] = header
            if field is not _FILLVALUE:
                column["field"] = field
            columns.append(column)

    # Head checks
    if not fatal_error:
        head_checks = self.__filter_checks(checks, context="head")
        for check in head_checks:
            if not columns:
                break
            check["func"](errors, columns, sample)
        for error in errors:
            error["row"] = None

    # Body checks
    if not fatal_error:
        states = {}
        colmap = {column["number"]: column for column in columns}
        body_checks = self.__filter_checks(checks, context="body")
        with stream:
            extended_rows = stream.iter(extended=True)
            while True:
                try:
                    row_number, headers, row = next(extended_rows)
                except StopIteration:
                    break
                except Exception as exception:
                    fatal_error = True
                    error = self.__compose_error_from_exception(exception)
                    errors.append(error)
                    break
                columns = []
                iterator = zip_longest(headers, row, fillvalue=_FILLVALUE)
                for number, (header, value) in enumerate(iterator, start=1):
                    colref = colmap.get(number, {})
                    column = {"number": number}
                    if header is not _FILLVALUE:
                        column["header"] = colref.get("header", header)
                    if "field" in colref:
                        column["field"] = colref["field"]
                    if value is not _FILLVALUE:
                        column["value"] = value
                    columns.append(column)
                for check in body_checks:
                    if not columns:
                        break
                    state = states.setdefault(check["code"], {})
                    check["func"](errors, columns, row_number, state)
                for error in reversed(errors):
                    if "row" in error:
                        break
                    error["row"] = row
                if row_number >= self.__row_limit:
                    break
                if len(errors) >= self.__error_limit:
                    break

    # Stop timer
    stop = datetime.datetime.now()

    # Compose report
    errors = errors[: self.__error_limit]
    report = copy(extra)
    report.update(
        {
            "time": round((stop - start).total_seconds(), 3),
            "valid": not bool(errors),
            "error-count": len(errors),
            "row-count": row_number,
            "headers": headers,
            "source": source,
            "errors": errors,
        }
    )

    return report