roll: @brew 
Could you please review
roll: Thanks Brook!
diff --git a/data/empty.csv b/data/empty.csv
new file mode 100644
index 0000000..e69de29
diff --git a/data/invalid_no_headers.csv b/data/invalid_no_headers.csv
new file mode 100644
index 0000000..090c676
--- /dev/null
+++ b/data/invalid_no_headers.csv
@@ -0,0 +1,3 @@
+1,english
+2,中国人
+3,german,extra
diff --git a/goodtables/inspector.py b/goodtables/inspector.py
index dce6a06..ca64eea 100644
--- a/goodtables/inspector.py
+++ b/goodtables/inspector.py
@@ -4,6 +4,7 @@ from __future__ import print_function
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import six
 import inspect
 import datetime
 import operator
@@ -133,7 +134,7 @@ class Inspector(object):
         # Prepare vars
         errors = []
         warnings = []
-        headers = None
+        headers = []
         row_number = 0
         fatal_error = False
         checks = copy(self.__checks)
@@ -147,6 +148,8 @@ class Inspector(object):
             stream.open()
             sample = stream.sample
             headers = stream.headers
+            if headers is None:
+                headers = [None] * len(sample[0]) if sample else []
             if _filter_checks(checks, type='schema'):
                 if schema is None and self.__infer_schema:
                     schema = Schema(infer(headers, sample))
@@ -174,13 +177,14 @@ class Inspector(object):
 
         # Head checks
         if not fatal_error:
-            head_checks = _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
+            if None not in headers:
+                head_checks = _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:
@@ -191,7 +195,7 @@ class Inspector(object):
                 extended_rows = stream.iter(extended=True)
                 while True:
                     try:
-                        row_number, headers, row = next(extended_rows)
+                        row_number, _, row = next(extended_rows)
                     except StopIteration:
                         break
                     except Exception as exception:
@@ -235,6 +239,7 @@ class Inspector(object):
         stop = datetime.datetime.now()
 
         # Compose report
+        headers = headers if None not in headers else None
         errors = errors[:self.__error_limit]
         errors = _sort_errors(errors)
         report = copy(extra)
@@ -319,10 +324,13 @@ def _prepare_checks(setup, custom, order_fields, infer_fields):
 
     # Bind options
     for check in checks:
-        args, _, _, _ = inspect.getargspec(check['func'])
-        if 'order_fields' in args:
+        if six.PY2:
+            parameters, _, _, _ = inspect.getargspec(check['func'])
+        else:
+            parameters = inspect.signature(check['func']).parameters
+        if 'order_fields' in parameters:
             check['func'] = partial(check['func'], order_fields=order_fields)
-        if 'infer_fields' in args:
+        if 'infer_fields' in parameters:
             check['func'] = partial(check['func'], infer_fields=infer_fields)
 
     return checks

Ability to check table without headers?
(This would obviously not run checks related to headers.)

```
(pilot-dm4t) legend:refit-dirty dan (master)$ head archive/House1.csv 
1381323977,523,74,0,69,0,0,0,0,0,1
1381323990,526,75,0,69,0,0,0,0,0,1
1381324005,540,74,0,68,0,0,0,0,0,1
1381324020,532,74,0,68,0,0,0,0,0,1
1381324035,540,74,0,69,0,0,0,0,0,1
```

Adding a `"dialect": {"header": False}` to resource has no effect.

`datapackage.json`:

```
...
"resources": [
    {
      "name": "house_1.csv",
      "path": "archive/House1.csv",
      "dialect": {
        "header": false
      },
...
```

I would opt to just skip the header-related checks on command line, but can't see how (#175)

---

# Tasks

