diff --git a/README.rst b/README.rst
index e84d334..aeb15f9 100644
--- a/README.rst
+++ b/README.rst
@@ -72,7 +72,7 @@ rebase your commits for you.
 
 All changes should include tests and pass flake8_.
 
-.. image:: https://api.travis-ci.org/PyCQA/pyflakes.svg
+.. image:: https://api.travis-ci.org/PyCQA/pyflakes.svg?branch=master
    :target: https://travis-ci.org/PyCQA/pyflakes
    :alt: Build status
 
diff --git a/pyflakes/api.py b/pyflakes/api.py
index a535bff..e30f920 100644
--- a/pyflakes/api.py
+++ b/pyflakes/api.py
@@ -5,6 +5,7 @@ from __future__ import with_statement
 
 import sys
 import os
+import re
 import _ast
 
 from pyflakes import checker, __version__
@@ -13,6 +14,9 @@ from pyflakes import reporter as modReporter
 __all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
 
 
+PYTHON_SHEBANG_REGEX = re.compile(br'^#!.*\bpython[23]?\b\s*$')
+
+
 def check(codeString, filename, reporter=None):
     """
     Check the Python source given by C{codeString} for flakes.
@@ -108,6 +112,25 @@ def checkPath(filename, reporter=None):
     return check(codestr, filename, reporter)
 
 
+def isPythonFile(filename):
+    """Return True if filename points to a Python file."""
+    if filename.endswith('.py'):
+        return True
+
+    max_bytes = 128
+
+    try:
+        with open(filename, 'rb') as f:
+            text = f.read(max_bytes)
+            if not text:
+                return False
+    except IOError:
+        return False
+
+    first_line = text.splitlines()[0]
+    return PYTHON_SHEBANG_REGEX.match(first_line)
+
+
 def iterSourceCode(paths):
     """
     Iterate over all Python source files in C{paths}.
@@ -120,8 +143,9 @@ def iterSourceCode(paths):
         if os.path.isdir(path):
             for dirpath, dirnames, filenames in os.walk(path):
                 for filename in filenames:
-                    if filename.endswith('.py'):
-                        yield os.path.join(dirpath, filename)
+                    full_path = os.path.join(dirpath, filename)
+                    if isPythonFile(full_path):
+                        yield full_path
         else:
             yield path
 

Please also check python scripts that are not named *.py

*Original report by [morph-debian](https://launchpad.net/~morph-debian) on [Launchpad](https://bugs.launchpad.net/bugs/970465):*

------------------------------------

Hello,
as reported on Debian at http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=653890:

"""
It would be great if pyflakes could detect files that are python scripts
but are not named *.py. It would have to read the first two bytes of
each file to check for scripts and then read the rest of the line to
determine if it is a python script. It would need to process stuff like:

#!/usr/bin/python
#!/usr/local/bin/python
#!/usr/bin/env python
#!/usr/bin/python2.6
#!/usr/bin/python3.2
"""

Regards,
Sandro

diff --git a/pyflakes/test/test_api.py b/pyflakes/test/test_api.py
index 51b0027..a1e0be2 100644
--- a/pyflakes/test/test_api.py
+++ b/pyflakes/test/test_api.py
@@ -187,6 +187,22 @@ class TestIterSourceCode(TestCase):
             sorted(iterSourceCode([self.tempdir])),
             sorted([apath, bpath, cpath]))
 
+    def test_shebang(self):
+        """
+        Find Python files that don't end with `.py`, but contain a Python
+        shebang.
+        """
+        apath = os.path.join(self.tempdir, 'a')
+        fd = open(apath, 'w')
+        fd.write('#!/usr/bin/env python\n')
+        fd.close()
+
+        self.makeEmptyFile('b')
+
+        self.assertEqual(
+            list(iterSourceCode([self.tempdir])),
+            list([apath]))
+
     def test_multipleDirectories(self):
         """
         L{iterSourceCode} can be given multiple directories.  It will recurse

pip install -e .
parse_log_pytest
pytest
flake8 pytest
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
certifi==2021.5.30
flake8==5.0.4
importlib-metadata==4.2.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mccabe==0.7.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.9.1
-e git+https://github.com/PyCQA/pyflakes.git@1af4f14ad4675bf5c61c47bbb7c2421b50d1cba4#egg=pyflakes
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: pyflakes
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:
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - mccabe==0.7.0
      - pycodestyle==2.9.1
prefix: /opt/conda/envs/pyflakes

pyflakes/test/test_api.py::TestIterSourceCode::test_shebang
pyflakes/test/test_api.py::TestIterSourceCode::test_emptyDirectory pyflakes/test/test_api.py::TestIterSourceCode::test_explicitFiles pyflakes/test/test_api.py::TestIterSourceCode::test_multipleDirectories pyflakes/test/test_api.py::TestIterSourceCode::test_onlyPythonSource pyflakes/test/test_api.py::TestIterSourceCode::test_recurses pyflakes/test/test_api.py::TestIterSourceCode::test_singleFile pyflakes/test/test_api.py::TestReporter::test_flake pyflakes/test/test_api.py::TestReporter::test_multiLineSyntaxError pyflakes/test/test_api.py::TestReporter::test_syntaxError pyflakes/test/test_api.py::TestReporter::test_syntaxErrorNoOffset pyflakes/test/test_api.py::TestReporter::test_unexpectedError pyflakes/test/test_api.py::CheckTests::test_CRLFLineEndings pyflakes/test/test_api.py::CheckTests::test_checkPathNonExisting pyflakes/test/test_api.py::CheckTests::test_checkRecursive pyflakes/test/test_api.py::CheckTests::test_encodedFileUTF8 pyflakes/test/test_api.py::CheckTests::test_eofSyntaxError pyflakes/test/test_api.py::CheckTests::test_eofSyntaxErrorWithTab pyflakes/test/test_api.py::CheckTests::test_invalidEscape pyflakes/test/test_api.py::CheckTests::test_legacyScript pyflakes/test/test_api.py::CheckTests::test_misencodedFileUTF16 pyflakes/test/test_api.py::CheckTests::test_misencodedFileUTF8 pyflakes/test/test_api.py::CheckTests::test_missingTrailingNewline pyflakes/test/test_api.py::CheckTests::test_multilineSyntaxError pyflakes/test/test_api.py::CheckTests::test_nonDefaultFollowsDefaultSyntaxError pyflakes/test/test_api.py::CheckTests::test_nonKeywordAfterKeywordSyntaxError pyflakes/test/test_api.py::CheckTests::test_pyflakesWarning pyflakes/test/test_api.py::IntegrationTests::test_errors_io pyflakes/test/test_api.py::IntegrationTests::test_errors_syntax pyflakes/test/test_api.py::IntegrationTests::test_fileWithFlakes pyflakes/test/test_api.py::IntegrationTests::test_goodFile pyflakes/test/test_api.py::IntegrationTests::test_readFromStdin pyflakes/test/test_api.py::TestMain::test_errors_io pyflakes/test/test_api.py::TestMain::test_errors_syntax pyflakes/test/test_api.py::TestMain::test_fileWithFlakes pyflakes/test/test_api.py::TestMain::test_goodFile pyflakes/test/test_api.py::TestMain::test_readFromStdin
README.rst pyflakes/api.py
README.rst pyflakes/api.py