- [x] support inspection for tables without headers
- [x] support inspection for data package resources without headers (now it's possible with supported CSV dialect `header` option- https://github.com/frictionlessdata/datapackage-py/blob/master/tests/test_resource.py#L517-L528) - MOVED #174
diff --git a/tests/test_inspector.py b/tests/test_inspector.py
index 82d4626..aedb14a 100644
--- a/tests/test_inspector.py
+++ b/tests/test_inspector.py
@@ -4,6 +4,7 @@ from __future__ import print_function
 from __future__ import absolute_import
 from __future__ import unicode_literals
 
+import pytest
 from goodtables import Inspector
 
 
@@ -179,3 +180,22 @@ def test_inspector_warnings_table_and_error_limit():
     assert len(report['warnings']) == 2
     assert 'table(s) limit' in report['warnings'][0]
     assert 'error(s) limit' in report['warnings'][1]
+
+
+# Empty source
+
+def test_inspector_empty_source():
+    inspector = Inspector()
+    report = inspector.inspect('data/empty.csv')
+    assert report['tables'][0]['row-count'] == 0
+    assert report['tables'][0]['error-count'] == 0
+
+
+# No headers source
+
+def test_inspector_no_headers():
+    inspector = Inspector()
+    report = inspector.inspect('data/invalid_no_headers.csv', headers=None)
+    assert report['tables'][0]['row-count'] == 3
+    assert report['tables'][0]['error-count'] == 1
+    assert report['tables'][0]['errors'][0]['code'] == 'extra-value'

pip install -e .[develop,ods]
parse_log_pytest
pytest
pytest pytest-cov pytest-mock mock pyyaml
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
boto3==1.23.10
botocore==1.26.10
certifi==2021.5.30
chardet==5.0.0
charset-normalizer==2.0.12
click==6.7
coverage==6.2
datapackage==0.8.9
distlib==0.3.9
et-xmlfile==1.1.0
ezodf==0.3.2
filelock==3.4.1
future==0.18.3
-e git+https://github.com/frictionlessdata/goodtables-py.git@faf5dddc7c99c4ea9581fe7565150a49c0e4bdd9#egg=goodtables
greenlet==2.0.2
idna==3.10
ijson==3.3.0
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isodate==0.6.1
jmespath==0.10.0
jsonlines==3.1.0
jsonschema==2.6.0
jsontableschema==0.10.1
linear-tsv==1.1.0
lxml==3.8.0
mccabe==0.7.0
mock==5.2.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
openpyxl==3.1.3
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
platformdirs==2.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.10.0
pydocstyle==6.3.0
pyflakes==3.0.1
pylama==7.7.1
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
pytest-mock==3.6.1
python-dateutil==2.9.0.post0
PyYAML==6.0.1
requests==2.27.1
rfc3986==0.4.1
s3transfer==0.5.2
six==1.17.0
snowballstemmer==2.2.0
SQLAlchemy==1.4.54
tabulator==1.53.5
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
tox==3.28.0
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
unicodecsv==0.14.1
urllib3==1.26.20
virtualenv==20.17.1
xlrd==2.0.1
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: goodtables-py
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - boto3==1.23.10
      - botocore==1.26.10
      - chardet==5.0.0
      - charset-normalizer==2.0.12
      - click==6.7
      - coverage==6.2
      - datapackage==0.8.9
      - distlib==0.3.9
      - et-xmlfile==1.1.0
      - ezodf==0.3.2
      - filelock==3.4.1
      - future==0.18.3
      - greenlet==2.0.2
      - idna==3.10
      - ijson==3.3.0
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - isodate==0.6.1
      - jmespath==0.10.0
      - jsonlines==3.1.0
      - jsonschema==2.6.0
      - jsontableschema==0.10.1
      - linear-tsv==1.1.0
      - lxml==3.8.0
      - mccabe==0.7.0
      - mock==5.2.0
      - openpyxl==3.1.3
      - platformdirs==2.4.0
      - pycodestyle==2.10.0
      - pydocstyle==6.3.0
      - pyflakes==3.0.1
      - pylama==7.7.1
      - pytest-cov==4.0.0
      - pytest-mock==3.6.1
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.1
      - requests==2.27.1
      - rfc3986==0.4.1
      - s3transfer==0.5.2
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sqlalchemy==1.4.54
      - tabulator==1.53.5
      - tomli==1.2.3
      - tox==3.28.0
      - unicodecsv==0.14.1
      - urllib3==1.26.20
      - virtualenv==20.17.1
      - xlrd==2.0.1
prefix: /opt/conda/envs/goodtables-py

tests/test_inspector.py::test_inspector_empty_source tests/test_inspector.py::test_inspector_no_headers
tests/test_inspector.py::test_inspector_catch_all_open_exceptions
tests/test_inspector.py::test_inspector_table_valid tests/test_inspector.py::test_inspector_table_invalid tests/test_inspector.py::test_inspector_table_invalid_error_limit tests/test_inspector.py::test_inspector_table_invalid_row_limit tests/test_inspector.py::test_inspector_datapackage_valid tests/test_inspector.py::test_inspector_datapackage_invalid tests/test_inspector.py::test_inspector_datapackage_invalid_table_limit tests/test_inspector.py::test_inspector_tables_invalid tests/test_inspector.py::test_inspector_catch_all_iter_exceptions tests/test_inspector.py::test_inspector_warnings_no tests/test_inspector.py::test_inspector_warnings_bad_datapackage_json tests/test_inspector.py::test_inspector_warnings_table_limit tests/test_inspector.py::test_inspector_warnings_row_limit tests/test_inspector.py::test_inspector_warnings_error_limit tests/test_inspector.py::test_inspector_warnings_table_and_row_limit tests/test_inspector.py::test_inspector_warnings_table_and_error_limit
data/empty.csv goodtables/inspector.py data/invalid_no_headers.csv
data/empty.csv goodtables/inspector.py data/invalid_no_headers.csv